1 /**
  2  * Cisco Finesse - JavaScript Library
  3  * Version 11.6.1)
  4  * Cisco Systems, Inc.
  5  * http://www.cisco.com/
  6  *
  7  * Portions created or assigned to Cisco Systems, Inc. are
  8  * Copyright (c) 2017 Cisco Systems, Inc. or its affiliated entities.  All Rights Reserved.
  9  */
 10 /**
 11  * This JavaScript library is made available to Cisco partners and customers as
 12  * a convenience to help minimize the cost of Cisco Finesse customizations.
 13  * This library can be used in Cisco Finesse deployments.  Cisco does not
 14  * permit the use of this library in customer deployments that do not include
 15  * Cisco Finesse.  Support for the JavaScript library is provided on a
 16  * "best effort" basis via CDN.  Like any custom deployment, it is the
 17  * responsibility of the partner and/or customer to ensure that the
 18  * customization works correctly and this includes ensuring that the Cisco
 19  * Finesse JavaScript is properly integrated into 3rd party applications.
 20  * Cisco reserves the right to make changes to the JavaScript code and
 21  * corresponding API as part of the normal Cisco Finesse release cycle.  The
 22  * implication of this is that new versions of the JavaScript might be
 23  * incompatible with applications built on older Finesse integrations.  That
 24  * said, it is Cisco's intention to ensure JavaScript compatibility across
 25  * versions as much as possible and Cisco will make every effort to clearly
 26  * document any differences in the JavaScript across versions in the event
 27  * that a backwards compatibility impacting change is made.
 28  */
 29 (function (root, factory) {
 30 	if (typeof define === 'function' && define.amd) {
 31 		define('finesse', [], factory);
 32 	} else {
 33 		root.finesse = factory();
 34 	}
 35 }(this, function () {
 36 /**
 37  * @license almond 0.2.9 Copyright (c) 2011-2014, The Dojo Foundation All Rights Reserved.
 38  * Available via the MIT or new BSD license.
 39  * see: http://github.com/jrburke/almond for details
 40  */
 41 //Going sloppy to avoid 'use strict' string cost, but strict practices should
 42 //be followed.
 43 /*jslint sloppy: true */
 44 /*global setTimeout: false */
 45 
 46 var requirejs, require, define;
 47 (function (undef) {
 48     var main, req, makeMap, handlers,
 49         defined = {},
 50         waiting = {},
 51         config = {},
 52         defining = {},
 53         hasOwn = Object.prototype.hasOwnProperty,
 54         aps = [].slice,
 55         jsSuffixRegExp = /\.js$/;
 56 
 57     function hasProp(obj, prop) {
 58         return hasOwn.call(obj, prop);
 59     }
 60 
 61     /**
 62      * Given a relative module name, like ./something, normalize it to
 63      * a real name that can be mapped to a path.
 64      * @param {String} name the relative name
 65      * @param {String} baseName a real name that the name arg is relative
 66      * to.
 67      * @returns {String} normalized name
 68      */
 69     function normalize(name, baseName) {
 70         var nameParts, nameSegment, mapValue, foundMap, lastIndex,
 71             foundI, foundStarMap, starI, i, j, part,
 72             baseParts = baseName && baseName.split("/"),
 73             map = config.map,
 74             starMap = (map && map['*']) || {};
 75 
 76         //Adjust any relative paths.
 77         if (name && name.charAt(0) === ".") {
 78             //If have a base name, try to normalize against it,
 79             //otherwise, assume it is a top-level require that will
 80             //be relative to baseUrl in the end.
 81             if (baseName) {
 82                 //Convert baseName to array, and lop off the last part,
 83                 //so that . matches that "directory" and not name of the baseName's
 84                 //module. For instance, baseName of "one/two/three", maps to
 85                 //"one/two/three.js", but we want the directory, "one/two" for
 86                 //this normalization.
 87                 baseParts = baseParts.slice(0, baseParts.length - 1);
 88                 name = name.split('/');
 89                 lastIndex = name.length - 1;
 90 
 91                 // Node .js allowance:
 92                 if (config.nodeIdCompat && jsSuffixRegExp.test(name[lastIndex])) {
 93                     name[lastIndex] = name[lastIndex].replace(jsSuffixRegExp, '');
 94                 }
 95 
 96                 name = baseParts.concat(name);
 97 
 98                 //start trimDots
 99                 for (i = 0; i < name.length; i += 1) {
100                     part = name[i];
101                     if (part === ".") {
102                         name.splice(i, 1);
103                         i -= 1;
104                     } else if (part === "..") {
105                         if (i === 1 && (name[2] === '..' || name[0] === '..')) {
106                             //End of the line. Keep at least one non-dot
107                             //path segment at the front so it can be mapped
108                             //correctly to disk. Otherwise, there is likely
109                             //no path mapping for a path starting with '..'.
110                             //This can still fail, but catches the most reasonable
111                             //uses of ..
112                             break;
113                         } else if (i > 0) {
114                             name.splice(i - 1, 2);
115                             i -= 2;
116                         }
117                     }
118                 }
119                 //end trimDots
120 
121                 name = name.join("/");
122             } else if (name.indexOf('./') === 0) {
123                 // No baseName, so this is ID is resolved relative
124                 // to baseUrl, pull off the leading dot.
125                 name = name.substring(2);
126             }
127         }
128 
129         //Apply map config if available.
130         if ((baseParts || starMap) && map) {
131             nameParts = name.split('/');
132 
133             for (i = nameParts.length; i > 0; i -= 1) {
134                 nameSegment = nameParts.slice(0, i).join("/");
135 
136                 if (baseParts) {
137                     //Find the longest baseName segment match in the config.
138                     //So, do joins on the biggest to smallest lengths of baseParts.
139                     for (j = baseParts.length; j > 0; j -= 1) {
140                         mapValue = map[baseParts.slice(0, j).join('/')];
141 
142                         //baseName segment has  config, find if it has one for
143                         //this name.
144                         if (mapValue) {
145                             mapValue = mapValue[nameSegment];
146                             if (mapValue) {
147                                 //Match, update name to the new value.
148                                 foundMap = mapValue;
149                                 foundI = i;
150                                 break;
151                             }
152                         }
153                     }
154                 }
155 
156                 if (foundMap) {
157                     break;
158                 }
159 
160                 //Check for a star map match, but just hold on to it,
161                 //if there is a shorter segment match later in a matching
162                 //config, then favor over this star map.
163                 if (!foundStarMap && starMap && starMap[nameSegment]) {
164                     foundStarMap = starMap[nameSegment];
165                     starI = i;
166                 }
167             }
168 
169             if (!foundMap && foundStarMap) {
170                 foundMap = foundStarMap;
171                 foundI = starI;
172             }
173 
174             if (foundMap) {
175                 nameParts.splice(0, foundI, foundMap);
176                 name = nameParts.join('/');
177             }
178         }
179 
180         return name;
181     }
182 
183     function makeRequire(relName, forceSync) {
184         return function () {
185             //A version of a require function that passes a moduleName
186             //value for items that may need to
187             //look up paths relative to the moduleName
188             return req.apply(undef, aps.call(arguments, 0).concat([relName, forceSync]));
189         };
190     }
191 
192     function makeNormalize(relName) {
193         return function (name) {
194             return normalize(name, relName);
195         };
196     }
197 
198     function makeLoad(depName) {
199         return function (value) {
200             defined[depName] = value;
201         };
202     }
203 
204     function callDep(name) {
205         if (hasProp(waiting, name)) {
206             var args = waiting[name];
207             delete waiting[name];
208             defining[name] = true;
209             main.apply(undef, args);
210         }
211 
212         if (!hasProp(defined, name) && !hasProp(defining, name)) {
213             throw new Error('No ' + name);
214         }
215         return defined[name];
216     }
217 
218     //Turns a plugin!resource to [plugin, resource]
219     //with the plugin being undefined if the name
220     //did not have a plugin prefix.
221     function splitPrefix(name) {
222         var prefix,
223             index = name ? name.indexOf('!') : -1;
224         if (index > -1) {
225             prefix = name.substring(0, index);
226             name = name.substring(index + 1, name.length);
227         }
228         return [prefix, name];
229     }
230 
231     /**
232      * Makes a name map, normalizing the name, and using a plugin
233      * for normalization if necessary. Grabs a ref to plugin
234      * too, as an optimization.
235      */
236     makeMap = function (name, relName) {
237         var plugin,
238             parts = splitPrefix(name),
239             prefix = parts[0];
240 
241         name = parts[1];
242 
243         if (prefix) {
244             prefix = normalize(prefix, relName);
245             plugin = callDep(prefix);
246         }
247 
248         //Normalize according
249         if (prefix) {
250             if (plugin && plugin.normalize) {
251                 name = plugin.normalize(name, makeNormalize(relName));
252             } else {
253                 name = normalize(name, relName);
254             }
255         } else {
256             name = normalize(name, relName);
257             parts = splitPrefix(name);
258             prefix = parts[0];
259             name = parts[1];
260             if (prefix) {
261                 plugin = callDep(prefix);
262             }
263         }
264 
265         //Using ridiculous property names for space reasons
266         return {
267             f: prefix ? prefix + '!' + name : name, //fullName
268             n: name,
269             pr: prefix,
270             p: plugin
271         };
272     };
273 
274     function makeConfig(name) {
275         return function () {
276             return (config && config.config && config.config[name]) || {};
277         };
278     }
279 
280     handlers = {
281         require: function (name) {
282             return makeRequire(name);
283         },
284         exports: function (name) {
285             var e = defined[name];
286             if (typeof e !== 'undefined') {
287                 return e;
288             } else {
289                 return (defined[name] = {});
290             }
291         },
292         module: function (name) {
293             return {
294                 id: name,
295                 uri: '',
296                 exports: defined[name],
297                 config: makeConfig(name)
298             };
299         }
300     };
301 
302     main = function (name, deps, callback, relName) {
303         var cjsModule, depName, ret, map, i,
304             args = [],
305             callbackType = typeof callback,
306             usingExports;
307 
308         //Use name if no relName
309         relName = relName || name;
310 
311         //Call the callback to define the module, if necessary.
312         if (callbackType === 'undefined' || callbackType === 'function') {
313             //Pull out the defined dependencies and pass the ordered
314             //values to the callback.
315             //Default to [require, exports, module] if no deps
316             deps = !deps.length && callback.length ? ['require', 'exports', 'module'] : deps;
317             for (i = 0; i < deps.length; i += 1) {
318                 map = makeMap(deps[i], relName);
319                 depName = map.f;
320 
321                 //Fast path CommonJS standard dependencies.
322                 if (depName === "require") {
323                     args[i] = handlers.require(name);
324                 } else if (depName === "exports") {
325                     //CommonJS module spec 1.1
326                     args[i] = handlers.exports(name);
327                     usingExports = true;
328                 } else if (depName === "module") {
329                     //CommonJS module spec 1.1
330                     cjsModule = args[i] = handlers.module(name);
331                 } else if (hasProp(defined, depName) ||
332                            hasProp(waiting, depName) ||
333                            hasProp(defining, depName)) {
334                     args[i] = callDep(depName);
335                 } else if (map.p) {
336                     map.p.load(map.n, makeRequire(relName, true), makeLoad(depName), {});
337                     args[i] = defined[depName];
338                 } else {
339                     throw new Error(name + ' missing ' + depName);
340                 }
341             }
342 
343             ret = callback ? callback.apply(defined[name], args) : undefined;
344 
345             if (name) {
346                 //If setting exports via "module" is in play,
347                 //favor that over return value and exports. After that,
348                 //favor a non-undefined return value over exports use.
349                 if (cjsModule && cjsModule.exports !== undef &&
350                         cjsModule.exports !== defined[name]) {
351                     defined[name] = cjsModule.exports;
352                 } else if (ret !== undef || !usingExports) {
353                     //Use the return value from the function.
354                     defined[name] = ret;
355                 }
356             }
357         } else if (name) {
358             //May just be an object definition for the module. Only
359             //worry about defining if have a module name.
360             defined[name] = callback;
361         }
362     };
363 
364     requirejs = require = req = function (deps, callback, relName, forceSync, alt) {
365         if (typeof deps === "string") {
366             if (handlers[deps]) {
367                 //callback in this case is really relName
368                 return handlers[deps](callback);
369             }
370             //Just return the module wanted. In this scenario, the
371             //deps arg is the module name, and second arg (if passed)
372             //is just the relName.
373             //Normalize module name, if it contains . or ..
374             return callDep(makeMap(deps, callback).f);
375         } else if (!deps.splice) {
376             //deps is a config object, not an array.
377             config = deps;
378             if (config.deps) {
379                 req(config.deps, config.callback);
380             }
381             if (!callback) {
382                 return;
383             }
384 
385             if (callback.splice) {
386                 //callback is an array, which means it is a dependency list.
387                 //Adjust args if there are dependencies
388                 deps = callback;
389                 callback = relName;
390                 relName = null;
391             } else {
392                 deps = undef;
393             }
394         }
395 
396         //Support require(['a'])
397         callback = callback || function () {};
398 
399         //If relName is a function, it is an errback handler,
400         //so remove it.
401         if (typeof relName === 'function') {
402             relName = forceSync;
403             forceSync = alt;
404         }
405 
406         //Simulate async callback;
407         if (forceSync) {
408             main(undef, deps, callback, relName);
409         } else {
410             //Using a non-zero value because of concern for what old browsers
411             //do, and latest browsers "upgrade" to 4 if lower value is used:
412             //http://www.whatwg.org/specs/web-apps/current-work/multipage/timers.html#dom-windowtimers-settimeout:
413             //If want a value immediately, use require('id') instead -- something
414             //that works in almond on the global level, but not guaranteed and
415             //unlikely to work in other AMD implementations.
416             setTimeout(function () {
417                 main(undef, deps, callback, relName);
418             }, 4);
419         }
420 
421         return req;
422     };
423 
424     /**
425      * Just drops the config on the floor, but returns req in case
426      * the config return value is used.
427      */
428     req.config = function (cfg) {
429         return req(cfg);
430     };
431 
432     /**
433      * Expose module registry for debugging and tooling
434      */
435     requirejs._defined = defined;
436 
437     define = function (name, deps, callback) {
438 
439         //This module may not have dependencies
440         if (!deps.splice) {
441             //deps is not an array, so probably means
442             //an object literal or factory function for
443             //the value. Adjust args.
444             callback = deps;
445             deps = [];
446         }
447 
448         if (!hasProp(defined, name) && !hasProp(waiting, name)) {
449             waiting[name] = [name, deps, callback];
450         }
451     };
452 
453     define.amd = {
454         jQuery: true
455     };
456 }());
457 define("../thirdparty/almond", function(){});
458 
459 /* Simple JavaScript Inheritance
460  * By John Resig http://ejohn.org/
461  * MIT Licensed.
462  */
463 // Inspired by base2 and Prototype
464 define('../thirdparty/Class',[], function () {
465         var initializing = false, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;
466         // The base Class implementation (does nothing)
467         /** @private */
468         Class = function(){};
469         
470         // Create a new Class that inherits from this class
471         /** @private */
472         Class.extend = function(prop) {
473           var _super = this.prototype;
474           
475           // Instantiate a base class (but only create the instance,
476           // don't run the init constructor)
477           initializing = true;
478           var prototype = new this();
479           initializing = false;
480           
481           // Copy the properties over onto the new prototype
482           for (var name in prop) {
483             // Check if we're overwriting an existing function
484             prototype[name] = typeof prop[name] == "function" && 
485               typeof _super[name] == "function" && fnTest.test(prop[name]) ?
486               (function(name, fn){
487                 return function() {
488                   var tmp = this._super;
489                   
490                   // Add a new ._super() method that is the same method
491                   // but on the super-class
492                   this._super = _super[name];
493                   
494                   // The method only need to be bound temporarily, so we
495                   // remove it when we're done executing
496                   var ret = fn.apply(this, arguments);        
497                   this._super = tmp;
498                   
499                   return ret;
500                 };
501               })(name, prop[name]) :
502               prop[name];
503           }
504           
505           // The dummy class constructor
506           /** @private */
507           function Class() {
508             // All construction is actually done in the init method
509             if ( !initializing && this.init )
510               this.init.apply(this, arguments);
511           }
512           
513           // Populate our constructed prototype object
514           Class.prototype = prototype;
515           
516           // Enforce the constructor to be what we expect
517           Class.prototype.constructor = Class;
518 
519           // And make this class extendable
520           Class.extend = arguments.callee;
521           
522           return Class;
523         };
524     return Class;
525 });
526 
527 /**
528  * JavaScript base object that all finesse objects should inherit
529  * from because it encapsulates and provides the common functionality.
530  *
531  * Note: This javascript class requires the "inhert.js" to be included
532  * (which simplifies the class inheritance).
533  *
534  *
535  * @requires finesse.utilities.Logger
536  */
537 
538 /** The following comment is to prevent jslint errors about 
539  * using variables before they are defined.
540  */
541 /*global Class */
542 define('FinesseBase', ["../thirdparty/Class"], function (Class) {
543     var FinesseBase = Class.extend({
544         init: function () {
545         }
546     }); 
547     
548     window.finesse = window.finesse || {};
549     window.finesse.FinesseBase = FinesseBase;
550     
551     return FinesseBase;
552 });
553 
554 /**
555  * A collection of conversion utilities.
556  * Last modified 07-06-2011, Cisco Systems
557  *
558  */
559 /** @private */
560 define('utilities/../../thirdparty/util/converter',[], function () {
561     /**
562      * @class
563      * Contains a collection of utility functions.
564      * @private
565      */
566     Converter = (function () {
567         return {
568             /*  This work is licensed under Creative Commons GNU LGPL License.
569 
570                 License: http://creativecommons.org/licenses/LGPL/2.1/
571                Version: 0.9
572                 Author:  Stefan Goessner/2006
573                 Web:     http://goessner.net/ 
574 
575                 2013-09-16 Modified to remove use of XmlNode.innerHTML in the innerXml function by Cisco Systems, Inc.
576             */
577             xml2json: function (xml, tab) {
578                 var X = {
579                     toObj: function (xml) {
580                         var o = {};
581                         if (xml.nodeType === 1) {
582                             // element node ..
583                             if (xml.attributes.length)
584                             // element with attributes  ..
585                             for (var i = 0; i < xml.attributes.length; i++)
586                             o["@" + xml.attributes[i].nodeName] = (xml.attributes[i].nodeValue || "").toString();
587                             if (xml.firstChild) {
588                                 // element has child nodes ..
589                                 var textChild = 0,
590                                 cdataChild = 0,
591                                 hasElementChild = false;
592                                 for (var n = xml.firstChild; n; n = n.nextSibling) {
593                                     if (n.nodeType == 1) hasElementChild = true;
594                                     else if (n.nodeType == 3 && n.nodeValue.match(/[^ \f\n\r\t\v]/)) textChild++;
595                                     // non-whitespace text
596                                     else if (n.nodeType == 4) cdataChild++;
597                                     // cdata section node
598                                 }
599                                 if (hasElementChild) {
600                                     if (textChild < 2 && cdataChild < 2) {
601                                         // structured element with evtl. a single text or/and cdata node ..
602                                         X.removeWhite(xml);
603                                         for (var n = xml.firstChild; n; n = n.nextSibling) {
604                                             if (n.nodeType == 3)
605                                             // text node
606                                             o["#text"] = X.escape(n.nodeValue);
607                                             else if (n.nodeType == 4)
608                                             // cdata node
609                                             o["#cdata"] = X.escape(n.nodeValue);
610                                             else if (o[n.nodeName]) {
611                                                 // multiple occurence of element ..
612                                                 if (o[n.nodeName] instanceof Array)
613                                                 o[n.nodeName][o[n.nodeName].length] = X.toObj(n);
614                                                 else
615                                                 o[n.nodeName] = [o[n.nodeName], X.toObj(n)];
616                                             }
617                                             else
618                                             // first occurence of element..
619                                             o[n.nodeName] = X.toObj(n);
620                                         }
621                                     }
622                                     else {
623                                         // mixed content
624                                         if (!xml.attributes.length)
625                                         o = X.escape(X.innerXml(xml));
626                                         else
627                                         o["#text"] = X.escape(X.innerXml(xml));
628                                     }
629                                 }
630                                 else if (textChild) {
631                                     // pure text
632                                     if (!xml.attributes.length)
633                                     o = X.escape(X.innerXml(xml));
634                                     else
635                                     o["#text"] = X.escape(X.innerXml(xml));
636                                 }
637                                 else if (cdataChild) {
638                                     // cdata
639                                     if (cdataChild > 1)
640                                     o = X.escape(X.innerXml(xml));
641                                     else
642                                     for (var n = xml.firstChild; n; n = n.nextSibling)
643                                     o["#cdata"] = X.escape(n.nodeValue);
644                                 }
645                             }
646                             if (!xml.attributes.length && !xml.firstChild) o = null;
647                         }
648                         else if (xml.nodeType == 9) {
649                             // document.node
650                             o = X.toObj(xml.documentElement);
651                         }
652                         else
653                             throw ("unhandled node type: " + xml.nodeType);
654                         return o;
655                     },
656                     toJson: function(o, name, ind) {
657                         var json = name ? ("\"" + name + "\"") : "";
658                         if (o instanceof Array) {
659                             for (var i = 0, n = o.length; i < n; i++)
660                             o[i] = X.toJson(o[i], "", ind + "\t");
661                             json += (name ? ":[": "[") + (o.length > 1 ? ("\n" + ind + "\t" + o.join(",\n" + ind + "\t") + "\n" + ind) : o.join("")) + "]";
662                         }
663                         else if (o == null)
664                         json += (name && ":") + "null";
665                         else if (typeof(o) == "object") {
666                             var arr = [];
667                             for (var m in o)
668                             arr[arr.length] = X.toJson(o[m], m, ind + "\t");
669                             json += (name ? ":{": "{") + (arr.length > 1 ? ("\n" + ind + "\t" + arr.join(",\n" + ind + "\t") + "\n" + ind) : arr.join("")) + "}";
670                         }
671                         else if (typeof(o) == "string")
672                         json += (name && ":") + "\"" + o.toString() + "\"";
673                         else
674                         json += (name && ":") + o.toString();
675                         return json;
676                     },
677                     innerXml: function(node) {
678                         var s = "";
679                         var asXml = function(n) {
680                             var s = "";
681                             if (n.nodeType == 1) {
682                                 s += "<" + n.nodeName;
683                                 for (var i = 0; i < n.attributes.length; i++)
684                                 s += " " + n.attributes[i].nodeName + "=\"" + (n.attributes[i].nodeValue || "").toString() + "\"";
685                                 if (n.firstChild) {
686                                     s += ">";
687                                     for (var c = n.firstChild; c; c = c.nextSibling)
688                                     s += asXml(c);
689                                     s += "</" + n.nodeName + ">";
690                                 }
691                                 else
692                                 s += "/>";
693                             }
694                             else if (n.nodeType == 3)
695                             s += n.nodeValue;
696                             else if (n.nodeType == 4)
697                             s += "<![CDATA[" + n.nodeValue + "]]>";
698                             return s;
699                         };
700                         for (var c = node.firstChild; c; c = c.nextSibling)
701                         s += asXml(c);
702                         return s;
703                     },
704                     escape: function(txt) {
705                         return txt.replace(/[\\]/g, "\\\\")
706                         .replace(/[\"]/g, '\\"')
707                         .replace(/[\n]/g, '\\n')
708                         .replace(/[\r]/g, '\\r');
709                     },
710                     removeWhite: function(e) {
711                         e.normalize();
712                         for (var n = e.firstChild; n;) {
713                             if (n.nodeType == 3) {
714                                 // text node
715                                 if (!n.nodeValue.match(/[^ \f\n\r\t\v]/)) {
716                                     // pure whitespace text node
717                                     var nxt = n.nextSibling;
718                                     e.removeChild(n);
719                                     n = nxt;
720                                 }
721                                 else
722                                 n = n.nextSibling;
723                             }
724                             else if (n.nodeType == 1) {
725                                 // element node
726                                 X.removeWhite(n);
727                                 n = n.nextSibling;
728                             }
729                             else
730                             // any other node
731                             n = n.nextSibling;
732                         }
733                         return e;
734                     }
735                 };
736                 if (xml.nodeType == 9)
737                 // document node
738                 xml = xml.documentElement;
739                 var json = X.toJson(X.toObj(X.removeWhite(xml)), xml.nodeName, "\t");
740                 return "{\n" + tab + (tab ? json.replace(/\t/g, tab) : json.replace(/\t|\n/g, "")) + "\n}";
741             },
742             
743             /*  This work is licensed under Creative Commons GNU LGPL License.
744 
745                 License: http://creativecommons.org/licenses/LGPL/2.1/
746                Version: 0.9
747                 Author:  Stefan Goessner/2006
748                 Web:     http://goessner.net/ 
749             */
750             json2xml: function(o, tab) {
751                 var toXml = function(v, name, ind) {
752                     var xml = "";
753                     if (v instanceof Array) {
754                         for (var i = 0, n = v.length; i < n; i++)
755                         xml += ind + toXml(v[i], name, ind + "\t") + "\n";
756                     }
757                     else if (typeof(v) == "object") {
758                         var hasChild = false;
759                         xml += ind + "<" + name;
760                         for (var m in v) {
761                             if (m.charAt(0) == "@")
762                             xml += " " + m.substr(1) + "=\"" + v[m].toString() + "\"";
763                             else
764                             hasChild = true;
765                         }
766                         xml += hasChild ? ">": "/>";
767                         if (hasChild) {
768                             for (var m in v) {
769                                 if (m == "#text")
770                                 xml += v[m];
771                                 else if (m == "#cdata")
772                                 xml += "<![CDATA[" + v[m] + "]]>";
773                                 else if (m.charAt(0) != "@")
774                                 xml += toXml(v[m], m, ind + "\t");
775                             }
776                             xml += (xml.charAt(xml.length - 1) == "\n" ? ind: "") + "</" + name + ">";
777                         }
778                     }
779                     else {
780                         xml += ind + "<" + name + ">" + v.toString() + "</" + name + ">";
781                     }
782                     return xml;
783                 },
784                 xml = "";
785                 for (var m in o)
786                 xml += toXml(o[m], m, "");
787                 return tab ? xml.replace(/\t/g, tab) : xml.replace(/\t|\n/g, "");
788             }
789         };
790     })();
791 
792     window.finesse = window.finesse || {};
793     window.finesse.Converter = Converter;
794 
795     return Converter;
796 });
797 
798 /**
799  * SaxParser.js: provides a simple SAX parser
800  *
801  * NONVALIDATING - this will not validate whether you have valid XML or not. It will simply report what it finds.
802  * Only supports elements, attributes, and text. No comments, cdata, processing instructions, etc.
803  */
804 
805 /**
806  * @requires
807  * @ignore
808  */
809 // Add SaxParser to the finesse.utilities namespace
810 define('utilities/SaxParser',[], function () {
811 	var SaxParser = {
812 		parse: function(xml, callback) {
813 			// Event callbacks
814             /** @private */
815 			var triggerEvent = function (type, data) {
816 					callback.call(null, type, data);
817 				},
818                 /** @private */
819 				triggerStartElement = function (name) {
820 					triggerEvent("StartElement", name);
821 				},
822                 /** @private */
823 				triggerEndElement = function (name) {
824 					triggerEvent("EndElement", name);
825 				},
826                 /** @private */
827 				triggerAttribute = function (name, value) {
828 					triggerEvent("Attribute", { "name": name, "value": value });
829 				},
830                 /** @private */
831 				triggerText = function (text) {
832 					triggerEvent("Text", text);
833 				},
834 
835 				// Parsing
836 				cursor = 0,
837 				xmlLength = xml.length,
838 				whitespaceRegex = /^[ \t\r\n]*$/,
839 				/** @private */
840 				isWhitespace = function (text) {
841 					return whitespaceRegex.test(text);
842 				},
843                 /** @private */
844 				moveToNonWhitespace = function () {
845 					while (isWhitespace(xml.charAt(cursor))) {
846 						cursor += 1;
847 					}
848 				},
849                 /** @private */
850 				parseAttribute = function () {
851 					var nameBuffer = [],
852 						valueBuffer = [],
853 						valueIsQuoted = false,
854 						cursorChar = "";
855 
856 					nameBuffer.push(xml.charAt(cursor));
857 
858 					// Get the name
859 					cursor += 1;
860 					while (cursor < xmlLength) {
861 						cursorChar = xml.charAt(cursor);
862 						if (isWhitespace(cursorChar) || cursorChar === "=") {
863 							// Move on to gathering value
864 							break;
865 						}
866 						else {
867 							nameBuffer.push(cursorChar);
868 						}
869 						cursor += 1;
870 					}
871 
872 					// Skip the equals sign and any whitespace
873 					moveToNonWhitespace();
874 					if (cursorChar === "=") {
875 						cursor += 1;
876 					} else {
877 						throw new Error("Did not find = following attribute name at " + cursor);
878 					}
879 					moveToNonWhitespace();
880 
881 					// Get the value
882 					valueIsQuoted = cursor !== xmlLength - 1 ? xml.charAt(cursor) === "\"": false;
883 					if (valueIsQuoted) {
884 						cursor += 1;
885 						while (cursor < xmlLength) {
886 							cursorChar = xml.charAt(cursor);
887 							if (cursorChar === "\"") {
888 								// Found the closing quote, so end value
889 								triggerAttribute(nameBuffer.join(""), valueBuffer.join(""));
890 								break;
891 							}
892 							else {
893 								valueBuffer.push(cursorChar);
894 							}
895 							cursor += 1;
896 						}
897 					}
898 					else {
899 						throw new Error("Found unquoted attribute value at " + cursor);
900 					}
901 				},
902                 /** @private */
903 				parseEndElement = function () {
904 					var elementNameBuffer = [],
905 						cursorChar = "";
906 					cursor += 2;
907 					while (cursor < xmlLength) {
908 						cursorChar = xml.charAt(cursor);
909 						if (cursorChar === ">") {
910 							triggerEndElement(elementNameBuffer.join(""));
911 							break;
912 						}
913 						else {
914 							elementNameBuffer.push(cursorChar);
915 						}
916 						cursor += 1;
917 					}
918 				},
919                 /** @private */
920 				parseReference = function() {
921 					var type,
922 						TYPE_DEC_CHAR_REF = 1,
923 						TYPE_HEX_CHAR_REF = 2,
924 						TYPE_ENTITY_REF = 3,
925 						buffer = "";
926 					cursor += 1;
927 					// Determine the type of reference.
928 					if (xml.charAt(cursor) === "#") {
929 						cursor += 1;
930 						if (xml.charAt(cursor) === "x") {
931 							type = TYPE_HEX_CHAR_REF;
932 							cursor += 1;
933 						} else {
934 							type = TYPE_DEC_CHAR_REF;
935 						}
936 					} else {
937 						type = TYPE_ENTITY_REF;
938 					}
939 					// Read the reference into a buffer.
940 					while (xml.charAt(cursor) !== ";") {
941 						buffer += xml.charAt(cursor);
942 						cursor += 1;
943 						if (cursor >= xmlLength) {
944 							throw new Error("Unterminated XML reference: " + buffer);
945 						}
946 					}
947 					// Convert the reference to the appropriate character.
948 					switch (type) {
949 						case TYPE_DEC_CHAR_REF:
950 							return String.fromCharCode(parseInt(buffer, 10));
951 						case TYPE_HEX_CHAR_REF:
952 							return String.fromCharCode(parseInt(buffer, 16));
953 						case TYPE_ENTITY_REF:
954 							switch (buffer) {
955 								case "amp":
956 									return "&";
957 								case "lt":
958 									return "<";
959 								case "gt":
960 									return ">";
961 								case "apos":
962 									return "'";
963 								case "quot":
964 									return "\"";
965 								default:
966 									throw new Error("Invalid XML entity reference: " + buffer);
967 							}
968 							// break; (currently unreachable)
969 					}
970 				},
971                 /** @private */
972 				parseElement = function () {
973 					var elementNameBuffer = [],
974 						textBuffer = [],
975 						cursorChar = "",
976 						whitespace = false;
977 
978 					// Get element name
979 					cursor += 1;
980 					while (cursor < xmlLength) {
981 						cursorChar = xml.charAt(cursor);
982 						whitespace = isWhitespace(cursorChar);
983 						if (!whitespace && cursorChar !== "/" && cursorChar !== ">") {
984 							elementNameBuffer.push(cursorChar);
985 						}
986 						else {
987 							elementNameBuffer = elementNameBuffer.join("");
988 							triggerStartElement(elementNameBuffer);
989 							break;
990 						}
991 						cursor += 1;
992 					}
993 
994 					// Get attributes
995 					if (whitespace) {
996 						while (cursor < xmlLength) {
997 							moveToNonWhitespace();
998 							cursorChar = xml.charAt(cursor);
999 							if (cursorChar !== "/" && cursorChar !== ">") {
1000 								// Start of attribute
1001 								parseAttribute();
1002 							}
1003 							cursorChar = xml.charAt(cursor);
1004 							if (cursorChar === "/" || cursorChar === ">") {
1005 								break;
1006 							}
1007 							else {
1008 								cursor += 1;
1009 							}
1010 						}
1011 					}
1012 
1013 					// End tag if "/>" was found,
1014 					// otherwise we're at the end of the start tag and have to parse into it
1015 					if (cursorChar === "/") {
1016 						if (cursor !== xmlLength - 1 && xml.charAt(cursor + 1) === ">") {
1017 							cursor += 1;
1018 							triggerEndElement(elementNameBuffer);
1019 						}
1020 					}
1021 					else {
1022 						// cursor is on ">", so parse into element content. Assume text until we find a "<",
1023 						// which could be a child element or the current element's end tag. We do not support
1024 						// mixed content of text and elements as siblings unless the text is only whitespace.
1025 						// Text cannot contain <, >, ", or &. They should be <, >, ", & respectively.
1026 						cursor += 1;
1027 						while (cursor < xmlLength) {
1028 							cursorChar = xml.charAt(cursor);
1029 							if (cursorChar === "<") {
1030 								// Determine if end tag or element
1031 								if (cursor !== xmlLength - 1 && xml.charAt(cursor + 1) === "/") {
1032 									// At end tag
1033 									textBuffer = textBuffer.join("");
1034 									if (!isWhitespace(textBuffer)) {
1035 										triggerText(textBuffer);
1036 									}
1037 									parseEndElement();
1038 									break;
1039 								}
1040 								else {
1041 									// At start tag
1042 									textBuffer = textBuffer.join("");
1043 									if (!isWhitespace(textBuffer)) {
1044 										triggerText(textBuffer);
1045 									}
1046 									parseElement();
1047 									textBuffer = [];
1048 								}
1049 							} else if (cursorChar === "&") {
1050 								textBuffer.push(parseReference());
1051 							}
1052 							else {
1053 								textBuffer.push(cursorChar);
1054 							}
1055 							cursor += 1;
1056 						}
1057 					}
1058 				},
1059                 /** @private */
1060 				skipXmlDeclaration = function() {
1061 					if (xml.substr(0, 5) === "<?xml" && isWhitespace(xml.charAt(5))) {
1062 						cursor = xml.indexOf(">") + 1;
1063 					}
1064 					moveToNonWhitespace();
1065 				};
1066 
1067 			// Launch.
1068 			skipXmlDeclaration();
1069 			parseElement();
1070 		}
1071 	};
1072 
1073     window.finesse = window.finesse || {};
1074     window.finesse.utilities = window.finesse.utilities || {};
1075     window.finesse.utilities.SaxParser = SaxParser;
1076 
1077 	return SaxParser;
1078 });
1079 
1080 /**
1081 * Date.parse with progressive enhancement for ISO 8601 <https://github.com/csnover/js-iso8601>
1082 * ?? 2011 Colin Snover <http://zetafleet.com>
1083 * Released under MIT license.
1084 */
1085 define('iso8601',[], function () {
1086     (function (Date, undefined) {
1087         var origParse = Date.parse, numericKeys = [ 1, 4, 5, 6, 7, 10, 11 ];
1088         /** @private **/
1089         Date.parse = function (date) {
1090             var timestamp, struct, minutesOffset = 0;
1091 
1092             // ES5 ??15.9.4.2 states that the string should attempt to be parsed as a Date Time String Format string
1093             // before falling back to any implementation-specific date parsing, so that???s what we do, even if native
1094             // implementations could be faster
1095             // 1 YYYY 2 MM 3 DD 4 HH 5 mm 6 ss 7 msec 8 Z 9 ?? 10 tzHH 11 tzmm
1096             if ((struct = /^(\d{4}|[+\-]\d{6})(?:-(\d{2})(?:-(\d{2}))?)?(?:T(\d{2}):(\d{2})(?::(\d{2})(?:\.(\d{3}))?)?(?:(Z)|([+\-])(\d{2})(?::(\d{2}))?)?)?$/.exec(date))) {
1097                 // avoid NaN timestamps caused by ???undefined??? values being passed to Date.UTC
1098                 for (var i = 0, k; (k = numericKeys[i]); ++i) {
1099                     struct[k] = +struct[k] || 0;
1100                 }
1101 
1102                 // allow undefined days and months
1103                 struct[2] = (+struct[2] || 1) - 1;
1104                 struct[3] = +struct[3] || 1;
1105 
1106                 if (struct[8] !== 'Z' && struct[9] !== undefined) {
1107                     minutesOffset = struct[10] * 60 + struct[11];
1108 
1109                     if (struct[9] === '+') {
1110                         minutesOffset = 0 - minutesOffset;
1111                     }
1112                 }
1113 
1114                 timestamp = Date.UTC(struct[1], struct[2], struct[3], struct[4], struct[5] + minutesOffset, struct[6], struct[7]);
1115             }
1116             else {
1117                 timestamp = origParse ? origParse(date) : NaN;
1118             }
1119 
1120             return timestamp;
1121         };
1122     }(Date));
1123 });
1124 
1125 /*!
1126 Math.uuid.js (v1.4)
1127 http://www.broofa.com
1128 mailto:robert@broofa.com
1129 
1130 Copyright (c) 2010 Robert Kieffer
1131 Dual licensed under the MIT and GPL licenses.
1132 */
1133 
1134 /*
1135  * Generate a random uuid.
1136  *
1137  * USAGE: Math.uuid(length, radix)
1138  *   length - the desired number of characters
1139  *   radix  - the number of allowable values for each character.
1140  *
1141  * EXAMPLES:
1142  *   // No arguments  - returns RFC4122, version 4 ID
1143  *   >>> Math.uuid()
1144  *   "92329D39-6F5C-4520-ABFC-AAB64544E172"
1145  *
1146  *   // One argument - returns ID of the specified length
1147  *   >>> Math.uuid(15)     // 15 character ID (default base=62)
1148  *   "VcydxgltxrVZSTV"
1149  *
1150  *   // Two arguments - returns ID of the specified length, and radix. (Radix must be <= 62)
1151  *   >>> Math.uuid(8, 2)  // 8 character ID (base=2)
1152  *   "01001010"
1153  *   >>> Math.uuid(8, 10) // 8 character ID (base=10)
1154  *   "47473046"
1155  *   >>> Math.uuid(8, 16) // 8 character ID (base=16)
1156  *   "098F4D35"
1157  */
1158 define('Math.uuid',[], function () {
1159     (function() {
1160         // Private array of chars to use
1161         var CHARS = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('');
1162 
1163         /** @private **/
1164         Math.uuid = function (len, radix) {
1165           var chars = CHARS, uuid = [], i;
1166           radix = radix || chars.length;
1167 
1168           if (len) {
1169             // Compact form
1170             for (i = 0; i < len; i++) uuid[i] = chars[0 | Math.random()*radix];
1171           } else {
1172             // rfc4122, version 4 form
1173             var r;
1174 
1175             // rfc4122 requires these characters
1176             uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-';
1177             uuid[14] = '4';
1178 
1179             // Fill in random data.  At i==19 set the high bits of clock sequence as
1180             // per rfc4122, sec. 4.1.5
1181             for (i = 0; i < 36; i++) {
1182               if (!uuid[i]) {
1183                 r = 0 | Math.random()*16;
1184                 uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r];
1185               }
1186             }
1187           }
1188 
1189           return uuid.join('');
1190         };
1191 
1192         // A more performant, but slightly bulkier, RFC4122v4 solution.  We boost performance
1193         // by minimizing calls to random()
1194         /** @private **/
1195         Math.uuidFast = function() {
1196           var chars = CHARS, uuid = new Array(36), rnd=0, r;
1197           for (var i = 0; i < 36; i++) {
1198             if (i==8 || i==13 ||  i==18 || i==23) {
1199               uuid[i] = '-';
1200             } else if (i==14) {
1201               uuid[i] = '4';
1202             } else {
1203               if (rnd <= 0x02) rnd = 0x2000000 + (Math.random()*0x1000000)|0;
1204               r = rnd & 0xf;
1205               rnd = rnd >> 4;
1206               uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r];
1207             }
1208           }
1209           return uuid.join('');
1210         };
1211 
1212         // A more compact, but less performant, RFC4122v4 solution:
1213         /** @private **/
1214         Math.uuidCompact = function() {
1215           return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
1216             var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
1217             return v.toString(16);
1218           });
1219         };
1220       })();
1221 });
1222 
1223 /**
1224  * The following comment prevents JSLint errors concerning undefined global variables.
1225  * It tells JSLint that these identifiers are defined elsewhere.
1226  */
1227 /*jslint bitwise:true, browser:true, nomen:true, regexp:true, sloppy:true, white:true, plusplus: true, unparam: true, forin: true */
1228 
1229 /** The following comment is to prevent jslint errors about 
1230  * using variables before they are defined.
1231  */
1232 /*global $, _prefs,_uiMsg,ciscowidgets,dojo,finesse,gadgets,hostUrl, Handlebars */
1233 
1234 /**
1235  *  A collection of utility functions.
1236  *
1237  * @requires finesse.Converter
1238  */
1239 define('utilities/Utilities',[
1240     "../../thirdparty/util/converter",
1241     "utilities/SaxParser",
1242     "iso8601",
1243     "Math.uuid"
1244 ],
1245 function (Converter, SaxParser) {
1246     var Utilities = /** @lends finesse.utilities.Utilities */ {
1247 
1248         /**
1249          * @class
1250          * 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                 }
1427             } // Otherwise, don't return anything (undefined)
1428         },
1429 
1430         /**
1431          * @private
1432          * Tries to get extract the AWS error data from a
1433          * finesse.clientservices.ClientServices parsed error response object.
1434          * @param {Object} rsp
1435          *     The handler to validate
1436          * @returns {String}
1437          *     The error data, HTTP status code, or undefined
1438          */
1439         getErrData: function (rsp) {
1440             try { // Best effort to get the error data
1441                 return rsp.object.ApiErrors.ApiError.ErrorData;
1442             } catch (e) { // Second best effort to get the HTTP Status code
1443                 if (rsp && rsp.status) {
1444                     return "HTTP " + rsp.status;
1445                 }
1446             } // Otherwise, don't return anything (undefined)
1447         },
1448         
1449         /**
1450          * @private
1451          * Tries to get extract the AWS overrideable boolean from a
1452          * finesse.clientservices.ClientServices parsed error response object.
1453          * @param {Object} rsp
1454          *     The handler to validate
1455          * @returns {String}
1456          *     The overrideable boolean, HTTP status code, or undefined
1457          */
1458         getErrOverrideable: function (rsp) {
1459             try { // Best effort to get the override boolean
1460                 return rsp.object.ApiErrors.ApiError.Overrideable;
1461             } catch (e) { // Second best effort to get the HTTP Status code
1462                 if (rsp && rsp.status) {
1463                     return "HTTP " + rsp.status;
1464                 }
1465             } // Otherwise, don't return anything (undefined)
1466         },
1467 
1468         /**
1469          * Trims leading and trailing whitespace from a string.
1470          * @param {String} str
1471          *     The string to trim
1472          * @returns {String}
1473          *     The trimmed string
1474          */
1475         trim: function (str) {
1476             return str.replace(/^\s*/, "").replace(/\s*$/, "");
1477         },
1478 
1479         /**
1480          * Utility method for getting the current time in milliseconds.
1481          * @returns {String}
1482          *     The current time in milliseconds
1483          */
1484         currentTimeMillis : function () {
1485             return (new Date()).getTime();
1486         },
1487 
1488        /**
1489         * Gets the current drift (between client and server)
1490         *
1491         * @returns {integer} which is the current drift (last calculated; 0 if we have not calculated yet)
1492         */
1493        getCurrentDrift : function () {
1494             var drift;
1495             
1496             //Get the current client drift from localStorage
1497             drift = window.sessionStorage.getItem("clientTimestampDrift");
1498             if (drift) {
1499                  drift = parseInt(drift, 10);
1500                  if (isNaN(drift)) {
1501                       drift = 0; 
1502                  }
1503             }
1504           return drift;
1505         },
1506 
1507        /**
1508         * Converts the specified clientTime to server time by adjusting by the current drift.
1509         *
1510         * @param clientTime is the time in milliseconds
1511         * @returns {int} serverTime in milliseconds
1512         */
1513         convertToServerTimeMillis : function(clientTime) {
1514             var drift = this.getCurrentDrift();
1515             return (clientTime + drift);
1516         },
1517 
1518         /**
1519          * Utility method for getting the current time,
1520          * adjusted by the calculated "drift" to closely
1521          * approximate the server time.  This is used
1522          * when calculating durations based on a server
1523          * timestamp, which otherwise can produce unexpected
1524          * results if the times on client and server are
1525          * off.
1526          * 
1527          * @returns {String}
1528          *     The current server time in milliseconds
1529          */
1530         currentServerTimeMillis : function () {
1531             var drift = this.getCurrentDrift();
1532             return (new Date()).getTime() + drift;
1533         },
1534 
1535         /**
1536          * Given a specified timeInMs, this method will builds a string which displays minutes and seconds. 
1537          *
1538          * @param timeInMs is the time in milliseconds
1539          * @returns {String} which corresponds to minutes and seconds (e.g. 11:23)
1540          */
1541         buildTimeString : function (timeInMs) {
1542            var min, sec, timeStr = "00:00";
1543           
1544            if (timeInMs && timeInMs !== "-1") {
1545               // calculate minutes, and seconds
1546               min = this.pad(Math.floor(timeInMs / 60000));
1547               sec = this.pad(Math.floor((timeInMs % 60000) / 1000));
1548               
1549               // construct MM:SS time string
1550               timeStr =  min + ":" + sec;
1551            }
1552            return timeStr;  
1553         },
1554         
1555         /**
1556          * Given a specified timeInMs, this method will builds a string which displays minutes and seconds (and optionally hours)
1557          *
1558          * @param timeInMs is the time in milliseconds
1559          * @returns {String} which corresponds to hours, minutes and seconds (e.g. 01:11:23 or 11:23)
1560          */
1561         buildTimeStringWithOptionalHours: function (timeInMs) {
1562            var hour, min, sec, timeStr = "00:00", optionalHour = "", timeInSecs;
1563           
1564            if (timeInMs && timeInMs !== "-1") {
1565               timeInSecs = timeInMs / 1000;
1566               
1567               // calculate {hours}, minutes, and seconds
1568               hour = this.pad(Math.floor(timeInSecs / 3600));
1569               min = this.pad(Math.floor((timeInSecs % 3600) / 60));
1570               sec = this.pad(Math.floor((timeInSecs % 3600) % 60));   
1571               
1572               //Optionally add the hour if we have hours
1573               if (hour > 0) {
1574                 optionalHour = hour + ":";
1575               }
1576               
1577               // construct MM:SS time string (or optionally HH:MM:SS)
1578               timeStr = optionalHour + min + ":" + sec; 
1579            }
1580            return timeStr;
1581         },
1582         
1583         
1584         /**
1585          * Builds a string which specifies the amount of time user has been in this state (e.g. 11:23).
1586          *
1587          * @param adjustedServerTimeInMs is integer argument which specifies the expected server time (accounting for clientdrift)
1588          * @param stateStartTimeInMs is integer argument which specifies time call entered current state
1589          * @returns {String} which is the elapsed time (MINUTES:SECONDS) 
1590          *
1591          */
1592         buildElapsedTimeString : function (adjustedServerTimeInMs, stateStartTimeInMs) {
1593            var result, delta;
1594            
1595            result = "--:--";
1596            if (stateStartTimeInMs !== 0) {
1597              delta = adjustedServerTimeInMs - stateStartTimeInMs;
1598              
1599              if (delta > 0) {
1600                result = this.buildTimeString(delta);
1601              }
1602           }
1603           return result;
1604        },
1605        
1606         /**
1607          * Builds a string which specifies the amount of time user has been in this state with optional hours (e.g. 01:11:23 or 11:23).
1608          *
1609          * @param adjustedServerTimeInMs is integer argument which specifies the expected server time (accounting for clientdrift)
1610          * @param startTimeInMs is integer argument which specifies the start time
1611          * @returns {String} which is the elapsed time (MINUTES:SECONDS) or (HOURS:MINUTES:SECONDS)
1612          *
1613          */
1614         buildElapsedTimeStringWithOptionalHours : function (adjustedServerTimeInMs, stateStartTimeInMs) {
1615            var result, delta;
1616            
1617            result = "--:--";
1618            if (stateStartTimeInMs !== 0) {
1619              delta = adjustedServerTimeInMs - stateStartTimeInMs;
1620              
1621              if (delta > 0) {
1622                result = this.buildTimeStringWithOptionalHours(delta);
1623              }
1624           }
1625           return result;
1626        },
1627        
1628        
1629        /**
1630         * Builds a string which displays the total call time in minutes and seconds.
1631         *
1632         * @param adjustedServerTimeInMs is integer argument which specifies the expected server time (accounting for clientdrift)
1633         * @param callStartTimeInMs is integer argument which specifies time the call started
1634         * @returns {String} which is the elapsed time [MINUTES:SECONDS]
1635         */
1636        buildTotalTimeString : function (adjustedServerTimeInMs, callStartTimeInMs) {
1637           return this.buildElapsedTimeString(adjustedServerTimeInMs, callStartTimeInMs);
1638        },
1639        
1640        /**
1641         * Builds a string which displays the hold time in minutes and seconds.
1642         *
1643         * @param adjustedServerTimeInMs is integer argument which specifies the expected server time (accounting for clientdrift)
1644         * @param holdStartTimeInMs is integer argument which specifies time the hold started
1645         * @returns {String} which is the elapsed time [MINUTES:SECONDS] 
1646         */
1647        buildHoldTimeString : function (adjustedServerTimeInMs, holdStartTimeInMs) {
1648           return this.buildElapsedTimeString(adjustedServerTimeInMs, holdStartTimeInMs);
1649       },
1650       
1651       /**
1652        * Builds a string which displays the elapsed time the call has been in wrap up.
1653        *
1654        * @param adjustedServerTimeInMs is integer argument which specifies the expected server time (accounting for clientdrift)
1655        * @param wrapupStartTimeInMs is integer argument which specifies time call entered wrapup state
1656        * @returns {String} which is the elapsed wrapup time
1657        *
1658        */
1659       buildWrapupTimeString : function (adjustedServerTimeInMs, wrapupStartTimeInMs) {
1660          return this.buildElapsedTimeString(adjustedServerTimeInMs, wrapupStartTimeInMs);
1661       },
1662       
1663       /**
1664        * Extracts a time from the timeStr.  Note: The timeStr could be empty.  In this case, the time returned will be 0.
1665        * @param timeStr is a time string in ISO8601 format (note: could be empty)
1666        * @returns {long} is the time 
1667        */
1668       extractTime : function (timeStr) {
1669          var result = 0, theDate;
1670          if (timeStr === "") {
1671            result = 0;
1672          } else if (timeStr === null) {
1673            result = 0;
1674          } else {
1675            theDate = this.parseDateStringISO8601(timeStr);
1676            result = theDate.getTime();
1677          }
1678          return result;
1679       },
1680       
1681       /**
1682        * @private
1683        * Generates an RFC1422v4-compliant UUID using pesudorandom numbers.
1684        * @returns {String}
1685        *     An RFC1422v4-compliant UUID using pesudorandom numbers.
1686        **/        
1687         generateUUID: function () {
1688             return Math.uuidCompact();
1689         },
1690 
1691         /** @private */
1692         xml2json: finesse.Converter.xml2json,
1693         
1694         
1695         /**
1696          * @private
1697          * Utility method to get the JSON parser either from gadgets.json
1698          * or from window.JSON (which will be initialized by CUIC if 
1699          * browser doesn't support
1700          */
1701         getJSONParser: function() {
1702             var _container = window.gadgets || {},
1703                 parser = _container.json || window.JSON;
1704             return parser;
1705         },
1706 
1707        /**
1708         * @private
1709         * Utility method to convert a javascript object to XML.
1710         * @param {Object} object
1711         *   The object to convert to XML.
1712         * @param {Boolean} escapeFlag
1713         *   If escapeFlag evaluates to true:
1714         *       - XML escaping is done on the element values.
1715         *       - Attributes, #cdata, and #text is not supported.
1716         *       - The XML is unformatted (no whitespace between elements).
1717         *   If escapeFlag evaluates to false:
1718         *       - Element values are written 'as is' (no escaping).
1719         *       - Attributes, #cdata, and #text is supported.
1720         *       - The XML is formatted.
1721         * @returns The XML string.
1722         */
1723         json2xml: function (object, escapeFlag) {
1724             var xml;
1725             if (escapeFlag) {
1726                 xml = this._json2xmlWithEscape(object);
1727             }
1728             else {
1729                 xml = finesse.Converter.json2xml(object, '\t');
1730             }
1731             return xml;
1732         },
1733 
1734         /**
1735          * @private
1736          * Utility method to convert XML string into javascript object.
1737          */
1738         xml2JsObj : function (event) {
1739             var parser = this.getJSONParser();
1740             return parser.parse(finesse.Converter.xml2json(jQuery.parseXML(event), ""));
1741         },
1742 
1743        /**
1744         * @private
1745         * Utility method to convert an XML string to a javascript object.
1746         * @desc This function calls to the SAX parser and responds to callbacks
1747         *     received from the parser. Entity translation is not handled here.
1748         * @param {String} xml
1749         *   The XML to parse.
1750         * @returns The javascript object.
1751         */
1752         xml2js: function (xml) {
1753             var STATES = {
1754                     INVALID: 0,
1755                     NEW_NODE: 1,
1756                     ATTRIBUTE_NODE: 2,
1757                     TEXT_NODE: 3,
1758                     END_NODE: 4
1759                 },
1760                 state = STATES.INVALID,
1761                 rootObj = {},
1762                 newObj,
1763                 objStack = [rootObj],
1764                 nodeName = "",
1765 
1766                 /**
1767                 * @private
1768                 * Adds a property to the current top JSO.
1769                 * @desc This is also where we make considerations for arrays.
1770                 * @param {String} name
1771                 *   The name of the property to add.
1772                 * @param (String) value
1773                 *     The value of the property to add.
1774                 */
1775                 addProperty = function (name, value) {
1776                     var current = objStack[objStack.length - 1];
1777                     if(current.hasOwnProperty(name) && current[name] instanceof Array){
1778                         current[name].push(value);
1779                     }else if(current.hasOwnProperty(name)){
1780                         current[name] = [current[name], value];
1781                     }else{
1782                         current[name] = value;
1783                     }
1784                 },
1785 
1786                 /**
1787                 * @private
1788                 * The callback passed to the SAX parser which processes events from
1789                 * the SAX parser in order to construct the resulting JSO.
1790                 * @param (String) type
1791                 *     The type of event received.
1792                 * @param (String) data
1793                 *     The data received from the SAX parser. The contents of this
1794                 *     parameter vary based on the type of event.
1795                 */
1796                 xmlFound = function (type, data) {
1797                     switch (type) {
1798                     case "StartElement":
1799                         // Because different node types have different expectations
1800                         // of parenting, we don't push another JSO until we know
1801                         // what content we're getting
1802 
1803                         // If we're already in the new node state, we're running
1804                         // into a child node. There won't be any text here, so
1805                         // create another JSO
1806                         if(state === STATES.NEW_NODE){
1807                             newObj = {};
1808                             addProperty(nodeName, newObj);
1809                             objStack.push(newObj);
1810                         }
1811                         state = STATES.NEW_NODE;
1812                         nodeName = data;
1813                         break;
1814                     case "EndElement":
1815                         // If we're in the new node state, we've found no content
1816                         // set the tag property to null
1817                         if(state === STATES.NEW_NODE){
1818                             addProperty(nodeName, null);
1819                         }else if(state === STATES.END_NODE){
1820                             objStack.pop();
1821                         }
1822                         state = STATES.END_NODE;
1823                         break;
1824                     case "Attribute":
1825                         // If were in the new node state, no JSO has yet been created
1826                         // for this node, create one
1827                         if(state === STATES.NEW_NODE){
1828                             newObj = {};
1829                             addProperty(nodeName, newObj);
1830                             objStack.push(newObj);
1831                         }
1832                         // Attributes are differentiated from child elements by a
1833                         // preceding "@" in the property name
1834                         addProperty("@" + data.name, data.value);
1835                         state = STATES.ATTRIBUTE_NODE;
1836                         break;
1837                     case "Text":
1838                         // In order to maintain backwards compatibility, when no
1839                         // attributes are assigned to a tag, its text contents are
1840                         // assigned directly to the tag property instead of a JSO.
1841 
1842                         // If we're in the attribute node state, then the JSO for
1843                         // this tag was already created when the attribute was
1844                         // assigned, differentiate this property from a child
1845                         // element by naming it "#text"
1846                         if(state === STATES.ATTRIBUTE_NODE){
1847                             addProperty("#text", data);
1848                         }else{
1849                             addProperty(nodeName, data);
1850                         }
1851                         state = STATES.TEXT_NODE;
1852                         break;
1853                     }
1854                 };
1855             SaxParser.parse(xml, xmlFound);
1856             return rootObj;
1857         },
1858 
1859        /**
1860         * @private
1861         * Traverses a plain-old-javascript-object recursively and outputs its XML representation.
1862         * @param {Object} obj
1863         *     The javascript object to be converted into XML.
1864         * @returns {String} The XML representation of the object.
1865         */
1866         js2xml: function (obj) {
1867             var xml = "", i, elem;
1868 
1869             if (obj !== null) {
1870                 if (obj.constructor === Object) {
1871                     for (elem in obj) {
1872                         if (obj[elem] === null || typeof(obj[elem]) === 'undefined') {
1873                             xml += '<' + elem + '/>';
1874                         } else if (obj[elem].constructor === Array) {
1875                             for (i = 0; i < obj[elem].length; i++) {
1876                                 xml += '<' + elem + '>' + this.js2xml(obj[elem][i]) + '</' + elem + '>';
1877                             }
1878                         } else if (elem[0] !== '@') {
1879                             if (this.js2xmlObjIsEmpty(obj[elem])) {
1880                                 xml += '<' + elem + this.js2xmlAtt(obj[elem]) + '/>';
1881                             } else if (elem === "#text") {
1882                                 xml += obj[elem];
1883                             } else {
1884                                 xml += '<' + elem + this.js2xmlAtt(obj[elem]) + '>' + this.js2xml(obj[elem]) + '</' + elem + '>';
1885                             }
1886                         }
1887                     }
1888                 } else {
1889                     xml = obj;
1890                 }
1891             }
1892 
1893             return xml;
1894         },
1895 
1896        /**
1897         * @private
1898         * Utility method called exclusively by js2xml() to find xml attributes.
1899         * @desc Traverses children one layer deep of a javascript object to "look ahead"
1900         * for properties flagged as such (with '@').
1901         * @param {Object} obj
1902         *   The obj to traverse.
1903         * @returns {String} Any attributes formatted for xml, if any.
1904         */
1905         js2xmlAtt: function (obj) {
1906             var elem;
1907 
1908             if (obj !== null) {
1909                 if (obj.constructor === Object) {
1910                     for (elem in obj) {
1911                         if (obj[elem] !== null && typeof(obj[elem]) !== "undefined" && obj[elem].constructor !== Array) {
1912                             if (elem[0] === '@'){
1913                                 return ' ' + elem.substring(1) + '="' + obj[elem] + '"';
1914                             }
1915                         }
1916                     }
1917                 }
1918             }
1919 
1920             return '';
1921         },
1922 
1923        /**
1924         * @private
1925         * Utility method called exclusively by js2xml() to determine if
1926         * a node has any children, with special logic for ignoring attributes.
1927         * @desc Attempts to traverse the elements in the object while ignoring attributes.
1928         * @param {Object} obj
1929         *   The obj to traverse.
1930         * @returns {Boolean} whether or not the JS object is "empty"
1931         */
1932         js2xmlObjIsEmpty: function (obj) {
1933             var elem;
1934 
1935             if (obj !== null) {
1936                 if (obj.constructor === Object) {
1937                     for (elem in obj) {
1938                         if (obj[elem] !== null) {
1939                             if (obj[elem].constructor === Array){
1940                                 return false;
1941                             }
1942 
1943                             if (elem[0] !== '@'){
1944                                 return false;
1945                             }
1946                         } else {
1947                             return false;
1948                         }
1949                     }
1950                 } else {
1951                     return false;
1952                 }
1953             }
1954 
1955             return true;
1956         },
1957 
1958         /**
1959          * Encodes the given string into base64.
1960          *<br>
1961          * <b>NOTE:</b> {input} is assumed to be UTF-8; only the first
1962          * 8 bits of each input element are significant.
1963          *
1964          * @param {String} input
1965          *     The string to convert to base64.
1966          * @returns {String}
1967          *     The converted string.
1968          */
1969         b64Encode: function (input) {
1970             var output = "", idx, data,
1971                 table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
1972 
1973             for (idx = 0; idx < input.length; idx += 3) {
1974                 data =  input.charCodeAt(idx) << 16 |
1975                             input.charCodeAt(idx + 1) << 8 |
1976                             input.charCodeAt(idx + 2);
1977 
1978                 //assume the first 12 bits are valid
1979                 output +=   table.charAt((data >>> 18) & 0x003f) +
1980                             table.charAt((data >>> 12) & 0x003f);
1981                 output +=   ((idx + 1) < input.length) ?
1982                             table.charAt((data >>> 6) & 0x003f) :
1983                             "=";
1984                 output +=   ((idx + 2) < input.length) ?
1985                             table.charAt(data & 0x003f) :
1986                             "=";
1987             }
1988 
1989             return output;
1990         },
1991 
1992         /**
1993          * Decodes the given base64 string.
1994          * <br>
1995          * <b>NOTE:</b> output is assumed to be UTF-8; only the first
1996          * 8 bits of each output element are significant.
1997          *
1998          * @param {String} input
1999          *     The base64 encoded string
2000          * @returns {String}
2001          *     Decoded string
2002          */
2003         b64Decode: function (input) {
2004             var output = "", idx, h, data,
2005                 table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
2006 
2007             for (idx = 0; idx < input.length; idx += 4) {
2008                 h = [
2009                     table.indexOf(input.charAt(idx)),
2010                     table.indexOf(input.charAt(idx + 1)),
2011                     table.indexOf(input.charAt(idx + 2)),
2012                     table.indexOf(input.charAt(idx + 3))
2013                 ];
2014 
2015                 data = (h[0] << 18) | (h[1] << 12) | (h[2] << 6) | h[3];
2016                 if (input.charAt(idx + 2) === '=') {
2017                     data = String.fromCharCode(
2018                         (data >>> 16) & 0x00ff
2019                     );
2020                 } else if (input.charAt(idx + 3) === '=') {
2021                     data = String.fromCharCode(
2022                         (data >>> 16) & 0x00ff,
2023                         (data >>> 8) & 0x00ff
2024                     );
2025                 } else {
2026                     data = String.fromCharCode(
2027                         (data >>> 16) & 0x00ff,
2028                         (data >>> 8) & 0x00ff,
2029                         data & 0x00ff
2030                     );
2031                 }
2032                 output += data;
2033             }
2034 
2035             return output;
2036         },
2037 
2038         /**
2039          * @private
2040          * Extracts the username and the password from the Base64 encoded string.
2041          * @params {String}
2042          *     A base64 encoded string containing credentials that (when decoded)
2043          *     are colon delimited.
2044          * @returns {Object}
2045          *     An object with the following structure:
2046          *     {id:string, password:string}
2047          */
2048         getCredentials: function (authorization) {
2049             var credObj = {},
2050                 credStr = this.b64Decode(authorization),
2051                 colonIndx = credStr.indexOf(":");
2052 
2053             //Check to ensure that string is colon delimited.
2054             if (colonIndx === -1) {
2055                 throw new Error("String is not colon delimited.");
2056             }
2057 
2058             //Extract ID and password.
2059             credObj.id = credStr.substring(0, colonIndx);
2060             credObj.password = credStr.substring(colonIndx + 1);
2061             return credObj;
2062         },
2063 
2064         /**
2065          * Takes a string and removes any spaces within the string.
2066          * @param {String} string
2067          *     The string to remove spaces from
2068          * @returns {String}
2069          *     The string without spaces
2070          */
2071         removeSpaces: function (string) {
2072             return string.split(' ').join('');
2073         },
2074 
2075         /**
2076          * Escapes spaces as encoded " " characters so they can
2077          * be safely rendered by jQuery.text(string) in all browsers.
2078          *
2079          * (Although IE behaves as expected, Firefox collapses spaces if this function is not used.)
2080          *
2081          * @param text
2082          *    The string whose spaces should be escaped
2083          *
2084          * @returns
2085          *    The string with spaces escaped
2086          */
2087         escapeSpaces: function (string) {
2088             return string.replace(/\s/g, '\u00a0');
2089         },
2090 
2091         /**
2092          * Adds a span styled to line break at word edges around the string passed in.
2093          * @param str String to be wrapped in word-breaking style.
2094          * @private
2095          */
2096         addWordWrapping : function (str) {
2097             return '<span style="word-wrap: break-word;">' + str + '</span>';
2098         },
2099 
2100         /**
2101          * Takes an Object and determines whether it is an Array or not.
2102          * @param {Object} obj
2103          *     The Object in question
2104          * @returns {Boolean}
2105          *     true if the object is an Array, else false.
2106          */
2107         isArray: function (obj) {
2108             return obj.constructor.toString().indexOf("Array") !== -1;
2109         },
2110 
2111         /**
2112          * @private
2113          * Takes a data object and returns an array extracted
2114          * @param {Object} data
2115          *     JSON payload
2116          *
2117          * @returns {array}
2118          *     extracted array
2119          */
2120         getArray: function (data) {
2121             if (this.isArray(data)) {
2122                 //Return if already an array.
2123                 return data;
2124             } else {
2125                 //Create an array, iterate through object, and push to array. This
2126                 //should only occur with one object, and therefore one obj in array.
2127                 var arr = [];
2128                 arr.push(data);
2129                 return arr;
2130             }
2131         },
2132 
2133         /**
2134          * @private
2135          * Extracts the ID for an entity given the Finesse REST URI. The ID is
2136          * assumed to be the last element in the URI (after the last "/").
2137          * @param {String} uri
2138          *     The Finesse REST URI to extract the ID from.
2139          * @returns {String}
2140          *     The ID extracted from the REST URI.
2141          */
2142         getId: function (uri) {
2143             if (!uri) {
2144                 return "";
2145             }
2146             var strLoc = uri.lastIndexOf("/");
2147             return uri.slice(strLoc + 1);
2148         },
2149 
2150         /**
2151          * Compares two objects for equality.
2152          * @param {Object} obj1 
2153          *      First of two objects to compare.
2154          * @param {Object} obj2
2155          *      Second of two objects to compare.
2156          */
2157         getEquals: function (objA, objB) {
2158             var key;
2159 
2160             for (key in objA) {
2161                 if (objA.hasOwnProperty(key)) {
2162                     if (!objA[key]) {
2163                         objA[key] = "";
2164                     }
2165 
2166                     if (typeof objB[key] === 'undefined') {
2167                         return false;
2168                     }
2169                     if (typeof objB[key] === 'object') {
2170                         if (!objB[key].equals(objA[key])) {
2171                             return false;
2172                         }
2173                     }
2174                     if (objB[key] !== objA[key]) {
2175                         return false;
2176                     }
2177                 }
2178             }
2179             return true;
2180         },
2181 
2182         /**
2183          * Regular expressions used in translating HTML and XML entities
2184          */
2185         ampRegEx : new RegExp('&', 'gi'),
2186         ampEntityRefRegEx : new RegExp('&', 'gi'),
2187         ltRegEx : new RegExp('<', 'gi'),
2188         ltEntityRefRegEx : new RegExp('<', 'gi'),
2189         gtRegEx : new RegExp('>', 'gi'),
2190         gtEntityRefRegEx : new RegExp('>', 'gi'),
2191         xmlSpecialCharRegEx: new RegExp('[&<>"\']', 'g'),
2192         entityRefRegEx: new RegExp('&[^;]+(?:;|$)', 'g'),
2193 
2194         /**
2195          * Translates between special characters and HTML entities
2196          *
2197          * @param text
2198          *     The text to translate
2199          *
2200          * @param makeEntityRefs
2201          *    If true, encode special characters as HTML entities; if
2202          *    false, decode HTML entities back to special characters
2203          *
2204          * @private
2205          */
2206         translateHTMLEntities: function (text, makeEntityRefs) {
2207             if (typeof(text) !== "undefined" && text !== null && text !== "") {
2208                 if (makeEntityRefs) {
2209                     text = text.replace(this.ampRegEx, '&');
2210                     text = text.replace(this.ltRegEx, '<');
2211                     text = text.replace(this.gtRegEx, '>');
2212                 } else {
2213                     text = text.replace(this.gtEntityRefRegEx, '>');
2214                     text = text.replace(this.ltEntityRefRegEx, '<');
2215                     text = text.replace(this.ampEntityRefRegEx, '&');
2216                 }
2217             }
2218 
2219             return text;
2220         },
2221 
2222         /**
2223          * Translates between special characters and XML entities
2224          *
2225          * @param text
2226          *     The text to translate
2227          *
2228          * @param makeEntityRefs
2229          *    If true, encode special characters as XML entities; if
2230          *    false, decode XML entities back to special characters
2231          *
2232          * @private
2233          */
2234         translateXMLEntities: function (text, makeEntityRefs) {
2235             /** @private */
2236             var escape = function (character) {
2237                 switch (character) {
2238                     case "&":
2239                         return "&";
2240                     case "<":
2241                         return "<";
2242                     case ">":
2243                         return ">";
2244                     case "'":
2245                         return "'";
2246                     case "\"":
2247                         return """;
2248                     default:
2249                         return character;
2250                 }
2251             },
2252             /** @private */
2253             unescape = function (entity) {
2254                 switch (entity) {
2255                     case "&":
2256                         return "&";
2257                     case "<":
2258                         return "<";
2259                     case ">":
2260                         return ">";
2261                     case "'":
2262                         return "'";
2263                     case """:
2264                         return "\"";
2265                     default:
2266                         if (entity.charAt(1) === "#" && entity.charAt(entity.length - 1) === ";") {
2267                             if (entity.charAt(2) === "x") {
2268                                 return String.fromCharCode(parseInt(entity.slice(3, -1), 16));
2269                             } else {
2270                                 return String.fromCharCode(parseInt(entity.slice(2, -1), 10));
2271                             }
2272                         } else {
2273                             throw new Error("Invalid XML entity: " + entity);
2274                         }
2275                 }
2276             };
2277 
2278             if (typeof(text) !== "undefined" && text !== null && text !== "") {
2279                 if (makeEntityRefs) {
2280                     text = text.replace(this.xmlSpecialCharRegEx, escape);
2281                 } else {
2282                     text = text.replace(this.entityRefRegEx, unescape);
2283                 }
2284             }
2285 
2286             return text;
2287         },
2288 
2289         /**
2290          * @private
2291          * Utility method to pad the number with a leading 0 for single digits
2292          * @param (Number) num
2293          *     the number to pad
2294          */
2295         pad : function (num) {
2296             if (num < 10) {
2297                 return "0" + num;
2298             }
2299 
2300             return String(num);
2301         },
2302         
2303         /**
2304          * Pad with zeros based on a padWidth.
2305          *
2306          * @param num
2307          * @param padWidth
2308          * @returns {String} with padded zeros (based on padWidth)
2309          */
2310         padWithWidth : function (num, padWidth) {
2311             var value, index, result;
2312             
2313             result = "";
2314             for(index=padWidth;index>1;index--)
2315             {
2316                 value = Math.pow(10, index-1);
2317                 
2318                 if (num < value) {
2319                    result = result + "0";
2320                 }
2321             }
2322             result = result + num;
2323             
2324             return String(result);
2325         },
2326         
2327         /**
2328          * Converts a date to an ISO date string.
2329          *
2330          * @param aDate
2331          * @returns {String} in ISO date format
2332          *
2333          * Note: Some browsers don't support this method (e.g. IE8).
2334          */
2335         convertDateToISODateString : function (aDate) {
2336              var result;
2337              
2338              result =  aDate.getUTCFullYear() + "-" + this.padWithWidth(aDate.getUTCMonth()+1, 2) + "-" + this.padWithWidth(aDate.getUTCDate(), 2) + "T" + this.padWithWidth(aDate.getUTCHours(), 2) + ":" + this.padWithWidth(aDate.getUTCMinutes(), 2) + ":" + this.padWithWidth(aDate.getUTCSeconds(), 2)+ "." + this.padWithWidth(aDate.getUTCMilliseconds(), 3) + "Z";
2339              return result;
2340         },
2341         
2342        /**
2343         * Get the date in ISO date format. 
2344         * 
2345         * @param aDate is the date
2346         * @returns {String} date in ISO format
2347         *
2348         * Note: see convertDateToISODateString() above.
2349         */
2350         dateToISOString : function (aDate) {
2351              var result;
2352              
2353              try {
2354                 result = aDate.toISOString();
2355              } catch (e) {
2356                 result = this.convertDateToISODateString(aDate);
2357              }
2358              return result;
2359         },
2360         
2361         /**
2362          * Parse string (which is formated as ISO8601 date) into Javascript Date object.
2363          *
2364          * @param s ISO8601 string
2365          * @return {Date}
2366          * Note: Some browsers don't support Date constructor which take ISO8601 date (e.g. IE 8).
2367          */
2368         parseDateStringISO8601 : function (s) {
2369              var i, re = /(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:.(\d+))?(Z|[+\-]\d{2})(?::(\d{2}))?/,
2370              d = s.match(re);
2371              if( !d ) {
2372                 return null;
2373              }
2374              for( i in d ) {
2375                 d[i] = ~~d[i];
2376              }
2377              return new Date(Date.UTC(d[1], d[2] - 1, d[3], d[4], d[5], d[6], d[7]) + (d[8] * 60 + d[9]) * 60000);
2378         },
2379         
2380         /**
2381          * Utility method to render a timestamp value (in seconds) into HH:MM:SS format.
2382          * @param {Number} time
2383          *     The timestamp in ms to render
2384          * @returns {String}
2385          * Time string in HH:MM:SS format.
2386          */
2387         getDisplayTime : function (time) {
2388             var hour, min, sec, timeStr = "00:00:00";
2389 
2390             if (time && time !== "-1") {
2391                 // calculate hours, minutes, and seconds
2392                 hour = this.pad(Math.floor(time / 3600));
2393                 min = this.pad(Math.floor((time % 3600) / 60));
2394                 sec = this.pad(Math.floor((time % 3600) % 60));
2395                 // construct HH:MM:SS time string
2396                 timeStr = hour + ":" + min + ":" + sec;
2397             }
2398 
2399             return timeStr;
2400         },
2401 
2402         /**
2403          * Checks if the string is null. If it is, return empty string; else return
2404          * the string itself.
2405          * 
2406          * @param  {String} str 
2407          * The string to check
2408          * @return {String}     
2409          * Empty string or string itself
2410          */
2411         convertNullToEmptyString : function (str) {
2412             return str || "";
2413         },
2414 
2415         /**
2416          * Utility method to render a timestamp string (of format
2417          * YYYY-MM-DDTHH:MM:SSZ) into a duration of HH:MM:SS format.
2418          * 
2419          * @param {String} timestamp
2420          *           The timestamp to render
2421          * @param {Date} [now]
2422          *            Optional argument to provide the time from which to
2423          *            calculate the duration instead of using the current time
2424          * @returns {String}
2425          * Duration string in HH:MM:SS format.
2426          */
2427         convertTsToDuration : function (timestamp, now) {
2428             return this.convertTsToDurationWithFormat(timestamp, false, now); 
2429         },
2430         
2431         /**
2432          * Utility method to render a timestamp string (of format
2433          * YYYY-MM-DDTHH:MM:SSZ) into a duration of HH:MM:SS format,
2434          * with optional -1 for null or negative times.
2435          * 
2436          * @param {String} timestamp
2437          *             The timestamp to render
2438          * @param {Boolean} forFormat
2439          *            If True, if duration is null or negative, return -1 so that the duration can be formated
2440          *            as needed in the Gadget. 
2441          * @param {Date} [now]
2442          *             Optional argument to provide the time from which to
2443          *            calculate the duration instead of using the current time
2444          * @returns {String}
2445          * Duration string in HH:MM:SS format.
2446          */
2447         convertTsToDurationWithFormat : function (timestamp, forFormat, now) {
2448             var startTimeInMs, nowInMs, durationInSec = "-1";
2449             
2450             // Calculate duration
2451             if (timestamp && typeof timestamp === "string") {
2452                 // first check it '--' for a msg in grid
2453                 if (timestamp === '--' || timestamp ==="" || timestamp === "-1") {
2454                     return "-1";
2455                 }
2456                 // else try convert string into a time
2457                 startTimeInMs = Date.parse(timestamp);
2458                 if (!isNaN(startTimeInMs)) {
2459                     if (!now || !(now instanceof Date)) {
2460                         nowInMs = this.currentServerTimeMillis();
2461                     } else {
2462                         nowInMs = this.convertToServerTimeMillis(now.getTime());
2463                     }
2464                     durationInSec = Math.floor((nowInMs - startTimeInMs) / 1000);
2465                     // Since currentServerTime is not exact (lag between sending and receiving
2466                     // messages will differ slightly), treat a slightly negative (less than 1 sec) 
2467                     // value as 0, to avoid "--" showing up when a state first changes.
2468                     if (durationInSec === -1) {
2469                         durationInSec = 0;
2470                     }
2471                     
2472                     if (durationInSec < 0) {
2473                         if (forFormat) {
2474                             return "-1";
2475                         } else {
2476                             return this.getDisplayTime("-1");
2477                         }
2478                     }
2479                 }
2480             }else {
2481                 if(forFormat){
2482                     return "-1";
2483                 }
2484             }
2485             return this.getDisplayTime(durationInSec);
2486          },
2487          
2488          /**
2489           * @private
2490           * Takes the time in seconds and duration in % and return the duration in milliseconds.
2491           *
2492           * @param time in seconds
2493           * @param duration in %
2494           */
2495          
2496          getRefreshTime :function(expiryTime , duration){
2497           var durationInMs = Math.floor((expiryTime * duration * 1000) / 100);
2498             return durationInMs;
2499          },
2500          
2501         /**
2502          * Takes a string (typically from window.location) and finds the value which corresponds to a name. For
2503          * example: http://www.company.com/?param1=value1¶m2=value2
2504          *
2505          * @param str is the string to search
2506          * @param name is the name to search for
2507          */
2508         getParameterByName : function(str, name) {
2509             var regex, results;
2510             name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
2511             regex = new RegExp("[\\?&]" + name + "=([^&#]*)");
2512             results = regex.exec(str);
2513             return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
2514         }, 
2515         
2516         /**
2517          *
2518          * Returns the base64 encoded user authorization String.
2519          * @returns {String} the Authorization String
2520          * 
2521          */
2522         getUserAuthString: function () {
2523             var authString = window.sessionStorage.getItem('userFinesseAuth');
2524             return authString;
2525         },
2526         
2527         /**
2528          * Return the user access token as JSON Object.
2529          * @returns {Object} the access token JSON object
2530          * 
2531          */
2532         getAuthTokenObj: function(){
2533            var authTokenString = window.sessionStorage.getItem('ssoTokenObject');
2534            return this.getJSONParser().parse(authTokenString);
2535         },
2536         
2537         /**
2538          * Returns the user access token as String.
2539          * @returns {String} the access token
2540          * 
2541          */
2542 
2543         getToken: function () {
2544             var tokenString = window.sessionStorage.getItem('ssoTokenObject'), tokenObj;
2545 					if (tokenString && typeof tokenString === "string") {
2546 						tokenObj = this.getJSONParser().parse(tokenString);
2547 						if (tokenObj.token) {
2548 							return tokenObj.token;
2549 						} else {
2550 							throw new Error(
2551 									"Unable to retrieve token : Invalid token Object in browser session");
2552 						}
2553 					} 
2554         },
2555         
2556         /**
2557 		 * The authorization header based on SSO or non SSO deployment.
2558 		 *          Can be "Bearer " or "Basic "
2559 		 * @returns {String} The authorization header string.
2560 		 */
2561 		 getAuthHeaderString : function(configObj) {
2562 					var authHeader;
2563 					if (configObj.systemAuthMode === this.getAuthModes().SSO) {
2564 						authHeader = "Bearer " + configObj.authToken;
2565 					} else if (configObj.systemAuthMode === this.getAuthModes().NONSSO) {
2566 						authHeader = "Basic " + configObj.authorization;
2567 					} else {
2568 						throw new Error("Unknown auth mode "+configObj.systemAuthMode);
2569 					}
2570 					return authHeader;
2571 				},
2572 		
2573 		/**
2574 		 * Can be used as a constant for auth modes
2575 		 *          Can be "SSO" , "NON_SSO" or "HYBRID"
2576 		 * @returns {String} The authorization header string.
2577 		 */		
2578 		getAuthModes : function(){
2579 		     return {
2580                     SSO: "SSO",
2581 		            NONSSO: "NON_SSO",
2582 		            HYBRID: "HYBRID"
2583 		     };
2584 		},
2585 		
2586 		/**
2587 		 * Encodes the node name
2588 		 */
2589 		encodeNodeName : function(node){
2590 			if (node === null){
2591 			    return null;
2592 			}
2593 			var originalChars, encodedChars,encodedNode, i;
2594 			originalChars = ["?", "@", "&","'"];
2595 			encodedChars = ["?3F", "?40", "?26","?27"];
2596 			encodedNode = node;
2597 			
2598 			if(encodedNode.indexOf(originalChars[0]) !== -1){
2599 			   encodedNode = encodedNode.replace(/\?/g, encodedChars[0]);
2600 			}
2601 			for (i = 1; i < originalChars.length; i++){
2602 			    if(encodedNode.indexOf(originalChars[i]) !== -1){
2603 			        encodedNode = encodedNode.replace(new RegExp(originalChars[i], "g"), encodedChars[i]);
2604 			    }
2605 			}
2606 			return encodedNode;
2607 		},
2608 		
2609 		/**
2610 		 * @private Utility method to convert milliseconds into minutes.
2611 		 * @param {String} Time in milliseconds
2612 		 * @returns {String} Time in minutes
2613 		 */
2614 		convertMilliSecondsToMinutes : function(millisec){
2615 			if(!millisec || isNaN(millisec)){
2616 				throw new Error("passed argument is not a number");
2617 			}else{
2618 				var minutes = Math.floor(millisec / (1000 * 60));
2619 	            return minutes;
2620 			}
2621 		},
2622 
2623         
2624         /**
2625 		 * @private Adds a new cookie to the page with a default domain.
2626 		 * @param {String}
2627 		 *            key the key to assign a value to
2628 		 * @param {String}
2629 		 *            value the value to assign to the key
2630 		 * @param {Number}
2631 		 *            days number of days (from current) until the cookie should
2632 		 *            expire
2633 		 */
2634         addCookie : function (key, value, days) {
2635             var date, expires = "",
2636                 cookie = key + "=" + escape(value);
2637             if (typeof days === "number") {
2638                 date = new Date();
2639                 date.setTime(date.getTime() + (days * 24 * 3600 * 1000));
2640                 cookie += "; expires=" + date.toGMTString();
2641             }
2642             document.cookie = cookie + "; path=/";
2643         },
2644 
2645         /**
2646          * @private
2647          * Get the value of a cookie given a key.
2648          * @param {String} key
2649          *      a key to lookup
2650          * @returns {String}
2651          *      the value mapped to a key, null if key doesn't exist
2652          */
2653         getCookie : function (key) {
2654             var i, pairs, pair;
2655             if (document.cookie) {
2656                 pairs = document.cookie.split(";");
2657                 for (i = 0; i < pairs.length; i += 1) {
2658                     pair = this.trim(pairs[i]).split("=");
2659                     if (pair[0] === key) {
2660                         return unescape(pair[1]);
2661                     }
2662                 }
2663             }
2664             return null;
2665         },
2666 
2667         /**
2668          * @private
2669          * Deletes the cookie mapped to specified key.
2670          * @param {String} key
2671          *      the key to delete
2672          */
2673         deleteCookie : function (key) {
2674             this.addCookie(key, "", -1);
2675         },
2676 
2677         /**
2678          * @private
2679          * Case insensitive sort for use with arrays or Dojox stores
2680          * @param {String} a
2681          *      first value
2682          * @param {String} b
2683          *      second value
2684          */
2685         caseInsensitiveSort: function (a, b) {
2686             var ret = 0, emptyString = "";
2687             a = a + emptyString;
2688             b = b + emptyString;
2689             a = a.toLowerCase();
2690             b = b.toLowerCase();
2691             if (a > b) {
2692                 ret = 1;
2693             }
2694             if (a < b) { 
2695                 ret = -1;
2696             }
2697             return ret;
2698         },
2699 
2700         /**
2701          * @private
2702         * Calls the specified function to render the dojo wijit for a gadget  when the gadget first becomes visible.
2703         *
2704         * The displayWjitFunc function will be called once and only once when the div for our wijit 
2705         * becomes visible for the first time.  This is necessary because some dojo wijits such as the grid
2706         * throw exceptions and do not render properly if they are created in a display:none div.
2707         * If our gadget is visisble the function will be called immediately.
2708         * If our gadget is not yet visisble, then it sets a timer and waits for it to become visible.
2709         * NOTE:  The timer may seem inefficent, originally I tried connecting to the tab onclick handler, but
2710         * there is a problem with dojo.connnect to an iframe's parent node in Internet Explorer. 
2711         * In Firefox the click handler works OK, but it happens before the node is actually visisble, so you
2712         * end up waiting for the node to become visisble anyway.
2713         * @displayWjitFunc:  A function to be called once our gadget has become visisble for th first time.
2714         */  
2715         onGadgetFirstVisible: function (displayWjitFunc) {
2716             var i, q, frameId, gadgetNbr, gadgetTitleId, panelId, panelNode, link, iterval, once = false, active = false, tabId = "#finesse-tab-selector";
2717             try {
2718                 frameId = dojo.attr(window.frameElement, "id"); // Figure out what gadget number we are by looking at our frameset
2719                 gadgetNbr = frameId.match(/\d+$/)[0];  // Strip the number off the end of the frame Id, that's our gadget number
2720                 gadgetTitleId = "#finesse_gadget_" + gadgetNbr + "_title";  // Create a a gadget title id from the number
2721                 
2722                 // Loop through all of the tab panels to find one that has our gadget id
2723                 dojo.query('.tab-panel', window.parent.document).some(function (node, index, arr) {
2724                     q = dojo.query(gadgetTitleId, node);  // Look in this panel for our gadget id
2725                     if (q.length > 0) {  // You found it
2726                         panelNode = node;
2727                         panelId = dojo.attr(panelNode, "id");  // Get panel id  e.g. panel_Workgroups
2728                         active = dojo.hasClass(panelNode, "active");
2729                         tabId = "#tab_" + panelId.slice(6);  // Turn it into a tab id e.g.tab_Workgroups
2730                         return;
2731                     }
2732                 });
2733                 // If panel is already active - execute the function - we're done
2734                 if (active) {
2735                     //?console.log(frameId + " is visible display it");
2736                     setTimeout(displayWjitFunc);
2737                 } 
2738                 // If its not visible - wait for the active class to show up.
2739                 else {
2740                     //?console.log(frameId  + " (" + tabId + ") is NOT active wait for it");
2741                     iterval = setInterval(dojo.hitch(this, function () {
2742                         if (dojo.hasClass(panelNode, "active")) {
2743                             //?console.log(frameId  + " (" + tabId + ") is visible display it");
2744                             clearInterval(iterval);
2745                             setTimeout(displayWjitFunc);
2746                         } 
2747                     }), 250);
2748                 }
2749             } catch (err) {
2750                 //?console.log("Could not figure out what tab " + frameId + " is in: " + err);
2751             }
2752         },
2753 
2754         /**
2755          * @private
2756          * Downloads the specified url using a hidden iframe. In order to cause the browser to download rather than render
2757          * in the hidden iframe, the server code must append the header "Content-Disposition" with a value of 
2758          * "attachment; filename=\"<WhateverFileNameYouWant>\"".
2759          */
2760         downloadFile : function (url) {
2761             var iframe = document.getElementById("download_iframe");
2762 
2763             if (!iframe)
2764             {
2765                 iframe = document.createElement("iframe");
2766                 $(document.body).append(iframe);
2767                 $(iframe).css("display", "none");
2768             }
2769 
2770             iframe.src = url;
2771         },
2772 
2773         /**
2774          * @private
2775          * bitMask has functions for testing whether bit flags specified by integers are set in the supplied value
2776          */
2777         bitMask: {
2778             /** @private */
2779             isSet: function (value, mask) {
2780                 return (value & mask) === mask;
2781             },
2782             /**
2783              * Returns true if all flags in the intArray are set on the specified value
2784              * @private 
2785              */
2786             all: function (value, intArray) {
2787                 var i = intArray.length;
2788                 if (typeof(i) === "undefined")
2789                 {
2790                     intArray = [intArray];
2791                     i = 1;
2792                 }
2793                 while ((i = i - 1) !== -1)
2794                 {
2795                     if (!this.isSet(value, intArray[i]))
2796                     {
2797                         return false;
2798                     }
2799                 }
2800                 return true;
2801             },
2802             /**
2803              * @private
2804              * Returns true if any flags in the intArray are set on the specified value
2805              */
2806             any: function (value, intArray) {
2807                 var i = intArray.length;
2808                 if (typeof(i) === "undefined")
2809                 {
2810                     intArray = [intArray];
2811                     i = 1;
2812                 }
2813                 while ((i = i - 1) !== -1)
2814                 {
2815                     if (this.isSet(value, intArray[i]))
2816                     {
2817                         return true;
2818                     }
2819                 }
2820                 return false;
2821             }
2822         },
2823 
2824         /** @private */
2825         renderDojoGridOffScreen: function (grid) {
2826             var offscreenDiv = $("<div style='position: absolute; left: -5001px; width: 5000px;'></div>")[0];
2827             $(document.body).append(offscreenDiv);
2828             grid.placeAt(offscreenDiv);
2829             grid.startup();
2830             document.body.removeChild(offscreenDiv);
2831             return grid;
2832         },
2833 
2834         /** @private */
2835         initializeSearchInput: function(searchInput, changeCallback, callbackDelay, callbackScope, placeholderText) {
2836             var timerId = null,
2837                 theControl = typeof(searchInput) === "string" ? $("#" + searchInput) : $(searchInput),
2838                 theInputControl = theControl.find("input"),
2839                 theClearButton = theControl.find("a"),
2840                 inputControlWidthWithClear = 204,
2841                 inputControlWidthNoClear = 230,
2842                 sPreviousInput = theInputControl.val(),
2843                 /** @private **/
2844                 toggleClearButton = function(){
2845                     if (theInputControl.val() === "") {
2846                         theClearButton.hide();
2847                         theControl.removeClass("input-append");
2848                         theInputControl.width(inputControlWidthNoClear);
2849                     } else {
2850                         theInputControl.width(inputControlWidthWithClear);
2851                         theClearButton.show();
2852                         theControl.addClass("input-append");
2853                     }
2854                 };
2855 
2856             // set placeholder text
2857             theInputControl.attr('placeholder', placeholderText);
2858 
2859             theInputControl.unbind('keyup').bind('keyup', function() {
2860                 if (sPreviousInput !== theInputControl.val()) {
2861                     window.clearTimeout(timerId);
2862                     sPreviousInput = theInputControl.val();
2863                     timerId = window.setTimeout(function() {
2864                         changeCallback.call((callbackScope || window), theInputControl.val());
2865                         theInputControl[0].focus();
2866                     }, callbackDelay);
2867                 }
2868 
2869                 toggleClearButton();
2870             });
2871 
2872             theClearButton.bind('click', function() {
2873                 theInputControl.val('');
2874                 changeCallback.call((callbackScope || window), '');
2875 
2876                 toggleClearButton();
2877                 theInputControl[0].focus(); // jquery and dojo on the same page break jquery's focus() method
2878             });
2879 
2880             theInputControl.val("");
2881             toggleClearButton();
2882         },
2883 
2884         DataTables: {
2885             /** @private */
2886             createDataTable: function (options, dataTableOptions) {
2887                 var grid,
2888                     table = $('<table cellpadding="0" cellspacing="0" border="0" class="finesse"><thead><tr></tr></thead></table>'),
2889                     headerRow = table.find("tr"),
2890                     defaultOptions = {
2891                         "aaData": [],
2892                         "bPaginate": false,
2893                         "bLengthChange": false,
2894                         "bFilter": false,
2895                         "bInfo": false,
2896                         "sScrollY": "176",
2897                         "oLanguage": {
2898                             "sEmptyTable": "",
2899                             "sZeroRecords": ""
2900                         }
2901                     },
2902                     gridOptions = $.extend({}, defaultOptions, dataTableOptions),
2903                     columnDefs = [],
2904                     columnFormatter;
2905 
2906                 // Create a header cell for each column, and set up the datatable definition for the column
2907                 $(options.columns).each(function (index, column) {
2908                     headerRow.append($("<th></th>"));
2909                     columnDefs[index] = {
2910                         "mData": column.propertyName,
2911                         "sTitle": column.columnHeader,
2912                         "sWidth": column.width,
2913                         "aTargets": [index],
2914                         "bSortable": column.sortable,
2915                         "bVisible": column.visible,
2916                         "mRender": column.render
2917                     };
2918                     if (typeof(column.renderFunction) === "function")
2919                     {
2920                         /** @ignore **/
2921                         columnDefs[index].mRender = /** @ignore **/ function (value, type, dataObject) { 
2922                             var returnValue;
2923 
2924                             //Apply column render logic to value before applying extra render function
2925                             if (typeof(column.render) === "function")
2926                             {
2927                                 value = column.render.call(value, value, value);
2928                             }
2929 
2930                             if (typeof(type) === "string")
2931                             {
2932                                 switch (type)
2933                                 {
2934                                 case "undefined":
2935                                 case "sort":
2936                                     returnValue = value;
2937                                     break;
2938                                 case "set":
2939                                     throw new Error("Unsupported set data in Finesse Grid");
2940                                 case "filter":
2941                                 case "display":
2942                                 case "type":
2943                                     returnValue = column.renderFunction.call(dataObject, value, dataObject);
2944                                     break;
2945                                 default:
2946                                     break;
2947                                 }
2948                             }
2949                             else
2950                             {
2951                                 throw new Error("type param not specified in Finesse DataTable mData");
2952                             }
2953 
2954                             return  returnValue;
2955                         };
2956                     }
2957                 });
2958                 gridOptions.aoColumnDefs = columnDefs;
2959 
2960                 // Set the height
2961                 if (typeof(options.bodyHeightPixels) !== "undefined" && options.bodyHeightPixels !== null)
2962                 {
2963                     gridOptions.sScrollY = options.bodyHeightPixels + "px";
2964                 }
2965 
2966                 // Place it into the DOM
2967                 if (typeof(options.container) !== "undefined" && options.container !== null)
2968                 {
2969                     $(options.container).append(table);
2970                 }
2971 
2972                 // Create the DataTable
2973                 table.dataTable(gridOptions);
2974 
2975                 return table;
2976             }
2977         },
2978         
2979         /**
2980          * @private
2981          * Sets a dojo button to the specified disable state, removing it from
2982          * the tab order if disabling, and restoring it to the tab order if enabling.
2983          * @param {Object} dojoButton Reference to the dijit.form.Button object. This is not the DOM element.
2984          * @param {bool} disabled
2985          */
2986         setDojoButtonDisabledAttribute: function (dojoButton, disabled) {
2987             var labelNode,
2988                 tabIndex;
2989 
2990             dojoButton.set("disabled", disabled);
2991 
2992             // Remove the tabindex attribute on disabled buttons, store it, 
2993             // and replace it when it becomes enabled again
2994             labelNode = $("#" + dojoButton.id + "_label");
2995             if (disabled)
2996             {
2997                 labelNode.data("finesse:dojoButton:tabIndex", labelNode.attr("tabindex"));
2998                 labelNode.removeAttr("tabindex");
2999             }
3000             else
3001             {
3002                 tabIndex = labelNode.data("finesse:dojoButton:tabIndex");
3003                 if (typeof(tabIndex) === "string")
3004                 {
3005                     labelNode.attr("tabindex", Number(tabIndex));
3006                 }
3007             }
3008         },
3009 
3010         /**
3011          * @private
3012          * Use this utility to disable the tab stop for a Dojo Firebug iframe within a gadget.
3013          *
3014          * Dojo sometimes adds a hidden iframe for enabling a firebug lite console in older
3015          * browsers. Unfortunately, this adds an additional tab stop that impacts accessibility.
3016          */
3017         disableTabStopForDojoFirebugIframe: function () {
3018             var iframe = $("iframe[src*='loadFirebugConsole']");
3019 
3020             if ((iframe.length) && (iframe.attr("tabIndex") !== "-1")) {
3021                 iframe.attr('tabIndex', '-1'); 
3022             }
3023         },
3024 
3025         /**
3026          * @private
3027          * Measures the given text using the supplied fontFamily and fontSize
3028          * @param  {string} text       text to measure
3029          * @param  {string} fontFamily
3030          * @param  {string} fontSize
3031          * @return {number} pixel width
3032          */
3033         measureText: function (text, fontFamily, fontSize) {
3034             var width,
3035                 element = $("<div></div>").text(text).css({
3036                     "fontSize": fontSize,
3037                     "fontFamily": fontFamily
3038                 }).addClass("offscreen").appendTo(document.body);
3039 
3040             width = element.width();
3041             element.remove();
3042 
3043             return width;
3044         },
3045 
3046         /**
3047          * Adjusts the gadget height. Shindig's gadgets.window.adjustHeight fails when
3048          * needing to resize down in IE. This gets around that by calculating the height
3049          * manually and passing it in.
3050          * @return {undefined}
3051          */
3052         "adjustGadgetHeight": function () {
3053             var bScrollHeight = $("body").height() + 20;
3054             gadgets.window.adjustHeight(bScrollHeight);
3055         },
3056 
3057         /**
3058         * Private helper method for converting a javascript object to xml, where the values of the elements are
3059         * appropriately escaped for XML.
3060         * This is a simple implementation that does not implement cdata or attributes. It is also 'unformatted' in that
3061         * there is no whitespace between elements.
3062         * @param object The javascript object to convert to XML.
3063         * @returns The XML string.
3064         * @private
3065         */
3066         _json2xmlWithEscape: function(object) {
3067             var that = this,
3068                 xml = "",
3069                 m,
3070                 /** @private **/
3071                 toXmlHelper = function(value, name) {
3072                 var xml = "",
3073                     i,
3074                     m;
3075                 if (value instanceof Array) {
3076                     for (i = 0; i < value.length; ++i) {
3077                         xml += toXmlHelper(value[i], name);
3078                     }
3079                 }
3080                 else if (typeof value === "object") {
3081                     xml += "<" + name + ">";
3082                     for (m in value) {
3083                         if (value.hasOwnProperty(m)) {
3084                            xml += toXmlHelper(value[m], m);
3085                         }
3086                     }
3087                     xml += "</" + name + ">";
3088                 }
3089                 else {
3090                     // is a leaf node
3091                     xml += "<" + name + ">" + that.translateHTMLEntities(value.toString(), true) +
3092                         "</" + name + ">";
3093                 }
3094                 return xml;
3095             };
3096             for (m in object) {
3097                 if (object.hasOwnProperty(m)) {
3098                     xml += toXmlHelper(object[m], m);
3099                 }
3100             }
3101             return xml;
3102         },
3103 
3104         /**
3105          * Private method for returning a sanitized version of the user agent string.
3106          * @returns the user agent string, but sanitized!
3107          * @private
3108          */
3109         getSanitizedUserAgentString: function () {
3110             return this.translateXMLEntities(navigator.userAgent, true);
3111         },
3112 
3113         /**
3114          * Use JQuery's implementation of Promises (Deferred) to execute code when 
3115          * multiple async processes have finished. An example use:
3116          *
3117          * var asyncProcess1 = $.Deferred(),
3118          *     asyncProcess2 = $.Deferred();
3119          *     
3120          * finesse.utilities.Utilities.whenAllDone(asyncProcess1, asyncProcess2) // WHEN both asyncProcess1 and asyncProcess2 are resolved or rejected ...
3121          *     .then(
3122          *         // First function passed to then() is called when all async processes are complete, regardless of errors
3123          *         function () {
3124          *             console.log("all processes completed");
3125          *         },
3126          *         // Second function passed to then() is called if any async processed threw an exception
3127          *         function (failures) { // Array of failure messages
3128          *             console.log("Number of failed async processes: " + failures.length);
3129          *         });
3130          *
3131          * Found at:
3132          * http://stackoverflow.com/a/15094263/1244030
3133          *
3134          * Pass in any number of $.Deferred instances.
3135          * @returns {Object}
3136          */
3137         whenAllDone: function () {
3138             var deferreds = [],
3139                 result = $.Deferred();
3140 
3141             $.each(arguments, function(i, current) {
3142                 var currentDeferred = $.Deferred();
3143                 current.then(function() {
3144                     currentDeferred.resolve(false, arguments);
3145                 }, function() {
3146                     currentDeferred.resolve(true, arguments);
3147                 });
3148                 deferreds.push(currentDeferred);
3149             });
3150 
3151             $.when.apply($, deferreds).then(function() {
3152                 var failures = [],
3153                     successes = [];
3154 
3155                 $.each(arguments, function(i, args) {
3156                     // If we resolved with `true` as the first parameter
3157                     // we have a failure, a success otherwise
3158                     var target = args[0] ? failures : successes,
3159                         data = args[1];
3160                     // Push either all arguments or the only one
3161                     target.push(data.length === 1 ? data[0] : args);
3162                 });
3163 
3164                 if(failures.length) {
3165                     return result.reject.apply(result, failures);
3166                 }
3167 
3168                 return result.resolve.apply(result, successes);
3169             });
3170 
3171             return result;
3172         },
3173 
3174         /**
3175          * Private method to format a given string by replacing the place holders (like {0}) with the
3176          * corresponding supplied arguments. For example, calling this method as follows:
3177          *     formatString("Hello {0}, {1} rocks!", "there", "Finesse");
3178          * results in the following output string:
3179          *     "Hello there, Finesse rocks!"
3180          * 
3181          * @param  {String} format - a string that holds the place holder to be replaced
3182          * 
3183          * @returns {String} - string where the place holders are replaced with respective values
3184          * @private
3185          */
3186         formatString : function(format) {
3187             if (!format || arguments.length <= 1) {
3188                 return format;
3189             }
3190 
3191             var i, retStr = format;
3192             for (i = 1; i < arguments.length; i += 1) {
3193                 retStr = retStr.replace(new RegExp("\\{" + (i - 1) + "\\}", "g"), arguments[i]);
3194             }
3195 
3196             // in order to fix French text with single quotes in it, we need to replace \' with '
3197             return retStr.replace(/\\'/g, "'");
3198         }
3199     };
3200 
3201     window.finesse = window.finesse || {};
3202     window.finesse.utilities = window.finesse.utilities || {};
3203     window.finesse.utilities.Utilities = Utilities;
3204     
3205     return Utilities;
3206 });
3207 
3208 /** The following comment is to prevent jslint errors about 
3209  * using variables before they are defined.
3210  */
3211 /*global finesse*/
3212 
3213 /**
3214  * Initiated by the Master to create a shared BOSH connection.
3215  *
3216  * @requires Utilities
3217  */
3218 
3219 /**
3220  * @class
3221  * Establishes a shared event connection by creating a communication tunnel
3222  * with the notification server and consume events which could be published.
3223  * Public functions are exposed to register to the connection status information
3224  * and events.
3225  * @constructor
3226  * @param {String} host
3227  *     The host name/ip of the Finesse server.
3228  * @throws {Error} If required constructor parameter is missing.
3229  */
3230 /** @private */
3231 define('clientservices/MasterTunnel',["utilities/Utilities"], function (Utilities) {
3232      var MasterTunnel = function (host, scheme) { 
3233         if (typeof host !== "string" || host.length === 0) {
3234             throw new Error("Required host parameter missing.");
3235         }
3236 
3237         var
3238 
3239         /**
3240          * Flag to indicate whether the tunnel frame is loaded.
3241          * @private
3242          */
3243         _isTunnelLoaded = false,
3244 
3245         /**
3246          * Short reference to the Finesse utility.
3247          * @private
3248          */
3249         _util = Utilities,
3250 
3251         /**
3252          * The URL with host and port to the Finesse server.
3253          * @private
3254          */
3255         _tunnelOrigin,
3256 
3257         /**
3258          * Location of the tunnel HTML URL.
3259          * @private
3260          */
3261         _tunnelURL,
3262         
3263         /**
3264          * The port on which to connect to the Finesse server to load the eventing resources.
3265          * @private
3266          */
3267         _tunnelOriginPort,
3268         
3269         /**
3270          * Flag to indicate whether we have processed the tunnel config yet.
3271          * @private
3272          */
3273         _isTunnelConfigInit = false,
3274 
3275         /**
3276          * The tunnel frame window object.
3277          * @private
3278          */
3279         _tunnelFrame,
3280 
3281         /**
3282          * The handler registered with the object to be invoked when an event is
3283          * delivered by the notification server.
3284          * @private
3285          */
3286         _eventHandler,
3287         
3288         /**
3289          * The handler registered with the object to be invoked when presence is
3290          * delivered by the notification server.
3291          * @private
3292          */
3293         _presenceHandler,
3294 
3295         /**
3296          * The handler registered with the object to be invoked when the BOSH
3297          * connection has changed states. The object will contain the "status"
3298          * property and a "resourceID" property only if "status" is "connected".
3299          * @private
3300          */
3301         _connInfoHandler,
3302 
3303         /**
3304          * The last connection status published by the JabberWerx library.
3305          * @private
3306          */
3307         _statusCache,
3308 
3309         /**
3310          * The last event sent by notification server.
3311          * @private
3312          */
3313         _eventCache,
3314 
3315         /**
3316          * The ID of the user logged into notification server.
3317          * @private
3318          */
3319         _id,
3320 
3321         /**
3322          * The domain of the XMPP server, representing the portion of the JID
3323          * following '@': userid@domain.com
3324          * @private
3325          */
3326         _xmppDomain,
3327 
3328         /**
3329          * The password of the user logged into notification server.
3330          * @private
3331          */
3332         _password,
3333 
3334         /**
3335          * The jid of the pubsub service on the XMPP server
3336          * @private
3337          */
3338         _pubsubDomain,
3339 
3340         /**
3341          * The resource to use for the BOSH connection.
3342          * @private
3343          */
3344         _resource,
3345 
3346         /**
3347          * The resource ID identifying the client device (that we receive from the server).
3348          * @private
3349          */
3350         _resourceID,
3351 
3352         /**
3353          * The different types of messages that could be sent to the parent frame.
3354          * The types here should be understood by the parent frame and used to
3355          * identify how the message is formatted.
3356          * @private
3357          */
3358         _TYPES = {
3359             EVENT: 0,
3360             ID: 1,
3361             PASSWORD: 2,
3362             RESOURCEID: 3,
3363             STATUS: 4,
3364             XMPPDOMAIN: 5,
3365             PUBSUBDOMAIN: 6,
3366             SUBSCRIBE: 7,
3367             UNSUBSCRIBE: 8,
3368             PRESENCE: 9,
3369             CONNECT_REQ: 10
3370         },
3371 
3372         _handlers = {
3373             subscribe: {},
3374             unsubscribe: {}
3375         },
3376         
3377 
3378         /**
3379          * Create a connection info object.
3380          * @returns {Object}
3381          *     A connection info object containing a "status" and "resourceID".
3382          * @private
3383          */
3384         _createConnInfoObj = function () {
3385             return {
3386                 status: _statusCache,
3387                 resourceID: _resourceID
3388             };
3389         },
3390 
3391         /**
3392          * Utility function which sends a message to the dynamic tunnel frame
3393          * event frame formatted as follows: "type|message".
3394          * @param {Number} type
3395          *     The category type of the message.
3396          * @param {String} message
3397          *     The message to be sent to the tunnel frame.
3398          * @private
3399          */
3400         _sendMessage = function (type, message) {
3401             message = type + "|" + message;
3402             _util.sendMessage(message, _tunnelFrame, _tunnelOrigin);
3403         },
3404 
3405         /**
3406          * Utility to process the response of a subscribe request from
3407          * the tunnel frame, then invoking the stored callback handler
3408          * with the respective data (error, when applicable)
3409          * @param {String} data
3410          *     The response in the format of "node[|error]"
3411          * @private
3412          */
3413         _processSubscribeResponse = function (data) {
3414             var dataArray = data.split("|"),
3415             node = dataArray[0],
3416             err;
3417             
3418             //Error is optionally the second item in the array
3419             if (dataArray.length) {
3420                 err = dataArray[1];
3421             }
3422             
3423             // These response handlers are short lived and should be removed and cleaned up immediately after invocation.
3424             if (_handlers.subscribe[node]) {
3425                 _handlers.subscribe[node](err);
3426                 delete _handlers.subscribe[node];
3427             }
3428         },
3429 
3430         /**
3431          * Utility to process the response of an unsubscribe request from
3432          * the tunnel frame, then invoking the stored callback handler
3433          * with the respective data (error, when applicable)
3434          * @param {String} data
3435          *     The response in the format of "node[|error]"
3436          * @private
3437          */
3438         _processUnsubscribeResponse = function (data) {
3439             var dataArray = data.split("|"),
3440             node = dataArray[0],
3441             err;
3442             
3443             //Error is optionally the second item in the array
3444             if (dataArray.length) {
3445                 err = dataArray[1];
3446             }
3447             
3448             // These response handlers are short lived and should be removed and cleaned up immediately after invocation.
3449             if (_handlers.unsubscribe[node]) {
3450                 _handlers.unsubscribe[node](err);
3451                 delete _handlers.unsubscribe[node];
3452             }
3453         },
3454 
3455         /**
3456          * Handler for messages delivered by window.postMessage. Listens for events
3457          * published by the notification server, connection status published by
3458          * the JabberWerx library, and the resource ID created when the BOSH
3459          * connection has been established.
3460          * @param {Object} e
3461          *     The message object as provided by the window.postMessage feature.
3462          * @private
3463          */
3464         _messageHandler = function (e) {
3465             var
3466 
3467             //Extract the message type and message data. The expected format is
3468             //"type|data" where type is a number represented by the TYPES object.
3469             delimPos = e.data.indexOf("|"),
3470             type = Number(e.data.substr(0, delimPos)),
3471             data =  e.data.substr(delimPos + 1);
3472             
3473             //Accepts messages and invoke the correct registered handlers.
3474             switch (type) {
3475             case _TYPES.EVENT:
3476                 _eventCache = data;
3477                 if (typeof _eventHandler === "function") {
3478                     _eventHandler(data);
3479                 }
3480                 break;
3481             case _TYPES.STATUS:
3482                 _statusCache = data;
3483 
3484                 //A "loaded" status means that the frame is ready to accept
3485                 //credentials for establishing a BOSH connection.
3486                 if (data === "loaded") {
3487                     _isTunnelLoaded = true;
3488                     if(_resource) {
3489                         _sendMessage(_TYPES.RESOURCEID, _resource);
3490                     }
3491                     _sendMessage(_TYPES.ID, _id);
3492                     _sendMessage(_TYPES.XMPPDOMAIN, _xmppDomain);
3493                     _sendMessage(_TYPES.PASSWORD, _password);
3494                     _sendMessage(_TYPES.PUBSUBDOMAIN, _pubsubDomain);
3495                 } else if (typeof _connInfoHandler === "function") {
3496                     _connInfoHandler(_createConnInfoObj());
3497                 }
3498                 break;
3499             case _TYPES.RESOURCEID:
3500                 _resourceID = data;
3501                 break;
3502             case _TYPES.SUBSCRIBE:
3503                 _processSubscribeResponse(data);
3504                 break;
3505             case _TYPES.UNSUBSCRIBE:
3506                 _processUnsubscribeResponse(data);
3507                 break;
3508             case _TYPES.PRESENCE:
3509                 if (typeof _presenceHandler === "function") {
3510                     _presenceHandler(data);
3511                 }
3512                 break;
3513             default:
3514                 break;
3515             }
3516         },
3517 
3518         /**
3519          * Initialize the tunnel config so that the url can be http or https with the appropriate port
3520          * @private
3521          */
3522         _initTunnelConfig = function () {
3523             if (_isTunnelConfigInit === true) {
3524                 return;
3525             }
3526             
3527             //Initialize tunnel origin
3528             //Determine tunnel origin based on host and scheme
3529             _tunnelOriginPort = (scheme && scheme.indexOf("https") !== -1) ? "7443" : "7071";
3530             if (scheme) {
3531                 _tunnelOrigin = scheme + "://" + host + ":" + _tunnelOriginPort;
3532             } else {
3533                 _tunnelOrigin = "http://" + host + ":" + _tunnelOriginPort;
3534             }
3535             _tunnelURL = _tunnelOrigin + "/tunnel/";
3536             
3537             _isTunnelConfigInit = true;
3538         },
3539 
3540         /**
3541          * Create the tunnel iframe which establishes the shared BOSH connection.
3542          * Messages are sent across frames using window.postMessage.
3543          * @private
3544          */
3545         _createTunnel = function () {
3546             var tunnelID = ((self === parent) ? "tunnel-frame" : "autopilot-tunnel-frame"),
3547             iframe = document.createElement("iframe");         
3548             iframe.style.display = "none";
3549             iframe.setAttribute("id", tunnelID);
3550             iframe.setAttribute("name", tunnelID);
3551             iframe.setAttribute("src", _tunnelURL);
3552             document.body.appendChild(iframe);
3553             _tunnelFrame = window.frames[tunnelID];
3554         };
3555 
3556         /**
3557          * Sends a message via postmessage to the EventTunnel to attempt to connect to the XMPP server
3558          * @private
3559          */
3560         this.makeConnectReq = function () {
3561             _sendMessage(_TYPES.PASSWORD, _password);
3562         };
3563         
3564         /**
3565          * @private
3566          * Returns the host of the Finesse server.
3567          * @returns {String}
3568          *     The host specified during the creation of the object.
3569          */
3570         this.getHost = function () {
3571             return host;
3572         };
3573 
3574         /**
3575          * @private
3576          * The resource ID of the user who is logged into the notification server.
3577          * @returns {String}
3578          *     The resource ID generated by the notification server.
3579          */
3580         this.getResourceID = function () {
3581             return _resourceID;
3582         };
3583 
3584         /**
3585          * @private
3586          * Indicates whether the tunnel frame is loaded.
3587          * @returns {Boolean}
3588          *     True if the tunnel frame is loaded, false otherwise.
3589          */
3590         this.isTunnelLoaded = function () {
3591             return _isTunnelLoaded;
3592         };
3593 
3594         /**
3595          * @private
3596          * The location of the tunnel HTML URL.
3597          * @returns {String}
3598          *     The location of the tunnel HTML URL.
3599          */
3600         this.getTunnelURL = function () {
3601             return _tunnelURL;
3602         };
3603 
3604         /**
3605          * @private
3606          * Tunnels a subscribe request to the eventing iframe.
3607          * @param {String} node
3608          *     The node to subscribe to
3609          * @param {Function} handler
3610          *     Handler to invoke upon success or failure
3611          */
3612         this.subscribe = function (node, handler) {
3613             if (handler && typeof handler !== "function") {
3614                 throw new Error("Parameter is not a function.");
3615             }
3616             _handlers.subscribe[node] = handler;
3617             _sendMessage(_TYPES.SUBSCRIBE, node);
3618         };
3619 
3620         /**
3621          * @private
3622          * Tunnels an unsubscribe request to the eventing iframe.
3623          * @param {String} node
3624          *     The node to unsubscribe from
3625          * @param {Function} handler
3626          *     Handler to invoke upon success or failure
3627          */
3628         this.unsubscribe = function (node, handler) {
3629             if (handler && typeof handler !== "function") {
3630                 throw new Error("Parameter is not a function.");
3631             }
3632             _handlers.unsubscribe[node] = handler;
3633             _sendMessage(_TYPES.UNSUBSCRIBE, node);
3634         };
3635 
3636         /**
3637          * @private
3638          * Registers a handler to be invoked when an event is delivered. Only one
3639          * is registered at a time. If there has already been an event that was
3640          * delivered, the handler will be invoked immediately.
3641          * @param {Function} handler
3642          *     Invoked when an event is delivered through the event connection.
3643          */
3644         this.registerEventHandler = function (handler) {
3645             if (typeof handler !== "function") {
3646                 throw new Error("Parameter is not a function.");
3647             }
3648             _eventHandler = handler;
3649             if (_eventCache) {
3650                 handler(_eventCache);
3651             }
3652         };
3653 
3654         /**
3655          * @private
3656          * Unregisters the event handler completely.
3657          */
3658         this.unregisterEventHandler = function () {
3659             _eventHandler = undefined;
3660         };
3661         
3662         /**
3663          * @private
3664          * Registers a handler to be invoked when a presence event is delivered. Only one
3665          * is registered at a time. 
3666          * @param {Function} handler
3667          *     Invoked when a presence event is delivered through the event connection.
3668          */
3669         this.registerPresenceHandler = function (handler) {
3670             if (typeof handler !== "function") {
3671                 throw new Error("Parameter is not a function.");
3672             }
3673             _presenceHandler = handler;
3674         };
3675         
3676         /**
3677          * @private
3678          * Unregisters the presence event handler completely.
3679          */
3680         this.unregisterPresenceHandler = function () {
3681             _presenceHandler = undefined;
3682         };
3683 
3684         /**
3685          * @private
3686          * Registers a handler to be invoked when a connection status changes. The
3687          * object passed will contain a "status" property, and a "resourceID"
3688          * property, which will contain the most current resource ID assigned to
3689          * the client. If there has already been an event that was delivered, the
3690          * handler will be invoked immediately.
3691          * @param {Function} handler
3692          *     Invoked when a connection status changes.
3693          */
3694         this.registerConnectionInfoHandler = function (handler) {
3695             if (typeof handler !== "function") {
3696                 throw new Error("Parameter is not a function.");
3697             }
3698             _connInfoHandler = handler;
3699             if (_statusCache) {
3700                 handler(_createConnInfoObj());
3701             }
3702         };
3703 
3704         /**
3705          * @private
3706          * Unregisters the connection information handler.
3707          */
3708         this.unregisterConnectionInfoHandler = function () {
3709             _connInfoHandler = undefined;
3710         };
3711 
3712         /**
3713          * @private
3714          * Start listening for events and create a event tunnel for the shared BOSH
3715          * connection.
3716          * @param {String} id
3717          *     The ID of the user for the notification server.
3718          * @param {String} password
3719          *     The password of the user for the notification server.
3720          * @param {String} xmppDomain
3721          *     The XMPP domain of the notification server
3722          * @param {String} pubsubDomain
3723          *     The location (JID) of the XEP-0060 PubSub service
3724          * @param {String} resource
3725          *     The resource to connect to the notification servier with.
3726          */
3727         this.init = function (id, password, xmppDomain, pubsubDomain, resource) {
3728             
3729             if (typeof id !== "string" || typeof password !== "string" || typeof xmppDomain !== "string" || typeof pubsubDomain !== "string") {
3730                 throw new Error("Invalid or missing required parameters.");
3731             }
3732 
3733             _initTunnelConfig();
3734             
3735             _id = id;
3736             _password = password;
3737             _xmppDomain = xmppDomain;
3738             _pubsubDomain = pubsubDomain;
3739             _resource = resource;
3740 
3741             //Attach a listener for messages sent from tunnel frame.
3742             _util.receiveMessage(_messageHandler, _tunnelOrigin);
3743 
3744             //Create the tunnel iframe which will establish the shared connection.
3745             _createTunnel();
3746         };
3747 
3748         //BEGIN TEST CODE//
3749 //        /**
3750 //         * Test code added to expose private functions that are used by unit test
3751 //         * framework. This section of code is removed during the build process
3752 //         * before packaging production code. The [begin|end]TestSection are used
3753 //         * by the build to identify the section to strip.
3754 //         * @ignore
3755 //         */
3756 //        this.beginTestSection = 0;
3757 //
3758 //        /**
3759 //         * @ignore
3760 //         */
3761 //        this.getTestObject = function () {
3762 //            //Load mock dependencies.
3763 //            var _mock = new MockControl();
3764 //            _util = _mock.createMock(finesse.utilities.Utilities);
3765 //
3766 //            return {
3767 //                //Expose mock dependencies
3768 //                mock: _mock,
3769 //                util: _util,
3770 //
3771 //                //Expose internal private functions
3772 //                types: _TYPES,
3773 //                createConnInfoObj: _createConnInfoObj,
3774 //                sendMessage: _sendMessage,
3775 //                messageHandler: _messageHandler,
3776 //                createTunnel: _createTunnel,
3777 //                handlers: _handlers,
3778 //                initTunnelConfig : _initTunnelConfig
3779 //            };
3780 //        };
3781 //
3782 //        /**
3783 //         * @ignore
3784 //         */
3785 //        this.endTestSection = 0;
3786 //        //END TEST CODE//
3787     };
3788     
3789     /** @namespace JavaScript class objects and methods to handle the subscription to Finesse events.*/
3790     finesse.clientservices = finesse.clientservices || {};
3791 
3792     window.finesse = window.finesse || {};
3793     window.finesse.clientservices = window.finesse.clientservices || {};
3794     window.finesse.clientservices.MasterTunnel = MasterTunnel;
3795 
3796     return MasterTunnel;
3797 
3798 });
3799 
3800 /**
3801  * Contains a list of topics used for client side pubsub.
3802  *
3803  */
3804 
3805 /** @private */
3806 define('clientservices/Topics',[], function () {
3807     
3808    var Topics = (function () {
3809 
3810         /**
3811          * @private
3812          * The namespace prepended to all Finesse topics.
3813          */
3814         this.namespace = "finesse.info";
3815     
3816         /**
3817          * @private
3818          * Gets the full topic name with the Finesse namespace prepended.
3819          * @param {String} topic
3820          *     The topic category.
3821          * @returns {String}
3822          *     The full topic name with prepended namespace.
3823          */
3824         var _getNSTopic = function (topic) {
3825             return this.namespace + "." + topic;
3826         };
3827         
3828         /** @scope finesse.clientservices.Topics */
3829         return {
3830             /** 
3831              * @private
3832              * Client side request channel. 
3833              */
3834             REQUESTS: _getNSTopic("requests"),
3835     
3836             /** 
3837              * @private
3838              * Client side response channel. 
3839              */
3840             RESPONSES: _getNSTopic("responses"),
3841 
3842             /** 
3843              * @private
3844              * Connection status. 
3845              */
3846             EVENTS_CONNECTION_INFO: _getNSTopic("connection"),
3847             
3848             /** 
3849              * @private
3850              * Presence channel 
3851              */
3852             PRESENCE: _getNSTopic("presence"),
3853             
3854             /**
3855              * Topic for listening to token refresh events.
3856              * The provided callback will be invoked when the access token is refreshed.
3857              * This event is only meant for updating the access token in gadget Config object
3858              */
3859             ACCESS_TOKEN_REFRESHED_EVENT: _getNSTopic("accessTokenRefresh"),
3860     
3861             /**
3862              * @private
3863              * Convert a Finesse REST URI to a OpenAjax compatible topic name.
3864              */
3865             getTopic: function (restUri) {
3866                 //The topic should not start with '/' else it will get replaced with
3867                 //'.' which is invalid.
3868                 //Thus, remove '/' if it is at the beginning of the string
3869                 if (restUri.indexOf('/') === 0) {
3870                     restUri = restUri.substr(1);
3871                 }
3872     
3873                 //Replace every instance of "/" with ".". This is done to follow the
3874                 //OpenAjaxHub topic name convention.
3875                 return restUri.replace(/\//g, ".");
3876             }
3877         };
3878     }());
3879     window.finesse = window.finesse || {};
3880     window.finesse.clientservices = window.finesse.clientservices || {};
3881     /** @private */
3882     window.finesse.clientservices.Topics = Topics;
3883     
3884     return Topics;
3885 });
3886 /** The following comment is to prevent jslint errors about 
3887  * using variables before they are defined.
3888  */
3889 /*global finesse*/
3890 
3891 /**
3892  * Registers with the MasterTunnel to receive events, which it
3893  *     could publish to the OpenAjax gadget pubsub infrastructure.
3894  *
3895  * @requires OpenAjax, finesse.clientservices.MasterTunnel, finesse.clientservices.Topics
3896  */
3897 
3898 /** @private */
3899 define('clientservices/MasterPublisher',[
3900     "clientservices/MasterTunnel",
3901     "clientservices/Topics",
3902     "utilities/Utilities"
3903 ],
3904 function (MasterTunnel, Topics, Utilities) {
3905     
3906      var MasterPublisher = function (tunnel, hub) {
3907         if (!(tunnel instanceof MasterTunnel)) {
3908             throw new Error("Required tunnel object missing or invalid.");
3909         }
3910 
3911         var
3912         
3913         ClientServices = finesse.clientservices.ClientServices,
3914 
3915         /**
3916          * Reference to the gadget pubsub Hub instance.
3917          * @private
3918          */
3919         _hub = hub,
3920 
3921         /**
3922          * Reference to the Topics class.
3923          * @private
3924          */
3925         _topics = Topics,
3926         
3927         /**
3928          * Reference to conversion utilities class.
3929          * @private
3930          */
3931         _utils = Utilities,
3932         
3933         /**
3934          * References to ClientServices logger methods
3935          * @private
3936          */
3937         _logger = {
3938             log: ClientServices.log
3939         },
3940         
3941         /**
3942          * Store the passed in tunnel.
3943          * @private
3944          */
3945         _tunnel = tunnel,
3946 
3947         /**
3948          * Caches the connection info event so that it could be published if there
3949          * is a request for it.
3950          * @private
3951          */
3952         _connInfoCache = {},
3953 
3954         /**
3955          * The types of possible request types supported when listening to the
3956          * requests channel. Each request type could result in different operations.
3957          * @private
3958          */
3959         _REQTYPES = {
3960             CONNECTIONINFO: "ConnectionInfoReq",
3961             SUBSCRIBE: "SubscribeNodeReq",
3962             UNSUBSCRIBE: "UnsubscribeNodeReq",
3963             CONNECT: "ConnectionReq"
3964         },
3965 
3966         /**
3967          * Will store list of nodes that have OF subscriptions created
3968          *     _nodesList[node][subscribing].reqIds[subid]
3969          *     _nodesList[node][active].reqIds[subid]
3970          *     _nodesList[node][unsubscribing].reqIds[subid]
3971          *     _nodesList[node][holding].reqIds[subid]
3972          * @private
3973          */
3974         _nodesList = {},
3975         
3976         /**
3977          * The states that a subscription can be in
3978          * @private
3979          */
3980         _CHANNELSTATES = {
3981             UNINITIALIZED: "Uninitialized",
3982             PENDING: "Pending",
3983             OPERATIONAL: "Operational"
3984         },
3985 
3986         /**
3987           * Checks if the payload is JSON 
3988           * @returns {Boolean}
3989           * @private
3990           */
3991         _isJsonPayload = function(event) {
3992             var delimStart, delimEnd, retval = false;
3993             
3994             try { 
3995               delimStart = event.indexOf('{');
3996               delimEnd = event.lastIndexOf('}');
3997 
3998               if ((delimStart !== -1 ) && (delimEnd === (event.length - 1))) {
3999                 retval = true;  //event contains JSON payload
4000               }
4001             } catch (err) {
4002               _logger.log("MasterPublisher._isJsonPayload() - Caught error: " + err);
4003             }
4004             return retval;
4005         },
4006         
4007                 /**
4008           * Parses a JSON event and then publishes.
4009           *
4010           * @param {String} event
4011           *     The full event payload.
4012           * @throws {Error} If the payload object is malformed.
4013           * @private
4014           */
4015         _parseAndPublishJSONEvent = function(event) {
4016             var topic, eventObj, publishEvent,
4017             delimPos = event.indexOf("{"),
4018             node, parser,
4019             eventJson = event,
4020             returnObj = {node: null, data: null};
4021 
4022             try {
4023                //Extract and strip the node path from the message
4024                if (delimPos > 0) 
4025                {
4026                   //We need to decode the URI encoded node path
4027                   //TODO: make sure this is kosher with OpenAjax topic naming
4028                   node = decodeURI(event.substr(0, delimPos));
4029                   eventJson = event.substr(delimPos);
4030                   
4031                   //Converting the node path to openAjaxhub topic
4032                   topic = _topics.getTopic(node);
4033                   
4034                   returnObj.node = node;
4035                   returnObj.payload = eventJson;
4036                } 
4037                else 
4038                {
4039                   _logger.log("MasterPublisher._parseAndPublishJSONEvent() - [ERROR] node is not given in postMessage: " + eventJson);
4040                   throw new Error("node is not given in postMessage: " + eventJson);
4041                }
4042 
4043                parser = _utils.getJSONParser();
4044 
4045                eventObj = parser.parse(eventJson);
4046                returnObj.data = eventObj;
4047 
4048             } catch (err) {
4049                _logger.log("MasterPublisher._parseAndPublishJSONEvent() - [ERROR] Malformed event payload: " + err);
4050                throw new Error("Malformed event payload : " + err);
4051             }
4052             
4053             _logger.log("MasterPublisher._parseAndPublishJSONEvent() - Received JSON event on node '" + node + "': " + eventJson); 
4054             
4055             publishEvent = {content : event, object : eventObj };
4056 
4057             //Publish event to proper event topic.
4058             if (topic && eventObj) {
4059                _hub.publish(topic, publishEvent);
4060             }
4061         },
4062         
4063         /**
4064           * Parses an XML event and then publishes.
4065           *
4066           * @param {String} event
4067           *     The full event payload.
4068           * @throws {Error} If the payload object is malformed.
4069           * @private
4070           */
4071         _parseAndPublishXMLEvent = function(event) {
4072             var topic, eventObj, publishEvent, restTopic,
4073             delimPos = event.indexOf("<"),
4074             node,
4075             eventXml = event;
4076             
4077             try {
4078                //Extract and strip the node path from the message
4079                if (delimPos > 0) {
4080                   //We need to decode the URI encoded node path
4081                   //TODO: make sure this is kosher with OpenAjax topic naming
4082                   node = decodeURI(event.substr(0, delimPos));
4083                   eventXml = event.substr(delimPos);
4084                   //Converting the node path to openAjaxhub topic
4085                   topic = _topics.getTopic(node);
4086                } else {
4087                   _logger.log("MasterPublisher._parseAndPublishXMLEvent() - [ERROR] node is not given in postMessage: " + eventXml);
4088                   throw new Error("node is not given in postMessage: " + eventXml);
4089                }
4090 
4091                eventObj = _utils.xml2JsObj(eventXml);
4092                   
4093            } catch (err) {
4094                _logger.log("MasterPublisher._parseAndPublishXMLEvent() - [ERROR] Malformed event payload: " + err);
4095                throw new Error("Malformed event payload : " + err);
4096            }
4097            
4098            _logger.log("MasterPublisher._parseAndPublishXMLEvent() - Received XML event on node '" + node + "': " + eventXml);
4099            
4100            publishEvent = {content : event, object : eventObj };
4101 
4102            //Publish event to proper event topic.
4103            if (topic && eventObj) {
4104                _hub.publish(topic, publishEvent);
4105            }
4106         },
4107         
4108         /**
4109          * Publishes events to the appropriate topic. The topic name is determined
4110          * by fetching the source value from the event.
4111          * @param {String} event
4112          *     The full event payload.
4113          * @throws {Error} If the payload object is malformed.
4114          * @private
4115          */
4116         _eventHandler = function (event) {
4117             
4118             //Handle JSON or XML events
4119             if (!_isJsonPayload(event))
4120             {
4121                //XML
4122                _parseAndPublishXMLEvent(event);
4123             }
4124             else
4125             {
4126                //JSON
4127                _parseAndPublishJSONEvent(event);
4128             }
4129         },
4130         
4131         
4132         /**
4133          * Handler for when presence events are sent through the MasterTunnel.
4134          * @returns {Object}
4135          *     A presence xml event.
4136          * @private
4137          */
4138         _presenceHandler = function (event) {
4139             var eventObj = _utils.xml2JsObj(event), publishEvent;
4140             
4141             publishEvent = {content : event, object : eventObj};
4142             
4143             if (eventObj) {
4144                 _hub.publish(_topics.PRESENCE, publishEvent);
4145             }
4146         },
4147 
4148         /**
4149          * Clone the connection info object from cache.
4150          * @returns {Object}
4151          *     A connection info object containing a "status" and "resourceID".
4152          * @private
4153          */
4154         _cloneConnInfoObj = function () {
4155             if (_connInfoCache) {
4156                 return {
4157                     status: _connInfoCache.status,
4158                     resourceID: _connInfoCache.resourceID
4159                 };
4160             } else {
4161                 return null;
4162             }
4163         },
4164 
4165         /**
4166          * Cleans up any outstanding subscribe/unsubscribe requests and notifies them of errors.
4167          * This is done if we get disconnected because we cleanup explicit subscriptions on disconnect.
4168          * @private
4169          */
4170         _cleanupPendingRequests = function () {
4171             var node, curSubid, errObj = {
4172                 error: {
4173                     errorType: "Disconnected",
4174                     errorMessage: "Outstanding request will never complete."
4175                 }
4176             };
4177 
4178             // Iterate through all outstanding subscribe requests to notify them that it will never return
4179             for (node in _nodesList) {
4180                 if (_nodesList.hasOwnProperty(node)) {
4181                     for (curSubid in _nodesList[node].subscribing.reqIds) {
4182                         if (_nodesList[node].subscribing.reqIds.hasOwnProperty(curSubid)) {
4183                             // Notify this outstanding subscribe request to give up and error out
4184                             _hub.publish(_topics.RESPONSES + "." + curSubid, errObj); 
4185                         }
4186                     }
4187                     for (curSubid in _nodesList[node].unsubscribing.reqIds) {
4188                         if (_nodesList[node].unsubscribing.reqIds.hasOwnProperty(curSubid)) {
4189                             // Notify this outstanding unsubscribe request to give up and error out
4190                             _hub.publish(_topics.RESPONSES + "." + curSubid, errObj); 
4191                         }
4192                     }
4193                 }
4194             }
4195         },
4196 
4197         /**
4198          * Publishes the connection info to the connection info topic.
4199          * @param {Object} connInfo
4200          *     The connection info object containing the status and resource ID.
4201          * @private
4202          */
4203         _connInfoHandler = function (connInfo) {
4204             var before = _connInfoCache.status;
4205             _connInfoCache = connInfo;
4206             _logger.log("MasterPublisher._connInfoHandler() - Connection status: " + connInfo.status);
4207             _hub.publish(_topics.EVENTS_CONNECTION_INFO, _cloneConnInfoObj());
4208             if (before === "connected" && connInfo.status !== "connected") {
4209                 // Fail all pending requests when we transition to disconnected
4210                 _cleanupPendingRequests();
4211             }
4212         },
4213 
4214         
4215         /**
4216          * Utility method to bookkeep node subscription requests and determine
4217          * whehter it is necessary to tunnel the request to JabberWerx.
4218          * @param {String} node
4219          *     The node of interest
4220          * @param {String} reqId
4221          *     A unique string identifying the request/subscription
4222          * @private
4223          */
4224         _subscribeNode = function (node, subid) {
4225             if (_connInfoCache.status !== "connected") {
4226                 _hub.publish(_topics.RESPONSES + "." + subid, {
4227                     error: {
4228                         errorType: "Not connected",
4229                         errorMessage: "Cannot subscribe without connection."
4230                     }
4231                 });
4232                 return;
4233             }
4234             // NODE DOES NOT YET EXIST
4235             if (!_nodesList[node]) {
4236                 _nodesList[node] = {
4237                     "subscribing": {
4238                         "reqIds": {},
4239                         "length": 0
4240                     },
4241                     "active": {
4242                         "reqIds": {},
4243                         "length": 0
4244                     },
4245                     "unsubscribing": {
4246                         "reqIds": {},
4247                         "length": 0
4248                     },
4249                     "holding": {
4250                         "reqIds": {},
4251                         "length": 0
4252                     }
4253                 };
4254             }
4255             if (_nodesList[node].active.length === 0) {
4256                 if (_nodesList[node].unsubscribing.length === 0) {
4257                     if (_nodesList[node].subscribing.length === 0) {
4258                         _nodesList[node].subscribing.reqIds[subid] = true;
4259                         _nodesList[node].subscribing.length += 1;
4260 
4261                         _logger.log("MasterPublisher._subscribeNode() - Attempting to subscribe to node '" + node + "'");
4262                         _tunnel.subscribe(node, function (err) {
4263                             var errObj, curSubid;
4264                             if (err) {
4265                                 errObj = {
4266                                     subscribe: {
4267                                         content: err
4268                                     }
4269                                 };
4270 
4271                                 try {
4272                                     errObj.subscribe.object = gadgets.json.parse((_utils.xml2json(jQuery.parseXML(err), "")));
4273                                 } catch (e) {
4274                                     errObj.error = {
4275                                         errorType: "parseError",
4276                                         errorMessage: "Could not serialize XML: " + e
4277                                     };
4278                                 }
4279                                 _logger.log("MasterPublisher._subscribeNode() - Error subscribing to node '" + node + "': " + err);
4280                             } else {
4281                                 _logger.log("MasterPublisher._subscribeNode() - Subscribed to node '" + node + "'");
4282                             }
4283 
4284                             for (curSubid in _nodesList[node].subscribing.reqIds) {
4285                                 if (_nodesList[node].subscribing.reqIds.hasOwnProperty(curSubid)) {
4286                                     _hub.publish(_topics.RESPONSES + "." + curSubid, errObj);
4287                                     if (!err) {
4288                                         _nodesList[node].active.reqIds[curSubid] = true;
4289                                         _nodesList[node].active.length += 1;
4290                                     }
4291                                     delete _nodesList[node].subscribing.reqIds[curSubid];
4292                                     _nodesList[node].subscribing.length -= 1;
4293                                 }
4294                             }
4295                         });
4296                         
4297                     } else { //other ids are subscribing
4298                         _nodesList[node].subscribing.reqIds[subid] = true;
4299                         _nodesList[node].subscribing.length += 1;
4300                     }                       
4301                 } else { //An unsubscribe request is pending, hold onto these subscribes until it is done
4302                     _nodesList[node].holding.reqIds[subid] = true;
4303                     _nodesList[node].holding.length += 1;
4304                 }
4305             } else { // The node has active subscriptions; add this subid and return successful response
4306                 _nodesList[node].active.reqIds[subid] = true;
4307                 _nodesList[node].active.length += 1;
4308                 _hub.publish(_topics.RESPONSES + "." + subid, undefined); 
4309             }
4310         },
4311 
4312         /**
4313          * Utility method to bookkeep node unsubscribe requests and determine
4314          * whehter it is necessary to tunnel the request to JabberWerx.
4315          * @param {String} node
4316          *     The node to unsubscribe from
4317          * @param {String} reqId
4318          *     A unique string identifying the subscription to remove
4319          * @private
4320          */
4321         _unsubscribeNode = function (node, subid) {
4322             if (!_nodesList[node]) { //node DNE, publish success response
4323                 _hub.publish(_topics.RESPONSES + "." + subid, undefined); 
4324             } else {
4325                 if (_connInfoCache.status !== "connected") {
4326                     _hub.publish(_topics.RESPONSES + "." + subid, {
4327                         error: {
4328                             errorType: "Not connected",
4329                             errorMessage: "Cannot unsubscribe without connection."
4330                         }
4331                     });
4332                     return;
4333                 }
4334                 if (_nodesList[node].active.length > 1) {
4335                     delete _nodesList[node].active.reqIds[subid];
4336                     _hub.publish(_topics.RESPONSES + "." + subid, undefined); 
4337                     _nodesList[node].active.length -= 1;
4338                 } else if (_nodesList[node].active.length === 1) { // transition subid from active category to unsubscribing category
4339                     _nodesList[node].unsubscribing.reqIds[subid] = true;
4340                     _nodesList[node].unsubscribing.length += 1;
4341                     delete _nodesList[node].active.reqIds[subid];
4342                     _nodesList[node].active.length -= 1;
4343 
4344                     _logger.log("MasterPublisher._unsubscribeNode() - Attempting to unsubscribe from node '" + node + "'");
4345                     _tunnel.unsubscribe(node, function (err) {
4346                         var errObj, curSubid;
4347                         if (err) {
4348                             errObj = {
4349                                 subscribe: {
4350                                     content: err
4351                                 }
4352                             };
4353 
4354                             try {
4355                                 errObj.subscribe.object = gadgets.json.parse((_utils.xml2json(jQuery.parseXML(err), "")));
4356                             } catch (e) {
4357                                 errObj.error = {
4358                                     errorType: "parseError",
4359                                     errorMessage: "Could not serialize XML: " + e
4360                                 };
4361                             }
4362                             _logger.log("MasterPublisher._unsubscribeNode() - Error unsubscribing from node '" + node + "': " + err);
4363                         } else {
4364                             _logger.log("MasterPublisher._unsubscribeNode() - Unsubscribed from node '" + node + "'");
4365                         }
4366 
4367                         for (curSubid in _nodesList[node].unsubscribing.reqIds) {
4368                             if (_nodesList[node].unsubscribing.reqIds.hasOwnProperty(curSubid)) {
4369                                 // publish to all subids whether unsubscribe failed or succeeded
4370                                 _hub.publish(_topics.RESPONSES + "." + curSubid, errObj); 
4371                                 if (!err) {
4372                                     delete _nodesList[node].unsubscribing.reqIds[curSubid];
4373                                     _nodesList[node].unsubscribing.length -= 1;
4374                                 } else { // Just remove the subid from unsubscribing; the next subscribe request will operate with node already created
4375                                     delete _nodesList[node].unsubscribing.reqIds[curSubid];
4376                                     _nodesList[node].unsubscribing.length -= 1;
4377                                 }   
4378                             }
4379                         }
4380                         
4381                         if (!err && _nodesList[node].holding.length > 0) { // if any subscribe requests came in while unsubscribing from OF, now transition from holding to subscribing
4382                             for (curSubid in _nodesList[node].holding.reqIds) {
4383                                 if (_nodesList[node].holding.reqIds.hasOwnProperty(curSubid)) {
4384                                     delete _nodesList[node].holding.reqIds[curSubid];
4385                                     _nodesList[node].holding.length -= 1;
4386                                     _subscribeNode(node, curSubid);                             
4387                                 }
4388                             }
4389                         }
4390                     });
4391                 } else { // length <= 0?
4392                     _hub.publish(_topics.RESPONSES + "." + subid, undefined);
4393                 }
4394             }
4395         },
4396         
4397         /**
4398          * Handles client requests to establish a BOSH connection.
4399          * @param {String} id
4400          *     id of the xmpp user
4401          * @param {String} password
4402          *     password of the xmpp user
4403          * @param {String} xmppDomain
4404          *     xmppDomain of the xmpp user account
4405          * @private
4406          */
4407         _connect = function (id, password, xmppDomain) {
4408             _tunnel.makeConnectReq(id, password, xmppDomain);
4409         },
4410 
4411         /**
4412          * Handles client requests made to the request topic. The type of the
4413          * request is described in the "type" property within the data payload. Each
4414          * type can result in a different operation.
4415          * @param {String} topic
4416          *     The topic which data was published to.
4417          * @param {Object} data
4418          *     The data containing requests information published by clients.
4419          * @param {String} data.type
4420          *     The type of the request. Supported: "ConnectionInfoReq"
4421          * @param {Object} data.data
4422          *     May contain data relevant for the particular requests.
4423          * @param {String} [data.invokeID]
4424          *     The ID used to identify the request with the response. The invoke ID
4425          *     will be included in the data in the publish to the topic. It is the
4426          *     responsibility of the client to correlate the published data to the
4427          *     request made by using the invoke ID.
4428          * @private
4429          */
4430         _clientRequestHandler = function (topic, data) {
4431             var dataCopy;
4432 
4433             //Ensure a valid data object with "type" and "data" properties.
4434             if (typeof data === "object" &&
4435                     typeof data.type === "string" &&
4436                     typeof data.data === "object") {
4437                 switch (data.type) {
4438                 case _REQTYPES.CONNECTIONINFO:
4439                     //It is possible that Slave clients come up before the Master
4440                     //client. If that is the case, the Slaves will need to make a
4441                     //request for the Master to send the latest connection info to the
4442                     //connectionInfo topic.
4443                     dataCopy = _cloneConnInfoObj();
4444                     if (dataCopy) {
4445                         if (data.invokeID !== undefined) {
4446                             dataCopy.invokeID = data.invokeID;
4447                         }
4448                         _hub.publish(_topics.EVENTS_CONNECTION_INFO, dataCopy);
4449                     }
4450                     break;
4451                 case _REQTYPES.SUBSCRIBE:
4452                     if (typeof data.data.node === "string") {
4453                         _subscribeNode(data.data.node, data.invokeID);
4454                     }
4455                     break;
4456                 case _REQTYPES.UNSUBSCRIBE:
4457                     if (typeof data.data.node === "string") {
4458                         _unsubscribeNode(data.data.node, data.invokeID);
4459                     }
4460                     break;
4461                 case _REQTYPES.CONNECT:
4462                     // Deprecated: Disallow others (non-masters) from administering BOSH connections that are not theirs
4463                     _logger.log("MasterPublisher._clientRequestHandler(): F403: Access denied. Non-master ClientService instances do not have the clearance level to make this request: makeConnectionReq");
4464                     break;
4465                 default:
4466                     break;
4467                 }
4468             }
4469         };
4470 
4471         (function () {
4472             //Register to receive events and connection status from tunnel.
4473             _tunnel.registerEventHandler(_eventHandler);
4474             _tunnel.registerPresenceHandler(_presenceHandler);
4475             _tunnel.registerConnectionInfoHandler(_connInfoHandler);
4476 
4477             //Listen to a request channel to respond to any requests made by other
4478             //clients because the Master may have access to useful information.
4479             _hub.subscribe(_topics.REQUESTS, _clientRequestHandler);
4480         }());
4481 
4482         /**
4483          * @private
4484          * Handles client requests to establish a BOSH connection.
4485          * @param {String} id
4486          *     id of the xmpp user
4487          * @param {String} password
4488          *     password of the xmpp user
4489          * @param {String} xmppDomain
4490          *     xmppDomain of the xmpp user account
4491          */
4492         this.connect = function (id, password, xmppDomain) {
4493           _connect(id, password, xmppDomain);
4494         };
4495 
4496         /**
4497          * @private
4498          * Resets the list of explicit subscriptions
4499          */
4500         this.wipeout = function () {
4501             _cleanupPendingRequests();
4502             _nodesList = {};
4503        };
4504 
4505         //BEGIN TEST CODE//
4506        /**
4507         * Test code added to expose private functions that are used by unit test
4508         * framework. This section of code is removed during the build process
4509         * before packaging production code. The [begin|end]TestSection are used
4510         * by the build to identify the section to strip.
4511         * @ignore
4512         */
4513        this.beginTestSection = 0;
4514 
4515        /**
4516         * @ignore
4517         */
4518        this.getTestObject = function () {
4519            //Load mock dependencies.
4520            var _mock = new MockControl();
4521            _hub = _mock.createMock(gadgets.Hub);
4522            _tunnel = _mock.createMock();
4523 
4524            return {
4525                //Expose mock dependencies
4526                mock: _mock,
4527                hub: _hub,
4528                tunnel: _tunnel,
4529                setTunnel: function (tunnel) {
4530                    _tunnel = tunnel;
4531                },
4532                getTunnel: function () {
4533                    return _tunnel;
4534                },
4535 
4536                //Expose internal private functions
4537                reqtypes: _REQTYPES,
4538                eventHandler: _eventHandler,
4539                presenceHandler: _presenceHandler,
4540                
4541                subscribeNode: _subscribeNode,
4542                unsubscribeNode: _unsubscribeNode,
4543                
4544                getNodeList: function () {
4545                    return _nodesList;
4546                },
4547                setNodeList: function (nodelist) {
4548                    _nodesList = nodelist;
4549                },
4550                
4551                cloneConnInfoObj: _cloneConnInfoObj,
4552                connInfoHandler: _connInfoHandler,
4553                clientRequestHandler: _clientRequestHandler
4554 
4555            };
4556        };
4557 
4558 
4559        /**
4560         * @ignore
4561         */
4562        this.endTestSection = 0;
4563        //END TEST CODE//
4564 
4565     };
4566     
4567     window.finesse = window.finesse || {};
4568     window.finesse.clientservices = window.finesse.clientservices || {};
4569     window.finesse.clientservices.MasterPublisher = MasterPublisher;
4570 
4571     return MasterPublisher;
4572 });
4573 
4574 /** The following comment is to prevent jslint errors about 
4575  * using variables before they are defined.
4576  */
4577 /*global publisher:true */
4578 
4579 /**
4580  * Exposes a set of API wrappers that will hide the dirty work of
4581  *     constructing Finesse API requests and consuming Finesse events.
4582  *
4583  * @requires OpenAjax, jQuery 1.5, finesse.utilities.Utilities
4584  */
4585 
4586 
4587 /**
4588  * Allow clients to make Finesse API requests and consume Finesse events by
4589  * calling a set of exposed functions. The Services layer will do the dirty
4590  * work of establishing a shared BOSH connection (for designated Master
4591  * modules), consuming events for client subscriptions, and constructing API
4592  * requests.
4593  */
4594 /** @private */
4595 define('clientservices/ClientServices',[
4596     "clientservices/MasterTunnel",
4597     "clientservices/MasterPublisher",
4598     "clientservices/Topics",
4599     "utilities/Utilities"
4600 ],
4601 function (MasterTunnel, MasterPublisher, Topics, Utilities) {
4602     
4603      var ClientServices = (function () {/** @lends finesse.clientservices.ClientServices.prototype */
4604         var
4605 
4606         /**
4607          * Shortcut reference to the master tunnel
4608          * @private
4609          */
4610         _tunnel,
4611 
4612         _publisher,
4613 
4614         /**
4615          * Shortcut reference to the finesse.utilities.Utilities singleton
4616          * This will be set by init()
4617          * @private
4618          */
4619         _util,
4620 
4621         /**
4622          * Shortcut reference to the gadgets.io object.
4623          * This will be set by init()
4624          * @private
4625          */
4626         _io,
4627 
4628         /**
4629          * Shortcut reference to the gadget pubsub Hub instance.
4630          * This will be set by init()
4631          * @private
4632          */
4633         _hub,
4634 
4635         /**
4636          * Logger object set externally by setLogger, defaults to nothing.
4637          * @private
4638          */
4639         _logger = {},
4640 
4641         /**
4642          * Shortcut reference to the Topics class.
4643          * This will be set by init()
4644          * @private
4645          */
4646         _topics,
4647 
4648         /**
4649          * Config object needed to initialize this library
4650          * This must be set by init()
4651          * @private
4652          */
4653         _config,
4654 
4655         /**
4656          * @private
4657          * Whether or not this ClientService instance is a Master.
4658          */
4659         _isMaster = false,
4660 
4661         /**
4662          * @private
4663          * Whether the Client Services have been initiated yet.
4664          */
4665         _inited = false,
4666 
4667         /**
4668          * Stores the list of subscription IDs for all subscriptions so that it
4669          * could be retrieve for unsubscriptions.
4670          * @private
4671          */
4672         _subscriptionID = {},
4673 
4674         /**
4675          * The possible states of the JabberWerx BOSH connection.
4676          * @private
4677          */
4678         _STATUS = {
4679             CONNECTING: "connecting",
4680             CONNECTED: "connected",
4681             DISCONNECTED: "disconnected",
4682             DISCONNECTED_CONFLICT: "conflict",
4683             DISCONNECTED_UNAUTHORIZED: "unauthorized",
4684             DISCONNECTING: "disconnecting",
4685             RECONNECTING: "reconnecting",
4686             UNLOADING: "unloading",
4687             FAILING: "failing",
4688             RECOVERED: "recovered"
4689         },
4690         
4691         /**
4692          * Local reference for authMode enum object.
4693          * @private
4694          */
4695         _authModes,
4696 
4697         _failoverMode = false,
4698 
4699         /**
4700          * Handler function to be invoked when BOSH connection is connecting.
4701          * @private
4702          */
4703         _onConnectingHandler,
4704 
4705         /**
4706          * Handler function to be invoked when BOSH connection is connected
4707          * @private
4708          */
4709         _onConnectHandler,
4710 
4711         /**
4712          * Handler function to be invoked when BOSH connection is disconnecting.
4713          * @private
4714          */
4715         _onDisconnectingHandler,
4716 
4717         /**
4718          * Handler function to be invoked when the BOSH is disconnected.
4719          * @private
4720          */
4721         _onDisconnectHandler,
4722 
4723         /**
4724          * Handler function to be invoked when the BOSH is reconnecting.
4725          * @private
4726          */
4727         _onReconnectingHandler,
4728         
4729         /**
4730          * Handler function to be invoked when the BOSH is unloading.
4731          * @private
4732          */
4733         _onUnloadingHandler,
4734 
4735         /**
4736          * Contains a cache of the latest connection info containing the current
4737          * state of the BOSH connection and the resource ID.
4738          * @private
4739          */
4740         _connInfo,
4741 
4742         /**
4743          * Keeps track of all the objects that need to be refreshed when we recover
4744          * due to our resilient connection. Only objects that we subscribe to will
4745          * be added to this list.
4746          * @private
4747          */
4748         _refreshList = [],
4749         
4750         /**
4751          * Needs to be passed as authorization header inside makeRequest wrapper function
4752          */
4753         _authHeaderString,
4754 
4755         /**
4756          * @private
4757          * Centralized logger.log method for external logger
4758          * @param {String} msg
4759          */
4760         _log = function (msg) {
4761             // If the external logger throws up, it stops here.
4762             try {
4763                 if (_logger.log) {
4764                     _logger.log("[ClientServices] " + msg);
4765                 }
4766             } catch (e) { }
4767         },
4768 
4769         /**
4770          * Go through each object in the _refreshList and call its refresh() function
4771          * @private
4772          */
4773         _refreshObjects = function () {
4774             var i;
4775 
4776             // wipe out the explicit subscription list before we refresh objects
4777             if (_publisher) {
4778                 _publisher.wipeout();
4779             }
4780 
4781             // refresh each item in the refresh list
4782             for (i = _refreshList.length - 1; i >= 0; i -= 1) {
4783                 _log("Refreshing " + _refreshList[i].getRestUrl());
4784                 _refreshList[i].refresh(10);
4785             }
4786         },
4787 
4788         /**
4789          * Handler to process connection info publishes.
4790          * @param {Object} data
4791          *     The connection info data object.
4792          * @param {String} data.status
4793          *     The BOSH connection status.
4794          * @param {String} data.resourceID
4795          *     The resource ID for the connection.
4796          * @private
4797          */
4798         _connInfoHandler =  function (data) {
4799 
4800             //Invoke registered handler depending on status received. Due to the
4801             //request topic where clients can make request for the Master to publish
4802             //the connection info, there is a chance that duplicate connection info
4803             //events may be sent, so ensure that there has been a state change
4804             //before invoking the handlers.
4805             if (_connInfo === undefined || _connInfo.status !== data.status) {
4806                 _connInfo = data;
4807                 switch (data.status) {
4808                 case _STATUS.CONNECTING:
4809                     if (_isMaster && _onConnectingHandler) {
4810                         _onConnectingHandler();
4811                     }
4812                     break;
4813                 case _STATUS.CONNECTED:
4814                     if ((_isMaster || !_failoverMode) && _onConnectHandler) {
4815                         _onConnectHandler();
4816                     }
4817                     break;
4818                 case _STATUS.DISCONNECTED:
4819                     if (_isMaster && _onDisconnectHandler) {
4820                         _onDisconnectHandler();
4821                     }
4822                     break;
4823                 case _STATUS.DISCONNECTED_CONFLICT:
4824                     if (_isMaster && _onDisconnectHandler) {
4825                         _onDisconnectHandler("conflict");
4826                     }
4827                     break;
4828                 case _STATUS.DISCONNECTED_UNAUTHORIZED:
4829                     if (_isMaster && _onDisconnectHandler) {
4830                         _onDisconnectHandler("unauthorized");
4831                     }
4832                     break;
4833                 case _STATUS.DISCONNECTING:
4834                     if (_isMaster && _onDisconnectingHandler) {
4835                         _onDisconnectingHandler();
4836                     }
4837                     break;
4838                 case _STATUS.RECONNECTING:
4839                     if (_isMaster && _onReconnectingHandler) {
4840                         _onReconnectingHandler();
4841                     }
4842                     break;
4843                 case _STATUS.UNLOADING:
4844                     if (_isMaster && _onUnloadingHandler) {
4845                         _onUnloadingHandler();
4846                     }
4847                     break;
4848                 case _STATUS.FAILING:
4849                     if (!_isMaster) {
4850                         // Stop
4851                         _failoverMode = true;
4852                         if (_onDisconnectHandler) {
4853                             _onDisconnectHandler();
4854                         }
4855                     }
4856                     break;
4857                 case _STATUS.RECOVERED:
4858                     if (!_isMaster) {
4859                         _failoverMode = false;
4860                         if (_onConnectHandler) {
4861                             _onConnectHandler();
4862                         }
4863                     }
4864                     // Whenever we are recovered, we need to refresh any objects
4865                     // that are stored.
4866                     _refreshObjects();
4867                     break;
4868                 }
4869             }
4870         },
4871 
4872         /**
4873          * Ensure that ClientServices have been inited.
4874          * @private
4875          */
4876         _isInited = function () {
4877             if (!_inited) {
4878                 throw new Error("ClientServices needs to be inited.");
4879             }
4880         },
4881 
4882         /**
4883          * Have the client become the Master by initiating a tunnel to a shared
4884          * event BOSH connection. The Master is responsible for publishing all
4885          * events to the pubsub infrastructure.
4886          * 
4887          * TODO: Currently we only check the global auth mode. This code has to 
4888          * handle mixed mode - in this case the user specfic SSO mode has to be
4889          * exposed via an API.
4890          * 
4891          * @private
4892          */
4893         _becomeMaster = function () {
4894             var creds , id;
4895             _tunnel = new MasterTunnel(_config.host, _config.scheme);
4896             _publisher = new MasterPublisher(_tunnel, _hub);
4897             if(_authModes.SSO === _config.systemAuthMode ) {
4898                 creds = _config.authToken;
4899             } else {
4900                 creds = _config.password;
4901             }
4902             _util = Utilities;
4903              id = _util.encodeNodeName(_config.id);
4904             _tunnel.init(id, creds, _config.xmppDomain, _config.pubsubDomain, _config.resource);
4905             _isMaster = true;
4906         },
4907 
4908         /**
4909          * Make a request to the request channel to have the Master publish the
4910          * connection info object.
4911          * @private
4912          */
4913         _makeConnectionInfoReq = function () {
4914             var data = {
4915                 type: "ConnectionInfoReq",
4916                 data: {},
4917                 invokeID: (new Date()).getTime()
4918             };
4919             _hub.publish(_topics.REQUESTS, data);
4920         },
4921 
4922         /**
4923          * Utility method to register a handler which is associated with a
4924          * particular connection status.
4925          * @param {String} status
4926          *     The connection status string.
4927          * @param {Function} handler
4928          *     The handler to associate with a particular connection status.
4929          * @throws {Error}
4930          *     If the handler provided is not a function.
4931          * @private
4932          */
4933         _registerHandler = function (status, handler) {
4934             if (typeof handler === "function") {
4935                 if (_connInfo && _connInfo.status === status) {
4936                     handler();
4937                 }
4938                 switch (status) {
4939                 case _STATUS.CONNECTING:
4940                     _onConnectingHandler = handler;
4941                     break;
4942                 case _STATUS.CONNECTED:
4943                     _onConnectHandler = handler;
4944                     break;
4945                 case _STATUS.DISCONNECTED:
4946                     _onDisconnectHandler = handler;
4947                     break;
4948                 case _STATUS.DISCONNECTING:
4949                     _onDisconnectingHandler = handler;
4950                     break;
4951                 case _STATUS.RECONNECTING:
4952                     _onReconnectingHandler = handler;
4953                     break;
4954                 case _STATUS.UNLOADING:
4955                     _onUnloadingHandler = handler;
4956                     break;
4957                 }
4958 
4959             } else {
4960                 throw new Error("Callback is not a function");
4961             }
4962         },
4963         
4964         /**
4965          * Callback function that is called when a refresh access token event message is posted to the Hub.
4966          *
4967          * Get access token from the data and update the finesse.gadget.Config object!
4968          *
4969          * @param {String} topic
4970          *      which topic the event came on (unused)
4971          * @param {Object} data
4972          *      the data published with the event
4973          * @private
4974          */
4975         _accessTokenRefreshHandler = function(topic , data){
4976             _log("Access token refreshed - topic :" + topic + ", authToken :" + data.authToken);
4977             
4978             if(data.authToken){
4979                _config.authToken = data.authToken;
4980                if(finesse.gadget && finesse.gadget.Config){
4981                    finesse.gadget.Config.authToken = data.authToken;
4982                }
4983             }
4984          },
4985         
4986         /**
4987          * @private
4988          * Retrieves systemAuthMode from parent Finesse Container. If parent is not available, mode will be retrieved from the systemInfo rest object
4989          * @throws {Error}
4990          *     If unable to retrieve systemAuthMode
4991          */
4992         _getSystemAuthMode = function(){
4993           var parentFinesse , sysInfo;
4994           // For gadgets hosted outside of finesse container , finesse parent object will not be available
4995             try{
4996                 parentFinesse = window.parent.finesse;
4997             } catch (e){
4998                 parentFinesse = undefined;
4999             }
5000 
5001             if( parentFinesse ){
5002                _config.systemAuthMode =  parentFinesse.container.Config.systemAuthMode; 
5003             } else {
5004                sysInfo = new finesse.restservices.SystemInfo({
5005                    id: _config.id,
5006                    onLoad: function (systemInfo) {
5007                         _config.systemAuthMode = systemInfo.getSystemAuthMode();
5008                    },
5009                    onError: function (errRsp) {
5010                         throw new Error("Unable to retrieve systemAuthMode from config. Initialization failed......");
5011                    }
5012               });
5013                        
5014             }
5015        };
5016 
5017         return {
5018 
5019             /**
5020              * @private
5021              * Adds an item to the list to be refreshed upon reconnect
5022              * @param {RestBase} object - rest object to be refreshed
5023              */
5024             addToRefreshList: function (object) {
5025                 _refreshList.push(object);
5026             },
5027 
5028             /**
5029              * @private
5030              * Removes the given item from the refresh list
5031              * @param  {RestBase} object - rest object to be removed
5032              */
5033             removeFromRefreshList: function (object) {
5034                 var i;
5035                 for (i = _refreshList.length - 1; i >= 0; i -= 1) {
5036                     if (_refreshList[i] === object) {
5037                         _refreshList.splice(i, 1);
5038                         break;
5039                     }
5040                 }
5041             },
5042 
5043             /**
5044              * @private
5045              * The location of the tunnel HTML URL.
5046              * @returns {String}
5047              *     The location of the tunnel HTML URL.
5048              */
5049             getTunnelURL: function () {
5050                 return _tunnel.getTunnelURL();            
5051             },
5052             
5053             /**
5054              * @private
5055              * Indicates whether the tunnel frame is loaded.
5056              * @returns {Boolean}
5057              *     True if the tunnel frame is loaded, false otherwise.
5058              */
5059             isTunnelLoaded: function () {
5060                 return _tunnel.isTunnelLoaded();            
5061             },
5062             
5063             /**
5064              * @private
5065              * Indicates whether the ClientServices instance is a Master.
5066              * @returns {Boolean}
5067              *     True if this instance of ClientServices is a Master, false otherwise.
5068              */
5069             isMaster: function () {
5070                 return _isMaster;
5071             },
5072 
5073             /**
5074              * @private
5075              * Get the resource ID. An ID is only available if the BOSH connection has
5076              * been able to connect successfully.
5077              * @returns {String}
5078              *     The resource ID string. Null if the BOSH connection was never
5079              *     successfully created and/or the resource ID has not been associated.
5080              */
5081             getResourceID: function () {
5082                 if (_connInfo !== undefined) {
5083                     return _connInfo.resourceID;
5084                 }
5085                 return null;
5086             },
5087             
5088             /*
5089             getHub: function () {
5090                 return _hub;
5091             },
5092         */
5093             /**
5094              * @private
5095              * Add a callback to be invoked when the BOSH connection is attempting
5096              * to connect. If the connection is already trying to connect, the
5097              * callback will be invoked immediately.
5098              * @param {Function} handler
5099              *      An empty param function to be invoked on connecting. Only one
5100              *      handler can be registered at a time. Handlers already registered
5101              *      will be overwritten.
5102              */
5103             registerOnConnectingHandler: function (handler) {
5104                 _registerHandler(_STATUS.CONNECTING, handler);
5105             },
5106 
5107             /**
5108              * @private
5109              * Removes the on connecting callback that was registered.
5110              */
5111             unregisterOnConnectingHandler: function () {
5112                 _onConnectingHandler = undefined;
5113             },
5114 
5115             /**
5116              * Add a callback to be invoked when all of the following conditions are met:
5117              * <ul>
5118              *   <li>When Finesse goes IN_SERVICE</li>
5119              *   <li>The BOSH connection is established</li>
5120              *   <li>The Finesse user presence becomes available</li>
5121              * </ul>
5122              * If all these conditions are met at the time this function is called, then
5123              * the handler will be invoked immediately.
5124              * @param {Function} handler
5125              *      An empty param function to be invoked on connect. Only one handler
5126              *      can be registered at a time. Handlers already registered will be
5127              *      overwritten.
5128              * @example
5129              *      finesse.clientservices.ClientServices.registerOnConnectHandler(gadget.myCallback);
5130              */
5131             registerOnConnectHandler: function (handler) {
5132                 _registerHandler(_STATUS.CONNECTED, handler);
5133             },
5134 
5135             /**
5136              * @private
5137              * Removes the on connect callback that was registered.
5138              */
5139             unregisterOnConnectHandler: function () {
5140                 _onConnectHandler = undefined;
5141             },
5142 
5143             /**
5144              * Add a callback to be invoked when any of the following occurs:
5145              * <ul>
5146              *   <li>Finesse is no longer IN_SERVICE</li>
5147              *   <li>The BOSH connection is lost</li>
5148              *   <li>The presence of the Finesse user is no longer available</li>
5149              * </ul>
5150              * If any of these conditions are met at the time this function is
5151              * called, the callback will be invoked immediately.
5152              * @param {Function} handler
5153              *      An empty param function to be invoked on disconnected. Only one
5154              *      handler can be registered at a time. Handlers already registered
5155              *      will be overwritten.
5156              * @example
5157              *      finesse.clientservices.ClientServices.registerOnDisconnectHandler(gadget.myCallback);
5158              */
5159             registerOnDisconnectHandler: function (handler) {
5160                 _registerHandler(_STATUS.DISCONNECTED, handler);
5161             },
5162 
5163             /**
5164              * @private
5165              * Removes the on disconnect callback that was registered.
5166              */
5167             unregisterOnDisconnectHandler: function () {
5168                 _onDisconnectHandler = undefined;
5169             },
5170 
5171             /**
5172              * @private
5173              * Add a callback to be invoked when the BOSH is currently disconnecting. If
5174              * the connection is already disconnecting, invoke the callback immediately.
5175              * @param {Function} handler
5176              *      An empty param function to be invoked on disconnected. Only one
5177              *      handler can be registered at a time. Handlers already registered
5178              *      will be overwritten.
5179              */
5180             registerOnDisconnectingHandler: function (handler) {
5181                 _registerHandler(_STATUS.DISCONNECTING, handler);
5182             },
5183 
5184             /**
5185              * @private
5186              * Removes the on disconnecting callback that was registered.
5187              */
5188             unregisterOnDisconnectingHandler: function () {
5189                 _onDisconnectingHandler = undefined;
5190             },
5191 
5192             /**
5193              * @private
5194              * Add a callback to be invoked when the BOSH connection is attempting
5195              * to connect. If the connection is already trying to connect, the
5196              * callback will be invoked immediately.
5197              * @param {Function} handler
5198              *      An empty param function to be invoked on connecting. Only one
5199              *      handler can be registered at a time. Handlers already registered
5200              *      will be overwritten.
5201              */
5202             registerOnReconnectingHandler: function (handler) {
5203                 _registerHandler(_STATUS.RECONNECTING, handler);
5204             },
5205 
5206             /**
5207              * @private
5208              * Removes the on reconnecting callback that was registered.
5209              */
5210             unregisterOnReconnectingHandler: function () {
5211                 _onReconnectingHandler = undefined;
5212             },
5213             
5214             /**
5215              * @private
5216              * Add a callback to be invoked when the BOSH connection is unloading
5217              * 
5218              * @param {Function} handler
5219              *      An empty param function to be invoked on connecting. Only one
5220              *      handler can be registered at a time. Handlers already registered
5221              *      will be overwritten.
5222              */
5223             registerOnUnloadingHandler: function (handler) {
5224                 _registerHandler(_STATUS.UNLOADING, handler);
5225             },
5226             
5227             /**
5228              * @private
5229              * Removes the on unloading callback that was registered.
5230              */
5231             unregisterOnUnloadingHandler: function () {
5232                 _onUnloadingHandler = undefined;
5233             },
5234 
5235             /**
5236              * @private
5237              * Proxy method for gadgets.io.makeRequest. The will be identical to gadgets.io.makeRequest
5238              * ClientServices will mixin the BASIC Auth string, locale, and host, since the
5239              * configuration is encapsulated in here anyways.
5240              * This removes the dependency
5241              * @param {String} url
5242              *     The relative url to make the request to (the host from the passed in config will be
5243              *     appended). It is expected that any encoding to the URL is already done.
5244              * @param {Function} handler
5245              *     Callback handler for makeRequest to invoke when the response returns.
5246              *     Completely passed through to gadgets.io.makeRequest
5247              * @param {Object} params
5248              *     The params object that gadgets.io.makeRequest expects. Authorization and locale
5249              *     headers are mixed in.
5250              */
5251             makeRequest: function (url, handler, params) {
5252                 var requestedScheme, scheme = "http";
5253                 
5254                 // ClientServices needs to be initialized with a config for restHost, auth, and locale
5255                 _isInited();
5256                 
5257                 // Allow mixin of auth and locale headers
5258                 params = params || {};
5259 
5260                 // Override refresh interval to 0 instead of default 3600 as a way to workaround makeRequest 
5261                 // using GET http method because then the params are added to the url as query params, which 
5262                 // exposes the authorization string in the url. This is a placeholder until oauth comes in
5263                 params[gadgets.io.RequestParameters.REFRESH_INTERVAL] = params[gadgets.io.RequestParameters.REFRESH_INTERVAL] || 0;
5264                 
5265                 params[gadgets.io.RequestParameters.HEADERS] = params[gadgets.io.RequestParameters.HEADERS] || {};
5266                 
5267                 // Add Basic auth to request header
5268                 params[gadgets.io.RequestParameters.HEADERS].Authorization = _util.getAuthHeaderString(_config);
5269 
5270                 //Locale
5271                 params[gadgets.io.RequestParameters.HEADERS].locale = _config.locale;
5272 
5273                 //Allow clients to override the scheme:
5274                 //  - If not specified  => we use HTTP
5275                 //  - If null specified => we use _config.scheme
5276                 //  - Otherwise         => we use whatever they provide
5277                 requestedScheme = params.SCHEME; 
5278                 if (!(requestedScheme === undefined || requestedScheme === "undefined")) {
5279                     if (requestedScheme === null) {
5280                        scheme = _config.scheme;
5281                     } else {
5282                        scheme = requestedScheme;
5283                     }
5284                 }
5285                 scheme = _config.restScheme || scheme;
5286                 
5287                 _log("RequestedScheme: " + requestedScheme + "; Scheme: " + scheme);
5288                 gadgets.io.makeRequest(encodeURI(scheme + "://" + _config.restHost + ":" + _config.localhostPort) + url, handler, params);
5289             },
5290 
5291             /**
5292              * @private
5293              * Utility function to make a subscription to a particular topic. Only one
5294              * callback function is registered to a particular topic at any time.
5295              * @param {String} topic
5296              *     The full topic name. The topic name should follow the OpenAjax
5297              *     convention using dot notation (ex: finesse.api.User.1000).
5298              * @param {Function} callback
5299              *     The function that should be invoked with the data when an event
5300              *     is delivered to the specific topic.
5301              * @returns {Boolean}
5302              *     True if the subscription was made successfully and the callback was
5303              *     been registered. False if the subscription already exist, the
5304              *     callback was not overwritten.
5305              */
5306             subscribe: function (topic, callback, disableDuringFailover) {
5307                 _isInited();
5308 
5309                 //Ensure that the same subscription isn't made twice.
5310                 if (!_subscriptionID[topic]) {
5311                     //Store the subscription ID using the topic name as the key.
5312                     _subscriptionID[topic] = _hub.subscribe(topic,
5313                         //Invoke the callback just with the data object.
5314                         function (topic, data) {
5315                             if (!disableDuringFailover || _isMaster || !_failoverMode) {
5316                                 // Halt all intermediate event processing while the master instance attempts to rebuild the connection. This only occurs:
5317                                 // - For RestBase objects (which pass the disableDuringFailover flag), so that intermediary events while recovering are hidden away until everything is all good
5318                                 //    - We shouldn't be halting anything else because we have infrastructure requests that use the hub pub sub
5319                                 // - Master instance will get all events regardless, because it is responsible for managing failover
5320                                 // - If we are not in a failover mode, everything goes
5321                                 // _refreshObjects will reconcile anything that was missed once we are back in action
5322                                 callback(data);
5323                             } 
5324                         });
5325                     return true;
5326                 }
5327                 return false;
5328             },
5329 
5330             /**
5331              * @private
5332              * Unsubscribe from a particular topic.
5333              * @param {String} topic
5334              *     The full topic name.
5335              */
5336             unsubscribe: function (topic) {
5337                 _isInited();
5338 
5339                 //Unsubscribe from the topic using the subscription ID recorded when
5340                 //the subscription was made, then delete the ID from data structure.
5341                 if (_subscriptionID[topic]) {
5342                     _hub.unsubscribe(_subscriptionID[topic]);
5343                     delete _subscriptionID[topic];
5344                 }
5345             },
5346 
5347             /**
5348              * @private
5349              * Make a request to the request channel to have the Master subscribe
5350              * to a node.
5351              * @param {String} node
5352              *     The node to subscribe to.
5353              */
5354             subscribeNode: function (node, handler) {
5355                 if (handler && typeof handler !== "function") {
5356                     throw new Error("ClientServices.subscribeNode: handler is not a function");
5357                 }
5358                 
5359                 // Construct the request to send to MasterPublisher through the OpenAjax Hub
5360                 var data = {
5361                     type: "SubscribeNodeReq",
5362                     data: {node: node},
5363                     invokeID: _util.generateUUID()
5364                 },
5365                 responseTopic = _topics.RESPONSES + "." + data.invokeID,
5366                 _this = this;
5367 
5368                 // We need to first subscribe to the response channel
5369                 this.subscribe(responseTopic, function (rsp) {
5370                     // Since this channel is only used for this singular request,
5371                     // we are not interested anymore.
5372                     // This is also critical to not leaking memory by having OpenAjax
5373                     // store a bunch of orphaned callback handlers that enclose on
5374                     // our entire ClientServices singleton
5375                     _this.unsubscribe(responseTopic);
5376                     if (handler) {
5377                         handler(data.invokeID, rsp);
5378                     }
5379                 });
5380                 // Then publish the request on the request channel
5381                 _hub.publish(_topics.REQUESTS, data);
5382             },
5383 
5384             /**
5385              * @private
5386              * Make a request to the request channel to have the Master unsubscribe
5387              * from a node.
5388              * @param {String} node
5389              *     The node to unsubscribe from.
5390              */
5391             unsubscribeNode: function (node, subid, handler) {
5392                 if (handler && typeof handler !== "function") {
5393                     throw new Error("ClientServices.unsubscribeNode: handler is not a function");
5394                 }
5395                 
5396                 // Construct the request to send to MasterPublisher through the OpenAjax Hub
5397                 var data = {
5398                     type: "UnsubscribeNodeReq",
5399                     data: {
5400                         node: node,
5401                         subid: subid
5402                     },
5403                     invokeID: _util.generateUUID()
5404                 },
5405                 responseTopic = _topics.RESPONSES + "." + data.invokeID,
5406                 _this = this;
5407 
5408                 // We need to first subscribe to the response channel
5409                 this.subscribe(responseTopic, function (rsp) {
5410                     // Since this channel is only used for this singular request,
5411                     // we are not interested anymore.
5412                     // This is also critical to not leaking memory by having OpenAjax
5413                     // store a bunch of orphaned callback handlers that enclose on
5414                     // our entire ClientServices singleton
5415                     _this.unsubscribe(responseTopic);
5416                     if (handler) {
5417                         handler(rsp);
5418                     }
5419                 });
5420                 // Then publish the request on the request channel
5421                 _hub.publish(_topics.REQUESTS, data);
5422             },
5423             
5424             /**
5425              * @private
5426              * Make a request to the request channel to have the Master connect to the XMPP server via BOSH
5427              */
5428             makeConnectionReq : function () {
5429                 // Disallow others (non-masters) from administering BOSH connections that are not theirs
5430                 if (_isMaster && _publisher) {
5431                     _publisher.connect(_config.id, _config.password, _config.xmppDomain);
5432                 } else {
5433                     _log("F403: Access denied. Non-master ClientService instances do not have the clearance level to make this request: makeConnectionReq");
5434                 }
5435             },
5436         
5437             /**
5438              * @private
5439              * Set's the global logger for this Client Services instance.
5440              * @param {Object} logger
5441              *     Logger object with the following attributes defined:<ul>
5442              *         <li><b>log:</b> function (msg) to simply log a message
5443              *     </ul>
5444              */
5445             setLogger: function (logger) {
5446                 // We want to check the logger coming in so we don't have to check every time it is called.
5447                 if (logger && typeof logger === "object" && typeof logger.log === "function") {
5448                     _logger = logger;
5449                 } else {
5450                     // We are resetting it to an empty object so that _logger.log in .log is falsy.
5451                     _logger = {};
5452                 }
5453             },
5454             
5455             /**
5456              * @private
5457              * Centralized logger.log method for external logger
5458              * @param {String} msg
5459              *     Message to log
5460              */
5461             log: _log,
5462 
5463             /**
5464              * @class
5465              * Allow clients to make Finesse API requests and consume Finesse events by
5466              * calling a set of exposed functions. The Services layer will do the dirty
5467              * work of establishing a shared BOSH connection (for designated Master
5468              * modules), consuming events for client subscriptions, and constructing API
5469              * requests.
5470              * 
5471              * @constructs
5472              */
5473             _fakeConstuctor: function () {
5474                 /* This is here so we can document init() as a method rather than as a constructor. */
5475             },
5476             
5477             /**
5478              * Initiates the Client Services with the specified config parameters.
5479              * Enabling the Client Services as Master will trigger the establishment
5480              * of a BOSH event connection.
5481              * @param {finesse.gadget.Config} config
5482              *     Configuration object containing properties used for making REST requests:<ul>
5483              *         <li><b>host:</b> The Finesse server IP/host as reachable from the browser
5484              *         <li><b>restHost:</b> The Finesse API IP/host as reachable from the gadget container
5485              *         <li><b>id:</b> The ID of the user. This is an optional param as long as the
5486              *         appropriate authorization string is provided, otherwise it is
5487              *         required.</li>
5488              *         <li><b>password:</b> The password belonging to the user. This is an optional param as
5489              *         long as the appropriate authorization string is provided,
5490              *         otherwise it is required.</li>
5491              *         <li><b>authorization:</b> The base64 encoded "id:password" authentication string. This
5492              *         param is provided to allow the ability to hide the password
5493              *         param. If provided, the id and the password extracted from this
5494              *         string will be used over the config.id and config.password.</li>
5495              *     </ul>
5496              * @throws {Error} If required constructor parameter is missing.
5497              * @example
5498              *      finesse.clientservices.ClientServices.init(finesse.gadget.Config);
5499              */
5500             init: function (config) {
5501                 if (!_inited) {
5502                     //Validate the properties within the config object if one is provided.
5503                     if (!(typeof config === "object" &&
5504                          typeof config.host === "string" && config.host.length > 0 && config.restHost && 
5505                          (typeof config.authorization === "string" ||
5506                                  (typeof config.id === "string")))) {
5507                         throw new Error("Config object contains invalid properties.");
5508                     }
5509 
5510                     // Initialize configuration
5511                     _config = config;
5512 
5513                     // Set shortcuts
5514                     _util = Utilities;
5515                     _authModes = _util.getAuthModes();
5516                     _topics = Topics;
5517                     
5518                     //TODO: document when this is properly supported
5519                     // Allows hub and io dependencies to be passed in. Currently only used for unit tests.
5520                     _hub = config.hub || gadgets.Hub;
5521                     _io = config.io || gadgets.io;
5522 
5523                     //If the authorization string is provided, then use that to
5524                     //extract the ID and the password. Otherwise use the ID and
5525                     //password from the respective ID and password params.
5526                     if (_config.authorization) {
5527                         var creds = _util.getCredentials(_config.authorization);
5528                         _config.id = creds.id;
5529                         _config.password = creds.password;
5530                     }
5531                     else {
5532                         _config.authorization = _util.b64Encode(
5533                                 _config.id + ":" + _config.password);
5534                     }
5535                     
5536                     //In case if gadgets create their own config instance , add systemAuthMode property inside config object
5537                     if(!_config.systemAuthMode || _config.systemAuthMode === ""){
5538                         _getSystemAuthMode();
5539                     }
5540                     
5541                     if(_config.systemAuthMode === _authModes.SSO){
5542                         _accessTokenRefreshHandler(undefined , {authToken : _util.getToken()});                        
5543                         if(!_config.authToken){
5544                             throw new Error("ClientServices.init() - Access token is unavailable inside Config object.");
5545                         }
5546                           
5547                         if (_hub){
5548                               _hub.subscribe(_topics.ACCESS_TOKEN_REFRESHED_EVENT, _accessTokenRefreshHandler);
5549                         }
5550                     }
5551                     
5552                     _inited = true;
5553 
5554                     if (_hub) {
5555                         //Subscribe to receive connection information. Since it is possible that
5556                         //the client comes up after the Master comes up, the client will need
5557                         //to make a request to have the Master send the latest connection info.
5558                         //It would be possible that all clients get connection info again.
5559                         this.subscribe(_topics.EVENTS_CONNECTION_INFO, _connInfoHandler);
5560                         _makeConnectionInfoReq();
5561                     }
5562                 }
5563 
5564                 //Return the CS object for object chaining.
5565                 return this;
5566             },
5567 
5568             /**
5569              * @private
5570              * Initializes the BOSH component of this ClientServices instance. This establishes
5571              * the BOSH connection and will trigger the registered handlers as the connection
5572              * status changes respectively:<ul>
5573              *     <li>registerOnConnectingHandler</li>
5574              *     <li>registerOnConnectHandler</li>
5575              *     <li>registerOnDisconnectHandler</li>
5576              *     <li>registerOnDisconnectingHandler</li>
5577              *     <li>registerOnReconnectingHandler</li>
5578              *     <li>registerOnUnloadingHandler</li>
5579              * <ul>
5580              *
5581              * @param {Object} config
5582              *     An object containing the following (optional) handlers for the request:<ul>
5583              *         <li><b>xmppDomain:</b> {String} The domain of the XMPP server. Available from the SystemInfo object.
5584              *         This is used to construct the JID: user@domain.com</li>
5585              *         <li><b>pubsubDomain:</b> {String} The pub sub domain where the pub sub service is running.
5586              *         Available from the SystemInfo object.
5587              *         This is used for creating or removing subscriptions.</li>
5588              *         <li><b>resource:</b> {String} The resource to connect to the notification server with.</li>
5589              *     </ul>
5590              */
5591             initBosh: function (config) {
5592                 //Validate the properties within the config object if one is provided.
5593                 if (!(typeof config === "object" && typeof config.xmppDomain === "string" && typeof config.pubsubDomain === "string")) {
5594                     throw new Error("Config object contains invalid properties.");
5595                 }
5596                 
5597                 // Mixin the required information for establishing the BOSH connection
5598                 _config.xmppDomain = config.xmppDomain;
5599                 _config.pubsubDomain = config.pubsubDomain;
5600                 _config.resource = config.resource;
5601                 
5602                 //Initiate Master launch sequence
5603                 _becomeMaster(); 
5604             },
5605 
5606             /**
5607              * @private
5608              * Sets the failover mode to either FAILING or RECOVERED. This will only occur in the master instance and is meant to be
5609              * used by a stereotypical failover monitor type module to notify non-master instances (i.e. in gadgets)
5610              * @param {Object} failoverMode
5611              *     true if failing, false or something falsy when recovered
5612              */
5613             setFailoverMode: function (failoverMode) {
5614                 if (_isMaster) {
5615                     _hub.publish(_topics.EVENTS_CONNECTION_INFO, {
5616                         status: (failoverMode ? _STATUS.FAILING : _STATUS.RECOVERED)
5617                     });
5618                 }
5619             },
5620 
5621             /**
5622              * @private
5623              * Private accessor used to inject mocked private dependencies for unit testing
5624              */
5625             _getTestObj: function () {
5626                 return {
5627                     setPublisher: function (publisher) {
5628                         _publisher = publisher;
5629                     }
5630                 };
5631             }
5632         };
5633     }());
5634      
5635     window.finesse = window.finesse || {};
5636     window.finesse.clientservices = window.finesse.clientservices || {};
5637     window.finesse.clientservices.ClientServices = ClientServices;
5638     
5639     return ClientServices;
5640 
5641 });
5642 /**
5643  * The following comment prevents JSLint errors concerning undefined global variables.
5644  * It tells JSLint that these identifiers are defined elsewhere.
5645  */
5646 /*jslint bitwise:true, browser:true, nomen:true, regexp:true, sloppy:true, white:true */
5647 
5648 /** The following comment is to prevent jslint errors about 
5649  * using variables before they are defined.
5650  */
5651 /*global Handlebars */
5652 
5653 /**
5654  * JavaScript class to implement common notification
5655  *               functionality.
5656  * 
5657  * @requires Class
5658  * @requires finesse.FinesseBase
5659  */
5660 /** @private */
5661 define('restservices/Notifier',[
5662     'FinesseBase',
5663     'clientservices/ClientServices'
5664 ],
5665 function (FinesseBase, ClientServices) {
5666     var Notifier = FinesseBase.extend({
5667 		/**
5668          * Initializes the notifier object.
5669          */
5670         init : function () {
5671             this._super();
5672             this._listenerCallback = [];
5673         },
5674 
5675         /**
5676          * Add a listener.
5677          * 
5678          * @param callback_function
5679          * @param scope
5680          *            is the callback function to add
5681          */
5682         addListener : function (callback_function, scope) {
5683             var len = this._listenerCallback.length, i, cb, add = true;
5684             for (i = 0; i < len; i += 1) {
5685                 cb = this._listenerCallback[i].callback;
5686                 if (cb === callback_function) {
5687                     // this callback already exists
5688                     add = false;
5689                     break;
5690                 }
5691             }
5692             if (add) {
5693                 this._listenerCallback.push({ "callback": this._isAFunction(callback_function), "scope": (scope || window) });
5694             }            
5695         },
5696 
5697         /**
5698          * Remove a listener.
5699          * 
5700          * @param callback_function
5701          *            is the callback function to remove
5702          * @return {Boolean} true if removed
5703          */
5704         removeListener : function (callback_function) {
5705 
5706             var result = false, len = this._listenerCallback.length, i, cb;
5707             for (i = len - 1; i >= 0; i -=1) {
5708                 cb = this._listenerCallback[i].callback;
5709                 if (cb === callback_function) {
5710                     this._listenerCallback[i] = undefined;
5711                     this._listenerCallback.splice(i, 1);
5712                     result = true;
5713                     break;
5714                 }
5715             }
5716             
5717             return result;
5718         },
5719 
5720         /**
5721 	 * Removes all listeners
5722 	 * @return {undefined}
5723 	 */
5724 	reset: function () {
5725 		this._listenerCallback = [];
5726 	},
5727 
5728 	/**
5729          * Notify all listeners.
5730          * 
5731          * @param obj
5732          *            is the object that has changed
5733          */
5734         notifyListeners : function (obj) {
5735             var len = this._listenerCallback.length, i, callbackFunction, scope;
5736 
5737             for (i = 0; i < len; i += 1) {
5738                 // Be sure that one bad callback does not prevent other listeners
5739                 // from receiving.
5740                 try {
5741                     callbackFunction = this._listenerCallback[i].callback;
5742                     scope = this._listenerCallback[i].scope;
5743                     if (typeof callbackFunction === 'function') {
5744                         callbackFunction.call(scope, obj);
5745                     }
5746                 } catch (err) {
5747                     ClientServices.log("Exception caught: " + err);
5748                 }
5749             }
5750         },
5751 
5752         /**
5753          * Gets a copy of the listeners.
5754          * @return changeListenerCopy (array of callbacks)
5755          */
5756         getListeners : function () {
5757             var changeListenerCopy = [], len = this._listenerCallback.length, i;
5758 
5759             for (i = 0; i < len; i += 1) {
5760                 changeListenerCopy.push(this._listenerCallback[i].callback);
5761             }
5762 
5763             return changeListenerCopy;
5764         },
5765         
5766         /**
5767          * Verifies that the handler is function.
5768          * @param handler to verify
5769          * @return the handler 
5770          * @throws Error if not a function
5771          */
5772         _isAFunction : function (handler) {
5773             if (handler === undefined || typeof handler === "function") {
5774                 return handler;
5775             } else {
5776                 throw new Error("handler must be a function");
5777             }
5778         }
5779 	});
5780 	
5781 	window.finesse = window.finesse || {};
5782 	window.finesse.restservices = window.finesse.restservices || {};
5783 	window.finesse.restservices.Notifier = Notifier;
5784 
5785 	/** @namespace JavaScript classes and methods that represent REST objects and collections. */
5786     finesse.restservices = finesse.restservices || {};
5787 	
5788     return Notifier;
5789 });
5790 
5791 /**
5792  * JavaScript base object that all REST objects should inherit
5793  * from because it encapsulates and provides the common functionality that
5794  * all REST objects need.
5795  *
5796  * @requires finesse.clientservices.ClientServices
5797  * @requires Class
5798  */
5799 
5800 /** @private */
5801 define('restservices/RestBase',[
5802     "FinesseBase",
5803     "utilities/Utilities",
5804     "restservices/Notifier",
5805     "clientservices/ClientServices",
5806     "clientservices/Topics"
5807 ],
5808 function (FinesseBase, Utilities, Notifier, ClientServices, Topics) {
5809     
5810     var RestBase = FinesseBase.extend(/** @lends finesse.restservices.RestBase.prototype */{
5811 
5812         doNotLog: false,        
5813 
5814         /**
5815          * Used by _processUpdate() and restRequest().
5816          * Maps requestIds to object-wrapped callbacks passed to restRequest(),
5817          * so that one of the callbacks can be fired when a corresponding event is
5818          * received inside _processUpdate().
5819          * @private
5820          */
5821         _pendingCallbacks: {},
5822         
5823         /**
5824          * Gets the REST class for the current object.  This object throws an
5825          * exception because subtype must implement.
5826          * @throws {Error} because subtype must implement
5827          * @private
5828          */
5829         getRestClass: function () {
5830             throw new Error("getRestClass(): Not implemented in subtype.");
5831         },
5832 
5833         /**
5834          * Gets the REST type for the current object.  This object throws an
5835          * exception because subtype must implement.
5836          * @throws {Error} because subtype must implement.
5837          * @private
5838          */
5839         getRestType: function () {
5840             throw new Error("getRestType(): Not implemented in subtype.");
5841         },
5842 
5843         /**
5844          * Gets the node path for the current object.
5845          * @private
5846          */
5847         getXMPPNodePath: function () {
5848             return this.getRestUrl();
5849         },
5850         
5851         /**
5852          * Boolean function that specifies whether the REST object supports
5853          * requests. True by default. Subclasses should override if false.
5854          * @private
5855          */
5856         supportsRequests: true,
5857 
5858         /**
5859          * Boolean function that specifies whether the REST object supports
5860          * subscriptions. True by default. Subclasses should override if false.
5861          * @private
5862          */
5863         supportsSubscriptions: true,
5864         
5865         /**
5866          * Boolean function that specifies whether the REST object should retain
5867          * a copy of the REST response. False by default. Subclasses should override if true.
5868          * @private
5869          */
5870         keepRestResponse: false,
5871 
5872         /**
5873          * Number that represents the REST Response status that is returned. Only
5874          * set if keepRestResponse is set to true.
5875          * @public
5876          */
5877         restResponseStatus: null,
5878 
5879         /**
5880          * Object to store additional headers to be sent with the REST Request, null by default.
5881          * @private
5882          */
5883         extraHeaders: null,
5884 
5885         /**
5886          * Boolean function that specifies whether the REST object explicitly
5887          * subscribes. False by default. Subclasses should override if true.
5888          * @private
5889          */
5890         explicitSubscription: false,
5891 
5892         /**
5893          * Boolean function that specifies whether subscribing should be
5894          * automatically done at construction. Defaults to true.
5895          * This be overridden at object construction, not by implementing subclasses
5896          * @private
5897          */
5898         autoSubscribe: true,
5899         
5900         /**
5901          * Private reference to default logger
5902          * @private
5903          */
5904         _logger: {
5905             log: ClientServices.log,
5906             error: ClientServices.log
5907         },
5908 
5909         /**
5910          * @class
5911          * JavaScript representation of a REST object. Also exposes methods to operate
5912          * on the object against the server.  This object is typically extended into individual
5913          * REST Objects (like Dialog, User, etc...), and shouldn't be used directly.
5914          *
5915          * @constructor
5916          * @param {String} id
5917          *     The ID that uniquely identifies the REST object.
5918          * @param {Object} callbacks
5919          *     An object containing callbacks for instantiation and runtime
5920          *     Callback to invoke upon successful instantiation, passes in REST object.
5921          * @param {Function} callbacks.onLoad(this)
5922          *     Callback to invoke upon loading the data for the first time.
5923          * @param {Function} callbacks.onChange(this)
5924          *     Callback to invoke upon successful update object (PUT)
5925          * @param {Function} callbacks.onAdd(this)
5926          *     Callback to invoke upon successful update to add object (POST)
5927          * @param {Function} callbacks.onDelete(this)
5928          *     Callback to invoke upon successful update to delete object (DELETE)
5929          * @param {Function} callbacks.onError(rsp)
5930          *     Callback to invoke on update error (refresh or event)
5931          *     as passed by finesse.restservices.RestBase.restRequest()
5932          *     {
5933          *         status: {Number} The HTTP status code returned
5934          *         content: {String} Raw string of response
5935          *         object: {Object} Parsed object of response
5936          *         error: {Object} Wrapped exception that was caught
5937          *         error.errorType: {String} Type of error that was caught
5938          *         error.errorMessage: {String} Message associated with error
5939          *     }
5940          * @param {RestBase} [restObj]
5941          *     A RestBase parent object which this object has an association with.
5942          * @constructs
5943          */
5944         init: function (options, callbacks, restObj) {
5945             /**
5946               * Initialize the base class
5947               */
5948             var _this = this;
5949 
5950             this._super();
5951 
5952             if (typeof options === "object") {
5953                 this._id = options.id;
5954                 this._restObj = options.parentObj;
5955                 this.autoSubscribe = (options.autoSubscribe === false) ? false : true;
5956                 this.doNotSubscribe = options.doNotSubscribe;
5957                 this.doNotRefresh = this.doNotRefresh || options.doNotRefresh;
5958                 callbacks = {
5959                     onLoad: options.onLoad,
5960                     onChange: options.onChange,
5961                     onAdd: options.onAdd,
5962                     onDelete: options.onDelete,
5963                     onError: options.onError
5964                 };
5965             } else {
5966                 this._id = options;
5967                 this._restObj = restObj;
5968             }
5969             
5970             // Common stuff
5971             
5972             this._data = {};
5973             
5974             //Contains the full rest response to be processed by upper layers if needed
5975             this._restResponse = undefined;
5976 
5977             this._lastUpdate = {};
5978 
5979             this._util = Utilities;
5980 
5981             //Should be correctly initialized in either a window OR gadget context
5982             this._config = finesse.container.Config;
5983 
5984             // Setup all the notifiers - change, load and error.
5985             this._changeNotifier = new Notifier();
5986             this._loadNotifier = new Notifier();
5987             this._addNotifier = new Notifier();
5988             this._deleteNotifier = new Notifier();
5989             this._errorNotifier = new Notifier();
5990 
5991             this._loaded = false;
5992 
5993             // Protect against null dereferencing of options allowing its
5994             // (nonexistent) keys to be read as undefined
5995             callbacks = callbacks || {};
5996 
5997             this.addHandler('load', callbacks.onLoad);
5998             this.addHandler('change', callbacks.onChange);
5999             this.addHandler('add', callbacks.onAdd);
6000             this.addHandler('delete', callbacks.onDelete);
6001             this.addHandler('error', callbacks.onError);
6002 
6003             // Attempt to get the RestType then synchronize
6004             try {
6005                 this.getRestType();
6006 
6007                 // Only subscribe if this REST object supports subscriptions
6008                 // and autoSubscribe was not requested to be disabled as a construction option
6009                 if (this.supportsSubscriptions && this.autoSubscribe && !this.doNotSubscribe) {
6010                     this.subscribe({
6011                         success: function () {
6012                             //TODO: figure out how to use Function.call() or Function.apply() here...
6013                             //this is exactly the same as the below else case other than the scope of "this"
6014                             if (typeof options === "object" && options.data) {
6015                                 if (!_this._processObject(_this._normalize(options.data))) {
6016                                     // notify of error if we fail to construct
6017                                     _this._errorNotifier.notifyListeners(_this);
6018                                 }
6019                             } else {
6020                                 // Only subscribe if this REST object supports requests
6021                                 if (_this.supportsRequests) {
6022                                     _this._synchronize();
6023                                 }
6024                             }
6025                         },
6026                         error: function (err) {
6027                             _this._errorNotifier.notifyListeners(err);
6028                         }
6029                     });
6030                 } else {
6031                     if (typeof options === "object" && options.data) {
6032                         if (!this._processObject(this._normalize(options.data))) {
6033                             // notify of error if we fail to construct
6034                             this._errorNotifier.notifyListeners(this);
6035                         }
6036                     } else {
6037                         // Only subscribe if this REST object supports requests
6038                         if (this.supportsRequests) {
6039                             this._synchronize();
6040                         }
6041                     }
6042                 }
6043 
6044             } catch (err) {
6045                 this._logger.error('id=' + this._id + ': ' + err);
6046             }
6047         },
6048 
6049         /**
6050          * Determines if the object has a particular property.
6051          * @param obj is the object to examine
6052          * @param property is the property to check for
6053          * @returns {Boolean}
6054          */
6055         hasProperty: function (obj, prop) {
6056             return (obj !== null) && (obj.hasOwnProperty(prop));
6057         },
6058 
6059         /**
6060          * Gets a property from the object.
6061          * @param obj is the object to examine
6062          * @param property is the property to get
6063          * @returns {Property Value} or {Null} if not found
6064          */
6065         getProperty: function (obj, property) {
6066             var result = null;
6067 
6068             if (this.hasProperty(obj, property) === false) {
6069                 result = null;
6070             } else {
6071                 result = obj[property];
6072             }
6073             return result;
6074         },
6075 
6076         /**
6077          * Utility to extracts the ID from the specified REST URI. This is with the
6078          * assumption that the ID is always the last element in the URI after the
6079          * "/" delimiter.
6080          * @param {String} restUri
6081          *     The REST uri (i.e. /finesse/api/User/1000).
6082          * @private
6083          */
6084         _extractId: function (restObj) {
6085             var obj, restUri = "", strLoc;
6086             for (obj in restObj) {
6087                 if (restObj.hasOwnProperty(obj)) {
6088                     restUri = restObj[obj].uri;
6089                     break;
6090                 }
6091             }
6092             return Utilities.getId(restUri);
6093         },
6094 
6095         /**
6096          * Gets the data for this object.
6097          * @returns {Object} which is contained in data
6098          */
6099         getData: function () {
6100             return this._data;
6101         },
6102         
6103         /**
6104          * Gets the complete REST response to the request made
6105          * @returns {Object} which is contained in data
6106          * @private
6107          */
6108         getRestResponse: function () {
6109             return this._restResponse;
6110         },
6111 
6112         /**
6113          * The REST URL in which this object can be referenced.
6114          * @return {String}
6115          *     The REST URI for this object.
6116          * @private
6117          */
6118         getRestUrl: function () {
6119             var
6120             restObj = this._restObj,
6121             restUrl = "";
6122 
6123             //Prepend the base REST object if one was provided.
6124             if (restObj instanceof RestBase) {
6125                 restUrl += restObj.getRestUrl();
6126             }
6127             //Otherwise prepend with the default webapp name.
6128             else {
6129                 restUrl += "/finesse/api";
6130             }
6131 
6132             //Append the REST type.
6133             restUrl += "/" + this.getRestType();
6134 
6135             //Append ID if it is not undefined, null, or empty.
6136             if (this._id) {
6137                 restUrl += "/" + this._id;
6138             }
6139             return restUrl;
6140         },
6141 
6142         /**
6143          * Getter for the id of this RestBase
6144          * @returns {String}
6145          *     The id of this RestBase
6146          */
6147         getId: function () {
6148             return this._id;
6149         },
6150 
6151         /**
6152          * Synchronize this object with the server using REST GET request.
6153          * @returns {Object}
6154          *     {
6155          *         abort: {function} Function that signifies the callback handler to NOT process the response of the rest request
6156          *     }
6157          * @private
6158          */
6159         _synchronize: function (retries) {
6160             // Fetch this REST object
6161             if (typeof this._id === "string") {
6162                 var _this = this, isLoaded = this._loaded, _RETRY_INTERVAL = 10 * 1000;
6163 
6164                 return this._doGET(
6165                     {
6166                         success: function (rsp) {
6167                             if (!_this._processResponse(rsp)) {
6168                                 if (retries > 0) {
6169                                     setTimeout(function () {
6170                                         _this._synchronize(retries - 1);
6171                                     }, _RETRY_INTERVAL);
6172                                 } else {
6173                                     _this._errorNotifier.notifyListeners(_this);
6174                                 }
6175                             } else {
6176                                 // If this object was already "loaded" prior to
6177                                 // the _doGET request, then call the
6178                                 // changeNotifier
6179                                 if (isLoaded) {
6180                                     _this._changeNotifier.notifyListeners(_this);
6181                                 }
6182                             }
6183                         },
6184                         error: function (rsp) {
6185                             if (retries > 0) {
6186                                 setTimeout(function () {
6187                                     _this._synchronize(retries - 1);
6188                                 }, _RETRY_INTERVAL);
6189                                 
6190                             } else {
6191                                 _this._errorNotifier.notifyListeners(rsp);
6192                             }
6193                         }
6194                     }
6195                 );
6196 
6197             } else {
6198                 throw new Error("Can't construct a <" + this.getRestType() + "> due to invalid id type.");
6199             }
6200         },
6201 
6202         /**
6203          * Adds an handler to this object.
6204          * If notifierType is 'load' and the object has already loaded, the callback is invoked immediately
6205          * @param {String} notifierType
6206          *     The type of notifier to add to ('load', 'change', 'add', 'delete', 'error')
6207          * @param {Function} callback
6208          *     The function callback to invoke.
6209          * @example
6210          *   // Handler for additions to the Dialogs collection object.  
6211          *   // When Dialog (a RestBase object) is created, add a change handler.
6212          *   _handleDialogAdd = function(dialog) {
6213          *      dialog.addHandler('change', _handleDialogChange);
6214          *   }
6215          */
6216         addHandler: function (notifierType, callback, scope) {
6217             var notifier = null;
6218             try {
6219                 Utilities.validateHandler(callback);
6220 
6221                 notifier = this._getNotifierReference(notifierType);
6222 
6223                 notifier.addListener(callback, scope);
6224                 
6225                 // If load handler is added and object has
6226                 // already been loaded, invoke callback
6227                 // immediately
6228                 if (notifierType === 'load' && this._loaded) {
6229                     callback.call((scope || window), this);
6230                 }
6231             } catch (err) {
6232                 this._logger.error('id=' + this._id + ': ' + err);
6233             }
6234         },
6235 
6236         /**
6237          * Removes a handler from this object.
6238          * @param {String} notifierType
6239          *     The type of notifier to remove ('load', 'change', 'add', 'delete', 'error')
6240          * @param {Function} callback
6241          *     The function to remove.
6242          */
6243         removeHandler: function (notifierType, callback) {
6244             var notifier = null;
6245             try {
6246                 Utilities.validateHandler(callback);
6247 
6248                 notifier = this._getNotifierReference(notifierType);
6249 
6250                 if (typeof(callback) === "undefined")
6251                 {
6252                     // Remove all listeners for the type
6253                     notifier.reset();
6254                 }
6255                 else
6256                 {
6257                     // Remove the specified listener
6258                     finesse.utilities.Utilities.validateHandler(callback);
6259                     notifier.removeListener(callback);
6260                 }
6261             } catch (err) {
6262                 this._logger.error('id=' + this._id + ': ' + err);
6263             }
6264         },
6265 
6266         /**
6267          * Utility method gating any operations that require complete instantiation
6268          * @throws Error
6269          *     If this object was not fully instantiated yet
6270          * @returns {finesse.restservices.RestBase}
6271          *     This RestBase object to allow cascading
6272          */
6273         isLoaded: function () {
6274             if (!this._loaded) {
6275                 throw new Error("Cannot operate on object that is not fully instantiated, use onLoad/onLoadError handlers");
6276             }
6277             return this; // Allow cascading
6278         },
6279 
6280 
6281 
6282         /**
6283          * Force an update on this object. Since an asynchronous GET is performed,
6284          * it is necessary to have an onChange handler registered in order to be
6285          * notified when the response of this returns.
6286          * @param {Integer} retries
6287          *    The number or retry attempts to make.
6288          * @returns {Object}
6289          *     {
6290          *         abort: {function} Function that signifies the callback handler to NOT process the response of the asynchronous request
6291          *     }
6292          */
6293         refresh: function (retries) {
6294             var _this = this;
6295 
6296             if (this.explicitSubscription) {
6297                 this._subscribeNode({
6298                     success: function () {
6299                         //Disallow GETs if object doesn't support it.
6300                         if (!_this.supportsRequests) {
6301                             throw new Error("Object doesn't support request operations.");
6302                         }
6303 
6304                         _this._synchronize(retries);
6305 
6306                         return this; // Allow cascading
6307                     },
6308                     error: function (err) {
6309                         _this._errorNotifier.notifyListeners(err);
6310                     }
6311                 });
6312             } else {
6313                 //Disallow GETs if object doesn't support it.
6314                 if (!this.supportsRequests) {
6315                     throw new Error("Object doesn't support request operations.");
6316                 }
6317 
6318                 return this._synchronize(retries);
6319             }
6320         },
6321 
6322         /**
6323          * Utility method to validate against the known schema of this RestBase
6324          * @param {Object} obj
6325          *     The object to validate
6326          * @returns {Boolean}
6327          *     True if the object fits the schema of this object. This usually
6328          *     means all required keys or nested objects are present.
6329          *     False otherwise.
6330          * @private
6331          */
6332         _validate: function (obj) {
6333             var valid = (typeof obj === "object" && this.hasProperty(obj, this.getRestType()));
6334             if (!valid)
6335             {
6336                 this._logger.error(this.getRestType() + " failed validation! Does your JS REST class return the correct string from getRestType()?");
6337             }
6338             return valid;
6339         },
6340 
6341         /**
6342          * Utility method to fetch this RestBase from the server
6343          * @param {finesse.interfaces.RequestHandlers} handlers
6344          *     An object containing the handlers for the request
6345          * @returns {Object}
6346          *     {
6347          *         abort: {function} Function that signifies the callback handler to NOT process the response of the rest request
6348          *     }
6349          * @private
6350          */
6351         _doGET: function (handlers) {
6352             return this.restRequest(this.getRestUrl(), handlers);
6353         },
6354 
6355         /**
6356          * Common update event handler used by the pubsub callback closure.
6357          * Processes the update event then notifies listeners.
6358          * @param {Object} scope
6359          *     An object containing callbacks to handle the asynchronous get
6360          * @param {Object} update
6361          *     An object containing callbacks to handle the asynchronous get
6362          * @private
6363          */
6364         _updateEventHandler: function (scope, update) {
6365             if (scope._processUpdate(update)) {
6366                 switch (update.object.Update.event) {
6367                 case "POST":
6368                     scope._addNotifier.notifyListeners(scope);
6369                     break;
6370                 case "PUT":
6371                     scope._changeNotifier.notifyListeners(scope);
6372                     break;
6373                 case "DELETE":
6374                     scope._deleteNotifier.notifyListeners(scope);
6375                     break;
6376                 }
6377             }   
6378         },
6379 
6380         /**
6381          * Utility method to create a callback to be given to OpenAjax to invoke when a message
6382          * is published on the topic of our REST URL (also XEP-0060 node).
6383          * This needs to be its own defined method so that subclasses can have their own implementation.
6384          * @returns {Function} callback(update)
6385          *     The callback to be invoked when an update event is received. This callback will
6386          *     process the update and notify listeners.
6387          * @private
6388          */
6389         _createPubsubCallback: function () {
6390             var _this = this;
6391             return function (update) {
6392                 _this._updateEventHandler(_this, update);
6393             };
6394         },
6395 
6396         /**
6397          * Subscribe to pubsub infra using the REST URL as the topic name.
6398          * @param {finesse.interfaces.RequestHandlers} handlers
6399          *     An object containing the handlers for the request
6400          * @private
6401          */
6402         subscribe: function (callbacks) {
6403             // Only need to do a subscription to client pubsub. No need to trigger
6404             // a subscription on the Finesse server due to implicit subscribe (at
6405             // least for now).
6406             var _this = this,
6407             topic = Topics.getTopic(this.getXMPPNodePath()),
6408             handlers,
6409             successful = ClientServices.subscribe(topic, this._createPubsubCallback(), true);
6410 
6411             callbacks = callbacks || {};
6412 
6413             handlers = {
6414                 /** @private */
6415                 success: function () {
6416                     // Add item to the refresh list in ClientServices to refresh if
6417                     // we recover due to our resilient connection. However, do
6418                     // not add if doNotRefresh flag is set.
6419                     if (!_this.doNotRefresh) {
6420                         ClientServices.addToRefreshList(_this);
6421                     }
6422 
6423                     if (typeof callbacks.success === "function") {
6424                         callbacks.success();
6425                     }
6426                 },
6427                 /** @private */
6428                 error: function (err) {
6429                     if (successful) {
6430                         ClientServices.unsubscribe(topic);
6431                     }
6432 
6433                     if (typeof callbacks.error === "function") {
6434                         callbacks.error(err);
6435                     }
6436                 }
6437             };
6438 
6439             // Request a node subscription only if this object requires explicit subscriptions
6440             if (this.explicitSubscription === true) {
6441                 this._subscribeNode(handlers);
6442             } else {
6443                 if (successful) {
6444                     this._subid = "OpenAjaxOnly";
6445                     handlers.success();
6446                 } else {
6447                     handlers.error();
6448                 }
6449             }
6450 
6451             return this;
6452         },
6453 
6454         /**
6455          * Unsubscribe to pubsub infra using the REST URL as the topic name.
6456          * @param {finesse.interfaces.RequestHandlers} handlers
6457          *     An object containing the handlers for the request
6458          * @private
6459          */
6460         unsubscribe: function (callbacks) {
6461             // Only need to do a subscription to client pubsub. No need to trigger
6462             // a subscription on the Finesse server due to implicit subscribe (at
6463             // least for now).
6464             var _this = this,
6465             topic = Topics.getTopic(this.getRestUrl()),
6466             handlers;
6467 
6468             // no longer keep track of object to refresh on reconnect
6469             ClientServices.removeFromRefreshList(_this);
6470 
6471             callbacks = callbacks || {};
6472 
6473             handlers = {
6474                 /** @private */
6475                 success: function () {
6476                     if (typeof callbacks.success === "function") {
6477                         callbacks.success();
6478                     }
6479                 },
6480                 /** @private */
6481                 error: function (err) {
6482                     if (typeof callbacks.error === "function") {
6483                         callbacks.error(err);
6484                     }
6485                 }
6486             };
6487 
6488             if (this._subid) {
6489                 ClientServices.unsubscribe(topic);
6490                 // Request a node unsubscribe only if this object requires explicit subscriptions
6491                 if (this.explicitSubscription === true) {
6492                     this._unsubscribeNode(handlers);
6493                 } else {
6494                     this._subid = undefined;
6495                     handlers.success();
6496                 }
6497             } else {
6498                 handlers.success();
6499             }
6500 
6501             return this;
6502         },
6503 
6504         /**
6505          * Private utility to perform node subscribe requests for explicit subscriptions
6506          * @param {finesse.interfaces.RequestHandlers} handlers
6507          *     An object containing the handlers for the request
6508          * @private
6509          */
6510         _subscribeNode: function (callbacks) {
6511             var _this = this;
6512 
6513             // Protect against null dereferencing of callbacks allowing its (nonexistent) keys to be read as undefined
6514             callbacks = callbacks || {};
6515 
6516             ClientServices.subscribeNode(this.getXMPPNodePath(), function (subid, err) {
6517                 if (err) {
6518                     if (typeof callbacks.error === "function") {
6519                         callbacks.error(err);
6520                     }
6521                 } else {
6522                     // Store the subid on a successful subscribe
6523                     _this._subid = subid;
6524                     if (typeof callbacks.success === "function") {
6525                         callbacks.success();
6526                     }
6527                 }
6528             });
6529         },
6530 
6531         /**
6532          * Private utility to perform node unsubscribe requests for explicit subscriptions
6533          * @param {finesse.interfaces.RequestHandlers} handlers
6534          *     An object containing the handlers for the request
6535          * @private
6536          */
6537         _unsubscribeNode: function (callbacks) {
6538             var _this = this;
6539 
6540             // Protect against null dereferencing of callbacks allowing its (nonexistent) keys to be read as undefined
6541             callbacks = callbacks || {};
6542 
6543             ClientServices.unsubscribeNode(this.getXMPPNodePath(), this._subid, function (err) {
6544                 _this._subid = undefined;
6545                 if (err) {
6546                     if (typeof callbacks.error === "function") {
6547                         callbacks.error(err);
6548                     }
6549                 } else {
6550                     if (typeof callbacks.success === "function") {
6551                         callbacks.success();
6552                     }
6553                 }
6554             });
6555         },
6556 
6557         /**
6558          * Validate and store the object into the internal data store.
6559          * @param {Object} object
6560          *     The JavaScript object that should match of schema of this REST object.
6561          * @returns {Boolean}
6562          *     True if the object was validated and stored successfully.
6563          * @private
6564          */
6565         _processObject: function (object) {
6566             if (this._validate(object)) {
6567                 this._data = this.getProperty(object, this.getRestType()); // Should clone the object here?
6568 
6569                 // If loaded for the first time, call the load notifiers.
6570                 if (!this._loaded) {
6571                     this._loaded = true;
6572                     this._loadNotifier.notifyListeners(this);
6573                 }
6574 
6575                 return true;
6576             }
6577             return false;
6578         },
6579 
6580         /**
6581          * Normalize the object to mitigate the differences between the backend
6582          * and what this REST object should hold. For example, the backend sends
6583          * send an event with the root property name being lower case. In order to
6584          * match the GET, the property should be normalized to an upper case.
6585          * @param {Object} object
6586          *     The object which should be normalized.
6587          * @returns {Object}
6588          *     Return the normalized object.
6589          * @private
6590          */
6591         _normalize: function (object) {
6592             var
6593             restType = this.getRestType(),
6594             // Get the REST object name with first character being lower case.
6595             objRestType = restType.charAt(0).toLowerCase() + restType.slice(1);
6596 
6597             // Normalize payload to match REST object. The payload for an update
6598             // use a lower case object name as oppose to upper case. Only normalize
6599             // if necessary.
6600             if (!this.hasProperty(object, restType) && this.hasProperty(object, objRestType)) {
6601                 //Since the object is going to be modified, clone the object so that
6602                 //it doesn't affect others (due to OpenAjax publishing to other
6603                 //subscriber.
6604                 object = jQuery.extend(true, {}, object);
6605 
6606                 object[restType] = object[objRestType];
6607                 delete(object[objRestType]);
6608             }
6609             return object;
6610         },
6611 
6612         /**
6613          * Utility method to process the response of a successful get
6614          * @param {Object} rsp
6615          *     The response of a successful get
6616          * @returns {Boolean}
6617          *     True if the update was successfully processed (the response object
6618          *     passed the schema validation) and updated the internal data cache,
6619          *     false otherwise.
6620          * @private
6621          */
6622         _processResponse: function (rsp) {
6623             try {
6624                 if (this.keepRestResponse) {
6625                     this._restResponse = rsp.content;
6626                     this.restResponseStatus = rsp.status;
6627                 }
6628                 return this._processObject(rsp.object);
6629             }
6630             catch (err) {
6631                 this._logger.error(this.getRestType() + ': ' + err);
6632             }
6633             return false;
6634         },
6635 
6636         /**
6637          * Method that is called at the end of _processUpdate() which by default
6638          * will just delete the requestId-to-callbacks mapping but can be overridden.
6639          * @param  {String} requestId The requestId of the event
6640          */
6641         _postProcessUpdateStrategy: function (requestId) {
6642             //Clean up _pendingCallbacks now that we fired a callback corresponding to the received requestId.
6643             delete this._pendingCallbacks[requestId];
6644         },
6645 
6646         /**
6647          * Utility method to process the update notification.
6648          * @param {Object} update
6649          *     The payload of an update notification.
6650          * @returns {Boolean}
6651          *     True if the update was successfully processed (the update object
6652          *     passed the schema validation) and updated the internal data cache,
6653          *     false otherwise.
6654          * @private
6655          */
6656         _processUpdate: function (update) {
6657             try {
6658                 var updateObj, requestId, fakeResponse, receivedError;
6659 
6660                 // The backend will send the data object with a lower case. To be
6661                 // consistent with what should be represented in this object, the
6662                 // object name should be upper case. This will normalize the object.
6663                 updateObj = this._normalize(update.object.Update.data);
6664 
6665                 // Store the last event.
6666                 this._lastUpdate = update.object;
6667 
6668                 requestId = this._lastUpdate.Update ? this._lastUpdate.Update.requestId : undefined;
6669 
6670                 if (requestId && this._pendingCallbacks[requestId]) {
6671 
6672                     /*
6673                      * The passed success/error callbacks are expecting to be passed an AJAX response, so construct
6674                      * a simulated/"fake" AJAX response object from the information in the received event.
6675                      * The constructed object should conform to the contract for response objects specified
6676                      * in _createAjaxHandler().
6677                      */
6678                     fakeResponse = {};
6679 
6680                     //The contract says that rsp.content should contain the raw text of the response so we simulate that here.
6681                     //For some reason json2xml has trouble with the native update object, so we serialize a clone of it by
6682                     //doing a parse(stringify(update)).
6683                     fakeResponse.content = this._util.json2xml(gadgets.json.parse(gadgets.json.stringify(update)));
6684 
6685                     fakeResponse.object = {};
6686 
6687                     if (updateObj.apiErrors && updateObj.apiErrors.apiError) { //Error case
6688 
6689                         //TODO: The lowercase -> uppercase ApiErrors translation method below is undesirable, can it be improved?
6690                         receivedError = updateObj.apiErrors.apiError;
6691                         fakeResponse.object.ApiErrors = {};
6692                         fakeResponse.object.ApiErrors.ApiError = {};
6693                         fakeResponse.object.ApiErrors.ApiError.ErrorData = receivedError.errorData || undefined;
6694                         fakeResponse.object.ApiErrors.ApiError.ErrorMessage = receivedError.errorMessage || undefined;
6695                         fakeResponse.object.ApiErrors.ApiError.ErrorType = receivedError.errorType || undefined;
6696 
6697                         /*
6698                          * Since this is the error case, supply the error callback with a '400 BAD REQUEST' status code. We don't know what the real
6699                          * status code should be since the event we're constructing fakeResponse from doesn't include a status code.
6700                          * This is just to conform to the contract for the error callback in _createAjaxHandler().
6701                          **/
6702                         fakeResponse.status = 400;
6703 
6704                     } else { //Success case
6705 
6706                         fakeResponse.object = this._lastUpdate;
6707 
6708                         /*
6709                          * Since this is the success case, supply the success callback with a '200 OK' status code. We don't know what the real
6710                          * status code should be since the event we're constructing fakeResponse from doesn't include a status code.
6711                          * This is just to conform to the contract for the success callback in _createAjaxHandler().
6712                          **/
6713                         fakeResponse.status = 200;
6714                     }
6715 
6716                     try {
6717 
6718                         if (fakeResponse.object.ApiErrors && this._pendingCallbacks[requestId].error) {
6719                             this._pendingCallbacks[requestId].error(fakeResponse);
6720                         } 
6721                         // HTTP 202 is handled as a success, besides, we cannot infer that a non-error is a success.
6722                         /*else if (this._pendingCallbacks[requestId].success) {
6723                             this._pendingCallbacks[requestId].success(fakeResponse);
6724                         }*/
6725 
6726                     } catch (callbackErr) {
6727 
6728                         this._logger.error(this.getRestType() + ": Caught error while firing callback: " + callbackErr);
6729 
6730                     }
6731 
6732                     this._postProcessUpdateStrategy(requestId);
6733 
6734                 } 
6735 
6736                 return this._processObject(updateObj);
6737             }
6738             catch (err) {
6739                 this._logger.error(this.getRestType() + ': ' + err);
6740             }
6741             return false;
6742         },
6743 
6744         /**
6745          * Utility method to create ajax response handler closures around the
6746          * provided callbacks. Callbacks should be passed through from .ajax().
6747          * makeRequest is responsible for garbage collecting these closures.
6748          * @param {finesse.interfaces.RequestHandlers} handlers
6749          *     An object containing the handlers for the request
6750          * @returns {Object}
6751          *     {
6752          *         abort: {function} Function that signifies the callback handler to NOT process the response when the response returns
6753          *         callback: {function} Callback handler to be invoked when the response returns
6754          *     }
6755          * @private
6756          */
6757         _createAjaxHandler: function (options) {
6758             //We should not need to check this again since it has already been done in .restRequest()
6759             //options = options || {};
6760 
6761             //Flag to indicate whether or not to process the response
6762             var abort = false,
6763 
6764             //Get a reference to the parent User object
6765             _this = this;
6766 
6767             return {
6768 
6769                 abort: function () {
6770                     abort = true;
6771                 },
6772 
6773                 callback: function (rsp) {
6774 
6775                     if (abort) {
6776                         // Do not process the response
6777                         return;
6778                     }
6779 
6780                     var requestId, error = false, rspObj;
6781 
6782                     if (options.success || options.error) {
6783                         rspObj = {
6784                             status: rsp.rc,
6785                             content: rsp.text,
6786                             isUnsent: rsp.isUnsent
6787                         };
6788 
6789                         if (!_this.doNotLog) {
6790                         	_this._logger.log(_this.getRestType() + ": requestId='" + options.uuid + "', Returned with status=" + rspObj.status + ", content='" + rspObj.content + "', isUnsent = " + rspObj.isUnsent);
6791                         }
6792 
6793                         //Some responses may not have a body.
6794                         if (rsp.text && rsp.text.length > 0) {
6795                             try {
6796                                 rspObj.object = _this._util.xml2js(rsp.text);
6797                             } catch (e) {
6798                                 error = true;
6799                                 rspObj.error = {
6800                                     errorType: "parseError",
6801                                     errorMessage: "Could not serialize XML: " + e
6802                                 };
6803                             }
6804                         } else {
6805                             rspObj.object = {};
6806                         }
6807 
6808                         if (!error && rspObj.status >= 200 && rspObj.status < 300) {
6809                             if (options.success) {
6810                                 options.success(rspObj);
6811                             }
6812                         } else {
6813                             if (options.error) {
6814                                 options.error(rspObj);
6815                             }
6816                         }
6817 
6818                         /*
6819                          * If a synchronous error happened after a non-GET request (usually a validation error), we
6820                          * need to clean up the request's entry in _pendingCallbacks since no corresponding event
6821                          * will arrive later. The corresponding requestId should be present in the response headers.
6822                          *
6823                          * It appears Shindig changes the header keys to lower case, hence 'requestid' instead of
6824                          * 'requestId' below.
6825                          **/
6826                         if (rspObj.status !== 202 && rsp.headers && rsp.headers.requestid) {
6827                             requestId = rsp.headers.requestid[0];
6828                             if (_this._pendingCallbacks[requestId]) {
6829                                 delete _this._pendingCallbacks[requestId];
6830                             }
6831                         }
6832                     }
6833                 }
6834             };
6835         },
6836 
6837         /**
6838          * Utility method to make an asynchronous request
6839          * @param {String} url
6840          *     The unencoded URL to which the request is sent (will be encoded)
6841          * @param {Object} options
6842          *     An object containing additional options for the request.
6843          * @param {Object} options.content
6844          *     An object to send in the content body of the request. Will be
6845          *     serialized into XML before sending.
6846          * @param {String} options.method
6847          *     The type of request. Defaults to "GET" when none is specified.
6848          * @param {Function} options.success(rsp)
6849          *     A callback function to be invoked for a successful request.
6850          *     {
6851          *         status: {Number} The HTTP status code returned
6852          *         content: {String} Raw string of response
6853          *         object: {Object} Parsed object of response
6854          *     }
6855          * @param {Function} options.error(rsp)
6856          *     A callback function to be invoked for an unsuccessful request.
6857          *     {
6858          *         status: {Number} The HTTP status code returned
6859          *         content: {String} Raw string of response
6860          *         object: {Object} Parsed object of response
6861          *         error: {Object} Wrapped exception that was caught
6862          *         error.errorType: {String} Type of error that was caught
6863          *         error.errorMessage: {String} Message associated with error
6864          *     }
6865          * @returns {Object}
6866          *     {
6867          *         abort: {function} Function that signifies the callback handler to NOT process the response of this asynchronous request
6868          *     }
6869          * @private
6870         */
6871 			        restRequest : function(url, options) {
6872 			    var encodedUrl, ajaxHandler, scheme = window.location.protocol, host = window.location.hostname, port = window.location.port, dataTypeAX, contentTypeAX, mtype, postdata = "", auth, rspObj, locale = this._config.locale;
6873 			    // Protect against null dereferencing of options
6874 			    // allowing its (nonexistent) keys to be read as
6875 			    // undefined
6876 			    options = options || {};
6877 			    options.success = this._util
6878 				    .validateHandler(options.success);
6879 			    options.error = this._util
6880 				    .validateHandler(options.error);
6881 
6882 			    // true if this should be a GET request, false
6883 			    // otherwise
6884 			    if (!options.method || options.method === "GET") {
6885 				// Disable caching for GETs
6886 				if (url.indexOf("?") > -1) {
6887 				    url += "&";
6888 				} else {
6889 				    url += "?";
6890 				}
6891 				url += "nocache="
6892 					+ this._util.currentTimeMillis();
6893 			    } else {
6894 				/**
6895 				 * If not GET, generate a requestID and add it
6896 				 * to the headers, then wrap callbacks into an
6897 				 * object and store it in _pendingCallbacks. If
6898 				 * we receive a synchronous error response
6899 				 * instead of a 202 as expected, the AJAX
6900 				 * handler will clean up _pendingCallbacks.
6901 				 */
6902 				/*
6903 				 * TODO: Clean up _pendingCallbacks if an entry
6904 				 * persists after a certain amount of time has
6905 				 * passed. In the block below, can store the
6906 				 * current time (new Date().getTime()) alongside
6907 				 * the callbacks in the new _pendingCallbacks
6908 				 * entry. Then iterate through a copty of
6909 				 * _pendingCallbacks, deleting all entries
6910 				 * inside _pendingCallbacks that are older than
6911 				 * a certain threshold (2 minutes for example.)
6912 				 * This solves a potential memory leak issue if
6913 				 * we never receive an event for a given stored
6914 				 * requestId; we don't want to store unfired
6915 				 * callbacks forever.
6916 				 */
6917 				/** @private */
6918 				options.uuid = this._util.generateUUID();
6919 				// params[gadgets.io.RequestParameters.HEADERS].requestId
6920 				// = options.uuid;
6921 				// By default, Shindig strips nearly all of the
6922 				// response headers, but this parameter tells
6923 				// Shindig
6924 				// to send the headers through unmodified; we
6925 				// need to be able to read the 'requestId'
6926 				// header if we
6927 				// get a synchronous error as a result of a
6928 				// non-GET request. (See the bottom of
6929 				// _createAjaxHandler().)
6930 				// params[gadgets.io.RequestParameters.GET_FULL_HEADERS]
6931 				// = "true";
6932 				this._pendingCallbacks[options.uuid] = {};
6933 				this._pendingCallbacks[options.uuid].success = options.success;
6934 				this._pendingCallbacks[options.uuid].error = options.error;
6935 			    }
6936 			    encodedUrl = encodeURI(url)
6937 				    + (window.errorOnRestRequest ? "ERROR" : "");
6938 			    ajaxHandler = this._createAjaxHandler(options);
6939 			    // ClientServices.makeRequest(encodedUrl,
6940 			    // ajaxHandler.callback, params);
6941 			    mtype = options.method || 'GET';
6942 			    encodedUrl = scheme + "//" + host + ":" + port
6943 				    + encodedUrl;
6944 
6945 			    if (typeof options.content === "object"
6946 				    && typeof options.content !== "undefined") {
6947 				// Except get, all the other operations accepts
6948 				// application/xml only.
6949 				// @Consumes in all the webservices except GET
6950 				// are having this.
6951 				postdata = this._util.js2xml(options.content);
6952 				if (postdata !== null && postdata !== "") {
6953 				    contentTypeAX = "application/xml";
6954 				} else {
6955 				    // in desktop, reason code GET request was
6956 				    // called with empty object content
6957 				    // if not able to parse any content to post
6958 				    // data, then it is GET only
6959 				    contentTypeAX = "";
6960 				    dataTypeAX = "text";
6961 				}
6962 			    } else {
6963 				// No content type for GET operation, by default
6964 				// it will take text/plain || application/xml.
6965 				// for dataType - GET will result plain text,
6966 				// which we are taking as xml.
6967 				// Queried list of @Produces from code base, all
6968 				// the GET operations accepts text/plain OR
6969 				// application/xml and produces application/xml
6970 				contentTypeAX = "";
6971 				dataTypeAX = "text";
6972 			    }
6973 			    auth = this._util.getAuthHeaderString(this._config);
6974 
6975 			    if (!this.doNotLog) {
6976 				this._logger.log(this.getRestType()
6977 					+ ": requestId='" + options.uuid
6978 					+ "', Making REST request: method="
6979 					+ (options.method || "GET") + ", url='"
6980 					+ encodedUrl + "'");
6981 			    }
6982 			    $
6983 				    .ajax({
6984 					url : encodedUrl,
6985 					headers : this.extraHeaders || {},
6986 					beforeSend : function(xhr) {
6987 					    xhr.setRequestHeader(
6988 						    "Authorization", auth);
6989 					    xhr.setRequestHeader("locale",
6990 						    locale);
6991 					    if (options.uuid) {
6992 						xhr.setRequestHeader(
6993 							"requestId",
6994 							options.uuid);
6995 					    }
6996 					},
6997 					dataType : dataTypeAX, // xml or json?
6998 					contentType : contentTypeAX,
6999 					type : mtype,
7000 					data : postdata,
7001 					timeout : 5000,
7002 					success : function(response, status,
7003 						xhr) {
7004 					    var rspObj = {
7005 						rc : xhr.status,
7006 						text : response,
7007 						isUnsent : xhr.readyState==0,
7008 						headers : {
7009 						    requestid : xhr
7010 							    .getResponseHeader("requestId")
7011 						}
7012 					    };
7013 					    ajaxHandler.callback(rspObj);
7014 					},
7015 					error : function(jqXHR, request, error) {
7016 					    var resObj = {
7017 						rc : jqXHR.status,
7018 						text : jqXHR.responseText,
7019 						isUnsent : jqXHR.readyState==0,
7020 						headers : {
7021 						    requestid : jqXHR
7022 							    .getResponseHeader("requestId")
7023 						}
7024 					    };
7025 					    ajaxHandler.callback(resObj);
7026 					}
7027 				    });
7028 			    return {
7029 				abort : ajaxHandler.abort
7030 			    };
7031 			},
7032 
7033 
7034         /**
7035 	 * Retrieves a reference to a particular notifierType.
7036 	 * 
7037 	 * @param notifierType
7038 	 *                is a string which indicates the notifier to retrieve
7039 	 *                ('load', 'change', 'add', 'delete', 'error')
7040 	 * @return {Notifier}
7041 	 * @private
7042 	 */
7043         _getNotifierReference: function (notifierType) {
7044             var notifierReference = null;
7045             if (notifierType === 'load') {
7046                 notifierReference = this._loadNotifier;
7047             } else if (notifierType === 'change') {
7048                 notifierReference = this._changeNotifier;
7049             } else if (notifierType === 'add') {
7050                 notifierReference = this._addNotifier;
7051             } else if (notifierType === 'delete') {
7052                 notifierReference = this._deleteNotifier;
7053             } else if (notifierType === 'error') {
7054                 notifierReference = this._errorNotifier;
7055             } else {
7056                 throw new Error("_getNotifierReference(): Trying to get unknown notifier(notifierType=" + notifierType + ")");
7057             }
7058 
7059             return notifierReference;
7060         }
7061     });
7062 
7063     window.finesse = window.finesse || {};
7064     window.finesse.restservices = window.finesse.restservices || {};
7065     window.finesse.restservices.RestBase = RestBase;
7066     
7067     return RestBase;
7068 });
7069 
7070 /** The following comment is to prevent jslint errors about 
7071  * using variables before they are defined.
7072  */
7073 /*global finesse*/
7074 
7075 /**
7076  * JavaScript base object that all REST collection objects should
7077  * inherit from because it encapsulates and provides the common functionality
7078  * that all REST objects need.
7079  *
7080  * @requires finesse.clientservices.ClientServices
7081  * @requires Class
7082  * @requires finesse.FinesseBase
7083  * @requires finesse.restservices.RestBase
7084  */
7085 
7086 /**
7087  * @class
7088  * JavaScript representation of a REST collection object.
7089  *
7090  * @constructor
7091  * @param {Function} callbacks.onCollectionAdd(this)
7092  *     Callback to invoke upon successful item addition to the collection.
7093  * @param {Function} callbacks.onCollectionDelete(this)
7094  *     Callback to invoke upon successful item deletion from the collection.
7095  * @borrows finesse.restservices.RestBase as finesse.restservices.RestCollectionBase
7096  */
7097 /** @private */
7098 define('restservices/RestCollectionBase',[
7099     'restservices/RestBase',
7100     'utilities/Utilities',
7101     'restservices/Notifier'
7102 ],
7103 function (RestBase, Utilities, Notifier) {
7104     var RestCollectionBase = RestBase.extend(/** @lends finesse.restservices.RestCollectionBase.prototype */{
7105 
7106         /**
7107          * Boolean function that specifies whether the collection handles subscribing
7108          * and propagation of events for the individual REST object items the
7109          * collection holds. False by default. Subclasses should override if true.
7110          * @private
7111          */
7112         supportsRestItemSubscriptions: false,
7113 
7114         /**
7115          * Gets the constructor the individual items that make of the collection.
7116          * For example, a Dialogs collection object will hold a list of Dialog items.
7117          * @throws Error because subtype must implement.
7118          * @private
7119          */
7120         getRestItemClass: function () {
7121             throw new Error("getRestItemClass(): Not implemented in subtype.");
7122         },
7123 
7124         /**
7125          * Gets the REST type of the individual items that make of the collection.
7126          * For example, a Dialogs collection object will hold a list of Dialog items.
7127          * @throws Error because subtype must implement.
7128          * @private
7129          */
7130         getRestItemType: function () {
7131             throw new Error("getRestItemType(): Not implemented in subtype.");
7132         },
7133 
7134         /**
7135          * The base REST URL in which items this object contains can be referenced.
7136          * @return {String}
7137          *     The REST URI for items this object contains.
7138          * @private
7139          */
7140         getRestItemBaseUrl: function () {
7141             var
7142             restUrl = "/finesse/api";
7143 
7144             //Append the REST type.
7145             restUrl += "/" + this.getRestItemType();
7146 
7147             return restUrl;
7148         },
7149 
7150          /*
7151          * Creates a new object from the given data
7152          * @param data - data object
7153          * @private
7154          */
7155         _objectCreator: function (data) {
7156             var objectId = this._extractId(data),
7157             newRestObj = this._collection[objectId],
7158             _this = this;
7159 
7160             //Prevent duplicate entries into collection.
7161             if (!newRestObj) {
7162                 //Create a new REST object using the subtype defined by the
7163                 //overridden method.
7164                 newRestObj = new (this.getRestItemClass())({
7165                     doNotSubscribe: this.handlesItemSubscription,
7166                     doNotRefresh: this.handlesItemRefresh,
7167                     id: objectId,
7168                     data: data,
7169                     onLoad: function (newObj) {
7170                         //Normalize and add  REST object to collection datastore.
7171                         _this._collection[objectId] = newObj;
7172                         _this._collectionAddNotifier.notifyListeners(newObj);
7173                         _this.length += 1;
7174                     }
7175                 });
7176             }
7177             else {
7178                 //If entry already exist in collection, process the new event,
7179                 //and notify all change listeners since an existing object has
7180                 //change. This could happen in the case when the Finesse server
7181                 //cycles, and sends a snapshot of the user's calls.
7182                 newRestObj._processObject(data);
7183                 newRestObj._changeNotifier.notifyListeners(newRestObj);
7184             }
7185         },
7186 
7187         /*
7188          * Deletes and object and notifies its handlers
7189          * @param data - data object
7190          * @private
7191          */
7192         _objectDeleter: function (data) {
7193             var objectId = this._extractId(data),
7194             object = this._collection[objectId];
7195             if (object) {
7196                 //Even though this is a delete, let's make sure the object we are passing has got good data
7197                 object._processObject(data);
7198                 //Notify listeners and delete from internal datastore.
7199                 this._collectionDeleteNotifier.notifyListeners(object);
7200                 delete this._collection[objectId];
7201                 this.length -= 1;
7202             }
7203         },
7204 
7205          /**
7206           * Creates an anonymous function for notifiying error listeners of a particular object
7207           * data.
7208           * @param obj - the objects whose error listeners to notify
7209           * @returns {Function}
7210           *     Callback for notifying of errors
7211           * @private
7212           */
7213         _createErrorNotifier: function (obj) {
7214             return function (err) {
7215                 obj._errorNotifier.notifyListeners(err);
7216             };
7217         },
7218 
7219          /**
7220           * Replaces the collection with a refreshed list using the passed in
7221           * data.
7222           * @param data - data object (usually this._data)
7223           * @private
7224           */
7225          _buildRefreshedCollection: function (data) {
7226             var i, dataObject, object, objectId, dataArray, newIds = [], foundFlag;
7227             if (data && this.getProperty(data, this.getRestItemType()) !== null) {
7228                 dataArray = Utilities.getArray(this.getProperty(data, this.getRestItemType()));
7229             } else {
7230                 dataArray = [];
7231             }
7232 
7233             // iterate through each item in the new data and add to or update collection
7234             for (i = 0; i < dataArray.length; i += 1) {
7235                 dataObject = {};
7236                 dataObject[this.getRestItemType()] = dataArray[i];
7237                 objectId = this._extractId(dataObject);
7238 
7239                 this._objectCreator(dataObject);
7240                 newIds.push(objectId);
7241 
7242                 // resubscribe if the object requires an explicit subscription
7243                 object = this._collection[objectId];
7244                 if (this.handlesItemRefresh && object.explicitSubscription) {
7245                     object._subscribeNode({
7246                         error: this._createErrorNotifier(object)
7247                     });
7248                 }
7249             }
7250 
7251             // now clean up items (if any) that were removed
7252             for (objectId in this._collection) {
7253                 if (this._collection.hasOwnProperty(objectId)) {
7254                     foundFlag = false;
7255                     for (i = newIds.length - 1; i >= 0; i -= 1) {
7256                         if (newIds[i] === objectId) {
7257                             foundFlag = true;
7258                             break;
7259                         }
7260                     }
7261                     // did not find in updated list, so delete it
7262                     if (!foundFlag) {
7263                         this._objectDeleter({'data': this._collection[objectId]._data});
7264                     }
7265                 }
7266             }
7267         },
7268 
7269          /**
7270           * The actual refresh operation, refactored out so we don't have to repeat code
7271           * @private
7272           */
7273         _RESTRefresh: function () {
7274             var _this = this;
7275             this._doGET({
7276                 success: function(rsp) {
7277                     if (_this._processResponse(rsp)) {
7278                         _this._buildRefreshedCollection(_this._data);
7279                     } else {
7280                         _this._errorNotifier.notifyListeners(_this);
7281                     }
7282                 },
7283                 error: function(rsp) {
7284                     _this._errorNotifier.notifyListeners(rsp);
7285                 }
7286             });            
7287         },
7288 
7289         /**
7290          * Force an update on this object. Since an asynchronous GET is performed,
7291          * it is necessary to have an onChange handler registered in order to be
7292          * notified when the response of this returns.
7293          * @returns {finesse.restservices.RestBaseCollection}
7294          *     This RestBaseCollection object to allow cascading
7295          */
7296          refresh: function() {
7297             var _this = this, isLoaded = this._loaded;
7298 
7299             // resubscribe if the collection requires an explicit subscription
7300             if (this.explicitSubscription) {
7301                 this._subscribeNode({
7302                     success: function () {
7303                         _this._RESTRefresh();
7304                     },
7305                     error: function (err) {
7306                         _this._errorNotifier.notifyListeners(err);
7307                     }
7308                 });
7309             } else {
7310                 this._RESTRefresh();
7311             }
7312 
7313             return this; // Allow cascading
7314          },
7315 
7316         /**
7317          * @private
7318          * The _addHandlerCb and _deleteHandlerCb require that data be passed in the
7319          * format of an array of {(Object Type): object} objects. For example, a
7320          * queues object would return [{Queue: queue1}, {Queue: queue2}, ...].
7321          * @param skipOuterObject If {true} is passed in for this param, then the "data"
7322          *                           property is returned instead of an object with the
7323          *                           data appended.
7324          * @return {Array}
7325          */
7326         extractCollectionData: function (skipOuterObject) {
7327             var restObjs,
7328             obj,
7329             result = [],
7330             _this = this;
7331             
7332             if (this._data)
7333             {
7334                 restObjs = this._data[this.getRestItemType()];
7335     
7336                 if (restObjs)
7337                 {
7338                     // check if there are multiple objects to pass
7339                     if (!$.isArray(restObjs))
7340                     {
7341                         restObjs = [restObjs];
7342                     }
7343     
7344                     // if so, create an object for each and add to result array
7345                     $.each(restObjs, function (id, object) {
7346                         if (skipOuterObject === true)
7347                         {
7348                             obj = object;
7349                         }
7350                         else
7351                         {
7352                             obj = {};
7353                             obj[_this.getRestItemType()] = object;
7354                         }
7355                         result.push(obj);
7356                     });
7357                 }
7358                 
7359             }
7360             
7361             return result;
7362         },
7363 
7364         /**
7365          * For Finesse, collections are handled uniquely on a POST and
7366          * doesn't necessary follow REST conventions. A POST on a collection
7367          * doesn't mean that the collection has been created, it means that an
7368          * item has been added to the collection. This function will generate
7369          * a closure which will handle this logic appropriately.
7370          * @param {Object} scope
7371          *     The scope of where the callback should be invoked.
7372          * @private
7373          */
7374         _addHandlerCb: function (scope) {
7375             return function (restItem) {
7376                 var data = restItem.extractCollectionData();               
7377 
7378                 $.each(data, function (id, object) {
7379                     scope._objectCreator(object);
7380                 });
7381             };
7382         },
7383 
7384         /**
7385          * For Finesse, collections are handled uniquely on a DELETE and
7386          * doesn't necessary follow REST conventions. A DELETE on a collection
7387          * doesn't mean that the collection has been deleted, it means that an
7388          * item has been deleted from the collection. This function will generate
7389          * a closure which will handle this logic appropriately.
7390          * @param {Object} scope
7391          *     The scope of where the callback should be invoked.
7392          * @private
7393          */
7394         _deleteHandlerCb: function (scope) {
7395             return function (restItem) {
7396                 var data = restItem.extractCollectionData();
7397 
7398                 $.each(data, function (id, obj) {
7399                     scope._objectDeleter(obj);
7400                 });
7401             };
7402         },
7403 
7404         /**
7405          * Utility method to process the update notification for Rest Items
7406          * that are children of the collection whose events are published to
7407          * the collection's node.
7408          * @param {Object} update
7409          *     The payload of an update notification.
7410          * @returns {Boolean}
7411          *     True if the update was successfully processed (the update object
7412          *     passed the schema validation) and updated the internal data cache,
7413          *     false otherwise.
7414          * @private
7415          */
7416         _processRestItemUpdate: function (update) {
7417             var object, objectId, updateObj = update.object.Update;
7418 
7419             //Extract the ID from the source if the Update was an error.
7420             if (updateObj.data.apiErrors) {
7421                 objectId = Utilities.getId(updateObj.source);
7422             }
7423             //Otherwise extract from the data object itself.
7424             else {
7425                 objectId = this._extractId(updateObj.data);
7426             }
7427 
7428             object = this._collection[objectId];
7429             if (object) {
7430                 if (object._processUpdate(update)) {
7431                     switch (updateObj.event) {
7432                     case "POST":
7433                         object._addNotifier.notifyListeners(object);
7434                         break;
7435                     case "PUT":
7436                         object._changeNotifier.notifyListeners(object);
7437                         break;
7438                     case "DELETE":
7439                         object._deleteNotifier.notifyListeners(object);
7440                         break;
7441                     }
7442                 }
7443             }
7444         },
7445 
7446         /**
7447          * SUBCLASS IMPLEMENTATION (override):
7448          * For collections, this callback has the additional responsibility of passing events
7449          * of collection item updates to the item objects themselves. The collection needs to
7450          * do this because item updates are published to the collection's node.
7451          * @returns {Function}
7452          *     The callback to be invoked when an update event is received
7453          * @private
7454          */
7455         _createPubsubCallback: function () {
7456             var _this = this;
7457             return function (update) {
7458                 //If the source of the update is our REST URL, this means the collection itself is modified
7459                 if (update.object.Update.source === _this.getRestUrl()) {
7460                     _this._updateEventHandler(_this, update);
7461                 } else {
7462                     //Otherwise, it is safe to assume that if we got an event on our topic, it must be a
7463                     //rest item update of one of our children that was published on our node (OpenAjax topic)
7464                     _this._processRestItemUpdate(update);
7465                 }
7466             };
7467         },
7468 
7469         /**
7470          * @class
7471          * This is the base collection object. 
7472          *
7473          * @constructs
7474          * @augments finesse.restservices.RestBase
7475          * @see finesse.restservices.Contacts
7476          * @see finesse.restservices.Dialogs
7477          * @see finesse.restservices.PhoneBooks
7478          * @see finesse.restservices.Queues
7479          * @see finesse.restservices.WorkflowActions
7480          * @see finesse.restservices.Workflows
7481          * @see finesse.restservices.WrapUpReasons
7482          */
7483         _fakeConstuctor: function () {
7484             /* This is here to hide the real init constructor from the public docs */
7485         },
7486         
7487        /**
7488          * @private
7489          * @param {Object} options
7490          *     An object with the following properties:<ul>
7491          *         <li><b>id:</b> The id of the object being constructed</li>
7492          *         <li><b>onCollectionAdd(this): (optional)</b> when an object is added to this collection</li>
7493          *         <li><b>onCollectionDelete(this): (optional)</b> when an object is removed from this collection</li>
7494          *         <li><b>onLoad(this): (optional)</b> when the collection is successfully loaded from the server</li>
7495          *         <li><b>onChange(this): (optional)</b> when an update notification of the collection is received. 
7496          *         This does not include adding and deleting members of the collection</li>
7497          *         <li><b>onAdd(this): (optional)</b> when a notification that the collection is created is received</li>
7498          *         <li><b>onDelete(this): (optional)</b> when a notification that the collection is deleted is received</li>
7499          *         <li><b>onError(rsp): (optional)</b> if loading of the collection fails, invoked with the error response object:<ul>
7500          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
7501          *             <li><b>content:</b> {String} Raw string of response</li>
7502          *             <li><b>object:</b> {Object} Parsed object of response</li>
7503          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
7504          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
7505          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
7506          *             </ul></li>
7507          *         </ul></li>
7508          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
7509          **/
7510         init: function (options) {
7511 
7512             options = options || {};
7513             options.id = "";
7514 
7515             //Make internal datastore collection to hold a list of objects.
7516             this._collection = {};
7517             this.length = 0;
7518 
7519             //Collections will have additional callbacks that will be invoked when
7520             //an item has been added/deleted.
7521             this._collectionAddNotifier = new Notifier();
7522             this._collectionDeleteNotifier = new Notifier();
7523 
7524             //Initialize the base class.
7525             this._super(options);
7526 
7527             this.addHandler('collectionAdd', options.onCollectionAdd);
7528             this.addHandler('collectionDelete', options.onCollectionDelete);
7529 
7530             //For Finesse, collections are handled uniquely on a POST/DELETE and
7531             //doesn't necessary follow REST conventions. A POST on a collection
7532             //doesn't mean that the collection has been created, it means that an
7533             //item has been added to the collection. A DELETE means that an item has
7534             //been removed from the collection. Due to this, we are attaching
7535             //special callbacks to the add/delete that will handle this logic.
7536             this.addHandler("add", this._addHandlerCb(this));
7537             this.addHandler("delete", this._deleteHandlerCb(this));
7538         },
7539 
7540         /**
7541          * Returns the collection.
7542          * @returns {Object}
7543          *     The collection as an object
7544          */
7545         getCollection: function () {
7546             //TODO: is this safe? or should we instead return protected functions such as .each(function)?
7547             return this._collection;
7548         },
7549 
7550         /**
7551          * Utility method to build the internal collection data structure (object) based on provided data
7552          * @param {Object} data
7553          *     The data to build the internal collection from
7554          * @private
7555          */
7556         _buildCollection: function (data) {
7557             var i, object, objectId, dataArray;
7558             if (data && this.getProperty(data, this.getRestItemType()) !== null) {
7559                 dataArray = Utilities.getArray(this.getProperty(data, this.getRestItemType()));
7560                 for (i = 0; i < dataArray.length; i += 1) {
7561 
7562                     object = {};
7563                     object[this.getRestItemType()] = dataArray[i];
7564                     objectId = this._extractId(object);
7565                     this._collection[objectId] = new (this.getRestItemClass())({
7566                         doNotSubscribe: this.handlesItemSubscription,
7567                         doNotRefresh: this.handlesItemRefresh,
7568                         id: objectId,
7569                         data: object
7570                     });
7571                     this.length += 1;
7572                 }
7573             }
7574         },
7575 
7576         /**
7577          * Called to know whether to include an item in the _collection and _data. Return false to keep it, true to filter out (discard) it.
7578          * Override this in subclasses if you need only object with certain attribute values.
7579          * @param  {Object} item Item to test.
7580          * @return {Boolean} False to keep, true to filter out (discard);
7581          */
7582         _filterOutItem: function (item) {
7583             return false;
7584         },
7585     
7586         /**
7587          * Validate and store the object into the internal data store.
7588          * SUBCLASS IMPLEMENTATION (override):
7589          * Performs collection specific logic to _buildCollection internally based on provided data
7590          * @param {Object} object
7591          *     The JavaScript object that should match of schema of this REST object.
7592          * @returns {Boolean}
7593          *     True if the object was validated and stored successfully.
7594          * @private
7595          */
7596         _processObject: function (object) {
7597             var i,
7598                 restItemType = this.getRestItemType(),
7599                 items;
7600             if (this._validate(object)) {
7601                 this._data = this.getProperty(object, this.getRestType()); // Should clone the object here?
7602     
7603                 // If a subclass has overriden _filterOutItem then we'll need to run through the items and remove them
7604                 if (this._data)
7605                 {
7606                     items = this._data[restItemType];
7607     
7608                     if (typeof(items) !== "undefined")
7609                     {
7610                         if (typeof(items.length) === "undefined")
7611                         {
7612                             // Single object
7613                             if (this._filterOutItem(items))
7614                             {
7615                                 this._data[restItemType] = items = [];
7616                             }
7617                             
7618                         }
7619                         else
7620                         {
7621                             // filter out objects
7622                             for (i = items.length - 1; i !== -1; i = i - 1)
7623                             {
7624                                 if (this._filterOutItem(items[i]))
7625                                 {
7626                                     items.splice(i, 1);
7627                                 }
7628                             }
7629                         }
7630                     }
7631                 }
7632     
7633                 // If loaded for the first time, call the load notifiers.
7634                 if (!this._loaded) {
7635                     this._buildCollection(this._data);
7636                     this._loaded = true;
7637                     this._loadNotifier.notifyListeners(this);
7638                 }
7639                 
7640                 return true;
7641                 
7642             }
7643             return false;
7644         },
7645 
7646         /**
7647          * Retrieves a reference to a particular notifierType.
7648          * @param {String} notifierType
7649          *      Specifies the notifier to retrieve (load, change, error, add, delete)
7650          * @return {Notifier} The notifier object.
7651          */
7652         _getNotifierReference: function (notifierType) {
7653             var notifierReference;
7654 
7655             try {
7656                 //Use the base method to get references for load/change/error.
7657                 notifierReference = this._super(notifierType);
7658             } catch (err) {
7659                 //Check for add/delete
7660                 if (notifierType === "collectionAdd") {
7661                     notifierReference = this._collectionAddNotifier;
7662                 } else if (notifierType === "collectionDelete") {
7663                     notifierReference = this._collectionDeleteNotifier;
7664                 } else {
7665                     //Rethrow exception from base class.
7666                     throw err;
7667                 }
7668             }
7669             return notifierReference;
7670         }
7671     });
7672     
7673     window.finesse = window.finesse || {};
7674     window.finesse.restservices = window.finesse.restservices || {};
7675     window.finesse.restservices.RestCollectionBase = RestCollectionBase;
7676     
7677     return RestCollectionBase;
7678 });
7679 
7680 /**
7681  * JavaScript representation of the Finesse Dialog object.
7682  *
7683  * @requires finesse.clientservices.ClientServices
7684  * @requires Class
7685  * @requires finesse.FinesseBase
7686  * @requires finesse.restservices.RestBase
7687  */
7688 
7689 /** @private */
7690 define('restservices/DialogBase',[
7691         'restservices/RestBase',
7692         'utilities/Utilities'
7693     ],
7694     function (RestBase, Utilities) {
7695         var DialogBase = RestBase.extend(/** @lends finesse.restservices.DialogBase.prototype */{
7696 
7697             /**
7698              * @class
7699              * A DialogBase is an attempted connection between or among multiple participants,
7700              * for example, a regular phone call, a chat, or an email.
7701              *
7702              * This object is typically extended into individual
7703              * REST Objects (like Dialog, MediaDialog, etc...), and shouldn't be used directly.
7704              *
7705              * @augments finesse.restservices.RestBase
7706              * @constructs
7707              */
7708             _fakeConstuctor: function () {
7709                 /* This is here to hide the real init constructor from the public docs */
7710             },
7711 
7712             /**
7713              * @private
7714              *
7715              * @param {Object} options
7716              *     An object with the following properties:<ul>
7717              *         <li><b>id:</b> The id of the object being constructed</li>
7718              *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
7719              *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
7720              *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
7721              *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
7722              *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
7723              *             <li><b>status:</b> {Number} The HTTP status code returned</li>
7724              *             <li><b>content:</b> {String} Raw string of response</li>
7725              *             <li><b>object:</b> {Object} Parsed object of response</li>
7726              *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
7727              *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
7728              *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
7729              *             </ul></li>
7730              *         </ul></li>
7731              *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
7732              **/
7733             init: function (options) {
7734                 this._super(options);
7735             },
7736 
7737             /**
7738              * @private
7739              * Gets the REST class for the current object - this is the Dialog class.
7740              * @returns {Object} The Dialog class.
7741              */
7742             getRestClass: function () {
7743                 throw new Error("getRestClass(): Not implemented in subtype.");
7744             },
7745 
7746             /**
7747              * @private
7748              * The constant for agent device.
7749              */
7750             _agentDeviceType: "AGENT_DEVICE",
7751 
7752             /**
7753              * @private
7754              * Gets the REST type for the current object - this is a "Dialog".
7755              * @returns {String} The Dialog string.
7756              */
7757             getRestType: function () {
7758                 return "Dialog";
7759             },
7760 
7761             /**
7762              * @private
7763              * Override default to indicate that this object doesn't support making
7764              * requests.
7765              */
7766             supportsRequests: false,
7767 
7768             /**
7769              * @private
7770              * Override default to indicate that this object doesn't support subscriptions.
7771              */
7772             supportsSubscriptions: false,
7773 
7774 
7775             /**
7776              * Getter for the media type.
7777              * @returns {String} The media type.
7778              */
7779             getMediaType: function () {
7780                 this.isLoaded();
7781                 return this.getData().mediaType;
7782             },
7783 
7784             /**
7785              * @private
7786              * Getter for the uri.
7787              * @returns {String} The uri.
7788              */
7789             getDialogUri: function () {
7790                 this.isLoaded();
7791                 return this.getData().uri;
7792             },
7793 
7794             /**
7795              * Getter for the callType.
7796              * @deprecated Use getMediaProperties().callType instead.
7797              * @returns {String} The callType.
7798              */
7799             getCallType: function () {
7800                 this.isLoaded();
7801                 return this.getData().mediaProperties.callType;
7802             },
7803 
7804 
7805             /**
7806              * Getter for the Dialog state.
7807              * @returns {String} The Dialog state.
7808              */
7809             getState: function () {
7810                 this.isLoaded();
7811                 return this.getData().state;
7812             },
7813 
7814             /**
7815              * Retrieves a list of participants within the Dialog object.
7816              * @returns {Object} Array list of participants.
7817              * Participant entity properties are as follows:<ul>
7818              *     <li>state - The state of the Participant. 
7819              *     <li>stateCause - The state cause of the Participant.
7820              *     <li>mediaAddress - The media address of the Participant.
7821              *     <li>startTime - The start Time of the Participant.
7822              *     <li>stateChangeTime - The time when participant state has changed.
7823              *     <li>actions - These are the actions that a Participant can perform</ul>
7824              */
7825             getParticipants: function () {
7826                 this.isLoaded();
7827                 var participants = this.getData().participants.Participant;
7828                 //Due to the nature of the XML->JSO converter library, a single
7829                 //element in the XML array will be considered to an object instead of
7830                 //a real array. This will handle those cases to ensure that an array is
7831                 //always returned.
7832 
7833                 return Utilities.getArray(participants);
7834             },
7835 
7836             /**
7837              * This method retrieves the participant timer counters
7838              *
7839              * @param {String} participantExt Extension of participant.
7840              * @returns {Object} Array of Participants which contains properties :<ul>
7841              *     <li>state - The state of the Participant. 
7842              *     <li>startTime - The start Time of the Participant.
7843              *     <li>stateChangeTime - The time when participant state has changed.</ul>
7844              * 
7845              */
7846             getParticipantTimerCounters : function (participantExt) {
7847                 var part, participantTimerCounters = {}, idx, participants;
7848 
7849                 participants = this.getParticipants();
7850 
7851 
7852                 //Loop through all the participants and find the right participant (based on participantExt)
7853                 for(idx=0;idx<participants.length;idx=idx+1)
7854                 {
7855                     part = participants[idx];
7856 
7857                     if (part.mediaAddress === participantExt)
7858                     {
7859                         participantTimerCounters.startTime= part.startTime;
7860                         participantTimerCounters.stateChangeTime= part.stateChangeTime;
7861                         participantTimerCounters.state= part.state;
7862                         break;
7863                     }
7864                 }
7865 
7866                 return participantTimerCounters;
7867             },
7868 
7869 
7870             /**
7871              * Retrieves a list of media properties from the dialog object.
7872              * @returns {Object} Map of call variables; names mapped to values.
7873              * Variables may include the following:<ul>
7874              * <li>dialedNumber: The number dialed.
7875              * <li>callType: The type of call. Call types include:<ul>
7876              *     <li>ACD_IN
7877              *     <li>PREROUTE_ACD_IN
7878              *     <li>PREROUTE_DIRECT_AGENT
7879              *     <li>TRANSFER
7880              *     <li>OTHER_IN
7881              *     <li>OUT
7882              *     <li>AGENT_INSIDE
7883              *     <li>CONSULT
7884              *     <li>CONFERENCE
7885              *     <li>SUPERVISOR_MONITOR
7886              *     <li>OUTBOUND
7887              *     <li>OUTBOUND_PREVIEW</ul>
7888              * <li>DNIS: The DNIS provided. For routed calls, this is the route point.
7889              * <li>wrapUpReason: A description of the call.
7890              * <li>queueNumber: Number of the agent Skill Group the call is attributed to.
7891              * <li>queueName: Name of the agent Skill Group the call is attributed to.
7892              * <li>callKeyCallId: unique number of the call routed on a particular day.
7893              * <li>callKeyPrefix: represents the day when the call is routed.
7894              * <li>Call Variables, by name.  The name indicates whether it is a call variable or ECC variable.
7895              * Call variable names start with callVariable#, where # is 1-10. ECC variable names (both scalar and array) are prepended with "user".
7896              * ECC variable arrays include an index enclosed within square brackets located at the end of the ECC array name.
7897              * <li>The following call variables provide additional details about an Outbound Option call:<ul>
7898              *     <li>BACampaign
7899              *     <li>BAAccountNumber
7900              *     <li>BAResponse
7901              *     <li>BAStatus<ul>
7902              *         <li>PREDICTIVE_OUTBOUND: A predictive outbound call.
7903              *         <li>PROGRESSIVE_OUTBOUND: A progressive outbound call.
7904              *         <li>PREVIEW_OUTBOUND_RESERVATION: Agent is reserved for a preview outbound call.
7905              *         <li>PREVIEW_OUTBOUND: Agent is on a preview outbound call.</ul>
7906              *     <li>BADialedListID
7907              *     <li>BATimeZone
7908              *     <li>BABuddyName</ul></ul>
7909              *
7910              */
7911             getMediaProperties: function () {
7912                 var mpData, currentMediaPropertiesMap, thisMediaPropertiesJQuery;
7913 
7914                 this.isLoaded();
7915 
7916                 // We have to convert to jQuery object to do a proper compare
7917                 thisMediaPropertiesJQuery = jQuery(this.getData().mediaProperties);
7918 
7919                 if ((this._lastMediaPropertiesJQuery !== undefined)
7920                     && (this._lastMediaPropertiesMap !== undefined)
7921                     && (this._lastMediaPropertiesJQuery.is(thisMediaPropertiesJQuery))) {
7922 
7923                     return this._lastMediaPropertiesMap;
7924                 }
7925 
7926                 currentMediaPropertiesMap = {};
7927 
7928                 mpData = this.getData().mediaProperties;
7929 
7930                 if (mpData) {
7931                     if (mpData.callvariables && mpData.callvariables.CallVariable) {
7932                         if (mpData.callvariables.CallVariable.length === undefined) {
7933                             mpData.callvariables.CallVariable = [mpData.callvariables.CallVariable];
7934                         }
7935                         jQuery.each(mpData.callvariables.CallVariable, function (i, callVariable) {
7936                             currentMediaPropertiesMap[callVariable.name] = callVariable.value;
7937                         });
7938                     }
7939 
7940                     jQuery.each(mpData, function (key, value) {
7941                         if (key !== 'callvariables') {
7942                             currentMediaPropertiesMap[key] = value;
7943                         }
7944                     });
7945                 }
7946 
7947                 this._lastMediaPropertiesMap = currentMediaPropertiesMap;
7948                 this._lastMediaPropertiesJQuery = thisMediaPropertiesJQuery;
7949 
7950                 return this._lastMediaPropertiesMap;
7951             },
7952 
7953 
7954 
7955             /**
7956              * @private
7957              * Invoke a request to the server given a content body and handlers.
7958              *
7959              * @param {Object} contentBody
7960              *     A JS object containing the body of the action request.
7961              * @param {finesse.interfaces.RequestHandlers} handlers
7962              *     An object containing the handlers for the request
7963              */
7964             _makeRequest: function (contentBody, handlers) {
7965                 // Protect against null dereferencing of options allowing its
7966                 // (nonexistent) keys to be read as undefined
7967                 handlers = handlers || {};
7968 
7969                 this.restRequest(this.getRestUrl(), {
7970                     method: 'PUT',
7971                     success: handlers.success,
7972                     error: handlers.error,
7973                     content: contentBody
7974                 });
7975             }
7976 
7977         });
7978 
7979         window.finesse = window.finesse || {};
7980         window.finesse.restservices = window.finesse.restservices || {};
7981         window.finesse.restservices.DialogBase = DialogBase;
7982 
7983 
7984         return DialogBase;
7985     });
7986 
7987 /**
7988  * JavaScript representation of the Finesse Dialog object.
7989  *
7990  * @requires finesse.clientservices.ClientServices
7991  * @requires Class
7992  * @requires finesse.FinesseBase
7993  * @requires finesse.restservices.RestBase
7994  */
7995 
7996 /** @private */
7997 define('restservices/Dialog',[
7998     'restservices/DialogBase',
7999     'utilities/Utilities'
8000 ],
8001 function (DialogBase, Utilities) {
8002     var Dialog = DialogBase.extend(/** @lends finesse.restservices.Dialog.prototype */{
8003 
8004         /**
8005          * @class
8006          * A Dialog is an attempted connection between or among multiple participants,
8007          * for example, a regular phone call, a conference, or a silent monitor session.
8008          * 
8009          * @augments finesse.restservices.DialogBase
8010          * @constructs
8011          */
8012         _fakeConstuctor: function () {
8013             /* This is here to hide the real init constructor from the public docs */
8014         },
8015         
8016         /**
8017          * @private
8018          *
8019          * @param {Object} options
8020          *     An object with the following properties:<ul>
8021          *         <li><b>id:</b> The id of the object being constructed</li>
8022          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
8023          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
8024          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
8025          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
8026          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
8027          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
8028          *             <li><b>content:</b> {String} Raw string of response</li>
8029          *             <li><b>object:</b> {Object} Parsed object of response</li>
8030          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
8031          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
8032          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
8033          *             </ul></li>
8034          *         </ul></li>
8035          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
8036          **/
8037         init: function (options) {
8038             this._super(options);
8039         },
8040 
8041         /**
8042          * @private
8043          * Gets the REST class for the current object - this is the Dialog class.
8044          * @returns {Object} The Dialog class.
8045          */
8046         getRestClass: function () {
8047             return Dialog;
8048         },
8049 
8050         /**
8051          * The requestId reaper timeout in ms
8052          */
8053         REQUESTID_REAPER_TIMEOUT: 5000,
8054 
8055         /**
8056          * Getter for the from address.
8057          * @returns {String} The from address.
8058          */
8059         getFromAddress: function () {
8060             this.isLoaded();
8061             return this.getData().fromAddress;
8062         },
8063 
8064         /**
8065          * Getter for the to address.
8066          * @returns {String} The to address.
8067          */
8068         getToAddress: function () {
8069             this.isLoaded();
8070             return this.getData().toAddress;
8071         },
8072         
8073         /**
8074          * Getter for the secondaryId of a dialog.
8075          * A CONSULT call has two call legs (primary leg and a consult leg). 
8076          * As the CONSULT call is completed (either with TRANSFER or CONFERENCE), call legs would be merged. 
8077          * The surviving call's Dialog will contain the dropped call's Dialog Id in secondaryId field.
8078          * For CCE deployments, DIRECT_TRANSFER also have the secondaryId populated as mentioned above.  
8079          * @returns {String} The id of the secondary dialog.
8080          * @since   11.6(1)-ES1 onwards
8081          */
8082         getSecondaryId: function () {
8083             this.isLoaded();
8084             return this.getData().secondaryId;
8085         },
8086         
8087        /**
8088          * gets the participant timer counters 
8089          *
8090          * @param {String} participantExt Extension of participant.
8091          * @returns {Object} Array of Participants which contains properties :<ul>
8092          *     <li>state - The state of the Participant. 
8093          *     <li>startTime - The start Time of the Participant.
8094          *     <li>stateChangeTime - The time when participant state has changed.</ul>
8095          */
8096         getParticipantTimerCounters : function (participantExt) {
8097           var part, participantTimerCounters = {}, idx, participants;
8098           
8099           participants = this.getParticipants();
8100 
8101 
8102           //Loop through all the participants and find the right participant (based on participantExt)
8103           for(idx=0;idx<participants.length;idx=idx+1)
8104           {
8105             part = participants[idx];
8106             
8107             if (part.mediaAddress === participantExt)
8108             {
8109                 participantTimerCounters.startTime= part.startTime;
8110                 participantTimerCounters.stateChangeTime= part.stateChangeTime;
8111                 participantTimerCounters.state= part.state;
8112                 break;
8113             }
8114           }
8115           
8116           return participantTimerCounters;
8117         },
8118         
8119         /**
8120          * Determines the droppable participants.  A droppable participant is a participant that is an agent extension.   
8121          * (It is not a CTI Route Point, IVR Port, or the caller)
8122          * 
8123          * @param {String} filterExtension used to remove a single extension from the list
8124          * @returns {Object} Array of Participants that can be dropped.
8125          * Participant entity properties are as follows:<ul>
8126          *     <li>state - The state of the Participant. 
8127          *     <li>stateCause - The state cause of the Participant.
8128          *     <li>mediaAddress - The media address of the Participant.
8129          *     <li>startTime - The start Time of the Participant.
8130          *     <li>stateChangeTime - The time when participant state has changed.
8131          *     <li>actions - These are the actions that a Participant can perform</ul>
8132          */
8133         getDroppableParticipants: function (filterExtension) {
8134           this.isLoaded();
8135           var droppableParticipants = [], participants, index, idx, filterExtensionToRemove = "", callStateOk, part;
8136 
8137           participants = this.getParticipants();
8138 
8139           if (filterExtension)
8140           {
8141             filterExtensionToRemove = filterExtension;
8142           }
8143 
8144           //Loop through all the participants to remove non-agents & remove filterExtension
8145           //We could have removed filterExtension using splice, but we have to iterate through
8146           //the list anyway.
8147           for(idx=0;idx<participants.length;idx=idx+1)
8148           {
8149             part = participants[idx];
8150 
8151             //Skip the filterExtension
8152             if (part.mediaAddress !== filterExtensionToRemove)
8153             {
8154                 callStateOk = this._isParticipantStateDroppable(part);
8155 
8156                 //Remove non-agents & make sure callstate 
8157                 if (callStateOk === true && part.mediaAddressType === this._agentDeviceType)
8158                 {
8159                   droppableParticipants.push(part);
8160                 }
8161             }
8162         }
8163 
8164         return Utilities.getArray(droppableParticipants);
8165         },
8166 
8167         _isParticipantStateDroppable : function (part)
8168         {
8169           var isParticipantStateDroppable = false;
8170           if (part.state === Dialog.ParticipantStates.ACTIVE || part.state === Dialog.ParticipantStates.ACCEPTED || part.state === Dialog.ParticipantStates.HELD)
8171           {
8172             isParticipantStateDroppable = true;
8173           }
8174           
8175           return isParticipantStateDroppable;
8176         },
8177         
8178         /**
8179          * Is the participant droppable
8180          *
8181          * @param {String} participantExt Extension of participant.
8182          * @returns {Boolean} True is droppable.
8183          */
8184         isParticipantDroppable : function (participantExt) {
8185           var droppableParticipants = null, isDroppable = false, idx, part, callStateOk;
8186           
8187           droppableParticipants = this.getDroppableParticipants();
8188           
8189           if (droppableParticipants) 
8190           {
8191             for(idx=0;idx<droppableParticipants.length;idx=idx+1)
8192             {
8193               part = droppableParticipants[idx];
8194              
8195               if (part.mediaAddress === participantExt)
8196               {
8197                 callStateOk = this._isParticipantStateDroppable(part);
8198 
8199                 //Remove non-agents & make sure callstate 
8200                 if (callStateOk === true && part.mediaAddressType === this._agentDeviceType)
8201                 {
8202                   isDroppable = true;
8203                   break;
8204                 }
8205               }
8206             }
8207           }
8208           
8209           return isDroppable;
8210         },
8211 
8212         /**
8213          * Retrieves information about the currently scheduled callback, if any.
8214          * @returns {Object} If no callback has been set, will return undefined. If 
8215          * a callback has been set, it will return a map with one or more of the 
8216          * following entries, depending on what values have been set. 
8217          *    callbackTime   - the callback time, if it has been set.
8218          *    callbackNumber - the callback number, if it has been set.
8219          */
8220         getCallbackInfo: function() {
8221             this.isLoaded();
8222             return this.getData().scheduledCallbackInfo;
8223         },
8224 
8225         /**
8226          * Invoke a consult call out to a destination.
8227          *
8228          * @param {String} mediaAddress
8229          *     The media address of the user performing the consult call.
8230          * @param {String} toAddress
8231          *     The destination address of the consult call.
8232          * @param {finesse.interfaces.RequestHandlers} handlers
8233          *     An object containing the handlers for the request
8234          */
8235         makeConsultCall: function (mediaAddress, toAddress, handlers) {
8236             this.isLoaded();
8237             var contentBody = {};
8238             contentBody[this.getRestType()] = {
8239                 "targetMediaAddress": mediaAddress,
8240                 "toAddress": toAddress,
8241                 "requestedAction": Dialog.Actions.CONSULT_CALL
8242             };
8243             this._makeRequest(contentBody, handlers);
8244             return this; // Allow cascading
8245         },
8246         
8247         /**
8248          * Invoke a single step transfer request.
8249          *
8250          * @param {String} mediaAddress
8251          *     The media address of the user performing the single step transfer.
8252          * @param {String} toAddress
8253          *     The destination address of the single step transfer.
8254          * @param {finesse.interfaces.RequestHandlers} handlers
8255          *     An object containing the handlers for the request
8256          */
8257         initiateDirectTransfer: function (mediaAddress, toAddress, handlers) {
8258             this.isLoaded();
8259             var contentBody = {};
8260             contentBody[this.getRestType()] = {
8261                 "targetMediaAddress": mediaAddress,
8262                 "toAddress": toAddress,
8263                 "requestedAction": Dialog.Actions.TRANSFER_SST
8264             };
8265             this._makeRequest(contentBody, handlers);
8266             return this; // Allow cascading
8267         },
8268 
8269         /**
8270          * Update this dialog's wrap-up reason.
8271          *
8272          * @param {String} wrapUpReason
8273          *     The new wrap-up reason for this dialog
8274          * @param {finesse.interfaces.RequestHandlers} handlers
8275          *     An object containing the handlers for the request
8276          */
8277         updateWrapUpReason: function (wrapUpReason, options)
8278         {
8279             this.isLoaded();
8280             var mediaProperties =
8281             {
8282                 "wrapUpReason": wrapUpReason
8283             };
8284 
8285             options = options || {};
8286             options.content = {};
8287             options.content[this.getRestType()] =
8288             {
8289                 "mediaProperties": mediaProperties,
8290                 "requestedAction": Dialog.Actions.UPDATE_CALL_DATA
8291             };
8292             options.method = "PUT";
8293             this.restRequest(this.getRestUrl(), options);
8294 
8295             return this;
8296         },
8297 
8298         /**
8299          * Invoke a request to server based on the action given.
8300          * @param {String} mediaAddress
8301          *     The media address of the user performing the action.
8302          * @param {finesse.restservices.Dialog.Actions} action
8303          *     The action string indicating the action to invoke on dialog.
8304          * @param {finesse.interfaces.RequestHandlers} handlers
8305          *     An object containing the handlers for the request
8306          */
8307         requestAction: function (mediaAddress, action, handlers) {
8308             this.isLoaded();
8309             var contentBody = {};
8310             contentBody[this.getRestType()] = {
8311                 "targetMediaAddress": mediaAddress,
8312                 "requestedAction": action
8313             };
8314             this._makeRequest(contentBody, handlers);
8315             return this; // Allow cascading
8316         },
8317         
8318         /**
8319          * Wrapper around "requestAction" to request PARTICIPANT_DROP action.
8320          *
8321          * @param targetMediaAddress is the address to drop
8322          * @param {finesse.interfaces.RequestHandlers} handlers
8323          *     An object containing the handlers for the request
8324          */
8325         dropParticipant: function (targetMediaAddress, handlers) {
8326             this.requestAction(targetMediaAddress, Dialog.Actions.PARTICIPANT_DROP, handlers);
8327         },
8328         
8329         /**
8330          * Invoke a request to server to send DTMF digit tones.
8331          * @param {String} mediaAddress
8332          * @param {finesse.interfaces.RequestHandlers} handlers
8333          *     An object containing the handlers for the request
8334          * @param {String} digit
8335          *     The digit which causes invocation of an action on the dialog.
8336          */
8337         sendDTMFRequest: function (mediaAddress, handlers, digit) {
8338             this.isLoaded();
8339             var contentBody = {};
8340             contentBody[this.getRestType()] = {
8341                 "targetMediaAddress": mediaAddress,
8342                 "requestedAction": "SEND_DTMF",
8343                 "actionParams": {
8344                     "ActionParam": {
8345                         "name": "dtmfString",
8346                         "value": digit
8347                     }
8348                 }
8349             };
8350             this._makeRequest(contentBody, handlers);
8351             return this; // Allow cascading
8352         },
8353 
8354         /**
8355          * Invoke a request to server to set the time for a callback.
8356          * @param {String} mediaAddress
8357          * @param {String} callbackTime 
8358          *     The requested time for the callback, in YYYY-MM-DDTHH:MM format
8359          *     (ex: 2013-12-24T23:59)
8360          * @param {finesse.interfaces.RequestHandlers} handlers
8361          *     An object containing the handlers for the request
8362          */
8363         updateCallbackTime: function (mediaAddress, callbackTime, handlers) {
8364             this.isLoaded();
8365             var contentBody = {};
8366             contentBody[this.getRestType()] = {
8367                 "targetMediaAddress": mediaAddress,
8368                 "requestedAction": Dialog.Actions.UPDATE_SCHEDULED_CALLBACK,
8369                 "actionParams": {
8370                     "ActionParam": {
8371                         "name": "callbackTime",
8372                         "value": callbackTime
8373                     }
8374                 }
8375             };
8376             this._makeRequest(contentBody, handlers);
8377             return this; // Allow cascading
8378         },
8379 
8380         /**
8381          * Invoke a request to server to set the number for a callback.
8382          * @param {String} mediaAddress
8383          * @param {String} callbackNumber
8384          *     The requested number to call for the callback
8385          * @param {finesse.interfaces.RequestHandlers} handlers
8386          *     An object containing the handlers for the request
8387          */
8388         updateCallbackNumber: function (mediaAddress, callbackNumber, handlers) {
8389             this.isLoaded();
8390             var contentBody = {};
8391             contentBody[this.getRestType()] = {
8392                 "targetMediaAddress": mediaAddress,
8393                 "requestedAction": Dialog.Actions.UPDATE_SCHEDULED_CALLBACK,
8394                 "actionParams": {
8395                     "ActionParam": {
8396                         "name": "callbackNumber",
8397                         "value": callbackNumber
8398                     }
8399                 }
8400             };
8401             this._makeRequest(contentBody, handlers);
8402             return this; // Allow cascading
8403         },
8404 
8405         /**
8406          * Invoke a request to server to cancel a callback.
8407          * @param {String} mediaAddress
8408          * @param {finesse.interfaces.RequestHandlers} handlers
8409          *     An object containing the handlers for the request
8410          */
8411         cancelCallback: function (mediaAddress, handlers) {
8412             this.isLoaded();
8413             var contentBody = {};
8414             contentBody[this.getRestType()] = {
8415                 "targetMediaAddress": mediaAddress,
8416                 "requestedAction": Dialog.Actions.CANCEL_SCHEDULED_CALLBACK
8417             };
8418             this._makeRequest(contentBody, handlers);
8419             return this; // Allow cascading
8420         },
8421 
8422         /**
8423          * Invoke a request to server to reclassify the call type.
8424          * @param {String} mediaAddress
8425          *     The media address of the user performing the consult call.
8426          * @param {String} classification
8427          *     The classification to assign to the call. Valid values are "VOICE", "FAX",
8428          *     "ANS_MACHINE", "INVALID", "BUSY" (CCX only), and "DO_NOT_CALL".
8429          * @param {finesse.interfaces.RequestHandlers} handlers
8430          *     An object containing the handlers for the request
8431          */
8432         reclassifyCall: function (mediaAddress, classification, handlers) {
8433             this.isLoaded();
8434             var contentBody = {};
8435             contentBody[this.getRestType()] = {
8436                 "targetMediaAddress": mediaAddress,
8437                 "requestedAction": Dialog.Actions.RECLASSIFY,
8438                 "actionParams": {
8439                     "ActionParam": {
8440                         "name": "outboundClassification",
8441                         "value": classification
8442                     }
8443                 }
8444             };
8445             this._makeRequest(contentBody, handlers);
8446             return this; // Allow cascading
8447         },
8448 
8449         /**
8450          * Utility method to create a closure containing the requestId and the Dialogs object so 
8451          * that the _pendingCallbacks map can be manipulated when the timer task is executed.
8452          * @param  {String} requestId The requestId of the event
8453          * @return {Function}           The function to be executed by setTimeout
8454          */
8455         _createRequestIdReaper: function (requestId) {
8456             var that = this;
8457             return function () {
8458                 that._logger.log("Dialog: clearing the requestId-to-callbacks mapping for requestId=" + requestId);
8459                 delete that._pendingCallbacks[requestId];
8460             };
8461         },
8462 
8463         /**
8464          * Overriding implementation of the one in RestBase.js
8465          * This determines the strategy that Dialogs will take after processing an event that contains a requestId.
8466          * @param  {String} requestId The requestId of the event
8467          */
8468         _postProcessUpdateStrategy: function (requestId) {
8469             this._logger.log("Dialog: determining whether to set timeout for clearing requestId-to-callbacks mapping requestId=" + requestId);
8470             var callbacksObj = this._pendingCallbacks[requestId];
8471             if (callbacksObj && !callbacksObj.used) {
8472                 this._logger.log("Dialog: setting timeout for clearing requestId-to-callbacks mapping requestId=" + requestId);
8473                 setTimeout(this._createRequestIdReaper(requestId), this.REQUESTID_REAPER_TIMEOUT);
8474                 callbacksObj.used = true;
8475             }            
8476         }
8477 
8478     });
8479 
8480     Dialog.Actions = /** @lends finesse.restservices.Dialog.Actions.prototype */ {
8481             /**
8482              * Drops the Participant from the Dialog.
8483              */
8484             DROP: "DROP",
8485             /**
8486              * Answers a Dialog.
8487              */
8488             ANSWER: "ANSWER",
8489             /**
8490              * Holds the Dialog.
8491              */
8492             HOLD: "HOLD",
8493             /**
8494              * Barges into a Call Dialog.
8495              */
8496             BARGE_CALL: "BARGE_CALL",
8497             /**
8498              * Allow as Supervisor to Drop a Participant from the Dialog.
8499              */
8500             PARTICIPANT_DROP: "PARTICIPANT_DROP",
8501             /**
8502              * Makes a new Call Dialog.
8503              */
8504             MAKE_CALL: "MAKE_CALL",
8505             /**
8506              * Retrieves a Dialog that is on Hold.
8507              */
8508             RETRIEVE: "RETRIEVE",
8509             /**
8510              * Sets the time or number for a callback. Can be
8511              * either a new callback, or updating an existing one.
8512              */
8513             UPDATE_SCHEDULED_CALLBACK: "UPDATE_SCHEDULED_CALLBACK",
8514             /**
8515              * Cancels a callback.
8516              */
8517             CANCEL_SCHEDULED_CALLBACK: "CANCEL_SCHEDULED_CALLBACK",
8518             /**
8519              * Initiates a Consult Call.
8520              */
8521             CONSULT_CALL: "CONSULT_CALL",
8522             /**
8523              * Initiates a Transfer of a Dialog.
8524              */
8525             TRANSFER: "TRANSFER",
8526             /**
8527              * Initiates a Single-Step Transfer of a Dialog.
8528              */
8529             TRANSFER_SST: "TRANSFER_SST",
8530             /**
8531              * Initiates a Conference of a Dialog.
8532              */
8533             CONFERENCE: "CONFERENCE",
8534             /**
8535              * Changes classification for a call
8536              */
8537             RECLASSIFY: "RECLASSIFY", 
8538             /**
8539              * Updates data on a Call Dialog.
8540              */
8541             UPDATE_CALL_DATA: "UPDATE_CALL_DATA",
8542             /**
8543              * Initiates a Recording on a Call Dialog.
8544              */
8545             START_RECORDING : "START_RECORDING",
8546             /**
8547              * Sends DTMF (dialed digits) to a Call Dialog.
8548              */
8549             DTMF : "SEND_DTMF",            
8550             /**
8551              * Accepts a Dialog that is being Previewed.
8552              */
8553             ACCEPT: "ACCEPT",
8554             /**
8555              * Rejects a Dialog.
8556              */
8557             REJECT: "REJECT",
8558             /**
8559              * Closes a Dialog.
8560              */
8561             CLOSE : "CLOSE",
8562             /**
8563              * @class Set of action constants for a Dialog.  These should be used for
8564              * {@link finesse.restservices.Dialog#requestAction}.
8565              * @constructs
8566              */
8567             _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
8568         };
8569 
8570     Dialog.States = /** @lends finesse.restservices.Dialog.States.prototype */ {
8571        /**
8572          * Indicates that the call is ringing at a device.
8573          */
8574         ALERTING: "ALERTING",
8575         /**
8576          * Indicates that the phone is off the hook at a device.
8577          */
8578         INITIATING: "INITIATING",
8579         /**
8580          * Indicates that the dialog has a least one active participant.
8581          */
8582         ACTIVE: "ACTIVE",
8583         /**
8584          * Indicates that the dialog has no active participants.
8585          */
8586         DROPPED: "DROPPED",
8587         /**
8588          * Indicates that the phone is dialing at the device.
8589          */
8590         INITIATED: "INITIATED",
8591         /**
8592          * Indicates that the dialog has failed.
8593          * @see Dialog.ReasonStates
8594          */
8595         FAILED: "FAILED",
8596         /**
8597          * Indicates that the user has accepted an OUTBOUND_PREVIEW dialog.
8598          */
8599         ACCEPTED: "ACCEPTED",
8600         /**
8601          * @class Possible Dialog State constants.
8602          * The State flow of a typical in-bound Dialog is as follows: INITIATING, INITIATED, ALERTING, ACTIVE, DROPPED.
8603          * @constructs
8604          */
8605         _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
8606     };
8607 
8608     Dialog.ParticipantStates = /** @lends finesse.restservices.Dialog.ParticipantStates.prototype */ {
8609         /**
8610           * Indicates that an incoming call is ringing on the device.
8611           */
8612          ALERTING: "ALERTING",
8613          /**
8614           * Indicates that an outgoing call, not yet active, exists on the device.
8615           */
8616          INITIATING: "INITIATING",
8617          /**
8618           * Indicates that the participant is active on the call.
8619           */
8620          ACTIVE: "ACTIVE",
8621          /**
8622           * Indicates that the participant has dropped from the call.
8623           */
8624          DROPPED: "DROPPED",
8625          /**
8626           * Indicates that the participant has held their connection to the call.
8627           */
8628          HELD: "HELD",
8629          /**
8630           * Indicates that the phone is dialing at a device.
8631           */
8632          INITIATED: "INITIATED",
8633          /**
8634           * Indicates that the call failed.
8635           * @see Dialog.ReasonStates
8636           */
8637          FAILED: "FAILED",
8638          /**
8639           * Indicates that the participant is not in active state on the call, but is wrapping up after the participant has dropped from the call.
8640           */
8641          WRAP_UP: "WRAP_UP",
8642          /**
8643           * Indicates that the participant has accepted the dialog.  This state is applicable to OUTBOUND_PREVIEW dialogs.
8644           */
8645          ACCEPTED: "ACCEPTED",
8646          /**
8647           * @class Possible Dialog Participant State constants.
8648           * @constructs
8649           */
8650          _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
8651      };
8652 
8653     Dialog.ReasonStates = /** @lends finesse.restservices.Dialog.ReasonStates.prototype */ {
8654        /**
8655         * Dialog was Busy.  This will typically be for a Failed Dialog.
8656         */
8657         BUSY: "BUSY",
8658         /**
8659          * Dialog reached a Bad Destination.  This will typically be for a Failed Dialog.
8660          */
8661         BAD_DESTINATION: "BAD_DESTINATION",
8662         /**
8663          * All Other Reasons.  This will typically be for a Failed Dialog.
8664          */
8665         OTHER: "OTHER",
8666         /**
8667          * The Device Resource for the Dialog was not available.
8668          */
8669         DEVICE_RESOURCE_NOT_AVAILABLE : "DEVICE_RESOURCE_NOT_AVAILABLE",
8670         /**
8671          * @class Possible dialog state reasons code constants.
8672              * @constructs
8673              */
8674             _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
8675     };
8676 
8677     window.finesse = window.finesse || {};
8678     window.finesse.restservices = window.finesse.restservices || {};
8679     window.finesse.restservices.Dialog = Dialog;
8680     
8681     
8682     return Dialog;
8683 });
8684 
8685 /**
8686  * JavaScript representation of the Finesse Dialogs collection
8687  * object which contains a list of Dialog objects.
8688  *
8689  * @requires finesse.clientservices.ClientServices
8690  * @requires Class
8691  * @requires finesse.FinesseBase
8692  * @requires finesse.restservices.RestBase
8693  * @requires finesse.restservices.Dialog
8694  */
8695 /** @private */
8696 define('restservices/Dialogs',[
8697     'restservices/RestCollectionBase',
8698     'restservices/Dialog'
8699 ],
8700 function (RestCollectionBase, Dialog) {
8701     var Dialogs = RestCollectionBase.extend(/** @lends finesse.restservices.Dialogs.prototype */{
8702 
8703         /**
8704          * @class
8705          * JavaScript representation of a Dialogs collection object. Also exposes
8706          * methods to operate on the object against the server.
8707          * @augments finesse.restservices.RestCollectionBase
8708          * @constructs
8709          * @see finesse.restservices.Dialog
8710          * @example
8711          *  _dialogs = _user.getDialogs( {
8712          *      onCollectionAdd : _handleDialogAdd,
8713          *      onCollectionDelete : _handleDialogDelete,
8714          *      onLoad : _handleDialogsLoaded
8715          *  });
8716          *  
8717          * _dialogCollection = _dialogs.getCollection();
8718          * for (var dialogId in _dialogCollection) {
8719          *     if (_dialogCollection.hasOwnProperty(dialogId)) {
8720          *         _dialog = _dialogCollection[dialogId];
8721          *         etc...
8722          *     }
8723          * }
8724          */
8725         _fakeConstuctor: function () {
8726             /* This is here to hide the real init constructor from the public docs */
8727         },
8728         
8729         /**
8730          * @private
8731          * @param {Object} options
8732          *     An object with the following properties:<ul>
8733          *         <li><b>id:</b> The id of the object being constructed</li>
8734          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
8735          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
8736          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
8737          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
8738          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
8739          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
8740          *             <li><b>content:</b> {String} Raw string of response</li>
8741          *             <li><b>object:</b> {Object} Parsed object of response</li>
8742          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
8743          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
8744          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
8745          *             </ul></li>
8746          *         </ul></li>
8747          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
8748          **/
8749         init: function (options) {
8750             this._super(options);
8751         },
8752 
8753         /**
8754          * @private
8755          * Gets the REST class for the current object - this is the Dialogs class.
8756          */
8757         getRestClass: function () {
8758             return Dialogs;
8759         },
8760 
8761         /**
8762          * @private
8763          * Gets the REST class for the objects that make up the collection. - this
8764          * is the Dialog class.
8765          */
8766         getRestItemClass: function () {
8767             return Dialog;
8768         },
8769 
8770         /**
8771          * @private
8772          * Gets the REST type for the current object - this is a "Dialogs".
8773          */
8774         getRestType: function () {
8775             return "Dialogs";
8776         },
8777 
8778         /**
8779          * @private
8780          * Gets the REST type for the objects that make up the collection - this is "Dialogs".
8781          */
8782         getRestItemType: function () {
8783             return "Dialog";
8784         },
8785 
8786         /**
8787          * @private
8788          * Override default to indicates that the collection doesn't support making
8789          * requests.
8790          */
8791         supportsRequests: true,
8792 
8793         /**
8794          * @private
8795          * Override default to indicates that the collection subscribes to its objects.
8796          */
8797         supportsRestItemSubscriptions: true,
8798 
8799         /**
8800          * The requestId reaper timeout in ms
8801          */
8802         REQUESTID_REAPER_TIMEOUT: 5000,
8803 
8804         /**
8805          * @private
8806          * Create a new Dialog in this collection
8807          *
8808          * @param {String} toAddress
8809          *     The to address of the new Dialog
8810          * @param {String} fromAddress
8811          *     The from address of the new Dialog
8812          * @param {finesse.interfaces.RequestHandlers} handlers
8813          *     An object containing the (optional) handlers for the request.
8814          * @return {finesse.restservices.Dialogs}
8815          *     This Dialogs object, to allow cascading.
8816          */
8817         createNewCallDialog: function (toAddress, fromAddress, handlers)
8818         {
8819             var contentBody = {};
8820             contentBody[this.getRestItemType()] = {
8821                 "requestedAction": "MAKE_CALL",
8822                 "toAddress": toAddress,
8823                 "fromAddress": fromAddress
8824             };
8825 
8826             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
8827             handlers = handlers || {};
8828 
8829             this.restRequest(this.getRestUrl(), {
8830                 method: 'POST',
8831                 success: handlers.success,
8832                 error: handlers.error,
8833                 content: contentBody
8834             });
8835             return this; // Allow cascading
8836         },
8837 
8838         /**
8839          * @private
8840          * Create a new Dialog in this collection as a result of a requested action
8841          *
8842          * @param {String} toAddress
8843          *     The to address of the new Dialog
8844          * @param {String} fromAddress
8845          *     The from address of the new Dialog
8846          * @param {finesse.restservices.Dialog.Actions} actionType
8847          *     The associated action to request for creating this new dialog
8848          * @param {finesse.interfaces.RequestHandlers} handlers
8849          *     An object containing the (optional) handlers for the request.
8850          * @return {finesse.restservices.Dialogs}
8851          *     This Dialogs object, to allow cascading.
8852          */
8853         createNewSuperviseCallDialog: function (toAddress, fromAddress, actionType, handlers)
8854         {
8855             var contentBody = {};
8856             this._isLoaded = true;
8857 
8858             contentBody[this.getRestItemType()] = {
8859                 "requestedAction": actionType,
8860                 "toAddress": toAddress,
8861                 "fromAddress": fromAddress
8862             };
8863 
8864             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
8865             handlers = handlers || {};
8866 
8867             this.restRequest(this.getRestUrl(), {
8868                 method: 'POST',
8869                 success: handlers.success,
8870                 error: handlers.error,
8871                 content: contentBody
8872             });
8873             return this; // Allow cascading
8874         },
8875         
8876         /**
8877          * @private
8878          * Create a new Dialog in this collection as a result of a requested action
8879          * @param {String} fromAddress
8880          *     The from address of the new Dialog
8881          * @param {String} toAddress
8882          *     The to address of the new Dialog
8883          * @param {finesse.restservices.Dialog.Actions} actionType
8884          *     The associated action to request for creating this new dialog
8885          * @param {String} dialogUri
8886          *     The associated uri of SUPERVISOR_MONITOR call
8887          * @param {finesse.interfaces.RequestHandlers} handlers
8888          *     An object containing the (optional) handlers for the request.
8889          * @return {finesse.restservices.Dialogs}
8890          *     This Dialogs object, to allow cascading.
8891          */
8892         createNewBargeCall: function (fromAddress, toAddress, actionType, dialogURI, handlers) {
8893             this.isLoaded();
8894          
8895             var contentBody = {};
8896             contentBody[this.getRestItemType()] = {
8897                 "fromAddress": fromAddress,
8898                 "toAddress": toAddress,
8899                 "requestedAction": actionType,
8900                 "associatedDialogUri": dialogURI
8901                 
8902             };
8903             // (nonexistent) keys to be read as undefined
8904             handlers = handlers || {};  
8905             this.restRequest(this.getRestUrl(), {
8906                 method: 'POST',
8907                 success: handlers.success,
8908                 error: handlers.error,
8909                 content: contentBody
8910             });
8911             return this; // Allow cascading
8912         },
8913 
8914         /**
8915          * Utility method to get the number of dialogs in this collection.
8916          * 'excludeSilentMonitor' flag is provided as an option to exclude calls with type
8917          * 'SUPERVISOR_MONITOR' from the count.
8918          * @param  {Boolean} excludeSilentMonitor If true, calls with type of 'SUPERVISOR_MONITOR' will be excluded from the count.
8919          * @return {Number} The number of dialogs in this collection.
8920          */
8921         getDialogCount: function (excludeSilentMonitor) {
8922             this.isLoaded();
8923 
8924             var dialogId, count = 0;
8925             if (excludeSilentMonitor) {
8926                 for (dialogId in this._collection) {
8927                     if (this._collection.hasOwnProperty(dialogId)) {
8928                         if (this._collection[dialogId].getCallType() !== 'SUPERVISOR_MONITOR') {
8929                             count += 1;
8930                         }
8931                     }
8932                 }
8933 
8934                 return count;
8935             } else {
8936                 return this.length;
8937             }        
8938         },
8939 
8940         /**
8941          * Utility method to create a closure containing the requestId and the Dialogs object so 
8942          * that the _pendingCallbacks map can be manipulated when the timer task is executed.
8943          * @param  {String} requestId The requestId of the event
8944          * @return {Function}           The function to be executed by setTimeout
8945          */
8946         _createRequestIdReaper: function (requestId) {
8947             var that = this;
8948             return function () {
8949                 that._logger.log("Dialogs: clearing the requestId-to-callbacks mapping for requestId=" + requestId);
8950                 delete that._pendingCallbacks[requestId];
8951             };
8952         },
8953 
8954         /**
8955          * Overriding implementation of the one in RestBase.js
8956          * This determines the strategy that Dialogs will take after processing an event that contains a requestId.
8957          * @param  {String} requestId The requestId of the event
8958          */
8959         _postProcessUpdateStrategy: function (requestId) {
8960             this._logger.log("Dialogs: determining whether to set timeout for clearing requestId-to-callbacks mapping requestId=" + requestId);
8961             var callbacksObj = this._pendingCallbacks[requestId];
8962             if (callbacksObj && !callbacksObj.used) {
8963                 this._logger.log("Dialogs: setting timeout for clearing requestId-to-callbacks mapping requestId=" + requestId);
8964                 setTimeout(this._createRequestIdReaper(requestId), this.REQUESTID_REAPER_TIMEOUT);
8965                 callbacksObj.used = true;
8966             }            
8967         }
8968 
8969     });
8970     
8971     window.finesse = window.finesse || {};
8972     window.finesse.restservices = window.finesse.restservices || {};
8973     window.finesse.restservices.Dialogs = Dialogs;
8974     
8975     return Dialogs;
8976 });
8977 
8978 /**
8979  * JavaScript representation of the Finesse ClientLog object
8980  *
8981  * @requires finesse.clientservices.ClientServices
8982  * @requires Class
8983  * @requires finesse.FinesseBase
8984  * @requires finesse.restservices.RestBase
8985  */
8986 
8987 /** The following comment is to prevent jslint errors about 
8988  * using variables before they are defined.
8989  */
8990 /** @private */
8991 /*global finesse*/
8992 
8993 define('restservices/ClientLog',["restservices/RestBase"], function (RestBase) {
8994     
8995     var ClientLog = RestBase.extend(/** @lends finesse.restservices.ClientLog.prototype */{    
8996         /**
8997          * @private
8998          * Returns whether this object supports transport logs
8999          */
9000         doNotLog : true,
9001         
9002         explicitSubscription : true,
9003         
9004         /**
9005          * @class
9006          * @private
9007          * JavaScript representation of a ClientLog object. Also exposes methods to operate
9008          * on the object against the server.
9009          *
9010          * @param {Object} options
9011          *     An object with the following properties:<ul>
9012          *         <li><b>id:</b> The id of the object being constructed</li>
9013          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
9014          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
9015          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
9016          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
9017          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
9018          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
9019          *             <li><b>content:</b> {String} Raw string of response</li>
9020          *             <li><b>object:</b> {Object} Parsed object of response</li>
9021          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
9022          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
9023          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
9024          *             </ul></li>
9025          *         </ul></li>
9026          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
9027          * @constructs
9028          * @augments finesse.restservices.RestBase
9029          **/
9030         init: function (options) {
9031             this._super({
9032                 id: "", 
9033                 data: {clientLog : null},
9034                 onAdd: options.onAdd,
9035                 onChange: options.onChange,
9036                 onLoad: options.onLoad,
9037                 onError: options.onError,
9038                 parentObj: options.parentObj
9039                 });
9040         },
9041 
9042         /**
9043          * @private
9044          * Gets the REST class for the current object - this is the ClientLog object.
9045          */
9046         getRestClass: function () {
9047             return ClientLog;
9048         },
9049 
9050         /**
9051          * @private
9052          * Gets the REST type for the current object - this is a "ClientLog".
9053          */
9054         getRestType: function ()
9055         {
9056             return "ClientLog";
9057         },
9058         
9059         /**
9060          * @private
9061          * Gets the node path for the current object
9062          * @returns {String} The node path
9063          */
9064         getXMPPNodePath: function () {
9065             return this.getRestUrl();
9066         },
9067 
9068         /**
9069          * @private
9070          * Utility method to fetch this object from the server, however we
9071          * override it for ClientLog to not do anything because GET is not supported
9072          * for ClientLog object.
9073          */
9074         _doGET: function (handlers) {
9075             return;
9076         },
9077            
9078         /**
9079          * @private
9080          * Invoke a request to the server given a content body and handlers.
9081          *
9082          * @param {Object} contentBody
9083          *     A JS object containing the body of the action request.
9084          * @param {Object} handlers
9085          *     An object containing the following (optional) handlers for the request:<ul>
9086          *         <li><b>success(rsp):</b> A callback function for a successful request to be invoked with the following
9087          *         response object as its only parameter:<ul>
9088          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
9089          *             <li><b>content:</b> {String} Raw string of response</li>
9090          *             <li><b>object:</b> {Object} Parsed object of response</li></ul>
9091          *         <li>A error callback function for an unsuccessful request to be invoked with the
9092          *         error response object as its only parameter:<ul>
9093          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
9094          *             <li><b>content:</b> {String} Raw string of response</li>
9095          *             <li><b>object:</b> {Object} Parsed object of response (HTTP errors)</li>
9096          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
9097          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
9098          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
9099          *             </ul></li>
9100          *         </ul>
9101          */
9102         sendLogs: function (contentBody, handlers) {
9103             // Protect against null dereferencing of options allowing its
9104             // (nonexistent) keys to be read as undefined
9105             handlers = handlers || {};
9106 
9107             this.restRequest(this.getRestUrl(), {
9108                 method: 'POST',
9109                 //success: handlers.success,
9110                 error: handlers.error,
9111                 content: contentBody
9112             });
9113         }
9114     });
9115     
9116     window.finesse = window.finesse || {};
9117     window.finesse.restservices = window.finesse.restservices || {};
9118     window.finesse.restservices.ClientLog = ClientLog;
9119     
9120     return ClientLog;
9121 });
9122 
9123 /**
9124  * JavaScript representation of the Finesse Queue object
9125  * @requires finesse.clientservices.ClientServices
9126  * @requires Class
9127  * @requires finesse.FinesseBase
9128  * @requires finesse.restservices.RestBase
9129  */
9130 
9131 /** @private */
9132 define('restservices/Queue',[
9133     'restservices/RestBase',
9134     'utilities/Utilities'
9135 ],
9136 function (RestBase, Utilities) {
9137     var Queue = RestBase.extend(/** @lends finesse.restservices.Queue.prototype */{
9138 
9139         /**
9140          * @class
9141          * A Queue is a list of Contacts available to a User for quick dial.
9142          * 
9143          * @augments finesse.restservices.RestBase
9144          * @constructs
9145          */
9146         _fakeConstuctor: function () {
9147             /* This is here to hide the real init constructor from the public docs */
9148         },
9149         
9150 		/**
9151 		 * @private
9152 		 * JavaScript representation of a Queue object. Also exposes methods to operate
9153 		 * on the object against the server.
9154 		 *
9155 		 * @constructor
9156 		 * @param {String} id
9157 		 *     Not required...
9158 		 * @param {Object} callbacks
9159 		 *     An object containing callbacks for instantiation and runtime
9160 		 * @param {Function} callbacks.onLoad(this)
9161 		 *     Callback to invoke upon successful instantiation
9162 		 * @param {Function} callbacks.onLoadError(rsp)
9163 		 *     Callback to invoke on instantiation REST request error
9164 		 *     as passed by finesse.clientservices.ClientServices.ajax()
9165 		 *     {
9166 		 *         status: {Number} The HTTP status code returned
9167 		 *         content: {String} Raw string of response
9168 		 *         object: {Object} Parsed object of response
9169 		 *         error: {Object} Wrapped exception that was caught
9170 		 *         error.errorType: {String} Type of error that was caught
9171 		 *         error.errorMessage: {String} Message associated with error
9172 		 *     }
9173 		 * @param {Function} callbacks.onChange(this)
9174 		 *     Callback to invoke upon successful update
9175 		 * @param {Function} callbacks.onError(rsp)
9176 		 *     Callback to invoke on update error (refresh or event)
9177 		 *     as passed by finesse.clientservices.ClientServices.ajax()
9178 		 *     {
9179 		 *         status: {Number} The HTTP status code returned
9180 		 *         content: {String} Raw string of response
9181 		 *         object: {Object} Parsed object of response
9182 		 *         error: {Object} Wrapped exception that was caught
9183 		 *         error.errorType: {String} Type of error that was caught
9184 		 *         error.errorMessage: {String} Message associated with error
9185 		 *     }
9186 		 *  
9187 		 */
9188         init: function (id, callbacks, restObj) {
9189             this._super(id, callbacks, restObj);
9190         },
9191 
9192         /**
9193          * @private
9194          * Gets the REST class for the current object - this is the Queue object.
9195          */
9196         getRestClass: function () {
9197             return Queue;
9198         },
9199 
9200         /**
9201          * @private
9202          * Gets the REST type for the current object - this is a "Queue".
9203          */
9204         getRestType: function () {
9205             return "Queue";
9206         },
9207 
9208         /**
9209          * @private
9210          * Returns whether this object supports subscriptions
9211          */
9212         supportsSubscriptions: function () {
9213             return true;
9214         },
9215         
9216         /**
9217          * @private
9218          * Specifies whether this object's subscriptions need to be explicitly requested
9219          */
9220         explicitSubscription: true,
9221         
9222         /**
9223          * @private
9224          * Gets the node path for the current object - this is the team Users node
9225          * @returns {String} The node path
9226          */
9227         getXMPPNodePath: function () {
9228             return this.getRestUrl();
9229         },
9230         
9231         /**
9232          * Getter for the queue id
9233          * @returns {String}
9234          *     The id of the Queue
9235          */
9236         getId: function () {
9237             this.isLoaded();
9238             return this._id;
9239         },
9240         
9241         /**
9242          * Getter for the queue name
9243          * @returns {String}
9244          *      The name of the Queue
9245          */
9246         getName: function () {
9247             this.isLoaded();
9248             return this.getData().name;
9249         },
9250         
9251         /**
9252          * Getter for the queue statistics.
9253          * Supported statistics include:<br>
9254          *  - callsInQueue<br>
9255          *  - startTimeOfLongestCallInQueue<br>
9256          *  <br>
9257          *  These statistics can be accessed via dot notation:<br>
9258          *  i.e.: getStatistics().callsInQueue
9259          * @returns {Object}
9260          *      The Object with different statistics as properties.
9261          */
9262         getStatistics: function () {
9263             this.isLoaded();
9264             return this.getData().statistics;       
9265         },
9266 
9267         /**
9268          * Parses a uriString to retrieve the id portion
9269          * @param {String} uriString
9270          * @return {String} id
9271          */
9272         _parseIdFromUriString : function (uriString) {
9273             return Utilities.getId(uriString);
9274         }
9275 
9276     });
9277 	
9278 	window.finesse = window.finesse || {};
9279     window.finesse.restservices = window.finesse.restservices || {};
9280     window.finesse.restservices.Queue = Queue;
9281     
9282     return Queue;
9283 });
9284 
9285 /**
9286  * JavaScript representation of the Finesse Queues collection
9287  * object which contains a list of Queue objects.
9288  * @requires finesse.clientservices.ClientServices
9289  * @requires Class
9290  * @requires finesse.FinesseBase
9291  * @requires finesse.restservices.RestBase
9292  * @requires finesse.restservices.RestCollectionBase
9293  */
9294 
9295 /**
9296  * @class
9297  * JavaScript representation of a Queues collection object.
9298  *
9299  * @constructor
9300  * @borrows finesse.restservices.RestCollectionBase as finesse.restservices.Queues
9301  */
9302 
9303 /** @private */
9304 define('restservices/Queues',[
9305     'restservices/RestCollectionBase',
9306     'restservices/Queue'
9307 ],
9308 function (RestCollectionBase, Queue) {
9309     var Queues = RestCollectionBase.extend(/** @lends finesse.restservices.Queues.prototype */{
9310 
9311         /**
9312          * @class
9313          * JavaScript representation of a Queues collection object. 
9314          * @augments finesse.restservices.RestCollectionBase
9315          * @constructs
9316          * @see finesse.restservices.Queue
9317          * @example
9318          *  _queues = _user.getQueues( {
9319          *      onCollectionAdd : _handleQueueAdd,
9320          *      onCollectionDelete : _handleQueueDelete,
9321          *      onLoad : _handleQueuesLoaded
9322          *  });
9323          *  
9324          * _queueCollection = _queues.getCollection();
9325          * for (var queueId in _queueCollection) {
9326          *     if (_queueCollection.hasOwnProperty(queueId)) {
9327          *         _queue = _queueCollection[queueId];
9328          *         etc...
9329          *     }
9330          * }
9331          */
9332         _fakeConstuctor: function () {
9333             /* This is here to hide the real init constructor from the public docs */
9334         },
9335 	    
9336          /**
9337          * @private
9338          * JavaScript representation of a Queues object. Also exposes
9339          * methods to operate on the object against the server.
9340          *
9341          * @param {Object} options
9342          *     An object with the following properties:<ul>
9343          *         <li><b>id:</b> The id of the object being constructed</li>
9344          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
9345          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
9346          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
9347          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
9348          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
9349          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
9350          *             <li><b>content:</b> {String} Raw string of response</li>
9351          *             <li><b>object:</b> {Object} Parsed object of response</li>
9352          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
9353          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
9354          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
9355          *             </ul></li>
9356          *         </ul></li>
9357          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
9358          **/
9359         init: function (options) {
9360             this._super(options);           
9361         },
9362 
9363         /**
9364          * @private
9365          * Gets xmpp node path.
9366          */
9367         getXMPPNodePath: function () {
9368             return this.getRestUrl();
9369         },
9370 
9371         /**
9372          * @private
9373          * Gets the REST class for the current object - this is the Queues class.
9374          */
9375         getRestClass: function () {
9376             return Queues;
9377         },
9378 
9379         /**
9380          * @private
9381          * Gets the REST class for the objects that make up the collection. - this
9382          * is the Queue class.
9383          */
9384         getRestItemClass: function () {
9385             return Queue;
9386         },
9387 
9388         /**
9389          * @private
9390          * Gets the REST type for the current object - this is a "Queues".
9391          */
9392         getRestType: function () {
9393             return "Queues";
9394         },
9395         
9396         /**
9397          * @private
9398          * Gets the REST type for the objects that make up the collection - this is "Queue".
9399          */
9400         getRestItemType: function () {
9401             return "Queue";
9402         },
9403 
9404         explicitSubscription: true,
9405         
9406         handlesItemRefresh: true
9407     });
9408     
9409     window.finesse = window.finesse || {};
9410     window.finesse.restservices = window.finesse.restservices || {};
9411     window.finesse.restservices.Queues = Queues;
9412     
9413     return Queues;
9414 });
9415 
9416 /**
9417  * JavaScript representation of the Finesse WrapUpReason object.
9418  *
9419  * @requires finesse.clientservices.ClientServices
9420  * @requires Class
9421  * @requires finesse.FinesseBase
9422  * @requires finesse.restservices.RestBase
9423  */
9424 
9425 /** @private */
9426 define('restservices/WrapUpReason',['restservices/RestBase'], function (RestBase) {
9427 
9428     var WrapUpReason = RestBase.extend(/** @lends finesse.restservices.WrapUpReason.prototype */{
9429 
9430         /**
9431          * @class
9432          * A WrapUpReason is a code and description identifying a particular reason that a
9433          * User is in WORK (WrapUp) mode.
9434          * 
9435          * @augments finesse.restservices.RestBase
9436          * @see finesse.restservices.User
9437          * @see finesse.restservices.User.States#WORK
9438          * @constructs
9439          */
9440         _fakeConstuctor: function () {
9441             /* This is here to hide the real init constructor from the public docs */
9442         },
9443         
9444         /** 
9445          * @private
9446          * JavaScript representation of a WrapUpReason object. Also exposes
9447          * methods to operate on the object against the server.
9448          *
9449          * @param {Object} options
9450          *     An object with the following properties:<ul>
9451          *         <li><b>id:</b> The id of the object being constructed</li>
9452          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
9453          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
9454          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
9455          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
9456          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
9457          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
9458          *             <li><b>content:</b> {String} Raw string of response</li>
9459          *             <li><b>object:</b> {Object} Parsed object of response</li>
9460          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
9461          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
9462          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
9463          *             </ul></li>
9464          *         </ul></li>
9465          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
9466          **/
9467         init: function (options) {
9468             this._super(options);
9469         },
9470 
9471         /**
9472          * @private
9473          * Gets the REST class for the current object - this is the WrapUpReason class.
9474          * @returns {Object} The WrapUpReason class.
9475          */
9476         getRestClass: function () {
9477             return WrapUpReason;
9478         },
9479 
9480         /**
9481          * @private
9482          * Gets the REST type for the current object - this is a "WrapUpReason".
9483          * @returns {String} The WrapUpReason string.
9484          */
9485         getRestType: function () {
9486             return "WrapUpReason";
9487         },
9488 
9489         /**
9490          * @private
9491          * Gets the REST type for the current object - this is a "WrapUpReasons".
9492          * @returns {String} The WrapUpReasons string.
9493          */
9494         getParentRestType: function () {
9495             return "WrapUpReasons";
9496         },
9497 
9498         /**
9499          * @private
9500          * Override default to indicate that this object doesn't support making
9501          * requests.
9502          */
9503         supportsRequests: false,
9504 
9505         /**
9506          * @private
9507          * Override default to indicate that this object doesn't support subscriptions.
9508          */
9509         supportsSubscriptions: false,
9510 
9511         /**
9512          * Getter for the label.
9513          * @returns {String} The label.
9514          */
9515         getLabel: function () {
9516             this.isLoaded();
9517             return this.getData().label;
9518         },
9519 
9520         /**
9521          * @private
9522          * Getter for the forAll flag.
9523          * @returns {Boolean} True if global.
9524          */
9525         getForAll: function () {
9526             this.isLoaded();
9527             return this.getData().forAll;
9528         },
9529 
9530         /**
9531          * @private
9532          * Getter for the Uri value.
9533          * @returns {String} The Uri.
9534          */
9535         getUri: function () {
9536             this.isLoaded();
9537             return this.getData().uri;
9538         }
9539     });
9540 
9541     window.finesse = window.finesse || {};
9542     window.finesse.restservices = window.finesse.restservices || {};
9543     window.finesse.restservices.WrapUpReason = WrapUpReason;
9544         
9545     return WrapUpReason;
9546 });
9547 
9548 /**
9549 * JavaScript representation of the Finesse WrapUpReasons collection
9550 * object which contains a list of WrapUpReason objects.
9551  *
9552  * @requires finesse.clientservices.ClientServices
9553  * @requires Class
9554  * @requires finesse.FinesseBase
9555  * @requires finesse.restservices.RestBase
9556  * @requires finesse.restservices.Dialog
9557  * @requires finesse.restservices.RestCollectionBase
9558  */
9559 
9560 /** @private */
9561 define('restservices/WrapUpReasons',[
9562     'restservices/RestCollectionBase',
9563     'restservices/WrapUpReason'
9564 ],
9565 function (RestCollectionBase, WrapUpReason) {
9566 
9567     var WrapUpReasons = RestCollectionBase.extend(/** @lends finesse.restservices.WrapUpReasons.prototype */{
9568         
9569         /**
9570          * @class
9571          * JavaScript representation of a WrapUpReasons collection object. 
9572          * @augments finesse.restservices.RestCollectionBase
9573          * @constructs
9574          * @see finesse.restservices.WrapUpReason
9575          * @example
9576          *  _wrapUpReasons = _user.getWrapUpReasons ( {
9577          *      onCollectionAdd : _handleWrapUpReasonAdd,
9578          *      onCollectionDelete : _handleWrapUpReasonDelete,
9579          *      onLoad : _handleWrapUpReasonsLoaded
9580          *  });
9581          *  
9582          * _wrapUpReasonCollection = _wrapUpReasons.getCollection();
9583          * for (var wrapUpReasonId in _wrapUpReasonCollection) {
9584          *     if (_wrapUpReasonCollection.hasOwnProperty(wrapUpReasonId)) {
9585          *         _wrapUpReason = _wrapUpReasonCollection[wrapUpReasonId];
9586          *         etc...
9587          *     }
9588          * }
9589         */
9590         _fakeConstuctor: function () {
9591             /* This is here to hide the real init constructor from the public docs */
9592         },
9593         
9594         /** 
9595          * @private
9596          * JavaScript representation of a WrapUpReasons collection object. Also exposes
9597          * methods to operate on the object against the server.
9598          *
9599          * @param {Object} options
9600          *     An object with the following properties:<ul>
9601          *         <li><b>id:</b> The id of the object being constructed</li>
9602          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
9603          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
9604          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
9605          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
9606          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
9607          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
9608          *             <li><b>content:</b> {String} Raw string of response</li>
9609          *             <li><b>object:</b> {Object} Parsed object of response</li>
9610          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
9611          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
9612          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
9613          *             </ul></li>
9614          *         </ul></li>
9615          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
9616          **/
9617         init: function (options) {
9618             this._super(options);           
9619         },
9620 
9621         /**
9622          * @private
9623          * Gets the REST class for the current object - this is the WrapUpReasons class.
9624          */
9625         getRestClass: function () {
9626             return WrapUpReasons;
9627         },
9628 
9629         /**
9630          * @private
9631          * Gets the REST class for the objects that make up the collection. - this
9632          * is the WrapUpReason class.
9633          */
9634         getRestItemClass: function () {
9635             return WrapUpReason;
9636         },
9637 
9638         /**
9639          * @private
9640          * Gets the REST type for the current object - this is a "WrapUpReasons".
9641          */
9642         getRestType: function () {
9643             return "WrapUpReasons";
9644         },
9645         
9646         /**
9647          * @private
9648          * Gets the REST type for the objects that make up the collection - this is "WrapUpReason".
9649          */
9650         getRestItemType: function () {
9651             return "WrapUpReason";
9652         },
9653 
9654         /**
9655          * @private
9656          * Override default to indicates that the collection supports making
9657          * requests.
9658          */
9659         supportsRequests: true,
9660 
9661         /**
9662          * @private
9663          * Override default to indicate that this object doesn't support subscriptions.
9664          */
9665         supportsRestItemSubscriptions: false,
9666 
9667         /**
9668          * @private
9669          * Retrieve the Wrap-Up Reason Codes. This call will re-query the server and refresh the collection.
9670          *
9671          * @returns {finesse.restservices.WrapUpReasons}
9672          *     This ReadyReasonCodes object to allow cascading.
9673          */
9674         get: function () {
9675             // set loaded to false so it will rebuild the collection after the get
9676             this._loaded = false;
9677             // reset collection
9678             this._collection = {};
9679             // perform get
9680             this._synchronize();
9681             return this;
9682         }
9683         
9684     });
9685  
9686     window.finesse = window.finesse || {};
9687     window.finesse.restservices = window.finesse.restservices || {};
9688     window.finesse.restservices.WrapUpReasons = WrapUpReasons;
9689        
9690     return WrapUpReasons;
9691 });
9692 
9693 /**
9694  * JavaScript representation of the Finesse Contact object.
9695  * @requires finesse.clientservices.ClientServices
9696  * @requires Class
9697  * @requires finesse.FinesseBase
9698  * @requires finesse.restservices.RestBase
9699  */
9700 /** @private */
9701 define('restservices/Contact',['restservices/RestBase'], function (RestBase) {
9702 
9703     var Contact = RestBase.extend(/** @lends finesse.restservices.Contact.prototype */{
9704 
9705         /**
9706          * @class
9707          * A Contact is a single entry in a PhoneBook, consisting of a First and Last Name,
9708          * a Phone Number, and a Description.
9709          * 
9710          * @augments finesse.restservices.RestBase
9711          * @see finesse.restservices.PhoneBook
9712          * @constructs
9713          */
9714         _fakeConstuctor: function () {
9715             /* This is here to hide the real init constructor from the public docs */
9716         },
9717         
9718         /**
9719          * @private
9720          * @param {Object} options
9721          *     An object with the following properties:<ul>
9722          *         <li><b>id:</b> The id of the object being constructed</li>
9723          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
9724          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
9725          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
9726          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
9727          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
9728          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
9729          *             <li><b>content:</b> {String} Raw string of response</li>
9730          *             <li><b>object:</b> {Object} Parsed object of response</li>
9731          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
9732          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
9733          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
9734          *             </ul></li>
9735          *         </ul></li>
9736          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
9737          **/
9738         init: function (options) {
9739             this._super(options);
9740         },
9741 
9742         /**
9743          * @private
9744          * Gets the REST class for the current object - this is the Contact class.
9745          * @returns {Object} The Contact class.
9746          */
9747         getRestClass: function () {
9748             return Contact;
9749         },
9750 
9751         /**
9752          * @private
9753          * Gets the REST type for the current object - this is a "Contact".
9754          * @returns {String} The Contact string.
9755          */
9756         getRestType: function () {
9757             return "Contact";
9758         },
9759 
9760         /**
9761          * @private
9762          * Override default to indicate that this object doesn't support making
9763          * requests.
9764          */
9765         supportsRequests: false,
9766 
9767         /**
9768          * @private
9769          * Override default to indicate that this object doesn't support subscriptions.
9770          */
9771         supportsSubscriptions: false,
9772 
9773         /**
9774          * Getter for the firstName.
9775          * @returns {String} The firstName.
9776          */
9777         getFirstName: function () {
9778             this.isLoaded();
9779             return this.getData().firstName;
9780         },
9781 
9782         /**
9783          * Getter for the lastName.
9784          * @returns {String} The lastName.
9785          */
9786         getLastName: function () {
9787             this.isLoaded();
9788             return this.getData().lastName;
9789         },
9790 
9791         /**
9792          * Getter for the phoneNumber.
9793          * @returns {String} The phoneNumber.
9794          */
9795         getPhoneNumber: function () {
9796             this.isLoaded();
9797             return this.getData().phoneNumber;
9798         },
9799 
9800         /**
9801          * Getter for the description.
9802          * @returns {String} The description.
9803          */
9804         getDescription: function () {
9805             this.isLoaded();
9806             return this.getData().description;
9807         },
9808 
9809         /** @private */
9810         createPutSuccessHandler: function(contact, contentBody, successHandler){
9811             return function (rsp) {
9812                 // Update internal structure based on response. Here we
9813                 // inject the contentBody from the PUT request into the
9814                 // rsp.object element to mimic a GET as a way to take
9815                 // advantage of the existing _processResponse method.
9816                 rsp.object = contentBody;
9817                 contact._processResponse(rsp);
9818 
9819                 //Remove the injected Contact object before cascading response
9820                 rsp.object = {};
9821                 
9822                 //cascade response back to consumer's response handler
9823                 successHandler(rsp);
9824             };
9825         },
9826 
9827         /** @private */
9828         createPostSuccessHandler: function (contact, contentBody, successHandler) {
9829             return function (rsp) {
9830                 rsp.object = contentBody;
9831                 contact._processResponse(rsp);
9832 
9833                 //Remove the injected Contact object before cascading response
9834                 rsp.object = {};
9835 
9836                 //cascade response back to consumer's response handler
9837                 successHandler(rsp);
9838             };
9839         },
9840 
9841         /**
9842          * Add
9843          * @private
9844          */
9845         add: function (newValues, handlers) {
9846             // this.isLoaded();
9847             var contentBody = {};
9848 
9849             contentBody[this.getRestType()] = {
9850                 "firstName": newValues.firstName,
9851                 "lastName": newValues.lastName,
9852                 "phoneNumber": newValues.phoneNumber,
9853                 "description": newValues.description
9854             };
9855 
9856             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
9857             handlers = handlers || {};
9858 
9859             this.restRequest(this.getRestUrl(), {
9860                 method: 'POST',
9861                 success: this.createPostSuccessHandler(this, contentBody, handlers.success),
9862                 error: handlers.error,
9863                 content: contentBody
9864             });
9865 
9866             return this; // Allow cascading
9867         },
9868 
9869         /**
9870          * Update
9871          * @private
9872          */
9873         update: function (newValues, handlers) {
9874             this.isLoaded();
9875             var contentBody = {};
9876 
9877             contentBody[this.getRestType()] = {
9878                 "uri": this.getId(),
9879                 "firstName": newValues.firstName,
9880                 "lastName": newValues.lastName,
9881                 "phoneNumber": newValues.phoneNumber,
9882                 "description": newValues.description
9883             };
9884 
9885             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
9886             handlers = handlers || {};
9887 
9888             this.restRequest(this.getRestUrl(), {
9889                 method: 'PUT',
9890                 success: this.createPutSuccessHandler(this, contentBody, handlers.success),
9891                 error: handlers.error,
9892                 content: contentBody
9893             });
9894 
9895             return this; // Allow cascading
9896         },
9897 
9898 
9899         /**
9900          * Delete
9901          * @private
9902          */
9903         "delete": function ( handlers) {
9904             this.isLoaded();
9905 
9906             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
9907             handlers = handlers || {};
9908 
9909             this.restRequest(this.getRestUrl(), {
9910                 method: 'DELETE',
9911                 success: this.createPutSuccessHandler(this, {}, handlers.success),
9912                 error: handlers.error,
9913                 content: undefined
9914             });
9915 
9916             return this; // Allow cascading
9917         }
9918     });
9919 
9920     window.finesse = window.finesse || {};
9921     window.finesse.restservices = window.finesse.restservices || {};
9922     window.finesse.restservices.Contact = Contact;
9923     
9924     return Contact;
9925 });
9926 
9927 /**
9928 * JavaScript representation of the Finesse Contacts collection
9929 * object which contains a list of Contact objects.
9930  *
9931  * @requires finesse.clientservices.ClientServices
9932  * @requires Class
9933  * @requires finesse.FinesseBase
9934  * @requires finesse.restservices.RestBase
9935  * @requires finesse.restservices.Dialog
9936  * @requires finesse.restservices.RestCollectionBase
9937  */
9938 /** @private */
9939 define('restservices/Contacts',[
9940     'restservices/RestCollectionBase',
9941     'restservices/Contact'
9942 ],
9943 function (RestCollectionBase, Contact) {
9944     var Contacts = RestCollectionBase.extend(/** @lends finesse.restservices.Contacts.prototype */{
9945         
9946         /**
9947          * @class
9948          * JavaScript representation of a Contacts collection object. Also exposes
9949          * methods to operate on the object against the server.
9950          * @augments finesse.restservices.RestCollectionBase
9951          * @constructs
9952          * @see finesse.restservices.Contact
9953          * @see finesse.restservices.PhoneBook
9954          * @example
9955          *  _contacts = _phonebook.getContacts( {
9956          *      onCollectionAdd : _handleContactAdd,
9957          *      onCollectionDelete : _handleContactDelete,
9958          *      onLoad : _handleContactsLoaded
9959          *  });
9960          *  
9961          * _contactCollection = _contacts.getCollection();
9962          * for (var contactId in _contactCollection) {
9963          *     if (_contactCollection.hasOwnProperty(contactId)) {
9964          *         _contact = _contactCollection[contactId];
9965          *         etc...
9966          *     }
9967          * }
9968          */
9969         _fakeConstuctor: function () {
9970             /* This is here to hide the real init constructor from the public docs */
9971         },
9972         
9973         /** 
9974          * @private
9975          * JavaScript representation of a Contacts collection object. Also exposes
9976          * methods to operate on the object against the server.
9977          *
9978          * @param {Object} options
9979          *     An object with the following properties:<ul>
9980          *         <li><b>id:</b> The id of the object being constructed</li>
9981          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
9982          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
9983          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
9984          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
9985          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
9986          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
9987          *             <li><b>content:</b> {String} Raw string of response</li>
9988          *             <li><b>object:</b> {Object} Parsed object of response</li>
9989          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
9990          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
9991          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
9992          *             </ul></li>
9993          *         </ul></li>
9994          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
9995          **/
9996         init: function (options) {
9997             this._super(options);           
9998         },
9999 
10000         /**
10001          * @private
10002          * Gets the REST class for the current object - this is the Contacts class.
10003          */
10004         getRestClass: function () {
10005             return Contacts;
10006         },
10007 
10008         /**
10009          * @private
10010          * Gets the REST class for the objects that make up the collection. - this
10011          * is the Contact class.
10012          */
10013         getRestItemClass: function () {
10014             return Contact;
10015         },
10016 
10017         /**
10018          * @private
10019          * Gets the REST type for the current object - this is a "Contacts".
10020          */
10021         getRestType: function () {
10022             return "Contacts";
10023         },
10024         
10025         /**
10026          * @private
10027          * Gets the REST type for the objects that make up the collection - this is "Contacts".
10028          */
10029         getRestItemType: function () {
10030             return "Contact";
10031         },
10032 
10033         /**
10034          * @private
10035          * Override default to indicates that the collection supports making
10036          * requests.
10037          */
10038         supportsRequests: true,
10039 
10040         /**
10041          * @private
10042          * Override default to indicates that the collection subscribes to its objects.
10043          */
10044         supportsRestItemSubscriptions: false,
10045         
10046         /**
10047          * @private
10048          * Retrieve the Contacts.  This call will re-query the server and refresh the collection.
10049          *
10050          * @returns {finesse.restservices.Contacts}
10051          *     This Contacts object, to allow cascading.
10052          */
10053         get: function () {
10054             // set loaded to false so it will rebuild the collection after the get
10055             this._loaded = false;
10056             // reset collection
10057             this._collection = {};
10058             // perform get
10059             this._synchronize();
10060             return this;
10061         }
10062         
10063     });
10064     
10065     window.finesse = window.finesse || {};
10066     window.finesse.restservices = window.finesse.restservices || {};
10067     window.finesse.restservices.Contacts = Contacts;
10068     
10069     
10070     return Contacts;
10071 });
10072 
10073 /**
10074  * JavaScript representation of the Finesse PhoneBook object.
10075  *
10076  * @requires finesse.clientservices.ClientServices
10077  * @requires Class
10078  * @requires finesse.FinesseBase
10079  * @requires finesse.restservices.RestBase
10080  */
10081 
10082 /** @private */
10083 define('restservices/PhoneBook',[
10084     'restservices/RestBase',
10085     'restservices/Contacts'
10086 ],
10087 function (RestBase, Contacts) {
10088     var PhoneBook = RestBase.extend(/** @lends finesse.restservices.PhoneBook.prototype */{
10089 
10090         _contacts: null,
10091 
10092         /**
10093          * @class
10094          * A PhoneBook is a list of Contacts available to a User for quick dial.
10095          * 
10096          * @augments finesse.restservices.RestBase
10097          * @see finesse.restservices.Contacts
10098          * @constructs
10099          */
10100         _fakeConstuctor: function () {
10101             /* This is here to hide the real init constructor from the public docs */
10102         },
10103         
10104         /** 
10105          * @private
10106          * JavaScript representation of a PhoneBook object. Also exposes
10107          * methods to operate on the object against the server.
10108          *
10109          * @param {Object} options
10110          *     An object with the following properties:<ul>
10111          *         <li><b>id:</b> The id of the object being constructed</li>
10112          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
10113          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
10114          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
10115          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
10116          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
10117          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
10118          *             <li><b>content:</b> {String} Raw string of response</li>
10119          *             <li><b>object:</b> {Object} Parsed object of response</li>
10120          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
10121          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
10122          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
10123          *             </ul></li>
10124          *         </ul></li>
10125          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
10126          **/
10127         init: function (options) {
10128             this._super(options);
10129         },
10130 
10131         /**
10132          * @private
10133          * Gets the REST class for the current object - this is the PhoneBook class.
10134          * @returns {Object} The PhoneBook class.
10135          */
10136         getRestClass: function () {
10137             return PhoneBook;
10138         },
10139 
10140         /**
10141          * @private
10142          * Gets the REST type for the current object - this is a "PhoneBook".
10143          * @returns {String} The PhoneBook string.
10144          */
10145         getRestType: function () {
10146             return "PhoneBook";
10147         },
10148 
10149         /**
10150          * @private
10151          * Override default to indicate that this object doesn't support making
10152          * requests.
10153          */
10154         supportsRequests: false,
10155 
10156         /**
10157          * @private
10158          * Override default to indicate that this object doesn't support subscriptions.
10159          */
10160         supportsSubscriptions: false,
10161 
10162         /**
10163          * Getter for the name of the Phone Book.
10164          * @returns {String} The name.
10165          */
10166         getName: function () {
10167             this.isLoaded();
10168             return this.getData().name;
10169         },
10170 
10171         /**
10172          * Getter for the type flag.
10173          * @returns {String} The type.
10174          */
10175         getType: function () {
10176             this.isLoaded();
10177             return this.getData().type;
10178         },
10179 
10180         /**
10181          * @private
10182          * Getter for the Uri value.
10183          * @returns {String} The Uri.
10184          */
10185         getUri: function () {
10186             this.isLoaded();
10187             return this.getData().uri;
10188         },
10189 
10190         /**
10191          * Getter for a Contacts collection object that is associated with PhoneBook.
10192          * @param {finesse.interfaces.RequestHandlers} handlers
10193          *     An object containing the handlers for the request
10194          * @returns {finesse.restservices.Contacts}
10195          *     A Contacts collection object.
10196          */
10197         getContacts: function (callbacks) {
10198             var options = callbacks || {};
10199             options.parentObj = this;
10200             this.isLoaded();
10201 
10202             if (this._contacts === null) {
10203                 this._contacts = new Contacts(options);
10204             }
10205 
10206             return this._contacts;
10207         },
10208 
10209         /**
10210          * Getter for <contacts> node within PhoneBook - sometimes it's just a URI, sometimes it is a Contacts collection
10211          * @returns {String} uri to contacts
10212          *          or {finesse.restservices.Contacts} collection
10213          */
10214         getEmbeddedContacts: function(){
10215             this.isLoaded();
10216             return this.getData().contacts;
10217         },
10218 
10219         /** @private */
10220         createPutSuccessHandler: function(phonebook, contentBody, successHandler){
10221             return function (rsp) {
10222                 // Update internal structure based on response. Here we
10223                 // inject the contentBody from the PUT request into the
10224                 // rsp.object element to mimic a GET as a way to take
10225                 // advantage of the existing _processResponse method.
10226                 rsp.object = contentBody;
10227                 phonebook._processResponse(rsp);
10228 
10229                 //Remove the injected PhoneBook object before cascading response
10230                 rsp.object = {};
10231                 
10232                 //cascade response back to consumer's response handler
10233                 successHandler(rsp);
10234             };
10235         },
10236 
10237         /** @private */
10238         createPostSuccessHandler: function (phonebook, contentBody, successHandler) {
10239             return function (rsp) {
10240                 rsp.object = contentBody;
10241                 phonebook._processResponse(rsp);
10242 
10243                 //Remove the injected PhoneBook object before cascading response
10244                 rsp.object = {};
10245 
10246                 //cascade response back to consumer's response handler
10247                 successHandler(rsp);
10248             };
10249         },
10250 
10251         /**
10252          * @private
10253          * Add a PhoneBook.
10254          * @param {Object} newValues
10255          * @param {String} newValues.name Name of PhoneBook
10256          * @param {String} newValues.type Type of PhoneBook
10257          * @param {finesse.interfaces.RequestHandlers} handlers
10258          *     An object containing the handlers for the request
10259          * @returns {finesse.restservices.PhoneBook}
10260          *     This PhoneBook object, to allow cascading
10261          */
10262         add: function (newValues, handlers) {
10263             // this.isLoaded();
10264             var contentBody = {};
10265 
10266             contentBody[this.getRestType()] = {
10267                 "name": newValues.name,
10268                 "type": newValues.type
10269             };
10270 
10271             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
10272             handlers = handlers || {};
10273 
10274             this.restRequest(this.getRestUrl(), {
10275                 method: 'POST',
10276                 success: this.createPostSuccessHandler(this, contentBody, handlers.success),
10277                 error: handlers.error,
10278                 content: contentBody
10279             });
10280 
10281             return this; // Allow cascading
10282         },
10283 
10284         /**
10285          * @private
10286          * Update a PhoneBook.
10287          * @param {Object} newValues
10288          * @param {String} newValues.name Name of PhoneBook
10289          * @param {String} newValues.type Type of PhoneBook
10290          * @param {finesse.interfaces.RequestHandlers} handlers
10291          *     An object containing the handlers for the request
10292          * @returns {finesse.restservices.PhoneBook}
10293          *     This PhoneBook object, to allow cascading
10294          */
10295         update: function (newValues, handlers) {
10296             this.isLoaded();
10297             var contentBody = {};
10298 
10299             contentBody[this.getRestType()] = {
10300                 "uri": this.getId(),
10301                 "name": newValues.name,
10302                 "type": newValues.type
10303             };
10304 
10305             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
10306             handlers = handlers || {};
10307 
10308             this.restRequest(this.getRestUrl(), {
10309                 method: 'PUT',
10310                 success: this.createPutSuccessHandler(this, contentBody, handlers.success),
10311                 error: handlers.error,
10312                 content: contentBody
10313             });
10314 
10315             return this; // Allow cascading
10316         },
10317 
10318 
10319         /**
10320          * Delete a PhoneBook.
10321          * @param {finesse.interfaces.RequestHandlers} handlers
10322          *     An object containing the handlers for the request
10323          * @returns {finesse.restservices.PhoneBook}
10324          *     This PhoneBook object, to allow cascading
10325          */
10326         "delete": function ( handlers) {
10327             this.isLoaded();
10328 
10329             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
10330             handlers = handlers || {};
10331 
10332             this.restRequest(this.getRestUrl(), {
10333                 method: 'DELETE',
10334                 success: this.createPutSuccessHandler(this, {}, handlers.success),
10335                 error: handlers.error,
10336                 content: undefined
10337             });
10338 
10339             return this; // Allow cascading
10340         }
10341 
10342 
10343 
10344     });
10345     
10346     window.finesse = window.finesse || {};
10347     window.finesse.restservices = window.finesse.restservices || {};
10348     window.finesse.restservices.PhoneBook = PhoneBook;
10349     
10350     return PhoneBook;
10351 });
10352 
10353 /**
10354 * JavaScript representation of the Finesse PhoneBooks collection
10355 * object which contains a list of PhoneBook objects.
10356  *
10357  * @requires finesse.clientservices.ClientServices
10358  * @requires Class
10359  * @requires finesse.FinesseBase
10360  * @requires finesse.restservices.RestBase
10361  * @requires finesse.restservices.Dialog
10362  * @requires finesse.restservices.RestCollectionBase
10363  */
10364 /** @private */
10365 define('restservices/PhoneBooks',[
10366     'restservices/RestCollectionBase',
10367     'restservices/PhoneBook'
10368 ],
10369 function (RestCollectionBase, PhoneBook) {
10370     var PhoneBooks = RestCollectionBase.extend(/** @lends finesse.restservices.PhoneBooks.prototype */{
10371         
10372         /**
10373          * @class
10374          * JavaScript representation of a PhoneBooks collection object. 
10375          * @augments finesse.restservices.RestCollectionBase
10376          * @constructs
10377          * @see finesse.restservices.PhoneBook
10378          * @see finesse.restservices.Contacts
10379          * @see finesse.restservices.Contact
10380          * @example
10381          *  _phoneBooks = _user.getPhoneBooks( {
10382          *      onCollectionAdd : _handlePhoneBookAdd,
10383          *      onCollectionDelete : _handlePhoneBookDelete,
10384          *      onLoad : _handlePhoneBooksLoaded
10385          *  });
10386          *  
10387          * _phoneBookCollection = _phoneBooks.getCollection();
10388          * for (var phoneBookId in _phoneBookCollection) {
10389          *     if (_phoneBookCollection.hasOwnProperty(phoneBookId)) {
10390          *         _phoneBook = _phoneBookCollection[phoneBookId];
10391          *         etc...
10392          *     }
10393          * }
10394         */
10395         _fakeConstuctor: function () {
10396             /* This is here to hide the real init constructor from the public docs */
10397         },
10398         
10399        /**
10400          * @private
10401          *
10402          * @param {Object} options
10403          *     An object with the following properties:<ul>
10404          *         <li><b>id:</b> The id of the object being constructed</li>
10405          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
10406          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
10407          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
10408          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
10409          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
10410          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
10411          *             <li><b>content:</b> {String} Raw string of response</li>
10412          *             <li><b>object:</b> {Object} Parsed object of response</li>
10413          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
10414          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
10415          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
10416          *             </ul></li>
10417          *         </ul></li>
10418          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
10419          **/
10420         init: function (options) {
10421             // Keep the REST response for PhoneBooks to check for 206 Partial Content.
10422             this.keepRestResponse = true;
10423             // Add in the Range header which is required for PhoneBooks API.
10424             this.extraHeaders = { "Range": "objects=1-1500" };
10425             this._super(options);
10426         },
10427 
10428         /**
10429          * @private
10430          * Gets the REST class for the current object - this is the PhoneBooks class.
10431          */
10432         getRestClass: function () {
10433             return PhoneBooks;
10434         },
10435 
10436         /**
10437          * @private
10438          * Gets the REST class for the objects that make up the collection. - this
10439          * is the PhoneBook class.
10440          */
10441         getRestItemClass: function () {
10442             return PhoneBook;
10443         },
10444 
10445         /**
10446          * @private
10447          * Gets the REST type for the current object - this is a "PhoneBooks".
10448          */
10449         getRestType: function () {
10450             return "PhoneBooks";
10451         },
10452         
10453         /**
10454          * @private
10455          * Gets the REST type for the objects that make up the collection - this is "PhoneBooks".
10456          */
10457         getRestItemType: function () {
10458             return "PhoneBook";
10459         },
10460 
10461         /**
10462          * @private
10463          * Override default to indicates that the collection supports making
10464          * requests.
10465          */
10466         supportsRequests: true,
10467 
10468         /**
10469          * @private
10470          * Override default to indicates that the collection subscribes to its objects.
10471          */
10472         supportsRestItemSubscriptions: false,
10473         
10474         /**
10475          * @private
10476          * Retrieve the PhoneBooks.  This call will re-query the server and refresh the collection.
10477          *
10478          * @returns {finesse.restservices.PhoneBooks}
10479          *     This PhoneBooks object, to allow cascading.
10480          */
10481         get: function () {
10482             // set loaded to false so it will rebuild the collection after the get
10483             this._loaded = false;
10484             // reset collection
10485             this._collection = {};
10486             // perform get
10487             this._synchronize();
10488             return this;
10489         }
10490         
10491     });
10492     
10493     window.finesse = window.finesse || {};
10494     window.finesse.restservices = window.finesse.restservices || {};
10495     window.finesse.restservices.PhoneBooks = PhoneBooks;
10496     
10497     return PhoneBooks;
10498 });
10499 
10500 /**
10501  * JavaScript representation of the Finesse WorkflowAction object.
10502  *
10503  * @requires finesse.clientservices.ClientServices
10504  * @requires Class
10505  * @requires finesse.FinesseBase
10506  * @requires finesse.restservices.RestBase
10507  */
10508 
10509 /*jslint browser: true, nomen: true, sloppy: true, forin: true */
10510 /*global define,finesse */
10511 
10512 /** @private */
10513 define('restservices/WorkflowAction',['restservices/RestBase'], function (RestBase) {
10514 
10515     var WorkflowAction = RestBase.extend({
10516 
10517         _contacts: null,
10518 
10519         actionTypes: [
10520             {
10521                 name: 'BROWSER_POP',
10522                 params: [
10523                     {
10524                         name: 'windowName',
10525                         type: 'text'
10526                     },
10527                     {
10528                         name: 'path',
10529                         type: 'systemVariableSingleLineEditor'
10530                     }
10531                 ]
10532             },
10533             {
10534                 name: 'HTTP_REQUEST',
10535                 params: [
10536                     {
10537                         name: 'method',
10538                         type: 'dropdown',
10539                         values: ['POST', 'PUT']
10540                     },
10541                     {
10542                         name: 'location',
10543                         type: 'dropdown',
10544                         values: ['FINESSE', 'OTHER']
10545                     },
10546                     {
10547                         name: 'contentType',
10548                         type: 'text'
10549                     },
10550                     {
10551                         name: 'path',
10552                         type: 'systemVariableSingleLineEditor'
10553                     },
10554                     {
10555                         name: 'body',
10556                         type: 'systemVariableMultiLineEditor'
10557                     }
10558                 ]
10559             }            
10560             // more action type definitions here
10561         ],
10562 
10563         /**
10564          * @class
10565          * A WorkflowAction is an action (e.g. Browser Pop, Rest Request) defined in a
10566          * Workflow and triggered by a system event (Call Received, Call Ended, etc.).
10567          * 
10568          * @augments finesse.restservices.RestBase
10569          * @see finesse.restservices.Workflow
10570          * @constructs
10571          */
10572         _fakeConstuctor: function () {
10573             /* This is here to hide the real init constructor from the public docs */
10574         },
10575         
10576         /**
10577          * @private
10578          * JavaScript representation of a WorkflowAction object. Also exposes
10579          * methods to operate on the object against the server.
10580          *
10581          * @param {Object} options
10582          *     An object with the following properties:<ul>
10583          *         <li><b>id:</b> The id of the object being constructed</li>
10584          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
10585          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
10586          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
10587          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
10588          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
10589          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
10590          *             <li><b>content:</b> {String} Raw string of response</li>
10591          *             <li><b>object:</b> {Object} Parsed object of response</li>
10592          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
10593          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
10594          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
10595          *             </ul></li>
10596          *         </ul></li>
10597          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
10598          **/
10599         init: function (options) {
10600             this._super(options);
10601         },
10602 
10603         /**
10604          * @private
10605          * Gets the REST class for the current object - this is the WorkflowAction class.
10606          * @returns {Object} The WorkflowAction class.
10607          */
10608         getRestClass: function () {
10609             return finesse.restservices.WorkflowAction;
10610         },
10611 
10612         /**
10613          * @private
10614          * Gets the REST type for the current object - this is a "WorkflowAction".
10615          * @returns {String} The WorkflowAction string.
10616          */
10617         getRestType: function () {
10618             return "WorkflowAction";
10619         },
10620 
10621         /**
10622          * @private
10623          * Override default to indicate that this object doesn't support making
10624          * requests.
10625          */
10626         supportsRequests: false,
10627 
10628         /**
10629          * @private
10630          * Override default to indicate that this object doesn't support subscriptions.
10631          */
10632         supportsSubscriptions: false,
10633 
10634         /**
10635          * Getter for the name.
10636          * @returns {String} The name.
10637          */
10638         getName: function () {
10639             this.isLoaded();
10640             return this.getData().name;
10641         },
10642 
10643         /**
10644          * Getter for the type flag.
10645          * @returns {String} The type.
10646          */
10647         getType: function () {
10648             this.isLoaded();
10649             return this.getData().type;
10650         },
10651 
10652         /**
10653          * @private
10654          * Getter for the Uri value.
10655          * @returns {String} The Uri.
10656          */
10657         getUri: function () {
10658             this.isLoaded();
10659             return this.getData().uri;
10660         },
10661 
10662         /**
10663          * @private
10664          * Getter for the handledBy value.
10665          * @returns {String} handledBy.
10666          */
10667         getHandledBy: function () {
10668             this.isLoaded();
10669             return this.getData().handledBy;
10670         },
10671 
10672         /**
10673          * Getter for the parameters.
10674          * @returns {Object} key = param name, value = param value
10675          */
10676         getParams: function () {
10677             var map = {},
10678                 params = this.getData().params.Param,
10679                 i,
10680                 param;
10681 
10682             for(i=0; i<params.length; i+=1){
10683                 param = params[i];
10684                 map[param.name] = param.value || "";
10685             }
10686 
10687             return map;
10688         },
10689 
10690         /**
10691          * Getter for the ActionVariables
10692          * @returns {Object} key = action variable name, value = Object{name, type, node, testValue}
10693          */
10694         getActionVariables: function() {
10695             var map = {},
10696                 actionVariablesParent = this.getData().actionVariables,
10697                 actionVariables,
10698                 i,
10699                 actionVariable;
10700 
10701             if (actionVariablesParent === null ||  typeof(actionVariablesParent) === "undefined" || actionVariablesParent.length === 0){
10702                 return map;
10703             }
10704             actionVariables = actionVariablesParent.ActionVariable;
10705 
10706             if(actionVariables.length > 0){
10707                 for(i=0; i<actionVariables.length; i+=1){
10708                     actionVariable = actionVariables[i];
10709                     // escape nulls to empty string
10710                     actionVariable.name = actionVariable.name || "";
10711                     actionVariable.type = actionVariable.type || "";
10712                     actionVariable.node = actionVariable.node || "";
10713                     actionVariable.testValue = actionVariable.testValue || "";
10714                     map[actionVariable.name] = actionVariable;
10715                 }
10716             } else {
10717                 map[actionVariables.name] = actionVariables;
10718             }
10719 
10720             return map;
10721         },
10722 
10723         /** @private */
10724         createPutSuccessHandler: function(action, contentBody, successHandler){
10725             return function (rsp) {
10726                 // Update internal structure based on response. Here we
10727                 // inject the contentBody from the PUT request into the
10728                 // rsp.object element to mimic a GET as a way to take
10729                 // advantage of the existing _processResponse method.
10730                 rsp.object = contentBody;
10731                 action._processResponse(rsp);
10732 
10733                 //Remove the injected WorkflowAction object before cascading response
10734                 rsp.object = {};
10735                 
10736                 //cascade response back to consumer's response handler
10737                 successHandler(rsp);
10738             };
10739         },
10740 
10741         /** @private */
10742         createPostSuccessHandler: function (action, contentBody, successHandler) {
10743             return function (rsp) {
10744                 rsp.object = contentBody;
10745                 action._processResponse(rsp);
10746 
10747                 //Remove the injected WorkflowAction object before cascading response
10748                 rsp.object = {};
10749 
10750                 //cascade response back to consumer's response handler
10751                 successHandler(rsp);
10752             };
10753         },
10754 
10755         /**
10756          * @private
10757          * Build params array out of all the values coming into add or update methods
10758          * paramMap is a map of params.. we need to translate it into an array of Param objects
10759          * where path and windowName are params for the BROWSER_POP type
10760          */
10761         buildParamsForRest: function(paramMap){
10762             var params = {"Param": []},
10763                 i;
10764             for(i in paramMap){
10765                 if(paramMap.hasOwnProperty(i)){
10766                     params.Param.push({name: i, value: paramMap[i]});
10767                 }
10768             }
10769             return params;
10770         },
10771 
10772         /**
10773          * @private
10774          * Build actionVariables array out of all the values coming into add or update methods
10775          * actionVariableMap is a map of actionVariables.. we need to translate it into an array of ActionVariable objects
10776          * where path and windowName are params for the BROWSER_POP type
10777          */
10778         buildActionVariablesForRest: function(actionVariableMap){
10779             var actionVariables = {"ActionVariable": []},
10780                 i,
10781                 actionVariable;
10782             for(i in actionVariableMap){
10783                 if(actionVariableMap.hasOwnProperty(i)){
10784                     // {name: "callVariable1", type: "SYSTEM", node: "", testValue: "<blink>"}
10785                     actionVariable = {
10786                         "name": actionVariableMap[i].name,
10787                         "type": actionVariableMap[i].type,
10788                         "node": actionVariableMap[i].node,
10789                         "testValue": actionVariableMap[i].testValue
10790                     };
10791                     actionVariables.ActionVariable.push(actionVariable);
10792                 }
10793             }
10794             return actionVariables;
10795         },
10796 
10797         /**
10798          * Add
10799          */
10800         add: function (newValues, handlers) {
10801             var contentBody = {};
10802 
10803             contentBody[this.getRestType()] = {
10804                 "name": newValues.name,
10805                 "type": newValues.type,
10806                 "handledBy": newValues.handledBy,
10807                 "params": this.buildParamsForRest(newValues.params),
10808                 "actionVariables": this.buildActionVariablesForRest(newValues.actionVariables)
10809             };
10810 
10811             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
10812             handlers = handlers || {};
10813 
10814             this.restRequest(this.getRestUrl(), {
10815                 method: 'POST',
10816                 success: this.createPostSuccessHandler(this, contentBody, handlers.success),
10817                 error: handlers.error,
10818                 content: contentBody
10819             });
10820 
10821             return this; // Allow cascading
10822         },
10823 
10824         /**
10825          * @private
10826          * Update
10827          */
10828         update: function (newValues, handlers) {
10829             this.isLoaded();
10830             var contentBody = {};
10831             
10832             contentBody[this.getRestType()] = {
10833                 "uri": this.getId(),
10834                 "name": newValues.name,
10835                 "type": newValues.type,
10836                 "handledBy": newValues.handledBy,
10837                 "params": this.buildParamsForRest(newValues.params),
10838                 "actionVariables": this.buildActionVariablesForRest(newValues.actionVariables)
10839             };
10840 
10841             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
10842             handlers = handlers || {};
10843 
10844             this.restRequest(this.getRestUrl(), {
10845                 method: 'PUT',
10846                 success: this.createPutSuccessHandler(this, contentBody, handlers.success),
10847                 error: handlers.error,
10848                 content: contentBody
10849             });
10850 
10851             return this; // Allow cascading
10852         },
10853 
10854 
10855         /**
10856          * @private
10857          * Delete
10858          */
10859         "delete": function ( handlers) {
10860             this.isLoaded();
10861 
10862             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
10863             handlers = handlers || {};
10864 
10865             this.restRequest(this.getRestUrl(), {
10866                 method: 'DELETE',
10867                 success: this.createPutSuccessHandler(this, {}, handlers.success),
10868                 error: handlers.error,
10869                 content: undefined
10870             });
10871 
10872             return this; // Allow cascading
10873         }
10874 
10875 
10876 
10877     });
10878 
10879     window.finesse = window.finesse || {};
10880     window.finesse.restservices = window.finesse.restservices || {};
10881     window.finesse.restservices.WorkflowAction = WorkflowAction;
10882     
10883     return WorkflowAction;
10884 });
10885 
10886 /**
10887 * JavaScript representation of the Finesse WorkflowActions collection
10888 * object which contains a list of WorkflowAction objects.
10889  *
10890  * @requires finesse.clientservices.ClientServices
10891  * @requires Class
10892  * @requires finesse.FinesseBase
10893  * @requires finesse.restservices.RestBase
10894  * @requires finesse.restservices.Dialog
10895  * @requires finesse.restservices.RestCollectionBase
10896  */
10897 
10898 /** @private */
10899 define('restservices/WorkflowActions',[
10900     'restservices/RestCollectionBase',
10901     'restservices/RestBase',
10902     'restservices/WorkflowAction'
10903 ],
10904 function (RestCollectionBase, RestBase, WorkflowAction) {
10905 
10906     var WorkflowActions = RestCollectionBase.extend({
10907         
10908         /**
10909          * @class
10910          * JavaScript representation of a WorkflowActions collection object. 
10911          * @augments finesse.restservices.RestCollectionBase
10912          * @constructs
10913          * @see finesse.restservices.WorkflowAction
10914          * @see finesse.restservices.Workflow
10915          * @see finesse.restservices.Workflows
10916          * @example
10917          *  _workflowActions = _user.getWorkflowActions( {
10918          *      onCollectionAdd : _handleWorkflowActionAdd,
10919          *      onCollectionDelete : _handleWorkflowActionDelete,
10920          *      onLoad : _handleWorkflowActionsLoaded
10921          *  });
10922         */
10923         _fakeConstuctor: function () {
10924             /* This is here to hide the real init constructor from the public docs */
10925         },
10926         
10927         /**
10928          * @private
10929          * JavaScript representation of a WorkflowActions collection object. Also exposes
10930          * methods to operate on the object against the server.
10931          *
10932          * @param {Object} options
10933          *     An object with the following properties:<ul>
10934          *         <li><b>id:</b> The id of the object being constructed</li>
10935          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
10936          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
10937          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
10938          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
10939          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
10940          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
10941          *             <li><b>content:</b> {String} Raw string of response</li>
10942          *             <li><b>object:</b> {Object} Parsed object of response</li>
10943          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
10944          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
10945          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
10946          *             </ul></li>
10947          *         </ul></li>
10948          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
10949          **/
10950         init: function (options) {
10951             this._super(options);           
10952         },
10953 
10954         /**
10955          * @private
10956          * Gets the REST class for the current object - this is the WorkflowActions class.
10957          */
10958         getRestClass: function () {
10959             return WorkflowActions;
10960         },
10961 
10962         /**
10963          * @private
10964          * Gets the REST class for the objects that make up the collection. - this
10965          * is the WorkflowAction class.
10966          */
10967         getRestItemClass: function () {
10968             return WorkflowAction;
10969         },
10970 
10971         /**
10972          * @private
10973          * Gets the REST type for the current object - this is a "WorkflowActions".
10974          */
10975         getRestType: function () {
10976             return "WorkflowActions";
10977         },
10978         
10979         /**
10980          * @private
10981          * Gets the REST type for the objects that make up the collection - this is "WorkflowActions".
10982          */
10983         getRestItemType: function () {
10984             return "WorkflowAction";
10985         },
10986 
10987         /**
10988          * @private
10989          * Override default to indicates that the collection supports making
10990          * requests.
10991          */
10992         supportsRequests: true,
10993 
10994         /**
10995          * @private
10996          * Override default to indicates that the collection subscribes to its objects.
10997          */
10998         supportsRestItemSubscriptions: false,
10999         
11000         /**
11001          * @private
11002          * Retrieve the WorkflowActions.
11003          *
11004          * @returns {finesse.restservices.WorkflowActions}
11005          *     This WorkflowActions object to allow cascading.
11006          */
11007         get: function () {
11008             // set loaded to false so it will rebuild the collection after the get
11009             this._loaded = false;
11010             // reset collection
11011             this._collection = {};
11012             // perform get
11013             this._synchronize();
11014             return this;
11015         }
11016     });
11017 
11018     window.finesse = window.finesse || {};
11019     window.finesse.restservices = window.finesse.restservices || {};
11020     window.finesse.restservices.WorkflowActions = WorkflowActions;
11021         
11022     return WorkflowActions;
11023 });
11024 
11025 /**
11026  * JavaScript representation of the Finesse Workflow object.
11027  *
11028  * @requires finesse.clientservices.ClientServices
11029  * @requires Class
11030  * @requires finesse.FinesseBase
11031  * @requires finesse.restservices.RestBase
11032  */
11033 
11034 /*jslint browser: true, nomen: true, sloppy: true, forin: true */
11035 /*global define,finesse */
11036 
11037 /** @private */
11038 define('restservices/Workflow',[
11039     'restservices/RestBase',
11040     'restservices/WorkflowActions'
11041 ],
11042 function (RestBase, WorkflowActions) {
11043 
11044     var Workflow = RestBase.extend({
11045 
11046         /**
11047          * @class
11048          * JavaScript representation of a Workflow object. Also exposes
11049          * methods to operate on the object against the server.
11050          *
11051          * @param {Object} options
11052          *     An object with the following properties:<ul>
11053          *         <li><b>id:</b> The id of the object being constructed</li>
11054          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
11055          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
11056          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
11057          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
11058          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
11059          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
11060          *             <li><b>content:</b> {String} Raw string of response</li>
11061          *             <li><b>object:</b> {Object} Parsed object of response</li>
11062          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
11063          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
11064          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
11065          *             </ul></li>
11066          *         </ul></li>
11067          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
11068          * @constructs
11069          **/
11070         init: function (options) {
11071             this._super(options);
11072         },
11073 
11074         /**
11075          * @private
11076          * Gets the REST class for the current object - this is the Workflow class.
11077          * @returns {Object} The Workflow class.
11078          */
11079         getRestClass: function () {
11080             return Workflow;
11081         },
11082 
11083         /**
11084          * @private
11085          * Gets the REST type for the current object - this is a "Workflow".
11086          * @returns {String} The Workflow string.
11087          */
11088         getRestType: function () {
11089             return "Workflow";
11090         },
11091 
11092         /**
11093          * @private
11094          * Override default to indicate that this object doesn't support making
11095          * requests.
11096          */
11097         supportsRequests: false,
11098 
11099         /**
11100          * @private
11101          * Override default to indicate that this object doesn't support subscriptions.
11102          */
11103         supportsSubscriptions: false,
11104 
11105         /**
11106          * @private
11107          * Getter for the Uri value.
11108          * @returns {String} The Uri.
11109          */
11110         getUri: function () {
11111             this.isLoaded();
11112             return this.getData().uri;
11113         },
11114 
11115         /**
11116          * Getter for the name.
11117          * @returns {String} The name.
11118          */
11119         getName: function () {
11120             this.isLoaded();
11121             return this.getData().name;
11122         },
11123 
11124         /**
11125          * Getter for the description.
11126          * @returns {String} The description.
11127          */
11128         getDescription: function () {
11129             this.isLoaded();
11130             return this.getData().description;
11131         },
11132 
11133         /**
11134          * Getter for the trigger set.
11135          * @returns {String} The trigger set.
11136          */
11137         getTriggerSet: function () {
11138             this.isLoaded();
11139             return this.getData().TriggerSet;
11140         },
11141 
11142         /**
11143          * Getter for the condition set.
11144          * @returns {String} The condition set.
11145          */
11146         getConditionSet: function () {
11147             this.isLoaded();
11148             return this.getData().ConditionSet;
11149         },
11150         
11151         /**
11152          * Getter for the assigned workflowActions.
11153          * @returns {String} The workflowActions object.
11154          */
11155         getWorkflowActions: function () {
11156             this.isLoaded();
11157             var workflowActions = this.getData().workflowActions;
11158             if (workflowActions === null) {
11159                 workflowActions = "";
11160             }
11161             return workflowActions;
11162         },
11163 
11164         createPutSuccessHandler: function (workflow, contentBody, successHandler) {
11165             return function (rsp) {
11166                 // Update internal structure based on response. Here we
11167                 // inject the contentBody from the PUT request into the
11168                 // rsp.object element to mimic a GET as a way to take
11169                 // advantage of the existing _processResponse method.
11170                 rsp.object = contentBody;
11171                 workflow._processResponse(rsp);
11172 
11173                 //Remove the injected Workflow object before cascading response
11174                 rsp.object = {};
11175 
11176                 //cascade response back to consumer's response handler
11177                 successHandler(rsp);
11178             };
11179         },
11180 
11181         createPostSuccessHandler: function (workflow, contentBody, successHandler) {
11182             return function (rsp) {
11183                 rsp.object = contentBody;
11184                 workflow._processResponse(rsp);
11185 
11186                 //Remove the injected Workflow object before cascading response
11187                 rsp.object = {};
11188 
11189                 //cascade response back to consumer's response handler
11190                 successHandler(rsp);
11191             };
11192         },
11193 
11194         /**
11195          * @private
11196          * Add
11197          */
11198         add: function (newValues, handlers) {
11199             // this.isLoaded();
11200             var contentBody = {};
11201 
11202             contentBody[this.getRestType()] = {
11203                 "name": newValues.name,
11204                 "description": newValues.description,
11205                 "TriggerSet" : newValues.TriggerSet,
11206                 "ConditionSet" : newValues.ConditionSet,
11207                 "workflowActions" : newValues.workflowActions
11208             };
11209 
11210             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
11211             handlers = handlers || {};
11212 
11213             this.restRequest(this.getRestUrl(), {
11214                 method: 'POST',
11215                 success: this.createPostSuccessHandler(this, contentBody, handlers.success),
11216                 error: handlers.error,
11217                 content: contentBody
11218             });
11219 
11220             return this; // Allow cascading
11221         },
11222 
11223         /**
11224          * @private
11225          * Update
11226          */
11227         update: function (newValues, handlers) {
11228             this.isLoaded();
11229             var contentBody = {};
11230 
11231             contentBody[this.getRestType()] = {
11232                 "uri": this.getId(),
11233                 "name": newValues.name,
11234                 "description": newValues.description,
11235                 "TriggerSet" : newValues.TriggerSet,
11236                 "ConditionSet" : newValues.ConditionSet,
11237                 "workflowActions" : newValues.workflowActions
11238             };
11239 
11240             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
11241             handlers = handlers || {};
11242 
11243             this.restRequest(this.getRestUrl(), {
11244                 method: 'PUT',
11245                 success: this.createPutSuccessHandler(this, contentBody, handlers.success),
11246                 error: handlers.error,
11247                 content: contentBody
11248             });
11249 
11250             return this; // Allow cascading
11251         },
11252 
11253 
11254         /**
11255          * @private
11256          * Delete
11257          */
11258         "delete": function (handlers) {
11259             this.isLoaded();
11260 
11261             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
11262             handlers = handlers || {};
11263 
11264             this.restRequest(this.getRestUrl(), {
11265                 method: 'DELETE',
11266                 success: this.createPutSuccessHandler(this, {}, handlers.success),
11267                 error: handlers.error,
11268                 content: undefined
11269             });
11270 
11271             return this; // Allow cascading
11272         }
11273 
11274 
11275 
11276     });
11277 
11278     window.finesse = window.finesse || {};
11279     window.finesse.restservices = window.finesse.restservices || {};
11280     window.finesse.restservices.Workflow = Workflow;
11281 
11282     return Workflow;
11283 });
11284 
11285 /**
11286 * JavaScript representation of the Finesse workflows collection
11287 * object which contains a list of workflow objects.
11288  *
11289  * @requires finesse.clientservices.ClientServices
11290  * @requires Class
11291  * @requires finesse.FinesseBase
11292  * @requires finesse.restservices.RestBase
11293  * @requires finesse.restservices.Dialog
11294  * @requires finesse.restservices.RestCollectionBase
11295  */
11296 
11297 /** @private */
11298 define('restservices/Workflows',[
11299     'restservices/RestCollectionBase',
11300     'restservices/RestBase',
11301     'restservices/Workflow'
11302 ],
11303 function (RestCollectionBase, RestBase, Workflow) {
11304 
11305     var Workflows = RestCollectionBase.extend({
11306 
11307         /**
11308          * @class
11309          * JavaScript representation of a workflows collection object. Also exposes
11310          * methods to operate on the object against the server.
11311          *
11312          * @param {Object} options
11313          *     An object with the following properties:<ul>
11314          *         <li><b>id:</b> The id of the object being constructed</li>
11315          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
11316          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
11317          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
11318          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
11319          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
11320          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
11321          *             <li><b>content:</b> {String} Raw string of response</li>
11322          *             <li><b>object:</b> {Object} Parsed object of response</li>
11323          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
11324          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
11325          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
11326          *             </ul></li>
11327          *         </ul></li>
11328          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
11329          *  @constructs
11330          **/
11331         init: function (options) {
11332             this._super(options);
11333         },
11334 
11335         /**
11336          * @private
11337          * Gets the REST class for the current object - this is the workflows class.
11338          */
11339         getRestClass: function () {
11340             return Workflows;
11341         },
11342 
11343         /**
11344          * @private
11345          * Gets the REST class for the objects that make up the collection. - this
11346          * is the workflow class.
11347          */
11348         getRestItemClass: function () {
11349             return Workflow;
11350         },
11351 
11352         /**
11353          * @private
11354          * Gets the REST type for the current object - this is a "workflows".
11355          */
11356         getRestType: function () {
11357             return "Workflows";
11358         },
11359 
11360         /**
11361          * @private
11362          * Gets the REST type for the objects that make up the collection - this is "workflows".
11363          */
11364         getRestItemType: function () {
11365             return "Workflow";
11366         },
11367 
11368         /**
11369          * @private
11370          * Override default to indicates that the collection supports making requests.
11371          */
11372         supportsRequests: true,
11373 
11374         /**
11375          * @private
11376          * Override default to indicates that the collection does not subscribe to its objects.
11377          */
11378         supportsRestItemSubscriptions: false,
11379 
11380         /**
11381          * @private
11382          * Retrieve the workflows. This call will re-query the server and refresh the collection.
11383          *
11384          * @returns {finesse.restservices.workflows}
11385          *     This workflows object to allow cascading.
11386          */
11387         get: function () {
11388             // set loaded to false so it will rebuild the collection after the get
11389             this._loaded = false;
11390             // reset collection
11391             this._collection = {};
11392             // perform get
11393             this._synchronize();
11394             return this;
11395         }
11396     });
11397 
11398     window.finesse = window.finesse || {};
11399     window.finesse.restservices = window.finesse.restservices || {};
11400     window.finesse.restservices.Workflows = Workflows;
11401         
11402     return Workflows;
11403 });
11404 
11405 /**
11406  * JavaScript representation of the Finesse MediaPropertiesLayout object for the Admin webapp.
11407  * @requires finesse.clientservices.ClientServices
11408  * @requires Class
11409  * @requires finesse.FinesseBase
11410  * @requires finesse.restservices.RestBase
11411  */
11412 
11413 /** The following comment is to prevent jslint errors about 
11414  * using variables before they are defined.
11415  */
11416 /*global finesse*/
11417 
11418 /**
11419  * @class
11420  * JavaScript representation of a MediaPropertiesLayout object for the Admin webapp. Also exposes
11421  * methods to operate on the object against the server.
11422  *
11423  * @constructor
11424  * @param {String} id
11425  *     Not required...
11426  * @param {Object} callbacks
11427  *     An object containing callbacks for instantiation and runtime
11428  * @param {Function} callbacks.onLoad(this)
11429  *     Callback to invoke upon successful instantiation, passes in MediaPropertiesLayout object
11430  * @param {Function} callbacks.onLoadError(rsp)
11431  *     Callback to invoke on instantiation REST request error
11432  *     as passed by finesse.clientservices.ClientServices.ajax()
11433  *     {
11434  *         status: {Number} The HTTP status code returned
11435  *         content: {String} Raw string of response
11436  *         object: {Object} Parsed object of response
11437  *         error: {Object} Wrapped exception that was caught
11438  *         error.errorType: {String} Type of error that was caught
11439  *         error.errorMessage: {String} Message associated with error
11440  *     }
11441  * @param {Function} callbacks.onChange(this)
11442  *     Callback to invoke upon successful update, passes in MediaPropertiesLayout object
11443  * @param {Function} callbacks.onError(rsp)
11444  *     Callback to invoke on update error (refresh or event)
11445  *     as passed by finesse.clientservices.ClientServices.ajax()
11446  *     {
11447  *         status: {Number} The HTTP status code returned
11448  *         content: {String} Raw string of response
11449  *         object: {Object} Parsed object of response
11450  *         error: {Object} Wrapped exception that was caught
11451  *         error.errorType: {String} Type of error that was caught
11452  *         error.errorMessage: {String} Message associated with error
11453  *     }
11454  */
11455 
11456 /** @private */
11457 define('restservices/MediaPropertiesLayout',['restservices/RestBase'], function (RestBase) {
11458     var MediaPropertiesLayout = RestBase.extend(/** @lends finesse.restservices.MediaPropertiesLayout.prototype */{
11459 
11460         /**
11461          * @class
11462          * The MediaPropertiesLayout handles which call variables are associated with Dialogs.
11463          * 
11464          * @augments finesse.restservices.RestBase
11465          * @see finesse.restservices.Dialog#getMediaProperties
11466          * @see finesse.restservices.User#getMediaPropertiesLayout
11467          * @constructs
11468          */
11469         _fakeConstuctor: function () {
11470             /* This is here to hide the real init constructor from the public docs */
11471         },
11472         
11473         /**
11474          * @private
11475          * JavaScript representation of a MediaPropertiesLayout object. Also exposes
11476          * methods to operate on the object against the server.
11477          *
11478          * @param {Object} options
11479          *     An object with the following properties:<ul>
11480          *         <li><b>id:</b> The id of the object being constructed</li>
11481          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
11482          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
11483          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
11484          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
11485          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
11486          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
11487          *             <li><b>content:</b> {String} Raw string of response</li>
11488          *             <li><b>object:</b> {Object} Parsed object of response</li>
11489          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
11490          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
11491          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
11492          *             </ul></li>
11493          *         </ul></li>
11494          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
11495          **/
11496         init: function (options) {
11497             this._super(options);
11498         },
11499 
11500         /**
11501          * @private
11502          * Gets the REST class for the current object - this is the MediaPropertiesLayout object.
11503          */
11504         getRestClass: function () {
11505             return MediaPropertiesLayout;
11506         },
11507 
11508         /**
11509          * @private
11510          * Gets the REST type for the current object - this is a "MediaPropertiesLayout".
11511          */
11512         getRestType: function () {
11513             return "MediaPropertiesLayout";
11514         },
11515 
11516         /**
11517          * @private
11518          * Returns whether this object supports subscriptions
11519          */
11520         supportsSubscriptions: false,
11521 
11522         /**
11523          * Getter for the name.
11524          * @returns {String} The name.
11525          */
11526         getName: function () {
11527             this.isLoaded();
11528             return this._data.name;
11529         },
11530 
11531         /**
11532          * Getter for the description.
11533          * @returns {String} The description.
11534          */
11535         getDescription: function () {
11536             this.isLoaded();
11537             return this._data.description || "";
11538         },
11539 
11540         /**
11541          * Getter for the layout type (should be DEFAULT or CUSTOM).
11542          * @returns {String} The layout type.
11543          */
11544         getType: function () {
11545             this.isLoaded();
11546             return this._data.type || "";
11547         },
11548 
11549         /**
11550          * Retrieve the media properties layout. This call will re-query the server and refresh the layout object.
11551          * @returns {finesse.restservices.MediaPropertiesLayout}
11552          *     This MediaPropertiesLayout object to allow cascading
11553          */
11554         get: function () {
11555             this._synchronize();
11556 
11557             return this; //Allow cascading
11558         },
11559 
11560         /**
11561          * Gets the data for this object.
11562          * 
11563          * Performs safe conversion from raw API data to ensure that the returned layout object
11564          * always has a header with correct entry fields, and exactly two columns with lists of entries.
11565          *
11566          * @returns {finesse.restservices.MediaPropertiesLayout.Object} Data in columns (unless only one defined).
11567          */
11568         getData: function () {
11569 
11570             var layout = this._data, result, _addColumnData;
11571 
11572             result = this.getEmptyData();
11573             result.name = layout.name;
11574             result.description = layout.description;
11575             result.type = layout.type;
11576 
11577             /**
11578              * @private
11579              */
11580             _addColumnData = function (entryData, colIndex) {
11581 
11582                 if (!entryData) {
11583                     //If there's no entry data at all, rewrite entryData to be an empty collection of entries
11584                     entryData = {};
11585                 } else if (entryData.mediaProperty) {
11586                     //If entryData contains the keys for a single entry rather than being a collection of entries,
11587                     //rewrite it to be a collection containing a single entry
11588                     entryData = { "": entryData };
11589                 }
11590 
11591                 //Add each of the entries in the list to the column
11592                 jQuery.each(entryData, function (i, entryData) {
11593 
11594                     //If the entry has no displayName specified, explicitly set it to the empty string
11595                     if (!entryData.displayName) {
11596                         entryData.displayName = "";
11597                     }
11598 
11599                     result.columns[colIndex].push(entryData);
11600 
11601                 });
11602 
11603             };
11604 
11605             //The header should only contain a single entry
11606             if (layout.header && layout.header.entry) {
11607 
11608                 //If the entry has no displayName specified, explicitly set it to the empty string
11609                 if (!layout.header.entry.displayName) {
11610                     layout.header.entry.displayName = "";
11611                 }
11612 
11613                 result.header = layout.header.entry;
11614 
11615             } else {
11616 
11617                 throw "MediaPropertiesLayout.getData() - Header does not contain an entry";
11618 
11619             }
11620 
11621             //If the column object contains an entry object that wasn't part of a list of entries,
11622             //it must be a single right-hand entry object (left-hand entry object would be part of a list.)
11623             //Force the entry object to be the 2nd element in an otherwise-empty list.
11624             if (layout.column && layout.column.entry) {
11625                 layout.column = [
11626                     null,
11627                     { "entry": layout.column.entry }
11628                 ];
11629             }
11630 
11631             if (layout.column && layout.column.length > 0 && layout.column.length <= 2) {
11632 
11633                 //Render left column entries
11634                 if (layout.column[0] && layout.column[0].entry) {
11635                     _addColumnData(layout.column[0].entry, 0);
11636                 }
11637 
11638                 //Render right column entries
11639                 if (layout.column[1] && layout.column[1].entry) {
11640                     _addColumnData(layout.column[1].entry, 1);
11641                 }
11642 
11643             }
11644 
11645             return result;
11646 
11647         },
11648 
11649         /**
11650          * @private
11651          * Empty/template version of getData().
11652          *
11653          * Used by getData(), and by callers of getData() in error cases.
11654          */
11655         getEmptyData: function () {
11656 
11657             return {
11658                 header : {
11659                     displayName: null,
11660                     mediaProperty: null
11661                 },
11662                 columns : [[], []]
11663             };
11664 
11665         },
11666 
11667         /**
11668          * Update the layout of this MediaPropertiesLayout
11669          * @param {Object} layout
11670          *      The object representation of the layout you are setting
11671          * @param {finesse.interfaces.RequestHandlers} handlers
11672          *      An object containing the handlers for the request
11673          * @returns {finesse.restservices.MediaPropertiesLayout}
11674          *      This MediaPropertiesLayout object to allow cascading
11675          * @private
11676          */
11677         update: function (newLayoutObject, handlers) {
11678             var contentBody = {};
11679 
11680             // Make sure type is kept the same
11681             newLayoutObject.type = this.getType();
11682 
11683             contentBody[this.getRestType()] = newLayoutObject;
11684 
11685             //Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
11686             handlers = handlers || {};
11687 
11688             this.restRequest(this.getRestUrl(), {
11689                 method: 'PUT',
11690                 success: handlers.success,
11691                 error: handlers.error,
11692                 content: contentBody
11693             });
11694 
11695             return this; // Allow cascading
11696         },
11697 
11698         /**
11699          * Create a new MediaPropertiesLayout object with the layout passed in
11700          * @param {Object} layout
11701          *      The object representation of the layout you are creating
11702          * @param {finesse.interfaces.RequestHandlers} handlers
11703          *      An object containing the handlers for the request
11704          * @returns {finesse.restservices.MediaPropertiesLayout}
11705          *      This MediaPropertiesLayout object to allow cascading
11706          * @private
11707          */
11708         add: function (layout, handlers) {
11709             var contentBody = {};
11710 
11711             contentBody[this.getRestType()] = layout;
11712 
11713             handlers = handlers || {};
11714 
11715             this.restRequest(this.getRestUrl(), {
11716                 method: 'POST',
11717                 success: handlers.success,
11718                 error: handlers.error,
11719                 content: contentBody
11720             });
11721 
11722             return this; // Allow cascading
11723         },
11724 
11725         /**
11726          * Delete this MediaPropertiesLayout
11727          * @param {finesse.interfaces.RequestHandlers} handlers
11728          *      An object containing the handlers for the request
11729          * @returns {finesse.restservices.MediaPropertiesLayout}
11730          *      This MediaPropertiesLayout object to allow cascading
11731          * @private
11732          */
11733         "delete": function (handlers) {
11734             handlers = handlers || {};
11735 
11736             this.restRequest(this.getRestUrl(), {
11737                 method: 'DELETE',
11738                 success: handlers.success,
11739                 error: handlers.error,
11740                 content: undefined
11741             });
11742 
11743             return this; // Allow cascading
11744         }
11745 
11746     });
11747     
11748     MediaPropertiesLayout.Object = /** @lends finesse.restservices.MediaPropertiesLayout.Object.prototype */ {
11749         /**
11750          * @class Format of MediaPropertiesLayout Object.<br>
11751          * Object { <ul>
11752          *      <li>header : { <ul>
11753          *          <li>dispayName {String} 
11754          *          <li>mediaProperty {String}</ul>}
11755          *      <li>columns : { <ul>
11756          *          <li>[ [] , [] ]
11757          *          </ul>
11758          *      where column arrays consists of the same Object format as header.<br>
11759          *          }</ul>
11760          *      }<br>         
11761          * @constructs
11762          */
11763         _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
11764         
11765     };
11766 
11767 	window.finesse = window.finesse || {};
11768     window.finesse.restservices = window.finesse.restservices || {};
11769     window.finesse.restservices.MediaPropertiesLayout = MediaPropertiesLayout;
11770     
11771     return MediaPropertiesLayout;
11772 });
11773 
11774 /**
11775  * JavaScript representation of the Finesse MediaPropertiesLayout object for a User
11776  *
11777  * @requires MediaPropertiesLayout
11778  * @requires ClientServices
11779  * @requires finesse.FinesseBase
11780  * @requires finesse.restservices.RestBase
11781  */
11782 
11783 /** The following comment is to prevent jslint errors about 
11784  * using variables before they are defined.
11785  */
11786 /*global finesse*/
11787 
11788 /** @private */
11789 define('restservices/UserMediaPropertiesLayout',['restservices/MediaPropertiesLayout'], function (MediaPropertiesLayout) {
11790      var UserMediaPropertiesLayout = MediaPropertiesLayout.extend(/** @lends finesse.restservices.UserMediaPropertiesLayout.prototype */{
11791 
11792 		/**
11793 		 * @class
11794 		 * JavaScript representation of a UserMediaPropertiesLayout collection object. Also exposes
11795 		 * methods to operate on the object against the server.
11796 		 * 
11797 		 * @param {Object} options
11798 		 * An object with the following properties:<ul>
11799 		 *        <li><b>id:</b> The id of the object being constructed</li>
11800 		 *        <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
11801 		 *        <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
11802 		 *        <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
11803 		 *        <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
11804 		 *        <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
11805 		 *            <li><b>status:</b> {Number} The HTTP status code returned</li>
11806 		 *            <li><b>content:</b> {String} Raw string of response</li>
11807 		 *            <li><b>object:</b> {Object} Parsed object of response</li>
11808 		 *            <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
11809 		 *                <li><b>errorType:</b> {String} Type of error that was caught</li>
11810 		 *                <li><b>errorMessage:</b> {String} Message associated with error</li>
11811 		 *            </ul></li>
11812 		 *        </ul></li>
11813 		 *        <li><b>parentObj: (optional)</b> The parent object</li></ul>
11814 		 * @constructs
11815 		**/
11816 		init: function (options) {
11817 		    this._super(options);
11818 		},
11819 		
11820 		/**
11821 		 * @private
11822 		 * Gets the REST class for the current object - this is the UserMediaPropertiesLayout class.
11823 		 */
11824 		getRestClass: function () {
11825 		    return UserMediaPropertiesLayout;
11826 		},
11827 
11828         /**
11829          * Overrides the parent class.  Returns the url for the UserMediaPropertiesLayout resource
11830          */
11831         getRestUrl: function () {
11832             return ("/finesse/api/User/" + this.getId() + "/" + this.getRestType());
11833         },
11834 
11835         /**
11836          * @private
11837          * Override to throw an error because we cannot do an update on the User's
11838          * MediaPropertiesLayout node
11839          */
11840         update: function (layout, handlers) {
11841             throw new Error("update(): Cannot update layout for User's MediaPropertiesLayout");
11842         },
11843 
11844         /**
11845          * @private
11846          * Override to throw an error because we cannot create a new layout on the User's
11847          * MediaPropertiesLayout node
11848          */
11849         add: function (layout, handlers) {
11850             throw new Error("add(): Cannot create a new layout for User's MediaPropertiesLayout");
11851         },
11852 
11853         /**
11854          * @private
11855          * Override to throw an error because we cannot delete the layout on the User's
11856          * MediaPropertiesLayout node
11857          */
11858         "delete": function (layout, handlers) {
11859             throw new Error("delete(): Cannot delete the layout for User's MediaPropertiesLayout");
11860         }
11861 
11862     });
11863 	
11864 	window.finesse = window.finesse || {};
11865     window.finesse.restservices = window.finesse.restservices || {};
11866     window.finesse.restservices.UserMediaPropertiesLayout = UserMediaPropertiesLayout;
11867     
11868     return UserMediaPropertiesLayout;
11869 });
11870 
11871 /**
11872 * JavaScript representation of the Finesse mediaPropertiesLayouts collection
11873 * object which contains a list of mediaPropertiesLayout objects.
11874  *
11875  * @requires finesse.clientservices.ClientServices
11876  * @requires Class
11877  * @requires finesse.FinesseBase
11878  * @requires finesse.restservices.RestBase
11879  * @requires finesse.restservices.Dialog
11880  * @requires finesse.restservices.RestCollectionBase
11881  */
11882 
11883 /** @private */
11884 define('restservices/MediaPropertiesLayouts',[
11885     'restservices/RestCollectionBase',
11886     'restservices/RestBase',
11887     'restservices/MediaPropertiesLayout'
11888 ],
11889 function (RestCollectionBase, RestBase, MediaPropertiesLayout) {
11890 
11891     var MediaPropertiesLayouts = RestCollectionBase.extend({
11892 
11893         /**
11894          * @class
11895          * JavaScript representation of a mediaPropertiesLayouts collection object. Also exposes
11896          * methods to operate on the object against the server.
11897          *
11898          * @param {Object} options
11899          *     An object with the following properties:<ul>
11900          *         <li><b>id:</b> The id of the object being constructed</li>
11901          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
11902          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
11903          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
11904          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
11905          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
11906          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
11907          *             <li><b>content:</b> {String} Raw string of response</li>
11908          *             <li><b>object:</b> {Object} Parsed object of response</li>
11909          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
11910          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
11911          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
11912          *             </ul></li>
11913          *         </ul></li>
11914          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
11915          *  @constructs
11916          **/
11917         init: function (options) {
11918             this._super(options);
11919         },
11920 
11921         /**
11922          * @private
11923          * Gets the REST class for the current object - this is the mediaPropertiesLayouts class.
11924          */
11925         getRestClass: function () {
11926             return MediaPropertiesLayouts;
11927         },
11928 
11929         /**
11930          * @private
11931          * Gets the REST class for the objects that make up the collection. - this
11932          * is the mediaPropertiesLayout class.
11933          */
11934         getRestItemClass: function () {
11935             return MediaPropertiesLayout;
11936         },
11937 
11938         /**
11939          * @private
11940          * Gets the REST type for the current object - this is a "mediaPropertiesLayouts".
11941          */
11942         getRestType: function () {
11943             return "MediaPropertiesLayouts";
11944         },
11945 
11946         /**
11947          * @private
11948          * Gets the REST type for the objects that make up the collection - this is "mediaPropertiesLayouts".
11949          */
11950         getRestItemType: function () {
11951             return "MediaPropertiesLayout";
11952         },
11953 
11954         /**
11955          * @private
11956          * Override default to indicates that the collection supports making requests.
11957          */
11958         supportsRequests: true,
11959 
11960         /**
11961          * @private
11962          * Override default to indicates that the collection does not subscribe to its objects.
11963          */
11964         supportsRestItemSubscriptions: false,
11965 
11966         /**
11967          * @private
11968          * Retrieve the MediaPropertiesLayouts. This call will re-query the server and refresh the collection.
11969          *
11970          * @returns {finesse.restservices.MediaPropertiesLayouts}
11971          *     This MediaPropertiesLayouts object to allow cascading.
11972          */
11973         get: function () {
11974             // set loaded to false so it will rebuild the collection after the get
11975             this._loaded = false;
11976             // reset collection
11977             this._collection = {};
11978             // perform get
11979             this._synchronize();
11980             return this;
11981         }
11982     });
11983 
11984     window.finesse = window.finesse || {};
11985     window.finesse.restservices = window.finesse.restservices || {};
11986     window.finesse.restservices.MediaPropertiesLayouts = MediaPropertiesLayouts;
11987         
11988     return MediaPropertiesLayouts;
11989 });
11990 
11991 /**
11992  * JavaScript representation of the Finesse MediaPropertiesLayout object for a User
11993  *
11994  * @requires MediaPropertiesLayout
11995  * @requires ClientServices
11996  * @requires finesse.FinesseBase
11997  * @requires finesse.restservices.RestBase
11998  */
11999 
12000 /** The following comment is to prevent jslint errors about 
12001  * using variables before they are defined.
12002  */
12003 /*global finesse*/
12004 
12005 /** @private */
12006 define('restservices/UserMediaPropertiesLayouts',[
12007 	'restservices/MediaPropertiesLayouts',
12008 	'restservices/UserMediaPropertiesLayout'
12009 ],
12010 function (MediaPropertiesLayouts, UserMediaPropertiesLayout) {
12011      var UserMediaPropertiesLayouts = MediaPropertiesLayouts.extend(/** @lends finesse.restservices.UserMediaPropertiesLayouts.prototype */{
12012 
12013 		/**
12014 		 * @class
12015 		 * JavaScript representation of a UserMediaPropertiesLayouts collection object. Also exposes
12016 		 * methods to operate on the object against the server.
12017 		 * 
12018 		 * @param {Object} options
12019 		 * An object with the following properties:<ul>
12020 		 *        <li><b>id:</b> The id of the object being constructed</li>
12021 		 *        <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
12022 		 *        <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
12023 		 *        <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
12024 		 *        <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
12025 		 *        <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
12026 		 *            <li><b>status:</b> {Number} The HTTP status code returned</li>
12027 		 *            <li><b>content:</b> {String} Raw string of response</li>
12028 		 *            <li><b>object:</b> {Object} Parsed object of response</li>
12029 		 *            <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
12030 		 *                <li><b>errorType:</b> {String} Type of error that was caught</li>
12031 		 *                <li><b>errorMessage:</b> {String} Message associated with error</li>
12032 		 *            </ul></li>
12033 		 *        </ul></li>
12034 		 *        <li><b>parentObj: (optional)</b> The parent object</li></ul>
12035 		 * @constructs
12036 		**/
12037 		init: function (options) {
12038 		    this._super(options);
12039 		},
12040 		
12041 		/**
12042 		 * @private
12043 		 * Gets the REST class for the current object - this is the UserMediaPropertiesLayouts class.
12044 		 */
12045 		getRestClass: function () {
12046 		    return UserMediaPropertiesLayouts;
12047 		},
12048 
12049         /**
12050          * @private
12051          * Gets the REST class for the objects that make up the collection. - this
12052          * is the UserMediaPropertiesLayout class.
12053          */
12054 		getRestItemClass: function() {
12055 			return UserMediaPropertiesLayout;
12056 		}
12057     });
12058 	
12059 	window.finesse = window.finesse || {};
12060     window.finesse.restservices = window.finesse.restservices || {};
12061     window.finesse.restservices.UserMediaPropertiesLayouts = UserMediaPropertiesLayouts;
12062     
12063     return UserMediaPropertiesLayouts;
12064 });
12065 
12066 /**
12067  * JavaScript representation of the Finesse Dialog object for non-voice media.
12068  *
12069  * @requires finesse.restservices.DialogBase
12070  */
12071 
12072 /** @private */
12073 define('restservices/MediaDialog',[
12074         'restservices/DialogBase'
12075     ],
12076     function (DialogBase) {
12077         var MediaDialog = DialogBase.extend(/** @lends finesse.restservices.MediaDialog.prototype */{
12078 
12079             /**
12080              * @private
12081              *
12082              * Support requests so that applications can refresh non-voice dialogs when the media channel that the
12083              * dialog belongs to is interrupted. An event is not sent to update a dialog's actions when the media is
12084              * interrupted so a refresh is required so that the application can get an updated set of actions.
12085              */
12086             supportsRequests: true,
12087 
12088             /**
12089              * @class
12090              * A MediaDialog is an attempted connection between or among multiple participants,
12091              * for example, a chat or email.
12092              *
12093              * @augments finesse.restservices.DialogBase
12094              * @constructs
12095              */
12096             _fakeConstuctor: function () {
12097                 /* This is here to hide the real init constructor from the public docs */
12098             },
12099 
12100             /**
12101              * @private
12102              *
12103              * @param {Object} options
12104              *     An object with the following properties:<ul>
12105              *         <li><b>id:</b> The id of the object being constructed</li>
12106              *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
12107              *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
12108              *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
12109              *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
12110              *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
12111              *             <li><b>status:</b> {Number} The HTTP status code returned</li>
12112              *             <li><b>content:</b> {String} Raw string of response</li>
12113              *             <li><b>object:</b> {Object} Parsed object of response</li>
12114              *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
12115              *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
12116              *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
12117              *             </ul></li>
12118              *         </ul></li>
12119              *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
12120              **/
12121             init: function (options) {
12122                 this._super(options);
12123             },
12124 
12125             /**
12126              * @private
12127              * Gets the REST class for the current object - this is the MediaDialog class.
12128              * @returns {Object} The Dialog class.
12129              */
12130             getRestClass: function () {
12131                 return MediaDialog;
12132             },
12133 
12134             /**
12135              * Transfers a Media Dialog to the target specified
12136              * @param {String} target script selector
12137              *     The script selector to transfer the dialog.
12138              * @param {finesse.interfaces.RequestHandlers} handlers
12139              *     An object containing the handlers for the request
12140              */
12141             transfer: function(target, handlers) {
12142                 this.setTaskState(MediaDialog.TaskActions.TRANSFER, handlers, target);
12143             },
12144 
12145             /**
12146              * Set the state on a Media Dialog based on the action given.
12147              * @param {finesse.restservices.MediaDialog.TaskActions} action
12148              *     The action string indicating the action to invoke on a Media dialog.
12149              * @param {finesse.interfaces.RequestHandlers} handlers
12150              *     An object containing the handlers for the request
12151              * @param {String} target
12152              *    The target to transfer the dialog.  Pass null if not transfer
12153              */
12154             setTaskState: function (state,handlers,target) {
12155                 this.isLoaded();
12156 
12157                 var contentBody = {};
12158                 contentBody[this.getRestType()] = {
12159                     "requestedAction": state,
12160                     "target": target
12161                 };
12162                 // (nonexistent) keys to be read as undefined
12163                 handlers = handlers || {};
12164                 this.restRequest(this.getRestUrl(), {
12165                     method: 'PUT',
12166                     success: handlers.success,
12167                     error: handlers.error,
12168                     content: contentBody
12169                 });
12170                 return this; // Allow cascading
12171             }
12172 
12173         });
12174 
12175         MediaDialog.TaskActions = /** @lends finesse.restservices.MediaDialog.TaskActions.prototype */ {
12176             /**
12177              * Accept an incoming task.
12178              */
12179             ACCEPT: "ACCEPT",
12180             /**
12181              * Start work on a task.
12182              */
12183             START : "START",
12184             /**
12185              * Pause work on an active task.
12186              */
12187             PAUSE: "PAUSE",
12188             /**
12189              * Resume work on a paused task.
12190              */
12191             RESUME : "RESUME",
12192             /**
12193              * Wrap up work for a task.
12194              */
12195             WRAP_UP : "WRAP_UP",
12196             /**
12197              * Transfer task to another target.
12198              */
12199             TRANSFER : "TRANSFER",
12200             /**
12201              * End a task.
12202              */
12203             CLOSE : "CLOSE",
12204             /**
12205              * @class Set of action constants for a Media Dialog.  These should be used for
12206              * {@link finesse.restservices.MediaDialog#setTaskState}.
12207              * @constructs
12208              */
12209             _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
12210         };
12211 
12212 
12213 
12214         MediaDialog.States = /** @lends finesse.restservices.MediaDialog.States.prototype */ {
12215             /**
12216              * Indicates that the task has been offered to an agent.
12217              */
12218             OFFERED: "OFFERED",
12219             /**
12220              * Indicates that the user has started work on the task.
12221              */
12222             ACTIVE: "ACTIVE",
12223             /**
12224              * Indicates that the user has paused work on the task.
12225              */
12226             PAUSED: "PAUSED",
12227             /**
12228              * Indicates that the user is wrapping up the task.
12229              */
12230             WRAPPING_UP: "WRAPPING_UP",
12231             /**
12232              * Indicates that the task was interrupted.
12233              */
12234             INTERRUPTED: "INTERRUPTED",
12235             /**
12236              * Indicates that the task has ended.
12237              */
12238             CLOSED: "CLOSED",
12239             /**
12240              * Indicates that the user has accepted the task.
12241              */
12242             ACCEPTED: "ACCEPTED",
12243             /**
12244              * Finesse has recovered a task after a failure. It does not have enough information to build a complete set
12245              * of actions for the task so it only allows the user to end the task.
12246              */
12247             UNKNOWN: "UNKNOWN",
12248             /**
12249              * @class Possible Dialog State constants.
12250              * The State flow of a typical in-bound Dialog is as follows: OFFERED, ACCEPTED, ACTIVE, CLOSED.
12251              * @constructs
12252              */
12253             _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
12254         };
12255 
12256         MediaDialog.ParticipantStates = MediaDialog.States;
12257 
12258         window.finesse = window.finesse || {};
12259         window.finesse.restservices = window.finesse.restservices || {};
12260         window.finesse.restservices.MediaDialog = MediaDialog;
12261 
12262 
12263         return MediaDialog;
12264     });
12265 
12266 /* Simple JavaScript Inheritance
12267  * By John Resig http://ejohn.org/
12268  * MIT Licensed.
12269  */
12270 // Inspired by base2 and Prototype
12271 define('restservices/../../thirdparty/Class',[], function () {
12272         var initializing = false, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;
12273         // The base Class implementation (does nothing)
12274         /** @private */
12275         Class = function(){};
12276         
12277         // Create a new Class that inherits from this class
12278         /** @private */
12279         Class.extend = function(prop) {
12280           var _super = this.prototype;
12281           
12282           // Instantiate a base class (but only create the instance,
12283           // don't run the init constructor)
12284           initializing = true;
12285           var prototype = new this();
12286           initializing = false;
12287           
12288           // Copy the properties over onto the new prototype
12289           for (var name in prop) {
12290             // Check if we're overwriting an existing function
12291             prototype[name] = typeof prop[name] == "function" && 
12292               typeof _super[name] == "function" && fnTest.test(prop[name]) ?
12293               (function(name, fn){
12294                 return function() {
12295                   var tmp = this._super;
12296                   
12297                   // Add a new ._super() method that is the same method
12298                   // but on the super-class
12299                   this._super = _super[name];
12300                   
12301                   // The method only need to be bound temporarily, so we
12302                   // remove it when we're done executing
12303                   var ret = fn.apply(this, arguments);        
12304                   this._super = tmp;
12305                   
12306                   return ret;
12307                 };
12308               })(name, prop[name]) :
12309               prop[name];
12310           }
12311           
12312           // The dummy class constructor
12313           /** @private */
12314           function Class() {
12315             // All construction is actually done in the init method
12316             if ( !initializing && this.init )
12317               this.init.apply(this, arguments);
12318           }
12319           
12320           // Populate our constructed prototype object
12321           Class.prototype = prototype;
12322           
12323           // Enforce the constructor to be what we expect
12324           Class.prototype.constructor = Class;
12325 
12326           // And make this class extendable
12327           Class.extend = arguments.callee;
12328           
12329           return Class;
12330         };
12331     return Class;
12332 });
12333 
12334 /**
12335  * Class used to establish a Media/Dialogs subscription to be shared by MediaDialogs objects for non-voice
12336  * dialog events.
12337  *
12338  * @requires Class
12339  * @requires finesse.clientservices.ClientServices
12340  * @requires finesse.clientservices.Topics
12341  */
12342 /** @private */
12343 define('restservices/MediaDialogsSubscriptionManager',[
12344         "../../thirdparty/Class",
12345         "clientservices/ClientServices",
12346         "clientservices/Topics"
12347     ],
12348     function (Class, ClientServices, Topics) {
12349         var MediaDialogsSubscriptionManager = Class.extend(/** @lends finesse.restservices.MediaDialogsSubscriptionManager.prototype */{
12350 
12351             /**
12352              * Map used to track the MediaDialogs objects managed by this object.
12353              * @private
12354              */
12355             _mediaDialogsMap: {},
12356 
12357             /**
12358              * The regex used to match the source of BOSH/XMPP events. If an event matches this source, the event will
12359              * be processed by the subscription manager.
12360              * @private
12361              */
12362             _sourceRegEx: null,
12363 
12364             /**
12365              * The subscription ID/handle for Media/Dialogs events.
12366              * @private
12367              */
12368             _subscriptionId: null,
12369 
12370             _fakeConstuctor: function ()
12371             {
12372                 /* This is here to hide the real init constructor from the public docs */
12373             },
12374 
12375             /**
12376              * Create the regex used to match the source of BOSH/XMPP events. If an event matches this source, the event
12377              * will be processed by the subscription manager.
12378              *
12379              * @param {Object} restObj
12380              *     The restObj whose REST URL will be used as the base of the regex.
12381              *
12382              * @returns {RegExp}
12383              *     The regex used to match the source of XMPP events.
12384              * @private
12385              */
12386             _makeSourceRegEx: function(restObj)
12387             {
12388                 return new RegExp("^" + restObj.getRestUrl() + "/Media/[0-9]+/Dialogs$");
12389             },
12390 
12391             /**
12392              * Return the media ID associated with the update.
12393              *
12394              * @param {Object} update
12395              *     The content of the update event.
12396              *
12397              * @returns {String}
12398              *     The media ID associated with the update.
12399              * @private
12400              */
12401             _getMediaIdFromEventUpdate: function(update)
12402             {
12403                 var parts = update.object.Update.source.split("/");
12404                 return parts[MediaDialogsSubscriptionManager.MEDIA_ID_INDEX_IN_SOURCE];
12405             },
12406 
12407             /**
12408              * Handler for update events. This handler forwards the update to the MediaDialogs object associated with
12409              * the media ID carried in the update event.
12410              *
12411              * @param {Object} update
12412              *     The content of the update event.
12413              *
12414              * @private
12415              */
12416             _updateEventHandler: function(update)
12417             {
12418                 var mediaId = this._getMediaIdFromEventUpdate(update),
12419                     mediaDialogs = this._mediaDialogsMap[mediaId];
12420 
12421                 if ( mediaDialogs )
12422                 {
12423                     mediaDialogs._updateEventHandler(mediaDialogs, update);
12424                 }
12425             },
12426 
12427             /**
12428              * Return the media ID associated with the REST update.
12429              *
12430              * @param {Object} update
12431              *     The content of the REST update.
12432              *
12433              * @returns {String}
12434              *     The media ID associated with the update.
12435              * @private
12436              */
12437             _getMediaIdFromRestUpdate: function(update)
12438             {
12439                 return update.object.Update.data.dialog.mediaProperties.mediaId;
12440             },
12441 
12442             /**
12443              * Handler for REST updates. This handler forwards the update to the MediaDialogs object associated with
12444              * the media ID carried in the REST update.
12445              *
12446              * @param {Object} update
12447              *     The content of the REST update.
12448              *
12449              * @private
12450              */
12451             _processRestItemUpdate: function(update)
12452             {
12453                 var mediaId = this._getMediaIdFromRestUpdate(update),
12454                     mediaDialogs = this._mediaDialogsMap[mediaId];
12455 
12456                 if ( mediaDialogs )
12457                 {
12458                     mediaDialogs._processRestItemUpdate(update);
12459                 }
12460             },
12461 
12462             /**
12463              * Utility method to create a callback to be given to OpenAjax to invoke when a message
12464              * is published on the topic of our REST URL (also XEP-0060 node).
12465              * This needs to be its own defined method so that subclasses can have their own implementation.
12466              * @returns {Function} callback(update)
12467              *     The callback to be invoked when an update event is received. This callback will
12468              *     process the update by notifying the MediaDialogs object associated with the media ID in the update.
12469              *
12470              * @private
12471              */
12472             _createPubsubCallback: function ()
12473             {
12474                 var _this = this;
12475                 return function (update) {
12476                     //If the source of the update is our REST URL, this means the collection itself is modified
12477                     if (update.object.Update.source.match(_this._sourceRegEx)) {
12478                         _this._updateEventHandler(update);
12479                     } else {
12480                         //Otherwise, it is safe to assume that if we got an event on our topic, it must be a
12481                         //rest item update of one of our children that was published on our node (OpenAjax topic)
12482                         _this._processRestItemUpdate(update);
12483                     }
12484                 };
12485             },
12486 
12487             /**
12488              * Track the MediaDialogs object so that events and REST updates signalled to this subscription manager
12489              * can be forwarded to the given MediaDialogs object.
12490              * @param {finesse.restservices.MediaDialogs} mediaDialogs MediaDialogs object to be tracked by the
12491              *     subscription manager.
12492              * @private
12493              */
12494             _manage: function(mediaDialogs)
12495             {
12496                 this._mediaDialogsMap[mediaDialogs.getMedia().getMediaId()] = mediaDialogs;
12497             },
12498 
12499             /**
12500              * Stop tracking the MediaDialogs object. Events and REST updates signalled to this subscription manager
12501              * will no longer be forwarded to the given MediaDialogs object.
12502              * @param {finesse.restservices.MediaDialogs} mediaDialogs MediaDialogs object to no longer track.
12503              * @private
12504              */
12505             _unManage: function(mediaDialogs)
12506             {
12507                 var mediaId = mediaDialogs.getMedia().getMediaId();
12508                 if ( this._callbackMap[mediaId] )
12509                 {
12510                     delete this._callbackMap[mediaId];
12511                 }
12512             },
12513 
12514             /**
12515              * @class
12516              * An internal class used to establish a Media/Dialogs subscription to be shared by MediaDialogs objects
12517              * for non-voice dialog events.
12518              *
12519              * @constructor
12520              * @param {RestBase} restObj
12521              *     A RestBase object used to build the user portion of XMPP and REST paths.
12522              * @constructs
12523              */
12524             init: function (restObj)
12525             {
12526                 var _this;
12527 
12528                 this._sourceRegEx = this._makeSourceRegEx(restObj);
12529             },
12530 
12531             /**
12532              * Create the BOSH/XMPP subscription used for non-voice dialog events. Additionally, store the given
12533              * MediaDialogs object so that events for the object can be forwarded to it.
12534              *
12535              * @param {finesse.restservices.MediaDialogs} mediaDialogs a MediaDialogs object to manage (forward events)
12536              * @param {Object} callbacks an object containing success and error callbacks used to signal the result of
12537              *     the subscription.
12538              * @returns {MediaDialogsSubscriptionManager}
12539              * @private
12540              */
12541             subscribe: function (mediaDialogs, callbacks)
12542             {
12543                 var topic = Topics.getTopic(mediaDialogs.getXMPPNodePath()),
12544                     _this = this,
12545                     handlers,
12546                     successful;
12547 
12548                 callbacks = callbacks || {};
12549 
12550                 handlers = {
12551                     /** @private */
12552                     success: function () {
12553                         // Add item to the refresh list in ClientServices to refresh if
12554                         // we recover due to our resilient connection.
12555                         ClientServices.addToRefreshList(_this);
12556                         if (typeof callbacks.success === "function") {
12557                             callbacks.success();
12558                         }
12559                     },
12560                     /** @private */
12561                     error: function (err) {
12562                         if (successful) {
12563                             _this._unManage(mediaDialogs);
12564                             ClientServices.unsubscribe(topic);
12565                         }
12566 
12567                         if (typeof callbacks.error === "function") {
12568                             callbacks.error(err);
12569                         }
12570                     }
12571                 };
12572 
12573                 this._manage(mediaDialogs);
12574                 if ( this._subscriptionId )
12575                 {
12576                     successful = true;
12577                 }
12578                 else
12579                 {
12580                     successful = ClientServices.subscribe(topic, this._createPubsubCallback(), true);
12581                     if ( successful )
12582                     {
12583                         this._subscriptionId = "OpenAjaxOnly";
12584                     }
12585                 }
12586 
12587                 if (successful) {
12588                     handlers.success();
12589                 } else {
12590                     handlers.error();
12591                 }
12592 
12593                 return this;
12594             }
12595         });
12596 
12597         MediaDialogsSubscriptionManager.MEDIA_ID_INDEX_IN_SOURCE = 6;
12598 
12599         window.finesse = window.finesse || {};
12600         window.finesse.restservices = window.finesse.restservices || {};
12601         window.finesse.restservices.MediaDialogsSubscriptionManager = MediaDialogsSubscriptionManager;
12602 
12603         return MediaDialogsSubscriptionManager;
12604     });
12605 
12606 /**
12607  * JavaScript representation of the Finesse MediaDialogs collection
12608  * object which contains a list of Dialog objects.
12609  *
12610  * @requires finesse.clientservices.ClientServices
12611  * @requires Class
12612  * @requires finesse.FinesseBase
12613  * @requires finesse.restservices.RestBase
12614  * @requires finesse.restservices.Dialogs
12615  * @requires finesse.restservices.MediaDialogsSubscriptionManager
12616  */
12617 /** @private */
12618 define('restservices/MediaDialogs',[
12619     'restservices/RestCollectionBase',
12620     'restservices/RestBase',
12621     'restservices/Dialogs',
12622     'restservices/MediaDialog',
12623     'restservices/MediaDialogsSubscriptionManager'
12624 ],
12625 function (RestCollectionBase, RestBase, Dialogs, MediaDialog, MediaDialogsSubscriptionManager) {
12626     var MediaDialogs = Dialogs.extend(/** @lends finesse.restservices.MediaDialogs.prototype */{
12627 
12628         /**
12629          * @class
12630          * JavaScript representation of a collection of Dialogs for a specific non-voice Media.
12631          * @augments finesse.restservices.Dialogs
12632          * @constructs
12633          * @see finesse.restservices.Dialog
12634          * @example
12635          *  _MediaDialogs = _media.getMediaDialogs( {
12636          *      onCollectionAdd : _handleDialogAdd,
12637          *      onCollectionDelete : _handleDialogDelete,
12638          *      onLoad : _handleMediaDialogsLoaded
12639          *  });
12640          *  
12641          * _dialogCollection = _MediaDialogs.getCollection();
12642          * for (var dialogId in _dialogCollection) {
12643          *     if (_dialogCollection.hasOwnProperty(dialogId)) {
12644          *         _dialog = _dialogCollection[dialogId];
12645          *         etc...
12646          *     }
12647          * }
12648          */
12649         _fakeConstuctor: function () {
12650             /* This is here to hide the real init constructor from the public docs */
12651         },
12652         
12653         /**
12654          * @private
12655          * @param {Object} options
12656          *     An object with the following properties:<ul>
12657          *         <li><b>id:</b> The id of the object being constructed</li>
12658          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
12659          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
12660          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
12661          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
12662          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
12663          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
12664          *             <li><b>content:</b> {String} Raw string of response</li>
12665          *             <li><b>object:</b> {Object} Parsed object of response</li>
12666          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
12667          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
12668          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
12669          *             </ul></li>
12670          *         </ul></li>
12671          *         <li><b>parentObj:</b> The parent object</li></ul>
12672          *         <li><b>mediaObj:</b> The media object</li></ul>
12673          **/
12674         init: function (options) {
12675             this._mediaObj = options.mediaObj;
12676             this._super(options);
12677         },
12678 
12679         getMedia: function() {
12680             return this._mediaObj;
12681         },
12682 
12683         /**
12684          * @private
12685          * Gets the REST class for the objects that make up the collection. - this
12686          * is the Dialog class.
12687          */
12688         getRestItemClass: function () {
12689             return MediaDialog;
12690         },
12691 
12692         /**
12693          * @private
12694          * Gets the node path for the current object - this is the media node
12695          * @returns {String} The node path
12696          */
12697         getXMPPNodePath: function () {
12698             var
12699                 restObj = this._restObj,
12700                 nodePath = "";
12701 
12702             //Prepend the base REST object if one was provided.
12703             if (restObj instanceof RestBase) {
12704                 nodePath += restObj.getRestUrl();
12705             }
12706             //Otherwise prepend with the default webapp name.
12707             else {
12708                 nodePath += "/finesse/api";
12709             }
12710             
12711             //Append the REST type.
12712             nodePath += "/" + this.getRestType() + "/Media";
12713             return nodePath;
12714         },
12715         
12716         /**
12717          * The REST URL in which this object can be referenced.
12718          * @return {String}
12719          *     The REST URI for this object.
12720          * @private
12721          */
12722         getRestUrl: function () {
12723             var
12724             restObj = this._mediaObj,
12725             restUrl = "";
12726 
12727             //Prepend the base REST object if one was provided.
12728             if (restObj instanceof RestBase) {
12729                 restUrl += restObj.getRestUrl();
12730             }
12731             //Otherwise prepend with the default webapp name.
12732             else {
12733                 restUrl += "/finesse/api";
12734             }
12735 
12736             //Append the REST type.
12737             restUrl += "/" + this.getRestType();
12738 
12739             //Append ID if it is not undefined, null, or empty.
12740             if (this._id) {
12741                 restUrl += "/" + this._id;
12742             }
12743             return restUrl;
12744         },
12745 
12746         /**
12747          * Overridden so that MediaDialogsSubscriptionManager can be used to share events across media dialogs.
12748          *
12749          * @param {Object} callbacks
12750          *     An object containing success and error handlers for the subscription request.
12751          * @private
12752          */
12753         subscribe: function (callbacks)
12754         {
12755             if ( !MediaDialogs.subscriptionManager )
12756             {
12757                 MediaDialogs.subscriptionManager = new MediaDialogsSubscriptionManager(this._restObj);
12758             }
12759 
12760             MediaDialogs.subscriptionManager.subscribe(this, callbacks);
12761 
12762             return this;
12763         }
12764     });
12765 
12766     MediaDialogs.subscriptionManager = /** @lends finesse.restservices.MediaDialogsSubscriptionManager.prototype */ null;
12767 
12768     window.finesse = window.finesse || {};
12769     window.finesse.restservices = window.finesse.restservices || {};
12770     window.finesse.restservices.MediaDialogs = MediaDialogs;
12771     
12772     return MediaDialogs;
12773 });
12774 
12775 /**
12776  * Allows gadgets to call the log function to publish client logging messages over the hub.
12777  *
12778  * @requires OpenAjax
12779  */
12780 /** @private */
12781 define('cslogger/ClientLogger',[], function () {
12782 
12783     var ClientLogger = ( function () { /** @lends finesse.cslogger.ClientLogger.prototype */
12784         var _hub, _logTopic, _originId, _sessId, _host,
12785             MONTH = { 0 : "Jan", 1 : "Feb", 2 : "Mar", 3 : "Apr", 4 : "May", 5 : "Jun", 
12786                       6 : "Jul", 7 : "Aug", 8 : "Sep", 9 : "Oct", 10 : "Nov", 11 : "Dec"},
12787  
12788         /**
12789          * Gets timestamp drift stored in sessionStorage
12790          * @returns drift in seconds if it is set in sessionStorage otherwise returns undefined.
12791          * @private
12792         */
12793         getTsDrift = function() {
12794             if (window.sessionStorage.getItem('clientTimestampDrift') !== null) {
12795                 return parseInt(window.sessionStorage.getItem('clientTimestampDrift'), 10);
12796             }
12797             else { 
12798                 return undefined;
12799             }
12800         },
12801          
12802         /**
12803           * Sets timestamp drift in sessionStorage
12804           * @param delta is the timestamp drift between server.and client.
12805           * @private
12806          */
12807         setTsDrift = function(delta) {
12808              window.sessionStorage.setItem('clientTimestampDrift', delta.toString());
12809         },
12810           
12811         /**
12812          * Gets Finesse server timezone offset from GMT in seconds 
12813          * @returns offset in seconds if it is set in sessionStorage otherwise returns undefined.
12814          * @private
12815         */
12816         getServerOffset = function() {
12817             if (window.sessionStorage.getItem('serverTimezoneOffset') !== null) {
12818                 return parseInt(window.sessionStorage.getItem('serverTimezoneOffset'), 10);
12819             }
12820             else { 
12821                 return undefined;
12822             }
12823         },
12824          
12825         /**
12826           * Sets server timezone offset 
12827           * @param sec is the server timezone GMT offset in seconds.
12828           * @private
12829          */
12830         setServerOffset = function(sec) {
12831              window.sessionStorage.setItem('serverTimezoneOffset', sec.toString());
12832         },
12833  
12834         /**
12835          * Checks to see if we have a console.
12836          * @returns Whether the console object exists.
12837          * @private
12838          */
12839         hasConsole = function () {
12840             try {
12841                 if (window.console !== undefined) {
12842                     return true;
12843                 }
12844             } 
12845             catch (err) {
12846               // ignore and return false
12847             }
12848     
12849             return false;
12850         },
12851         
12852         /**
12853          * Gets a short form (6 character) session ID from sessionStorage
12854          * @private
12855         */
12856         getSessId = function() {
12857             if (!_sessId) {
12858                //when _sessId not defined yet, initiate it
12859                if (window.sessionStorage.getItem('enableLocalLog') === 'true') {
12860                   _sessId= " "+window.sessionStorage.getItem('finSessKey');
12861                }
12862                else {
12863                   _sessId=" ";
12864                }
12865             }
12866             return _sessId;
12867          },
12868 
12869         /**
12870          * Pads a single digit number for display purposes (e.g. '4' shows as '04')
12871          * @param num is the number to pad to 2 digits
12872          * @returns a two digit padded string
12873          * @private
12874          */
12875         padTwoDigits = function (num)
12876         {
12877             return (num < 10) ? '0' + num : num;
12878         },
12879         
12880         /**
12881          * Pads a single digit number for display purposes (e.g. '4' shows as '004')
12882          * @param num is the number to pad to 3 digits
12883          * @returns a three digit padded string
12884          * @private
12885          */
12886         padThreeDigits = function (num)
12887         {
12888             if (num < 10)
12889             {
12890               return '00'+num;
12891             }
12892             else if (num < 100)
12893             {
12894               return '0'+num;
12895             }
12896             else  
12897             {
12898                return num;
12899             }
12900         },
12901               
12902         /**
12903          * Compute the "hour"
12904          * 
12905          * @param s is time in seconds
12906          * @returns {String} which is the hour
12907          * @private
12908          */
12909         ho = function (s) {
12910              return ((s/60).toString()).split(".")[0];
12911         },
12912           
12913         /**
12914          * Gets local timezone offset string.
12915          * 
12916          * @param t is the time in seconds
12917          * @param s is the separator character between hours and minutes, e.g. ':'
12918          * @returns {String} is local timezone GMT offset in the following format: [+|-]hh[|:]MM
12919          * @private
12920          */
12921         getGmtOffString = function (min,s) {
12922             var t, sign;
12923             if (min<0) {
12924                t = -min;
12925                sign = "-";
12926             }
12927             else {
12928                t = min;
12929                sign = "+";
12930             }
12931             
12932             if (s===':') {
12933                 return sign+padTwoDigits(ho(t))+s+padTwoDigits(t%60);
12934             }
12935             else {
12936                 return sign+padTwoDigits(ho(t))+padTwoDigits(t%60);
12937             }    
12938         },
12939 
12940         /**
12941          * Gets short form of a month name in English 
12942          * 
12943          * @param monthNum is zero-based month number 
12944          * @returns {String} is short form of month name in English
12945          * @private
12946          */
12947         getMonthShortStr = function (monthNum) {
12948             var result;
12949             try {
12950                 result = MONTH[monthNum];
12951             } 
12952             catch (err) {
12953                 if (hasConsole()) {
12954                     window.console.log("Month must be between 0 and 11");
12955                 }
12956             }
12957             return result;
12958         },
12959           
12960         /**
12961           * Gets a timestamp.
12962           * @param aDate is a javascript Date object
12963           * @returns {String} is a timestamp in the following format: yyyy-mm-ddTHH:MM:ss.SSS [+|-]HH:MM
12964           * @private
12965           */
12966         getDateTimeStamp = function (aDate)
12967         {
12968             var date, off, timeStr;
12969             if (aDate === null) {
12970                 date = new Date();
12971             }
12972             else {
12973                 date = aDate;
12974             }
12975             off = -1*date.getTimezoneOffset();
12976             timeStr = date.getFullYear().toString() + "-" +
12977                       padTwoDigits(date.getMonth()+1) + "-" +
12978                       padTwoDigits (date.getDate()) + "T"+
12979                       padTwoDigits(date.getHours()) + ":" + 
12980                       padTwoDigits(date.getMinutes()) + ":" +
12981                       padTwoDigits(date.getSeconds())+"." + 
12982                       padThreeDigits(date.getMilliseconds()) + " "+
12983                       getGmtOffString(off, ':');
12984     
12985             return timeStr;
12986         },
12987         
12988         /**
12989          * Gets drift-adjusted timestamp.
12990          * @param aTimestamp is a timestamp in milliseconds
12991          * @param drift is a timestamp drift in milliseconds
12992          * @param serverOffset is a timezone GMT offset in minutes
12993          * @returns {String} is a timestamp in the Finesse server log format, e.g. Jan 07 2104 HH:MM:ss.SSS -0500
12994          * @private
12995          */
12996         getDriftedDateTimeStamp = function (aTimestamp, drift, serverOffset)
12997         {
12998            var date, timeStr, localOffset;
12999            if (aTimestamp === null) {
13000                return "--- -- ---- --:--:--.--- -----";
13001            }
13002            else if (drift === undefined || serverOffset === undefined) {
13003                if (hasConsole()) {
13004                    window.console.log("drift or serverOffset must be a number");
13005                }
13006                return "--- -- ---- --:--:--.--- -----";
13007            }
13008            else {
13009                //need to get a zone diff in minutes
13010                localOffset = (new Date()).getTimezoneOffset();
13011                date = new Date(aTimestamp+drift+(localOffset+serverOffset)*60000);
13012                timeStr = getMonthShortStr(date.getMonth()) + " "+
13013                          padTwoDigits (date.getDate())+ " "+
13014                          date.getFullYear().toString() + " "+
13015                          padTwoDigits(date.getHours()) + ":" + 
13016                          padTwoDigits(date.getMinutes()) + ":" +
13017                          padTwoDigits(date.getSeconds())+"." + 
13018                          padThreeDigits(date.getMilliseconds())+" "+
13019                          getGmtOffString(serverOffset, '');
13020                 return timeStr;
13021             }
13022         },
13023     
13024         /**
13025         * Logs a message to a hidden textarea element on the page
13026         *
13027         * @param msg is the string to log.
13028         * @private
13029         */
13030         writeToLogOutput = function (msg) {
13031             var logOutput = document.getElementById("finesseLogOutput");
13032     
13033             if (logOutput === null)
13034             {
13035                 logOutput = document.createElement("textarea");
13036                 logOutput.id = "finesseLogOutput";
13037                 logOutput.style.display = "none";
13038                 document.body.appendChild(logOutput);
13039             }
13040     
13041             if (logOutput.value === "")
13042             {
13043                 logOutput.value = msg;
13044             }
13045             else
13046             {
13047                 logOutput.value = logOutput.value + "\n" + msg;
13048             }
13049         },
13050 
13051         /*
13052          * Logs a message to console 
13053         * @param str is the string to log.         * @private
13054          */
13055         logToConsole = function (str)
13056         {
13057             var now, msg, timeStr, driftedTimeStr, sessKey=getSessId();
13058             now = new Date();
13059             timeStr = getDateTimeStamp(now);
13060             if (getTsDrift() !== undefined) {
13061                 driftedTimeStr = getDriftedDateTimeStamp(now.getTime(), getTsDrift(), getServerOffset());
13062             }
13063             else {
13064                driftedTimeStr = getDriftedDateTimeStamp(null, 0, 0);
13065             }
13066             msg = timeStr + ":"+sessKey+": "+ _host + ": "+driftedTimeStr+ ": " + str;
13067             // Log to console
13068             if (hasConsole()) {
13069                 window.console.log(msg);
13070             }
13071     
13072             //Uncomment to print logs to hidden textarea.
13073             //writeToLogOutput(msg);
13074     
13075             return msg;
13076         };
13077         return {
13078     
13079             /**
13080              * Publishes a Log Message over the hub.
13081              *
13082              * @param {String} message
13083              *     The string to log.
13084              * @example
13085              * _clientLogger.log("This is some important message for MyGadget");
13086              * 
13087              */
13088             log : function (message) {
13089                 if(_hub) {
13090                     _hub.publish(_logTopic, logToConsole(_originId + message));
13091                 }
13092             },
13093             
13094             /**
13095              * @class
13096              * Allows gadgets to call the log function to publish client logging messages over the hub.
13097              * 
13098              * @constructs
13099              */
13100             _fakeConstuctor: function () {
13101                 /* This is here so we can document init() as a method rather than as a constructor. */
13102             },
13103             
13104             /**
13105              * Initiates the client logger with a hub a gadgetId and gadget's config object.
13106              * @param {Object} hub
13107              *      The hub to communicate with.
13108              * @param {String} gadgetId
13109              *      A unique string to identify which gadget is doing the logging.
13110              * @param {finesse.gadget.Config} config
13111              *      The config object used to get host name for that thirdparty gadget
13112              * @example
13113              * var _clientLogger = finesse.cslogger.ClientLogger;
13114              * _clientLogger.init(gadgets.Hub, "MyGadgetId", config);
13115              * 
13116              */
13117             init: function (hub, gadgetId, config) {
13118                 _hub = hub;
13119                 _logTopic = "finesse.clientLogging." + gadgetId;
13120                 _originId = gadgetId + " : ";
13121                 if ((config === undefined) || (config === "undefined")) 
13122                 {
13123                      _host = ((finesse.container && finesse.container.Config && finesse.container.Config.host)?finesse.container.Config.host : "?.?.?.?");
13124                  } 
13125                 else 
13126                 {
13127                      _host = ((config && config.host)?config.host : "?.?.?.?");
13128                  }
13129             }
13130         };
13131     }());
13132     
13133     window.finesse = window.finesse || {};
13134     window.finesse.cslogger = window.finesse.cslogger || {};
13135     window.finesse.cslogger.ClientLogger = ClientLogger;
13136     
13137     finesse = finesse || {};
13138     /** @namespace Supports writing messages to a central log. */
13139     finesse.cslogger = finesse.cslogger || {};
13140 
13141     return ClientLogger;
13142 });
13143 
13144 /**
13145  * Utility class used to recover a media object after recovering from a connection or system failure.
13146  *
13147  * @requires Class
13148  * @requires finesse.restservices.Notifier
13149  * @requires finesse.clientservices.ClientServices
13150  * @requires finesse.restservices.Media
13151  */
13152 
13153 /** @private */
13154 define('restservices/MediaOptionsHelper',['../../thirdparty/Class',
13155         '../utilities/Utilities',
13156         '../clientservices/ClientServices',
13157         '../cslogger/ClientLogger'
13158 ],
13159 function (Class, Utilities, ClientServices, ClientLogger)
13160 {
13161     /**
13162      * Utility class used to synchronize media login options after recovering from a connection or system failure. This
13163      * class will ensure that the Finesse server that the application fails over to has the same maxDialogLimit,
13164      * interruptAction, and dialogLogoutAction as the previous Finesse server.
13165      */
13166     var MediaOptionsHelper = Class.extend(/** @lends finesse.restservices.MediaOptionsHelper.prototype */
13167     {
13168         /**
13169          * @private
13170          *
13171          * The media that this helper is responsible for recovering in case of failover.
13172          */
13173         _media: null,
13174 
13175         /**
13176          * @private
13177          *
13178          * The media options that this helper will ensure are set properly across failures.
13179          */
13180         _mediaOptions: null,
13181 
13182         /**
13183          * @private
13184          *
13185          * The current state of the failover recovery.
13186          */
13187         _state: null,
13188 
13189         /**
13190          * @class
13191          *
13192          * Utility class used to synchronize media login options after recovering from a connection or system failure. This
13193          * class will ensure that the Finesse server that the application fails over to has the same maxDialogLimit,
13194          * interruptAction, and dialogLogoutAction as the previous Finesse server.
13195          *
13196          * @constructs
13197          */
13198         _fakeConstuctor: function ()
13199         {
13200             /* This is here to hide the real init constructor from the public docs */
13201         },
13202 
13203         /**
13204          * Utility method to format a message logged by an instance of this class.
13205          *
13206          * @param {string} message the message to format
13207          * @returns {string} the given message prefixed with the name of this class and the ID of the Media object
13208          *      associated with this class.
13209          * @private
13210          */
13211         _formatLogMessage: function(message)
13212         {
13213             return "MediaOptionsHelper[" + this.media.getMediaId() + "]: " + message;
13214         },
13215 
13216         /**
13217          * Utility method to log an informational message.
13218          *
13219          * Note that this method piggy-backs on the logger setup by the gadget. If the gadget does not initialize
13220          * logger, this class will not log.
13221          *
13222          * @param {string} message the message to log
13223          * @private
13224          */
13225         _log: function(message)
13226         {
13227             ClientLogger.log(this._formatLogMessage(message));
13228         },
13229 
13230         /**
13231          * Utility method to log an error message.
13232          *
13233          * Note that this method piggy-backs on the logger setup by the gadget. If the gadget does not initialize
13234          * logger, this class will not log.
13235          *
13236          * @param {string} message the message to log
13237          * @private
13238          */
13239         _error: function(message)
13240         {
13241             ClientLogger.error(this._formatLogMessage(message));
13242         },
13243 
13244         /**
13245          * @private
13246          *
13247          * Set the running state of this failover helper.
13248          *
13249          * @param {String} newState the new state of the failover helper.
13250          */
13251         _setState: function(newState)
13252         {
13253             this._state = newState;
13254             this._log("changed state to " + this._state);
13255         },
13256 
13257         /**
13258          * Check the given media object to see if the maxDialogLimit, interruptAction, and dialogLogoutAction options
13259          * need to be reset. These options need to be reset if the application specified login options and any of the
13260          * following conditions are true:<ul>
13261          *     <li>the dialogLogoutAction in the given media object does not match the action set by the application</li>
13262          *     <li>the interruptAction in the given media object does not match the action set by the application</li>
13263          *     <li>the maxDialogLimit in the given media object does not match the limit set by the application</li></ul>
13264          *
13265          * @param {Object} media the media object to evaluate
13266          * @returns {*|{}|boolean} true if a login request should be sent to correct the media options
13267          * @private
13268          */
13269         _shouldLoginToFixOptions: function(media)
13270         {
13271             return this._mediaOptions
13272                 && media.isLoggedIn()
13273                 && (media.getDialogLogoutAction() !== this._mediaOptions.dialogLogoutAction
13274                     || media.getInterruptAction() !== this._mediaOptions.interruptAction
13275                     || media.getMaxDialogLimit() !== this._mediaOptions.maxDialogLimit);
13276         },
13277 
13278         /**
13279          * @private
13280          *
13281          * Determine if the given response is an "agent already logged in" error.
13282          *
13283          * @param {Object} response the response to evaluate
13284          *
13285          * @returns {boolean} true if
13286          */
13287         _agentIsAlreadyLoggedIn: function(response)
13288         {
13289             return response
13290                 && response.object
13291                 && response.object.ApiErrors
13292                 && response.object.ApiErrors.ApiError
13293                 && response.object.ApiErrors.ApiError.ErrorMessage === "E_ARM_STAT_AGENT_ALREADY_LOGGED_IN";
13294         },
13295 
13296         /**
13297          * Determine if the given response to a media login request is successful. A response is successful under these
13298          * conditions:<ul>
13299          *     <li>the response has a 202 status</li>
13300          *     <li>the response has a 400 status and the error indicates the agent is already logged in</li>
13301          *     </ul>
13302          *
13303          * @param {Object} loginResponse the response to evaluate
13304          *
13305          * @returns {*|boolean} true if the response status is 202 or if the response status is 400 and the error states
13306          *      that the agent is already logged in.
13307          * @private
13308          */
13309         _isSuccessfulLoginResponse: function(loginResponse)
13310         {
13311             return loginResponse && ((loginResponse.status === 202) || this._agentIsAlreadyLoggedIn(loginResponse));
13312         },
13313 
13314         /**
13315          * Process a media load or change while in the connected state. This involves checking the media options to
13316          * ensure they are the same as those set by the application.
13317          *
13318          * @param {Object} media the media object that was loaded or changed.
13319          * @private
13320          */
13321         _processConnectedState: function(media)
13322         {
13323             var _this = this, processResponse;
13324 
13325             if ( this._shouldLoginToFixOptions(media) )
13326             {
13327                 processResponse = function(response)
13328                 {
13329                     _this._setState(MediaOptionsHelper.States.MONITORING_OPTIONS);
13330 
13331                     if ( !_this._isSuccessfulLoginResponse(response) )
13332                     {
13333                         _this._error("failed to reset options: " + response.status + ": " + response.content);
13334                     }
13335                 };
13336 
13337                 this._setState(MediaOptionsHelper.States.SETTING_OPTIONS);
13338 
13339                 this._log("logging in to fix options");
13340 
13341                 this.media.login({
13342                     dialogLogoutAction: _this._mediaOptions.dialogLogoutAction,
13343                     interruptAction: _this._mediaOptions.interruptAction,
13344                     maxDialogLimit: _this._mediaOptions.maxDialogLimit,
13345                     handlers: {
13346                         success: processResponse,
13347                         error: processResponse
13348                     }
13349                 });
13350             }
13351         },
13352 
13353         /**
13354          * Process a media load or change while in the resetting options state. All that is done in this state is log a
13355          * message that a reset is already in progress.
13356          *
13357          * @param {Object} media the media object that was loaded or changed.
13358          * @private
13359          */
13360         _processResettingOptionsState: function(media)
13361         {
13362             this._log("Resetting options is in progress");
13363         },
13364 
13365         /**
13366          * Initialize a helper class used to recover media objects following connectivity or component failures related
13367          * to Finesse and/or CCE services.
13368          *
13369          * Initialize the failover helper to manage the recovery of the given media object.
13370          *
13371          * @param {Object} media the media object to recover
13372          * @param {Object} mediaOptions an object containing the media options used by the application:<ul>
13373          *         <li><b>maxDialogLimit:</b> The id of the object being constructed</li>
13374          *         <li><b>interruptAction:</b> Accept or ignore interrupts</li>
13375          *         <li><b>dialogLogoutAction:</b> transfer or close the task at logout time</li></ul>
13376          */
13377         init: function (media, mediaOptions)
13378         {
13379             var _this = this, processMediaStateChange = function(media)
13380             {
13381                 switch ( _this._state )
13382                 {
13383                     case MediaOptionsHelper.States.MONITORING_OPTIONS:
13384                         _this._processConnectedState(media);
13385                         break;
13386                     case MediaOptionsHelper.States.SETTING_OPTIONS:
13387                         _this._processResettingOptionsState(media);
13388                         break;
13389                     default:
13390                         _this._error("unexpected state: " + _this.state);
13391                         break;
13392                 }
13393             };
13394 
13395             this.media = media;
13396 
13397             this._mediaOptions = mediaOptions || {};
13398 
13399             // The maxDialogLimit is a string in media events. Ensure _mediaOptions.maxDialogLimit is a string to
13400             // make sure it can be compared to the maxDialogLimit field in media events.
13401             //
13402             if ( this._mediaOptions.maxDialogLimit )
13403             {
13404                 this._mediaOptions.maxDialogLimit = this._mediaOptions.maxDialogLimit.toString();
13405             }
13406 
13407             // Add the media object to the refresh list so that ClientServices calls refresh on the media object when
13408             // the connection is reestablished. This will trigger processMediaStateChange() which will trigger a login
13409             // to restore media options if media options are different.
13410             //
13411             ClientServices.addToRefreshList(this.media);
13412 
13413             this._setState(MediaOptionsHelper.States.MONITORING_OPTIONS);
13414 
13415             this.media.addHandler('load', processMediaStateChange);
13416             this.media.addHandler('change', processMediaStateChange);
13417 
13418             this._log("initialized");
13419         }
13420     });
13421 
13422     /**
13423      * @private
13424      *
13425      * The states that a running MediaOptionsHelper executes.
13426      *
13427      * @type {{CONNECTED: string, RECOVERING: string, RECOVERY_LOGIN: string, _fakeConstructor: _fakeConstructor}}
13428      */
13429     MediaOptionsHelper.States = /** @lends finesse.restservices.MediaOptionsHelper.States.prototype */ {
13430 
13431         /**
13432          * The media is synchronized with the Finesse server. Media options are being monitored for changes.
13433          */
13434         MONITORING_OPTIONS: "MONITORING_OPTIONS",
13435 
13436         /**
13437          * A media login request has been sent to Finesse to set the correct media options.
13438          */
13439         SETTING_OPTIONS: "SETTING_OPTIONS",
13440 
13441         /**
13442          * @class Possible MediaOptionsHelper state values.
13443          * @constructs
13444          */
13445         _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
13446     };
13447 
13448     window.finesse = window.finesse || {};
13449     window.finesse.restservices = window.finesse.restservices || {};
13450     window.finesse.restservices.MediaOptionsHelper = MediaOptionsHelper;
13451 
13452     return MediaOptionsHelper;
13453 });
13454 /**
13455  * JavaScript representation of the Finesse Media object
13456  *
13457  * @requires finesse.clientservices.ClientServices
13458  * @requires Class
13459  * @requires finesse.FinesseBase
13460  * @requires finesse.restservices.RestBase
13461  * @requires finesse.restservices.MediaDialogs
13462  * @requires finesse.restservices.MediaOptionsHelper
13463  */
13464 
13465 /** @private */
13466 define('restservices/Media',[
13467     'restservices/RestBase',
13468     'restservices/MediaDialogs',
13469     'restservices/MediaOptionsHelper'
13470 ],
13471 function (RestBase, MediaDialogs, MediaOptionsHelper) {
13472     var Media = RestBase.extend(/** @lends finesse.restservices.Media.prototype */{
13473 
13474         /**
13475          * Media objects support GET REST requests.
13476          */
13477         supportsRequests: true,
13478 
13479         /**
13480          * @private
13481          * The list of dialogs associated with this media.
13482          */
13483         _mdialogs : null,
13484 
13485         /**
13486          * @private
13487          * The options used to log into this media.
13488          */
13489         _mediaOptions: null,
13490 
13491         /**
13492          * @private
13493          * Object used to keep the maxDialogLimit, interruptAction, and dialogLogoutAction in-synch across fail-overs.
13494          */
13495         _optionsHelper: null,
13496 
13497         /**
13498          * @class
13499          * A Media represents a non-voice channel,
13500          * for example, a chat or a email.
13501          *
13502          * @augments finesse.restservices.RestBase
13503          * @constructs
13504          */
13505         _fakeConstuctor: function () {
13506             /* This is here to hide the real init constructor from the public docs */
13507         },
13508 
13509         /**
13510          * @private
13511          *
13512          * @param {Object} options
13513          *     An object with the following properties:<ul>
13514          *         <li><b>id:</b> The id of the object being constructed</li>
13515          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
13516          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
13517          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
13518          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
13519          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
13520          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
13521          *             <li><b>content:</b> {String} Raw string of response</li>
13522          *             <li><b>object:</b> {Object} Parsed object of response</li>
13523          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
13524          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
13525          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
13526          *             </ul></li>
13527          *         </ul></li>
13528          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
13529          **/
13530         init: function (options) {
13531             this._super(options);
13532         },
13533 
13534         /**
13535          * @private
13536          * Utility method used to retrieve an attribute from this media object's underlying data.
13537          *
13538          * @param {String} attributeName the name of the attribute to retrieve
13539          * @returns {String} the value of the attribute or undefined if the attribute is not found
13540          */
13541         _getDataAttribute: function(attributeName) {
13542             this.isLoaded();
13543             return this.getData()[attributeName];
13544         },
13545 
13546         /**
13547          * @private
13548          * Gets the REST class for the current object - this is the Media class.
13549          * @returns {Object} The Media class.
13550          */
13551         getRestClass: function () {
13552             return Media;
13553         },
13554         
13555         /**
13556          * @private
13557          * Gets the REST type for the current object - this is a "Media".
13558          * @returns {String} The Media string.
13559          */
13560         getRestType: function () {
13561             return "Media";
13562         },
13563 
13564         /**
13565          * @private
13566          * Getter for the uri.
13567          * @returns {String} The uri.
13568          */
13569         getMediaUri: function () {
13570             return this._getDataAttribute('uri');
13571         },
13572 
13573         /**
13574          * Getter for the id.
13575          * @returns {String} The id.
13576          */
13577         getId: function () {
13578             return this._getDataAttribute('id');
13579         },
13580 
13581         /**
13582          * Getter for the name.
13583          * @returns {String} The name.
13584          */
13585         getName: function () {
13586             return this._getDataAttribute('name');
13587         },
13588 
13589         /**
13590          * Getter for the reason code id.
13591          * @returns {String} The reason code id.
13592          */
13593         getReasonCodeId: function () {
13594             return this._getDataAttribute('reasonCodeId');
13595         },
13596 
13597         /**
13598          * Getter for the reason code label.
13599          * @returns {String} The reason code label.
13600          */
13601         getReasonCodeLabel: function() {
13602             this.isLoaded();
13603             if (this.getData().reasonCode) {
13604                 return this.getData.reasonCode.label;
13605             }
13606             return "";
13607         },
13608 
13609         /**
13610          * Getter for the action to be taken in the event this media is interrupted. The action will be one of the
13611          * following:<ul>
13612          *     <li><b>ACCEPT:</b> the interrupt will be accepted and the agent will not work on tasks in this media
13613          *     until the media is no longer interrupted.</li>
13614          *     <li><b>IGNORE:</b> the interrupt will be ignored and the agent is allowed to work on the task while the
13615          *     media is interrupted.</li></ul>
13616          * @returns {*|Object}
13617          */
13618         getInterruptAction: function() {
13619             return this._getDataAttribute('interruptAction');
13620         },
13621 
13622         /**
13623          * Getter for the action to be taken in the event the agent logs out with dialogs associated with this media.
13624          * The action will be one of the following:<ul>
13625          *     <li><b>CLOSE:</b> the dialog will be closed.</li>
13626          *     <li><b>TRANSFER:</b> the dialog will be transferred to another agent.</li></ul>
13627          * @returns {*|Object}
13628          */
13629         getDialogLogoutAction: function() {
13630             return this._getDataAttribute('dialogLogoutAction');
13631         },
13632 
13633         /**
13634          * Getter for the state of the User on this Media.
13635          * @returns {String}
13636          *     The current (or last fetched) state of the User on this Media
13637          * @see finesse.restservices.Media.States
13638          */
13639         getState: function() {
13640             return this._getDataAttribute('state');
13641         },
13642 
13643         /**
13644          * Getter for the Media id
13645          * @returns {String} The Media id
13646          */
13647         getMediaId: function() {
13648             return this._getDataAttribute('id');
13649         },
13650 
13651         /**
13652          * Getter for maximum number of dialogs allowed on this Media
13653          * @returns {String} The max number of Dialogs on this Media
13654          */
13655         getMaxDialogLimit: function() {
13656             return this._getDataAttribute('maxDialogLimit');
13657         },
13658 
13659         /**
13660          * Getter for whether or not this media is interruptible
13661          * @returns {Boolean} true if interruptible; false otherwise
13662          */
13663         getInterruptible: function() {
13664             var interruptible = this._getDataAttribute('interruptible');
13665             return interruptible === 'true';
13666         },
13667 
13668         /**
13669          * Is the user interruptible on this Media.
13670          * @returns {Boolean} true if interruptible; false otherwise
13671          */
13672         isInterruptible: function() {
13673             return this.getInterruptible();
13674         },
13675 
13676         /**
13677          * Getter for routable field on this Media
13678          * @returns {Boolean} true if routable, false otherwise
13679          */
13680         getRoutable: function() {
13681             var routable = this._getDataAttribute('routable');
13682             return routable === 'true';
13683         },
13684 
13685         /**
13686          * Is the user routable on this Media.
13687          * @returns {Boolean} true if routable, false otherwise
13688          */
13689         isRoutable: function() {
13690             return this.getRoutable();
13691         },
13692 
13693         /**
13694          * @param {Object} options
13695          *     An object with the following properties:<ul>
13696          *         <li><b>routable:</b> true if the agent is routable, false otherwise</li>
13697          *         <li><b>{finesse.interfaces.RequestHandlers} handlers:</b> An object containing the handlers for the request</li></ul>
13698          * @returns {finesse.restservices.Media}
13699          *     This Media object, to allow cascading
13700          */
13701         setRoutable: function(params) {
13702             var handlers, contentBody = {},
13703                 restType = this.getRestType(),
13704                 url = this.getRestUrl();
13705             params = params || {};
13706 
13707             contentBody[restType] = {
13708                 "routable": params.routable
13709             };
13710 
13711             handlers = params.handlers || {};
13712 
13713             this._makeRequest(contentBody, url, handlers);
13714 
13715             return this;
13716         },
13717 
13718         /**
13719          * @private
13720          * Invoke a request to the server given a content body and handlers.
13721          *
13722          * @param {Object} contentBody
13723          *     A JS object containing the body of the action request.
13724          * @param {finesse.interfaces.RequestHandlers} handlers
13725          *     An object containing the handlers for the request
13726          */
13727         _makeRequest: function (contentBody, url, handlers) {
13728             // Protect against null dereferencing of options allowing its
13729             // (nonexistent) keys to be read as undefined
13730             handlers = handlers || {};
13731 
13732             this.restRequest(url, {
13733                 method: 'PUT',
13734                 success: handlers.success,
13735                 error: handlers.error,
13736                 content: contentBody
13737             });
13738         },
13739 
13740         /**
13741          * Return true if the params object contains one of the following:<ul>
13742          *     <li>maxDialogLimit</li>
13743          *     <li>interruptAction</li>
13744          *     <li>dialogLogoutAction</li></ul>
13745          *
13746          * @param {Object} params the parameters to evaluate
13747          * @returns {*|Boolean}
13748          * @private
13749          */
13750         _containsLoginOptions: function(params) {
13751             return params.maxDialogLimit || params.interruptAction || params.dialogLogoutAction;
13752         },
13753 
13754         /**
13755          * Create login parameters using the given algorithm:<ul>
13756          *     <li>if loginOptions have not be set in the call to MediaList.getMedia(), use the params given by the application</li>
13757          *     <li>if no params were set by the application, use the loginOptions set in the call to MediaList.getMedia()</li>
13758          *     <li>if the parameters given by the application contains login options, use the parameters given by the application</li>
13759          *     <li>if login options were given by the application but callbacks were given, create new login params with the
13760          *     loginOptions set in the call to MediaList.getMedia() and the callbacks specified in the given login params</li></ul>
13761          *
13762          * @param params the parameters specified by the application in the call to Media.login()
13763          *
13764          * @see finesse.restservices.Media#login
13765          * @see finesse.restservices.MediaList#getMedia
13766          *
13767          * @returns {Object} login parameters built based on the algorithm listed above.
13768          * @private
13769          */
13770         _makeLoginOptions: function(params) {
13771             if ( !this._mediaOptions ) {
13772                 // If loginOptions have not be set, use the params given by the application.
13773                 //
13774                 return params;
13775             }
13776 
13777             if ( !params ) {
13778                 // If there were no params given by the application, use the loginOptions set in the call to
13779                 // MediaList.getMedia().
13780                 //
13781                 return this._mediaOptions;
13782             }
13783 
13784             if (  this._containsLoginOptions(params) ) {
13785                 // if the parameters given by the application contains login options, use the parameters given by the
13786                 // application.
13787                 //
13788                 return params;
13789             }
13790 
13791             // If login options were given by the application but callbacks were given, create new login params with the
13792             // loginOptions set in the call to MediaList.getMedia() and the callbacks specified in the given login
13793             // params.
13794             //
13795             return {
13796                 maxDialogLimit: this._mediaOptions.maxDialogLimit,
13797                 interruptAction: this._mediaOptions.interruptAction,
13798                 dialogLogoutAction: this._mediaOptions.dialogLogoutAction,
13799                 handlers: params.handlers
13800             };
13801         },
13802 
13803         /**
13804          * Ensure that the maxDialogLimit, interruptAction, and dialogLogoutAction options are kept in synch across
13805          * fail-overs for this media object.
13806          * @private
13807          */
13808         _ensureOptionsAreInSynch: function() {
13809             if ( !this._optionsHelper && this._mediaOptions ) {
13810                 this._optionsHelper = new MediaOptionsHelper(this, this._mediaOptions);
13811             }
13812         },
13813 
13814         /**
13815          * Log the agent into this media.
13816          *
13817          * @param {Object} options
13818          *     An object with the following properties:<ul>
13819          *         <li><b>maxDialogLimit:</b> The id of the object being constructed</li>
13820          *         <li><b>interruptAction:</b> Accept or ignore interrupts</li>
13821          *         <li><b>dialogLogoutAction:</b> transfer or close the task at logout time</li>
13822          *         <li><b>{finesse.interfaces.RequestHandlers} handlers:</b> An object containing the handlers for the request</li></ul>
13823          *
13824          *     If maxDialogLimit, interruptAction, and dialogLogoutAction are not present, loginOptions specified in the
13825          *     call to finesse.restservices.MediaList.getMedia() will be used.
13826          *
13827          * @see finesse.restservices.MediaList#getMedia
13828          *
13829          * @returns {finesse.restservices.Media}
13830          *     This Media object, to allow cascading
13831          */
13832         login: function(params) {
13833             this.setState(Media.States.LOGIN, null, this._makeLoginOptions(params));
13834             return this; // Allow cascading
13835         },
13836 
13837         /**
13838          * Perform a logout for a user on this media.
13839          * @param {String} reasonCode
13840          *     The reason this user is logging out of this media.  Pass null for no reason.
13841          * @param {finesse.interfaces.RequestHandlers} handlers
13842          *     An object containing the handlers for the request
13843          * @returns {finesse.restservices.Media}
13844          *     This Media object, to allow cascading
13845          */
13846         logout: function(reasonCode, params) {
13847             var state = Media.States.LOGOUT;
13848             return this.setState(state, reasonCode, params);
13849         },
13850 
13851         /**
13852          * Set the state of the user on this Media.
13853          * @param {String} newState
13854          *     The state you are setting
13855          * @param {ReasonCode} reasonCode
13856          *     The reason this user is changing state for this media.  Pass null for no reason.
13857          * @param {finesse.interfaces.RequestHandlers} handlers
13858          *     An object containing the handlers for the request
13859          * @see finesse.restservices.User.States
13860          * @returns {finesse.restservices.Media}
13861          *     This Media object, to allow cascading
13862          */
13863         setState: function(state, reasonCode, params) {
13864             var handlers, reasonCodeId, contentBody = {},
13865                 restType = this.getRestType(),
13866                 url = this.getRestUrl();
13867             params = params || {};
13868 
13869             if(reasonCode) {
13870                 reasonCodeId = reasonCode.id;
13871             }
13872 
13873             contentBody[restType] = {
13874                 "state": state,
13875                 "maxDialogLimit": params.maxDialogLimit,
13876                 "interruptAction": params.interruptAction,
13877                 "dialogLogoutAction": params.dialogLogoutAction,
13878                 "reasonCodeId": reasonCodeId
13879             };
13880 
13881             handlers = params.handlers || {};
13882 
13883             this._makeRequest(contentBody, url, handlers);
13884 
13885             return this;
13886         },
13887 
13888         /**
13889          * Getter for a MediaDialogs collection object that is associated with User on this Media.
13890          * @param {finesse.interfaces.RestObjectHandlers} [handlers] Object that sets callback handlers (only
13891          * applicable when Object has not been previously created).
13892          * @returns {finesse.restservices.MediaDialogs}
13893          *     A MediaDialogs collection object.
13894          */
13895         getMediaDialogs: function (callbacks) {
13896             var options = callbacks || {};
13897             options.parentObj = this._restObj;
13898             options.mediaObj = this;
13899             this.isLoaded();
13900 
13901             if (this._mdialogs === null) {
13902                 this._mdialogs = new MediaDialogs(options);
13903             }
13904 
13905             return this._mdialogs;
13906         },
13907 
13908         /**
13909          * Refresh the dialog collection associated with this media.
13910          */
13911         refreshMediaDialogs: function() {
13912             if ( this._mdialogs ) {
13913                 this._mdialogs.refresh();
13914             }
13915         },
13916 
13917         /**
13918          * Set the maxDialogLimit, interruptAction, and dialogLogoutAction settings that the application will use for
13919          * this media. In the event of a failure, these options will be set on the new Finesse server.
13920          *
13921          * @param {Object} mediaOptions an object with the following properties:<ul>
13922          *         <li><b>maxDialogLimit:</b> The id of the object being constructed</li>
13923          *         <li><b>interruptAction:</b> Accept or ignore interrupts</li>
13924          *         <li><b>dialogLogoutAction:</b> transfer or close the task at logout time</li></ul>
13925          */
13926         setMediaOptions: function(mediaOptions) {
13927             if ( mediaOptions ) {
13928                 this._mediaOptions = mediaOptions;
13929                 if ( !this._optionsHelper ) {
13930                     this._optionsHelper = new MediaOptionsHelper(this, this._mediaOptions);
13931                 }
13932             }
13933         },
13934 
13935         /**
13936          * Refresh this media object and optionally refresh the list of media dialogs associated with this object.
13937          *
13938          * @param {Integer} retries the number of times to retry synchronizing this media object.
13939          */
13940         refresh: function(retries) {
13941             retries = retries || 1;
13942             this._synchronize(retries);
13943             this.refreshMediaDialogs();
13944         },
13945 
13946         /**
13947          * Is the user in work state on this Media.
13948          * @returns {boolean} returns true if the media is in work state; false otherwise
13949          */
13950         isInWorkState: function() {
13951             return this.getState() === Media.States.WORK;
13952         },
13953 
13954         /**
13955          * Is the user in any state except LOGOUT on this Media.
13956          * @returns {boolean} returns true if the agent is in any state except LOGOUT in this media
13957          */
13958         isLoggedIn: function() {
13959          var state = this.getState();
13960          return state && state !== Media.States.LOGOUT;
13961         }
13962     });
13963 
13964     Media.States = /** @lends finesse.restservices.Media.States.prototype */ {
13965         /**
13966          * User Login on a non-voice Media.  Note that while this is an action, is not technically a state, since a
13967          * logged-in User will always be in a specific state (READY, NOT_READY, etc.).
13968          */
13969         LOGIN: "LOGIN",
13970         /**
13971          * User is logged out of this Media.
13972          */
13973         LOGOUT: "LOGOUT",
13974         /**
13975          * User is not ready on this Media.
13976          */
13977         NOT_READY: "NOT_READY",
13978         /**
13979          * User is ready for activity on this Media.
13980          */
13981         READY: "READY",
13982         /**
13983          * User has a dialog coming in, but has not accepted it.
13984          */
13985         RESERVED: "RESERVED",
13986         /**
13987          * The dialogs in this media have been interrupted by a dialog in a non-interruptible media.
13988          */
13989         INTERRUPTED: "INTERRUPTED",
13990         /**
13991          * User enters this state when failing over from one Finesse to the other or when failing over from one
13992          * PG side to the other.
13993          */
13994         WORK: "WORK",
13995         /**
13996          * @class Possible Media state values.
13997          * @constructs
13998          */
13999         _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
14000 
14001     };
14002 
14003     window.finesse = window.finesse || {};
14004     window.finesse.restservices = window.finesse.restservices || {};
14005     window.finesse.restservices.Media = Media;
14006 
14007     return Media;
14008 });
14009 /**
14010  * JavaScript representation of the Finesse Media collection
14011  * object which contains a list of Media objects.
14012  *
14013  * @requires finesse.clientservices.ClientServices
14014  * @requires Class
14015  * @requires finesse.FinesseBase
14016  * @requires finesse.restservices.RestBase
14017  * @requires finesse.restservices.Media
14018  */
14019 /** @private */
14020 define('restservices/MediaList',[
14021         'restservices/RestCollectionBase',
14022         'restservices/Media',
14023         'restservices/RestBase',
14024         'utilities/Utilities'
14025     ],
14026     function (RestCollectionBase, Media, RestBase, Utilities) {
14027         var MediaList = RestCollectionBase.extend(/** @lends finesse.restservices.MediaList.prototype */{
14028 
14029             /**
14030              * @class
14031              * JavaScript representation of a MediaList collection object.
14032              * @augments finesse.restservices.RestCollectionBase
14033              * @constructs
14034              * @see finesse.restservices.Media
14035              * @example
14036              *  mediaList = _user.getMediaList( {
14037              *      onCollectionAdd : _handleMediaAdd,
14038              *      onCollectionDelete : _handleMediaDelete,
14039              *      onLoad : _handleMediaListLoaded
14040              *  });
14041              *
14042              * _mediaCollection = mediaList.getCollection();
14043              * for (var mediaId in _mediaCollection) {
14044              *     if (_mediaCollection.hasOwnProperty(mediaId)) {
14045              *         media = _mediaCollection[mediaId];
14046              *         etc...
14047              *     }
14048              * }
14049              */
14050             _fakeConstuctor: function () {
14051                 /* This is here to hide the real init constructor from the public docs */
14052             },
14053 
14054             /**
14055              * @private
14056              * @param {Object} options
14057              *     An object with the following properties:<ul>
14058              *         <li><b>id:</b> The id of the object being constructed</li>
14059              *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
14060              *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
14061              *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
14062              *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
14063              *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
14064              *             <li><b>status:</b> {Number} The HTTP status code returned</li>
14065              *             <li><b>content:</b> {String} Raw string of response</li>
14066              *             <li><b>object:</b> {Object} Parsed object of response</li>
14067              *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
14068              *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
14069              *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
14070              *             </ul></li>
14071              *         </ul></li>
14072              *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
14073              **/
14074             init: function (options) {
14075                 this._super(options);
14076             },
14077 
14078             /**
14079              * @private
14080              * Gets the REST class for the current object - this is the MediaList class.
14081              */
14082             getRestClass: function () {
14083                 return MediaList;
14084             },
14085 
14086             /**
14087              * @private
14088              * Gets the REST class for the objects that make up the collection. - this
14089              * is the Media class.
14090              */
14091             getRestItemClass: function () {
14092                 return Media;
14093             },
14094 
14095             /**
14096              * @private
14097              * Gets the REST type for the current object - this is a "MediaList".
14098              */
14099             getRestType: function () {
14100                 return "MediaList";
14101             },
14102 
14103             /**
14104              * @private
14105              * Gets the REST type for the objects that make up the collection - this is "Media".
14106              */
14107             getRestItemType: function () {
14108                 return "Media";
14109             },
14110 
14111             /**
14112              * @private
14113              * Override default to indicates that the collection doesn't support making
14114              * requests.
14115              */
14116             supportsRequests: true,
14117 
14118             /**
14119              * @private
14120              * Override default to indicates that the collection subscribes to its objects.
14121              */
14122             supportsRestItemSubscriptions: true,
14123 
14124             /**
14125              * The REST URL in which this object can be referenced.
14126              * @return {String}
14127              *     The REST URI for this object.
14128              * @private
14129              */
14130             getRestUrl: function () {
14131                 var
14132                     restObj = this._restObj,
14133                     restUrl = "";
14134 
14135                 //Prepend the base REST object if one was provided.
14136                 if (restObj instanceof RestBase) {
14137                     restUrl += restObj.getRestUrl();
14138                 }
14139                 //Otherwise prepend with the default webapp name.
14140                 else {
14141                     restUrl += "/finesse/api";
14142                 }
14143 
14144                 //Append the REST type. (Media not MediaList)
14145                 restUrl += "/" + this.getRestItemType();
14146 
14147                 //Append ID if it is not undefined, null, or empty.
14148                 if (this._id) {
14149                     restUrl += "/" + this._id;
14150                 }
14151 
14152                 return restUrl;
14153             },
14154 
14155             /**
14156              * Getter for a Media from the MediaList collection.
14157              *  * @param {Object} options
14158              *     An object with the following properties:<ul>
14159              *         <li><b>id:</b> The id of the media to fetch</li>
14160              *         <li><b>onLoad(this): (optional)</b> callback handler for when the object is successfully loaded from the server</li>
14161              *         <li><b>onChange(this): (optional)</b> callback handler for when an update notification of the object is received</li>
14162              *         <li><b>onAdd(this): (optional)</b> callback handler for when a notification that the object is created is received</li>
14163              *         <li><b>onDelete(this): (optional)</b> callback handler for when a notification that the object is deleted is received</li>
14164              *         <li><b>onError(rsp): (optional)</b> callback handler for if loading of the object fails, invoked with the error response object:<ul>
14165              *             <li><b>status:</b> {Number} The HTTP status code returned</li>
14166              *             <li><b>content:</b> {String} Raw string of response</li>
14167              *             <li><b>object:</b> {Object} Parsed object of response</li>
14168              *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
14169              *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
14170              *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
14171              *             </ul></li>
14172              *         </ul></li>
14173              *         <li><b>mediaOptions:</b> {Object} An object with the following properties:<ul>
14174              *             <li><b>maxDialogLimit:</b> The id of the object being constructed</li>
14175              *             <li><b>interruptAction:</b> Accept or ignore interrupts</li>
14176              *             <li><b>dialogLogoutAction:</b> transfer or close the task at logout time</li></ul>
14177              *         </li></ul>
14178              *
14179              * @returns {finesse.restservices.Media}
14180              *     A Media object.
14181              */
14182             getMedia: function (options) {
14183                 this.isLoaded();
14184                 options = options || {};
14185                 var objectId = options.id,
14186                     media = this._collection[objectId];
14187 
14188                 //throw error if media not found
14189                 if(!media) {
14190                     throw new Error("No media found with id: " + objectId);
14191                 }
14192 
14193                 media.addHandler('load', options.onLoad);
14194                 media.addHandler('change', options.onChange);
14195                 media.addHandler('add', options.onAdd);
14196                 media.addHandler('delete', options.onDelete);
14197                 media.addHandler('error', options.onError);
14198 
14199                 media.setMediaOptions(options.mediaOptions);
14200 
14201                 return media;
14202             },
14203 
14204             /**
14205              * Utility method to build the internal collection data structure (object) based on provided data
14206              * @param {Object} data
14207              *     The data to build the internal collection from
14208              * @private
14209              */
14210             _buildCollection: function (data) {
14211                 //overriding this from RestBaseCollection because we need to pass in parentObj
14212                 //because restUrl is finesse/api/User/<useri>/Media/<mediaId>
14213                 //other objects like dialog have finesse/api/Dialog/<dialogId>
14214 
14215                 var i, object, objectId, dataArray;
14216                 if (data && this.getProperty(data, this.getRestItemType()) !== null) {
14217                     dataArray = Utilities.getArray(this.getProperty(data, this.getRestItemType()));
14218                     for (i = 0; i < dataArray.length; i += 1) {
14219 
14220                         object = {};
14221                         object[this.getRestItemType()] = dataArray[i];
14222 
14223                         //get id from object.id instead of uri, since uri is not there for some reason
14224                         objectId = object[this.getRestItemType()].id;//this._extractId(object);
14225 
14226                         //create the Media Object
14227                         this._collection[objectId] = new (this.getRestItemClass())({
14228                             id: objectId,
14229                             data: object,
14230                             parentObj: this._restObj
14231                         });
14232                         this.length += 1;
14233                     }
14234                 }
14235             }
14236         });
14237 
14238         window.finesse = window.finesse || {};
14239         window.finesse.restservices = window.finesse.restservices || {};
14240         window.finesse.restservices.MediaList = MediaList;
14241 
14242         return MediaList;
14243     });
14244 
14245 /**
14246 * JavaScript representation of the Finesse ReasonCodes collection
14247 * object which contains a list of ReasonCodes objects.
14248  *
14249  * @requires Class
14250  */
14251 
14252 /** @private */
14253 define('restservices/ReasonCodes',[], function () {
14254 
14255     var ReasonCodes = Class.extend(/** @lends finesse.restservices.ReasonCodes.prototype */{
14256         
14257         /**
14258          * @class
14259          * JavaScript representation of a ReasonCodes collection object. 
14260          * @augments Class
14261          * @constructs
14262          * @example
14263          *  _user.getNotReadyReasonCodes({
14264          *           success: handleNotReadyReasonCodesSuccess,
14265          *           error: handleNotReadyReasonCodesError
14266          *       });
14267          *  
14268          * handleNotReadyReasonCodesSuccess = function(reasonCodes) {
14269          *          for(var i = 0; i < reasonCodes.length; i++) {
14270          *             var reasonCode = reasonCodes[i];
14271          *             var reasonCodeId = reasonCode.id;
14272          *             var reasonCodeLabel = reasonCode.label;
14273          *          }
14274          *      }
14275          * }
14276         */
14277 
14278         _fakeConstuctor: function () {
14279             /* This is here to hide the real init constructor from the public docs */
14280         }
14281         
14282     });
14283  
14284     window.finesse = window.finesse || {};
14285     window.finesse.restservices = window.finesse.restservices || {};
14286     window.finesse.restservices.ReasonCodes = ReasonCodes;
14287        
14288     return ReasonCodes;
14289 });
14290 
14291 /**
14292  * JavaScript representation of the Finesse User object
14293  *
14294  * @requires finesse.clientservices.ClientServices
14295  * @requires Class
14296  * @requires finesse.FinesseBase
14297  * @requires finesse.restservices.RestBase
14298  */
14299 
14300 /** @private */
14301 define('restservices/User',[
14302     'restservices/RestBase',
14303     'restservices/Dialogs',
14304     'restservices/ClientLog',
14305     'restservices/Queues',
14306     'restservices/WrapUpReasons',
14307     'restservices/PhoneBooks',
14308     'restservices/Workflows',
14309     'restservices/UserMediaPropertiesLayout',
14310     'restservices/UserMediaPropertiesLayouts',
14311     'restservices/Media',
14312     'restservices/MediaList',
14313     'restservices/ReasonCodes',
14314     'utilities/Utilities'
14315 ],
14316 function (RestBase, Dialogs, ClientLog, Queues, WrapUpReasons, PhoneBooks, Workflows, UserMediaPropertiesLayout, UserMediaPropertiesLayouts, Media, MediaList, ReasonCodes, Utilities) {
14317 
14318     var User = RestBase.extend(/** @lends finesse.restservices.User.prototype */{
14319 
14320         _dialogs : null,
14321         _clientLogObj : null,
14322         _wrapUpReasons : null,
14323         _phoneBooks : null,
14324         _workflows : null,
14325         _mediaPropertiesLayout : null,
14326         _mediaPropertiesLayouts : null,
14327         _queues : null,
14328         media : null,
14329         mediaList : null,
14330 
14331         /**
14332          * @class
14333          * The User represents a Finesse Agent or Supervisor.
14334          *
14335          * @param {Object} options
14336          *     An object with the following properties:<ul>
14337          *         <li><b>id:</b> The id of the object being constructed</li>
14338          *         <li><b>onLoad(this): (optional)</b> callback handler for when the object is successfully loaded from the server</li>
14339          *         <li><b>onChange(this): (optional)</b> callback handler for when an update notification of the object is received</li>
14340          *         <li><b>onAdd(this): (optional)</b> callback handler for when a notification that the object is created is received</li>
14341          *         <li><b>onDelete(this): (optional)</b> callback handler for when a notification that the object is deleted is received</li>
14342          *         <li><b>onError(rsp): (optional)</b> callback handler for if loading of the object fails, invoked with the error response object:<ul>
14343          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
14344          *             <li><b>content:</b> {String} Raw string of response</li>
14345          *             <li><b>object:</b> {Object} Parsed object of response</li>
14346          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
14347          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
14348          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
14349          *             </ul></li>
14350          *         </ul></li>
14351          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
14352          * @augments finesse.restservices.RestBase
14353          * @constructs
14354          * @example
14355          *      _user = new finesse.restservices.User({
14356          *                      id: _id,
14357          *                      onLoad : _handleUserLoad,
14358          *                      onChange : _handleUserChange
14359          *      });
14360          **/
14361         init: function (options) {
14362             this._super(options);
14363         },
14364 
14365         Callbacks: {},
14366 
14367         /**
14368          * @private
14369          * Gets the REST class for the current object - this is the User object.
14370          */
14371         getRestClass: function () {
14372             return User;
14373         },
14374 
14375         /**
14376         * @private
14377          * Gets the REST type for the current object - this is a "User".
14378          */
14379         getRestType: function () {
14380             return "User";
14381         },
14382         /**
14383          * @private
14384          * overloading this to return URI
14385          */
14386         getXMPPNodePath: function () {
14387             return this.getRestUrl();
14388         },
14389         /**
14390         * @private
14391          * Returns whether this object supports subscriptions
14392          */
14393         supportsSubscriptions: function () {
14394             return true;
14395         },
14396 
14397         /**
14398          * Getter for the firstName of this User.
14399          * @returns {String}
14400          *     The firstName for this User
14401          */
14402         getFirstName: function () {
14403             this.isLoaded();
14404             return Utilities.convertNullToEmptyString(this.getData().firstName);
14405         },
14406 
14407 						        
14408         /**
14409 	     * Getter for the reasonCode of this User.
14410 	     * @returns {Object} The reasonCode for the state of the User<br>
14411          * The contents may include the following:<ul>
14412          *      <li>uri: The URI for the reason code object.
14413          *      <li>id: The unique ID for the reason code.
14414          *      <li>category: The category. Can be either NOT_READY or LOGOUT.
14415          *      <li>code: The numeric reason code value.
14416          *      <li>label: The label for the reason code.
14417          *      <li>forAll: Boolean flag that denotes the global status for the reason code.
14418          *      <li>systemCode: Boolean flag which denotes whether the reason code is system generated or custom one.
14419          *  </ul>
14420          *
14421          */
14422 		getReasonCode : function() {
14423 			this.isLoaded();
14424 			return this.getData().reasonCode;
14425 		},
14426 		
14427 		/**
14428          * Getter for the pending state reasonCode of this User.
14429          * 
14430          * @returns {Object} The reasonCode for the pending state of the User.<br>
14431          * The contents may include the following:<ul>
14432          *      <li>uri: The URI for the reason code object.
14433          *      <li>id: The unique ID for the reason code.
14434          *      <li>category: The category. Can be either NOT_READY or LOGOUT.
14435          *      <li>code: The numeric reason code value.
14436          *      <li>label: The label for the reason code.
14437          *      <li>forAll: Boolean flag that denotes the global status for the reason code.
14438          *      <li>systemCode: Boolean flag which denotes whether the reason code is system generated or custom one.
14439          *  </ul>
14440          */
14441 		getPendingStateReasonCode : function() {
14442 			this.isLoaded();
14443 			return this.getData().pendingStateReasonCode;
14444 		},
14445 
14446 						        
14447         /**
14448 		 * Getter for the reasonCode of this User.
14449 		 * 
14450 		 * @returns {Boolean} True if the reason code for the state of the user
14451          * is a system generated reason code.
14452 		 */
14453 		isReasonCodeReserved : function() {
14454 			var resonCode = this.getReasonCode();
14455 			if (resonCode) {
14456 				return resonCode.systemCode === "true" ? true
14457 						: false;
14458 			}
14459 			return false;
14460 		},
14461 
14462 
14463         /**
14464          * Getter for the lastName of this User.
14465          * @returns {String}
14466          *     The lastName for this User
14467          */
14468         getLastName: function () {
14469             this.isLoaded();
14470             return Utilities.convertNullToEmptyString(this.getData().lastName);
14471         },
14472 
14473         /**
14474          * Getter for the full name of this User.
14475          * The full name is of the format "FirstName LastName" (for example: "John Doe")
14476          * 
14477          * @returns {String}
14478          *     The full name of this User.
14479          */
14480         getFullName : function() {
14481             this.isLoaded();
14482 
14483             /*
14484              * Currently, the expected format is "FirstName LastName", but can differ later
14485              * to something "LastName, FirstName".
14486              * To accommodate such, we use formatString utility method so that, if required,
14487              * the same can be achieved just by flipping the format place holders as follows:
14488              * "{1}, {0}"
14489              * Also, the function could be enhanced to take the format parameter.
14490              */
14491             var fmt = "{0} {1}";
14492             return Utilities.formatString(fmt,
14493                 Utilities.convertNullToEmptyString(this.getData().firstName),
14494                 Utilities.convertNullToEmptyString(this.getData().lastName));
14495         },
14496 
14497         /**
14498          * Getter for the extension of this User.
14499          * @returns {String}
14500          *     The extension, if any, of this User
14501          */
14502         getExtension: function () {
14503             this.isLoaded();
14504             return Utilities.convertNullToEmptyString(this.getData().extension);
14505         },
14506 
14507         /**
14508          * Getter for the id of the Team of this User
14509          * @returns {String}
14510          *     The current (or last fetched) id of the Team of this User
14511          */
14512         getTeamId: function () {
14513             this.isLoaded();
14514             return this.getData().teamId;
14515         },
14516 
14517         /**
14518          * Getter for the name of the Team of this User
14519          * @returns {String}
14520          *     The current (or last fetched) name of the Team of this User
14521          */
14522         getTeamName: function () {
14523             this.isLoaded();
14524             return this.getData().teamName;
14525         },
14526 
14527         /**
14528          * Is user an agent?
14529          * @returns {Boolean} True if user has role of agent, else false.
14530          */
14531         hasAgentRole: function () {
14532             this.isLoaded();
14533             return this.hasRole("Agent");
14534         },
14535 
14536         /**
14537          * Is user a supervisor?
14538          * @returns {Boolean} True if user has role of supervisor, else false.
14539          */
14540         hasSupervisorRole: function () {
14541             this.isLoaded();
14542             return this.hasRole("Supervisor");
14543         },
14544 
14545         /**
14546          * @private
14547          * Checks to see if user has "theRole"
14548          * @returns {Boolean}
14549          */
14550         hasRole: function (theRole) {
14551             this.isLoaded();
14552             var result = false, i, roles, len;
14553 
14554             roles = this.getData().roles.role;
14555             len = roles.length;
14556             if (typeof roles === 'string') {
14557                 if (roles === theRole) {
14558                     result = true;
14559                 }
14560             } else {
14561                 for (i = 0; i < len ; i = i + 1) {
14562                     if (roles[i] === theRole) {
14563                         result = true;
14564                         break;
14565                     }
14566                 }
14567             }
14568 
14569             return result;
14570         },
14571 
14572         /**
14573          * Getter for the pending state of this User.
14574          * @returns {String}
14575          *     The pending state of this User
14576          * @see finesse.restservices.User.States
14577          */
14578         getPendingState: function () {
14579             this.isLoaded();
14580             return Utilities.convertNullToEmptyString(this.getData().pendingState);
14581         },
14582 
14583         /**
14584          * Getter for the state of this User.
14585          * @returns {String}
14586          *     The current (or last fetched) state of this User
14587          * @see finesse.restservices.User.States
14588          */
14589         getState: function () {
14590             this.isLoaded();
14591             return this.getData().state;
14592         },
14593         
14594         /**
14595          * Getter for the media state of this User.
14596          * @returns {String}
14597          *     The current (or last fetched) media state of this User
14598          *     Will be applicable only in CCX deployments
14599          *     When the agent is talking on a manual outbound call, it returns busy 
14600          *     The value will not be present in other cases.
14601          */
14602         getMediaState: function () {
14603             this.isLoaded();
14604             /* There is an assertion that the value should not be undefined while setting the value in datastore. Hence setting it to null*/
14605             if(this.getData().mediaState === undefined) {
14606                   this.getData().mediaState = null;
14607             }
14608             
14609             return this.getData().mediaState;
14610          },
14611         
14612         /**
14613          * Getter for the state change time of this User.
14614          * @returns {String}
14615          *     The state change time of this User
14616          */
14617         getStateChangeTime: function () {
14618             this.isLoaded();
14619             return this.getData().stateChangeTime;
14620         },
14621 
14622         /**
14623          * Getter for the wrap-up mode of this User.
14624          * @see finesse.restservices.User.WrapUpMode
14625          * @returns {String} The wrap-up mode of this user that is a value of {@link finesse.restservices.User.WrapUpMode}
14626          */
14627         getWrapUpOnIncoming: function () {
14628             this.isLoaded();
14629             return this.getData().settings.wrapUpOnIncoming;
14630         },
14631 
14632         /**
14633          * Is User required to go into wrap-up?
14634          * @see finesse.restservices.User.WrapUpMode
14635          * @return {Boolean}
14636          *      True if this agent is required to go into wrap-up.
14637          */
14638         isWrapUpRequired: function () {
14639             return (this.getWrapUpOnIncoming() === User.WrapUpMode.REQUIRED ||
14640                     this.getWrapUpOnIncoming() === User.WrapUpMode.REQUIRED_WITH_WRAP_UP_DATA);
14641         },
14642 
14643         /**
14644          * Checks to see if the user is considered a mobile agent by checking for
14645          * the existence of the mobileAgent node.
14646          * @returns {Boolean}
14647          *      True if this agent is a mobile agent.
14648          */
14649         isMobileAgent: function () {
14650             this.isLoaded();
14651             var ma = this.getData().mobileAgent;
14652             return ma !== null && typeof ma === "object";
14653         },
14654 
14655         /**
14656          * Getter for the mobile agent work mode.
14657          * @returns {finesse.restservices.User.WorkMode}
14658          *      If available, return the mobile agent work mode, otherwise null.
14659          */
14660         getMobileAgentMode: function () {
14661             this.isLoaded();
14662             if (this.isMobileAgent()) {
14663                 return this.getData().mobileAgent.mode;
14664             }
14665             return null;
14666         },
14667 
14668         /**
14669          * Getter for the mobile agent dial number.
14670          * @returns {String}
14671          *      If available, return the mobile agent dial number, otherwise null.
14672          */
14673         getMobileAgentDialNumber: function () {
14674             this.isLoaded();
14675             if (this.isMobileAgent()) {
14676                 return this.getData().mobileAgent.dialNumber;
14677             }
14678             return null;
14679         },
14680 
14681         /**
14682          * Getter for a Dialogs collection object that is associated with User.
14683          * @param {finesse.interfaces.RestObjectHandlers} [handlers] Object that sets callback handlers (only
14684          * applicable when Object has not been previously created).
14685          * @returns {finesse.restservices.Dialogs}
14686          *     A Dialogs collection object.
14687          */
14688         getDialogs: function (callbacks) {
14689             var options = callbacks || {};
14690             options.parentObj = this;
14691             this.isLoaded();
14692 
14693             if (this._dialogs === null) {
14694                 this._dialogs = new Dialogs(options);
14695             }
14696 
14697             return this._dialogs;
14698         },
14699 
14700         /**
14701          * Getter for Media collection object that is associated with User.
14702          * @param {finesse.interfaces.RestObjectHandlers} [handlers] Object that sets callback handlers (only
14703          * applicable when Object has not been previously created).
14704          * @returns {finesse.restservices.MediaList}
14705          *     A Media Dialogs collection object.
14706          */
14707         getMediaList: function (callbacks) {
14708             var options = callbacks || {};
14709             options.parentObj = this;
14710             this.isLoaded();
14711 
14712             if (this.mediaList === null) {
14713                 this.mediaList = new MediaList(options);
14714             }
14715 
14716             return this.mediaList;
14717         },
14718 
14719         /**
14720          * @private
14721          * Getter for a ClientLog object that is associated with User.
14722          * @param {finesse.interfaces.RestObjectHandlers} [handlers] Object that sets callback handlers (only 
14723          * applicable when Object has not been previously created).
14724          * @returns {finesse.restservices.ClientLog}
14725          *     A ClientLog collection object.
14726          */
14727         getClientLog: function (callbacks) {
14728             var options = callbacks || {};
14729             options.parentObj = this;
14730             this.isLoaded();
14731            
14732             if (this._clientLogObj === null) {
14733                 this._clientLogObj = new ClientLog(options);
14734             }
14735             else {
14736                 if(options.onLoad && typeof options.onLoad === "function") {
14737                 options.onLoad(this._clientLogObj);
14738                 }
14739             }
14740             return this._clientLogObj;
14741         },
14742        
14743         /**
14744          * Getter for a Queues collection object that is associated with User.
14745          * @param {finesse.interfaces.RestObjectHandlers} [handlers] Object that sets callback handlers (only 
14746          * applicable when Object has not been previously created).
14747          * @returns {finesse.restservices.Queues}
14748          *     A Queues collection object.
14749          */
14750         getQueues: function (callbacks) {
14751             var options = callbacks || {};
14752             options.parentObj = this;
14753             this.isLoaded();
14754     
14755             if (this._queues === null) {
14756                 this._queues = new Queues(options);
14757             }
14758     
14759             return this._queues;
14760         },
14761 
14762         /**
14763          * Getter for a WrapUpReasons collection object that is associated with User.
14764          * @param {finesse.interfaces.RestObjectHandlers} [handlers] Object that sets callback handlers (only 
14765          * applicable when Object has not been previously created).
14766          * @returns {finesse.restservices.WrapUpReasons}
14767          *     A WrapUpReasons collection object.
14768          */
14769         getWrapUpReasons: function (callbacks) {
14770             var options = callbacks || {};
14771             options.parentObj = this;
14772             this.isLoaded();
14773     
14774             if (this._wrapUpReasons === null) {
14775                 this._wrapUpReasons = new WrapUpReasons(options);
14776             }
14777     
14778             return this._wrapUpReasons;
14779         },
14780 
14781         /**
14782          * Getter for a PhoneBooks collection object that is associated with User.
14783          * @param {finesse.interfaces.RestObjectHandlers} [handlers] Object that sets callback handlers (only 
14784          * applicable when Object has not been previously created).
14785          * @returns {finesse.restservices.PhoneBooks}
14786          *     A PhoneBooks collection object.
14787          */
14788         getPhoneBooks: function (callbacks) {
14789             var options = callbacks || {};
14790             options.parentObj = this;
14791             this.isLoaded();
14792     
14793             if (this._phoneBooks === null) {
14794                 this._phoneBooks = new PhoneBooks(options);
14795             }
14796     
14797             return this._phoneBooks;
14798         },
14799 
14800         /**
14801          * @private
14802          * Loads the Workflows collection object that is associated with User and
14803          * 'returns' them to the caller via the handlers.
14804          * @param {finesse.interfaces.RestObjectHandlers} [handlers] Object that sets callback handlers (only 
14805          * applicable when Object has not been previously created).
14806          * @see finesse.restservices.Workflow
14807          * @see finesse.restservices.Workflows
14808          * @see finesse.restservices.RestCollectionBase
14809          */
14810         loadWorkflows: function (callbacks) {
14811             var options = callbacks || {};
14812             options.parentObj = this;
14813             this.isLoaded();
14814 
14815             if (this._workflows === null) {
14816                 this._workflows = new Workflows(options);
14817             } else {
14818                 this._workflows.refresh();
14819             }
14820 
14821         },
14822 
14823         /**
14824          * Getter for a UserMediaPropertiesLayout object that is associated with User.
14825          * @param {finesse.interfaces.RestObjectHandlers} [handlers] Object that sets callback handlers (only 
14826          * applicable when Object has not been previously created).
14827          * @returns {finesse.restservices.UserMediaPropertiesLayout}
14828          *     The UserMediaPropertiesLayout object associated with this user
14829          */
14830         getMediaPropertiesLayout: function (callbacks) {
14831             var options = callbacks || {};
14832             options.parentObj = this;
14833             options.id = this._id;
14834     
14835             this.isLoaded();
14836             if (this._mediaPropertiesLayout === null) {
14837                 this._mediaPropertiesLayout = new UserMediaPropertiesLayout(options);
14838             }
14839             return this._mediaPropertiesLayout;
14840         },
14841 
14842         /**
14843          * Getter for a UserMediaPropertiesLayouts object that is associated with User.
14844          * @param {finesse.interfaces.RestObjectHandlers} [handlers] Object that sets callback handlers (only 
14845          * applicable when Object has not been previously created).
14846          * @returns {finesse.restservices.UserMediaPropertiesLayout}
14847          *     The UserMediaPropertiesLayout object associated with this user
14848          */
14849         getMediaPropertiesLayouts: function (callbacks) {
14850             var options = callbacks || {};
14851             options.parentObj = this;
14852     
14853             this.isLoaded();
14854             if (this._mediaPropertiesLayouts === null) {
14855                 this._mediaPropertiesLayouts = new UserMediaPropertiesLayouts(options);
14856             }
14857             return this._mediaPropertiesLayouts;
14858         },
14859     
14860         /**
14861          * Getter for the supervised Teams this User (Supervisor) supervises, if any.
14862          * @see finesse.restservices.Team
14863          * @returns {Array}
14864          *     An array of Teams supervised by this User (Supervisor)
14865          */
14866         getSupervisedTeams: function () {
14867             this.isLoaded();
14868     
14869             try {
14870                 return Utilities.getArray(this.getData().teams.Team);
14871             } catch (e) {
14872                 return [];
14873             }
14874     
14875         },
14876     
14877         /**
14878          * Perform an agent login for this user, associating him with the
14879          * specified extension.
14880          * @param {Object} params
14881          *     An object containing properties for agent login.
14882          * @param {String} params.extension
14883          *     The extension to associate with this user
14884          * @param {Object} [params.mobileAgent]
14885          *     A mobile agent object containing the mode and dial number properties.
14886          * @param {finesse.interfaces.RequestHandlers} params.handlers
14887          * @see finesse.interfaces.RequestHandlers
14888          * @returns {finesse.restservices.User}
14889          *     This User object, to allow cascading
14890          * @private
14891          */
14892         _login: function (params) {
14893             var handlers, contentBody = {},
14894             restType = this.getRestType();
14895             
14896             // Protect against null dereferencing.
14897             params = params || {};
14898     
14899             contentBody[restType] = {
14900                 "state": User.States.LOGIN,
14901                 "extension": params.extension
14902             };
14903     
14904             // Create mobile agent node if available.
14905             if (typeof params.mobileAgent === "object") {
14906                 contentBody[restType].mobileAgent = {
14907                     "mode": params.mobileAgent.mode,
14908                     "dialNumber": params.mobileAgent.dialNumber
14909                 };
14910             }
14911     
14912             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
14913             handlers = params.handlers || {};
14914     
14915             this.restRequest(this.getRestUrl(), {
14916                 method: 'PUT',
14917                 success: handlers.success,
14918                 error: handlers.error,
14919                 content: contentBody
14920             });
14921     
14922             return this; // Allow cascading
14923         },
14924     
14925         /**
14926          * Perform an agent login for this user, associating him with the
14927          * specified extension.
14928          * @param {String} extension
14929          *     The extension to associate with this user
14930          * @param {finesse.interfaces.RequestHandlers} handlers
14931          *     An object containing the handlers for the request
14932          * @returns {finesse.restservices.User}
14933          *     This User object, to allow cascading
14934          */
14935         login: function (extension, handlers) {
14936             this.isLoaded();
14937             var params = {
14938                 "extension": extension,
14939                 "handlers": handlers
14940             };
14941             return this._login(params);
14942         },
14943         
14944         
14945 
14946     
14947         /**
14948          * Perform an agent login for this user, associating him with the
14949          * specified extension.
14950          * @param {String} extension
14951          *     The extension to associate with this user
14952          * @param {String} mode
14953          *     The mobile agent work mode as defined in finesse.restservices.User.WorkMode.
14954          * @param {String} extension
14955          *     The external dial number desired to be used by the mobile agent.
14956          * @param {finesse.interfaces.RequestHandlers} handlers
14957          *     An object containing the handlers for the request
14958          * @returns {finesse.restservices.User}
14959          *     This User object, to allow cascading
14960          */
14961         loginMobileAgent: function (extension, mode, dialNumber, handlers) {
14962             this.isLoaded();
14963             var params = {
14964                 "extension": extension,
14965                 "mobileAgent": {
14966                     "mode": mode,
14967                     "dialNumber": dialNumber
14968                 },
14969                 "handlers": handlers
14970             };
14971             return this._login(params);
14972         },
14973         
14974         
14975         _updateMobileAgent: function (params) {
14976             var handlers, contentBody = {},
14977             restType = this.getRestType();
14978 
14979             params = params || {};
14980 
14981             contentBody[restType] = {
14982             };
14983 
14984             if (typeof params.mobileAgent === "object") {
14985                 contentBody[restType].mobileAgent = {
14986                     "mode": params.mobileAgent.mode,
14987                     "dialNumber": params.mobileAgent.dialNumber
14988                 };
14989             }
14990 
14991             handlers = params.handlers || {};
14992 
14993             this.restRequest(this.getRestUrl(), {
14994                 method: 'PUT',
14995                 success: handlers.success,
14996                 error: handlers.error,
14997                 content: contentBody
14998             });
14999 
15000             return this;
15001         },
15002         
15003         /**
15004          * Update user object in Finesse with agent's mobile login information (Refer defect CSCvc35407)
15005          * @param {String} mode
15006          *      The mobile agent work mode as defined in finesse.restservices.User.WorkMode.
15007          * @param {String} dialNumber
15008          *      The external dial number desired to be used by the mobile agent.
15009          * @param {finesse.interfaces.RequestHandlers} handlers
15010          *     An object containing the handlers for the request
15011          * @returns {finesse.restservices.User}
15012          *     This User object, to allow cascading
15013          */
15014         updateToMobileAgent: function (mode, dialNumber, handlers) {
15015             this.isLoaded();
15016             var params = {
15017                 "mobileAgent": {
15018                     "mode": mode,
15019                     "dialNumber": dialNumber
15020                 },
15021                 "handlers": handlers
15022             };
15023             return this._updateMobileAgent(params);
15024         },
15025     
15026         /**
15027          * Perform an agent logout for this user.
15028          * @param {String} reasonCode
15029          *     The reason this user is logging out.  Pass null for no reason.
15030          * @param {finesse.interfaces.RequestHandlers} handlers
15031          *     An object containing the handlers for the request
15032          * @returns {finesse.restservices.User}
15033          *     This User object, to allow cascading
15034          */
15035         logout: function (reasonCode, handlers) {
15036             return this.setState("LOGOUT", reasonCode, handlers);
15037         },
15038     
15039         /**
15040          * Set the state of the user.
15041          * @param {String} newState
15042          *     The state you are setting
15043          * @param {ReasonCode} reasonCode
15044          *     The reason this user is logging out.  Pass null for no reason.
15045          * @param {finesse.interfaces.RequestHandlers} handlers
15046          *     An object containing the handlers for the request
15047          * @see finesse.restservices.User.States
15048          * @returns {finesse.restservices.User}
15049          *     This User object, to allow cascading
15050          */
15051         setState: function (newState, reasonCode, handlers) {
15052             this.isLoaded();
15053     
15054             var options, contentBody = {};
15055     
15056             if (!reasonCode) {
15057                 if(newState === "LOGOUT"){
15058                         contentBody[this.getRestType()] = {
15059                             "state": newState,
15060                             "logoutAllMedia": "true"
15061                         };
15062                 }
15063                 else{
15064                         contentBody[this.getRestType()] = {
15065                             "state": newState
15066                         };
15067                 }
15068                 
15069             } else {
15070                   if(newState === "LOGOUT"){
15071                         contentBody[this.getRestType()] = {
15072                              "state": newState,
15073                              "reasonCodeId": reasonCode.id,
15074                              "logoutAllMedia": "true"
15075                          };
15076                 }
15077                 else{
15078                     contentBody[this.getRestType()] = {
15079                              "state": newState,
15080                              "reasonCodeId": reasonCode.id
15081                          };
15082                     }
15083                
15084             }
15085     
15086             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
15087             handlers = handlers || {};
15088     
15089             options = {
15090                 method: 'PUT',
15091                 success: handlers.success,
15092                 error: handlers.error,
15093                 content: contentBody
15094             };
15095     
15096             // After removing the selective 202 handling, we should be able to just use restRequest
15097             this.restRequest(this.getRestUrl(), options);
15098     
15099             return this; // Allow cascading
15100         },
15101     
15102         /**
15103          * Make call to a particular phone number.
15104          *
15105          * @param {String} 
15106          *     The number to call
15107          * @param {finesse.interfaces.RequestHandlers} handlers
15108          *     An object containing the handlers for the request
15109          * @returns {finesse.restservices.User}
15110          *     This User object, to allow cascading
15111          */ 
15112         makeCall: function (number, handlers) {
15113             this.isLoaded();
15114     
15115             this.getDialogs().createNewCallDialog(number, this.getExtension(), handlers);
15116     
15117             return this; // Allow cascading
15118         },
15119     
15120         /**
15121          * Make a silent monitor call to a particular agent's phone number.
15122          *
15123          * @param {String} 
15124          *     The number to call
15125          * @param {finesse.interfaces.RequestHandlers} handlers
15126          *     An object containing the handlers for the request
15127          * @returns {finesse.restservices.User}
15128          *     This User object, to allow cascading
15129          */
15130         makeSMCall: function (number, handlers) {
15131             this.isLoaded();
15132     
15133             var actionType = "SILENT_MONITOR";
15134     
15135             this.getDialogs().createNewSuperviseCallDialog(number, this.getExtension(), actionType, handlers);
15136     
15137             return this; // Allow cascading
15138         },
15139         
15140     
15141         /**
15142          * Make a silent monitor call to a particular agent's phone number.
15143          *
15144          * @param {String}
15145          *     The number to call
15146          * @param {String} dialogUri
15147          *     The associated dialog uri of SUPERVISOR_MONITOR call
15148          * @param {finesse.interfaces.RequestHandlers} handlers
15149          *     An object containing the handlers for the request
15150          * @see finesse.restservices.dialog
15151          * @returns {finesse.restservices.User}
15152          *     This User object, to allow cascading
15153          */
15154         makeBargeCall:function (number, dialogURI, handlers) {
15155             this.isLoaded();
15156             var actionType = "BARGE_CALL";
15157             this.getDialogs().createNewBargeCall( this.getExtension(), number, actionType, dialogURI,handlers);
15158     
15159             return this; // Allow cascading
15160         },
15161         
15162         /**
15163          * Returns true if the user's current state will result in a pending state change. A pending state
15164          * change is a request to change state that does not result in an immediate state change. For
15165          * example if an agent attempts to change to the NOT_READY state while in the TALKING state, the
15166          * agent will not change state until the call ends.
15167          *
15168          * The current set of states that result in pending state changes is as follows:
15169          *     TALKING
15170          *     HOLD
15171          *     RESERVED_OUTBOUND_PREVIEW
15172          *  @returns {Boolean} True if there is a pending state change.
15173          *  @see finesse.restservices.User.States
15174          */
15175         isPendingStateChange: function () {
15176             var state = this.getState();
15177             return state && ((state === User.States.TALKING) || (state === User.States.HOLD) || (state === User.States.RESERVED_OUTBOUND_PREVIEW));
15178         },
15179         
15180         /**
15181          * Returns true if the user's current state is WORK or WORK_READY. This is used so
15182          * that a pending state is not cleared when moving into wrap up (work) mode. 
15183          * Note that we don't add this as a pending state, since changes while in wrap up
15184          * occur immediately (and we don't want any "pending state" to flash on screen.
15185          * 
15186          * @see finesse.restservices.User.States
15187          * @returns {Boolean} True if user is in wrap-up mode.
15188          */
15189         isWrapUp: function () {
15190             var state = this.getState();
15191             return state && ((state === User.States.WORK) || (state === User.States.WORK_READY));
15192         },
15193     
15194         /**
15195          * @private
15196          * Parses a uriString to retrieve the id portion
15197          * @param {String} uriString
15198          * @return {String} id
15199          */
15200         _parseIdFromUriString : function (uriString) {
15201             return Utilities.getId(uriString);
15202         },
15203 						
15204         /**
15205 		 * Gets the user's Reason Code label. Works for both Not Ready and
15206 		 * Logout reason codes
15207 		 * 
15208 		 * @return {String} the reason code label, or empty string if none
15209 		 */
15210 		getReasonCodeLabel : function() {
15211 			this.isLoaded();
15212 
15213 			if (this.getData().reasonCode) {
15214 				return this.getData().reasonCode.label;
15215 			} else {
15216 				return "";
15217 			}
15218 		},
15219     
15220         /**
15221 		 * Gets the user's Not Ready reason code.
15222 		 * 
15223 		 * @return {String} Reason Code Id, or undefined if not set or
15224 		 *         indeterminate
15225 		 */
15226         getNotReadyReasonCodeId : function () {
15227             this.isLoaded();
15228     
15229             var reasoncodeIdResult, finesseServerReasonCodeId;
15230             finesseServerReasonCodeId = this.getData().reasonCodeId;
15231     
15232             //FinesseServer will give "-l" => we will set to undefined (for convenience)
15233             if (finesseServerReasonCodeId !== "-1") {
15234                 reasoncodeIdResult = finesseServerReasonCodeId;
15235             }
15236     
15237             return reasoncodeIdResult;
15238         },
15239     
15240         /**
15241          * Performs a GET against the Finesse server looking up the reasonCodeId specified.
15242          * Note that there is no return value; use the success handler to process a
15243          * valid return.
15244          * @param {finesse.interfaces.RequestHandlers} handlers
15245          *     An object containing the handlers for the request
15246          * @param {String} reasonCodeId The id for the reason code to lookup
15247          * 
15248          */
15249         getReasonCodeById : function (handlers, reasonCodeId)
15250         {
15251             var self = this, contentBody, reasonCode, url;
15252             contentBody = {};
15253     
15254             url = this.getRestUrl() + "/ReasonCode/" + reasonCodeId;
15255             this.restRequest(url, {
15256                 method: 'GET',
15257                 success: function (rsp) {
15258                     reasonCode = {
15259                         uri: rsp.object.ReasonCode.uri,
15260                         label: rsp.object.ReasonCode.label,
15261                         id: self._parseIdFromUriString(rsp.object.ReasonCode.uri)
15262                     };
15263                     handlers.success(reasonCode);
15264                 },
15265                 error: function (rsp) {
15266                     handlers.error(rsp);
15267                 },
15268                 content: contentBody
15269             });
15270         },
15271     
15272         /**
15273          * Performs a GET against Finesse server retrieving all the specified type of reason codes.
15274          * @param {String} type (LOGOUT or NOT_READY)
15275          * @param {finesse.interfaces.RequestHandlers} handlers
15276          *     An object containing the handlers for the request
15277          */
15278         _getReasonCodesByType : function (type, handlers)
15279         {
15280             var self = this, contentBody = {}, url, reasonCodes, i, reasonCodeArray;
15281     
15282             url = this.getRestUrl() + "/ReasonCodes?category=" + type;
15283             this.restRequest(url, {
15284                 method: 'GET',
15285                 success: function (rsp) {
15286                     reasonCodes = [];
15287     
15288                     reasonCodeArray = rsp.object.ReasonCodes.ReasonCode;
15289                     if (reasonCodeArray === undefined) {
15290                         reasonCodes = undefined;
15291                     } else if (reasonCodeArray[0] !== undefined) {
15292                         for (i = 0; i < reasonCodeArray.length; i = i + 1) {
15293                             reasonCodes[i] = {
15294                                 label: rsp.object.ReasonCodes.ReasonCode[i].label,
15295                                 id: self._parseIdFromUriString(rsp.object.ReasonCodes.ReasonCode[i].uri)
15296                             };
15297                         }
15298                     } else {
15299                         reasonCodes[0] = {
15300                             label: rsp.object.ReasonCodes.ReasonCode.label,
15301                             id: self._parseIdFromUriString(rsp.object.ReasonCodes.ReasonCode.uri)
15302                         };
15303                     }
15304                     handlers.success(reasonCodes);
15305                 },
15306                 error: function (rsp) {
15307                     handlers.error(rsp);
15308                 },
15309                 content: contentBody
15310             });
15311         },
15312     
15313         /**
15314          * Performs a GET against the Finesse server and retrieves all of the Not Ready
15315          * reason codes. Note that there is no return value; use the success handler to
15316          * process a valid return.
15317          *
15318          * <pre class="code">
15319          *      _user.getSignoutReasonCodes({
15320          *           success: handleSignoutReasonCodesSuccess,
15321          *           error: handleSignoutReasonCodesError
15322          *       });
15323          * </pre>
15324          *
15325          * @see finesse.restservices.ReasonCodes
15326          *
15327          * @param {finesse.interfaces.RequestHandlers} handlers
15328          *     An object containing the handlers for the request
15329          */
15330         getSignoutReasonCodes : function (handlers)
15331         {
15332             this._getReasonCodesByType("LOGOUT", handlers);
15333         },
15334     
15335         /**
15336          * Performs a GET against the Finesse server and retrieves all of the Not Ready
15337          * reason codes. Note that there is no return value; use the success handler to
15338          * process a valid return.
15339          *
15340          * <pre class="code">
15341          *      _user.getNotReadyReasonCodes({
15342          *           success: handleNotReadyReasonCodesSuccess,
15343          *           error: handleNotReadyReasonCodesError
15344          *       });
15345          * </pre>
15346          *
15347          * @see finesse.restservices.ReasonCodes
15348          *
15349          * @param {finesse.interfaces.RequestHandlers} handlers
15350          *     An object containing the handlers for the request
15351          */
15352         getNotReadyReasonCodes : function (handlers)
15353         {
15354             this._getReasonCodesByType("NOT_READY", handlers);
15355         }
15356     });
15357     User.MediaStates = /** @lends finesse.restservices.User.MediaStates.prototype */ {
15358          /**
15359          * Will be applicable only in CCX deployments
15360          * When the agent is talking on a manual outbound call
15361          */
15362          BUSY: "BUSY",
15363 			 /**
15364 	         * @class Possible Agent Media States.
15365 	         * @constructs
15366 	         */
15367 	        _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
15368 	        
15369     };
15370     User.States = /** @lends finesse.restservices.User.States.prototype */ {
15371             /**
15372              * User Login.  Note that while this is an action, is not technically a state, since a 
15373              * logged-in User will always be in a specific state (READY, NOT_READY, TALKING, etc.).
15374              */
15375             LOGIN: "LOGIN",
15376             /**
15377              * User is logged out.
15378              */
15379             LOGOUT: "LOGOUT",
15380             /**
15381              * User is not ready. Note that in UCCX implementations, the user is in this state while on a non-routed call.
15382              */
15383             NOT_READY: "NOT_READY",
15384             /**
15385              * User is ready for calls.
15386              */
15387             READY: "READY",
15388             /**
15389              * User has a call coming in, but has not answered it.
15390              */
15391             RESERVED: "RESERVED",
15392             /**
15393              * User has an outbound call being made, but has not been connected to it.
15394              */
15395             RESERVED_OUTBOUND: "RESERVED_OUTBOUND",
15396             /**
15397              * User has an outbound call's preview information being displayed, but has not acted on it.
15398              */
15399             RESERVED_OUTBOUND_PREVIEW: "RESERVED_OUTBOUND_PREVIEW",
15400             /**
15401              * User is on a call.  Note that in UCCX implementations, this is for routed calls only.
15402              */
15403             TALKING: "TALKING",
15404             /**
15405              * User is on hold.  Note that in UCCX implementations, the user remains in TALKING state while on hold.
15406              */
15407             HOLD: "HOLD",
15408             /**
15409              * User is wrap-up/work mode.  This mode is typically configured to time out, after which the user becomes NOT_READY.
15410              */
15411             WORK: "WORK",
15412             /**
15413              * This is the same as WORK, except that after time out user becomes READY.
15414              */
15415             WORK_READY: "WORK_READY",
15416             /**
15417              * @class Possible User state values.
15418              * @constructs
15419              */
15420             _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
15421           
15422         };
15423     
15424     User.WorkMode = { /** @lends finesse.restservices.User.WorkMode.prototype */
15425         /**
15426          * Mobile agent is connected (dialed) for each incoming call received.
15427          */
15428         CALL_BY_CALL: "CALL_BY_CALL",
15429         /**
15430          * Mobile agent is connected (dialed) at login.
15431          */
15432         NAILED_CONNECTION: "NAILED_CONNECTION",
15433         /**
15434          * @class Possible Mobile Agent Work Mode Types.
15435          * @constructs
15436          */
15437         _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
15438         
15439     };
15440 
15441     User.WrapUpMode = { /** @lends finesse.restservices.User.WrapUpMode.prototype */
15442         /**
15443          * Agent must go into wrap-up when call ends
15444          */
15445         REQUIRED: "REQUIRED",
15446         /**
15447          * Agent must go into wrap-up when call ends and must enter wrap-up data
15448          */
15449         REQUIRED_WITH_WRAP_UP_DATA: "REQUIRED_WITH_WRAP_UP_DATA",
15450         /**
15451          * Agent can choose to go into wrap-up on a call-by-call basis when the call ends
15452          */
15453         OPTIONAL: "OPTIONAL",
15454         /**
15455          * Agent is not allowed to go into wrap-up when call ends.
15456          */
15457         NOT_ALLOWED: "NOT_ALLOWED",
15458         /**
15459          * @class Possible Wrap-up Mode Types.
15460          * @constructs
15461          */
15462         _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
15463         
15464     };
15465 
15466     window.finesse = window.finesse || {};
15467     window.finesse.restservices = window.finesse.restservices || {};
15468     window.finesse.restservices.User = User;
15469         
15470     return User;
15471 });
15472 
15473 /**
15474  * JavaScript representation of the Finesse Users collection
15475  * object which contains a list of Users objects.
15476  *
15477  * @requires finesse.clientservices.ClientServices
15478  * @requires Class
15479  * @requires finesse.FinesseBase
15480  * @requires finesse.restservices.RestBase
15481  * @requires finesse.restservices.RestCollectionBase
15482  * @requires finesse.restservices.User
15483  */
15484 
15485 /** @private */
15486 define('restservices/Users',[
15487     'restservices/RestCollectionBase',
15488     'restservices/RestBase',
15489     'restservices/User'
15490 ],
15491 function (RestCollectionBase, RestBase, User) {
15492 
15493     var Users = RestCollectionBase.extend(/** @lends finesse.restservices.Users.prototype */{
15494 
15495     /**
15496      * @class
15497      * JavaScript representation of a Users collection object. 
15498      * While there is no method provided to retrieve all Users, this collection is
15499      * used to return the Users in a supervised Team.
15500      * @augments finesse.restservices.RestCollectionBase
15501      * @constructs
15502      * @see finesse.restservices.Team
15503      * @see finesse.restservices.User
15504      * @see finesse.restservices.User#getSupervisedTeams
15505      * @example
15506      *  // Note: The following method gets an Array of Teams, not a Collection.
15507      *  _teams = _user.getSupervisedTeams();
15508      *  if (_teams.length > 0) {
15509      *      _team0Users = _teams[0].getUsers();
15510      *  }
15511      */
15512     _fakeConstuctor: function () {
15513         /* This is here to hide the real init constructor from the public docs */
15514     },
15515         
15516     /**
15517      * @private
15518      * JavaScript representation of the Finesse Users collection
15519      * object which contains a list of Users objects.
15520      *
15521 	 * @param {Object} options
15522 	 *     An object with the following properties:<ul>
15523      *         <li><b>id:</b> The id of the object being constructed</li>
15524      *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
15525      *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
15526      *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
15527      *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
15528      *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
15529      *             <li><b>status:</b> {Number} The HTTP status code returned</li>
15530      *             <li><b>content:</b> {String} Raw string of response</li>
15531      *             <li><b>object:</b> {Object} Parsed object of response</li>
15532      *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
15533      *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
15534      *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
15535      *             </ul></li>
15536      *         </ul></li>
15537      *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
15538      **/
15539 	init: function (options) {
15540 		this._super(options);
15541 	},
15542 
15543 	/**
15544      * @private
15545 	 * Gets the REST class for the current object - this is the Users class.
15546 	 */
15547 	getRestClass: function () {
15548 	    return Users;
15549 	},
15550 
15551 	/**
15552      * @private
15553 	 * Gets the REST class for the objects that make up the collection. - this
15554 	 * is the User class.
15555 	 */
15556 	getRestItemClass: function () {
15557 		return User;
15558 	},
15559 
15560 	/**
15561      * @private
15562 	 * Gets the REST type for the current object - this is a "Users".
15563 	 */
15564 	getRestType: function () {
15565 	    return "Users";
15566 	},
15567 
15568 	/**
15569      * @private
15570 	 * Gets the REST type for the objects that make up the collection - this is "User".
15571 	 */
15572 	getRestItemType: function () {
15573 	    return "User";
15574 	},
15575 
15576 	/**
15577      * @private
15578      * Gets the node path for the current object - this is the team Users node
15579      * @returns {String} The node path
15580      */
15581     getXMPPNodePath: function () {
15582 		return this.getRestUrl();
15583     },
15584 
15585     /**
15586      * @private
15587      * Overloading _doGET to reroute the GET to /Team/id from /Team/id/Users
15588      * This needs to be done because the GET /Team/id/Users API is missing
15589      * @returns {Users} This Users (collection) object to allow cascading
15590      */
15591     _doGET: function (handlers) {
15592         var _this = this;
15593         handlers = handlers || {};
15594         // Only do this for /Team/id/Users
15595         if (this._restObj && this._restObj.getRestType() === "Team") {
15596             this._restObj._doGET({
15597                 success: function (rspObj) {
15598                     // Making sure the response was a valid Team
15599                     if (_this._restObj._validate(rspObj.object)) {
15600                         // Shimmying the response to look like a Users collection by extracting it from the Team response
15601                         rspObj.object[_this.getRestType()] = rspObj.object[_this._restObj.getRestType()][_this.getRestType().toLowerCase()];
15602                         handlers.success(rspObj);
15603                     } else {
15604                         handlers.error(rspObj);
15605                     }
15606                 },
15607                 error: handlers.error
15608             });
15609             return this; // Allow cascading
15610         } else {
15611             return this._super(handlers);
15612         }
15613     },
15614 
15615 	/**
15616      * @private
15617      * Override default to indicates that the collection doesn't support making
15618 	 * requests.
15619 	 */
15620 	supportsRequests: false,
15621 
15622     /**
15623      * @private
15624      * Indicates that this collection handles the subscription for its items
15625      */
15626     handlesItemSubscription: true,
15627 	
15628     /**
15629      * @private
15630      * Override default to indicate that we need to subscribe explicitly
15631      */
15632     explicitSubscription: true
15633     
15634 	});
15635 
15636     window.finesse = window.finesse || {};
15637     window.finesse.restservices = window.finesse.restservices || {};
15638     window.finesse.restservices.Users = Users;
15639 
15640     return Users;
15641 });
15642 
15643 /**
15644  * JavaScript representation of the Finesse Team Not Ready Reason Code Assignment object.
15645  *
15646  * @requires finesse.clientservices.ClientServices
15647  * @requires Class
15648  * @requires finesse.FinesseBase
15649  * @requires finesse.restservices.RestBase
15650  */
15651 
15652 /** @private */
15653 define('restservices/TeamNotReadyReasonCode',['restservices/RestBase'], function (RestBase) {
15654     
15655     var TeamNotReadyReasonCode = RestBase.extend(/** @lends finesse.restservices.TeamNotReadyReasonCode.prototype */ {
15656 
15657         /**
15658          * @class
15659          * JavaScript representation of a Team Not Ready ReasonCode object. Also exposes
15660          * methods to operate on the object against the server.
15661          *
15662          * @param {Object} options
15663          *     An object with the following properties:<ul>
15664          *         <li><b>id:</b> The id of the object being constructed</li>
15665          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
15666          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
15667          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
15668          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
15669          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
15670          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
15671          *             <li><b>content:</b> {String} Raw string of response</li>
15672          *             <li><b>object:</b> {Object} Parsed object of response</li>
15673          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
15674          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
15675          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
15676          *             </ul></li>
15677          *         </ul></li>
15678          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
15679          * @augments finesse.restservices.RestBase
15680          * @constructs
15681          **/
15682         init: function (options) {
15683             this._super(options);
15684         },
15685     
15686         /**
15687          * @private
15688          * Gets the REST class for the current object - this is the TeamNotReadyReasonCode class.
15689          * @returns {Object} The TeamNotReadyReasonCode class.
15690          */
15691         getRestClass: function () {
15692             return TeamNotReadyReasonCode;
15693         },
15694     
15695         /**
15696          * @private
15697          * Gets the REST type for the current object - this is a "ReasonCode".
15698          * @returns {String} The ReasonCode string.
15699          */
15700         getRestType: function () {
15701             return "ReasonCode";
15702         },
15703     
15704         /**
15705          * @private
15706          * Override default to indicate that this object doesn't support making
15707          * requests.
15708          */
15709         supportsRequests: false,
15710     
15711         /**
15712          * @private
15713          * Override default to indicate that this object doesn't support subscriptions.
15714          */
15715         supportsSubscriptions: false,
15716     
15717         /**
15718          * Getter for the category.
15719          * @returns {String} The category.
15720          */
15721         getCategory: function () {
15722             this.isLoaded();
15723             return this.getData().category;
15724         },
15725     
15726         /**
15727          * Getter for the code.
15728          * @returns {String} The code.
15729          */
15730         getCode: function () {
15731             this.isLoaded();
15732             return this.getData().code;
15733         },
15734     
15735         /**
15736          * Getter for the label.
15737          * @returns {String} The label.
15738          */
15739         getLabel: function () {
15740             this.isLoaded();
15741             return this.getData().label;
15742         },
15743     
15744         /**
15745          * Getter for the forAll value.
15746          * @returns {String} The forAll.
15747          */
15748         getForAll: function () {
15749             this.isLoaded();
15750             return this.getData().forAll;
15751         },
15752     
15753         /**
15754          * Getter for the Uri value.
15755          * @returns {String} The Uri.
15756          */
15757         getUri: function () {
15758             this.isLoaded();
15759             return this.getData().uri;
15760         },
15761         
15762         /**
15763          * Getter for the systemCode value.
15764          * @returns {String} The value for systemCode.
15765          * @since   11.6(1)-ES1 onwards
15766          */
15767         getSystemCode: function () {
15768             this.isLoaded();
15769             return this.getData().systemCode;
15770         }
15771 
15772     });
15773     
15774     window.finesse = window.finesse || {};
15775     window.finesse.restservices = window.finesse.restservices || {};
15776     window.finesse.restservices.TeamNotReadyReasonCode = TeamNotReadyReasonCode;
15777         
15778     return TeamNotReadyReasonCode;
15779 });
15780 
15781 /**
15782 * JavaScript representation of the Finesse TeamNotReadyReasonCodes collection
15783 * object which contains a list of TeamNotReadyReasonCode objects.
15784  *
15785  * @requires finesse.clientservices.ClientServices
15786  * @requires Class
15787  * @requires finesse.FinesseBase
15788  * @requires finesse.restservices.RestBase
15789  * @requires finesse.restservices.Dialog
15790  * @requires finesse.restservices.RestCollectionBase
15791  */
15792 
15793 /** @private */
15794 define('restservices/TeamNotReadyReasonCodes',[
15795     'restservices/RestCollectionBase',
15796     'restservices/RestBase',
15797     'restservices/TeamNotReadyReasonCode'
15798 ],
15799 function (RestCollectionBase, RestBase, TeamNotReadyReasonCode) {
15800 
15801     var TeamNotReadyReasonCodes = RestCollectionBase.extend( {
15802 
15803       /**
15804        * @class
15805        * JavaScript representation of a TeamNotReadyReasonCodes collection object. Also exposes
15806        * methods to operate on the object against the server.
15807        *
15808        * @param {Object} options
15809        *     An object with the following properties:<ul>
15810        *         <li><b>id:</b> The id of the object being constructed</li>
15811        *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
15812        *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
15813        *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
15814        *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
15815        *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
15816        *             <li><b>status:</b> {Number} The HTTP status code returned</li>
15817        *             <li><b>content:</b> {String} Raw string of response</li>
15818        *             <li><b>object:</b> {Object} Parsed object of response</li>
15819        *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
15820        *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
15821        *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
15822        *             </ul></li>
15823        *         </ul></li>
15824        *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
15825        * @augments finesse.restservices.RestCollectionBase
15826        * @constructs
15827        **/
15828       init: function (options) {
15829           this._super(options);
15830       },
15831     
15832       /**
15833        * @private
15834        * Gets the REST class for the current object - this is the TeamNotReadyReasonCodes class.
15835        */
15836       getRestClass: function () {
15837           return TeamNotReadyReasonCodes;
15838       },
15839     
15840       /**
15841        * @private
15842        * Gets the REST class for the objects that make up the collection. - this
15843        * is the TeamNotReadyReasonCode class.
15844        */
15845       getRestItemClass: function () {
15846           return TeamNotReadyReasonCode;
15847       },
15848     
15849       /**
15850        * @private
15851        * Gets the REST type for the current object - this is a "ReasonCodes".
15852        */
15853       getRestType: function () {
15854           return "ReasonCodes";
15855       },
15856     
15857       /**
15858        * @private
15859        * Overrides the parent class.  Returns the url for the NotReadyReasonCodes resource
15860        */
15861       getRestUrl: function () {
15862           // return ("/finesse/api/" + this.getRestType() + "?category=NOT_READY");
15863           var restObj = this._restObj,
15864               restUrl = "";
15865           //Prepend the base REST object if one was provided.
15866           //Otherwise prepend with the default webapp name.
15867           if (restObj instanceof RestBase) {
15868               restUrl += restObj.getRestUrl();
15869           }
15870           else {
15871               restUrl += "/finesse/api";
15872           }
15873           //Append the REST type.
15874           restUrl += "/ReasonCodes?category=NOT_READY";
15875           //Append ID if it is not undefined, null, or empty.
15876           if (this._id) {
15877               restUrl += "/" + this._id;
15878           }
15879           return restUrl;
15880       },
15881     
15882       /**
15883        * @private
15884        * Gets the REST type for the objects that make up the collection - this is "ReasonCode".
15885        */
15886       getRestItemType: function () {
15887           return "ReasonCode";
15888       },
15889     
15890       /**
15891        * @private
15892        * Override default to indicates that the collection supports making
15893        * requests.
15894        */
15895       supportsRequests: true,
15896     
15897       /**
15898        * @private
15899        * Override default to indicate that this object doesn't support subscriptions.
15900        */
15901       supportsRestItemSubscriptions: false,
15902     
15903       /**
15904        * @private
15905        * Retrieve the Not Ready Reason Codes.
15906        *
15907        * @returns {TeamNotReadyReasonCodes}
15908        *     This TeamNotReadyReasonCodes object to allow cascading.
15909        */
15910       get: function () {
15911           // set loaded to false so it will rebuild the collection after the get
15912           this._loaded = false;
15913           // reset collection
15914           this._collection = {};
15915           // perform get
15916           this._synchronize();
15917           return this;
15918       },
15919     
15920       /**
15921        * @private
15922        * Set up the PutSuccessHandler for TeamNotReadyReasonCodes
15923        * @param {Object} reasonCodes
15924        * @param {String} contentBody
15925        * @param successHandler    
15926        * @return {function}
15927        */
15928       createPutSuccessHandler: function (reasonCodes, contentBody, successHandler) {
15929           return function (rsp) {
15930               // Update internal structure based on response. Here we
15931               // inject the contentBody from the PUT request into the
15932               // rsp.object element to mimic a GET as a way to take
15933               // advantage of the existing _processResponse method.
15934               rsp.object = contentBody;
15935               reasonCodes._processResponse(rsp);
15936     
15937               //Remove the injected contentBody object before cascading response
15938               rsp.object = {};
15939     
15940               //cascade response back to consumer's response handler
15941               successHandler(rsp);
15942           };
15943       },
15944     
15945       /**
15946        * @private
15947        * Perform the REST API PUT call to update the reason code assignments for the team
15948        * @param {string[]} newValues
15949        * @param handlers     
15950        */
15951       update: function (newValues, handlers) {
15952           this.isLoaded();
15953           var contentBody = {}, contentBodyInner = [], i, innerObject = {};
15954     
15955           contentBody[this.getRestType()] = {
15956           };
15957     
15958           for (i in newValues) {
15959               if (newValues.hasOwnProperty(i)) {
15960                   innerObject = {
15961                       "uri": newValues[i]
15962                   };
15963                   contentBodyInner.push(innerObject);
15964               }
15965           }
15966     
15967           contentBody[this.getRestType()] = {
15968               "ReasonCode" : contentBodyInner
15969           };
15970     
15971           // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
15972           handlers = handlers || {};
15973     
15974           this.restRequest(this.getRestUrl(), {
15975               method: 'PUT',
15976               success: this.createPutSuccessHandler(this, contentBody, handlers.success),
15977               error: handlers.error,
15978               content: contentBody
15979           });
15980     
15981           return this; // Allow cascading
15982       }
15983   });
15984   
15985     window.finesse = window.finesse || {};
15986     window.finesse.restservices = window.finesse.restservices || {};
15987     window.finesse.restservices.TeamNotReadyReasonCodes = TeamNotReadyReasonCodes;
15988     
15989   return TeamNotReadyReasonCodes;
15990 });
15991 
15992 /**
15993  * JavaScript representation of the Finesse Team Wrap Up Reason object.
15994  *
15995  * @requires finesse.clientservices.ClientServices
15996  * @requires Class
15997  * @requires finesse.FinesseBase
15998  * @requires finesse.restservices.RestBase
15999  */
16000 /** @private */
16001 define('restservices/TeamWrapUpReason',['restservices/RestBase'], function (RestBase) {
16002 
16003     var TeamWrapUpReason = RestBase.extend({
16004 
16005     /**
16006      * @class
16007      * JavaScript representation of a TeamWrapUpReason object. Also exposes
16008      * methods to operate on the object against the server.
16009      *
16010      * @param {Object} options
16011      *     An object with the following properties:<ul>
16012      *         <li><b>id:</b> The id of the object being constructed</li>
16013      *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
16014      *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
16015      *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
16016      *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
16017      *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
16018      *             <li><b>status:</b> {Number} The HTTP status code returned</li>
16019      *             <li><b>content:</b> {String} Raw string of response</li>
16020      *             <li><b>object:</b> {Object} Parsed object of response</li>
16021      *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
16022      *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
16023      *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
16024      *             </ul></li>
16025      *         </ul></li>
16026      *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
16027      * @constructs
16028      **/
16029     init: function (options) {
16030         this._super(options);
16031     },
16032 
16033     /**
16034      * @private
16035      * Gets the REST class for the current object - this is the TeamWrapUpReason class.
16036      * @returns {Object} The TeamWrapUpReason class.
16037      */
16038     getRestClass: function () {
16039         return TeamWrapUpReason;
16040     },
16041 
16042     /**
16043      * @private
16044      * Gets the REST type for the current object - this is a "WrapUpReason".
16045      * @returns {String} The WrapUpReason string.
16046      */
16047     getRestType: function () {
16048         return "WrapUpReason";
16049     },
16050 
16051     /**
16052      * @private
16053      * Override default to indicate that this object doesn't support making
16054      * requests.
16055      */
16056     supportsRequests: false,
16057 
16058     /**
16059      * @private
16060      * Override default to indicate that this object doesn't support subscriptions.
16061      */
16062     supportsSubscriptions: false,
16063 
16064     /**
16065      * Getter for the label.
16066      * @returns {String} The label.
16067      */
16068     getLabel: function () {
16069         this.isLoaded();
16070         return this.getData().label;
16071     },
16072 
16073     /**
16074      * @private
16075      * Getter for the forAll value.
16076      * @returns {Boolean} True if global
16077      */
16078     getForAll: function () {
16079         this.isLoaded();
16080         return this.getData().forAll;
16081     },
16082 
16083     /**
16084      * @private
16085      * Getter for the Uri value.
16086      * @returns {String} The Uri.
16087      */
16088     getUri: function () {
16089         this.isLoaded();
16090         return this.getData().uri;
16091     }
16092 	});
16093 
16094     window.finesse = window.finesse || {};
16095     window.finesse.restservices = window.finesse.restservices || {};
16096     window.finesse.restservices.TeamWrapUpReason = TeamWrapUpReason;
16097 
16098     return TeamWrapUpReason;
16099 });
16100 
16101 /**
16102 * JavaScript representation of the Finesse Team Wrap-Up Reasons collection
16103 * object which contains a list of Wrap-Up Reasons objects.
16104  *
16105  * @requires finesse.clientservices.ClientServices
16106  * @requires Class
16107  * @requires finesse.FinesseBase
16108  * @requires finesse.restservices.RestBase
16109  * @requires finesse.restservices.Dialog
16110  * @requires finesse.restservices.RestCollectionBase
16111  */
16112 /** @private */
16113 define('restservices/TeamWrapUpReasons',[
16114     'restservices/RestCollectionBase',
16115     'restservices/RestBase',
16116     'restservices/TeamWrapUpReason'
16117 ],
16118 function (RestCollectionBase, RestBase, TeamWrapUpReason) {
16119 
16120     var TeamWrapUpReasons = RestCollectionBase.extend({
16121 
16122     /**
16123      * @class
16124      * JavaScript representation of a TeamWrapUpReasons collection object. Also exposes
16125      * methods to operate on the object against the server.
16126      *
16127      * @param {Object} options
16128      *     An object with the following properties:<ul>
16129      *         <li><b>id:</b> The id of the object being constructed</li>
16130      *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
16131      *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
16132      *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
16133      *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
16134      *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
16135      *             <li><b>status:</b> {Number} The HTTP status code returned</li>
16136      *             <li><b>content:</b> {String} Raw string of response</li>
16137      *             <li><b>object:</b> {Object} Parsed object of response</li>
16138      *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
16139      *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
16140      *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
16141      *             </ul></li>
16142      *         </ul></li>
16143      *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
16144      * @constructs
16145      **/
16146     init: function (options) {
16147         this._super(options);
16148     },
16149 
16150     /**
16151      * @private
16152      * Gets the REST class for the current object - this is the TeamWrapUpReasons class.
16153      */
16154     getRestClass: function () {
16155         return TeamWrapUpReasons;
16156     },
16157 
16158     /**
16159      * @private
16160      * Gets the REST class for the objects that make up the collection. - this
16161      * is the TeamWrapUpReason class.
16162      */
16163     getRestItemClass: function () {
16164         return TeamWrapUpReason;
16165     },
16166 
16167     /**
16168      * @private
16169      * Gets the REST type for the current object - this is a "WrapUpReasons".
16170      */
16171     getRestType: function () {
16172         return "WrapUpReasons";
16173     },
16174 
16175     /**
16176      * @private
16177      * Gets the REST type for the objects that make up the collection - this is "WrapUpReason".
16178      */
16179     getRestItemType: function () {
16180         return "WrapUpReason";
16181     },
16182 
16183     /**
16184      * @private
16185      * Override default to indicates that the collection supports making
16186      * requests.
16187      */
16188     supportsRequests: true,
16189 
16190     /**
16191      * @private
16192      * Override default to indicate that this object doesn't support subscriptions.
16193      */
16194     supportsRestItemSubscriptions: false,
16195 
16196     /**
16197      * Retrieve the Team Wrap Up Reasons.
16198      *
16199      * @returns {finesse.restservices.TeamWrapUpReasons}
16200      *     This TeamWrapUpReasons object to allow cascading.
16201      */
16202     get: function () {
16203         // set loaded to false so it will rebuild the collection after the get
16204         this._loaded = false;
16205         // reset collection
16206         this._collection = {};
16207         // perform get
16208         this._synchronize();
16209         return this;
16210     },
16211 
16212     /**
16213      * Set up the PutSuccessHandler for TeamWrapUpReasons
16214      * @param {Object} wrapUpReasons
16215      * @param {Object} contentBody
16216      * @param successHandler
16217      * @returns response
16218      */
16219     createPutSuccessHandler: function (wrapUpReasons, contentBody, successHandler) {
16220         return function (rsp) {
16221             // Update internal structure based on response. Here we
16222             // inject the contentBody from the PUT request into the
16223             // rsp.object element to mimic a GET as a way to take
16224             // advantage of the existing _processResponse method.
16225             rsp.object = contentBody;
16226             
16227             wrapUpReasons._processResponse(rsp);
16228 
16229             //Remove the injected contentBody object before cascading response
16230             rsp.object = {};
16231 
16232             //cascade response back to consumer's response handler
16233             successHandler(rsp);
16234         };
16235     },
16236 
16237     /**    
16238      * Perform the REST API PUT call to update the reason code assignments for the team
16239      * @param {String Array} newValues
16240      * @param handlers
16241      * @returns {Object} this
16242      */
16243     update: function (newValues, handlers) {
16244         this.isLoaded();
16245         var contentBody = {}, contentBodyInner = [], i, innerObject = {};
16246 
16247         contentBody[this.getRestType()] = {
16248         };
16249 
16250         for (i in newValues) {
16251             if (newValues.hasOwnProperty(i)) {
16252                 innerObject = {
16253                     "uri": newValues[i]
16254                 };
16255                 contentBodyInner.push(innerObject);
16256             }
16257         }
16258 
16259         contentBody[this.getRestType()] = {
16260             "WrapUpReason" : contentBodyInner
16261         };
16262 
16263         // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
16264         handlers = handlers || {};
16265 
16266         this.restRequest(this.getRestUrl(), {
16267             method: 'PUT',
16268             success: this.createPutSuccessHandler(this, contentBody, handlers.success),
16269             error: handlers.error,
16270             content: contentBody
16271         });
16272 
16273         return this; // Allow cascading
16274     }
16275 	});
16276 
16277     window.finesse = window.finesse || {};
16278     window.finesse.restservices = window.finesse.restservices || {};
16279     window.finesse.restservices.TeamWrapUpReasons = TeamWrapUpReasons;
16280 
16281     return TeamWrapUpReasons;
16282 });
16283 
16284 /**
16285  * JavaScript representation of a TeamSignOutReasonCode.
16286  *
16287  * @requires finesse.clientservices.ClientServices
16288  * @requires Class
16289  * @requires finesse.FinesseBase
16290  * @requires finesse.restservices.RestBase
16291  */
16292 
16293 /** @private */
16294 define('restservices/TeamSignOutReasonCode',['restservices/RestBase'], function (RestBase) {
16295     var TeamSignOutReasonCode = RestBase.extend(/** @lends finesse.restservices.TeamSignOutReasonCode.prototype */ {
16296 
16297         /**
16298          * @class
16299          * JavaScript representation of a TeamSignOutReasonCode object. Also exposes
16300          * methods to operate on the object against the server.
16301          *
16302          * @param {Object} options
16303          *     An object with the following properties:<ul>
16304          *         <li><b>id:</b> The id of the object being constructed</li>
16305          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
16306          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
16307          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
16308          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
16309          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
16310          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
16311          *             <li><b>content:</b> {String} Raw string of response</li>
16312          *             <li><b>object:</b> {Object} Parsed object of response</li>
16313          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
16314          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
16315          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
16316          *             </ul></li>
16317          *         </ul></li>
16318          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
16319          * @augments finesse.restservices.RestBase
16320          * @constructs
16321          **/
16322         init: function (options) {
16323             this._super(options);
16324         },
16325 
16326         /**
16327          * @private
16328          * Gets the REST class for the current object - this is the TeamSignOutReasonCode class.
16329          * @returns {Object} The TeamSignOutReasonCode class.
16330          */
16331         getRestClass: function () {
16332             return TeamSignOutReasonCode;
16333         },
16334 
16335         /**
16336          * @private
16337          * Gets the REST type for the current object - this is a "ReasonCode".
16338          * @returns {String} The ReasonCode string.
16339          */
16340         getRestType: function () {
16341             return "ReasonCode";
16342         },
16343 
16344         /**
16345          * @private
16346          * Override default to indicate that this object doesn't support making
16347          * requests.
16348          */
16349         supportsRequests: false,
16350 
16351         /**
16352          * @private
16353          * Override default to indicate that this object doesn't support subscriptions.
16354          */
16355         supportsSubscriptions: false,
16356 
16357         /**
16358          * Getter for the category.
16359          * @returns {String} The category.
16360          */
16361         getCategory: function () {
16362             this.isLoaded();
16363             return this.getData().category;
16364         },
16365 
16366         /**
16367          * Getter for the code.
16368          * @returns {String} The code.
16369          */
16370         getCode: function () {
16371             this.isLoaded();
16372             return this.getData().code;
16373         },
16374 
16375         /**
16376          * Getter for the label.
16377          * @returns {String} The label.
16378          */
16379         getLabel: function () {
16380             this.isLoaded();
16381             return this.getData().label;
16382         },
16383 
16384         /**
16385          * Getter for the forAll value.
16386          * @returns {String} The forAll.
16387          */
16388         getForAll: function () {
16389             this.isLoaded();
16390             return this.getData().forAll;
16391         },
16392 
16393         /**
16394          * Getter for the Uri value.
16395          * @returns {String} The Uri.
16396          */
16397         getUri: function () {
16398             this.isLoaded();
16399             return this.getData().uri;
16400         },
16401         
16402         /**
16403          * Getter for the systemCode value.
16404          * @returns {String} The value for systemCode.
16405          * @since   11.6(1)-ES1 onwards
16406          */
16407         getSystemCode: function () {
16408             this.isLoaded();
16409             return this.getData().systemCode;
16410         }
16411 
16412     });
16413 
16414     window.finesse = window.finesse || {};
16415     window.finesse.restservices = window.finesse.restservices || {};
16416     window.finesse.restservices.TeamSignOutReasonCode = TeamSignOutReasonCode;
16417     
16418     return TeamSignOutReasonCode;
16419 });
16420 
16421 /**
16422 * JavaScript representation of the TeamSignOutReasonCodes collection
16423 * object which contains a list of TeamSignOutReasonCode objects.
16424  *
16425  * @requires finesse.clientservices.ClientServices
16426  * @requires Class
16427  * @requires finesse.FinesseBase
16428  * @requires finesse.restservices.RestBase
16429  * @requires finesse.restservices.Dialog
16430  * @requires finesse.restservices.RestCollectionBase
16431  */
16432 
16433 /** @private */
16434 define('restservices/TeamSignOutReasonCodes',[
16435     'restservices/RestCollectionBase',
16436     'restservices/RestBase',
16437     'restservices/TeamSignOutReasonCode'
16438 ],
16439 function (RestCollectionBase, RestBase, TeamSignOutReasonCode) {
16440     
16441     var TeamSignOutReasonCodes = RestCollectionBase.extend({
16442         /**
16443          * @class
16444          * JavaScript representation of a TeamSignOutReasonCodes collection object. Also exposes
16445          * methods to operate on the object against the server.
16446          *
16447          * @param {Object} options
16448          *     An object with the following properties:<ul>
16449          *         <li><b>id:</b> The id of the object being constructed</li>
16450          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
16451          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
16452          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
16453          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
16454          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
16455          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
16456          *             <li><b>content:</b> {String} Raw string of response</li>
16457          *             <li><b>object:</b> {Object} Parsed object of response</li>
16458          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
16459          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
16460          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
16461          *             </ul></li>
16462          *         </ul></li>
16463          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
16464          * @constructs
16465          **/
16466         init: function (options) {
16467             this._super(options);
16468         },
16469 
16470         /**
16471          * @private
16472          * Gets the REST class for the current object - this is the TeamSignOutReasonCodes class.
16473          */
16474         getRestClass: function () {
16475             return TeamSignOutReasonCodes;
16476         },
16477 
16478         /**
16479          * @private
16480          * Gets the REST class for the objects that make up the collection. - this
16481          * is the TeamSignOutReasonCode class.
16482          */
16483         getRestItemClass: function () {
16484             return TeamSignOutReasonCode;
16485         },
16486 
16487         /**
16488          * @private
16489          * Gets the REST type for the current object - this is a "ReasonCodes".
16490          */
16491         getRestType: function () {
16492             return "ReasonCodes";
16493         },
16494 
16495         /**
16496          * Overrides the parent class.  Returns the url for the SignOutReasonCodes resource
16497          */
16498         getRestUrl: function () {
16499             var restObj = this._restObj, restUrl = "";
16500 
16501             //Prepend the base REST object if one was provided.
16502             //Otherwise prepend with the default webapp name.
16503             if (restObj instanceof RestBase) {
16504                 restUrl += restObj.getRestUrl();
16505             } else {
16506                 restUrl += "/finesse/api";
16507             }
16508             //Append the REST type.
16509             restUrl += "/ReasonCodes?category=LOGOUT";
16510             //Append ID if it is not undefined, null, or empty.
16511             if (this._id) {
16512                 restUrl += "/" + this._id;
16513             }
16514             return restUrl;
16515         },
16516 
16517         /**
16518          * @private
16519          * Gets the REST type for the objects that make up the collection - this is "ReasonCode".
16520          */
16521         getRestItemType: function () {
16522             return "ReasonCode";
16523         },
16524 
16525         /**
16526          * @private
16527          * Override default to indicates that the collection supports making requests.
16528          */
16529         supportsRequests: true,
16530 
16531         /**
16532          * @private
16533          * Override default to indicates that the collection does not subscribe to its objects.
16534          */
16535         supportsRestItemSubscriptions: false,
16536 
16537         /**
16538          * Retrieve the Sign Out Reason Codes.
16539          *
16540          * @returns {finesse.restservices.TeamSignOutReasonCodes}
16541          *     This TeamSignOutReasonCodes object to allow cascading.
16542          */
16543         get: function () {
16544             // set loaded to false so it will rebuild the collection after the get
16545             this._loaded = false;
16546             // reset collection
16547             this._collection = {};
16548             // perform get
16549             this._synchronize();
16550             return this;
16551         },
16552 
16553         /* We only use PUT and GET on Reason Code team assignments
16554          * @param {Object} contact
16555          * @param {Object} contentBody
16556          * @param {Function} successHandler
16557          */
16558         createPutSuccessHandler: function (contact, contentBody, successHandler) {
16559             return function (rsp) {
16560                 // Update internal structure based on response. Here we
16561                 // inject the contentBody from the PUT request into the
16562                 // rsp.object element to mimic a GET as a way to take
16563                 // advantage of the existing _processResponse method.
16564                 rsp.object = contentBody;
16565                 contact._processResponse(rsp);
16566 
16567                 //Remove the injected contentBody object before cascading response
16568                 rsp.object = {};
16569 
16570                 //cascade response back to consumer's response handler
16571                 successHandler(rsp);
16572             };
16573         },
16574 
16575         /**
16576          * Update - This should be all that is needed.
16577          * @param {Object} newValues
16578          * @param {Object} handlers
16579          * @returns {finesse.restservices.TeamSignOutReasonCodes}
16580          *     This TeamSignOutReasonCodes object to allow cascading.
16581          */
16582         update: function (newValues, handlers) {
16583             this.isLoaded();
16584             var contentBody = {}, contentBodyInner = [], i, innerObject = {};
16585 
16586             contentBody[this.getRestType()] = {
16587             };
16588 
16589             for (i in newValues) {
16590                 if (newValues.hasOwnProperty(i)) {
16591                     innerObject = {
16592                         "uri": newValues[i]
16593                     };
16594                     contentBodyInner.push(innerObject);
16595                 }
16596             }
16597 
16598             contentBody[this.getRestType()] = {
16599                 "ReasonCode" : contentBodyInner
16600             };
16601 
16602             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
16603             handlers = handlers || {};
16604 
16605             this.restRequest(this.getRestUrl(), {
16606                 method: 'PUT',
16607                 success: this.createPutSuccessHandler(this, contentBody, handlers.success),
16608                 error: handlers.error,
16609                 content: contentBody
16610             });
16611 
16612             return this; // Allow cascading
16613         }
16614 
16615     });
16616     
16617     window.finesse = window.finesse || {};
16618     window.finesse.restservices = window.finesse.restservices || {};
16619     window.finesse.restservices.TeamSignOutReasonCodes = TeamSignOutReasonCodes;
16620     
16621     return TeamSignOutReasonCodes;
16622 });
16623 
16624 /**
16625  * JavaScript representation of the Finesse PhoneBook Assignment object.
16626  *
16627  * @requires finesse.clientservices.ClientServices
16628  * @requires Class
16629  * @requires finesse.FinesseBase
16630  * @requires finesse.restservices.RestBase
16631  */
16632 
16633 /**
16634  * The following comment prevents JSLint errors concerning undefined global variables.
16635  * It tells JSLint that these identifiers are defined elsewhere.
16636  */
16637 /*jslint bitwise:true, browser:true, nomen:true, regexp:true, sloppy:true, white:true */
16638 
16639 /** The following comment is to prevent jslint errors about 
16640  * using variables before they are defined.
16641  */
16642 /*global $, jQuery, Handlebars, dojox, dojo, finesse */
16643 
16644 /** @private */
16645 define('restservices/TeamPhoneBook',['restservices/RestBase'], function (RestBase) {
16646     var TeamPhoneBook = RestBase.extend({
16647 
16648         /**
16649          * @class
16650          * JavaScript representation of a PhoneBook object. Also exposes
16651          * methods to operate on the object against the server.
16652          *
16653          * @param {Object} options
16654          *     An object with the following properties:<ul>
16655          *         <li><b>id:</b> The id of the object being constructed</li>
16656          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
16657          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
16658          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
16659          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
16660          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
16661          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
16662          *             <li><b>content:</b> {String} Raw string of response</li>
16663          *             <li><b>object:</b> {Object} Parsed object of response</li>
16664          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
16665          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
16666          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
16667          *             </ul></li>
16668          *         </ul></li>
16669          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
16670          * @constructs
16671          **/
16672         init: function (options) {
16673             this._super(options);
16674         },
16675 
16676         /**
16677          * @private
16678          * Gets the REST class for the current object - this is the PhoneBooks class.
16679          * @returns {Object} The PhoneBooks class.
16680          */
16681         getRestClass: function () {
16682             return TeamPhoneBook;
16683         },
16684 
16685         /**
16686          * @private
16687          * Gets the REST type for the current object - this is a "PhoneBook".
16688          * @returns {String} The PhoneBook string.
16689          */
16690         getRestType: function () {
16691             return "PhoneBook";
16692         },
16693 
16694         /**
16695          * @private
16696          * Override default to indicate that this object doesn't support making
16697          * requests.
16698          */
16699         supportsRequests: false,
16700 
16701         /**
16702          * @private
16703          * Override default to indicate that this object doesn't support subscriptions.
16704          */
16705         supportsSubscriptions: false,
16706 
16707         /**
16708          * Getter for the name.
16709          * @returns {String} The name.
16710          */
16711         getName: function () {
16712             this.isLoaded();
16713             return this.getData().name;
16714         },
16715 
16716         /**
16717          * Getter for the Uri value.
16718          * @returns {String} The Uri.
16719          */
16720         getUri: function () {
16721             this.isLoaded();
16722             return this.getData().uri;
16723         }
16724 
16725     });
16726 
16727     window.finesse = window.finesse || {};
16728     window.finesse.restservices = window.finesse.restservices || {};
16729     window.finesse.restservices.TeamPhoneBook = TeamPhoneBook;
16730     
16731     return TeamPhoneBook;
16732 });
16733 
16734 /**
16735 * JavaScript representation of the Finesse PhoneBook Assignments collection
16736 * object which contains a list of Not Ready Reason Codes objects.
16737  *
16738  * @requires finesse.clientservices.ClientServices
16739  * @requires Class
16740  * @requires finesse.FinesseBase
16741  * @requires finesse.restservices.RestBase
16742  * @requires finesse.restservices.Dialog
16743  * @requires finesse.restservices.RestCollectionBase
16744  */
16745 
16746 /**
16747  * The following comment prevents JSLint errors concerning undefined global variables.
16748  * It tells JSLint that these identifiers are defined elsewhere.
16749  */
16750 /*jslint bitwise:true, browser:true, nomen:true, regexp:true, sloppy:true, white:true */
16751 
16752 /** The following comment is to prevent jslint errors about 
16753  * using variables before they are defined.
16754  */
16755 /*global $, jQuery, Handlebars, dojox, dojo, finesse */
16756 
16757 /** @private */
16758 define('restservices/TeamPhoneBooks',[
16759     'restservices/RestCollectionBase',
16760     'restservices/RestBase',
16761     'restservices/TeamPhoneBook'
16762 ],
16763 function (RestCollectionBase, RestBase, TeamPhoneBook) {
16764     var TeamPhoneBooks = RestCollectionBase.extend({
16765         
16766         /**
16767          * @class
16768          * JavaScript representation of a TeamPhoneBooks collection object. Also exposes
16769          * methods to operate on the object against the server.
16770          *
16771          * @param {Object} options
16772          *     An object with the following properties:<ul>
16773          *         <li><b>id:</b> The id of the object being constructed</li>
16774          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
16775          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
16776          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
16777          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
16778          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
16779          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
16780          *             <li><b>content:</b> {String} Raw string of response</li>
16781          *             <li><b>object:</b> {Object} Parsed object of response</li>
16782          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
16783          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
16784          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
16785          *             </ul></li>
16786          *         </ul></li>
16787          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
16788          * @constructs
16789          **/
16790         init: function (options) {
16791             this._super(options);           
16792         },
16793 
16794         /**
16795          * @private
16796          * Gets the REST class for the current object - this is the TeamPhoneBooks class.
16797          */
16798         getRestClass: function () {
16799             return TeamPhoneBooks;
16800         },
16801 
16802         /**
16803          * @private
16804          * Gets the REST class for the objects that make up the collection. - this
16805          * is the TeamPhoneBooks class.
16806          */
16807         getRestItemClass: function () {
16808             return TeamPhoneBook;
16809         },
16810 
16811         /**
16812          * @private
16813          * Gets the REST type for the current object - this is a "ReasonCodes".
16814          */
16815         getRestType: function () {
16816             return "PhoneBooks";
16817         },
16818         
16819         /**
16820          * Overrides the parent class.  Returns the url for the PhoneBooks resource
16821          */
16822         getRestUrl: function () {
16823             // return ("/finesse/api/" + this.getRestType() + "?category=NOT_READY");
16824             var restObj = this._restObj,
16825             restUrl = "";
16826             //Prepend the base REST object if one was provided.
16827             if (restObj instanceof RestBase) {
16828                 restUrl += restObj.getRestUrl();
16829             }
16830             //Otherwise prepend with the default webapp name.
16831             else {
16832                 restUrl += "/finesse/api";
16833             }
16834             //Append the REST type.
16835             restUrl += "/PhoneBooks";
16836             //Append ID if it is not undefined, null, or empty.
16837             if (this._id) {
16838                 restUrl += "/" + this._id;
16839             }
16840             return restUrl;        
16841         },
16842         
16843         /**
16844          * @private
16845          * Gets the REST type for the objects that make up the collection - this is "ReasonCode".
16846          */
16847         getRestItemType: function () {
16848             return "PhoneBook";
16849         },
16850 
16851         /**
16852          * @private
16853          * Override default to indicates that the collection supports making
16854          * requests.
16855          */
16856         supportsRequests: true,
16857 
16858         /**
16859          * @private
16860          * Override default to indicates that the collection subscribes to its objects.
16861          */
16862         supportsRestItemSubscriptions: false,
16863         
16864         /**
16865          * Retrieve the Not Ready Reason Codes.
16866          *
16867          * @returns {finesse.restservices.TeamPhoneBooks}
16868          *     This TeamPhoneBooks object to allow cascading.
16869          */
16870         get: function () {
16871             // set loaded to false so it will rebuild the collection after the get
16872             /** @private */
16873             this._loaded = false;
16874             // reset collection
16875             /** @private */
16876             this._collection = {};
16877             // perform get
16878             this._synchronize();
16879             return this;
16880         },
16881 
16882         /* We only use PUT and GET on Reason Code team assignments 
16883          */
16884         createPutSuccessHandler: function(contact, contentBody, successHandler){
16885             return function (rsp) {
16886                 // Update internal structure based on response. Here we
16887                 // inject the contentBody from the PUT request into the
16888                 // rsp.object element to mimic a GET as a way to take
16889                 // advantage of the existing _processResponse method.
16890                 rsp.object = contentBody;
16891                 contact._processResponse(rsp);
16892 
16893                 //Remove the injected Contact object before cascading response
16894                 rsp.object = {};
16895                 
16896                 //cascade response back to consumer's response handler
16897                 successHandler(rsp);
16898             };
16899         },
16900 
16901         /**
16902          * Update - This should be all that is needed.
16903          */
16904         update: function (newValues, handlers) {
16905             this.isLoaded();
16906             var contentBody = {}, contentBodyInner = [], i, innerObject;
16907 
16908             contentBody[this.getRestType()] = {
16909             };
16910         
16911             for (i in newValues) {
16912                 if (newValues.hasOwnProperty(i)) {
16913                     innerObject = {};
16914                     innerObject = {
16915                         "uri": newValues[i]
16916                     };
16917                     contentBodyInner.push(innerObject);
16918                 }
16919             }
16920 
16921             contentBody[this.getRestType()] = {
16922                 "PhoneBook" : contentBodyInner
16923             };
16924 
16925             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
16926             handlers = handlers || {};
16927 
16928             this.restRequest(this.getRestUrl(), {
16929                 method: 'PUT',
16930                 success: this.createPutSuccessHandler(this, contentBody, handlers.success),
16931                 error: handlers.error,
16932                 content: contentBody
16933             });
16934 
16935             return this; // Allow cascading
16936         }       
16937         
16938     });
16939         
16940     window.finesse = window.finesse || {};
16941     window.finesse.restservices = window.finesse.restservices || {};
16942     window.finesse.restservices.TeamPhoneBooks = TeamPhoneBooks;
16943     
16944     return TeamPhoneBooks;
16945 });
16946 
16947 /**
16948  * JavaScript representation of the Finesse LayoutConfig object
16949  * @requires ClientServices
16950  * @requires finesse.FinesseBase
16951  * @requires finesse.restservices.RestBase
16952  */
16953 
16954 /** @private */
16955 define('restservices/LayoutConfig',['restservices/RestBase'], function (RestBase) {
16956     /** @private */
16957 	var LayoutConfig = RestBase.extend({
16958 
16959 		/**
16960 		 * @class
16961 		 * JavaScript representation of a LayoutConfig object. Also exposes methods to operate
16962 		 * on the object against the server.
16963 		 *
16964 		 * @param {String} id
16965 		 *     Not required...
16966 		 * @param {Object} callbacks
16967 		 *     An object containing callbacks for instantiation and runtime
16968 		 * @param {Function} callbacks.onLoad(this)
16969 		 *     Callback to invoke upon successful instantiation
16970 		 * @param {Function} callbacks.onLoadError(rsp)
16971 		 *     Callback to invoke on instantiation REST request error
16972 		 *     as passed by finesse.clientservices.ClientServices.ajax()
16973 		 *     {
16974 		 *         status: {Number} The HTTP status code returned
16975 		 *         content: {String} Raw string of response
16976 		 *         object: {Object} Parsed object of response
16977 		 *         error: {Object} Wrapped exception that was caught
16978 		 *         error.errorType: {String} Type of error that was caught
16979 		 *         error.errorMessage: {String} Message associated with error
16980 		 *     }
16981 		 * @param {Function} callbacks.onChange(this)
16982 		 *     Callback to invoke upon successful update
16983 		 * @param {Function} callbacks.onError(rsp)
16984 		 *     Callback to invoke on update error (refresh or event)
16985 		 *     as passed by finesse.clientservices.ClientServices.ajax()
16986 		 *     {
16987 		 *         status: {Number} The HTTP status code returned
16988 		 *         content: {String} Raw string of response
16989 		 *         object: {Object} Parsed object of response
16990 		 *         error: {Object} Wrapped exception that was caught
16991 		 *         error.errorType: {String} Type of error that was caught
16992 		 *         error.errorMessage: {String} Message associated with error
16993 		 *     }
16994 		 *  
16995 	     * @constructs
16996 		 */
16997 		init: function (callbacks) {
16998 			this._super("", callbacks);
16999 			//when post is performed and id is empty
17000 			/*if (id === "") {
17001 				this._loaded = true;
17002 			}*/
17003 	        this._layoutxml = {};
17004 		},
17005 	
17006 		/**
17007 		 * Returns REST class of LayoutConfig object
17008 		 */
17009 		getRestClass: function () {
17010 			return LayoutConfig;
17011 		},
17012 	
17013 		/**
17014 		 * The type of this REST object is LayoutConfig
17015 		 */
17016 		getRestType: function () {
17017 			return "LayoutConfig";
17018 		},
17019 
17020 		/**
17021 		 * Gets the REST URL of this object.
17022 		 * 
17023 		 * If the parent has an id, the id is appended.
17024 		 * On occasions of POST, it will not have an id.
17025 		 */
17026 		getRestUrl: function () {
17027 			var layoutUri = "/finesse/api/" + this.getRestType() + "/default";
17028 			/*if (this._id) {
17029 				layoutUri = layoutUri + "/" + this._id;
17030 			}*/
17031 			return layoutUri;
17032 		},
17033 	
17034 		/**
17035 		 * This API does not support subscription
17036 		 */
17037 		supportsSubscriptions: false,
17038 		
17039 		keepRestResponse: true,
17040 
17041 
17042 		/**
17043 		 * Gets finesselayout.xml retrieved from the API call
17044 		 */
17045 		getLayoutxml: function () {
17046 			this.isLoaded();
17047 			var layoutxml = this.getData().layoutxml;
17048 
17049             // We need to unescape everything that is unallowed in xml so consumers don't have to deal with it (used in tandem with update())
17050             layoutxml = layoutxml.replace(/&/g,"&");
17051 
17052             return layoutxml;
17053 		},
17054 	
17055 		/**
17056 		 * Gets the type of this LayoutConfig object
17057 		 */
17058 		/*
17059 		getType: function () {
17060 			this.isLoaded();
17061 			return this.getData().type;
17062 		},*/
17063 	
17064 		/**
17065 		 * Retrieve the LayoutConfig settings.
17066 		 * If the id is not provided the API call will fail.
17067 		 * @returns {LayoutConfig}
17068 		 *     This LayoutConfig object to allow cascading.
17069 		 */
17070 		get: function () {      
17071 			this._synchronize();
17072 			return this;
17073 		},
17074 
17075 		/**
17076 		 * Closure handle updating of the internal data for the LayoutConfig object
17077 		 * upon a successful update (PUT) request before calling the intended
17078 		 * success handler provided by the consumer
17079 		 * 
17080 		 * @param {Object}
17081 		 *            layoutconfig Reference to this LayoutConfig object
17082 		 * @param {Object}
17083 		 *            LayoutConfig Object that contains the  settings to be
17084 		 *            submitted in the api request
17085 		 * @param {Function}
17086 		 *            successHandler The success handler specified by the consumer
17087 		 *            of this object
17088 		 * @returns {LayoutConfig} This LayoutConfig object to allow cascading
17089 		 */
17090 	
17091 		createPutSuccessHandler: function (layoutconfig, contentBody, successHandler) {
17092 			return function (rsp) {			
17093 				// Update internal structure based on response. Here we
17094 				// inject the contentBody from the PUT request into the
17095 				// rsp.object element to mimic a GET as a way to take
17096 				// advantage of the existing _processResponse method.
17097 				rsp.content = contentBody;
17098 				rsp.object.LayoutConfig = {};
17099 				rsp.object.LayoutConfig.finesseLayout = contentBody;
17100 				layoutconfig._processResponse(rsp);
17101 	
17102 				//Remove the injected layoutConfig object before cascading response
17103 				rsp.object.LayoutConfig = {};
17104 	
17105 				//cascade response back to consumer's response handler
17106 				successHandler(rsp);
17107 			};
17108 		},
17109 	
17110 		/**
17111 		 *  Update LayoutConfig
17112 		 * @param {Object} finesselayout
17113 		 *     The XML for FinesseLayout being stored
17114 		 * 
17115 		 * @param {Object} handlers
17116 		 *     An object containing callback handlers for the request. Optional.
17117 		 * @param {Function} options.success(rsp)
17118 		 *     A callback function to be invoked for a successful request.
17119 		 *     {
17120 		 *         status: {Number} The HTTP status code returned
17121 		 *         content: {String} Raw string of response
17122 		 *         object: {Object} Parsed object of response
17123 		 *     }
17124 		 * @param {Function} options.error(rsp)
17125 		 *     A callback function to be invoked for an unsuccessful request.
17126 		 *     {
17127 		 *         status: {Number} The HTTP status code returned
17128 		 *         content: {String} Raw string of response
17129 		 *         object: {Object} Parsed object of response (HTTP errors)
17130 		 *         error: {Object} Wrapped exception that was caught
17131 		 *         error.errorType: {String} Type of error that was caught
17132 		 *         error.errorMessage: {String} Message associated with error
17133 		 *     }
17134 		 * @returns {finesse.restservices.LayoutConfig}
17135 		 *     This LayoutConfig object to allow cascading
17136 		 */
17137 	
17138 		update: function (layoutxml, handlers) {
17139 			this.isLoaded();
17140 
17141 			
17142 			var contentBody = {}, 
17143 			//Created a regex (re) to scoop out just the gadget URL (without before and after whitespace characters)
17144 			re = /<gadget>\s*(\S+)\s*<\/gadget>/g;
17145 
17146 			// We need to escape everything that is unallowed in xml so consumers don't have to deal with it (used in tandem with getLayoutxml())
17147 			layoutxml = layoutxml.replace(/&(?!amp;)/g, "&");
17148 
17149 			//used the regex (re) to the update and save the layoutxml with the improved gadget URL (without before/after whitespace)
17150 			layoutxml = layoutxml.replace(re, "<gadget>$1</gadget>");
17151 
17152 			contentBody[this.getRestType()] = {
17153 				"layoutxml": finesse.utilities.Utilities.translateHTMLEntities(layoutxml, true)
17154 			};
17155 
17156 			// Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
17157 			handlers = handlers || {};
17158 
17159 			this.restRequest(this.getRestUrl(), {
17160 				method: 'PUT',
17161 				success: this.createPutSuccessHandler(this, layoutxml, handlers.success),
17162 				error: handlers.error,
17163 				content: contentBody
17164 			});
17165 
17166 			return this; // Allow cascading
17167 		}
17168 	
17169 		/**
17170 		 *TODO createPostSuccessHandler needs to be debugged to make it working
17171 		 * Closure handle creating new  LayoutConfig object
17172 		 * upon a successful create (POST) request before calling the intended
17173 		 * success handler provided by the consumer
17174 		 * 
17175 		 * @param {Object}
17176 		 *            layoutconfig Reference to this LayoutConfig object
17177 		 * @param {Object}
17178 		 *            LayoutConfig Object that contains the  settings to be
17179 		 *            submitted in the api request
17180 		 * @param {Function}
17181 		 *            successHandler The success handler specified by the consumer
17182 		 *            of this object
17183 		 * @returns {finesse.restservices.LayoutConfig} This LayoutConfig object to allow cascading
17184 		 */
17185 	/*
17186 		createPostSuccessHandler: function (layoutconfig, contentBody, successHandler) {
17187 			return function (rsp) {
17188 	
17189 				rsp.object = contentBody;
17190 				layoutconfig._processResponse(rsp);
17191 	
17192 				//Remove the injected layoutConfig object before cascading response
17193 				rsp.object = {};
17194 	
17195 				//cascade response back to consumer's response handler
17196 				successHandler(rsp);
17197 			};
17198 		}, */
17199 	
17200 		/**
17201 		 * TODO Method needs to be debugged to make POST working
17202 		 *  Add LayoutConfig
17203 		 * @param {Object} finesselayout
17204 		 *     The XML for FinesseLayout being stored
17205 		 * 
17206 		 * @param {Object} handlers
17207 		 *     An object containing callback handlers for the request. Optional.
17208 		 * @param {Function} options.success(rsp)
17209 		 *     A callback function to be invoked for a successful request.
17210 		 *     {
17211 		 *         status: {Number} The HTTP status code returned
17212 		 *         content: {String} Raw string of response
17213 		 *         object: {Object} Parsed object of response
17214 		 *     }
17215 		 * @param {Function} options.error(rsp)
17216 		 *     A callback function to be invoked for an unsuccessful request.
17217 		 *     {
17218 		 *         status: {Number} The HTTP status code returned
17219 		 *         content: {String} Raw string of response
17220 		 *         object: {Object} Parsed object of response (HTTP errors)
17221 		 *         error: {Object} Wrapped exception that was caught
17222 		 *         error.errorType: {String} Type of error that was caught
17223 		 *         error.errorMessage: {String} Message associated with error
17224 		 *     }
17225 		 * @returns {finesse.restservices.LayoutConfig}
17226 		 *     This LayoutConfig object to allow cascading
17227 		 */
17228 	/*
17229 		add: function (layoutxml, handlers) {
17230 			this.isLoaded();
17231 			var contentBody = {};
17232 	
17233 	
17234 			contentBody[this.getRestType()] = {
17235 					"layoutxml": layoutxml,
17236 					"type": "current"
17237 			    };
17238 	
17239 			// Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
17240 			handlers = handlers || {};
17241 	
17242 			this.restRequest(this.getRestUrl(), {
17243 				method: 'POST',
17244 				success: this.createPostSuccessHandler(this, contentBody, handlers.success),
17245 				error: handlers.error,
17246 				content: contentBody
17247 			});
17248 	
17249 			return this; // Allow cascading
17250 		} */
17251 	});
17252 	
17253 	window.finesse = window.finesse || {};
17254     window.finesse.restservices = window.finesse.restservices || {};
17255     window.finesse.restservices.LayoutConfig = LayoutConfig;
17256     
17257 	return LayoutConfig;
17258 	
17259 });
17260 
17261 /**
17262  * JavaScript representation of the Finesse LayoutConfig object for a Team.
17263  *
17264  * @requires finesse.clientservices.ClientServices
17265  * @requires Class
17266  * @requires finesse.FinesseBase
17267  * @requires finesse.restservices.RestBase
17268  * @requires finesse.utilities.Utilities
17269  * @requires finesse.restservices.LayoutConfig
17270  */
17271 
17272 /** The following comment is to prevent jslint errors about 
17273  * using variables before they are defined.
17274  */
17275 /*global Exception */
17276 
17277 /** @private */
17278 define('restservices/TeamLayoutConfig',[
17279     'restservices/RestBase',
17280     'utilities/Utilities',
17281     'restservices/LayoutConfig'
17282 ],
17283 function (RestBase, Utilities, LayoutConfig) {
17284     
17285     var TeamLayoutConfig = RestBase.extend({
17286       // Keep the restresponse so we can parse the layoutxml out of it in getLayoutXML()
17287       keepRestResponse: true,
17288     
17289       /**
17290        * @class
17291        * JavaScript representation of a LayoutConfig object for a Team. Also exposes
17292        * methods to operate on the object against the server.
17293        *
17294        * @param {Object} options
17295        *     An object with the following properties:<ul>
17296        *         <li><b>id:</b> The id of the object being constructed</li>
17297        *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
17298        *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
17299        *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
17300        *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
17301        *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
17302        *             <li><b>status:</b> {Number} The HTTP status code returned</li>
17303        *             <li><b>content:</b> {String} Raw string of response</li>
17304        *             <li><b>object:</b> {Object} Parsed object of response</li>
17305        *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
17306        *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
17307        *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
17308        *             </ul></li>
17309        *         </ul></li>
17310        *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
17311        * @constructs
17312        **/
17313       init: function (options) {
17314           this._super(options);
17315       },
17316     
17317       /**
17318        * @private
17319        * Gets the REST class for the current object - this is the LayoutConfigs class.
17320        * @returns {Object} The LayoutConfigs class.
17321        */
17322       getRestClass: function () {
17323           return TeamLayoutConfig;
17324       },
17325     
17326       /**
17327        * @private
17328        * Gets the REST type for the current object - this is a "LayoutConfig".
17329        * @returns {String} The LayoutConfig string.
17330        */
17331       getRestType: function () {
17332           return "TeamLayoutConfig";
17333       },
17334     
17335       /**
17336        * @private
17337        * Override default to indicate that this object doesn't support making
17338        * requests.
17339        */
17340       supportsRequests: false,
17341     
17342       /**
17343        * @private
17344        * Override default to indicate that this object doesn't support subscriptions.
17345        */
17346       supportsSubscriptions: false,
17347     
17348       /**
17349        * Getter for the category.
17350        * @returns {String} The category.
17351        */
17352       getLayoutXML: function () {
17353           this.isLoaded();
17354           var layoutxml = this.getData().layoutxml;
17355 
17356           // We need to unescape everything that is unallowed in xml so consumers don't have to deal with it (used in tandem with put())
17357           layoutxml = layoutxml.replace(/&/g,"&");
17358 
17359           return layoutxml;
17360       },
17361     
17362       /**
17363        * Getter for the code.
17364        * @returns {String} The code.
17365        */
17366       getUseDefault: function () {
17367           this.isLoaded();
17368           return this.getData().useDefault;
17369       },
17370       
17371       /**
17372        * Retrieve the TeamLayoutConfig.
17373        *
17374        * @returns {finesse.restservices.TeamLayoutConfig}
17375        */
17376       get: function () {
17377           // this._id is needed, but is not used in this object.. we're overriding getRestUrl anyway
17378           this._id = "0";
17379           // set loaded to false so it will rebuild the collection after the get
17380           this._loaded = false;
17381           // reset collection
17382           this._collection = {};
17383           // perform get
17384           this._synchronize();
17385           return this;
17386       },
17387     
17388       createPutSuccessHandler: function(contact, contentBody, successHandler){
17389           return function (rsp) {
17390               // Update internal structure based on response. Here we
17391               // inject the contentBody from the PUT request into the
17392               // rsp.object element to mimic a GET as a way to take
17393               // advantage of the existing _processResponse method.
17394               rsp.object = contentBody;
17395               contact._processResponse(rsp);
17396     
17397               //Remove the injected Contact object before cascading response
17398               rsp.object = {};
17399               
17400               //cascade response back to consumer's response handler
17401               successHandler(rsp);
17402           };
17403       },
17404       
17405       put: function (newValues, handlers) {
17406           // this._id is needed, but is not used in this object.. we're overriding getRestUrl anyway
17407           this._id = "0";
17408           this.isLoaded();
17409 
17410           // We need to escape everything that is unallowed in xml so consumers don't have to deal with it (used in tandem with getLayoutxml())
17411           var layoutxml = newValues.layoutXML.replace(/&(?!amp;)/g, "&"),
17412               contentBody = {}, 
17413               //Created a regex (re) to scoop out just the gadget URL (without before and after whitespace characters)
17414               re = /<gadget>\s*(\S+)\s*<\/gadget>/g;
17415 
17416           //used the regex (re) to the update and save the layoutxml with the improved gadget URL (without before/after whitespace)
17417           layoutxml = layoutxml.replace(re, "<gadget>$1</gadget>");
17418           
17419           contentBody[this.getRestType()] = {
17420               "useDefault": newValues.useDefault,
17421               // The LayoutConfig restservice javascript class only translates ampersands, so we'll do that also
17422               "layoutxml": finesse.utilities.Utilities.translateHTMLEntities(layoutxml, true)
17423           };
17424     
17425           // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
17426           handlers = handlers || {};
17427     
17428           this.restRequest(this.getRestUrl(), {
17429               method: 'PUT',
17430               success: this.createPutSuccessHandler(this, contentBody, handlers.success),
17431               error: handlers.error,
17432               content: contentBody
17433           });
17434     
17435           return this; // Allow cascading
17436       },
17437     
17438       getRestUrl: function(){
17439           // return team's url + /LayoutConfig
17440           // eg: /api/Team/1/LayoutConfig
17441           if(this._restObj === undefined){
17442               throw new Exception("TeamLayoutConfig instances must have a parent team object.");
17443           }
17444           return this._restObj.getRestUrl() + '/LayoutConfig';
17445       }
17446     
17447       });
17448         
17449     window.finesse = window.finesse || {};
17450     window.finesse.restservices = window.finesse.restservices || {};
17451     window.finesse.restservices.TeamLayoutConfig = TeamLayoutConfig;
17452       
17453     return TeamLayoutConfig;
17454 });
17455 
17456 /**
17457  * JavaScript representation of a TeamWorkflow.
17458  *
17459  * @requires finesse.clientservices.ClientServices
17460  * @requires Class
17461  * @requires finesse.FinesseBase
17462  * @requires finesse.restservices.RestBase
17463  */
17464 /** @private */
17465 define('restservices/TeamWorkflow',['restservices/RestBase'], function (RestBase) {
17466 
17467     var TeamWorkflow = RestBase.extend({
17468 
17469         /**
17470          * @class
17471          * JavaScript representation of a TeamWorkflow object. Also exposes
17472          * methods to operate on the object against the server.
17473          *
17474          * @param {Object} options
17475          *     An object with the following properties:<ul>
17476          *         <li><b>id:</b> The id of the object being constructed</li>
17477          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
17478          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
17479          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
17480          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
17481          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
17482          *             <li><b>status:</b> {Number} The HTTP status description returned</li>
17483          *             <li><b>content:</b> {String} Raw string of response</li>
17484          *             <li><b>object:</b> {Object} Parsed object of response</li>
17485          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
17486          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
17487          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
17488          *             </ul></li>
17489          *         </ul></li>
17490          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
17491          * @constructs
17492          **/
17493         init: function (options) {
17494             this._super(options);
17495         },
17496 
17497         /**
17498          * @private
17499          * Gets the REST class for the current object - this is the TeamWorkflow class.
17500          * @returns {Object} The TeamWorkflow class.
17501          */
17502         getRestClass: function () {
17503             return TeamWorkflow;
17504         },
17505 
17506         /**
17507          * @private
17508          * Gets the REST type for the current object - this is a "Workflow".
17509          * @returns {String} The Workflow string.
17510          */
17511         getRestType: function () {
17512             return "Workflow";
17513         },
17514 
17515         /**
17516          * @private
17517          * Override default to indicate that this object doesn't support making
17518          * requests.
17519          */
17520         supportsRequests: false,
17521 
17522         /**
17523          * @private
17524          * Override default to indicate that this object doesn't support subscriptions.
17525          */
17526         supportsSubscriptions: false,
17527 
17528         /**
17529          * Getter for the name.
17530          * @returns {String} The name.
17531          */
17532         getName: function () {
17533             this.isLoaded();
17534             return this.getData().name;
17535         },
17536 
17537         /**
17538          * Getter for the description.
17539          * @returns {String} The description.
17540          */
17541         getDescription: function () {
17542             this.isLoaded();
17543             return this.getData().description;
17544         },
17545 
17546         /**
17547          * Getter for the Uri value.
17548          * @returns {String} The Uri.
17549          */
17550         getUri: function () {
17551             this.isLoaded();
17552             return this.getData().uri;
17553         }
17554 
17555     });
17556     
17557 	window.finesse = window.finesse || {};
17558     window.finesse.restservices = window.finesse.restservices || {};
17559     window.finesse.restservices.TeamWorkflow = TeamWorkflow;
17560 
17561     return TeamWorkflow;
17562 });
17563 
17564 /**
17565 * JavaScript representation of the TeamWorkflows collection
17566 * object which contains a list of TeamWorkflow objects.
17567  *
17568  * @requires finesse.clientservices.ClientServices
17569  * @requires Class
17570  * @requires finesse.FinesseBase
17571  * @requires finesse.restservices.RestBase
17572  * @requires finesse.restservices.Dialog
17573  * @requires finesse.restservices.RestCollectionBase
17574  */
17575 /** @private */
17576 define('restservices/TeamWorkflows',[
17577     'restservices/RestCollectionBase',
17578     'restservices/TeamWorkflow',
17579     'restservices/RestBase'
17580 ],
17581 function (RestCollectionBase, TeamWorkflow, RestBase) {
17582 
17583     var TeamWorkflows = RestCollectionBase.extend({
17584     
17585         /**
17586          * @class
17587          * JavaScript representation of a TeamWorkflows collection object. Also exposes
17588          * methods to operate on the object against the server.
17589          *
17590          * @param {Object} options
17591          *     An object with the following properties:<ul>
17592          *         <li><b>id:</b> The id of the object being constructed</li>
17593          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
17594          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
17595          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
17596          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
17597          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
17598          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
17599          *             <li><b>content:</b> {String} Raw string of response</li>
17600          *             <li><b>object:</b> {Object} Parsed object of response</li>
17601          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
17602          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
17603          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
17604          *             </ul></li>
17605          *         </ul></li>
17606          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
17607          * @constructs
17608          **/
17609         init: function (options) {
17610             this._super(options);
17611         },
17612 
17613         /**
17614          * @private
17615          * Gets the REST class for the current object - this is the TeamWorkflows class.
17616          */
17617         getRestClass: function () {
17618             return TeamWorkflows;
17619         },
17620 
17621         /**
17622          * @private
17623          * Gets the REST class for the objects that make up the collection. - this
17624          * is the TeamWorkflow class.
17625          */
17626         getRestItemClass: function () {
17627             return TeamWorkflow;
17628         },
17629 
17630         /**
17631          * @private
17632          * Gets the REST type for the current object - this is a "Workflows".
17633          */
17634         getRestType: function () {
17635             return "Workflows";
17636         },
17637 
17638         /**
17639          * Overrides the parent class.  Returns the url for the Workflows resource
17640          */
17641         getRestUrl: function () {
17642             var restObj = this._restObj, restUrl = "";
17643 
17644             //Prepend the base REST object if one was provided.
17645             //Otherwise prepend with the default webapp name.
17646             if (restObj instanceof RestBase) {
17647                 restUrl += restObj.getRestUrl();
17648             } else {
17649                 restUrl += "/finesse/api/Team";
17650             }
17651             //Append ID if it is not undefined, null, or empty.
17652             if (this._id) {
17653                 restUrl += "/" + this._id;
17654             }
17655             //Append the REST type.
17656             restUrl += "/Workflows";
17657             
17658             return restUrl;
17659         },
17660 
17661         /**
17662          * @private
17663          * Gets the REST type for the objects that make up the collection - this is "Workflow".
17664          */
17665         getRestItemType: function () {
17666             return "Workflow";
17667         },
17668 
17669         /**
17670          * @private
17671          * Override default to indicates that the collection supports making requests.
17672          */
17673         supportsRequests: true,
17674 
17675         /**
17676          * @private
17677          * Override default to indicates that the collection does not subscribe to its objects.
17678          */
17679         supportsRestItemSubscriptions: false,
17680 
17681         /**
17682          * Retrieve the Sign Out Reason Codes.
17683          *
17684          * @returns {finesse.restservices.TeamWorkflows}
17685          *     This TeamWorkflows object to allow cascading.
17686          */
17687         get: function () {
17688             // set loaded to false so it will rebuild the collection after the get
17689             this._loaded = false;
17690             // reset collection
17691             this._collection = {};
17692             // perform get
17693             this._synchronize();
17694             return this;
17695         },
17696 
17697         /* We only use PUT and GET on Reason Code team assignments
17698          * @param {Object} contact
17699          * @param {Object} contentBody
17700          * @param {Function} successHandler
17701          */
17702         createPutSuccessHandler: function (contact, contentBody, successHandler) {
17703             return function (rsp) {
17704                 // Update internal structure based on response. Here we
17705                 // inject the contentBody from the PUT request into the
17706                 // rsp.object element to mimic a GET as a way to take
17707                 // advantage of the existing _processResponse method.
17708                 rsp.object = contentBody;
17709                 contact._processResponse(rsp);
17710 
17711                 //Remove the injected contentBody object before cascading response
17712                 rsp.object = {};
17713 
17714                 //cascade response back to consumer's response handler
17715                 successHandler(rsp);
17716             };
17717         },
17718 
17719         /**
17720          * Update - This should be all that is needed.
17721          * @param {Object} newValues
17722          * @param {Object} handlers
17723          * @returns {finesse.restservices.TeamWorkflows}
17724          *     This TeamWorkflows object to allow cascading.
17725          */
17726         update: function (newValues, handlers) {
17727             this.isLoaded();
17728             var contentBody = {}, contentBodyInner = [], i, innerObject = {};
17729 
17730             contentBody[this.getRestType()] = {
17731             };
17732 
17733             for (i in newValues) {
17734                 if (newValues.hasOwnProperty(i)) {
17735                     innerObject = {
17736                         "uri": newValues[i]
17737                     };
17738                     contentBodyInner.push(innerObject);
17739                 }
17740             }
17741 
17742             contentBody[this.getRestType()] = {
17743                 "Workflow" : contentBodyInner
17744             };
17745 
17746             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
17747             handlers = handlers || {};
17748 
17749             this.restRequest(this.getRestUrl(), {
17750                 method: 'PUT',
17751                 success: this.createPutSuccessHandler(this, contentBody, handlers.success),
17752                 error: handlers.error,
17753                 content: contentBody
17754             });
17755 
17756             return this; // Allow cascading
17757         }
17758 
17759     });
17760     
17761 	window.finesse = window.finesse || {};
17762     window.finesse.restservices = window.finesse.restservices || {};
17763     window.finesse.restservices.TeamWorkflows = TeamWorkflows;
17764     
17765     return TeamWorkflows;
17766 });
17767 
17768 /**
17769  * JavaScript representation of the Finesse Team REST object.
17770  *
17771  * @requires finesse.clientservices.ClientServices
17772  * @requires Class
17773  * @requires finesse.FinesseBase
17774  * @requires finesse.restservices.RestBase
17775  * @requires finesse.restservices.RestCollectionBase
17776  * @requires finesse.restservices.User
17777  * @requires finesse.restservices.Users
17778  */
17779 
17780 /**
17781  * The following comment prevents JSLint errors concerning undefined global variables.
17782  * It tells JSLint that these identifiers are defined elsewhere.
17783  */
17784 /*jslint bitwise:true, browser:true, nomen:true, regexp:true, sloppy:true, white:true */
17785 
17786 /** The following comment is to prevent jslint errors about 
17787  * using variables before they are defined.
17788  */
17789 /*global $, jQuery, Handlebars, dojox, dojo, finesse */
17790 
17791 /** @private */
17792 define('restservices/Team',[
17793     'restservices/RestBase',
17794     'utilities/Utilities',
17795     'restservices/Users',
17796     'restservices/TeamNotReadyReasonCodes',
17797     'restservices/TeamWrapUpReasons',
17798     'restservices/TeamSignOutReasonCodes',
17799     'restservices/TeamPhoneBooks',
17800     'restservices/TeamLayoutConfig',
17801     'restservices/TeamWorkflows'
17802 ],
17803 function (RestBase, Utilities, Users, TeamNotReadyReasonCodes, TeamWrapUpReasons, TeamSignOutReasonCodes, TeamPhoneBooks, TeamLayoutConfig, TeamWorkflows) {
17804     var Team = RestBase.extend(/** @lends finesse.restservices.Team.prototype */{
17805         
17806         _teamLayoutConfig: null,
17807 
17808         /**
17809          * @class
17810          * A Team is a set of Agent Users, typically supervised by one or more Supervisor Users.
17811          *
17812          * @augments finesse.restservices.RestBase
17813          * @see finesse.restservices.User#getSupervisedTeams
17814          * @see finesse.restservices.Users
17815          * @constructs
17816          */
17817         _fakeConstuctor: function () {
17818             /* This is here to hide the real init constructor from the public docs */
17819         },
17820         
17821         /**
17822          * @private
17823          * @class
17824          * JavaScript representation of a Team object. Also exposes methods to operate
17825          * on the object against the server.
17826          *
17827          * @param {Object} options
17828          *     An object with the following properties:<ul>
17829          *         <li><b>id:</b> The id of the object being constructed</li>
17830          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
17831          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
17832          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
17833          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
17834          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
17835          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
17836          *             <li><b>content:</b> {String} Raw string of response</li>
17837          *             <li><b>object:</b> {Object} Parsed object of response</li>
17838          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
17839          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
17840          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
17841          *             </ul></li>
17842          *         </ul></li>
17843          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
17844          **/
17845         init: function (options) {
17846             this._super(options);
17847         },
17848     
17849         /**
17850          * @private
17851          * Gets the REST class for the current object - this is the Team class.
17852          * @returns {Object} The Team constructor.
17853          */
17854         getRestClass: function () {
17855             return finesse.restesrvices.Team;
17856         },
17857     
17858         /**
17859          * @private
17860          * Gets the REST type for the current object - this is a "Team".
17861          * @returns {String} The Team string.
17862          */
17863         getRestType: function () {
17864             return "Team";
17865         },
17866     
17867         /**
17868          * @private
17869          * Override default to indicate that this object doesn't support making
17870          * requests.
17871          */
17872         supportsSubscriptions: false,
17873     
17874         /**
17875          * Getter for the team id.
17876          * @returns {String} The team id.
17877          */
17878         getId: function () {
17879             this.isLoaded();
17880             return this.getData().id;
17881         },
17882     
17883         /**
17884          * Getter for the team name.
17885          * @returns {String} The team name
17886          */
17887         getName: function () {
17888             this.isLoaded();
17889             return this.getData().name;
17890         },
17891     
17892         /**
17893          * @private
17894          * Getter for the team uri.
17895          * @returns {String} The team uri
17896          */
17897         getUri: function () {
17898             this.isLoaded();
17899             return this.getData().uri;        
17900         },
17901     
17902         /**
17903          * Constructs and returns a collection of Users.
17904          * @param {finesse.interfaces.RestObjectHandlers} [handlers] Object that sets callback handlers.
17905          * @returns {finesse.restservices.Users} Users collection of User objects.
17906          */
17907         getUsers: function (options) {
17908             this.isLoaded();
17909             options = options || {};
17910     
17911             options.parentObj = this;
17912             // We are using getData() instead of getData.Users because the superclass (RestCollectionBase)
17913             // for Users needs the "Users" key to validate the provided payload matches the class type.
17914             options.data = this.getData();
17915     
17916             return new Users(options);
17917         },
17918     
17919         /**
17920          * @private
17921          * Getter for a teamNotReadyReasonCodes collection object that is associated with Team.
17922          * @param callbacks
17923          * @returns {teamNotReadyReasonCodes}
17924          *     A teamNotReadyReasonCodes collection object.
17925          */
17926         getTeamNotReadyReasonCodes: function (callbacks) {
17927             var options = callbacks || {};
17928             options.parentObj = this;
17929             this.isLoaded();
17930     
17931             if (!this._teamNotReadyReasonCodes) {
17932                 this._teamNotReadyReasonCodes = new TeamNotReadyReasonCodes(options);
17933             }
17934     
17935             return this._teamNotReadyReasonCodes;
17936         },
17937     
17938         /**
17939          * @private
17940          * Getter for a teamWrapUpReasons collection object that is associated with Team.
17941          * @param callbacks
17942          * @returns {teamWrapUpReasons}
17943          *     A teamWrapUpReasons collection object.
17944          */
17945         getTeamWrapUpReasons: function (callbacks) {
17946             var options = callbacks || {};
17947             options.parentObj = this;
17948             this.isLoaded();
17949     
17950             if (!this._teamWrapUpReasons) {
17951                 this._teamWrapUpReasons = new TeamWrapUpReasons(options);
17952             }
17953     
17954             return this._teamWrapUpReasons;
17955         },
17956     
17957         /**
17958          * @private
17959          * Getter for a teamSignOutReasonCodes collection object that is associated with Team.
17960          * @param callbacks
17961          * @returns {teamSignOutReasonCodes}
17962          *     A teamSignOutReasonCodes collection object.
17963          */
17964     
17965         getTeamSignOutReasonCodes: function (callbacks) {
17966             var options = callbacks || {};
17967             options.parentObj = this;
17968             this.isLoaded();
17969     
17970             if (!this._teamSignOutReasonCodes) {
17971                 this._teamSignOutReasonCodes = new TeamSignOutReasonCodes(options);
17972             }
17973     
17974             return this._teamSignOutReasonCodes;
17975         },
17976     
17977         /**
17978          * @private
17979          * Getter for a teamPhoneBooks collection object that is associated with Team.
17980          * @param callbacks
17981          * @returns {teamPhoneBooks}
17982          *     A teamPhoneBooks collection object.
17983          */
17984         getTeamPhoneBooks: function (callbacks) {
17985             var options = callbacks || {};
17986             options.parentObj = this;
17987             this.isLoaded();
17988     
17989             if (!this._phonebooks) {
17990                 this._phonebooks = new TeamPhoneBooks(options);
17991             }
17992     
17993             return this._phonebooks;
17994         },
17995     
17996         /**
17997          * @private
17998          * Getter for a teamWorkflows collection object that is associated with Team.
17999          * @param callbacks
18000          * @returns {teamWorkflows}
18001          *     A teamWorkflows collection object.
18002          */
18003         getTeamWorkflows: function (callbacks) {
18004             var options = callbacks || {};
18005             options.parentObj = this;
18006             this.isLoaded();
18007     
18008             if (!this._workflows) {
18009                 this._workflows = new TeamWorkflows(options);
18010             }
18011     
18012             return this._workflows;
18013         },
18014     
18015         /**
18016          * @private
18017          * Getter for a teamLayoutConfig object that is associated with Team.
18018          * @param callbacks
18019          * @returns {teamLayoutConfig}
18020          */
18021         getTeamLayoutConfig: function (callbacks) {
18022             var options = callbacks || {};
18023             options.parentObj = this;
18024             this.isLoaded();
18025     
18026             if (this._teamLayoutConfig === null) {
18027                 this._teamLayoutConfig = new TeamLayoutConfig(options);
18028             }
18029     
18030             return this._teamLayoutConfig;
18031         }
18032     
18033     });
18034     
18035     window.finesse = window.finesse || {};
18036     window.finesse.restservices = window.finesse.restservices || {};
18037     window.finesse.restservices.Team = Team;
18038     
18039     return Team;    
18040 });
18041 
18042 /**
18043  * JavaScript representation of the Finesse Teams collection.
18044  * object which contains a list of Team objects
18045  * @requires finesse.clientservices.ClientServices
18046  * @requires Class
18047  * @requires finesse.FinesseBase
18048  * @requires finesse.restservices.RestBase
18049  * @requires finesse.restservices.RestCollectionBase
18050  */
18051 
18052 /** @private */
18053 define('restservices/Teams',[
18054     'restservices/RestCollectionBase',
18055     'restservices/Team'
18056 ],
18057 function (RestCollectionBase, Team) {
18058     /** @private */
18059     var Teams = RestCollectionBase.extend({
18060 
18061         /**
18062          * @class
18063          * JavaScript representation of a Teams collection object. Also exposes methods to operate
18064          * on the object against the server.
18065          *
18066          * @param {Object} options
18067          *     An object with the following properties:<ul>
18068          *         <li><b>id:</b> The id of the object being constructed</li>
18069          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
18070          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
18071          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
18072          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
18073          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
18074          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
18075          *             <li><b>content:</b> {String} Raw string of response</li>
18076          *             <li><b>object:</b> {Object} Parsed object of response</li>
18077          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
18078          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
18079          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
18080          *             </ul></li>
18081          *         </ul></li>
18082          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
18083          * @constructs
18084          **/
18085         init: function (options) {
18086             this._super(options);
18087         },
18088 
18089         /**
18090          * @private
18091          * Gets the REST class for the current object - this is the Teams class.
18092          * @returns {Object} The Teams constructor.
18093          */
18094         getRestClass: function () {
18095             return Teams;
18096         },
18097 
18098         /**
18099          * @private
18100          * Gets the REST class for the objects that make up the collection. - this
18101          * is the Team class.
18102          */
18103         getRestItemClass: function () {
18104             return Team;
18105         },
18106 
18107         /**
18108          * @private
18109          * Gets the REST type for the current object - this is a "Teams".
18110          * @returns {String} The Teams string.
18111          */
18112         getRestType: function () {
18113             return "Teams";
18114         },
18115         
18116         /**
18117          * @private
18118          * Gets the REST type for the objects that make up the collection - this is "Team".
18119          */
18120         getRestItemType: function () {
18121             return "Team";
18122         },
18123 
18124         /**
18125          * @private
18126          * Override default to indicates that the collection supports making
18127          * requests.
18128          */
18129         supportsRequests: true,
18130 
18131         /**
18132          * @private
18133          * Override default to indicate that this object doesn't support subscriptions.
18134          */
18135         supportsRestItemSubscriptions: false,
18136         
18137         /**
18138          * @private
18139          * Retrieve the Teams.  This call will re-query the server and refresh the collection.
18140          *
18141          * @returns {finesse.restservices.Teams}
18142          *     This Teams object to allow cascading.
18143          */
18144         get: function () {
18145             // set loaded to false so it will rebuild the collection after the get
18146             this._loaded = false;
18147             // reset collection
18148             this._collection = {};
18149             // perform get
18150             this._synchronize();
18151             return this;
18152         }
18153 
18154     });
18155 
18156     window.finesse = window.finesse || {};
18157     window.finesse.restservices = window.finesse.restservices || {};
18158     window.finesse.restservices.Teams = Teams;
18159     
18160     return Teams;
18161 });
18162 
18163 /**
18164  * JavaScript representation of the Finesse SystemInfo object
18165  *
18166  * @requires finesse.clientservices.ClientServices
18167  * @requires Class
18168  * @requires finesse.FinesseBase
18169  * @requires finesse.restservices.RestBase
18170  */
18171 
18172 /** @private */
18173 define('restservices/SystemInfo',['restservices/RestBase'], function (RestBase) {
18174     
18175     var SystemInfo = RestBase.extend(/** @lends finesse.restservices.SystemInfo.prototype */{
18176         /**
18177          * @private
18178          * Returns whether this object supports subscriptions
18179          */
18180         supportsSubscriptions: false,
18181 
18182         doNotRefresh: true,
18183       
18184         /**
18185          * @class
18186          * JavaScript representation of a SystemInfo object.
18187          * 
18188          * @augments finesse.restservices.RestBase
18189          * @see finesse.restservices.SystemInfo.Statuses
18190          * @constructs
18191          */
18192         _fakeConstuctor: function () {
18193             /* This is here to hide the real init constructor from the public docs */
18194         },
18195         
18196          /**
18197          * @private
18198          * JavaScript representation of a SystemInfo object. Also exposes methods to operate
18199          * on the object against the server.
18200          *
18201          * @param {Object} options
18202          *     An object with the following properties:<ul>
18203          *         <li><b>id:</b> The id of the object being constructed</li>
18204          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
18205          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
18206          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
18207          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
18208          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
18209          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
18210          *             <li><b>content:</b> {String} Raw string of response</li>
18211          *             <li><b>object:</b> {Object} Parsed object of response</li>
18212          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
18213          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
18214          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
18215          *             </ul></li>
18216          *         </ul></li>
18217          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
18218          **/
18219         init: function (id, callbacks, restObj)
18220         {
18221             this._super(id, callbacks, restObj);
18222         },
18223 
18224         /**
18225          * @private
18226          * Gets the REST class for the current object - this is the SystemInfo object.
18227          */
18228         getRestClass: function () {
18229             return SystemInfo;
18230         },
18231 
18232         /**
18233          * @private
18234          * Gets the REST type for the current object - this is a "SystemInfo".
18235          */
18236         getRestType: function ()
18237         {
18238             return "SystemInfo";
18239         },
18240         
18241         _validate: function (obj)
18242         {
18243             return true;
18244         },
18245         
18246         /**
18247          * Returns the status of the Finesse system.
18248          *   IN_SERVICE if the Finesse API reports that it is in service,
18249          *   OUT_OF_SERVICE otherwise.
18250          * @returns {finesse.restservices.SystemInfo.Statuses} System Status
18251          */
18252         getStatus: function () {
18253             this.isLoaded();
18254             return this.getData().status;
18255         },
18256         
18257         /**
18258          * Returns the reason due to which Finesse is OUT OF SERVICE.
18259          * It returns empty string when Finesse status is IN_SERVICE.
18260          * @returns {String} statusReason if finesse is OUT OF SERVICE , or empty string otherwise.
18261          */
18262         getStatusReason: function () {
18263             this.isLoaded();
18264             return this.getData().statusReason;
18265         },
18266         
18267         /**
18268          * Returns the current timestamp from this SystemInfo object.
18269          *   This is used to calculate time drift delta between server and client.
18270          *  @returns {String} Time (GMT): yyyy-MM-dd'T'HH:mm:ss'Z'
18271          */
18272         getCurrentTimestamp: function () {
18273             this.isLoaded();
18274             return this.getData().currentTimestamp;
18275         },
18276         
18277         /**
18278          * Getter for the xmpp domain of the system.
18279          * @returns {String} The xmpp domain corresponding to this SystemInfo object.
18280          */
18281         getXmppDomain: function () {
18282             this.isLoaded();
18283             return this.getData().xmppDomain;
18284         },
18285         
18286         /**
18287          * Getter for the xmpp pubsub domain of the system.
18288          * @returns {String} The xmpp pubsub domain corresponding to this SystemInfo object.
18289          */
18290         getXmppPubSubDomain: function () {
18291             this.isLoaded();
18292             return this.getData().xmppPubSubDomain;
18293         },
18294 
18295         /**
18296          * Getter for the deployment type (UCCE or UCCX).
18297          * @returns {String} "UCCE" or "UCCX"
18298          */ 
18299         getDeploymentType: function () {
18300             this.isLoaded();
18301             return this.getData().deploymentType;
18302         },
18303 
18304         /**
18305          * Returns whether this is a single node deployment or not by checking for the existence of the secondary node in SystemInfo.
18306          * @returns {Boolean} True for single node deployments, false otherwise.
18307          */ 
18308         isSingleNode: function () {
18309             var secondary = this.getData().secondaryNode;
18310             if (secondary && secondary.host) {
18311                 return false;
18312             }
18313             return true;
18314         },
18315 
18316         /**
18317          * Checks all arguments against the primary and secondary hosts (FQDN) and returns the match.
18318          * This is useful for getting the FQDN of the current Finesse server.
18319          * @param {String} ...arguments[]... - any number of arguments to match against
18320          * @returns {String} FQDN (if properly configured) of the matched host of the primary or secondary node, or undefined if no match is found.
18321          */ 
18322         getThisHost: function () {
18323             var i,
18324             primary = this.getData().primaryNode,
18325             secondary = this.getData().secondaryNode;
18326 
18327             for (i = 0; (i < arguments.length); i = i + 1) {
18328                 if (primary && arguments[i] === primary.host) {
18329                     return primary.host;
18330                 } else if (secondary && arguments[i] === secondary.host) {
18331                     return secondary.host;
18332                 }
18333             }
18334         },
18335 
18336         /**
18337          * Checks all arguments against the primary and secondary hosts (FQDN) and returns the other node.
18338          * This is useful for getting the FQDN of the other Finesse server, i.e. for failover purposes.
18339          * @param {String} arguments - any number of arguments to match against
18340          * @returns {String} FQDN (if properly configured) of the alternate node, defaults to primary if no match is found, undefined for single node deployments.
18341          */ 
18342         getAlternateHost: function () {
18343             var i,
18344             isPrimary = false,
18345             primary = this.getData().primaryNode,
18346             secondary = this.getData().secondaryNode,
18347             xmppDomain = this.getData().xmppDomain,
18348             alternateHost;
18349 
18350             if (primary && primary.host) {
18351                     if (xmppDomain === primary.host) {
18352                         isPrimary = true;
18353                     }
18354                 if (secondary && secondary.host) {
18355                     if (isPrimary) {
18356                         return secondary.host;
18357                     }
18358                     return primary.host;
18359                 }
18360             }
18361         },
18362         
18363         /**
18364          * Gets the peripheral ID that Finesse is connected to. The peripheral
18365          * ID is the ID of the PG Routing Client (PIM).
18366          * 
18367          * @returns {String} The peripheral Id if UCCE, or empty string otherwise.
18368          */
18369         getPeripheralId : function () {
18370              this.isLoaded();
18371              
18372              var peripheralId = this.getData().peripheralId;
18373              if (peripheralId === null) {
18374                   return "";
18375              } else {
18376                   return this.getData().peripheralId;
18377              }
18378         },
18379 
18380          /**
18381          * Gets the license. Only apply to UCCX.
18382          * 
18383          * @returns {String} The license if UCCX, or empty string otherwise.
18384          */
18385         getlicense : function () {
18386              this.isLoaded();
18387              return this.getData().license || "";
18388         },
18389         
18390         /**
18391          * Gets the systemAuthMode for the current deployment
18392          * 
18393          * @returns {String} The System auth mode for current deployment
18394          */
18395         getSystemAuthMode : function() {
18396             this.isLoaded();
18397             return this.getData().systemAuthMode;
18398         }
18399     });
18400     
18401     SystemInfo.Statuses = /** @lends finesse.restservices.SystemInfo.Statuses.prototype */ { 
18402         /** 
18403          * Finesse is in service. 
18404          */
18405         IN_SERVICE: "IN_SERVICE",
18406         /** 
18407          * Finesse is not in service. 
18408          */
18409         OUT_OF_SERVICE: "OUT_OF_SERVICE",
18410         /**
18411          * @class SystemInfo status values.
18412          * @constructs
18413          */
18414         _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
18415 
18416     };
18417     
18418     window.finesse = window.finesse || {};
18419     window.finesse.restservices = window.finesse.restservices || {};
18420     window.finesse.restservices.SystemInfo = SystemInfo;
18421     
18422     return SystemInfo;
18423 });
18424 
18425 define('restservices/DialogLogoutActions',[], function ()
18426 {
18427     var DialogLogoutActions = /** @lends finesse.restservices.DialogLogoutActions.prototype */ {
18428 
18429         /**
18430          * Set this action to close active dialogs when the agent logs out.
18431          */
18432         CLOSE: "CLOSE",
18433 
18434         /**
18435          * Set this action to transfer active dialogs when the agent logs out.
18436          */
18437         TRANSFER: "TRANSFER",
18438 
18439         /**
18440          * @class Actions used to handle tasks that are associated with a given media at logout time.
18441          *
18442          * @constructs
18443          */
18444         _fakeConstructor: function ()
18445         {
18446         }, // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
18447 
18448         /**
18449          * Is the given action a valid dialog logout action.
18450          *
18451          * @param {String} action the action to evaluate
18452          * @returns {Boolean} true if the action is valid; false otherwise
18453          */
18454         isValidAction: function(action)
18455         {
18456             if ( !action )
18457             {
18458                 return false;
18459             }
18460 
18461             return DialogLogoutActions.hasOwnProperty(action.toUpperCase());
18462         }
18463     };
18464 
18465     window.finesse = window.finesse || {};
18466     window.finesse.restservices = window.finesse.restservices || {};
18467     window.finesse.restservices.DialogLogoutActions = DialogLogoutActions;
18468 
18469     return DialogLogoutActions;
18470 });
18471 define('restservices/InterruptActions',[], function ()
18472 {
18473     var InterruptActions = /** @lends finesse.restservices.InterruptActions.prototype */
18474     {
18475         /**
18476          * The interrupt will be accepted and the agent will not work on dialogs in this media until the media is no longer interrupted.
18477          */
18478         ACCEPT: "ACCEPT",
18479 
18480         /**
18481          * the interrupt will be ignored and the agent is allowed to work on dialogs while the media is interrupted.
18482          */
18483         IGNORE: "IGNORE",
18484 
18485         /**
18486          * @class
18487          *
18488          * The action to be taken in the event this media is interrupted. The action will be one of the following:<ul>
18489          *     <li><b>ACCEPT:</b> the interrupt will be accepted and the agent will not work on dialogs in this media
18490          *     until the media is no longer interrupted.</li>
18491          *     <li><b>IGNORE:</b> the interrupt will be ignored and the agent is allowed to work on dialogs while the
18492          *     media is interrupted.</li></ul>
18493          *
18494          * @constructs
18495          */
18496         _fakeConstructor: function ()
18497         {
18498         }, // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
18499 
18500         /**
18501          * Is the given action a valid dialog logout action.
18502          *
18503          * @param {String} action the action to evaluate
18504          * @returns {Boolean} true if the action is valid; false otherwise
18505          */
18506         isValidAction: function(action)
18507         {
18508             if ( !action )
18509             {
18510                 return false;
18511             }
18512 
18513             return InterruptActions.hasOwnProperty(action.toUpperCase());
18514         }
18515     };
18516 
18517     window.finesse = window.finesse || {};
18518     window.finesse.restservices = window.finesse.restservices || {};
18519     window.finesse.restservices.InterruptActions = InterruptActions;
18520 
18521     return InterruptActions;
18522 });
18523 /**
18524  * JavaScript representation of the ReasonCode lookup object.
18525  * This has got reason code related APIs like looking up a reason code with
18526  * its code value and category.
18527  *
18528  */
18529 
18530 /** @private */
18531 define('restservices/ReasonCodeLookup',['restservices/RestBase', 'utilities/Utilities'], function (RestBase, Utilities) {
18532     
18533     var ReasonCodeLookup = RestBase.extend(/** @lends finesse.restservices.ReasonCodeLookup.prototype */{
18534         /**
18535          * @private
18536          * Returns whether this object supports subscriptions
18537          */
18538         supportsSubscriptions: false,
18539 
18540         doNotRefresh: true,
18541         
18542         autoSubscribe: false,
18543         
18544         supportsRequests: false,
18545       
18546         /**
18547          * @class
18548          * JavaScript representation of a ReasonCodeLookup object.
18549          * 
18550          * @constructs
18551          */
18552         _fakeConstuctor: function () {
18553             /* This is here to hide the real init constructor from the public docs */
18554         },
18555         
18556          /**
18557          * @private
18558          * JavaScript representation of a ReasonCodeLookup object. Also exposes methods to operate
18559          * on the object against the server.
18560          *
18561          * @param {Object} options
18562          *     An object with the following properties:<ul>
18563          *         <li><b>id:</b> The id of the object being constructed</li>
18564          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
18565          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
18566          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
18567          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
18568          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
18569          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
18570          *             <li><b>content:</b> {String} Raw string of response</li>
18571          *             <li><b>object:</b> {Object} Parsed object of response</li>
18572          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
18573          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
18574          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
18575          *             </ul></li>
18576          *         </ul></li>
18577          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
18578          **/
18579         init: function (options){
18580             this._super(options);
18581         },
18582         
18583         /**
18584          * Do get disabled.
18585          */
18586         doGet: function(handlers) {
18587             return;
18588         },
18589 
18590         /**
18591          * @private
18592          * Gets the REST class for the current object - this is the ReasonCodeLookup object.
18593          */
18594         getRestClass: function () {
18595             return ReasonCodeLookup;
18596         },
18597 
18598         /**
18599          * @private
18600          * Gets the REST type for the current object - this is a "ReasonCodeLookup".
18601          */
18602         getRestType: function () {
18603             return "ReasonCode";
18604         },
18605         
18606         
18607         /**
18608          * @private
18609          * Parses a uriString to retrieve the id portion
18610          * @param {String} uriString
18611          * @return {String} id
18612          */
18613         _parseIdFromUriString : function (uriString) {
18614             return Utilities.getId(uriString);
18615         },
18616         
18617         /**
18618          * Performs a GET against the Finesse server looking up the reason code 
18619          * with its reason code value, and category specified.
18620          * Note that there is no return value; use the success handler to process a
18621          * valid return.
18622          * 
18623          * @param {finesse.interfaces.RequestHandlers} handlers
18624          *     An object containing the handlers for the request
18625          * @param {String} reasonCodeValue The code for the reason code to lookup
18626          * @param {String} reasonCodeCategory The category for the reason code to lookup
18627          * 
18628          */
18629         lookupReasonCode : function (handlers, reasonCodeValue, reasonCodeCategory) {
18630             var self = this, contentBody, reasonCode, url;
18631             contentBody = {};
18632             
18633             url = this.getRestUrl();
18634             url = url + "?category=" + reasonCodeCategory + "&code=" + reasonCodeValue;
18635             this.restRequest(url, {
18636                 method: 'GET',
18637                 success: function (rsp) {
18638                     reasonCode = {
18639                         uri: rsp.object.ReasonCode.uri,
18640                         label: rsp.object.ReasonCode.label,
18641                         id: self._parseIdFromUriString(rsp.object.ReasonCode.uri)
18642                     };
18643                     handlers.success(reasonCode);
18644                 },
18645                 error: function (rsp) {
18646                     handlers.error(rsp);
18647                 },
18648                 content: contentBody
18649             });
18650         }
18651         
18652     });
18653     
18654         
18655     window.finesse = window.finesse || {};
18656     window.finesse.restservices = window.finesse.restservices || {};
18657     window.finesse.restservices.ReasonCodeLookup = ReasonCodeLookup;
18658     
18659     return ReasonCodeLookup;
18660 });
18661 
18662 /**
18663  * Provides standard way resolve message keys with substitution
18664  *
18665  * @requires finesse.container.I18n or gadgets.Prefs
18666  */
18667 
18668 // Add Utilities to the finesse.utilities namespace
18669 define('utilities/I18n',['utilities/Utilities'], function (Utilities) {
18670     var I18n = (function () {
18671 
18672         /**
18673          * Shortcut to finesse.container.I18n.getMsg or gadgets.Prefs.getMsg
18674          * @private
18675          */
18676         var _getMsg;
18677 
18678         return {
18679             /**
18680              * Provides a message resolver for this utility singleton.
18681              * @param {Function} getMsg
18682              *     A function that returns a string given a message key.
18683              *     If the key is not found, this function must return 
18684              *     something that tests false (i.e. undefined or "").
18685              */
18686             setGetter : function (getMsg) {
18687                 _getMsg = getMsg;
18688             },
18689 
18690             /**
18691              * Resolves the given message key, also performing substitution.
18692              * This generic utility will use a custom function to resolve the key
18693              * provided by finesse.utilities.I18n.setGetter. Otherwise, it will 
18694              * discover either finesse.container.I18n.getMsg or gadgets.Prefs.getMsg
18695              * upon the first invocation and store that reference for efficiency.
18696              * 
18697              * Since this will construct a new gadgets.Prefs object, it is recommended
18698              * for gadgets to explicitly provide the setter to prevent duplicate
18699              * gadgets.Prefs objects. This does not apply if your gadget does not need
18700              * access to gadgets.Prefs other than getMsg. 
18701              * 
18702              * @param {String} key
18703              *     The key to lookup
18704              * @param {String} arguments
18705              *     Arguments for substitution
18706              * @returns {String/Function}
18707              *     The resolved string if successful, otherwise a function that returns
18708              *     a '???' string that can also be casted into a string.
18709              */
18710             getString : function (key) {
18711                 var prefs, i, retStr, noMsg, getFailed = "", args;
18712                 if (!_getMsg) {
18713                     if (finesse.container && finesse.container.I18n) {
18714                         _getMsg = finesse.container.I18n.getMsg;
18715                     } else if (gadgets) {
18716                         prefs = new gadgets.Prefs();
18717                         _getMsg = prefs.getMsg;
18718                     }
18719                 }
18720                 
18721                 try {
18722                     retStr = _getMsg(key);
18723                 } catch (e) {
18724                     getFailed = "finesse.utilities.I18n.getString(): invalid _getMsg";
18725                 }
18726 
18727                 if (retStr) {
18728                     // Lookup was successful, perform substitution (if any)
18729                     args = [ retStr ];
18730                     for (i = 1; i < arguments.length; i += 1) {
18731                         args.push(arguments[i]);
18732                     }
18733                     return Utilities.formatString.apply(this, args);
18734                 }
18735                 // We want a function because jQuery.html() and jQuery.text() is smart enough to invoke it.
18736                 /** @private */
18737                 noMsg = function () {
18738                     return "???" + key + "???" + getFailed;
18739                 };
18740                 // We overload the toString() of this "function" to allow JavaScript to cast it into a string
18741                 // For example, var myMsg = "something " + finesse.utilities.I18n.getMsg("unresolvable.key");
18742                 /** @private */
18743                 noMsg.toString = function () {
18744                     return "???" + key + "???" + getFailed;
18745                 };
18746                 return noMsg;
18747 
18748             }
18749         };
18750     }());
18751     
18752     window.finesse = window.finesse || {};
18753     window.finesse.utilities = window.finesse.utilities || {};
18754     window.finesse.utilities.I18n = I18n;
18755 
18756     return I18n;
18757 });
18758 
18759 /**
18760  * Logging.js: provides simple logging for clients to use and overrides synchronous native methods: alert(), confirm(), and prompt().
18761  * 
18762  * On Firefox, it will hook into console for logging.  On IE, it will log to the status bar. 
18763  */
18764 // Add Utilities to the finesse.utilities namespace
18765 define('utilities/Logger',[], function () {
18766     var Logger = (function () {
18767         
18768         var
18769         
18770         /** @private **/
18771         debugOn,
18772         
18773         /**
18774          * Pads a single digit number for display purposes (e.g. '4' shows as '04')
18775          * @param num is the number to pad to 2 digits
18776          * @returns a two digit padded string
18777          * @private
18778          */
18779         padTwoDigits = function (num) {        
18780             return (num < 10) ? '0' + num : num;  
18781         },
18782         
18783         /**
18784          * Checks to see if we have a console - this allows us to support Firefox or IE.
18785          * @returns {Boolean} True for Firefox, False for IE
18786          * @private
18787          */
18788         hasConsole = function () {
18789             var retval = false;
18790             try
18791             {
18792                 if (window.console !== undefined) 
18793                 {
18794                     retval = true;
18795                 }
18796             } 
18797             catch (err)
18798             {
18799                 retval = false;
18800             }
18801               
18802             return retval;
18803         },
18804         
18805         /**
18806          * Gets a timestamp.
18807          * @returns {String} is a timestamp in the following format: HH:MM:SS
18808          * @private
18809          */
18810         getTimeStamp = function () {
18811             var date = new Date(), timeStr;
18812             timeStr = padTwoDigits(date.getHours()) + ":" + padTwoDigits(date.getMinutes()) + ":" + padTwoDigits(date.getSeconds());
18813 
18814             return timeStr;
18815         };
18816         
18817         return {
18818             /**
18819              * Enable debug mode. Debug mode may impact performance on the UI.
18820              *
18821              * @param {Boolean} enable
18822              *      True to enable debug logging.
18823              * @private
18824              */
18825             setDebug : function (enable) {
18826                 debugOn = enable;
18827             },
18828             
18829             /**
18830              * Logs a string as DEBUG.
18831              * 
18832              * @param str is the string to log. 
18833              * @private
18834              */
18835             log : function (str) {
18836                 var timeStr = getTimeStamp();
18837                 
18838                 if (debugOn) {
18839                     if (hasConsole())
18840                     {
18841                         window.console.log(timeStr + ": " + "DEBUG" + " - " + str);
18842                     }
18843                 }
18844             },
18845             
18846             /**
18847              * Logs a string as INFO.
18848              * 
18849              * @param str is the string to log. 
18850              * @private
18851              */
18852             info : function (str) {
18853                 var timeStr = getTimeStamp();
18854                 
18855                 if (hasConsole())
18856                 {
18857                     window.console.info(timeStr + ": " + "INFO" + " - " + str);
18858                 }
18859             },
18860             
18861             /**
18862              * Logs a string as WARN.
18863              * 
18864              * @param str is the string to log. 
18865              * @private
18866              */
18867             warn : function (str) {
18868                 var timeStr = getTimeStamp();
18869                 
18870                 if (hasConsole())
18871                 {
18872                     window.console.warn(timeStr + ": " + "WARN" + " - " + str);
18873                 }
18874             },
18875             /**
18876              * Logs a string as ERROR.
18877              * 
18878              * @param str is the string to log. 
18879              * @private
18880              */
18881             error : function (str) {
18882                 var timeStr = getTimeStamp();
18883                 
18884                 if (hasConsole())
18885                 {
18886                     window.console.error(timeStr + ": " + "ERROR" + " - " + str);
18887                 }
18888             }
18889         };
18890     }());
18891     
18892     return Logger;
18893 });
18894 
18895 /**
18896  * BackSpaceHandler.js: provides functionality to prevent the page from navigating back and hence losing the unsaved data when backspace is pressed.
18897  * 
18898  */
18899 define('utilities/BackSpaceHandler',[], function () {
18900 			var eventCallback = function(event) {
18901 				var doPrevent = false, d = event.srcElement || event.target;
18902 				if (event.keyCode === 8) {
18903 					if ((d.tagName.toUpperCase() === 'INPUT' && (d.type
18904 							.toUpperCase() === 'TEXT'
18905 							|| d.type.toUpperCase() === 'PASSWORD'
18906 							|| d.type.toUpperCase() === 'FILE'
18907 							|| d.type.toUpperCase() === 'SEARCH'
18908 							|| d.type.toUpperCase() === 'EMAIL'
18909 							|| d.type.toUpperCase() === 'NUMBER' || d.type
18910 							.toUpperCase() === 'DATE'))
18911 							|| d.tagName.toUpperCase() === 'TEXTAREA') {
18912 						doPrevent = d.readOnly || d.disabled;
18913 					} else {
18914 						//if HTML content is editable doPrevent will be false and vice versa
18915 						doPrevent = (!d.isContentEditable);
18916 					}
18917 				}
18918 
18919 				if (doPrevent) {
18920 					event.preventDefault();
18921 				}
18922 			};
18923 
18924 			if (window.addEventListener) {
18925 				window.addEventListener('keydown', eventCallback);
18926 			} else if (window.attachEvent) {
18927 				window.attachEvent('onkeydown', eventCallback);
18928 			} else {
18929 				window.console.error("Unable to attach backspace handler event ");
18930 			}
18931 });
18932 /* using variables before they are defined.
18933  */
18934 /*global navigator,unescape,sessionStorage,localStorage,_initSessionList,_initSessionListComplete */
18935 
18936 /**
18937  * Allows each gadget to communicate with the server to send logs.
18938  */
18939 
18940 /**
18941  * @class
18942  * @private
18943  * Allows each product to initialize its method of storage
18944  */
18945 define('cslogger/FinesseLogger',["clientservices/ClientServices", "utilities/Utilities"], function (ClientServices, Utilities) {
18946     
18947     var FinesseLogger = (function () { 
18948 
18949         var
18950 
18951         /**
18952          * Array use to collect ongoing logs in memory
18953          * @private
18954          */
18955         _logArray = [],
18956 
18957         /**
18958          * The final data string sent to the server, =_logArray.join
18959          * @private
18960          */
18961         _logStr = "",
18962 
18963         /**
18964          * Keep track of size of log
18965          * @private
18966          */
18967         _logSize = 0,
18968 
18969         /**
18970          * Flag to keep track show/hide of send log link
18971          * @private
18972          */
18973         _sendLogShown = false,
18974 
18975         /**
18976          * Flag to keep track if local log initialized
18977          * @private
18978          */
18979         _loggingInitialized = false,
18980         
18981 
18982         /**
18983          * local log size limit
18984          * @private
18985          */
18986         _maxLocalStorageSize = 5000000,
18987 
18988         /**
18989          * half local log size limit
18990          * @private
18991          */
18992         _halfMaxLocalStorageSize = 0.5*_maxLocalStorageSize,
18993 
18994         
18995         /**
18996          * threshold for purge 
18997          * @private
18998          */
18999         _purgeStartPercent = 0.75,
19000         
19001         /**
19002          * log item prefix 
19003          * @private
19004          */
19005         _linePrefix = null,
19006         
19007         /**
19008          * locallog session 
19009          * @private
19010          */
19011         _session = null,
19012         
19013         /**
19014          * Flag to keep track show/hide of send log link
19015          * @private
19016          */
19017         _sessionKey = null,
19018         /**
19019          * Log session metadata 
19020          * @private
19021          */
19022         _logInfo = {},
19023         
19024         /**
19025          * Flag to find sessions 
19026          * @private
19027          */
19028         _findSessionsObj = null,
19029 
19030         /**
19031          * Wrap up console.log esp. for IE9 
19032          * @private
19033          */
19034         _myConsoleLog = function (str) {
19035             if (window.console !== undefined) {
19036               window.console.log(str);
19037             }
19038         },
19039         /**
19040          * Initialize the Local Logging
19041          * @private
19042          */
19043         _initLogging = function () {
19044             if (_loggingInitialized) {
19045                 return;
19046             }
19047             //Build a new store
19048             _session = sessionStorage.getItem("finSessKey");
19049             //if the _session is null or empty, skip the init
19050             if (!_session) {
19051               return;
19052             }
19053             _sessionKey = "Fi"+_session;
19054             _linePrefix = _sessionKey + "_";
19055             _logInfo = {};
19056             _logInfo.name = _session;
19057             _logInfo.size = 0;
19058             _logInfo.head = 0;
19059             _logInfo.tail = 0;
19060             _logInfo.startTime = new Date().getTime();
19061             _loggingInitialized = true;
19062             _initSessionList();
19063         },
19064         
19065         /**
19066          * get total data size 
19067          *
19068          * @return {Integer} which is the amount of data stored in local storage.
19069          * @private
19070          */
19071         _getTotalData = function ()
19072         {
19073             var sessName, sessLogInfoStr,sessLogInfoObj, sessionsInfoObj, totalData = 0,
19074             sessionsInfoStr = localStorage.getItem("FinesseSessionsInfo");
19075             if (!sessionsInfoStr) {
19076                  return 0;
19077             }
19078             sessionsInfoObj = JSON.parse(sessionsInfoStr);
19079 
19080             for (sessName in sessionsInfoObj.sessions)
19081             {
19082                 if (sessionsInfoObj.sessions.hasOwnProperty(sessName)) {
19083                     sessLogInfoStr = localStorage.getItem("Fi" + sessName);
19084                     if (!sessLogInfoStr) {
19085                         _myConsoleLog("_getTotalData failed to get log info for "+sessName);
19086                     }
19087                     else {
19088                        sessLogInfoObj = JSON.parse(sessLogInfoStr);
19089                        totalData = totalData + sessLogInfoObj.size;
19090                     }
19091                 }
19092             }
19093 
19094               return totalData;
19095         },
19096         
19097         /**
19098          * Remove lines from tail up until store size decreases to half of max size limit.
19099          *
19100          * @private
19101          */
19102         _purgeCurrentSession = function() {
19103             var curStoreSize, purgedSize=0, line, tailKey, secLogInfoStr, logInfoStr, theLogInfo;
19104             curStoreSize = _getTotalData();
19105             if (curStoreSize < _halfMaxLocalStorageSize) {
19106                return;
19107             }
19108             logInfoStr = localStorage.getItem(_sessionKey);
19109             if (!logInfoStr) {
19110                return;
19111             }
19112             theLogInfo = JSON.parse(logInfoStr);
19113             //_myConsoleLog("Starting _purgeCurrentSession() - currentStoreSize=" + curStoreSize);
19114             while(curStoreSize > _halfMaxLocalStorageSize) {
19115                try {
19116                    tailKey = _sessionKey+"_"+theLogInfo.tail;
19117                    line = localStorage.getItem(tailKey);
19118                    if (line) {
19119                        purgedSize = purgedSize +line.length;
19120                        localStorage.removeItem(tailKey);
19121                        curStoreSize = curStoreSize - line.length;
19122                        theLogInfo.size = theLogInfo.size - line.length;
19123                    }
19124                }
19125                catch (err) {
19126                    _myConsoleLog("purgeCurrentSession encountered err="+err);
19127                }
19128                if (theLogInfo.tail < theLogInfo.head) {
19129                    theLogInfo.tail = theLogInfo.tail  + 1;
19130                }
19131                else {
19132                    break;
19133                }
19134             }
19135             //purge stops here, we need to update session's meta data in storage
19136             secLogInfoStr = localStorage.getItem(_sessionKey);
19137             if (!secLogInfoStr) {
19138                 //somebody cleared the localStorage
19139                 return;
19140             }
19141             
19142             //_myConsoleLog("In _purgeCurrentSession() - after purging current session, currentStoreSize=" + curStoreSize);
19143             //_myConsoleLog("In _purgeCurrentSession() - after purging purgedSize=" + purgedSize);
19144             //_myConsoleLog("In _purgeCurrentSession() - after purging logInfo.size=" + theLogInfo.size);
19145             //_myConsoleLog("In _purgeCurrentSession() - after purging logInfo.tail=" + theLogInfo.tail);
19146             localStorage.setItem(_sessionKey, JSON.stringify(theLogInfo));
19147             _myConsoleLog("Done _purgeCurrentSession() - currentStoreSize=" + curStoreSize);
19148         },
19149        
19150         /**
19151          * Purge a session 
19152          *
19153          * @param sessionName is the name of the session
19154          * @return {Integer} which is the current amount of data purged
19155          * @private
19156          */
19157         _purgeSession = function (sessionName) {
19158               var theLogInfo, logInfoStr, sessionsInfoStr, sessionsInfoObj;
19159               //Get the session logInfo
19160               logInfoStr = localStorage.getItem("Fi" + sessionName);
19161               if (!logInfoStr) {
19162                  _myConsoleLog("_purgeSession failed to get logInfo for "+sessionName);
19163                  return 0;
19164               }
19165               theLogInfo = JSON.parse(logInfoStr);
19166               
19167               //Note: This assumes that we don't crash in the middle of purging
19168               //=> if we do then it should get deleted next time
19169               //Purge tail->head
19170               while (theLogInfo.tail <= theLogInfo.head)
19171               {
19172                   try {
19173                       localStorage.removeItem("Fi" + sessionName + "_" + theLogInfo.tail);
19174                       theLogInfo.tail = theLogInfo.tail + 1;
19175                   }
19176                   catch (err) {
19177                       _myConsoleLog("In _purgeSession err="+err);
19178                       break;
19179                   }
19180               }
19181 
19182               //Remove the entire session
19183               localStorage.removeItem("Fi" + sessionName);
19184 
19185               //Update FinesseSessionsInfo
19186               sessionsInfoStr = localStorage.getItem("FinesseSessionsInfo");
19187               if (!sessionsInfoStr) {
19188                  _myConsoleLog("_purgeSession could not get sessions Info, it was cleared?");
19189                  return 0;
19190               }
19191               sessionsInfoObj = JSON.parse(sessionsInfoStr);
19192               if (sessionsInfoObj.sessions !== null)
19193               {
19194                  delete sessionsInfoObj.sessions[sessionName];
19195               
19196                  sessionsInfoObj.total = sessionsInfoObj.total - 1;
19197                  sessionsInfoObj.lastWrittenBy = _session;
19198                  localStorage.setItem("FinesseSessionsInfo", JSON.stringify(sessionsInfoObj));
19199               }
19200               
19201               return theLogInfo.size;
19202         },
19203         
19204          /**
19205           * purge old sessions
19206           * 
19207           * @param storeSize
19208 	  * @return {Boolean} whether purging reaches its target
19209           * @private
19210          */
19211          _purgeOldSessions = function (storeSize) {
19212              var sessionsInfoStr, purgedSize = 0, sessName, sessions, curStoreSize, activeSession, sessionsInfoObj;
19213              sessionsInfoStr = localStorage.getItem("FinesseSessionsInfo");
19214              if (!sessionsInfoStr) {
19215                 _myConsoleLog("Could not get FinesseSessionsInfo");
19216                 return true;
19217              }
19218              sessionsInfoObj = JSON.parse(sessionsInfoStr);
19219              curStoreSize = _getTotalData();
19220              
19221              activeSession = _session;
19222              sessions = sessionsInfoObj.sessions;
19223              for (sessName in sessions) {
19224                 if (sessions.hasOwnProperty(sessName)) {
19225                     if (sessName !== activeSession) {
19226                         purgedSize = purgedSize + _purgeSession(sessName);
19227                         if ((curStoreSize-purgedSize) < _halfMaxLocalStorageSize) {
19228                             return true;
19229                         }
19230                     }
19231                 }
19232              }
19233             //purge is not done, so return false
19234             return false;
19235          },
19236          
19237        /**
19238         * handle insert error
19239         *
19240         * @param error
19241         * @private
19242         */
19243         _insertLineHandleError = function (error) {
19244             _myConsoleLog(error);
19245         },
19246 
19247         /**
19248          * check storage data size and if need purge
19249          * @private
19250          */
19251         _checkSizeAndPurge = function () {
19252             var purgeIsDone=false, totalSize = _getTotalData();
19253             if (totalSize > 0.75*_maxLocalStorageSize) {
19254                _myConsoleLog("in _checkSizeAndPurge, totalSize ("+totalSize+") exceeds limit");
19255                purgeIsDone = _purgeOldSessions(totalSize);
19256                if (purgeIsDone) {
19257                   _myConsoleLog("in _checkSizeAndPurge after purging old session, purge is done");
19258                }
19259                else {
19260                   //after all old sessions purged, still need purge
19261                   totalSize = _getTotalData();
19262                   if (totalSize > 0.75*_maxLocalStorageSize) {
19263                       _myConsoleLog("in _checkSizeAndPurge after purging old session,still needs purging, now storeSize ("+totalSize+")");
19264                      _purgeCurrentSession();
19265                      _myConsoleLog("in _checkSizeAndPurge done purging current session.");
19266                   }
19267                }
19268             }
19269         },
19270         
19271         /**
19272          * check if the session is already in meta data  
19273          * 
19274          * @param metaData
19275          * @param sessionName
19276          * @return {Boolean} true if session has metaData (false otherwise)
19277          * @private
19278          */
19279         _sessionsInfoContains = function (metaData, sessionName) {
19280            if (metaData && metaData.sessions && metaData.sessions.hasOwnProperty(sessionName)) {
19281               return true;
19282            }
19283            return false;
19284         },
19285         
19286         
19287         /**
19288          * setup sessions in local storage 
19289          * 
19290          * @param logInfo
19291          * @private
19292          */
19293         _getAndSetNumberOfSessions = function (logInfo) {
19294             var numOfSessionsPass1, numOfSessionsPass2, l;
19295             numOfSessionsPass1 = localStorage.getItem("FinesseSessionsInfo");
19296             if (numOfSessionsPass1 === null) {
19297                 //Init first time
19298                 numOfSessionsPass1 = {};
19299                 numOfSessionsPass1.total = 1;
19300                 numOfSessionsPass1.sessions = {};
19301                 numOfSessionsPass1.sessions[logInfo.name] = logInfo.startTime;
19302                 numOfSessionsPass1.lastWrittenBy = logInfo.name;
19303                 localStorage.setItem("FinesseSessionsInfo", JSON.stringify(numOfSessionsPass1));
19304             }
19305             else {
19306                 numOfSessionsPass1 = JSON.parse(numOfSessionsPass1);
19307                 //check if the session is already in the FinesseSessionSInfo
19308                 if (_sessionsInfoContains(numOfSessionsPass1, logInfo.name)) {
19309                     return;
19310                 }             
19311                 //Save numOfSessionsPass1
19312                 numOfSessionsPass1.total = parseInt(numOfSessionsPass1.total, 10) + 1;
19313                 numOfSessionsPass1.sessions[logInfo.name] = logInfo.startTime;
19314                 numOfSessionsPass1.lastWrittenBy = logInfo.name;
19315                 localStorage.setItem("FinesseSessionsInfo", JSON.stringify(numOfSessionsPass1));
19316                 numOfSessionsPass2 = localStorage.getItem("FinesseSessionsInfo");
19317                 if (!numOfSessionsPass2) {
19318                    _myConsoleLog("Could not get FinesseSessionsInfo");
19319                    return;
19320                 }
19321                 numOfSessionsPass2 = JSON.parse(numOfSessionsPass2);
19322                 //in future we need to confirm the numOfSessionsPass2 is the same as numOfSessionsPass1
19323                 ////if (numOfSessionsPass1.lastWrittenBy !== numOfSessionsPass2.lastWrittenBy) {
19324                 ////    _myConsoleLog("Rebuild sessions");
19325                 ////    _sessionTimerId = setTimeout(_initSessionList, 10000);
19326                 ////}
19327                 ////else {
19328                 ////    _sessionTimerId = null;
19329                 ////callback(numOfSessionsPass2.sessions);
19330                 ////}
19331             }
19332             if (!localStorage.getItem(_sessionKey)) {
19333                 localStorage.setItem(_sessionKey, JSON.stringify(_logInfo));
19334             }
19335         },
19336         
19337         
19338         /**
19339          * init session list 
19340          * @private
19341          */
19342         _initSessionList = function () {
19343             _getAndSetNumberOfSessions(_logInfo);
19344         },
19345         
19346        /**
19347         * do the real store of log line
19348         * 
19349         * @param line
19350         * @private
19351         */
19352         _persistLine = function (line) {
19353             var key, logInfoStr;
19354             logInfoStr = localStorage.getItem(_sessionKey);
19355             if (logInfoStr === null) {
19356                return;
19357             }
19358             _logInfo = JSON.parse(logInfoStr);
19359             _logInfo.head = _logInfo.head + 1;
19360             key = _linePrefix + _logInfo.head;
19361             localStorage.setItem(key, line);
19362             //Save the size
19363             _logInfo.size = _logInfo.size + line.length;
19364             if (_logInfo.tail === 0) {
19365                 _logInfo.tail = _logInfo.head;
19366             }
19367         
19368             localStorage.setItem(_sessionKey, JSON.stringify(_logInfo));
19369             _checkSizeAndPurge();
19370         },
19371         
19372         /**
19373          * Insert a line into the localStorage.
19374          *
19375          * @param line line to be inserted 
19376          * @private
19377         */
19378         _insertLine = function (line) {
19379             //_myConsoleLog("_insertLine: [" + line + "]");
19380             //Write the next line to localStorage
19381             try {
19382                //Persist the line 
19383                _persistLine(line);
19384             }
19385             catch (err) {
19386                _myConsoleLog("error in _insertLine(), err="+err);
19387                //_insertLineHandleError(err);
19388             }
19389         },
19390          
19391         
19392         /**
19393          * Clear the local storage
19394          * @private
19395          */
19396         _clearLocalStorage = function() {
19397             localStorage.clear();
19398 
19399         },
19400 
19401         /**
19402          * Collect logs when onCollect called
19403          *
19404          * @param data
19405          * @private
19406          */
19407         _collectMethod = function(data) {
19408           //Size of log should not exceed 1.5MB
19409           var info, maxLength = 1572864;
19410           
19411           //add size buffer equal to the size of info to be added when publish
19412           info = Utilities.getSanitizedUserAgentString() + "
";
19413           info = escape(info);
19414 
19415             //If log was empty previously, fade in buttons
19416             if (!_sendLogShown) {
19417                 //call the fadeInSendLog() in Footer
19418                 finesse.modules.Footer.sendLogAppear();
19419                 _sendLogShown = true;
19420                 _logSize = info.length;
19421             }
19422             
19423             //if local storage logging is enabled, then insert the log into local storage
19424             if (window.sessionStorage.getItem('enableLocalLog')==='true') {
19425                 if (data) {
19426                    if (data.length>0 && data.substring(0,1) === '\n') {
19427                       _insertLine(data.substring(1));
19428                    }
19429                    else {
19430                       _insertLine(data);
19431                    }
19432                 }
19433             }
19434               
19435             //escape all data to get accurate size (shindig will escape when it builds request)
19436             //escape 6 special chars for XML: &<>"'\n
19437             data = data.replace(/&/g, "&").replace(/"/g, """).replace(/'/g, "'").replace(/>/g, ">").replace(/</g, "<").replace(/\n/g, "
");
19438             data = escape(data+"\n");
19439 
19440             if (data.length < maxLength){
19441                 //make room for new data if log is exceeding max length
19442                 while (_logSize + data.length > maxLength) {
19443                     _logSize -= (_logArray.shift()).length;
19444                 }
19445             }
19446 
19447             //Else push the log into memory, increment the log size
19448             _logArray.push(data);
19449 
19450             //inc the size accordingly
19451             _logSize+=data.length;
19452 
19453         };
19454 
19455         return {
19456 
19457             /**
19458              * @private
19459              * Initiate FinesseLogger.
19460              */
19461             init: function () {
19462                 ClientServices.subscribe("finesse.clientLogging.*", _collectMethod);
19463                 _initLogging();
19464             },
19465 
19466             /**
19467              * @private
19468              * Clear all items stored in localStorage.
19469             */
19470             clear : function () {
19471                _clearLocalStorage();
19472             },
19473 
19474             /**
19475              * @private
19476              * Initialize the local storage logging.
19477             */
19478             initLocalLog: function () {
19479                _initLogging();
19480             },
19481 
19482             /**
19483              * @private
19484              * Inserts a line into the localStorage.
19485              * @param line to insert
19486             */
19487             localLog : function (line) {
19488                _insertLine(line);
19489             },
19490 
19491            /**
19492             * @ignore
19493             * Publish logs to server and clear the memory
19494             *
19495             * @param userObj
19496             * @param options
19497             * @param callBack
19498             */
19499             publish: function(userObj, options, callBack) {
19500                 // Avoid null references.
19501                 options = options || {};
19502                 callBack = callBack || {};
19503 
19504                 if (callBack.sending === "function") {
19505                     callBack.sending();
19506                 }
19507 
19508                 //logs the basic version and machine info and escaped new line
19509                 _logStr = Utilities.getSanitizedUserAgentString() + "
";
19510                 
19511                 //join the logs to correct string format
19512                 _logStr += unescape(_logArray.join(""));
19513 
19514                 //turning log string to JSON obj
19515                 var logObj = {
19516                         ClientLog: {
19517                         logData : _logStr //_logStr
19518                     }
19519                 },
19520                 tmpOnAdd = (options.onAdd && typeof options.onAdd === "function")? options.onAdd : function(){};
19521                 /** @private */
19522                 options.onAdd = function(){
19523                     tmpOnAdd();
19524                     _logArray.length = 0; _logSize =0;
19525                     _sendLogShown = false;
19526                     };
19527                 //adding onLoad to the callbacks, this is the subscribe success case for the first time user subscribe to the client log node
19528                 /** @private */
19529                 options.onLoad = function (clientLogObj) {
19530                     clientLogObj.sendLogs(logObj,{
19531                             error: callBack.error
19532                         });
19533                     };
19534 
19535                 userObj.getClientLog(options);
19536             }
19537         };
19538     }());
19539 
19540     window.finesse = window.finesse || {};
19541     window.finesse.cslogger = window.finesse.cslogger || {};
19542     /** @private */
19543     window.finesse.cslogger.FinesseLogger = FinesseLogger;
19544 
19545     return FinesseLogger;
19546 });
19547 
19548 /**
19549  *  Contains a list of topics used for containerservices pubsub.
19550  *
19551  */
19552 
19553 /**
19554  * @class
19555  * Contains a list of topics with some utility functions.
19556  */
19557 /** @private */
19558 define('containerservices/Topics',[], function () {
19559 
19560     var Topics = (function () {
19561 
19562     /**
19563      * The namespace prepended to all Finesse topics.
19564      */
19565     this.namespace = "finesse.containerservices";
19566 
19567     /**
19568      * @private
19569      * Gets the full topic name with the ContainerServices namespace prepended.
19570      * @param {String} topic
19571      *     The topic category.
19572      * @returns {String}
19573      *     The full topic name with prepended namespace.
19574      */
19575     var _getNSTopic = function (topic) {
19576         return this.namespace + "." + topic;
19577     };
19578 
19579 
19580 
19581     /** @scope finesse.containerservices.Topics */
19582     return {
19583         /** 
19584          * @private
19585          * request channel. */
19586         REQUESTS: _getNSTopic("requests"),
19587 
19588         /** 
19589          * @private
19590          * reload gadget channel. */
19591         RELOAD_GADGET: _getNSTopic("reloadGadget"),
19592 
19593         /**
19594          * @private
19595          * Convert a Finesse REST URI to a OpenAjax compatible topic name.
19596          */
19597         getTopic: function (restUri) {
19598             //The topic should not start with '/' else it will get replaced with
19599             //'.' which is invalid.
19600             //Thus, remove '/' if it is at the beginning of the string
19601             if (restUri.indexOf('/') === 0) {
19602                 restUri = restUri.substr(1);
19603             }
19604 
19605             //Replace every instance of "/" with ".". This is done to follow the
19606             //OpenAjaxHub topic name convention.
19607             return restUri.replace(/\//g, ".");
19608         }
19609     };
19610 	}());
19611 	
19612 	window.finesse = window.finesse || {};
19613     window.finesse.containerservices = window.finesse.containerservices || {};
19614     window.finesse.containerservices.Topics = Topics;
19615     
19616     /** @namespace JavaScript class objects and methods to handle gadget container services.*/
19617     finesse.containerservices = finesse.containerservices || {};
19618 
19619     return Topics;
19620  });
19621 
19622 /** The following comment is to prevent jslint errors about 
19623  * using variables before they are defined.
19624  */
19625 /*global finesse*/
19626 
19627 /**
19628  * Per containerservices request, publish to the OpenAjax gadget pubsub infrastructure.
19629  *
19630  * @requires OpenAjax, finesse.containerservices.Topics
19631  */
19632 
19633 /** @private */
19634 define('containerservices/MasterPublisher',[
19635     "utilities/Utilities",
19636     "containerservices/Topics"
19637 ],
19638 function (Utilities, Topics) {
19639 
19640     var MasterPublisher = function () {
19641 
19642     var
19643     
19644     /**
19645      * Reference to the gadget pubsub Hub instance.
19646      * @private
19647      */
19648     _hub = gadgets.Hub,
19649 
19650     /**
19651      * Reference to the Topics class.
19652      * @private
19653      */
19654     _topics = Topics,
19655     
19656     /**
19657      * Reference to conversion utilities class.
19658      * @private
19659      */
19660     _utils = Utilities,
19661     
19662     /**
19663      * References to ClientServices logger methods
19664      * @private
19665      */
19666     _logger = {
19667         log: finesse.clientservices.ClientServices.log
19668     },
19669     
19670    /**
19671      * The types of possible request types supported when listening to the
19672      * requests channel. Each request type could result in different operations.
19673      * @private
19674      */
19675     _REQTYPES = {
19676 		ACTIVETAB: "ActiveTabReq",
19677 		SET_ACTIVETAB: "SetActiveTabReq",
19678         RELOAD_GADGET: "ReloadGadgetReq"
19679     },
19680 
19681     /**
19682      * Handles client requests made to the request topic. The type of the
19683      * request is described in the "type" property within the data payload. Each
19684      * type can result in a different operation.
19685      * @param {String} topic
19686      *     The topic which data was published to.
19687      * @param {Object} data
19688      *     The data containing requests information published by clients.
19689      * @param {String} data.type
19690      *     The type of the request. Supported: "ActiveTabReq", "SetActiveTabReq", "ReloadGadgetReq"
19691      * @param {Object} data.data
19692      *     May contain data relevant for the particular requests.
19693      * @param {String} [data.invokeID]
19694      *     The ID used to identify the request with the response. The invoke ID
19695      *     will be included in the data in the publish to the topic. It is the
19696      *     responsibility of the client to correlate the published data to the
19697      *     request made by using the invoke ID.
19698      * @private
19699      */
19700     _clientRequestHandler = function (topic, data) {
19701     
19702         //Ensure a valid data object with "type" and "data" properties.
19703         if (typeof data === "object" &&
19704                 typeof data.type === "string" &&
19705                 typeof data.data === "object") {
19706 			switch (data.type) {
19707 			case _REQTYPES.ACTIVETAB:
19708                 _hub.publish("finesse.containerservices.activeTab", finesse.container.Tabs.getActiveTab());
19709                 break;
19710             case _REQTYPES.SET_ACTIVETAB:
19711                 if (typeof data.data.id === "string") {
19712                     _logger.log("Handling request to activate tab: " + data.data.id);
19713                     if (!finesse.container.Tabs.activateTab(data.data.id)) {
19714                         _logger.log("No tab found with id: " + data.data.id);
19715                     }
19716                 }
19717                 break;
19718             case _REQTYPES.RELOAD_GADGET:
19719                 _hub.publish("finesse.containerservices.reloadGadget", data.data);
19720                 break;
19721 			default:
19722 				break;
19723 			}
19724         }
19725     };
19726 
19727     (function () {
19728 
19729         //Listen to a request channel to respond to any requests made by other
19730         //clients because the Master may have access to useful information.
19731         _hub.subscribe(_topics.REQUESTS, _clientRequestHandler);
19732     }());
19733 
19734     //BEGIN TEST CODE//
19735     /**
19736      * Test code added to expose private functions that are used by unit test
19737      * framework. This section of code is removed during the build process
19738      * before packaging production code. The [begin|end]TestSection are used
19739      * by the build to identify the section to strip.
19740      * @ignore
19741      */
19742     this.beginTestSection = 0;
19743 
19744     /**
19745      * @ignore
19746      */
19747     this.getTestObject = function () {
19748         //Load mock dependencies.
19749         var _mock = new MockControl();
19750         _hub = _mock.createMock(gadgets.Hub);
19751 
19752         return {
19753             //Expose mock dependencies
19754             mock: _mock,
19755             hub: _hub,
19756 			
19757             //Expose internal private functions
19758             reqtypes: _REQTYPES,
19759             
19760             clientRequestHandler: _clientRequestHandler
19761 
19762         };
19763     };
19764 
19765 
19766     /**
19767      * @ignore
19768      */
19769     this.endTestSection = 0;
19770     //END TEST CODE//
19771 	};
19772 	
19773 	window.finesse = window.finesse || {};
19774     window.finesse.containerservices = window.finesse.containerservices || {};
19775     window.finesse.containerservices.MasterPublisher = MasterPublisher;
19776 	
19777     return MasterPublisher;
19778 });
19779 
19780 /**
19781  * JavaScript representation of the Finesse WorkflowActionEvent object.
19782  *
19783  * @requires finesse.FinesseBase
19784  */
19785 
19786 /** The following comment is to prevent jslint errors about 
19787  * using variables before they are defined.
19788  */
19789 /*global FinesseBase: true, publisher:true, define:true, finesse:true, window:true */
19790 /** @private */
19791 define('containerservices/WorkflowActionEvent', ["FinesseBase"], function (FinesseBase) {
19792     var WorkflowActionEvent = FinesseBase.extend(/** @lends finesse.containerservices.WorkflowActionEvent.prototype */{
19793         /**
19794          * Reference to the WorkflowActionEvent name
19795          * This will be set by setWorkflowActionEvent
19796          * @private
19797          */
19798         _name: null,
19799 
19800         /**
19801          * Reference to the WorkflowActionEvent type
19802          * This will be set by setWorkflowActionEvent
19803          * @private
19804          */
19805         _type: null,
19806 
19807         /**
19808          * Reference to the WorkflowActionEvent handledBy value
19809          * This will be set by setWorkflowActionEvent
19810          * @private
19811          */
19812         _handledBy: null,
19813 
19814         /**
19815          * Reference to the WorkflowActionEvent params array
19816          * This will be set by setWorkflowActionEvent
19817          * @private
19818          */
19819         _params: [],
19820 
19821         /**
19822          * Reference to the WorkflowActionEvent actionVariables array
19823          * This will be set by setWorkflowActionEvent
19824          * @private
19825          */            
19826         _actionVariables: [], 
19827         
19828         /**
19829          * @class
19830          * JavaScript representation of a WorkflowActionEvent object.
19831          * The WorkflowActionEvent object is delivered as the payload of
19832          * a WorkflowAction callback.  This can be subscribed to by using
19833          * {@link finesse.containerservices.ContainerServices#addHandler} with a 
19834          * topic of {@link finesse.containerservices.ContainerServices.Topics#WORKFLOW_ACTION_EVENT}. 
19835          * Gadgets should key on events with a handleBy value of "OTHER".
19836          * 
19837          * @constructs
19838          **/
19839         init: function () {
19840             this._super();
19841         },        
19842 
19843         /**
19844 	     * Validate that the passed in object is a WorkflowActionEvent object
19845 	     * and sets the variables if it is
19846 	     * @param maybeWorkflowActionEvent A possible WorkflowActionEvent object to be evaluated and set if 
19847 	     *                                 it validates successfully.
19848 	     * @returns {Boolean} Whether it is valid or not.
19849          * @private
19850 	     */
19851 	    setWorkflowActionEvent: function(maybeWorkflowActionEvent) {
19852 	        var returnValue;
19853 	
19854 	        if (maybeWorkflowActionEvent.hasOwnProperty("name") === true &&
19855 	                maybeWorkflowActionEvent.hasOwnProperty("type") === true &&
19856                     maybeWorkflowActionEvent.hasOwnProperty("handledBy") === true &&
19857 	                maybeWorkflowActionEvent.hasOwnProperty("params") === true &&
19858 	                maybeWorkflowActionEvent.hasOwnProperty("actionVariables") === true) {
19859 	            this._name = maybeWorkflowActionEvent.name;
19860 	            this._type = maybeWorkflowActionEvent.type;
19861                 this._handledBy = maybeWorkflowActionEvent.handledBy;
19862 	            this._params = maybeWorkflowActionEvent.params;
19863 	            this._actionVariables = maybeWorkflowActionEvent.actionVariables;
19864 	            returnValue = true;
19865 	        } else {
19866 	            returnValue = false;
19867 	        }
19868 	
19869 	        return returnValue;
19870 	    },
19871 	
19872 	    /**
19873 	     * Getter for the WorkflowActionEvent name.
19874 	     * @returns {String} The name of the WorkflowAction.
19875 	     */
19876 	    getName: function () {
19877 	        // escape nulls to empty string
19878 	        return this._name || "";
19879 	    },
19880 	
19881 	    /**
19882 	     * Getter for the WorkflowActionEvent type.
19883 	     * @returns {String} The type of the WorkflowAction (BROWSER_POP, HTTP_REQUEST).
19884 	     */
19885 	    getType: function () {
19886 	        // escape nulls to empty string
19887 	        return this._type || "";
19888 	    },
19889 	
19890         /**
19891          * Getter for the WorkflowActionEvent handledBy value. Gadgets should look for
19892          * events with a handleBy of "OTHER".
19893          * @see finesse.containerservices.WorkflowActionEvent.HandledBy
19894          * @returns {String} The handledBy value of the WorkflowAction that is a value of {@link finesse.containerservices.WorkflowActionEvent.HandledBy}.
19895          */
19896         getHandledBy: function () {
19897             // escape nulls to empty string
19898             return this._handledBy || "";
19899         },
19900 
19901 
19902 	    /**
19903 	     * Getter for the WorkflowActionEvent Params map.
19904 	     * @returns {Object} key = param name, value = Object{name, value, expandedValue}
19905 	     * BROWSER_POP<ul>
19906 	     * <li>windowName : Name of window to pop into, or blank to always open new window.
19907 	     * <li>path : URL to open.</ul>
19908 	     * HTTP_REQUEST<ul>
19909 	     * <li>method : "PUT" or "POST".
19910 	     * <li>location : "FINESSE" or "OTHER".
19911 	     * <li>contentType : MIME type of request body, if applicable, e.g. "text/plain".
19912 	     * <li>path : Request URL.
19913 	     * <li>body : Request content for POST requests.</ul>
19914 	     */
19915 	    getParams: function () {
19916 	        var map = {},
19917 	            params = this._params,
19918 	            i,
19919 	            param;
19920 	
19921 	        if (params === null || params.length === 0) {
19922 	            return map;
19923 	        }
19924 	
19925 	        for (i = 0; i < params.length; i += 1) {
19926 	            param = params[i];
19927 	            // escape nulls to empty string
19928 	            param.name = param.name || "";
19929 	            param.value = param.value || "";
19930 	            param.expandedValue = param.expandedValue || "";
19931 	            map[param.name] = param;
19932 	        }
19933 	
19934 	        return map;
19935 	    },
19936 	    
19937 	    /**
19938 	     * Getter for the WorkflowActionEvent ActionVariables map
19939 	     * @returns {Object} key = action variable name, value = Object{name, type, node, testValue, actualValue}
19940 	     */
19941 	    getActionVariables: function() {
19942 	        var map = {},
19943 	            actionVariables = this._actionVariables,
19944 	            i,
19945 	            actionVariable;
19946 	
19947 	        if (actionVariables === null || actionVariables.length === 0) {
19948 	            return map;
19949 	        }
19950 	
19951 	        for (i = 0; i < actionVariables.length; i += 1) {
19952 	            actionVariable = actionVariables[i];
19953 	            // escape nulls to empty string
19954 	            actionVariable.name = actionVariable.name || "";
19955 	            actionVariable.type = actionVariable.type || "";
19956 	            actionVariable.node = actionVariable.node || "";
19957 	            actionVariable.testValue = actionVariable.testValue || "";
19958 	            actionVariable.actualValue = actionVariable.actualValue || "";
19959 	            map[actionVariable.name] = actionVariable;
19960 	        }
19961 	
19962 	        return map;
19963 	    }
19964     }); 
19965     
19966     
19967     WorkflowActionEvent.HandledBy = /** @lends finesse.containerservices.WorkflowActionEvent.HandledBy.prototype */ {
19968         /**
19969          * This specifies that Finesse will handle this WorkflowActionEvent.  A 3rd Party can do additional processing
19970          * with the action, but first and foremost Finesse will handle this WorkflowAction.
19971          */
19972         FINESSE: "FINESSE",
19973 
19974         /**
19975          * This specifies that a 3rd Party will handle this WorkflowActionEvent.  Finesse's Workflow Engine Executor will 
19976          * ignore this action and expects Gadget Developers to take action.
19977          */
19978         OTHER: "OTHER",
19979         
19980         /**
19981          * @class This is the set of possible HandledBy values used for WorkflowActionEvent from ContainerServices.  This
19982          * is provided from the {@link finesse.containerservices.WorkflowActionEvent#getHandledBy} method.
19983          * @constructs
19984          */
19985         _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
19986     };    
19987     
19988     window.finesse = window.finesse || {};
19989     window.finesse.containerservices = window.finesse.containerservices || {};
19990     window.finesse.containerservices.WorkflowActionEvent = WorkflowActionEvent;
19991     
19992     return WorkflowActionEvent;
19993 });
19994 
19995 /**
19996  * JavaScript representation of the Finesse TimerTickEvent
19997  *
19998  * @requires finesse.FinesseBase
19999  */
20000 
20001 /** The following comment is to prevent jslint errors about 
20002  * using variables before they are defined.
20003  */
20004 /*global FinesseBase: true, publisher:true, define:true, finesse:true, window:true */
20005 /** @private */
20006 define('containerservices/TimerTickEvent',[
20007     "FinesseBase"
20008 ],
20009 function (FinesseBase) {
20010     var TimerTickEvent = FinesseBase.extend(/** @lends finesse.containerservices.TimerTickEvent.prototype */{
20011         /**
20012          * date the TimerTickEvent was queued 
20013          * @private
20014          */
20015         _dateQueued: null,
20016 
20017         /**
20018          * the frequency of the timer tick (in miiliseconds)
20019          * @private
20020          */
20021         _tickFrequency: 1000,
20022 
20023         /**
20024          * @class
20025          * JavaScript representation of a TimerTickEvent object.
20026          * The TimerTickEvent object is delivered as the payload of
20027          * a TimerTickEvent callback.  This can be subscribed to by using
20028          * {@link finesse.containerservices.ContainerServices#addHandler} with a 
20029          * topic of {@link finesse.containerservices.ContainerServices.Topics#TIMER_TICK_EVENT}. 
20030          * 
20031          * @constructs
20032          **/
20033         init: function (tickFrequency, dateQueued) {
20034             this._super();
20035             
20036             this._tickFrequency = tickFrequency;
20037             this._dateQueued = dateQueued;
20038         },
20039 
20040        /**
20041          * Get the "tickFrequency" field
20042          * @param {int} which is the "TickFrequency" field
20043          * @private
20044          */
20045         getTickFrequency: function () {
20046             return this._tickFrequency;
20047         },
20048 
20049         /**
20050          * Getter for the TimerTickEvent "DateQueued" field. 
20051          * @returns {Date} which is a Date object when the TimerTickEvent was queued
20052          */
20053         getDateQueued: function () {
20054             return this._dateQueued;
20055         }
20056 
20057     });
20058     
20059     window.finesse = window.finesse || {};
20060     window.finesse.containerservices = window.finesse.containerservices || {};
20061     window.finesse.containerservices.TimerTickEvent = TimerTickEvent;
20062     
20063     return TimerTickEvent;
20064 });
20065 
20066 /**
20067  * JavaScript representation of the Finesse GadgetViewChangedEvent object.
20068  *
20069  * @requires finesse.FinesseBase
20070  */
20071 
20072 /** The following comment is to prevent jslint errors about 
20073  * using variables before they are defined.
20074  */
20075 /*global FinesseBase: true, publisher:true, define:true, finesse:true, window:true */
20076 /** @private */
20077 define('containerservices/GadgetViewChangedEvent',[
20078     "FinesseBase"
20079 ],
20080 function (FinesseBase) {
20081     var GadgetViewChangedEvent = FinesseBase.extend(/** @lends finesse.containerservices.GadgetViewChangedEvent.prototype */{
20082         /**
20083          * Reference to the gadget id
20084          * @private
20085          */
20086         _gadgetId: null,
20087 
20088         /**
20089          * Reference to the tab id
20090          * @private
20091          */
20092         _tabId: null,
20093 
20094         /**
20095          * Reference to the maxAvailableHeight
20096          * @private
20097          */
20098         _maxAvailableHeight: null,
20099 
20100         /**
20101          * Reference to the view
20102          * E.g. 'default' or 'canvas'
20103          * @private
20104          */
20105         _view: null,
20106         
20107         /**
20108          * @class
20109          * JavaScript representation of a GadgetViewChangedEvent object.
20110          * The GadgetViewChangedEvent object is delivered as the payload of
20111          * a GadgetViewChangedEvent callback.  This can be subscribed to by using
20112          * {@link finesse.containerservices.ContainerServices#addHandler} with a 
20113          * topic of {@link finesse.containerservices.ContainerServices.Topics#GADGET_VIEW_CHANGED_EVENT}. 
20114          * 
20115          * @constructs
20116          **/
20117         init: function (gadgetId, tabId, maxAvailableHeight, view) {
20118             this._super();
20119 
20120             this._gadgetId = gadgetId;
20121             this._tabId = tabId;
20122             this._maxAvailableHeight = maxAvailableHeight;
20123             this._view = view;
20124         },
20125     
20126         /**
20127          * Getter for the gadget id.
20128          * @returns {String} The identifier for the gadget changing view.
20129          */
20130         getGadgetId: function () {
20131             // escape nulls to empty string
20132             return this._gadgetId || "";
20133         },
20134     
20135         /**
20136          * Getter for the maximum available height.
20137          * @returns {String} The maximum available height for the gadget's view.
20138          */
20139         getMaxAvailableHeight: function () {
20140             // escape nulls to empty string
20141             return this._maxAvailableHeight || "";
20142         },
20143 
20144         /**
20145          * Getter for the tab id.
20146          * @returns {String} The identifier for the tab where the gadget changing view resides.
20147          */
20148         getTabId: function () {
20149             // escape nulls to empty string
20150             return this._tabId || "";
20151         },
20152 
20153         /**
20154          * Getter for the view.
20155          * @returns {String} The view type the gadget is changing to.
20156          */
20157         getView: function () {
20158             // escape nulls to empty string
20159             return this._view || "";
20160         }
20161     });
20162     
20163     window.finesse = window.finesse || {};
20164     window.finesse.containerservices = window.finesse.containerservices || {};
20165     window.finesse.containerservices.GadgetViewChangedEvent = GadgetViewChangedEvent;
20166     
20167     return GadgetViewChangedEvent;
20168 });
20169 
20170 /**
20171  * JavaScript representation of the Finesse MaxAvailableHeightChangedEvent object.
20172  *
20173  * @requires finesse.FinesseBase
20174  */
20175 
20176 /** The following comment is to prevent jslint errors about 
20177  * using variables before they are defined.
20178  */
20179 /*global FinesseBase: true, publisher:true, define:true, finesse:true, window:true */
20180 /** @private */
20181 define('containerservices/MaxAvailableHeightChangedEvent',[
20182     "FinesseBase"
20183 ],
20184 function (FinesseBase) {
20185     var MaxAvailableHeightChangedEvent = FinesseBase.extend(/** @lends finesse.containerservices.MaxAvailableHeightChangedEvent.prototype */{
20186 
20187         /**
20188          * Reference to the maxAvailableHeight
20189          * @private
20190          */
20191         _maxAvailableHeight: null,
20192         
20193         /**
20194          * @class
20195          * JavaScript representation of a MaxAvailableHeightChangedEvent object.
20196          * The MaxAvailableHeightChangedEvent object is delivered as the payload of
20197          * a MaxAvailableHeightChangedEvent callback.  This can be subscribed to by using
20198          * {@link finesse.containerservices.ContainerServices#addHandler} with a 
20199          * topic of {@link finesse.containerservices.ContainerServices.Topics#MAX_AVAILABLE_HEIGHT_CHANGED_EVENT}. 
20200          * 
20201          * @constructs
20202          **/
20203         init: function (maxAvailableHeight) {
20204             this._super();
20205 
20206             this._maxAvailableHeight = maxAvailableHeight;
20207         },
20208     
20209         /**
20210          * Getter for the maximum available height.
20211          * @returns {String} The maximum available height for a gadget in canvas view
20212          */
20213         getMaxAvailableHeight: function () {
20214             // escape nulls to empty string
20215             return this._maxAvailableHeight || "";
20216         }
20217     });
20218     
20219     window.finesse = window.finesse || {};
20220     window.finesse.containerservices = window.finesse.containerservices || {};
20221     window.finesse.containerservices.MaxAvailableHeightChangedEvent = MaxAvailableHeightChangedEvent;
20222     
20223     return MaxAvailableHeightChangedEvent;
20224 });
20225 
20226 /**
20227  * Exposes a set of API wrappers that will hide the dirty work of
20228  *     constructing Finesse API requests and consuming Finesse events.
20229  *
20230  * @requires OpenAjax, jQuery 1.5, finesse.utilities.Utilities
20231  */
20232 
20233 /** The following comment is to prevent jslint errors about using variables before they are defined. */
20234 /*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 */
20235 /*jslint nomen: true, unparam: true, sloppy: true, white: true */
20236 /** @private */
20237 define('containerservices/ContainerServices',[
20238     "utilities/Utilities",
20239     "restservices/Notifier",
20240     "containerservices/Topics",
20241     "containerservices/MasterPublisher",
20242     "containerservices/WorkflowActionEvent",
20243     "containerservices/TimerTickEvent",
20244     "containerservices/GadgetViewChangedEvent",
20245     "containerservices/MaxAvailableHeightChangedEvent"
20246 ],
20247 function (Utilities, Notifier, Topics, MasterPublisher, WorkflowActionEvent) {
20248 
20249     var ContainerServices = ( function () { /** @lends finesse.containerservices.ContainerServices.prototype */
20250 
20251     var
20252 
20253     /**
20254      * Shortcut reference to the Utilities singleton
20255      * This will be set by init()
20256      * @private
20257      */
20258     _util,
20259 
20260     /**
20261      * Shortcut reference to the gadget pubsub Hub instance.
20262      * This will be set by init()
20263      * @private
20264      */
20265     _hub,
20266 
20267     /**
20268      * Boolean whether this instance is master or not
20269      * @private
20270      */
20271     _master = false,
20272 
20273     /**
20274      * Whether the Client Services have been initiated yet.
20275      * @private
20276      */
20277     _inited = false,
20278     
20279     /**
20280      * References to ClientServices logger methods
20281      * @private
20282      */
20283     _logger = {
20284         log: finesse.clientservices.ClientServices.log
20285     },
20286     
20287      /**
20288      * Stores the list of subscription IDs for all subscriptions so that it
20289      * could be retrieve for unsubscriptions.
20290      * @private
20291      */
20292     _subscriptionID = {},
20293     
20294     /**
20295      * Reference to the gadget's parent container
20296      * @private
20297      */
20298     _container,
20299 
20300     /**
20301      * Reference to the MasterPublisher
20302      * @private
20303      */
20304     _publisher,
20305     
20306     /**
20307      * Object that will contain the Notifiers
20308      * @private
20309      */
20310     _notifiers = {},
20311 
20312     /**
20313      * Reference to the tabId that is associated with the gadget
20314      * @private
20315      */
20316     _myTab = null,
20317     
20318     /**
20319      * Reference to the visibility of current gadget
20320      * @private
20321      */
20322     _visible = false,
20323     
20324     /**
20325      * Reference for auth modes constants.
20326      * @private
20327      */
20328     _authModes,
20329     
20330     /**
20331      * Shortcut reference to the Topics class.
20332      * This will be set by init()
20333      * @private
20334      */
20335     _topics,
20336 
20337     /**
20338      * Associates a topic name with the private handler function.
20339      * Adding a new topic requires that you add this association here 
20340      *  in to keep addHandler generic.
20341      * @param {String} topic : Specifies the callback to retrieve
20342      * @return {Function} The callback function associated with the topic param.
20343      * @private
20344      */
20345     _topicCallback = function (topic) {
20346         var callback, notifier;
20347         switch (topic)
20348         {
20349             case finesse.containerservices.ContainerServices.Topics.ACTIVE_TAB:
20350                 callback = _tabTracker;
20351                 break;
20352             case finesse.containerservices.ContainerServices.Topics.WORKFLOW_ACTION_EVENT:
20353                 callback = _workflowActionEventTracker;
20354                 break;
20355             case finesse.containerservices.ContainerServices.Topics.RELOAD_GADGET_EVENT:
20356                 callback = _masterReloader;
20357                 break;
20358             case finesse.containerservices.ContainerServices.Topics.GADGET_VIEW_CHANGED_EVENT:
20359                 callback = _gadgetViewChanged;
20360                 break;
20361             case finesse.containerservices.ContainerServices.Topics.MAX_AVAILABLE_HEIGHT_CHANGED_EVENT:
20362                 callback = _maxAvailableHeightChanged;
20363                 break;
20364             case finesse.containerservices.ContainerServices.Topics.ACCESS_TOKEN_REFRESHED_EVENT:
20365                 callback = _accessTokenRefreshed;
20366                 break;
20367             default:
20368                 callback = function (param) {
20369                      var data = null;
20370                      
20371                      notifier = _getNotifierReference(topic);
20372                      
20373                      if (arguments.length === 1) {
20374                         data = param;
20375                      } else {
20376                         data = arguments;
20377                      }
20378                      notifier.notifyListeners(data);
20379                 };
20380         }
20381         return callback;
20382     },
20383 
20384     /**
20385      * Ensure that ClientServices have been inited.
20386      * @private
20387      */
20388     _isInited = function () {
20389         if (!_inited) {
20390             throw new Error("ContainerServices needs to be inited.");
20391         }
20392     },
20393 
20394     /**
20395      * Retrieves a Notifier reference to a particular topic, and creates one if it doesn't exist.
20396      * @param {String} topic : Specifies the notifier to retrieve
20397      * @return {Notifier} The notifier object.
20398      * @private
20399      */
20400     _getNotifierReference = function (topic) {
20401         if (!_notifiers.hasOwnProperty(topic))
20402         {
20403             _notifiers[topic] = new Notifier();
20404         }
20405 
20406         return _notifiers[topic];
20407     },
20408 
20409     /**
20410      * Utility function to make a subscription to a particular topic. Only one
20411      * callback function is registered to a particular topic at any time.
20412      * @param {String} topic
20413      *     The full topic name. The topic name should follow the OpenAjax
20414      *     convention using dot notation (ex: finesse.api.User.1000).
20415      * @param {Function} callback
20416      *     The function that should be invoked with the data when an event
20417      *     is delivered to the specific topic.
20418      * @returns {Boolean}
20419      *     True if the subscription was made successfully and the callback was
20420      *     been registered. False if the subscription already exist, the
20421      *     callback was not overwritten.
20422      * @private
20423      */
20424     _subscribe = function (topic, callback) {
20425         _isInited();
20426 
20427         //Ensure that the same subscription isn't made twice.
20428         if (!_subscriptionID[topic]) {
20429             //Store the subscription ID using the topic name as the key.
20430             _subscriptionID[topic] = _hub.subscribe(topic,
20431                 //Invoke the callback just with the data object.
20432                 function (topic, data) {
20433                     callback(data);
20434                 });
20435             return true;
20436         }
20437         return false;
20438     },
20439 
20440     /**
20441      * Unsubscribe from a particular topic.
20442      * @param {String} topic : The full topic name.
20443      * @private
20444      */
20445     _unsubscribe = function (topic) {
20446         _isInited();
20447 
20448         //Unsubscribe from the topic using the subscription ID recorded when
20449         //the subscription was made, then delete the ID from data structure.
20450         _hub.unsubscribe(_subscriptionID[topic]);
20451         delete _subscriptionID[topic];
20452     },
20453 
20454     /**
20455      * Get my tab id.
20456      * @returns {String} tabid : The tabid of this container/gadget.
20457      * @private
20458      */
20459     _getMyTab = function () {
20460         if (_myTab === null)
20461         {
20462             try {
20463             _myTab = $(frameElement).closest("div.tab-panel").attr("id").replace("panel_", "");
20464             } catch (err) {
20465                 _logger.log("Error accessing current tab: " + err.message);
20466                _myTab = null;
20467             }
20468         }
20469         return _myTab;
20470     },
20471     
20472     /**
20473      * Callback function that is called when an activeTab message is posted to the Hub.
20474      * Notifies listener functions if this tab is the one that was just made active.
20475      * @param {String} tabId : The tabId which was just made visible.
20476      * @private
20477      */
20478     _tabTracker = function(tabId) {
20479         if (tabId === _getMyTab()) {
20480             if(!_visible) {
20481                 _visible = true;
20482                 _notifiers[finesse.containerservices.ContainerServices.Topics.ACTIVE_TAB].notifyListeners(this);
20483             }
20484         } else {
20485             _visible = false;
20486         }
20487     },
20488     
20489     /**
20490      * Make a request to set a particular tab active. This
20491      * method should be called after {@link finesse.containerservices.ContainerServices#addHandler}
20492      * to ensure the gadget gets properly initialized.
20493      * @param {String} tabId
20494      *    The tabId (not the label text) of the tab to make active.  If the id is invalid, no action will occur.
20495      * @private
20496      */
20497     _activateTab = function ( tabId ) {
20498         _logger.log("Sending request to activate tab: " + tabId);
20499         if(_hub){
20500             var data = {
20501                 type: "SetActiveTabReq",
20502                 data: { id: tabId },
20503                 invokeID: (new Date()).getTime()          
20504             };
20505             _hub.publish(_topics.REQUESTS, data);
20506         } else {
20507             throw new Error("Hub is not defined.");
20508         }
20509         
20510     },
20511 
20512     /**
20513      * Callback function that is called when a gadget view changed message is posted to the Hub.
20514      * @private
20515      */
20516     _gadgetViewChanged = function (data) {
20517         if (data) {
20518             var gadgetViewChangedEvent = new finesse.containerservices.GadgetViewChangedEvent(
20519                 data.gadgetId,
20520                 data.tabId,
20521                 data.maxAvailableHeight,
20522                 data.view);
20523 
20524             _notifiers[finesse.containerservices.ContainerServices.Topics.GADGET_VIEW_CHANGED_EVENT].notifyListeners(gadgetViewChangedEvent);
20525         }
20526     },
20527 
20528     /**
20529      * Callback function that is called when a max available height changed message is posted to the Hub.
20530      * @private
20531      */
20532     _maxAvailableHeightChanged = function (data) {
20533         if (data) {
20534             var maxAvailableHeightChangedEvent = new finesse.containerservices.MaxAvailableHeightChangedEvent(
20535                 data.maxAvailableHeight);
20536 
20537             _notifiers[finesse.containerservices.ContainerServices.Topics.MAX_AVAILABLE_HEIGHT_CHANGED_EVENT].notifyListeners(maxAvailableHeightChangedEvent);
20538         }
20539     },
20540 
20541     /**
20542      * Callback function that is called when a workflowActionEvent message is posted to the Hub.
20543      * Notifies listener functions if the posted object can be converted to a proper WorkflowActionEvent object.
20544      * @param {String} workflowActionEvent : The workflowActionEvent that was posted to the Hub
20545      * @private
20546      */
20547     _workflowActionEventTracker = function(workflowActionEvent) {
20548         var vWorkflowActionEvent = new finesse.containerservices.WorkflowActionEvent();
20549                 
20550         if (vWorkflowActionEvent.setWorkflowActionEvent(workflowActionEvent)) {
20551             _notifiers[finesse.containerservices.ContainerServices.Topics.WORKFLOW_ACTION_EVENT].notifyListeners(vWorkflowActionEvent);
20552         }
20553         // else
20554         // {
20555             //?console.log("Error in ContainerServices : _workflowActionEventTracker - could not map published HUB object to WorkflowActionEvent");
20556         // }
20557 
20558     },
20559 
20560     /**
20561      * Callback function that is called when a reloadGadget event message is posted to the Hub.
20562      *
20563      * Grabs the id of the gadget we want to reload from the data and reload it!
20564      *
20565      * @param {String} topic
20566      *      which topic the event came on (unused)
20567      * @param {Object} data
20568      *      the data published with the event
20569      * @private
20570      */
20571     _masterReloader = function (topic, data) {
20572         var gadgetId = data.gadgetId;
20573         if (gadgetId) {
20574             _container.reloadGadget(gadgetId);
20575         }
20576     },
20577     
20578     /**
20579      * Pulls the gadget id from the url parameters
20580      * @return {String} id of the gadget
20581      * @private
20582      */
20583     _findMyGadgetId = function () {
20584         if (gadgets && gadgets.util && gadgets.util.getUrlParameters()) {
20585             return gadgets.util.getUrlParameters().mid;
20586         }
20587     };
20588 
20589     return {
20590         /**
20591          * @class
20592          * This class provides container-level services for gadget developers, exposing container events by
20593          * calling a set of exposed functions. Gadgets can utilize the container dialogs and 
20594          * event handling (add/remove).
20595          * @example
20596          *    containerServices = finesse.containerservices.ContainerServices.init();
20597          *    containerServices.addHandler(
20598          *      finesse.containerservices.ContainerServices.Topics.ACTIVE_TAB, 
20599          *      function() {
20600          *          clientLogs.log("Gadget is now visible");  // log to Finesse logger
20601          *          // automatically adjust the height of the gadget to show the html
20602          *          gadgets.window.adjustHeight();
20603          *      });
20604          *    containerServices.makeActiveTabReq();
20605          *    
20606          * @constructs
20607          */
20608         _fakeConstuctor: function () {
20609             /* This is here so we can document init() as a method rather than as a constructor. */
20610         },
20611         
20612         /**
20613          * Initialize ContainerServices for use in gadget.
20614          * @param {Boolean} [master=false] Do not use this parameter from your gadget.
20615          * @returns ContainerServices instance.
20616          */
20617         init: function (master) {
20618             if (!_inited) {
20619                 _inited = true;
20620                 // Set shortcuts
20621                 _util = Utilities;
20622                 _authModes = _util.getAuthModes();
20623 
20624                 //init the hub only when it's available
20625                 if(gadgets.Hub) {
20626                     _hub = gadgets.Hub;
20627                 }
20628 
20629                 if(Topics) {
20630                     _topics = Topics;
20631                 }
20632 
20633                 if (master) {
20634                     _master = true;
20635                     _container = finesse.container.Container;
20636                     _publisher = new MasterPublisher();
20637 
20638                     // subscribe for reloading gadget events
20639                     // we only want the master ContainerServices handling these events
20640                     _hub.subscribe(_topics.RELOAD_GADGET, _topicCallback(_topics.RELOAD_GADGET));
20641                 } else {
20642                     _container = parent.finesse.container.Container;
20643                 }
20644             }
20645             
20646             this.makeActiveTabReq();
20647             
20648             //Return the CS object for object chaining.
20649             return this;
20650         },
20651 
20652         /**
20653          * Shows the jQuery UI Dialog with the specified parameters. The following are the
20654          * default parameters: <ul>
20655          *     <li> Title of "Cisco Finesse".</li>
20656          *     <li>Message of "A generic error has occured".</li>
20657          *     <li>The only button, "Ok", closes the dialog.</li>
20658          *     <li>Modal (blocks other dialogs).</li>
20659          *     <li>Not draggable.</li>
20660          *     <li>Fixed size.</li></ul>
20661          * @param {Object} options
20662          *  An object containing additional options for the dialog.
20663          * @param {String/Boolean} options.title
20664          *  Title to use. undefined defaults to "Cisco Finesse". false to hide
20665          * @param {Function} options.close
20666          *  A function to invoke when the dialog is closed.
20667          * @param {String} options.message
20668          *  The message to display in the dialog.
20669          *  Defaults to "A generic error has occurred."
20670          * @param {Boolean} options.isBlocking
20671          *  Flag indicating whether this dialog will block other dialogs from being shown (Modal).
20672          * @returns {jQuery} JQuery wrapped object of the dialog DOM element.
20673          * @see finesse.containerservices.ContainerServices#hideDialog
20674          */
20675         showDialog: function(options) {
20676             if ((_container.showDialog !== undefined) && (_container.showDialog !== this.showDialog)) {
20677                 return _container.showDialog(options);
20678             }
20679         },
20680         
20681         /**
20682          * Hides the jQuery UI Dialog.
20683          * @returns {jQuery} jQuery wrapped object of the dialog DOM element
20684          * @see finesse.containerservices.ContainerServices#showDialog
20685          */
20686         hideDialog: function() {
20687             if ((_container.hideDialog !== undefined) && (_container.hideDialog !== this.hideDialog)) {
20688                 return _container.hideDialog();
20689             }
20690         },
20691 
20692         /**
20693          *  Reloads the current gadget. 
20694          *  For use from within a gadget only.
20695          */
20696         reloadMyGadget: function () {
20697             var topic, gadgetId, data;
20698 
20699             if (!_master) {
20700                 // first unsubscribe this gadget from all topics on the hub
20701                 for (topic in _notifiers) {
20702                     if (_notifiers.hasOwnProperty(topic)) {
20703                         _unsubscribe(topic);
20704                         delete _notifiers[topic];
20705                     }
20706                 }
20707 
20708                 // send an asynch request to the hub to tell the master container
20709                 // services that we want to refresh this gadget
20710                 gadgetId = _findMyGadgetId();
20711                 data = {
20712                     type: "ReloadGadgetReq",
20713                     data: {gadgetId: gadgetId},
20714                     invokeID: (new Date()).getTime()          
20715                 };
20716                 _hub.publish(_topics.REQUESTS, data);
20717             }            
20718         },
20719 
20720         /**
20721          * Updates the url for this gadget and then reload it.
20722          * 
20723          * This allows the gadget to be reloaded from a different location
20724          * than what is uploaded to the current server. For example, this
20725          * would be useful for 3rd party gadgets to implement their own failover
20726          * mechanisms.
20727          *
20728          * For use from within a gadget only.
20729          *
20730          * @param {String} url
20731          *      url from which to reload gadget
20732          */
20733         reloadMyGadgetFromUrl: function (url) {
20734             if (!_master) {
20735                 var gadgetId = _findMyGadgetId();
20736 
20737                 // update the url in the container
20738                 _container.modifyGadgetUrl(gadgetId, url);
20739 
20740                 // reload it
20741                 this.reloadMyGadget();
20742             }
20743         },
20744         
20745         /**
20746          * Adds a handler for one of the supported topics provided by ContainerServices.  The callbacks provided
20747          * will be invoked when that topic is notified.  
20748          * @param {String} topic
20749          *  The Hub topic to which we are listening.
20750          * @param {Function} callback
20751          *  The callback function to invoke.
20752          * @see finesse.containerservices.ContainerServices.Topics
20753          * @see finesse.containerservices.ContainerServices#removeHandler
20754          */
20755         addHandler: function (topic, callback) {
20756             _isInited();
20757             var notifier = null;
20758             
20759             try {    
20760                 // For backwards compatibility...
20761                 if (topic === "tabVisible") {
20762                     if (window.console && typeof window.console.log === "function") {
20763                         window.console.log("WARNING - Using tabVisible as topic.  This is deprecated.  Use finesse.containerservices.ContainerServices.Topics.ACTIVE_TAB now!");
20764                     }
20765                     
20766                     topic = finesse.containerservices.ContainerServices.Topics.ACTIVE_TAB;
20767                 }
20768                 
20769                 // Add the callback to the notifier.
20770                 _util.validateHandler(callback);
20771             
20772                 notifier = _getNotifierReference(topic);
20773             
20774                 notifier.addListener(callback);
20775             
20776                 // Subscribe to the topic. _subscribe ensures that a topic is only subscribed to once,
20777                 // so attempt to subscribe each time a handler is added. This ensures that a topic is subscribed
20778                 // to only when necessary.
20779                 _subscribe(topic, _topicCallback(topic));
20780             
20781             } catch (err) {
20782                 throw new Error("addHandler(): " + err);
20783             }
20784         }, 
20785         
20786         /**
20787          * Removes a previously-added handler for one of the supported topics.
20788          * @param {String} topic
20789          *  The Hub topic from which we are removing the callback.
20790          * @param {Function} callback
20791          *  The name of the callback function to remove.
20792          * @see finesse.containerservices.ContainerServices.Topics
20793          * @see finesse.containerservices.ContainerServices#addHandler
20794          */
20795         removeHandler: function(topic, callback) {
20796             var notifier = null;
20797             
20798             try {
20799                 _util.validateHandler(callback);
20800     
20801                 notifier = _getNotifierReference(topic);
20802     
20803                 notifier.removeListener(callback);
20804             } catch (err) {
20805                 throw new Error("removeHandler(): " + err);
20806             }
20807         },
20808         
20809         /**
20810          * Wrapper API for publishing data on the Openajax hub
20811          * @param {String} topic
20812          *  The Hub topic to which we are publishing.
20813          * @param {Object} data
20814          *  The data to be published on the hub.
20815          */
20816         publish : function(topic , data){
20817             if(_hub){
20818                 _hub.publish(topic, data);
20819             } else {
20820                 throw new Error("Hub is not defined.");
20821             }
20822         },
20823 
20824         /**
20825          * Returns the visibility of current gadget.  Note that this 
20826          * will not be set until after the initialization of the gadget.
20827          * @return {Boolean} The visibility of current gadget.
20828          */
20829         tabVisible: function(){
20830             return _visible;
20831         },
20832         
20833         /**
20834          * Make a request to check the current tab.  The 
20835          * activeTab event will be invoked if on the active tab.  This
20836          * method should be called after {@link finesse.containerservices.ContainerServices#addHandler}
20837          * to ensure the gadget gets properly initialized.
20838          */
20839         makeActiveTabReq : function () {
20840             if(_hub){
20841                 var data = {
20842                     type: "ActiveTabReq",
20843                     data: {},
20844                     invokeID: (new Date()).getTime()          
20845                 };
20846                 _hub.publish(_topics.REQUESTS, data);
20847             } else {
20848                 throw new Error("Hub is not defined.");
20849             }
20850             
20851         },
20852 
20853         /**
20854          * Make a request to set a particular tab active. This
20855          * method should be called after {@link finesse.containerservices.ContainerServices#addHandler}
20856          * to ensure the gadget gets properly initialized.
20857          * @param {String} tabId
20858          *    The tabId (not the label text) of the tab to make active.  If the id is invalid, no action will occur.
20859          */
20860         activateTab : function (tabId) {
20861             _activateTab(tabId);
20862         },
20863         
20864         /**
20865          * Make a request to set this container's tab active. This
20866          * method should be called after {@link finesse.containerservices.ContainerServices#addHandler}
20867          * to ensure the gadget gets properly initialized.
20868          */
20869         activateMyTab : function () {
20870             _activateTab( _getMyTab() );
20871         },
20872         
20873         /**
20874          * Get the tabId of my container/gadget.
20875          * @returns {String} tabid : The tabid of this container/gadget.
20876          */
20877         getMyTabId : function () {
20878             return _getMyTab();
20879         },
20880 
20881         /**
20882          * Gets the id of the gadget.
20883          * @returns {number} the id of the gadget
20884          */
20885         getMyGadgetId : function () {
20886             return _findMyGadgetId();
20887         },
20888 
20889         //BEGIN TEST CODE//
20890         /**
20891          * Test code added to expose private functions that are used by unit test
20892          * framework. This section of code is removed during the build process
20893          * before packaging production code. The [begin|end]TestSection are used
20894          * by the build to identify the section to strip.
20895          * @ignore
20896          */
20897         beginTestSection : 0,
20898 
20899         /**
20900          * @ignore
20901          */
20902         getTestObject: function () {
20903             //Load mock dependencies.
20904             var _mock = new MockControl();
20905             _util = _mock.createMock(Utilities);
20906             _hub = _mock.createMock(gadgets.Hub);
20907             _inited = true;
20908             return {
20909                 //Expose mock dependencies
20910                 mock: _mock,
20911                 hub: _hub,
20912                 util: _util,
20913                 addHandler: this.addHandler,
20914                 removeHandler: this.removeHandler
20915             };
20916         },
20917 
20918         /**
20919          * @ignore
20920          */
20921        endTestSection: 0
20922         //END TEST CODE//
20923     };
20924     }());
20925     
20926     ContainerServices.Topics = /** @lends finesse.containerservices.ContainerServices.Topics.prototype */ {
20927         /**
20928          * Topic for subscribing to be notified when the active tab changes.
20929          * The provided callback will be invoked when the tab that the gadget 
20930          * that subscribes with this becomes active.  To ensure code is called
20931          * when the gadget is already on the active tab use the 
20932          * {@link finesse.containerservices.ContainerServices#makeActiveTabReq}
20933          * method.
20934          */
20935         ACTIVE_TAB: "finesse.containerservices.activeTab",
20936 
20937         /**
20938          * Topic for WorkflowAction events traffic.
20939          * The provided callback will be invoked when a WorkflowAction needs
20940          * to be handled.  The callback will be passed a {@link finesse.containerservices.WorkflowActionEvent}
20941          * that can be used to interrogate the WorkflowAction and determine to use or not.
20942          */
20943         WORKFLOW_ACTION_EVENT: "finesse.containerservices.workflowActionEvent",
20944         
20945         /**
20946          * Topic for Timer Tick event.
20947          * The provided callback will be invoked when this event is fired.
20948          * The callback will be passed a {@link finesse.containerservices.TimerTickEvent}.
20949          */
20950         TIMER_TICK_EVENT : "finesse.containerservices.timerTickEvent",
20951 
20952         /**
20953          * Topic for Reload Gadget events traffic.
20954          * Only the master ContainerServices instance will handle this event.
20955          */
20956         RELOAD_GADGET_EVENT: "finesse.containerservices.reloadGadget",
20957         
20958         /**
20959          * Topic for listening to gadget view changed events.
20960          * The provided callback will be invoked when a gadget changes view.
20961          * The callback will be passed a {@link finesse.containerservices.GadgetViewChangedEvent}.
20962          */
20963         GADGET_VIEW_CHANGED_EVENT: "finesse.containerservices.gadgetViewChangedEvent",
20964 
20965         /**
20966          * Topic for listening to max available height changed events.
20967          * The provided callback will be invoked when the maximum height available to a maximized gadget changes.
20968          * This event is only meant for maximized gadgets and will not be published unless a maximized gadget exists.
20969          * The callback will be passed a {@link finesse.containerservices.MaxAvailableHeightChangedEvent}.
20970          */
20971         MAX_AVAILABLE_HEIGHT_CHANGED_EVENT: "finesse.containerservices.maxAvailableHeightChangedEvent",
20972         
20973         /**
20974          * @class This is the set of Topics used for subscribing for events from ContainerServices.
20975          * Use {@link finesse.containerservices.ContainerServices#addHandler} to subscribe to the topic.
20976          * 
20977          * @constructs
20978          */
20979         _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
20980     };
20981     
20982     window.finesse = window.finesse || {};
20983     window.finesse.containerservices = window.finesse.containerservices || {};
20984     window.finesse.containerservices.ContainerServices = ContainerServices;
20985 
20986     return ContainerServices;
20987  });
20988 /**
20989  * FinesseToaster is a utility class to show toaster notification in Finesse.
20990  * FinesseToaster leverages HTML5 Notification API to display Toaster
20991  * Notification.
20992  * 
20993  */
20994 
20995 define('containerservices/FinesseToaster',[],function() {
20996 
20997 	var FinesseToaster = (function() {
20998 		/** @lends finesse.containerservices.FinesseToaster.prototype */
20999 
21000 		var
21001 
21002 		/** How long the toaster will be displayed by default. Default timeout is 8 seconds */
21003 		AUTO_CLOSE_TIME = 8000,
21004 
21005 		/** PERMISSION_GRANTED constant for granted string */
21006 		PERMISSION_GRANTED = 'granted',
21007 
21008 		/** PERMISSION_DEFAULT constant for default string */
21009 		PERMISSION_DEFAULT = 'default',
21010 
21011 		/** PERMISSION_DENIED constant for denied string */
21012 		PERMISSION_DENIED = 'denied',
21013 
21014 		/** ICON_PATH constant for holding path icon images */
21015 		ICON_PATH = '/desktop/theme/finesse/images/modules/',
21016 
21017 		/**
21018 		 * Shortcut reference to finesse.cslogger.ClientLogger singleton This
21019 		 * will be set by init(), it should already be initialized by
21020 		 * PageServices
21021 		 *
21022 		 * @private
21023 		 */
21024 		_logger,
21025 		
21026 		/**
21027 		 * Boolean variable to determine if finesse toaster is enabled 
21028 		 * @private
21029 		 */
21030 		_isToasterDisabled = false,
21031 		
21032 		/**
21033 		 * Boolean variable to determine if finesse toaster is initialized 
21034 		 * @private
21035 		 */
21036 		_isToasterInitialized,
21037 		
21038 		/**
21039 		 * this function check of provided parameter is a javascript function.
21040 		 * 
21041 		 * @private
21042 		 */
21043 		isFunction = function(param) {
21044 			if (typeof param === "function") {
21045 				return true;
21046 			}
21047 			return false;
21048 		},
21049 
21050 		/**
21051 		 * _createNotification creates Notification instance
21052 		 *
21053 		 * @param {String}
21054 		 *            title title string should be displayed in the Toaster
21055 		 * @param {Object}
21056 		 *            options JSON object for notification options.
21057 		 */
21058 		_createNotification = function(title, options) {
21059 			var notification = new window.Notification(title, options);
21060 			return notification;
21061 		},
21062 
21063 		/**
21064 		 * _setAutoClose set the auto close time for toaster, it checks if
21065 		 * client code passed custom time out for the toaster, otherwise it set
21066 		 * the AUTO_CLOSE_TIME
21067 		 *
21068 		 * @param {Object}
21069 		 *            notification window.Notification that creates Html5
21070 		 *            Notification.
21071 		 * @param {String}
21072 		 *            autoClose autoClose time of the Toaster
21073 		 * @return toasterTimeout Toaster Timeout
21074 		 */
21075 		_setAutoClose = function(notification, autoClose) {
21076 
21077 			// check if custom close time passed other wise set
21078 			// DEFAULT_AUTO_CLOSE
21079 			var autoCloseTime = (autoClose && !isNaN(autoClose)) ? autoClose
21080 					: AUTO_CLOSE_TIME,
21081 			// set the time out for notification toaster
21082 			toasterTimer = setTimeout(function() {
21083 				notification.close();
21084 			}, autoCloseTime);
21085 
21086 			return toasterTimer;
21087 
21088 		},
21089 
21090 		/** This method will request permission to display Toaster. */
21091 		_requestPermission = function() {
21092 			// If they are not denied (i.e. default)
21093 			if (window.Notification
21094 					&& window.Notification.permission !== PERMISSION_DENIED) {
21095 				// Request permission
21096 				window.Notification
21097 						.requestPermission(function(status) {
21098 
21099 							// Change based on user's decision
21100 							if (window.Notification.permission !== status) {
21101 								window.Notification.permission = status;
21102 							}
21103 							_logger
21104 									.log("FinesseToaster.requestPermission(): request permission status "
21105 											+ status);
21106 
21107 						});
21108 
21109 			} else {
21110 				_logger
21111 						.log("FinesseToaster.requestPermission(): Notification not supported or permission denied.");
21112 			}
21113 
21114 		},
21115 
21116 		/**
21117 		 * This method will add onclick and onerror listener to Notification.
21118 		 * on click of toaster the gadget which originally had focus may loose
21119 		 * the focus. To get the back the focus on any element inside the gadget
21120 		 * use the on click callback handler.
21121 		 * 
21122 		 * @param {Object}
21123 		 *            notification window.Notification that creates Html5
21124 		 *            Notification.
21125 		 * @param {Object}
21126 		 *            options JSON object for notification options.
21127 		 */
21128 		_addToasterListeners = function(notification, options, toasterTimer) {
21129 			// this is onlcik handler of toaster. this handler will be invoked
21130 			// on click of toaster
21131 			notification.onclick = function() {
21132 				// in case of manually closed toaster, stop the notification
21133 				// auto close method to be invoked
21134 				clearTimeout(toasterTimer);
21135 				// This will maximize/activate chrome browser on click of
21136 				// toaster. this handling required only in case of Chrome
21137 				if (window.chrome) {
21138 					parent.focus();
21139 				}
21140 
21141 				if (options && options.onclick) {
21142 					if (isFunction(options.onclick)) {
21143 						options.onclick();
21144 					} else {
21145 						throw new Error("onclick callback must be a function");
21146 					}
21147 				}
21148 
21149 				//close toaster upon click
21150 				this.close();
21151 
21152 			};
21153 
21154 			// this is onerror handler of toaster, if there is any error while
21155 			// loading toaster this hadnler will be invoked
21156 			notification.onerror = function() {
21157 				if (options && options.onerror) {
21158 					if (isFunction(options.onerror)) {
21159 						options.onerror();
21160 					} else {
21161 						throw new Error("onerror callback must be a function");
21162 					}
21163 				}
21164 			};
21165 		};
21166 		
21167 		return {
21168 
21169 			/**
21170 			 * @class
21171 			 *  FinesseToaster is a utility class to show toaster
21172 			 *        notification in Finesse. FinesseToaster leverages <a
21173 			 *        href="https://www.w3.org/TR/notifications/">HTML5
21174 			 *        Notification</a> API to display Toaster Notification. 
21175 			 *       <p> <a
21176 			 *        href="https://developer.mozilla.org/en/docs/Web/API/notification#Browser_compatibility">For
21177 			 *        HTML5 Notification API and browser compatibility, please click
21178 			 *        here.</a></p>
21179 			 * 
21180 			 * @constructs
21181 			 */
21182 			_fakeConstuctor : function() {
21183 
21184 			},
21185 			/**
21186 			 * TOASTER_DEFAULT_ICONS constants has list of predefined icons (e.g INCOMING_CALL_ICON).
21187 			 * <p><b>Constant list</b></p>
21188 			 * <ul>
21189 			 * <li>TOASTER_DEFAULT_ICONS.INCOMING_CALL_ICON</li>
21190 			 * </ul>
21191 			 */
21192 			TOASTER_DEFAULT_ICONS : {
21193 				INCOMING_CALL_ICON : ICON_PATH + "incoming_call.png"
21194 			},
21195 
21196 			/**
21197 			 * <b>showToaster </b>: shows Toaster Notification.
21198 			 *
21199 			 * @param {String}
21200 			 *            <b>title</b> : title string should be displayed in the Toaster
21201 			 * @param {Object}
21202 			 *            options is JSON object for notification options. 
21203 			 *            <ul>
21204 			 *            <li><b>options</b> = { </li>
21205 			 *            <li><b>body</b> : The body string of the notification as
21206 			 *            specified in the options parameter of the constructor.</li>
21207 			 *            <li><b>icon</b>: The URL of the image used as an icon of the
21208 			 *            notification as specified in the options parameter of
21209 			 *            the constructor.</li>
21210 			 *            <li><b>autoClose</b> : custom auto close time of the toaster</li>
21211 			 *            <li><b>showWhenVisible</b> : 'true' toaster shows up even when page is 
21212 			 *            visible,'false' toaster  shows up only when page is invisible </li>
21213 			 *           <li> }</li>
21214 			 *            </ul>
21215 			 *
21216 			 */
21217 			showToaster : function(title, options) {
21218 				
21219 				if(!_isToasterInitialized){
21220 					throw new Error("FinesseToaster.showToaster() : Finesse toaster is not initialized");
21221 				}
21222 				
21223 				if(_isToasterDisabled){
21224 					_logger.log("FinesseToaster.showToaster() : FinesseToaster is disabled");
21225 					return;
21226 				}
21227 
21228 				var notification, toasterTimer;
21229 
21230 				// If notifications are granted show the notification
21231 				if (window.Notification
21232 						&& window.Notification.permission === PERMISSION_GRANTED) {
21233 
21234 					// document.hasFocus() used over document.hidden to keep the consistent behavior across mozilla/chrome
21235 					if (document.hasFocus() === false
21236 							|| options.showWhenVisible) {
21237 						if (_logger && AUTO_CLOSE_TIME > -1) {
21238 							notification = _createNotification(title, options);
21239 
21240 							// set the auto close time out of the toaster
21241 							toasterTimer = _setAutoClose(notification,
21242 									options.autoClose);
21243 
21244 							// and Toaster Event listeners. eg. onclick , onerror.
21245 							_addToasterListeners(notification, options,
21246 									toasterTimer);
21247 						} 
21248 					} else {
21249 						_logger
21250 								.log("FinesseToaster supressed : Page is visible and  FineeseToaster.options.showWhenVisible is false");
21251 					}
21252 
21253 				}
21254 
21255 				return notification;
21256 			},
21257 
21258 			/**
21259 			 * initialize FininseToaster and inject dependencies. this method
21260 			 * will also request permission in browser from user to display
21261 			 * Toaster Notification.
21262 			 *
21263 			 *@param {Object}
21264 			 *          Could be finesse.container.Config or finesse.gadget.Config based on where it is getting initialized from.
21265 			 *            
21266 			 * @param {Object}
21267 			 *            finesse.cslogger.ClientLogger
21268 			 * @return finesse.containerservices.FinesseToaster
21269 			 */
21270 			init : function(config , logger) {
21271 				
21272 				_isToasterInitialized = true;
21273 				
21274 				// This is for injecting mocked logger.
21275 				if (logger) {
21276 					_logger = logger;
21277 				} else {
21278 					_logger = finesse.cslogger.ClientLogger;
21279 				}
21280 				
21281 				//set default toaster notification timeout
21282 				if (config && config.toasterNotificationTimeout) {
21283 					AUTO_CLOSE_TIME = Number(config.toasterNotificationTimeout) * 1000;
21284 					
21285 					if(AUTO_CLOSE_TIME === 0){
21286 						//Finesse toaster has been disabled
21287 						_isToasterDisabled = true;
21288 					}
21289 				} 
21290 
21291 				// Request permission
21292 				_requestPermission();
21293 				return finesse.containerservices.FinesseToaster;
21294 			}
21295 		};
21296 
21297 	}());
21298 
21299 	window.finesse = window.finesse || {};
21300 	window.finesse.containerservices = window.finesse.containerservices || {};
21301 	window.finesse.containerservices.FinesseToaster = FinesseToaster;
21302 
21303 	return FinesseToaster;
21304 });
21305 
21306 /**
21307  * This "interface" is just a way to easily jsdoc the Object callback handlers.
21308  *
21309  * @requires finesse.clientservices.ClientServices
21310  * @requires Class
21311  */
21312 /** @private */
21313 define('interfaces/RestObjectHandlers',[
21314     "FinesseBase",
21315      "utilities/Utilities",
21316      "restservices/Notifier",
21317      "clientservices/ClientServices",
21318      "clientservices/Topics"
21319 ],
21320 function () {
21321 
21322     var RestObjectHandlers = ( function () { /** @lends finesse.interfaces.RestObjectHandlers.prototype */
21323         
21324         return {
21325 
21326             /**
21327              * @class
21328              * This "interface" defines REST Object callback handlers, passed as an argument to
21329              * Object getter methods in cases where the Object is going to be created.
21330              * 
21331              * @param {Object} [handlers]
21332              *     An object containing callback handlers for instantiation and runtime
21333              *     Callback to invoke upon successful instantiation, passes in REST object.
21334              * @param {Function} [handlers.onLoad(this)]
21335              *     Callback to invoke upon loading the data for the first time.
21336              * @param {Function} [handlers.onChange(this)]
21337              *     Callback to invoke upon successful update object (PUT)
21338              * @param {Function} [handlers.onAdd(this)]
21339              *     Callback to invoke upon successful update to add object (POST)
21340              * @param {Function} [handlers.onDelete(this)]
21341              *     Callback to invoke upon successful update to delete object (DELETE)
21342              * @param {Function} [handlers.onError(rsp)]
21343              *     Callback to invoke on update error (refresh or event)
21344              *     as passed by finesse.restservices.RestBase.restRequest()<br>
21345              *     {<br>
21346              *         status: {Number} The HTTP status code returned<br>
21347              *         content: {String} Raw string of response<br>
21348              *         object: {Object} Parsed object of response<br>
21349              *         error: {Object} Wrapped exception that was caught<br>
21350              *         error.errorType: {String} Type of error that was caught<br>
21351              *         error.errorMessage: {String} Message associated with error<br>
21352              *     }<br>
21353              *     <br>
21354              * Note that RestCollections have two additional callback handlers:<br>
21355              * <br>
21356              * @param {Function} [handlers.onCollectionAdd(this)]: when an object is added to this collection
21357              * @param {Function} [handlers.onCollectionDelete(this)]: when an object is removed from this collection
21358 
21359              * @constructs
21360              */
21361             _fakeConstuctor: function () {
21362                 /* This is here to enable jsdoc to document this as a class. */
21363             }
21364         };
21365     }());
21366 
21367 window.finesse = window.finesse || {};
21368 window.finesse.interfaces = window.finesse.interfaces || {};
21369 window.finesse.interfaces.RestObjectHandlers = RestObjectHandlers;
21370 
21371 return RestObjectHandlers;
21372 
21373 });
21374 
21375 
21376 /**
21377  * This "interface" is just a way to easily jsdoc the REST request handlers.
21378  *
21379  * @requires finesse.clientservices.ClientServices
21380  * @requires Class
21381  */
21382 /** @private */
21383 define('interfaces/RequestHandlers',[
21384     "FinesseBase",
21385      "utilities/Utilities",
21386      "restservices/Notifier",
21387      "clientservices/ClientServices",
21388      "clientservices/Topics"
21389 ],
21390 function () {
21391 
21392     var RequestHandlers = ( function () { /** @lends finesse.interfaces.RequestHandlers.prototype */
21393         
21394         return {
21395 
21396             /**
21397              * @class
21398              * This "interface" defines REST Object callback handlers, passed as an argument to
21399              * Object getter methods in cases where the Object is going to be created.
21400              * 
21401              * @param {Object} handlers
21402              *     An object containing the following (optional) handlers for the request:<ul>
21403              *         <li><b>success(rsp):</b> A callback function for a successful request to be invoked with the following
21404              *         response object as its only parameter:<ul>
21405              *             <li><b>status:</b> {Number} The HTTP status code returned</li>
21406              *             <li><b>content:</b> {String} Raw string of response</li>
21407              *             <li><b>object:</b> {Object} Parsed object of response</li></ul>
21408              *         <li><b>error(rsp):</b> An error callback function for an unsuccessful request to be invoked with the
21409              *         error response object as its only parameter:<ul>
21410              *             <li><b>status:</b> {Number} The HTTP status code returned</li>
21411              *             <li><b>content:</b> {String} Raw string of response</li>
21412              *             <li><b>object:</b> {Object} Parsed object of response (HTTP errors)</li>
21413              *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
21414              *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
21415              *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
21416              *             </ul></li>
21417              *         </ul>
21418 
21419              * @constructs 
21420              */
21421             _fakeConstuctor: function () {
21422                 /* This is here to enable jsdoc to document this as a class. */
21423             }
21424         };
21425     }());
21426 
21427 window.finesse = window.finesse || {};
21428 window.finesse.interfaces = window.finesse.interfaces || {};
21429 window.finesse.interfaces.RequestHandlers = RequestHandlers;
21430 
21431 finesse = finesse || {};
21432 /** @namespace These interfaces are just a convenience for documenting common parameter structures. */
21433 finesse.interfaces = finesse.interfaces || {};
21434 
21435 return RequestHandlers;
21436 
21437 });
21438 
21439 
21440 
21441 define('gadget/Config',[
21442 	"utilities/Utilities"
21443 ], function (Utilities) {  
21444 	var Config = (function () { /** @lends finesse.gadget.Config.prototype */
21445 		
21446 		if (gadgets && gadgets.Prefs) {
21447 		
21448 			var _prefs = new gadgets.Prefs();
21449 		
21450 			return {
21451 				/**
21452 				 * The base64 encoded "id:password" string used for authentication.
21453 				 */
21454 				authorization: Utilities.getUserAuthString(),
21455 				
21456 				/**
21457 				 * The  auth token string used for authentication in SSO deployments.
21458 				 */
21459 				authToken: Utilities.getToken(),
21460 				
21461 				/**
21462 				 * The country code of the client (derived from locale).
21463 				 */
21464 				country: _prefs.getString("country"),
21465 				
21466 				/**
21467 				 * The language code of the client (derived from locale).
21468 				 */
21469 				language: _prefs.getString("language"),
21470 				
21471 				/**
21472 				 * The locale of the client.
21473 				 */
21474 				locale: _prefs.getString("locale"),
21475 				
21476 				/**
21477 				 * The Finesse server IP/host as reachable from the browser.
21478 				 */
21479 				host: _prefs.getString("host"),
21480 				
21481 				/**
21482 				 * The Finesse server host's port reachable from the browser.
21483 				 */
21484 				hostPort: _prefs.getString("hostPort"),
21485 				
21486 				/**
21487 				 * The extension of the user.
21488 				 */
21489 				extension: _prefs.getString("extension"),
21490 				
21491 				/**
21492 				 * One of the work modes found in {@link finesse.restservices.User.WorkMode}, or something false (undefined) for a normal login.
21493 				 */
21494 				mobileAgentMode: _prefs.getString("mobileAgentMode"),
21495 				
21496 				/**
21497 				 * The dial number to use for mobile agent, or something false (undefined) for a normal login.
21498 				 */
21499 				mobileAgentDialNumber: _prefs.getString("mobileAgentDialNumber"),
21500 				
21501 				/**
21502 				 * The domain of the XMPP server.
21503 				 */
21504 				xmppDomain: _prefs.getString("xmppDomain"),
21505 				
21506 				/**
21507 				 * The pub sub domain where the pub sub service is running.
21508 				 */
21509 				pubsubDomain: _prefs.getString("pubsubDomain"),
21510 				
21511 				/**
21512 				 * The Finesse API IP/host as reachable from the gadget container.
21513 				 */
21514 				restHost: _prefs.getString("restHost"),
21515 				
21516 				/**
21517 				 * The type of HTTP protocol (http or https).
21518 				 */
21519 				scheme: _prefs.getString("scheme"),
21520 				
21521 				/**
21522 				 * The localhost fully qualified domain name.
21523 				 */
21524 				localhostFQDN: _prefs.getString("localhostFQDN"),
21525 				
21526 				/**
21527 				 * The localhost port.
21528 				 */
21529 				localhostPort: _prefs.getString("localhostPort"),
21530 				
21531 				/**
21532 				 * The id of the team the user belongs to.
21533 				 */
21534 				teamId: _prefs.getString("teamId"),
21535 				
21536 				/**
21537 				 * The name of the team the user belongs to.
21538 				 */
21539 				teamName: _prefs.getString("teamName"),
21540 				
21541 				/**
21542 				 * The drift time between the client and the server in milliseconds.
21543 				 */
21544 				clientDriftInMillis: _prefs.getInt("clientDriftInMillis"),
21545 				
21546 				/**
21547 				 * The client compatibility mode configuration (true if it is or false otherwise).
21548 				 */
21549 				compatibilityMode: _prefs.getString("compatibilityMode"),
21550 				
21551 				/**
21552 				 * The peripheral Id that Finesse is connected to.
21553 				 */
21554 				peripheralId: _prefs.getString("peripheralId"),
21555 				
21556 				/**
21557 				 * The auth mode of the finesse deployment.
21558 				 */
21559 				systemAuthMode: _prefs.getString("systemAuthMode"),
21560 				
21561 				
21562 				/**
21563 				 * The time for which fineese toaster stay on the browser.
21564 				 */
21565 				toasterNotificationTimeout: _prefs.getString("toasterNotificationTimeout"),
21566 				
21567 				/**
21568 				* @class
21569 				* The Config object for gadgets within the Finesse desktop container which
21570 				* contains configuration data provided by the container page.
21571 				* @constructs
21572 				*/
21573 				_fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
21574 				
21575 			};
21576 		} else {
21577 			return {};
21578 		}
21579 	}());
21580 	
21581 	/** Assign to container and gadget namespace to have config available in both  */
21582 	window.finesse = window.finesse || {};
21583 	window.finesse.container = window.finesse.container || {};
21584 	window.finesse.container.Config = window.finesse.container.Config || Config;
21585 
21586 	window.finesse.gadget = window.finesse.gadget || {};
21587 	window.finesse.gadget.Config = Config;
21588 
21589 	return Config;
21590 });
21591 define('finesse',[
21592     'restservices/Users',
21593     'restservices/Teams',
21594     'restservices/SystemInfo',
21595     'restservices/Media',
21596     'restservices/MediaDialogs',
21597     'restservices/DialogLogoutActions',
21598     'restservices/InterruptActions',
21599     'restservices/ReasonCodeLookup',
21600     'utilities/I18n',
21601     'utilities/Logger',
21602     'utilities/SaxParser',
21603     'utilities/BackSpaceHandler',
21604     'cslogger/ClientLogger',
21605     'cslogger/FinesseLogger',
21606     'containerservices/ContainerServices',
21607     'containerservices/FinesseToaster',
21608     'interfaces/RestObjectHandlers',
21609     'interfaces/RequestHandlers',
21610     'gadget/Config'
21611 ],
21612 function () {
21613     return window.finesse;
21614 });
21615 
21616 require(["finesse"]);
21617 return require('finesse'); }));
21618 
21619 // Prevent other JS files from wiping out window.finesse from the namespace
21620 var finesse = window.finesse;