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                         };
6787 
6788                         if (!_this.doNotLog) {
6789                             _this._logger.log(_this.getRestType() + ": requestId='" + options.uuid + "', Returned with status=" + rspObj.status + ", content='" + rspObj.content + "'");
6790                         }
6791 
6792                         //Some responses may not have a body.
6793                         if (rsp.text && rsp.text.length > 0) {
6794                             try {
6795                                 rspObj.object = _this._util.xml2js(rsp.text);
6796                             } catch (e) {
6797                                 error = true;
6798                                 rspObj.error = {
6799                                     errorType: "parseError",
6800                                     errorMessage: "Could not serialize XML: " + e
6801                                 };
6802                             }
6803                         } else {
6804                             rspObj.object = {};
6805                         }
6806 
6807                         if (!error && rspObj.status >= 200 && rspObj.status < 300) {
6808                             if (options.success) {
6809                                 options.success(rspObj);
6810                             }
6811                         } else {
6812                             if (options.error) {
6813                                 options.error(rspObj);
6814                             }
6815                         }
6816 
6817                         /*
6818                          * If a synchronous error happened after a non-GET request (usually a validation error), we
6819                          * need to clean up the request's entry in _pendingCallbacks since no corresponding event
6820                          * will arrive later. The corresponding requestId should be present in the response headers.
6821                          *
6822                          * It appears Shindig changes the header keys to lower case, hence 'requestid' instead of
6823                          * 'requestId' below.
6824                          **/
6825                         if (rspObj.status !== 202 && rsp.headers && rsp.headers.requestid) {
6826                             requestId = rsp.headers.requestid[0];
6827                             if (_this._pendingCallbacks[requestId]) {
6828                                 delete _this._pendingCallbacks[requestId];
6829                             }
6830                         }
6831                     }
6832                 }
6833             };
6834         },
6835 
6836         /**
6837          * Utility method to make an asynchronous request
6838          * @param {String} url
6839          *     The unencoded URL to which the request is sent (will be encoded)
6840          * @param {Object} options
6841          *     An object containing additional options for the request.
6842          * @param {Object} options.content
6843          *     An object to send in the content body of the request. Will be
6844          *     serialized into XML before sending.
6845          * @param {String} options.method
6846          *     The type of request. Defaults to "GET" when none is specified.
6847          * @param {Function} options.success(rsp)
6848          *     A callback function to be invoked for a successful request.
6849          *     {
6850          *         status: {Number} The HTTP status code returned
6851          *         content: {String} Raw string of response
6852          *         object: {Object} Parsed object of response
6853          *     }
6854          * @param {Function} options.error(rsp)
6855          *     A callback function to be invoked for an unsuccessful request.
6856          *     {
6857          *         status: {Number} The HTTP status code returned
6858          *         content: {String} Raw string of response
6859          *         object: {Object} Parsed object of response
6860          *         error: {Object} Wrapped exception that was caught
6861          *         error.errorType: {String} Type of error that was caught
6862          *         error.errorMessage: {String} Message associated with error
6863          *     }
6864          * @returns {Object}
6865          *     {
6866          *         abort: {function} Function that signifies the callback handler to NOT process the response of this asynchronous request
6867          *     }
6868          * @private
6869         */
6870 			        restRequest : function(url, options) {
6871 			    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;
6872 			    // Protect against null dereferencing of options
6873 			    // allowing its (nonexistent) keys to be read as
6874 			    // undefined
6875 			    options = options || {};
6876 			    options.success = this._util
6877 				    .validateHandler(options.success);
6878 			    options.error = this._util
6879 				    .validateHandler(options.error);
6880 
6881 			    // true if this should be a GET request, false
6882 			    // otherwise
6883 			    if (!options.method || options.method === "GET") {
6884 				// Disable caching for GETs
6885 				if (url.indexOf("?") > -1) {
6886 				    url += "&";
6887 				} else {
6888 				    url += "?";
6889 				}
6890 				url += "nocache="
6891 					+ this._util.currentTimeMillis();
6892 			    } else {
6893 				/**
6894 				 * If not GET, generate a requestID and add it
6895 				 * to the headers, then wrap callbacks into an
6896 				 * object and store it in _pendingCallbacks. If
6897 				 * we receive a synchronous error response
6898 				 * instead of a 202 as expected, the AJAX
6899 				 * handler will clean up _pendingCallbacks.
6900 				 */
6901 				/*
6902 				 * TODO: Clean up _pendingCallbacks if an entry
6903 				 * persists after a certain amount of time has
6904 				 * passed. In the block below, can store the
6905 				 * current time (new Date().getTime()) alongside
6906 				 * the callbacks in the new _pendingCallbacks
6907 				 * entry. Then iterate through a copty of
6908 				 * _pendingCallbacks, deleting all entries
6909 				 * inside _pendingCallbacks that are older than
6910 				 * a certain threshold (2 minutes for example.)
6911 				 * This solves a potential memory leak issue if
6912 				 * we never receive an event for a given stored
6913 				 * requestId; we don't want to store unfired
6914 				 * callbacks forever.
6915 				 */
6916 				/** @private */
6917 				options.uuid = this._util.generateUUID();
6918 				// params[gadgets.io.RequestParameters.HEADERS].requestId
6919 				// = options.uuid;
6920 				// By default, Shindig strips nearly all of the
6921 				// response headers, but this parameter tells
6922 				// Shindig
6923 				// to send the headers through unmodified; we
6924 				// need to be able to read the 'requestId'
6925 				// header if we
6926 				// get a synchronous error as a result of a
6927 				// non-GET request. (See the bottom of
6928 				// _createAjaxHandler().)
6929 				// params[gadgets.io.RequestParameters.GET_FULL_HEADERS]
6930 				// = "true";
6931 				this._pendingCallbacks[options.uuid] = {};
6932 				this._pendingCallbacks[options.uuid].success = options.success;
6933 				this._pendingCallbacks[options.uuid].error = options.error;
6934 			    }
6935 			    encodedUrl = encodeURI(url)
6936 				    + (window.errorOnRestRequest ? "ERROR" : "");
6937 			    ajaxHandler = this._createAjaxHandler(options);
6938 			    // ClientServices.makeRequest(encodedUrl,
6939 			    // ajaxHandler.callback, params);
6940 			    mtype = options.method || 'GET';
6941 			    encodedUrl = scheme + "//" + host + ":" + port
6942 				    + encodedUrl;
6943 
6944 			    if (typeof options.content === "object"
6945 				    && typeof options.content !== "undefined") {
6946 				// Except get, all the other operations accepts
6947 				// application/xml only.
6948 				// @Consumes in all the webservices except GET
6949 				// are having this.
6950 				postdata = this._util.js2xml(options.content);
6951 				if (postdata !== null && postdata !== "") {
6952 				    contentTypeAX = "application/xml";
6953 				} else {
6954 				    // in desktop, reason code GET request was
6955 				    // called with empty object content
6956 				    // if not able to parse any content to post
6957 				    // data, then it is GET only
6958 				    contentTypeAX = "";
6959 				    dataTypeAX = "text";
6960 				}
6961 			    } else {
6962 				// No content type for GET operation, by default
6963 				// it will take text/plain || application/xml.
6964 				// for dataType - GET will result plain text,
6965 				// which we are taking as xml.
6966 				// Queried list of @Produces from code base, all
6967 				// the GET operations accepts text/plain OR
6968 				// application/xml and produces application/xml
6969 				contentTypeAX = "";
6970 				dataTypeAX = "text";
6971 			    }
6972 			    auth = this._util.getAuthHeaderString(this._config);
6973 
6974 			    if (!this.doNotLog) {
6975 				this._logger.log(this.getRestType()
6976 					+ ": requestId='" + options.uuid
6977 					+ "', Making REST request: method="
6978 					+ (options.method || "GET") + ", url='"
6979 					+ encodedUrl + "'");
6980 			    }
6981 			    $
6982 				    .ajax({
6983 					url : encodedUrl,
6984 					headers : this.extraHeaders || {},
6985 					beforeSend : function(xhr) {
6986 					    xhr.setRequestHeader(
6987 						    "Authorization", auth);
6988 					    xhr.setRequestHeader("locale",
6989 						    locale);
6990 					    if (options.uuid) {
6991 						xhr.setRequestHeader(
6992 							"requestId",
6993 							options.uuid);
6994 					    }
6995 					},
6996 					dataType : dataTypeAX, // xml or json?
6997 					contentType : contentTypeAX,
6998 					type : mtype,
6999 					data : postdata,
7000 					timeout : 5000,
7001 					success : function(response, status,
7002 						xhr) {
7003 					    var rspObj = {
7004 						rc : xhr.status,
7005 						text : response,
7006 						headers : {
7007 						    requestid : xhr
7008 							    .getResponseHeader("requestId")
7009 						}
7010 					    };
7011 					    ajaxHandler.callback(rspObj);
7012 					},
7013 					error : function(jqXHR, request, error) {
7014 					    var resObj = {
7015 						rc : jqXHR.status,
7016 						text : jqXHR.responseText,
7017 						headers : {
7018 						    requestid : jqXHR
7019 							    .getResponseHeader("requestId")
7020 						}
7021 					    };
7022 					    ajaxHandler.callback(resObj);
7023 					}
7024 				    });
7025 			    return {
7026 				abort : ajaxHandler.abort
7027 			    };
7028 			},
7029 
7030 
7031         /**
7032 	 * Retrieves a reference to a particular notifierType.
7033 	 * 
7034 	 * @param notifierType
7035 	 *                is a string which indicates the notifier to retrieve
7036 	 *                ('load', 'change', 'add', 'delete', 'error')
7037 	 * @return {Notifier}
7038 	 * @private
7039 	 */
7040         _getNotifierReference: function (notifierType) {
7041             var notifierReference = null;
7042             if (notifierType === 'load') {
7043                 notifierReference = this._loadNotifier;
7044             } else if (notifierType === 'change') {
7045                 notifierReference = this._changeNotifier;
7046             } else if (notifierType === 'add') {
7047                 notifierReference = this._addNotifier;
7048             } else if (notifierType === 'delete') {
7049                 notifierReference = this._deleteNotifier;
7050             } else if (notifierType === 'error') {
7051                 notifierReference = this._errorNotifier;
7052             } else {
7053                 throw new Error("_getNotifierReference(): Trying to get unknown notifier(notifierType=" + notifierType + ")");
7054             }
7055 
7056             return notifierReference;
7057         }
7058     });
7059 
7060     window.finesse = window.finesse || {};
7061     window.finesse.restservices = window.finesse.restservices || {};
7062     window.finesse.restservices.RestBase = RestBase;
7063     
7064     return RestBase;
7065 });
7066 
7067 /** The following comment is to prevent jslint errors about 
7068  * using variables before they are defined.
7069  */
7070 /*global finesse*/
7071 
7072 /**
7073  * JavaScript base object that all REST collection objects should
7074  * inherit from because it encapsulates and provides the common functionality
7075  * that all REST objects need.
7076  *
7077  * @requires finesse.clientservices.ClientServices
7078  * @requires Class
7079  * @requires finesse.FinesseBase
7080  * @requires finesse.restservices.RestBase
7081  */
7082 
7083 /**
7084  * @class
7085  * JavaScript representation of a REST collection object.
7086  *
7087  * @constructor
7088  * @param {Function} callbacks.onCollectionAdd(this)
7089  *     Callback to invoke upon successful item addition to the collection.
7090  * @param {Function} callbacks.onCollectionDelete(this)
7091  *     Callback to invoke upon successful item deletion from the collection.
7092  * @borrows finesse.restservices.RestBase as finesse.restservices.RestCollectionBase
7093  */
7094 /** @private */
7095 define('restservices/RestCollectionBase',[
7096     'restservices/RestBase',
7097     'utilities/Utilities',
7098     'restservices/Notifier'
7099 ],
7100 function (RestBase, Utilities, Notifier) {
7101     var RestCollectionBase = RestBase.extend(/** @lends finesse.restservices.RestCollectionBase.prototype */{
7102 
7103         /**
7104          * Boolean function that specifies whether the collection handles subscribing
7105          * and propagation of events for the individual REST object items the
7106          * collection holds. False by default. Subclasses should override if true.
7107          * @private
7108          */
7109         supportsRestItemSubscriptions: false,
7110 
7111         /**
7112          * Gets the constructor the individual items that make of the collection.
7113          * For example, a Dialogs collection object will hold a list of Dialog items.
7114          * @throws Error because subtype must implement.
7115          * @private
7116          */
7117         getRestItemClass: function () {
7118             throw new Error("getRestItemClass(): Not implemented in subtype.");
7119         },
7120 
7121         /**
7122          * Gets the REST type of the individual items that make of the collection.
7123          * For example, a Dialogs collection object will hold a list of Dialog items.
7124          * @throws Error because subtype must implement.
7125          * @private
7126          */
7127         getRestItemType: function () {
7128             throw new Error("getRestItemType(): Not implemented in subtype.");
7129         },
7130 
7131         /**
7132          * The base REST URL in which items this object contains can be referenced.
7133          * @return {String}
7134          *     The REST URI for items this object contains.
7135          * @private
7136          */
7137         getRestItemBaseUrl: function () {
7138             var
7139             restUrl = "/finesse/api";
7140 
7141             //Append the REST type.
7142             restUrl += "/" + this.getRestItemType();
7143 
7144             return restUrl;
7145         },
7146 
7147          /*
7148          * Creates a new object from the given data
7149          * @param data - data object
7150          * @private
7151          */
7152         _objectCreator: function (data) {
7153             var objectId = this._extractId(data),
7154             newRestObj = this._collection[objectId],
7155             _this = this;
7156 
7157             //Prevent duplicate entries into collection.
7158             if (!newRestObj) {
7159                 //Create a new REST object using the subtype defined by the
7160                 //overridden method.
7161                 newRestObj = new (this.getRestItemClass())({
7162                     doNotSubscribe: this.handlesItemSubscription,
7163                     doNotRefresh: this.handlesItemRefresh,
7164                     id: objectId,
7165                     data: data,
7166                     onLoad: function (newObj) {
7167                         //Normalize and add  REST object to collection datastore.
7168                         _this._collection[objectId] = newObj;
7169                         _this._collectionAddNotifier.notifyListeners(newObj);
7170                         _this.length += 1;
7171                     }
7172                 });
7173             }
7174             else {
7175                 //If entry already exist in collection, process the new event,
7176                 //and notify all change listeners since an existing object has
7177                 //change. This could happen in the case when the Finesse server
7178                 //cycles, and sends a snapshot of the user's calls.
7179                 newRestObj._processObject(data);
7180                 newRestObj._changeNotifier.notifyListeners(newRestObj);
7181             }
7182         },
7183 
7184         /*
7185          * Deletes and object and notifies its handlers
7186          * @param data - data object
7187          * @private
7188          */
7189         _objectDeleter: function (data) {
7190             var objectId = this._extractId(data),
7191             object = this._collection[objectId];
7192             if (object) {
7193                 //Even though this is a delete, let's make sure the object we are passing has got good data
7194                 object._processObject(data);
7195                 //Notify listeners and delete from internal datastore.
7196                 this._collectionDeleteNotifier.notifyListeners(object);
7197                 delete this._collection[objectId];
7198                 this.length -= 1;
7199             }
7200         },
7201 
7202          /**
7203           * Creates an anonymous function for notifiying error listeners of a particular object
7204           * data.
7205           * @param obj - the objects whose error listeners to notify
7206           * @returns {Function}
7207           *     Callback for notifying of errors
7208           * @private
7209           */
7210         _createErrorNotifier: function (obj) {
7211             return function (err) {
7212                 obj._errorNotifier.notifyListeners(err);
7213             };
7214         },
7215 
7216          /**
7217           * Replaces the collection with a refreshed list using the passed in
7218           * data.
7219           * @param data - data object (usually this._data)
7220           * @private
7221           */
7222          _buildRefreshedCollection: function (data) {
7223             var i, dataObject, object, objectId, dataArray, newIds = [], foundFlag;
7224             if (data && this.getProperty(data, this.getRestItemType()) !== null) {
7225                 dataArray = Utilities.getArray(this.getProperty(data, this.getRestItemType()));
7226             } else {
7227                 dataArray = [];
7228             }
7229 
7230             // iterate through each item in the new data and add to or update collection
7231             for (i = 0; i < dataArray.length; i += 1) {
7232                 dataObject = {};
7233                 dataObject[this.getRestItemType()] = dataArray[i];
7234                 objectId = this._extractId(dataObject);
7235 
7236                 this._objectCreator(dataObject);
7237                 newIds.push(objectId);
7238 
7239                 // resubscribe if the object requires an explicit subscription
7240                 object = this._collection[objectId];
7241                 if (this.handlesItemRefresh && object.explicitSubscription) {
7242                     object._subscribeNode({
7243                         error: this._createErrorNotifier(object)
7244                     });
7245                 }
7246             }
7247 
7248             // now clean up items (if any) that were removed
7249             for (objectId in this._collection) {
7250                 if (this._collection.hasOwnProperty(objectId)) {
7251                     foundFlag = false;
7252                     for (i = newIds.length - 1; i >= 0; i -= 1) {
7253                         if (newIds[i] === objectId) {
7254                             foundFlag = true;
7255                             break;
7256                         }
7257                     }
7258                     // did not find in updated list, so delete it
7259                     if (!foundFlag) {
7260                         this._objectDeleter({'data': this._collection[objectId]._data});
7261                     }
7262                 }
7263             }
7264         },
7265 
7266          /**
7267           * The actual refresh operation, refactored out so we don't have to repeat code
7268           * @private
7269           */
7270         _RESTRefresh: function () {
7271             var _this = this;
7272             this._doGET({
7273                 success: function(rsp) {
7274                     if (_this._processResponse(rsp)) {
7275                         _this._buildRefreshedCollection(_this._data);
7276                     } else {
7277                         _this._errorNotifier.notifyListeners(_this);
7278                     }
7279                 },
7280                 error: function(rsp) {
7281                     _this._errorNotifier.notifyListeners(rsp);
7282                 }
7283             });            
7284         },
7285 
7286         /**
7287          * Force an update on this object. Since an asynchronous GET is performed,
7288          * it is necessary to have an onChange handler registered in order to be
7289          * notified when the response of this returns.
7290          * @returns {finesse.restservices.RestBaseCollection}
7291          *     This RestBaseCollection object to allow cascading
7292          */
7293          refresh: function() {
7294             var _this = this, isLoaded = this._loaded;
7295 
7296             // resubscribe if the collection requires an explicit subscription
7297             if (this.explicitSubscription) {
7298                 this._subscribeNode({
7299                     success: function () {
7300                         _this._RESTRefresh();
7301                     },
7302                     error: function (err) {
7303                         _this._errorNotifier.notifyListeners(err);
7304                     }
7305                 });
7306             } else {
7307                 this._RESTRefresh();
7308             }
7309 
7310             return this; // Allow cascading
7311          },
7312 
7313         /**
7314          * @private
7315          * The _addHandlerCb and _deleteHandlerCb require that data be passed in the
7316          * format of an array of {(Object Type): object} objects. For example, a
7317          * queues object would return [{Queue: queue1}, {Queue: queue2}, ...].
7318          * @param skipOuterObject If {true} is passed in for this param, then the "data"
7319          *                           property is returned instead of an object with the
7320          *                           data appended.
7321          * @return {Array}
7322          */
7323         extractCollectionData: function (skipOuterObject) {
7324             var restObjs,
7325             obj,
7326             result = [],
7327             _this = this;
7328             
7329             if (this._data)
7330             {
7331                 restObjs = this._data[this.getRestItemType()];
7332     
7333                 if (restObjs)
7334                 {
7335                     // check if there are multiple objects to pass
7336                     if (!$.isArray(restObjs))
7337                     {
7338                         restObjs = [restObjs];
7339                     }
7340     
7341                     // if so, create an object for each and add to result array
7342                     $.each(restObjs, function (id, object) {
7343                         if (skipOuterObject === true)
7344                         {
7345                             obj = object;
7346                         }
7347                         else
7348                         {
7349                             obj = {};
7350                             obj[_this.getRestItemType()] = object;
7351                         }
7352                         result.push(obj);
7353                     });
7354                 }
7355                 
7356             }
7357             
7358             return result;
7359         },
7360 
7361         /**
7362          * For Finesse, collections are handled uniquely on a POST and
7363          * doesn't necessary follow REST conventions. A POST on a collection
7364          * doesn't mean that the collection has been created, it means that an
7365          * item has been added to the collection. This function will generate
7366          * a closure which will handle this logic appropriately.
7367          * @param {Object} scope
7368          *     The scope of where the callback should be invoked.
7369          * @private
7370          */
7371         _addHandlerCb: function (scope) {
7372             return function (restItem) {
7373                 var data = restItem.extractCollectionData();               
7374 
7375                 $.each(data, function (id, object) {
7376                     scope._objectCreator(object);
7377                 });
7378             };
7379         },
7380 
7381         /**
7382          * For Finesse, collections are handled uniquely on a DELETE and
7383          * doesn't necessary follow REST conventions. A DELETE on a collection
7384          * doesn't mean that the collection has been deleted, it means that an
7385          * item has been deleted from the collection. This function will generate
7386          * a closure which will handle this logic appropriately.
7387          * @param {Object} scope
7388          *     The scope of where the callback should be invoked.
7389          * @private
7390          */
7391         _deleteHandlerCb: function (scope) {
7392             return function (restItem) {
7393                 var data = restItem.extractCollectionData();
7394 
7395                 $.each(data, function (id, obj) {
7396                     scope._objectDeleter(obj);
7397                 });
7398             };
7399         },
7400 
7401         /**
7402          * Utility method to process the update notification for Rest Items
7403          * that are children of the collection whose events are published to
7404          * the collection's node.
7405          * @param {Object} update
7406          *     The payload of an update notification.
7407          * @returns {Boolean}
7408          *     True if the update was successfully processed (the update object
7409          *     passed the schema validation) and updated the internal data cache,
7410          *     false otherwise.
7411          * @private
7412          */
7413         _processRestItemUpdate: function (update) {
7414             var object, objectId, updateObj = update.object.Update;
7415 
7416             //Extract the ID from the source if the Update was an error.
7417             if (updateObj.data.apiErrors) {
7418                 objectId = Utilities.getId(updateObj.source);
7419             }
7420             //Otherwise extract from the data object itself.
7421             else {
7422                 objectId = this._extractId(updateObj.data);
7423             }
7424 
7425             object = this._collection[objectId];
7426             if (object) {
7427                 if (object._processUpdate(update)) {
7428                     switch (updateObj.event) {
7429                     case "POST":
7430                         object._addNotifier.notifyListeners(object);
7431                         break;
7432                     case "PUT":
7433                         object._changeNotifier.notifyListeners(object);
7434                         break;
7435                     case "DELETE":
7436                         object._deleteNotifier.notifyListeners(object);
7437                         break;
7438                     }
7439                 }
7440             }
7441         },
7442 
7443         /**
7444          * SUBCLASS IMPLEMENTATION (override):
7445          * For collections, this callback has the additional responsibility of passing events
7446          * of collection item updates to the item objects themselves. The collection needs to
7447          * do this because item updates are published to the collection's node.
7448          * @returns {Function}
7449          *     The callback to be invoked when an update event is received
7450          * @private
7451          */
7452         _createPubsubCallback: function () {
7453             var _this = this;
7454             return function (update) {
7455                 //If the source of the update is our REST URL, this means the collection itself is modified
7456                 if (update.object.Update.source === _this.getRestUrl()) {
7457                     _this._updateEventHandler(_this, update);
7458                 } else {
7459                     //Otherwise, it is safe to assume that if we got an event on our topic, it must be a
7460                     //rest item update of one of our children that was published on our node (OpenAjax topic)
7461                     _this._processRestItemUpdate(update);
7462                 }
7463             };
7464         },
7465 
7466         /**
7467          * @class
7468          * This is the base collection object. 
7469          *
7470          * @constructs
7471          * @augments finesse.restservices.RestBase
7472          * @see finesse.restservices.Contacts
7473          * @see finesse.restservices.Dialogs
7474          * @see finesse.restservices.PhoneBooks
7475          * @see finesse.restservices.Queues
7476          * @see finesse.restservices.WorkflowActions
7477          * @see finesse.restservices.Workflows
7478          * @see finesse.restservices.WrapUpReasons
7479          */
7480         _fakeConstuctor: function () {
7481             /* This is here to hide the real init constructor from the public docs */
7482         },
7483         
7484        /**
7485          * @private
7486          * @param {Object} options
7487          *     An object with the following properties:<ul>
7488          *         <li><b>id:</b> The id of the object being constructed</li>
7489          *         <li><b>onCollectionAdd(this): (optional)</b> when an object is added to this collection</li>
7490          *         <li><b>onCollectionDelete(this): (optional)</b> when an object is removed from this collection</li>
7491          *         <li><b>onLoad(this): (optional)</b> when the collection is successfully loaded from the server</li>
7492          *         <li><b>onChange(this): (optional)</b> when an update notification of the collection is received. 
7493          *         This does not include adding and deleting members of the collection</li>
7494          *         <li><b>onAdd(this): (optional)</b> when a notification that the collection is created is received</li>
7495          *         <li><b>onDelete(this): (optional)</b> when a notification that the collection is deleted is received</li>
7496          *         <li><b>onError(rsp): (optional)</b> if loading of the collection fails, invoked with the error response object:<ul>
7497          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
7498          *             <li><b>content:</b> {String} Raw string of response</li>
7499          *             <li><b>object:</b> {Object} Parsed object of response</li>
7500          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
7501          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
7502          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
7503          *             </ul></li>
7504          *         </ul></li>
7505          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
7506          **/
7507         init: function (options) {
7508 
7509             options = options || {};
7510             options.id = "";
7511 
7512             //Make internal datastore collection to hold a list of objects.
7513             this._collection = {};
7514             this.length = 0;
7515 
7516             //Collections will have additional callbacks that will be invoked when
7517             //an item has been added/deleted.
7518             this._collectionAddNotifier = new Notifier();
7519             this._collectionDeleteNotifier = new Notifier();
7520 
7521             //Initialize the base class.
7522             this._super(options);
7523 
7524             this.addHandler('collectionAdd', options.onCollectionAdd);
7525             this.addHandler('collectionDelete', options.onCollectionDelete);
7526 
7527             //For Finesse, collections are handled uniquely on a POST/DELETE and
7528             //doesn't necessary follow REST conventions. A POST on a collection
7529             //doesn't mean that the collection has been created, it means that an
7530             //item has been added to the collection. A DELETE means that an item has
7531             //been removed from the collection. Due to this, we are attaching
7532             //special callbacks to the add/delete that will handle this logic.
7533             this.addHandler("add", this._addHandlerCb(this));
7534             this.addHandler("delete", this._deleteHandlerCb(this));
7535         },
7536 
7537         /**
7538          * Returns the collection.
7539          * @returns {Object}
7540          *     The collection as an object
7541          */
7542         getCollection: function () {
7543             //TODO: is this safe? or should we instead return protected functions such as .each(function)?
7544             return this._collection;
7545         },
7546 
7547         /**
7548          * Utility method to build the internal collection data structure (object) based on provided data
7549          * @param {Object} data
7550          *     The data to build the internal collection from
7551          * @private
7552          */
7553         _buildCollection: function (data) {
7554             var i, object, objectId, dataArray;
7555             if (data && this.getProperty(data, this.getRestItemType()) !== null) {
7556                 dataArray = Utilities.getArray(this.getProperty(data, this.getRestItemType()));
7557                 for (i = 0; i < dataArray.length; i += 1) {
7558 
7559                     object = {};
7560                     object[this.getRestItemType()] = dataArray[i];
7561                     objectId = this._extractId(object);
7562                     this._collection[objectId] = new (this.getRestItemClass())({
7563                         doNotSubscribe: this.handlesItemSubscription,
7564                         doNotRefresh: this.handlesItemRefresh,
7565                         id: objectId,
7566                         data: object
7567                     });
7568                     this.length += 1;
7569                 }
7570             }
7571         },
7572 
7573         /**
7574          * Called to know whether to include an item in the _collection and _data. Return false to keep it, true to filter out (discard) it.
7575          * Override this in subclasses if you need only object with certain attribute values.
7576          * @param  {Object} item Item to test.
7577          * @return {Boolean} False to keep, true to filter out (discard);
7578          */
7579         _filterOutItem: function (item) {
7580             return false;
7581         },
7582     
7583         /**
7584          * Validate and store the object into the internal data store.
7585          * SUBCLASS IMPLEMENTATION (override):
7586          * Performs collection specific logic to _buildCollection internally based on provided data
7587          * @param {Object} object
7588          *     The JavaScript object that should match of schema of this REST object.
7589          * @returns {Boolean}
7590          *     True if the object was validated and stored successfully.
7591          * @private
7592          */
7593         _processObject: function (object) {
7594             var i,
7595                 restItemType = this.getRestItemType(),
7596                 items;
7597             if (this._validate(object)) {
7598                 this._data = this.getProperty(object, this.getRestType()); // Should clone the object here?
7599     
7600                 // If a subclass has overriden _filterOutItem then we'll need to run through the items and remove them
7601                 if (this._data)
7602                 {
7603                     items = this._data[restItemType];
7604     
7605                     if (typeof(items) !== "undefined")
7606                     {
7607                         if (typeof(items.length) === "undefined")
7608                         {
7609                             // Single object
7610                             if (this._filterOutItem(items))
7611                             {
7612                                 this._data[restItemType] = items = [];
7613                             }
7614                             
7615                         }
7616                         else
7617                         {
7618                             // filter out objects
7619                             for (i = items.length - 1; i !== -1; i = i - 1)
7620                             {
7621                                 if (this._filterOutItem(items[i]))
7622                                 {
7623                                     items.splice(i, 1);
7624                                 }
7625                             }
7626                         }
7627                     }
7628                 }
7629     
7630                 // If loaded for the first time, call the load notifiers.
7631                 if (!this._loaded) {
7632                     this._buildCollection(this._data);
7633                     this._loaded = true;
7634                     this._loadNotifier.notifyListeners(this);
7635                 }
7636                 
7637                 return true;
7638                 
7639             }
7640             return false;
7641         },
7642 
7643         /**
7644          * Retrieves a reference to a particular notifierType.
7645          * @param {String} notifierType
7646          *      Specifies the notifier to retrieve (load, change, error, add, delete)
7647          * @return {Notifier} The notifier object.
7648          */
7649         _getNotifierReference: function (notifierType) {
7650             var notifierReference;
7651 
7652             try {
7653                 //Use the base method to get references for load/change/error.
7654                 notifierReference = this._super(notifierType);
7655             } catch (err) {
7656                 //Check for add/delete
7657                 if (notifierType === "collectionAdd") {
7658                     notifierReference = this._collectionAddNotifier;
7659                 } else if (notifierType === "collectionDelete") {
7660                     notifierReference = this._collectionDeleteNotifier;
7661                 } else {
7662                     //Rethrow exception from base class.
7663                     throw err;
7664                 }
7665             }
7666             return notifierReference;
7667         }
7668     });
7669     
7670     window.finesse = window.finesse || {};
7671     window.finesse.restservices = window.finesse.restservices || {};
7672     window.finesse.restservices.RestCollectionBase = RestCollectionBase;
7673     
7674     return RestCollectionBase;
7675 });
7676 
7677 /**
7678  * JavaScript representation of the Finesse Dialog object.
7679  *
7680  * @requires finesse.clientservices.ClientServices
7681  * @requires Class
7682  * @requires finesse.FinesseBase
7683  * @requires finesse.restservices.RestBase
7684  */
7685 
7686 /** @private */
7687 define('restservices/DialogBase',[
7688         'restservices/RestBase',
7689         'utilities/Utilities'
7690     ],
7691     function (RestBase, Utilities) {
7692         var DialogBase = RestBase.extend(/** @lends finesse.restservices.DialogBase.prototype */{
7693 
7694             /**
7695              * @class
7696              * A DialogBase is an attempted connection between or among multiple participants,
7697              * for example, a regular phone call, a chat, or an email.
7698              *
7699              * This object is typically extended into individual
7700              * REST Objects (like Dialog, MediaDialog, etc...), and shouldn't be used directly.
7701              *
7702              * @augments finesse.restservices.RestBase
7703              * @constructs
7704              */
7705             _fakeConstuctor: function () {
7706                 /* This is here to hide the real init constructor from the public docs */
7707             },
7708 
7709             /**
7710              * @private
7711              *
7712              * @param {Object} options
7713              *     An object with the following properties:<ul>
7714              *         <li><b>id:</b> The id of the object being constructed</li>
7715              *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
7716              *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
7717              *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
7718              *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
7719              *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
7720              *             <li><b>status:</b> {Number} The HTTP status code returned</li>
7721              *             <li><b>content:</b> {String} Raw string of response</li>
7722              *             <li><b>object:</b> {Object} Parsed object of response</li>
7723              *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
7724              *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
7725              *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
7726              *             </ul></li>
7727              *         </ul></li>
7728              *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
7729              **/
7730             init: function (options) {
7731                 this._super(options);
7732             },
7733 
7734             /**
7735              * @private
7736              * Gets the REST class for the current object - this is the Dialog class.
7737              * @returns {Object} The Dialog class.
7738              */
7739             getRestClass: function () {
7740                 throw new Error("getRestClass(): Not implemented in subtype.");
7741             },
7742 
7743             /**
7744              * @private
7745              * The constant for agent device.
7746              */
7747             _agentDeviceType: "AGENT_DEVICE",
7748 
7749             /**
7750              * @private
7751              * Gets the REST type for the current object - this is a "Dialog".
7752              * @returns {String} The Dialog string.
7753              */
7754             getRestType: function () {
7755                 return "Dialog";
7756             },
7757 
7758             /**
7759              * @private
7760              * Override default to indicate that this object doesn't support making
7761              * requests.
7762              */
7763             supportsRequests: false,
7764 
7765             /**
7766              * @private
7767              * Override default to indicate that this object doesn't support subscriptions.
7768              */
7769             supportsSubscriptions: false,
7770 
7771 
7772             /**
7773              * Getter for the media type.
7774              * @returns {String} The media type.
7775              */
7776             getMediaType: function () {
7777                 this.isLoaded();
7778                 return this.getData().mediaType;
7779             },
7780 
7781             /**
7782              * @private
7783              * Getter for the uri.
7784              * @returns {String} The uri.
7785              */
7786             getDialogUri: function () {
7787                 this.isLoaded();
7788                 return this.getData().uri;
7789             },
7790 
7791             /**
7792              * Getter for the callType.
7793              * @deprecated Use getMediaProperties().callType instead.
7794              * @returns {String} The callType.
7795              */
7796             getCallType: function () {
7797                 this.isLoaded();
7798                 return this.getData().mediaProperties.callType;
7799             },
7800 
7801 
7802             /**
7803              * Getter for the Dialog state.
7804              * @returns {String} The Dialog state.
7805              */
7806             getState: function () {
7807                 this.isLoaded();
7808                 return this.getData().state;
7809             },
7810 
7811             /**
7812              * Retrieves a list of participants within the Dialog object.
7813              * @returns {Object} Array list of participants.
7814              * Participant entity properties are as follows:<ul>
7815              *     <li>state - The state of the Participant. 
7816              *     <li>stateCause - The state cause of the Participant.
7817              *     <li>mediaAddress - The media address of the Participant.
7818              *     <li>startTime - The start Time of the Participant.
7819              *     <li>stateChangeTime - The time when participant state has changed.
7820              *     <li>actions - These are the actions that a Participant can perform</ul>
7821              */
7822             getParticipants: function () {
7823                 this.isLoaded();
7824                 var participants = this.getData().participants.Participant;
7825                 //Due to the nature of the XML->JSO converter library, a single
7826                 //element in the XML array will be considered to an object instead of
7827                 //a real array. This will handle those cases to ensure that an array is
7828                 //always returned.
7829 
7830                 return Utilities.getArray(participants);
7831             },
7832 
7833             /**
7834              * This method retrieves the participant timer counters
7835              *
7836              * @param {String} participantExt Extension of participant.
7837              * @returns {Object} Array of Participants which contains properties :<ul>
7838              *     <li>state - The state of the Participant. 
7839              *     <li>startTime - The start Time of the Participant.
7840              *     <li>stateChangeTime - The time when participant state has changed.</ul>
7841              * 
7842              */
7843             getParticipantTimerCounters : function (participantExt) {
7844                 var part, participantTimerCounters = {}, idx, participants;
7845 
7846                 participants = this.getParticipants();
7847 
7848 
7849                 //Loop through all the participants and find the right participant (based on participantExt)
7850                 for(idx=0;idx<participants.length;idx=idx+1)
7851                 {
7852                     part = participants[idx];
7853 
7854                     if (part.mediaAddress === participantExt)
7855                     {
7856                         participantTimerCounters.startTime= part.startTime;
7857                         participantTimerCounters.stateChangeTime= part.stateChangeTime;
7858                         participantTimerCounters.state= part.state;
7859                         break;
7860                     }
7861                 }
7862 
7863                 return participantTimerCounters;
7864             },
7865 
7866 
7867             /**
7868              * Retrieves a list of media properties from the dialog object.
7869              * @returns {Object} Map of call variables; names mapped to values.
7870              * Variables may include the following:<ul>
7871              * <li>dialedNumber: The number dialed.
7872              * <li>callType: The type of call. Call types include:<ul>
7873              *     <li>ACD_IN
7874              *     <li>PREROUTE_ACD_IN
7875              *     <li>PREROUTE_DIRECT_AGENT
7876              *     <li>TRANSFER
7877              *     <li>OTHER_IN
7878              *     <li>OUT
7879              *     <li>AGENT_INSIDE
7880              *     <li>CONSULT
7881              *     <li>CONFERENCE
7882              *     <li>SUPERVISOR_MONITOR
7883              *     <li>OUTBOUND
7884              *     <li>OUTBOUND_PREVIEW</ul>
7885              * <li>DNIS: The DNIS provided. For routed calls, this is the route point.
7886              * <li>wrapUpReason: A description of the call.
7887              * <li>queueNumber: queue ID of the call.
7888              * <li>queueName: queue name of the call.
7889              * <li>callKeyCallId: unique number of the call routed on a particular day.
7890              * <li>callKeyPrefix: represents the day when the call is routed.
7891              * <li>Call Variables, by name.  The name indicates whether it is a call variable or ECC variable.
7892              * Call variable names start with callVariable#, where # is 1-10. ECC variable names (both scalar and array) are prepended with "user".
7893              * ECC variable arrays include an index enclosed within square brackets located at the end of the ECC array name.
7894              * <li>The following call variables provide additional details about an Outbound Option call:<ul>
7895              *     <li>BACampaign
7896              *     <li>BAAccountNumber
7897              *     <li>BAResponse
7898              *     <li>BAStatus<ul>
7899              *         <li>PREDICTIVE_OUTBOUND: A predictive outbound call.
7900              *         <li>PROGRESSIVE_OUTBOUND: A progressive outbound call.
7901              *         <li>PREVIEW_OUTBOUND_RESERVATION: Agent is reserved for a preview outbound call.
7902              *         <li>PREVIEW_OUTBOUND: Agent is on a preview outbound call.</ul>
7903              *     <li>BADialedListID
7904              *     <li>BATimeZone
7905              *     <li>BABuddyName</ul></ul>
7906              *
7907              */
7908             getMediaProperties: function () {
7909                 var mpData, currentMediaPropertiesMap, thisMediaPropertiesJQuery;
7910 
7911                 this.isLoaded();
7912 
7913                 // We have to convert to jQuery object to do a proper compare
7914                 thisMediaPropertiesJQuery = jQuery(this.getData().mediaProperties);
7915 
7916                 if ((this._lastMediaPropertiesJQuery !== undefined)
7917                     && (this._lastMediaPropertiesMap !== undefined)
7918                     && (this._lastMediaPropertiesJQuery.is(thisMediaPropertiesJQuery))) {
7919 
7920                     return this._lastMediaPropertiesMap;
7921                 }
7922 
7923                 currentMediaPropertiesMap = {};
7924 
7925                 mpData = this.getData().mediaProperties;
7926 
7927                 if (mpData) {
7928                     if (mpData.callvariables && mpData.callvariables.CallVariable) {
7929                         if (mpData.callvariables.CallVariable.length === undefined) {
7930                             mpData.callvariables.CallVariable = [mpData.callvariables.CallVariable];
7931                         }
7932                         jQuery.each(mpData.callvariables.CallVariable, function (i, callVariable) {
7933                             currentMediaPropertiesMap[callVariable.name] = callVariable.value;
7934                         });
7935                     }
7936 
7937                     jQuery.each(mpData, function (key, value) {
7938                         if (key !== 'callvariables') {
7939                             currentMediaPropertiesMap[key] = value;
7940                         }
7941                     });
7942                 }
7943 
7944                 this._lastMediaPropertiesMap = currentMediaPropertiesMap;
7945                 this._lastMediaPropertiesJQuery = thisMediaPropertiesJQuery;
7946 
7947                 return this._lastMediaPropertiesMap;
7948             },
7949 
7950 
7951 
7952             /**
7953              * @private
7954              * Invoke a request to the server given a content body and handlers.
7955              *
7956              * @param {Object} contentBody
7957              *     A JS object containing the body of the action request.
7958              * @param {finesse.interfaces.RequestHandlers} handlers
7959              *     An object containing the handlers for the request
7960              */
7961             _makeRequest: function (contentBody, handlers) {
7962                 // Protect against null dereferencing of options allowing its
7963                 // (nonexistent) keys to be read as undefined
7964                 handlers = handlers || {};
7965 
7966                 this.restRequest(this.getRestUrl(), {
7967                     method: 'PUT',
7968                     success: handlers.success,
7969                     error: handlers.error,
7970                     content: contentBody
7971                 });
7972             }
7973 
7974         });
7975 
7976         window.finesse = window.finesse || {};
7977         window.finesse.restservices = window.finesse.restservices || {};
7978         window.finesse.restservices.DialogBase = DialogBase;
7979 
7980 
7981         return DialogBase;
7982     });
7983 
7984 /**
7985  * JavaScript representation of the Finesse Dialog object.
7986  *
7987  * @requires finesse.clientservices.ClientServices
7988  * @requires Class
7989  * @requires finesse.FinesseBase
7990  * @requires finesse.restservices.RestBase
7991  */
7992 
7993 /** @private */
7994 define('restservices/Dialog',[
7995     'restservices/DialogBase',
7996     'utilities/Utilities'
7997 ],
7998 function (DialogBase, Utilities) {
7999     var Dialog = DialogBase.extend(/** @lends finesse.restservices.Dialog.prototype */{
8000 
8001         /**
8002          * @class
8003          * A Dialog is an attempted connection between or among multiple participants,
8004          * for example, a regular phone call, a conference, or a silent monitor session.
8005          * 
8006          * @augments finesse.restservices.DialogBase
8007          * @constructs
8008          */
8009         _fakeConstuctor: function () {
8010             /* This is here to hide the real init constructor from the public docs */
8011         },
8012         
8013         /**
8014          * @private
8015          *
8016          * @param {Object} options
8017          *     An object with the following properties:<ul>
8018          *         <li><b>id:</b> The id of the object being constructed</li>
8019          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
8020          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
8021          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
8022          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
8023          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
8024          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
8025          *             <li><b>content:</b> {String} Raw string of response</li>
8026          *             <li><b>object:</b> {Object} Parsed object of response</li>
8027          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
8028          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
8029          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
8030          *             </ul></li>
8031          *         </ul></li>
8032          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
8033          **/
8034         init: function (options) {
8035             this._super(options);
8036         },
8037 
8038         /**
8039          * @private
8040          * Gets the REST class for the current object - this is the Dialog class.
8041          * @returns {Object} The Dialog class.
8042          */
8043         getRestClass: function () {
8044             return Dialog;
8045         },
8046 
8047         /**
8048          * The requestId reaper timeout in ms
8049          */
8050         REQUESTID_REAPER_TIMEOUT: 5000,
8051 
8052         /**
8053          * Getter for the from address.
8054          * @returns {String} The from address.
8055          */
8056         getFromAddress: function () {
8057             this.isLoaded();
8058             return this.getData().fromAddress;
8059         },
8060 
8061         /**
8062          * Getter for the to address.
8063          * @returns {String} The to address.
8064          */
8065         getToAddress: function () {
8066             this.isLoaded();
8067             return this.getData().toAddress;
8068         },
8069         
8070 
8071        /**
8072          * gets the participant timer counters 
8073          *
8074          * @param {String} participantExt Extension of participant.
8075          * @returns {Object} Array of Participants which contains properties :<ul>
8076          *     <li>state - The state of the Participant. 
8077          *     <li>startTime - The start Time of the Participant.
8078          *     <li>stateChangeTime - The time when participant state has changed.</ul>
8079          */
8080         getParticipantTimerCounters : function (participantExt) {
8081           var part, participantTimerCounters = {}, idx, participants;
8082           
8083           participants = this.getParticipants();
8084 
8085 
8086           //Loop through all the participants and find the right participant (based on participantExt)
8087           for(idx=0;idx<participants.length;idx=idx+1)
8088           {
8089             part = participants[idx];
8090             
8091             if (part.mediaAddress === participantExt)
8092             {
8093                 participantTimerCounters.startTime= part.startTime;
8094                 participantTimerCounters.stateChangeTime= part.stateChangeTime;
8095                 participantTimerCounters.state= part.state;
8096                 break;
8097             }
8098           }
8099           
8100           return participantTimerCounters;
8101         },
8102         
8103         /**
8104          * Determines the droppable participants.  A droppable participant is a participant that is an agent extension.   
8105          * (It is not a CTI Route Point, IVR Port, or the caller)
8106          * 
8107          * @param {String} filterExtension used to remove a single extension from the list
8108          * @returns {Object} Array of Participants that can be dropped.
8109          * Participant entity properties are as follows:<ul>
8110          *     <li>state - The state of the Participant. 
8111          *     <li>stateCause - The state cause of the Participant.
8112          *     <li>mediaAddress - The media address of the Participant.
8113          *     <li>startTime - The start Time of the Participant.
8114          *     <li>stateChangeTime - The time when participant state has changed.
8115          *     <li>actions - These are the actions that a Participant can perform</ul>
8116          */
8117         getDroppableParticipants: function (filterExtension) {
8118           this.isLoaded();
8119           var droppableParticipants = [], participants, index, idx, filterExtensionToRemove = "", callStateOk, part;
8120 
8121           participants = this.getParticipants();
8122 
8123           if (filterExtension)
8124           {
8125             filterExtensionToRemove = filterExtension;
8126           }
8127 
8128           //Loop through all the participants to remove non-agents & remove filterExtension
8129           //We could have removed filterExtension using splice, but we have to iterate through
8130           //the list anyway.
8131           for(idx=0;idx<participants.length;idx=idx+1)
8132           {
8133             part = participants[idx];
8134 
8135             //Skip the filterExtension
8136             if (part.mediaAddress !== filterExtensionToRemove)
8137             {
8138                 callStateOk = this._isParticipantStateDroppable(part);
8139 
8140                 //Remove non-agents & make sure callstate 
8141                 if (callStateOk === true && part.mediaAddressType === this._agentDeviceType)
8142                 {
8143                   droppableParticipants.push(part);
8144                 }
8145             }
8146         }
8147 
8148         return Utilities.getArray(droppableParticipants);
8149         },
8150 
8151         _isParticipantStateDroppable : function (part)
8152         {
8153           var isParticipantStateDroppable = false;
8154           if (part.state === Dialog.ParticipantStates.ACTIVE || part.state === Dialog.ParticipantStates.ACCEPTED || part.state === Dialog.ParticipantStates.HELD)
8155           {
8156             isParticipantStateDroppable = true;
8157           }
8158           
8159           return isParticipantStateDroppable;
8160         },
8161         
8162         /**
8163          * Is the participant droppable
8164          *
8165          * @param {String} participantExt Extension of participant.
8166          * @returns {Boolean} True is droppable.
8167          */
8168         isParticipantDroppable : function (participantExt) {
8169           var droppableParticipants = null, isDroppable = false, idx, part, callStateOk;
8170           
8171           droppableParticipants = this.getDroppableParticipants();
8172           
8173           if (droppableParticipants) 
8174           {
8175             for(idx=0;idx<droppableParticipants.length;idx=idx+1)
8176             {
8177               part = droppableParticipants[idx];
8178              
8179               if (part.mediaAddress === participantExt)
8180               {
8181                 callStateOk = this._isParticipantStateDroppable(part);
8182 
8183                 //Remove non-agents & make sure callstate 
8184                 if (callStateOk === true && part.mediaAddressType === this._agentDeviceType)
8185                 {
8186                   isDroppable = true;
8187                   break;
8188                 }
8189               }
8190             }
8191           }
8192           
8193           return isDroppable;
8194         },
8195 
8196         /**
8197          * Retrieves information about the currently scheduled callback, if any.
8198          * @returns {Object} If no callback has been set, will return undefined. If 
8199          * a callback has been set, it will return a map with one or more of the 
8200          * following entries, depending on what values have been set. 
8201          *    callbackTime   - the callback time, if it has been set.
8202          *    callbackNumber - the callback number, if it has been set.
8203          */
8204         getCallbackInfo: function() {
8205             this.isLoaded();
8206             return this.getData().scheduledCallbackInfo;
8207         },
8208 
8209         /**
8210          * Invoke a consult call out to a destination.
8211          *
8212          * @param {String} mediaAddress
8213          *     The media address of the user performing the consult call.
8214          * @param {String} toAddress
8215          *     The destination address of the consult call.
8216          * @param {finesse.interfaces.RequestHandlers} handlers
8217          *     An object containing the handlers for the request
8218          */
8219         makeConsultCall: function (mediaAddress, toAddress, handlers) {
8220             this.isLoaded();
8221             var contentBody = {};
8222             contentBody[this.getRestType()] = {
8223                 "targetMediaAddress": mediaAddress,
8224                 "toAddress": toAddress,
8225                 "requestedAction": Dialog.Actions.CONSULT_CALL
8226             };
8227             this._makeRequest(contentBody, handlers);
8228             return this; // Allow cascading
8229         },
8230         
8231         /**
8232          * Invoke a single step transfer request.
8233          *
8234          * @param {String} mediaAddress
8235          *     The media address of the user performing the single step transfer.
8236          * @param {String} toAddress
8237          *     The destination address of the single step transfer.
8238          * @param {finesse.interfaces.RequestHandlers} handlers
8239          *     An object containing the handlers for the request
8240          */
8241         initiateDirectTransfer: function (mediaAddress, toAddress, handlers) {
8242             this.isLoaded();
8243             var contentBody = {};
8244             contentBody[this.getRestType()] = {
8245                 "targetMediaAddress": mediaAddress,
8246                 "toAddress": toAddress,
8247                 "requestedAction": Dialog.Actions.TRANSFER_SST
8248             };
8249             this._makeRequest(contentBody, handlers);
8250             return this; // Allow cascading
8251         },
8252 
8253         /**
8254          * Update this dialog's wrap-up reason.
8255          *
8256          * @param {String} wrapUpReason
8257          *     The new wrap-up reason for this dialog
8258          * @param {finesse.interfaces.RequestHandlers} handlers
8259          *     An object containing the handlers for the request
8260          */
8261         updateWrapUpReason: function (wrapUpReason, options)
8262         {
8263             this.isLoaded();
8264             var mediaProperties =
8265             {
8266                 "wrapUpReason": wrapUpReason
8267             };
8268 
8269             options = options || {};
8270             options.content = {};
8271             options.content[this.getRestType()] =
8272             {
8273                 "mediaProperties": mediaProperties,
8274                 "requestedAction": Dialog.Actions.UPDATE_CALL_DATA
8275             };
8276             options.method = "PUT";
8277             this.restRequest(this.getRestUrl(), options);
8278 
8279             return this;
8280         },
8281 
8282         /**
8283          * Invoke a request to server based on the action given.
8284          * @param {String} mediaAddress
8285          *     The media address of the user performing the action.
8286          * @param {finesse.restservices.Dialog.Actions} action
8287          *     The action string indicating the action to invoke on dialog.
8288          * @param {finesse.interfaces.RequestHandlers} handlers
8289          *     An object containing the handlers for the request
8290          */
8291         requestAction: function (mediaAddress, action, handlers) {
8292             this.isLoaded();
8293             var contentBody = {};
8294             contentBody[this.getRestType()] = {
8295                 "targetMediaAddress": mediaAddress,
8296                 "requestedAction": action
8297             };
8298             this._makeRequest(contentBody, handlers);
8299             return this; // Allow cascading
8300         },
8301         
8302         /**
8303          * Wrapper around "requestAction" to request PARTICIPANT_DROP action.
8304          *
8305          * @param targetMediaAddress is the address to drop
8306          * @param {finesse.interfaces.RequestHandlers} handlers
8307          *     An object containing the handlers for the request
8308          */
8309         dropParticipant: function (targetMediaAddress, handlers) {
8310             this.requestAction(targetMediaAddress, Dialog.Actions.PARTICIPANT_DROP, handlers);
8311         },
8312         
8313         /**
8314          * Invoke a request to server to send DTMF digit tones.
8315          * @param {String} mediaAddress
8316          * @param {finesse.interfaces.RequestHandlers} handlers
8317          *     An object containing the handlers for the request
8318          * @param {String} digit
8319          *     The digit which causes invocation of an action on the dialog.
8320          */
8321         sendDTMFRequest: function (mediaAddress, handlers, digit) {
8322             this.isLoaded();
8323             var contentBody = {};
8324             contentBody[this.getRestType()] = {
8325                 "targetMediaAddress": mediaAddress,
8326                 "requestedAction": "SEND_DTMF",
8327                 "actionParams": {
8328                     "ActionParam": {
8329                         "name": "dtmfString",
8330                         "value": digit
8331                     }
8332                 }
8333             };
8334             this._makeRequest(contentBody, handlers);
8335             return this; // Allow cascading
8336         },
8337 
8338         /**
8339          * Invoke a request to server to set the time for a callback.
8340          * @param {String} mediaAddress
8341          * @param {String} callbackTime 
8342          *     The requested time for the callback, in YYYY-MM-DDTHH:MM format
8343          *     (ex: 2013-12-24T23:59)
8344          * @param {finesse.interfaces.RequestHandlers} handlers
8345          *     An object containing the handlers for the request
8346          */
8347         updateCallbackTime: function (mediaAddress, callbackTime, handlers) {
8348             this.isLoaded();
8349             var contentBody = {};
8350             contentBody[this.getRestType()] = {
8351                 "targetMediaAddress": mediaAddress,
8352                 "requestedAction": Dialog.Actions.UPDATE_SCHEDULED_CALLBACK,
8353                 "actionParams": {
8354                     "ActionParam": {
8355                         "name": "callbackTime",
8356                         "value": callbackTime
8357                     }
8358                 }
8359             };
8360             this._makeRequest(contentBody, handlers);
8361             return this; // Allow cascading
8362         },
8363 
8364         /**
8365          * Invoke a request to server to set the number for a callback.
8366          * @param {String} mediaAddress
8367          * @param {String} callbackNumber
8368          *     The requested number to call for the callback
8369          * @param {finesse.interfaces.RequestHandlers} handlers
8370          *     An object containing the handlers for the request
8371          */
8372         updateCallbackNumber: function (mediaAddress, callbackNumber, handlers) {
8373             this.isLoaded();
8374             var contentBody = {};
8375             contentBody[this.getRestType()] = {
8376                 "targetMediaAddress": mediaAddress,
8377                 "requestedAction": Dialog.Actions.UPDATE_SCHEDULED_CALLBACK,
8378                 "actionParams": {
8379                     "ActionParam": {
8380                         "name": "callbackNumber",
8381                         "value": callbackNumber
8382                     }
8383                 }
8384             };
8385             this._makeRequest(contentBody, handlers);
8386             return this; // Allow cascading
8387         },
8388 
8389         /**
8390          * Invoke a request to server to cancel a callback.
8391          * @param {String} mediaAddress
8392          * @param {finesse.interfaces.RequestHandlers} handlers
8393          *     An object containing the handlers for the request
8394          */
8395         cancelCallback: function (mediaAddress, handlers) {
8396             this.isLoaded();
8397             var contentBody = {};
8398             contentBody[this.getRestType()] = {
8399                 "targetMediaAddress": mediaAddress,
8400                 "requestedAction": Dialog.Actions.CANCEL_SCHEDULED_CALLBACK
8401             };
8402             this._makeRequest(contentBody, handlers);
8403             return this; // Allow cascading
8404         },
8405 
8406         /**
8407          * Invoke a request to server to reclassify the call type.
8408          * @param {String} mediaAddress
8409          *     The media address of the user performing the consult call.
8410          * @param {String} classification
8411          *     The classification to assign to the call. Valid values are "VOICE", "FAX",
8412          *     "ANS_MACHINE", "INVALID", "BUSY" (CCX only), and "DO_NOT_CALL".
8413          * @param {finesse.interfaces.RequestHandlers} handlers
8414          *     An object containing the handlers for the request
8415          */
8416         reclassifyCall: function (mediaAddress, classification, handlers) {
8417             this.isLoaded();
8418             var contentBody = {};
8419             contentBody[this.getRestType()] = {
8420                 "targetMediaAddress": mediaAddress,
8421                 "requestedAction": Dialog.Actions.RECLASSIFY,
8422                 "actionParams": {
8423                     "ActionParam": {
8424                         "name": "outboundClassification",
8425                         "value": classification
8426                     }
8427                 }
8428             };
8429             this._makeRequest(contentBody, handlers);
8430             return this; // Allow cascading
8431         },
8432 
8433         /**
8434          * Utility method to create a closure containing the requestId and the Dialogs object so 
8435          * that the _pendingCallbacks map can be manipulated when the timer task is executed.
8436          * @param  {String} requestId The requestId of the event
8437          * @return {Function}           The function to be executed by setTimeout
8438          */
8439         _createRequestIdReaper: function (requestId) {
8440             var that = this;
8441             return function () {
8442                 that._logger.log("Dialog: clearing the requestId-to-callbacks mapping for requestId=" + requestId);
8443                 delete that._pendingCallbacks[requestId];
8444             };
8445         },
8446 
8447         /**
8448          * Overriding implementation of the one in RestBase.js
8449          * This determines the strategy that Dialogs will take after processing an event that contains a requestId.
8450          * @param  {String} requestId The requestId of the event
8451          */
8452         _postProcessUpdateStrategy: function (requestId) {
8453             this._logger.log("Dialog: determining whether to set timeout for clearing requestId-to-callbacks mapping requestId=" + requestId);
8454             var callbacksObj = this._pendingCallbacks[requestId];
8455             if (callbacksObj && !callbacksObj.used) {
8456                 this._logger.log("Dialog: setting timeout for clearing requestId-to-callbacks mapping requestId=" + requestId);
8457                 setTimeout(this._createRequestIdReaper(requestId), this.REQUESTID_REAPER_TIMEOUT);
8458                 callbacksObj.used = true;
8459             }            
8460         }
8461 
8462     });
8463 
8464     Dialog.Actions = /** @lends finesse.restservices.Dialog.Actions.prototype */ {
8465             /**
8466              * Drops the Participant from the Dialog.
8467              */
8468             DROP: "DROP",
8469             /**
8470              * Answers a Dialog.
8471              */
8472             ANSWER: "ANSWER",
8473             /**
8474              * Holds the Dialog.
8475              */
8476             HOLD: "HOLD",
8477             /**
8478              * Barges into a Call Dialog.
8479              */
8480             BARGE_CALL: "BARGE_CALL",
8481             /**
8482              * Allow as Supervisor to Drop a Participant from the Dialog.
8483              */
8484             PARTICIPANT_DROP: "PARTICIPANT_DROP",
8485             /**
8486              * Makes a new Call Dialog.
8487              */
8488             MAKE_CALL: "MAKE_CALL",
8489             /**
8490              * Retrieves a Dialog that is on Hold.
8491              */
8492             RETRIEVE: "RETRIEVE",
8493             /**
8494              * Sets the time or number for a callback. Can be
8495              * either a new callback, or updating an existing one.
8496              */
8497             UPDATE_SCHEDULED_CALLBACK: "UPDATE_SCHEDULED_CALLBACK",
8498             /**
8499              * Cancels a callback.
8500              */
8501             CANCEL_SCHEDULED_CALLBACK: "CANCEL_SCHEDULED_CALLBACK",
8502             /**
8503              * Initiates a Consult Call.
8504              */
8505             CONSULT_CALL: "CONSULT_CALL",
8506             /**
8507              * Initiates a Transfer of a Dialog.
8508              */
8509             TRANSFER: "TRANSFER",
8510             /**
8511              * Initiates a Single-Step Transfer of a Dialog.
8512              */
8513             TRANSFER_SST: "TRANSFER_SST",
8514             /**
8515              * Initiates a Conference of a Dialog.
8516              */
8517             CONFERENCE: "CONFERENCE",
8518             /**
8519              * Changes classification for a call
8520              */
8521             RECLASSIFY: "RECLASSIFY", 
8522             /**
8523              * Updates data on a Call Dialog.
8524              */
8525             UPDATE_CALL_DATA: "UPDATE_CALL_DATA",
8526             /**
8527              * Initiates a Recording on a Call Dialog.
8528              */
8529             START_RECORDING : "START_RECORDING",
8530             /**
8531              * Sends DTMF (dialed digits) to a Call Dialog.
8532              */
8533             DTMF : "SEND_DTMF",            
8534             /**
8535              * Accepts a Dialog that is being Previewed.
8536              */
8537             ACCEPT: "ACCEPT",
8538             /**
8539              * Rejects a Dialog.
8540              */
8541             REJECT: "REJECT",
8542             /**
8543              * Closes a Dialog.
8544              */
8545             CLOSE : "CLOSE",
8546             /**
8547              * @class Set of action constants for a Dialog.  These should be used for
8548              * {@link finesse.restservices.Dialog#requestAction}.
8549              * @constructs
8550              */
8551             _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
8552         };
8553 
8554     Dialog.States = /** @lends finesse.restservices.Dialog.States.prototype */ {
8555        /**
8556          * Indicates that the call is ringing at a device.
8557          */
8558         ALERTING: "ALERTING",
8559         /**
8560          * Indicates that the phone is off the hook at a device.
8561          */
8562         INITIATING: "INITIATING",
8563         /**
8564          * Indicates that the dialog has a least one active participant.
8565          */
8566         ACTIVE: "ACTIVE",
8567         /**
8568          * Indicates that the dialog has no active participants.
8569          */
8570         DROPPED: "DROPPED",
8571         /**
8572          * Indicates that the phone is dialing at the device.
8573          */
8574         INITIATED: "INITIATED",
8575         /**
8576          * Indicates that the dialog has failed.
8577          * @see Dialog.ReasonStates
8578          */
8579         FAILED: "FAILED",
8580         /**
8581          * Indicates that the user has accepted an OUTBOUND_PREVIEW dialog.
8582          */
8583         ACCEPTED: "ACCEPTED",
8584         /**
8585          * @class Possible Dialog State constants.
8586          * The State flow of a typical in-bound Dialog is as follows: INITIATING, INITIATED, ALERTING, ACTIVE, DROPPED.
8587          * @constructs
8588          */
8589         _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
8590     };
8591 
8592     Dialog.ParticipantStates = /** @lends finesse.restservices.Dialog.ParticipantStates.prototype */ {
8593         /**
8594           * Indicates that an incoming call is ringing on the device.
8595           */
8596          ALERTING: "ALERTING",
8597          /**
8598           * Indicates that an outgoing call, not yet active, exists on the device.
8599           */
8600          INITIATING: "INITIATING",
8601          /**
8602           * Indicates that the participant is active on the call.
8603           */
8604          ACTIVE: "ACTIVE",
8605          /**
8606           * Indicates that the participant has dropped from the call.
8607           */
8608          DROPPED: "DROPPED",
8609          /**
8610           * Indicates that the participant has held their connection to the call.
8611           */
8612          HELD: "HELD",
8613          /**
8614           * Indicates that the phone is dialing at a device.
8615           */
8616          INITIATED: "INITIATED",
8617          /**
8618           * Indicates that the call failed.
8619           * @see Dialog.ReasonStates
8620           */
8621          FAILED: "FAILED",
8622          /**
8623           * Indicates that the participant is not in active state on the call, but is wrapping up after the participant has dropped from the call.
8624           */
8625          WRAP_UP: "WRAP_UP",
8626          /**
8627           * Indicates that the participant has accepted the dialog.  This state is applicable to OUTBOUND_PREVIEW dialogs.
8628           */
8629          ACCEPTED: "ACCEPTED",
8630          /**
8631           * @class Possible Dialog Participant State constants.
8632           * @constructs
8633           */
8634          _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
8635      };
8636 
8637     Dialog.ReasonStates = /** @lends finesse.restservices.Dialog.ReasonStates.prototype */ {
8638        /**
8639         * Dialog was Busy.  This will typically be for a Failed Dialog.
8640         */
8641         BUSY: "BUSY",
8642         /**
8643          * Dialog reached a Bad Destination.  This will typically be for a Failed Dialog.
8644          */
8645         BAD_DESTINATION: "BAD_DESTINATION",
8646         /**
8647          * All Other Reasons.  This will typically be for a Failed Dialog.
8648          */
8649         OTHER: "OTHER",
8650         /**
8651          * The Device Resource for the Dialog was not available.
8652          */
8653         DEVICE_RESOURCE_NOT_AVAILABLE : "DEVICE_RESOURCE_NOT_AVAILABLE",
8654         /**
8655          * @class Possible dialog state reasons code constants.
8656              * @constructs
8657              */
8658             _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
8659     };
8660 
8661     window.finesse = window.finesse || {};
8662     window.finesse.restservices = window.finesse.restservices || {};
8663     window.finesse.restservices.Dialog = Dialog;
8664     
8665     
8666     return Dialog;
8667 });
8668 
8669 /**
8670  * JavaScript representation of the Finesse Dialogs collection
8671  * object which contains a list of Dialog objects.
8672  *
8673  * @requires finesse.clientservices.ClientServices
8674  * @requires Class
8675  * @requires finesse.FinesseBase
8676  * @requires finesse.restservices.RestBase
8677  * @requires finesse.restservices.Dialog
8678  */
8679 /** @private */
8680 define('restservices/Dialogs',[
8681     'restservices/RestCollectionBase',
8682     'restservices/Dialog'
8683 ],
8684 function (RestCollectionBase, Dialog) {
8685     var Dialogs = RestCollectionBase.extend(/** @lends finesse.restservices.Dialogs.prototype */{
8686 
8687         /**
8688          * @class
8689          * JavaScript representation of a Dialogs collection object. Also exposes
8690          * methods to operate on the object against the server.
8691          * @augments finesse.restservices.RestCollectionBase
8692          * @constructs
8693          * @see finesse.restservices.Dialog
8694          * @example
8695          *  _dialogs = _user.getDialogs( {
8696          *      onCollectionAdd : _handleDialogAdd,
8697          *      onCollectionDelete : _handleDialogDelete,
8698          *      onLoad : _handleDialogsLoaded
8699          *  });
8700          *  
8701          * _dialogCollection = _dialogs.getCollection();
8702          * for (var dialogId in _dialogCollection) {
8703          *     if (_dialogCollection.hasOwnProperty(dialogId)) {
8704          *         _dialog = _dialogCollection[dialogId];
8705          *         etc...
8706          *     }
8707          * }
8708          */
8709         _fakeConstuctor: function () {
8710             /* This is here to hide the real init constructor from the public docs */
8711         },
8712         
8713         /**
8714          * @private
8715          * @param {Object} options
8716          *     An object with the following properties:<ul>
8717          *         <li><b>id:</b> The id of the object being constructed</li>
8718          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
8719          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
8720          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
8721          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
8722          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
8723          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
8724          *             <li><b>content:</b> {String} Raw string of response</li>
8725          *             <li><b>object:</b> {Object} Parsed object of response</li>
8726          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
8727          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
8728          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
8729          *             </ul></li>
8730          *         </ul></li>
8731          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
8732          **/
8733         init: function (options) {
8734             this._super(options);
8735         },
8736 
8737         /**
8738          * @private
8739          * Gets the REST class for the current object - this is the Dialogs class.
8740          */
8741         getRestClass: function () {
8742             return Dialogs;
8743         },
8744 
8745         /**
8746          * @private
8747          * Gets the REST class for the objects that make up the collection. - this
8748          * is the Dialog class.
8749          */
8750         getRestItemClass: function () {
8751             return Dialog;
8752         },
8753 
8754         /**
8755          * @private
8756          * Gets the REST type for the current object - this is a "Dialogs".
8757          */
8758         getRestType: function () {
8759             return "Dialogs";
8760         },
8761 
8762         /**
8763          * @private
8764          * Gets the REST type for the objects that make up the collection - this is "Dialogs".
8765          */
8766         getRestItemType: function () {
8767             return "Dialog";
8768         },
8769 
8770         /**
8771          * @private
8772          * Override default to indicates that the collection doesn't support making
8773          * requests.
8774          */
8775         supportsRequests: true,
8776 
8777         /**
8778          * @private
8779          * Override default to indicates that the collection subscribes to its objects.
8780          */
8781         supportsRestItemSubscriptions: true,
8782 
8783         /**
8784          * The requestId reaper timeout in ms
8785          */
8786         REQUESTID_REAPER_TIMEOUT: 5000,
8787 
8788         /**
8789          * @private
8790          * Create a new Dialog in this collection
8791          *
8792          * @param {String} toAddress
8793          *     The to address of the new Dialog
8794          * @param {String} fromAddress
8795          *     The from address of the new Dialog
8796          * @param {finesse.interfaces.RequestHandlers} handlers
8797          *     An object containing the (optional) handlers for the request.
8798          * @return {finesse.restservices.Dialogs}
8799          *     This Dialogs object, to allow cascading.
8800          */
8801         createNewCallDialog: function (toAddress, fromAddress, handlers)
8802         {
8803             var contentBody = {};
8804             contentBody[this.getRestItemType()] = {
8805                 "requestedAction": "MAKE_CALL",
8806                 "toAddress": toAddress,
8807                 "fromAddress": fromAddress
8808             };
8809 
8810             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
8811             handlers = handlers || {};
8812 
8813             this.restRequest(this.getRestUrl(), {
8814                 method: 'POST',
8815                 success: handlers.success,
8816                 error: handlers.error,
8817                 content: contentBody
8818             });
8819             return this; // Allow cascading
8820         },
8821 
8822         /**
8823          * @private
8824          * Create a new Dialog in this collection as a result of a requested action
8825          *
8826          * @param {String} toAddress
8827          *     The to address of the new Dialog
8828          * @param {String} fromAddress
8829          *     The from address of the new Dialog
8830          * @param {finesse.restservices.Dialog.Actions} actionType
8831          *     The associated action to request for creating this new dialog
8832          * @param {finesse.interfaces.RequestHandlers} handlers
8833          *     An object containing the (optional) handlers for the request.
8834          * @return {finesse.restservices.Dialogs}
8835          *     This Dialogs object, to allow cascading.
8836          */
8837         createNewSuperviseCallDialog: function (toAddress, fromAddress, actionType, handlers)
8838         {
8839             var contentBody = {};
8840             this._isLoaded = true;
8841 
8842             contentBody[this.getRestItemType()] = {
8843                 "requestedAction": actionType,
8844                 "toAddress": toAddress,
8845                 "fromAddress": fromAddress
8846             };
8847 
8848             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
8849             handlers = handlers || {};
8850 
8851             this.restRequest(this.getRestUrl(), {
8852                 method: 'POST',
8853                 success: handlers.success,
8854                 error: handlers.error,
8855                 content: contentBody
8856             });
8857             return this; // Allow cascading
8858         },
8859         
8860         /**
8861          * @private
8862          * Create a new Dialog in this collection as a result of a requested action
8863          * @param {String} fromAddress
8864          *     The from address of the new Dialog
8865          * @param {String} toAddress
8866          *     The to address of the new Dialog
8867          * @param {finesse.restservices.Dialog.Actions} actionType
8868          *     The associated action to request for creating this new dialog
8869          * @param {String} dialogUri
8870          *     The associated uri of SUPERVISOR_MONITOR call
8871          * @param {finesse.interfaces.RequestHandlers} handlers
8872          *     An object containing the (optional) handlers for the request.
8873          * @return {finesse.restservices.Dialogs}
8874          *     This Dialogs object, to allow cascading.
8875          */
8876         createNewBargeCall: function (fromAddress, toAddress, actionType, dialogURI, handlers) {
8877             this.isLoaded();
8878          
8879             var contentBody = {};
8880             contentBody[this.getRestItemType()] = {
8881                 "fromAddress": fromAddress,
8882                 "toAddress": toAddress,
8883                 "requestedAction": actionType,
8884                 "associatedDialogUri": dialogURI
8885                 
8886             };
8887             // (nonexistent) keys to be read as undefined
8888             handlers = handlers || {};  
8889             this.restRequest(this.getRestUrl(), {
8890                 method: 'POST',
8891                 success: handlers.success,
8892                 error: handlers.error,
8893                 content: contentBody
8894             });
8895             return this; // Allow cascading
8896         },
8897 
8898         /**
8899          * Utility method to get the number of dialogs in this collection.
8900          * 'excludeSilentMonitor' flag is provided as an option to exclude calls with type
8901          * 'SUPERVISOR_MONITOR' from the count.
8902          * @param  {Boolean} excludeSilentMonitor If true, calls with type of 'SUPERVISOR_MONITOR' will be excluded from the count.
8903          * @return {Number} The number of dialogs in this collection.
8904          */
8905         getDialogCount: function (excludeSilentMonitor) {
8906             this.isLoaded();
8907 
8908             var dialogId, count = 0;
8909             if (excludeSilentMonitor) {
8910                 for (dialogId in this._collection) {
8911                     if (this._collection.hasOwnProperty(dialogId)) {
8912                         if (this._collection[dialogId].getCallType() !== 'SUPERVISOR_MONITOR') {
8913                             count += 1;
8914                         }
8915                     }
8916                 }
8917 
8918                 return count;
8919             } else {
8920                 return this.length;
8921             }        
8922         },
8923 
8924         /**
8925          * Utility method to create a closure containing the requestId and the Dialogs object so 
8926          * that the _pendingCallbacks map can be manipulated when the timer task is executed.
8927          * @param  {String} requestId The requestId of the event
8928          * @return {Function}           The function to be executed by setTimeout
8929          */
8930         _createRequestIdReaper: function (requestId) {
8931             var that = this;
8932             return function () {
8933                 that._logger.log("Dialogs: clearing the requestId-to-callbacks mapping for requestId=" + requestId);
8934                 delete that._pendingCallbacks[requestId];
8935             };
8936         },
8937 
8938         /**
8939          * Overriding implementation of the one in RestBase.js
8940          * This determines the strategy that Dialogs will take after processing an event that contains a requestId.
8941          * @param  {String} requestId The requestId of the event
8942          */
8943         _postProcessUpdateStrategy: function (requestId) {
8944             this._logger.log("Dialogs: determining whether to set timeout for clearing requestId-to-callbacks mapping requestId=" + requestId);
8945             var callbacksObj = this._pendingCallbacks[requestId];
8946             if (callbacksObj && !callbacksObj.used) {
8947                 this._logger.log("Dialogs: setting timeout for clearing requestId-to-callbacks mapping requestId=" + requestId);
8948                 setTimeout(this._createRequestIdReaper(requestId), this.REQUESTID_REAPER_TIMEOUT);
8949                 callbacksObj.used = true;
8950             }            
8951         }
8952 
8953     });
8954     
8955     window.finesse = window.finesse || {};
8956     window.finesse.restservices = window.finesse.restservices || {};
8957     window.finesse.restservices.Dialogs = Dialogs;
8958     
8959     return Dialogs;
8960 });
8961 
8962 /**
8963  * JavaScript representation of the Finesse ClientLog object
8964  *
8965  * @requires finesse.clientservices.ClientServices
8966  * @requires Class
8967  * @requires finesse.FinesseBase
8968  * @requires finesse.restservices.RestBase
8969  */
8970 
8971 /** The following comment is to prevent jslint errors about 
8972  * using variables before they are defined.
8973  */
8974 /** @private */
8975 /*global finesse*/
8976 
8977 define('restservices/ClientLog',["restservices/RestBase"], function (RestBase) {
8978     
8979     var ClientLog = RestBase.extend(/** @lends finesse.restservices.ClientLog.prototype */{    
8980         /**
8981          * @private
8982          * Returns whether this object supports transport logs
8983          */
8984         doNotLog : true,
8985         
8986         explicitSubscription : true,
8987         
8988         /**
8989          * @class
8990          * @private
8991          * JavaScript representation of a ClientLog object. Also exposes methods to operate
8992          * on the object against the server.
8993          *
8994          * @param {Object} options
8995          *     An object with the following properties:<ul>
8996          *         <li><b>id:</b> The id of the object being constructed</li>
8997          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
8998          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
8999          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
9000          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
9001          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
9002          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
9003          *             <li><b>content:</b> {String} Raw string of response</li>
9004          *             <li><b>object:</b> {Object} Parsed object of response</li>
9005          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
9006          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
9007          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
9008          *             </ul></li>
9009          *         </ul></li>
9010          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
9011          * @constructs
9012          * @augments finesse.restservices.RestBase
9013          **/
9014         init: function (options) {
9015             this._super({
9016                 id: "", 
9017                 data: {clientLog : null},
9018                 onAdd: options.onAdd,
9019                 onChange: options.onChange,
9020                 onLoad: options.onLoad,
9021                 onError: options.onError,
9022                 parentObj: options.parentObj
9023                 });
9024         },
9025 
9026         /**
9027          * @private
9028          * Gets the REST class for the current object - this is the ClientLog object.
9029          */
9030         getRestClass: function () {
9031             return ClientLog;
9032         },
9033 
9034         /**
9035          * @private
9036          * Gets the REST type for the current object - this is a "ClientLog".
9037          */
9038         getRestType: function ()
9039         {
9040             return "ClientLog";
9041         },
9042         
9043         /**
9044          * @private
9045          * Gets the node path for the current object
9046          * @returns {String} The node path
9047          */
9048         getXMPPNodePath: function () {
9049             return this.getRestUrl();
9050         },
9051 
9052         /**
9053          * @private
9054          * Utility method to fetch this object from the server, however we
9055          * override it for ClientLog to not do anything because GET is not supported
9056          * for ClientLog object.
9057          */
9058         _doGET: function (handlers) {
9059             return;
9060         },
9061            
9062         /**
9063          * @private
9064          * Invoke a request to the server given a content body and handlers.
9065          *
9066          * @param {Object} contentBody
9067          *     A JS object containing the body of the action request.
9068          * @param {Object} handlers
9069          *     An object containing the following (optional) handlers for the request:<ul>
9070          *         <li><b>success(rsp):</b> A callback function for a successful request to be invoked with the following
9071          *         response object as its only parameter:<ul>
9072          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
9073          *             <li><b>content:</b> {String} Raw string of response</li>
9074          *             <li><b>object:</b> {Object} Parsed object of response</li></ul>
9075          *         <li>A error callback function for an unsuccessful request to be invoked with the
9076          *         error response object as its only parameter:<ul>
9077          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
9078          *             <li><b>content:</b> {String} Raw string of response</li>
9079          *             <li><b>object:</b> {Object} Parsed object of response (HTTP errors)</li>
9080          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
9081          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
9082          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
9083          *             </ul></li>
9084          *         </ul>
9085          */
9086         sendLogs: function (contentBody, handlers) {
9087             // Protect against null dereferencing of options allowing its
9088             // (nonexistent) keys to be read as undefined
9089             handlers = handlers || {};
9090 
9091             this.restRequest(this.getRestUrl(), {
9092                 method: 'POST',
9093                 //success: handlers.success,
9094                 error: handlers.error,
9095                 content: contentBody
9096             });
9097         }
9098     });
9099     
9100     window.finesse = window.finesse || {};
9101     window.finesse.restservices = window.finesse.restservices || {};
9102     window.finesse.restservices.ClientLog = ClientLog;
9103     
9104     return ClientLog;
9105 });
9106 
9107 /**
9108  * JavaScript representation of the Finesse Queue object
9109  * @requires finesse.clientservices.ClientServices
9110  * @requires Class
9111  * @requires finesse.FinesseBase
9112  * @requires finesse.restservices.RestBase
9113  */
9114 
9115 /** @private */
9116 define('restservices/Queue',[
9117     'restservices/RestBase',
9118     'utilities/Utilities'
9119 ],
9120 function (RestBase, Utilities) {
9121     var Queue = RestBase.extend(/** @lends finesse.restservices.Queue.prototype */{
9122 
9123         /**
9124          * @class
9125          * A Queue is a list of Contacts available to a User for quick dial.
9126          * 
9127          * @augments finesse.restservices.RestBase
9128          * @constructs
9129          */
9130         _fakeConstuctor: function () {
9131             /* This is here to hide the real init constructor from the public docs */
9132         },
9133         
9134 		/**
9135 		 * @private
9136 		 * JavaScript representation of a Queue object. Also exposes methods to operate
9137 		 * on the object against the server.
9138 		 *
9139 		 * @constructor
9140 		 * @param {String} id
9141 		 *     Not required...
9142 		 * @param {Object} callbacks
9143 		 *     An object containing callbacks for instantiation and runtime
9144 		 * @param {Function} callbacks.onLoad(this)
9145 		 *     Callback to invoke upon successful instantiation
9146 		 * @param {Function} callbacks.onLoadError(rsp)
9147 		 *     Callback to invoke on instantiation REST request error
9148 		 *     as passed by finesse.clientservices.ClientServices.ajax()
9149 		 *     {
9150 		 *         status: {Number} The HTTP status code returned
9151 		 *         content: {String} Raw string of response
9152 		 *         object: {Object} Parsed object of response
9153 		 *         error: {Object} Wrapped exception that was caught
9154 		 *         error.errorType: {String} Type of error that was caught
9155 		 *         error.errorMessage: {String} Message associated with error
9156 		 *     }
9157 		 * @param {Function} callbacks.onChange(this)
9158 		 *     Callback to invoke upon successful update
9159 		 * @param {Function} callbacks.onError(rsp)
9160 		 *     Callback to invoke on update error (refresh or event)
9161 		 *     as passed by finesse.clientservices.ClientServices.ajax()
9162 		 *     {
9163 		 *         status: {Number} The HTTP status code returned
9164 		 *         content: {String} Raw string of response
9165 		 *         object: {Object} Parsed object of response
9166 		 *         error: {Object} Wrapped exception that was caught
9167 		 *         error.errorType: {String} Type of error that was caught
9168 		 *         error.errorMessage: {String} Message associated with error
9169 		 *     }
9170 		 *  
9171 		 */
9172         init: function (id, callbacks, restObj) {
9173             this._super(id, callbacks, restObj);
9174         },
9175 
9176         /**
9177          * @private
9178          * Gets the REST class for the current object - this is the Queue object.
9179          */
9180         getRestClass: function () {
9181             return Queue;
9182         },
9183 
9184         /**
9185          * @private
9186          * Gets the REST type for the current object - this is a "Queue".
9187          */
9188         getRestType: function () {
9189             return "Queue";
9190         },
9191 
9192         /**
9193          * @private
9194          * Returns whether this object supports subscriptions
9195          */
9196         supportsSubscriptions: function () {
9197             return true;
9198         },
9199         
9200         /**
9201          * @private
9202          * Specifies whether this object's subscriptions need to be explicitly requested
9203          */
9204         explicitSubscription: true,
9205         
9206         /**
9207          * @private
9208          * Gets the node path for the current object - this is the team Users node
9209          * @returns {String} The node path
9210          */
9211         getXMPPNodePath: function () {
9212             return this.getRestUrl();
9213         },
9214         
9215         /**
9216          * Getter for the queue id
9217          * @returns {String}
9218          *     The id of the Queue
9219          */
9220         getId: function () {
9221             this.isLoaded();
9222             return this._id;
9223         },
9224         
9225         /**
9226          * Getter for the queue name
9227          * @returns {String}
9228          *      The name of the Queue
9229          */
9230         getName: function () {
9231             this.isLoaded();
9232             return this.getData().name;
9233         },
9234         
9235         /**
9236          * Getter for the queue statistics.
9237          * Supported statistics include:<br>
9238          *  - callsInQueue<br>
9239          *  - startTimeOfLongestCallInQueue<br>
9240          *  <br>
9241          *  These statistics can be accessed via dot notation:<br>
9242          *  i.e.: getStatistics().callsInQueue
9243          * @returns {Object}
9244          *      The Object with different statistics as properties.
9245          */
9246         getStatistics: function () {
9247             this.isLoaded();
9248             return this.getData().statistics;       
9249         },
9250 
9251         /**
9252          * Parses a uriString to retrieve the id portion
9253          * @param {String} uriString
9254          * @return {String} id
9255          */
9256         _parseIdFromUriString : function (uriString) {
9257             return Utilities.getId(uriString);
9258         }
9259 
9260     });
9261 	
9262 	window.finesse = window.finesse || {};
9263     window.finesse.restservices = window.finesse.restservices || {};
9264     window.finesse.restservices.Queue = Queue;
9265     
9266     return Queue;
9267 });
9268 
9269 /**
9270  * JavaScript representation of the Finesse Queues collection
9271  * object which contains a list of Queue objects.
9272  * @requires finesse.clientservices.ClientServices
9273  * @requires Class
9274  * @requires finesse.FinesseBase
9275  * @requires finesse.restservices.RestBase
9276  * @requires finesse.restservices.RestCollectionBase
9277  */
9278 
9279 /**
9280  * @class
9281  * JavaScript representation of a Queues collection object.
9282  *
9283  * @constructor
9284  * @borrows finesse.restservices.RestCollectionBase as finesse.restservices.Queues
9285  */
9286 
9287 /** @private */
9288 define('restservices/Queues',[
9289     'restservices/RestCollectionBase',
9290     'restservices/Queue'
9291 ],
9292 function (RestCollectionBase, Queue) {
9293     var Queues = RestCollectionBase.extend(/** @lends finesse.restservices.Queues.prototype */{
9294 
9295         /**
9296          * @class
9297          * JavaScript representation of a Queues collection object. 
9298          * @augments finesse.restservices.RestCollectionBase
9299          * @constructs
9300          * @see finesse.restservices.Queue
9301          * @example
9302          *  _queues = _user.getQueues( {
9303          *      onCollectionAdd : _handleQueueAdd,
9304          *      onCollectionDelete : _handleQueueDelete,
9305          *      onLoad : _handleQueuesLoaded
9306          *  });
9307          *  
9308          * _queueCollection = _queues.getCollection();
9309          * for (var queueId in _queueCollection) {
9310          *     if (_queueCollection.hasOwnProperty(queueId)) {
9311          *         _queue = _queueCollection[queueId];
9312          *         etc...
9313          *     }
9314          * }
9315          */
9316         _fakeConstuctor: function () {
9317             /* This is here to hide the real init constructor from the public docs */
9318         },
9319 	    
9320          /**
9321          * @private
9322          * JavaScript representation of a Queues object. Also exposes
9323          * methods to operate on the object against the server.
9324          *
9325          * @param {Object} options
9326          *     An object with the following properties:<ul>
9327          *         <li><b>id:</b> The id of the object being constructed</li>
9328          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
9329          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
9330          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
9331          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
9332          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
9333          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
9334          *             <li><b>content:</b> {String} Raw string of response</li>
9335          *             <li><b>object:</b> {Object} Parsed object of response</li>
9336          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
9337          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
9338          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
9339          *             </ul></li>
9340          *         </ul></li>
9341          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
9342          **/
9343         init: function (options) {
9344             this._super(options);           
9345         },
9346 
9347         /**
9348          * @private
9349          * Gets xmpp node path.
9350          */
9351         getXMPPNodePath: function () {
9352             return this.getRestUrl();
9353         },
9354 
9355         /**
9356          * @private
9357          * Gets the REST class for the current object - this is the Queues class.
9358          */
9359         getRestClass: function () {
9360             return Queues;
9361         },
9362 
9363         /**
9364          * @private
9365          * Gets the REST class for the objects that make up the collection. - this
9366          * is the Queue class.
9367          */
9368         getRestItemClass: function () {
9369             return Queue;
9370         },
9371 
9372         /**
9373          * @private
9374          * Gets the REST type for the current object - this is a "Queues".
9375          */
9376         getRestType: function () {
9377             return "Queues";
9378         },
9379         
9380         /**
9381          * @private
9382          * Gets the REST type for the objects that make up the collection - this is "Queue".
9383          */
9384         getRestItemType: function () {
9385             return "Queue";
9386         },
9387 
9388         explicitSubscription: true,
9389         
9390         handlesItemRefresh: true
9391     });
9392     
9393     window.finesse = window.finesse || {};
9394     window.finesse.restservices = window.finesse.restservices || {};
9395     window.finesse.restservices.Queues = Queues;
9396     
9397     return Queues;
9398 });
9399 
9400 /**
9401  * JavaScript representation of the Finesse WrapUpReason object.
9402  *
9403  * @requires finesse.clientservices.ClientServices
9404  * @requires Class
9405  * @requires finesse.FinesseBase
9406  * @requires finesse.restservices.RestBase
9407  */
9408 
9409 /** @private */
9410 define('restservices/WrapUpReason',['restservices/RestBase'], function (RestBase) {
9411 
9412     var WrapUpReason = RestBase.extend(/** @lends finesse.restservices.WrapUpReason.prototype */{
9413 
9414         /**
9415          * @class
9416          * A WrapUpReason is a code and description identifying a particular reason that a
9417          * User is in WORK (WrapUp) mode.
9418          * 
9419          * @augments finesse.restservices.RestBase
9420          * @see finesse.restservices.User
9421          * @see finesse.restservices.User.States#WORK
9422          * @constructs
9423          */
9424         _fakeConstuctor: function () {
9425             /* This is here to hide the real init constructor from the public docs */
9426         },
9427         
9428         /** 
9429          * @private
9430          * JavaScript representation of a WrapUpReason object. Also exposes
9431          * methods to operate on the object against the server.
9432          *
9433          * @param {Object} options
9434          *     An object with the following properties:<ul>
9435          *         <li><b>id:</b> The id of the object being constructed</li>
9436          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
9437          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
9438          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
9439          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
9440          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
9441          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
9442          *             <li><b>content:</b> {String} Raw string of response</li>
9443          *             <li><b>object:</b> {Object} Parsed object of response</li>
9444          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
9445          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
9446          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
9447          *             </ul></li>
9448          *         </ul></li>
9449          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
9450          **/
9451         init: function (options) {
9452             this._super(options);
9453         },
9454 
9455         /**
9456          * @private
9457          * Gets the REST class for the current object - this is the WrapUpReason class.
9458          * @returns {Object} The WrapUpReason class.
9459          */
9460         getRestClass: function () {
9461             return WrapUpReason;
9462         },
9463 
9464         /**
9465          * @private
9466          * Gets the REST type for the current object - this is a "WrapUpReason".
9467          * @returns {String} The WrapUpReason string.
9468          */
9469         getRestType: function () {
9470             return "WrapUpReason";
9471         },
9472 
9473         /**
9474          * @private
9475          * Gets the REST type for the current object - this is a "WrapUpReasons".
9476          * @returns {String} The WrapUpReasons string.
9477          */
9478         getParentRestType: function () {
9479             return "WrapUpReasons";
9480         },
9481 
9482         /**
9483          * @private
9484          * Override default to indicate that this object doesn't support making
9485          * requests.
9486          */
9487         supportsRequests: false,
9488 
9489         /**
9490          * @private
9491          * Override default to indicate that this object doesn't support subscriptions.
9492          */
9493         supportsSubscriptions: false,
9494 
9495         /**
9496          * Getter for the label.
9497          * @returns {String} The label.
9498          */
9499         getLabel: function () {
9500             this.isLoaded();
9501             return this.getData().label;
9502         },
9503 
9504         /**
9505          * @private
9506          * Getter for the forAll flag.
9507          * @returns {Boolean} True if global.
9508          */
9509         getForAll: function () {
9510             this.isLoaded();
9511             return this.getData().forAll;
9512         },
9513 
9514         /**
9515          * @private
9516          * Getter for the Uri value.
9517          * @returns {String} The Uri.
9518          */
9519         getUri: function () {
9520             this.isLoaded();
9521             return this.getData().uri;
9522         }
9523     });
9524 
9525     window.finesse = window.finesse || {};
9526     window.finesse.restservices = window.finesse.restservices || {};
9527     window.finesse.restservices.WrapUpReason = WrapUpReason;
9528         
9529     return WrapUpReason;
9530 });
9531 
9532 /**
9533 * JavaScript representation of the Finesse WrapUpReasons collection
9534 * object which contains a list of WrapUpReason objects.
9535  *
9536  * @requires finesse.clientservices.ClientServices
9537  * @requires Class
9538  * @requires finesse.FinesseBase
9539  * @requires finesse.restservices.RestBase
9540  * @requires finesse.restservices.Dialog
9541  * @requires finesse.restservices.RestCollectionBase
9542  */
9543 
9544 /** @private */
9545 define('restservices/WrapUpReasons',[
9546     'restservices/RestCollectionBase',
9547     'restservices/WrapUpReason'
9548 ],
9549 function (RestCollectionBase, WrapUpReason) {
9550 
9551     var WrapUpReasons = RestCollectionBase.extend(/** @lends finesse.restservices.WrapUpReasons.prototype */{
9552         
9553         /**
9554          * @class
9555          * JavaScript representation of a WrapUpReasons collection object. 
9556          * @augments finesse.restservices.RestCollectionBase
9557          * @constructs
9558          * @see finesse.restservices.WrapUpReason
9559          * @example
9560          *  _wrapUpReasons = _user.getWrapUpReasons ( {
9561          *      onCollectionAdd : _handleWrapUpReasonAdd,
9562          *      onCollectionDelete : _handleWrapUpReasonDelete,
9563          *      onLoad : _handleWrapUpReasonsLoaded
9564          *  });
9565          *  
9566          * _wrapUpReasonCollection = _wrapUpReasons.getCollection();
9567          * for (var wrapUpReasonId in _wrapUpReasonCollection) {
9568          *     if (_wrapUpReasonCollection.hasOwnProperty(wrapUpReasonId)) {
9569          *         _wrapUpReason = _wrapUpReasonCollection[wrapUpReasonId];
9570          *         etc...
9571          *     }
9572          * }
9573         */
9574         _fakeConstuctor: function () {
9575             /* This is here to hide the real init constructor from the public docs */
9576         },
9577         
9578         /** 
9579          * @private
9580          * JavaScript representation of a WrapUpReasons collection object. Also exposes
9581          * methods to operate on the object against the server.
9582          *
9583          * @param {Object} options
9584          *     An object with the following properties:<ul>
9585          *         <li><b>id:</b> The id of the object being constructed</li>
9586          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
9587          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
9588          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
9589          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
9590          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
9591          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
9592          *             <li><b>content:</b> {String} Raw string of response</li>
9593          *             <li><b>object:</b> {Object} Parsed object of response</li>
9594          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
9595          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
9596          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
9597          *             </ul></li>
9598          *         </ul></li>
9599          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
9600          **/
9601         init: function (options) {
9602             this._super(options);           
9603         },
9604 
9605         /**
9606          * @private
9607          * Gets the REST class for the current object - this is the WrapUpReasons class.
9608          */
9609         getRestClass: function () {
9610             return WrapUpReasons;
9611         },
9612 
9613         /**
9614          * @private
9615          * Gets the REST class for the objects that make up the collection. - this
9616          * is the WrapUpReason class.
9617          */
9618         getRestItemClass: function () {
9619             return WrapUpReason;
9620         },
9621 
9622         /**
9623          * @private
9624          * Gets the REST type for the current object - this is a "WrapUpReasons".
9625          */
9626         getRestType: function () {
9627             return "WrapUpReasons";
9628         },
9629         
9630         /**
9631          * @private
9632          * Gets the REST type for the objects that make up the collection - this is "WrapUpReason".
9633          */
9634         getRestItemType: function () {
9635             return "WrapUpReason";
9636         },
9637 
9638         /**
9639          * @private
9640          * Override default to indicates that the collection supports making
9641          * requests.
9642          */
9643         supportsRequests: true,
9644 
9645         /**
9646          * @private
9647          * Override default to indicate that this object doesn't support subscriptions.
9648          */
9649         supportsRestItemSubscriptions: false,
9650 
9651         /**
9652          * @private
9653          * Retrieve the Wrap-Up Reason Codes. This call will re-query the server and refresh the collection.
9654          *
9655          * @returns {finesse.restservices.WrapUpReasons}
9656          *     This ReadyReasonCodes object to allow cascading.
9657          */
9658         get: function () {
9659             // set loaded to false so it will rebuild the collection after the get
9660             this._loaded = false;
9661             // reset collection
9662             this._collection = {};
9663             // perform get
9664             this._synchronize();
9665             return this;
9666         }
9667         
9668     });
9669  
9670     window.finesse = window.finesse || {};
9671     window.finesse.restservices = window.finesse.restservices || {};
9672     window.finesse.restservices.WrapUpReasons = WrapUpReasons;
9673        
9674     return WrapUpReasons;
9675 });
9676 
9677 /**
9678  * JavaScript representation of the Finesse Contact object.
9679  * @requires finesse.clientservices.ClientServices
9680  * @requires Class
9681  * @requires finesse.FinesseBase
9682  * @requires finesse.restservices.RestBase
9683  */
9684 /** @private */
9685 define('restservices/Contact',['restservices/RestBase'], function (RestBase) {
9686 
9687     var Contact = RestBase.extend(/** @lends finesse.restservices.Contact.prototype */{
9688 
9689         /**
9690          * @class
9691          * A Contact is a single entry in a PhoneBook, consisting of a First and Last Name,
9692          * a Phone Number, and a Description.
9693          * 
9694          * @augments finesse.restservices.RestBase
9695          * @see finesse.restservices.PhoneBook
9696          * @constructs
9697          */
9698         _fakeConstuctor: function () {
9699             /* This is here to hide the real init constructor from the public docs */
9700         },
9701         
9702         /**
9703          * @private
9704          * @param {Object} options
9705          *     An object with the following properties:<ul>
9706          *         <li><b>id:</b> The id of the object being constructed</li>
9707          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
9708          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
9709          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
9710          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
9711          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
9712          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
9713          *             <li><b>content:</b> {String} Raw string of response</li>
9714          *             <li><b>object:</b> {Object} Parsed object of response</li>
9715          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
9716          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
9717          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
9718          *             </ul></li>
9719          *         </ul></li>
9720          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
9721          **/
9722         init: function (options) {
9723             this._super(options);
9724         },
9725 
9726         /**
9727          * @private
9728          * Gets the REST class for the current object - this is the Contact class.
9729          * @returns {Object} The Contact class.
9730          */
9731         getRestClass: function () {
9732             return Contact;
9733         },
9734 
9735         /**
9736          * @private
9737          * Gets the REST type for the current object - this is a "Contact".
9738          * @returns {String} The Contact string.
9739          */
9740         getRestType: function () {
9741             return "Contact";
9742         },
9743 
9744         /**
9745          * @private
9746          * Override default to indicate that this object doesn't support making
9747          * requests.
9748          */
9749         supportsRequests: false,
9750 
9751         /**
9752          * @private
9753          * Override default to indicate that this object doesn't support subscriptions.
9754          */
9755         supportsSubscriptions: false,
9756 
9757         /**
9758          * Getter for the firstName.
9759          * @returns {String} The firstName.
9760          */
9761         getFirstName: function () {
9762             this.isLoaded();
9763             return this.getData().firstName;
9764         },
9765 
9766         /**
9767          * Getter for the lastName.
9768          * @returns {String} The lastName.
9769          */
9770         getLastName: function () {
9771             this.isLoaded();
9772             return this.getData().lastName;
9773         },
9774 
9775         /**
9776          * Getter for the phoneNumber.
9777          * @returns {String} The phoneNumber.
9778          */
9779         getPhoneNumber: function () {
9780             this.isLoaded();
9781             return this.getData().phoneNumber;
9782         },
9783 
9784         /**
9785          * Getter for the description.
9786          * @returns {String} The description.
9787          */
9788         getDescription: function () {
9789             this.isLoaded();
9790             return this.getData().description;
9791         },
9792 
9793         /** @private */
9794         createPutSuccessHandler: function(contact, contentBody, successHandler){
9795             return function (rsp) {
9796                 // Update internal structure based on response. Here we
9797                 // inject the contentBody from the PUT request into the
9798                 // rsp.object element to mimic a GET as a way to take
9799                 // advantage of the existing _processResponse method.
9800                 rsp.object = contentBody;
9801                 contact._processResponse(rsp);
9802 
9803                 //Remove the injected Contact object before cascading response
9804                 rsp.object = {};
9805                 
9806                 //cascade response back to consumer's response handler
9807                 successHandler(rsp);
9808             };
9809         },
9810 
9811         /** @private */
9812         createPostSuccessHandler: function (contact, contentBody, successHandler) {
9813             return function (rsp) {
9814                 rsp.object = contentBody;
9815                 contact._processResponse(rsp);
9816 
9817                 //Remove the injected Contact object before cascading response
9818                 rsp.object = {};
9819 
9820                 //cascade response back to consumer's response handler
9821                 successHandler(rsp);
9822             };
9823         },
9824 
9825         /**
9826          * Add
9827          * @private
9828          */
9829         add: function (newValues, handlers) {
9830             // this.isLoaded();
9831             var contentBody = {};
9832 
9833             contentBody[this.getRestType()] = {
9834                 "firstName": newValues.firstName,
9835                 "lastName": newValues.lastName,
9836                 "phoneNumber": newValues.phoneNumber,
9837                 "description": newValues.description
9838             };
9839 
9840             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
9841             handlers = handlers || {};
9842 
9843             this.restRequest(this.getRestUrl(), {
9844                 method: 'POST',
9845                 success: this.createPostSuccessHandler(this, contentBody, handlers.success),
9846                 error: handlers.error,
9847                 content: contentBody
9848             });
9849 
9850             return this; // Allow cascading
9851         },
9852 
9853         /**
9854          * Update
9855          * @private
9856          */
9857         update: function (newValues, handlers) {
9858             this.isLoaded();
9859             var contentBody = {};
9860 
9861             contentBody[this.getRestType()] = {
9862                 "uri": this.getId(),
9863                 "firstName": newValues.firstName,
9864                 "lastName": newValues.lastName,
9865                 "phoneNumber": newValues.phoneNumber,
9866                 "description": newValues.description
9867             };
9868 
9869             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
9870             handlers = handlers || {};
9871 
9872             this.restRequest(this.getRestUrl(), {
9873                 method: 'PUT',
9874                 success: this.createPutSuccessHandler(this, contentBody, handlers.success),
9875                 error: handlers.error,
9876                 content: contentBody
9877             });
9878 
9879             return this; // Allow cascading
9880         },
9881 
9882 
9883         /**
9884          * Delete
9885          * @private
9886          */
9887         "delete": function ( handlers) {
9888             this.isLoaded();
9889 
9890             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
9891             handlers = handlers || {};
9892 
9893             this.restRequest(this.getRestUrl(), {
9894                 method: 'DELETE',
9895                 success: this.createPutSuccessHandler(this, {}, handlers.success),
9896                 error: handlers.error,
9897                 content: undefined
9898             });
9899 
9900             return this; // Allow cascading
9901         }
9902     });
9903 
9904     window.finesse = window.finesse || {};
9905     window.finesse.restservices = window.finesse.restservices || {};
9906     window.finesse.restservices.Contact = Contact;
9907     
9908     return Contact;
9909 });
9910 
9911 /**
9912 * JavaScript representation of the Finesse Contacts collection
9913 * object which contains a list of Contact objects.
9914  *
9915  * @requires finesse.clientservices.ClientServices
9916  * @requires Class
9917  * @requires finesse.FinesseBase
9918  * @requires finesse.restservices.RestBase
9919  * @requires finesse.restservices.Dialog
9920  * @requires finesse.restservices.RestCollectionBase
9921  */
9922 /** @private */
9923 define('restservices/Contacts',[
9924     'restservices/RestCollectionBase',
9925     'restservices/Contact'
9926 ],
9927 function (RestCollectionBase, Contact) {
9928     var Contacts = RestCollectionBase.extend(/** @lends finesse.restservices.Contacts.prototype */{
9929         
9930         /**
9931          * @class
9932          * JavaScript representation of a Contacts collection object. Also exposes
9933          * methods to operate on the object against the server.
9934          * @augments finesse.restservices.RestCollectionBase
9935          * @constructs
9936          * @see finesse.restservices.Contact
9937          * @see finesse.restservices.PhoneBook
9938          * @example
9939          *  _contacts = _phonebook.getContacts( {
9940          *      onCollectionAdd : _handleContactAdd,
9941          *      onCollectionDelete : _handleContactDelete,
9942          *      onLoad : _handleContactsLoaded
9943          *  });
9944          *  
9945          * _contactCollection = _contacts.getCollection();
9946          * for (var contactId in _contactCollection) {
9947          *     if (_contactCollection.hasOwnProperty(contactId)) {
9948          *         _contact = _contactCollection[contactId];
9949          *         etc...
9950          *     }
9951          * }
9952          */
9953         _fakeConstuctor: function () {
9954             /* This is here to hide the real init constructor from the public docs */
9955         },
9956         
9957         /** 
9958          * @private
9959          * JavaScript representation of a Contacts collection object. Also exposes
9960          * methods to operate on the object against the server.
9961          *
9962          * @param {Object} options
9963          *     An object with the following properties:<ul>
9964          *         <li><b>id:</b> The id of the object being constructed</li>
9965          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
9966          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
9967          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
9968          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
9969          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
9970          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
9971          *             <li><b>content:</b> {String} Raw string of response</li>
9972          *             <li><b>object:</b> {Object} Parsed object of response</li>
9973          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
9974          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
9975          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
9976          *             </ul></li>
9977          *         </ul></li>
9978          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
9979          **/
9980         init: function (options) {
9981             this._super(options);           
9982         },
9983 
9984         /**
9985          * @private
9986          * Gets the REST class for the current object - this is the Contacts class.
9987          */
9988         getRestClass: function () {
9989             return Contacts;
9990         },
9991 
9992         /**
9993          * @private
9994          * Gets the REST class for the objects that make up the collection. - this
9995          * is the Contact class.
9996          */
9997         getRestItemClass: function () {
9998             return Contact;
9999         },
10000 
10001         /**
10002          * @private
10003          * Gets the REST type for the current object - this is a "Contacts".
10004          */
10005         getRestType: function () {
10006             return "Contacts";
10007         },
10008         
10009         /**
10010          * @private
10011          * Gets the REST type for the objects that make up the collection - this is "Contacts".
10012          */
10013         getRestItemType: function () {
10014             return "Contact";
10015         },
10016 
10017         /**
10018          * @private
10019          * Override default to indicates that the collection supports making
10020          * requests.
10021          */
10022         supportsRequests: true,
10023 
10024         /**
10025          * @private
10026          * Override default to indicates that the collection subscribes to its objects.
10027          */
10028         supportsRestItemSubscriptions: false,
10029         
10030         /**
10031          * @private
10032          * Retrieve the Contacts.  This call will re-query the server and refresh the collection.
10033          *
10034          * @returns {finesse.restservices.Contacts}
10035          *     This Contacts object, to allow cascading.
10036          */
10037         get: function () {
10038             // set loaded to false so it will rebuild the collection after the get
10039             this._loaded = false;
10040             // reset collection
10041             this._collection = {};
10042             // perform get
10043             this._synchronize();
10044             return this;
10045         }
10046         
10047     });
10048     
10049     window.finesse = window.finesse || {};
10050     window.finesse.restservices = window.finesse.restservices || {};
10051     window.finesse.restservices.Contacts = Contacts;
10052     
10053     
10054     return Contacts;
10055 });
10056 
10057 /**
10058  * JavaScript representation of the Finesse PhoneBook object.
10059  *
10060  * @requires finesse.clientservices.ClientServices
10061  * @requires Class
10062  * @requires finesse.FinesseBase
10063  * @requires finesse.restservices.RestBase
10064  */
10065 
10066 /** @private */
10067 define('restservices/PhoneBook',[
10068     'restservices/RestBase',
10069     'restservices/Contacts'
10070 ],
10071 function (RestBase, Contacts) {
10072     var PhoneBook = RestBase.extend(/** @lends finesse.restservices.PhoneBook.prototype */{
10073 
10074         _contacts: null,
10075 
10076         /**
10077          * @class
10078          * A PhoneBook is a list of Contacts available to a User for quick dial.
10079          * 
10080          * @augments finesse.restservices.RestBase
10081          * @see finesse.restservices.Contacts
10082          * @constructs
10083          */
10084         _fakeConstuctor: function () {
10085             /* This is here to hide the real init constructor from the public docs */
10086         },
10087         
10088         /** 
10089          * @private
10090          * JavaScript representation of a PhoneBook object. Also exposes
10091          * methods to operate on the object against the server.
10092          *
10093          * @param {Object} options
10094          *     An object with the following properties:<ul>
10095          *         <li><b>id:</b> The id of the object being constructed</li>
10096          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
10097          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
10098          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
10099          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
10100          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
10101          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
10102          *             <li><b>content:</b> {String} Raw string of response</li>
10103          *             <li><b>object:</b> {Object} Parsed object of response</li>
10104          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
10105          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
10106          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
10107          *             </ul></li>
10108          *         </ul></li>
10109          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
10110          **/
10111         init: function (options) {
10112             this._super(options);
10113         },
10114 
10115         /**
10116          * @private
10117          * Gets the REST class for the current object - this is the PhoneBook class.
10118          * @returns {Object} The PhoneBook class.
10119          */
10120         getRestClass: function () {
10121             return PhoneBook;
10122         },
10123 
10124         /**
10125          * @private
10126          * Gets the REST type for the current object - this is a "PhoneBook".
10127          * @returns {String} The PhoneBook string.
10128          */
10129         getRestType: function () {
10130             return "PhoneBook";
10131         },
10132 
10133         /**
10134          * @private
10135          * Override default to indicate that this object doesn't support making
10136          * requests.
10137          */
10138         supportsRequests: false,
10139 
10140         /**
10141          * @private
10142          * Override default to indicate that this object doesn't support subscriptions.
10143          */
10144         supportsSubscriptions: false,
10145 
10146         /**
10147          * Getter for the name of the Phone Book.
10148          * @returns {String} The name.
10149          */
10150         getName: function () {
10151             this.isLoaded();
10152             return this.getData().name;
10153         },
10154 
10155         /**
10156          * Getter for the type flag.
10157          * @returns {String} The type.
10158          */
10159         getType: function () {
10160             this.isLoaded();
10161             return this.getData().type;
10162         },
10163 
10164         /**
10165          * @private
10166          * Getter for the Uri value.
10167          * @returns {String} The Uri.
10168          */
10169         getUri: function () {
10170             this.isLoaded();
10171             return this.getData().uri;
10172         },
10173 
10174         /**
10175          * Getter for a Contacts collection object that is associated with PhoneBook.
10176          * @param {finesse.interfaces.RequestHandlers} handlers
10177          *     An object containing the handlers for the request
10178          * @returns {finesse.restservices.Contacts}
10179          *     A Contacts collection object.
10180          */
10181         getContacts: function (callbacks) {
10182             var options = callbacks || {};
10183             options.parentObj = this;
10184             this.isLoaded();
10185 
10186             if (this._contacts === null) {
10187                 this._contacts = new Contacts(options);
10188             }
10189 
10190             return this._contacts;
10191         },
10192 
10193         /**
10194          * Getter for <contacts> node within PhoneBook - sometimes it's just a URI, sometimes it is a Contacts collection
10195          * @returns {String} uri to contacts
10196          *          or {finesse.restservices.Contacts} collection
10197          */
10198         getEmbeddedContacts: function(){
10199             this.isLoaded();
10200             return this.getData().contacts;
10201         },
10202 
10203         /** @private */
10204         createPutSuccessHandler: function(phonebook, contentBody, successHandler){
10205             return function (rsp) {
10206                 // Update internal structure based on response. Here we
10207                 // inject the contentBody from the PUT request into the
10208                 // rsp.object element to mimic a GET as a way to take
10209                 // advantage of the existing _processResponse method.
10210                 rsp.object = contentBody;
10211                 phonebook._processResponse(rsp);
10212 
10213                 //Remove the injected PhoneBook object before cascading response
10214                 rsp.object = {};
10215                 
10216                 //cascade response back to consumer's response handler
10217                 successHandler(rsp);
10218             };
10219         },
10220 
10221         /** @private */
10222         createPostSuccessHandler: function (phonebook, contentBody, successHandler) {
10223             return function (rsp) {
10224                 rsp.object = contentBody;
10225                 phonebook._processResponse(rsp);
10226 
10227                 //Remove the injected PhoneBook object before cascading response
10228                 rsp.object = {};
10229 
10230                 //cascade response back to consumer's response handler
10231                 successHandler(rsp);
10232             };
10233         },
10234 
10235         /**
10236          * @private
10237          * Add a PhoneBook.
10238          * @param {Object} newValues
10239          * @param {String} newValues.name Name of PhoneBook
10240          * @param {String} newValues.type Type of PhoneBook
10241          * @param {finesse.interfaces.RequestHandlers} handlers
10242          *     An object containing the handlers for the request
10243          * @returns {finesse.restservices.PhoneBook}
10244          *     This PhoneBook object, to allow cascading
10245          */
10246         add: function (newValues, handlers) {
10247             // this.isLoaded();
10248             var contentBody = {};
10249 
10250             contentBody[this.getRestType()] = {
10251                 "name": newValues.name,
10252                 "type": newValues.type
10253             };
10254 
10255             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
10256             handlers = handlers || {};
10257 
10258             this.restRequest(this.getRestUrl(), {
10259                 method: 'POST',
10260                 success: this.createPostSuccessHandler(this, contentBody, handlers.success),
10261                 error: handlers.error,
10262                 content: contentBody
10263             });
10264 
10265             return this; // Allow cascading
10266         },
10267 
10268         /**
10269          * @private
10270          * Update a PhoneBook.
10271          * @param {Object} newValues
10272          * @param {String} newValues.name Name of PhoneBook
10273          * @param {String} newValues.type Type of PhoneBook
10274          * @param {finesse.interfaces.RequestHandlers} handlers
10275          *     An object containing the handlers for the request
10276          * @returns {finesse.restservices.PhoneBook}
10277          *     This PhoneBook object, to allow cascading
10278          */
10279         update: function (newValues, handlers) {
10280             this.isLoaded();
10281             var contentBody = {};
10282 
10283             contentBody[this.getRestType()] = {
10284                 "uri": this.getId(),
10285                 "name": newValues.name,
10286                 "type": newValues.type
10287             };
10288 
10289             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
10290             handlers = handlers || {};
10291 
10292             this.restRequest(this.getRestUrl(), {
10293                 method: 'PUT',
10294                 success: this.createPutSuccessHandler(this, contentBody, handlers.success),
10295                 error: handlers.error,
10296                 content: contentBody
10297             });
10298 
10299             return this; // Allow cascading
10300         },
10301 
10302 
10303         /**
10304          * Delete a PhoneBook.
10305          * @param {finesse.interfaces.RequestHandlers} handlers
10306          *     An object containing the handlers for the request
10307          * @returns {finesse.restservices.PhoneBook}
10308          *     This PhoneBook object, to allow cascading
10309          */
10310         "delete": function ( handlers) {
10311             this.isLoaded();
10312 
10313             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
10314             handlers = handlers || {};
10315 
10316             this.restRequest(this.getRestUrl(), {
10317                 method: 'DELETE',
10318                 success: this.createPutSuccessHandler(this, {}, handlers.success),
10319                 error: handlers.error,
10320                 content: undefined
10321             });
10322 
10323             return this; // Allow cascading
10324         }
10325 
10326 
10327 
10328     });
10329     
10330     window.finesse = window.finesse || {};
10331     window.finesse.restservices = window.finesse.restservices || {};
10332     window.finesse.restservices.PhoneBook = PhoneBook;
10333     
10334     return PhoneBook;
10335 });
10336 
10337 /**
10338 * JavaScript representation of the Finesse PhoneBooks collection
10339 * object which contains a list of PhoneBook objects.
10340  *
10341  * @requires finesse.clientservices.ClientServices
10342  * @requires Class
10343  * @requires finesse.FinesseBase
10344  * @requires finesse.restservices.RestBase
10345  * @requires finesse.restservices.Dialog
10346  * @requires finesse.restservices.RestCollectionBase
10347  */
10348 /** @private */
10349 define('restservices/PhoneBooks',[
10350     'restservices/RestCollectionBase',
10351     'restservices/PhoneBook'
10352 ],
10353 function (RestCollectionBase, PhoneBook) {
10354     var PhoneBooks = RestCollectionBase.extend(/** @lends finesse.restservices.PhoneBooks.prototype */{
10355         
10356         /**
10357          * @class
10358          * JavaScript representation of a PhoneBooks collection object. 
10359          * @augments finesse.restservices.RestCollectionBase
10360          * @constructs
10361          * @see finesse.restservices.PhoneBook
10362          * @see finesse.restservices.Contacts
10363          * @see finesse.restservices.Contact
10364          * @example
10365          *  _phoneBooks = _user.getPhoneBooks( {
10366          *      onCollectionAdd : _handlePhoneBookAdd,
10367          *      onCollectionDelete : _handlePhoneBookDelete,
10368          *      onLoad : _handlePhoneBooksLoaded
10369          *  });
10370          *  
10371          * _phoneBookCollection = _phoneBooks.getCollection();
10372          * for (var phoneBookId in _phoneBookCollection) {
10373          *     if (_phoneBookCollection.hasOwnProperty(phoneBookId)) {
10374          *         _phoneBook = _phoneBookCollection[phoneBookId];
10375          *         etc...
10376          *     }
10377          * }
10378         */
10379         _fakeConstuctor: function () {
10380             /* This is here to hide the real init constructor from the public docs */
10381         },
10382         
10383        /**
10384          * @private
10385          *
10386          * @param {Object} options
10387          *     An object with the following properties:<ul>
10388          *         <li><b>id:</b> The id of the object being constructed</li>
10389          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
10390          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
10391          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
10392          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
10393          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
10394          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
10395          *             <li><b>content:</b> {String} Raw string of response</li>
10396          *             <li><b>object:</b> {Object} Parsed object of response</li>
10397          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
10398          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
10399          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
10400          *             </ul></li>
10401          *         </ul></li>
10402          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
10403          **/
10404         init: function (options) {
10405             // Keep the REST response for PhoneBooks to check for 206 Partial Content.
10406             this.keepRestResponse = true;
10407             // Add in the Range header which is required for PhoneBooks API.
10408             this.extraHeaders = { "Range": "objects=1-1500" };
10409             this._super(options);
10410         },
10411 
10412         /**
10413          * @private
10414          * Gets the REST class for the current object - this is the PhoneBooks class.
10415          */
10416         getRestClass: function () {
10417             return PhoneBooks;
10418         },
10419 
10420         /**
10421          * @private
10422          * Gets the REST class for the objects that make up the collection. - this
10423          * is the PhoneBook class.
10424          */
10425         getRestItemClass: function () {
10426             return PhoneBook;
10427         },
10428 
10429         /**
10430          * @private
10431          * Gets the REST type for the current object - this is a "PhoneBooks".
10432          */
10433         getRestType: function () {
10434             return "PhoneBooks";
10435         },
10436         
10437         /**
10438          * @private
10439          * Gets the REST type for the objects that make up the collection - this is "PhoneBooks".
10440          */
10441         getRestItemType: function () {
10442             return "PhoneBook";
10443         },
10444 
10445         /**
10446          * @private
10447          * Override default to indicates that the collection supports making
10448          * requests.
10449          */
10450         supportsRequests: true,
10451 
10452         /**
10453          * @private
10454          * Override default to indicates that the collection subscribes to its objects.
10455          */
10456         supportsRestItemSubscriptions: false,
10457         
10458         /**
10459          * @private
10460          * Retrieve the PhoneBooks.  This call will re-query the server and refresh the collection.
10461          *
10462          * @returns {finesse.restservices.PhoneBooks}
10463          *     This PhoneBooks object, to allow cascading.
10464          */
10465         get: function () {
10466             // set loaded to false so it will rebuild the collection after the get
10467             this._loaded = false;
10468             // reset collection
10469             this._collection = {};
10470             // perform get
10471             this._synchronize();
10472             return this;
10473         }
10474         
10475     });
10476     
10477     window.finesse = window.finesse || {};
10478     window.finesse.restservices = window.finesse.restservices || {};
10479     window.finesse.restservices.PhoneBooks = PhoneBooks;
10480     
10481     return PhoneBooks;
10482 });
10483 
10484 /**
10485  * JavaScript representation of the Finesse WorkflowAction object.
10486  *
10487  * @requires finesse.clientservices.ClientServices
10488  * @requires Class
10489  * @requires finesse.FinesseBase
10490  * @requires finesse.restservices.RestBase
10491  */
10492 
10493 /*jslint browser: true, nomen: true, sloppy: true, forin: true */
10494 /*global define,finesse */
10495 
10496 /** @private */
10497 define('restservices/WorkflowAction',['restservices/RestBase'], function (RestBase) {
10498 
10499     var WorkflowAction = RestBase.extend({
10500 
10501         _contacts: null,
10502 
10503         actionTypes: [
10504             {
10505                 name: 'BROWSER_POP',
10506                 params: [
10507                     {
10508                         name: 'windowName',
10509                         type: 'text'
10510                     },
10511                     {
10512                         name: 'path',
10513                         type: 'systemVariableSingleLineEditor'
10514                     }
10515                 ]
10516             },
10517             {
10518                 name: 'HTTP_REQUEST',
10519                 params: [
10520                     {
10521                         name: 'method',
10522                         type: 'dropdown',
10523                         values: ['POST', 'PUT']
10524                     },
10525                     {
10526                         name: 'location',
10527                         type: 'dropdown',
10528                         values: ['FINESSE', 'OTHER']
10529                     },
10530                     {
10531                         name: 'contentType',
10532                         type: 'text'
10533                     },
10534                     {
10535                         name: 'path',
10536                         type: 'systemVariableSingleLineEditor'
10537                     },
10538                     {
10539                         name: 'body',
10540                         type: 'systemVariableMultiLineEditor'
10541                     }
10542                 ]
10543             }            
10544             // more action type definitions here
10545         ],
10546 
10547         /**
10548          * @class
10549          * A WorkflowAction is an action (e.g. Browser Pop, Rest Request) defined in a
10550          * Workflow and triggered by a system event (Call Received, Call Ended, etc.).
10551          * 
10552          * @augments finesse.restservices.RestBase
10553          * @see finesse.restservices.Workflow
10554          * @constructs
10555          */
10556         _fakeConstuctor: function () {
10557             /* This is here to hide the real init constructor from the public docs */
10558         },
10559         
10560         /**
10561          * @private
10562          * JavaScript representation of a WorkflowAction object. Also exposes
10563          * methods to operate on the object against the server.
10564          *
10565          * @param {Object} options
10566          *     An object with the following properties:<ul>
10567          *         <li><b>id:</b> The id of the object being constructed</li>
10568          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
10569          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
10570          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
10571          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
10572          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
10573          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
10574          *             <li><b>content:</b> {String} Raw string of response</li>
10575          *             <li><b>object:</b> {Object} Parsed object of response</li>
10576          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
10577          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
10578          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
10579          *             </ul></li>
10580          *         </ul></li>
10581          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
10582          **/
10583         init: function (options) {
10584             this._super(options);
10585         },
10586 
10587         /**
10588          * @private
10589          * Gets the REST class for the current object - this is the WorkflowAction class.
10590          * @returns {Object} The WorkflowAction class.
10591          */
10592         getRestClass: function () {
10593             return finesse.restservices.WorkflowAction;
10594         },
10595 
10596         /**
10597          * @private
10598          * Gets the REST type for the current object - this is a "WorkflowAction".
10599          * @returns {String} The WorkflowAction string.
10600          */
10601         getRestType: function () {
10602             return "WorkflowAction";
10603         },
10604 
10605         /**
10606          * @private
10607          * Override default to indicate that this object doesn't support making
10608          * requests.
10609          */
10610         supportsRequests: false,
10611 
10612         /**
10613          * @private
10614          * Override default to indicate that this object doesn't support subscriptions.
10615          */
10616         supportsSubscriptions: false,
10617 
10618         /**
10619          * Getter for the name.
10620          * @returns {String} The name.
10621          */
10622         getName: function () {
10623             this.isLoaded();
10624             return this.getData().name;
10625         },
10626 
10627         /**
10628          * Getter for the type flag.
10629          * @returns {String} The type.
10630          */
10631         getType: function () {
10632             this.isLoaded();
10633             return this.getData().type;
10634         },
10635 
10636         /**
10637          * @private
10638          * Getter for the Uri value.
10639          * @returns {String} The Uri.
10640          */
10641         getUri: function () {
10642             this.isLoaded();
10643             return this.getData().uri;
10644         },
10645 
10646         /**
10647          * @private
10648          * Getter for the handledBy value.
10649          * @returns {String} handledBy.
10650          */
10651         getHandledBy: function () {
10652             this.isLoaded();
10653             return this.getData().handledBy;
10654         },
10655 
10656         /**
10657          * Getter for the parameters.
10658          * @returns {Object} key = param name, value = param value
10659          */
10660         getParams: function () {
10661             var map = {},
10662                 params = this.getData().params.Param,
10663                 i,
10664                 param;
10665 
10666             for(i=0; i<params.length; i+=1){
10667                 param = params[i];
10668                 map[param.name] = param.value || "";
10669             }
10670 
10671             return map;
10672         },
10673 
10674         /**
10675          * Getter for the ActionVariables
10676          * @returns {Object} key = action variable name, value = Object{name, type, node, testValue}
10677          */
10678         getActionVariables: function() {
10679             var map = {},
10680                 actionVariablesParent = this.getData().actionVariables,
10681                 actionVariables,
10682                 i,
10683                 actionVariable;
10684 
10685             if (actionVariablesParent === null ||  typeof(actionVariablesParent) === "undefined" || actionVariablesParent.length === 0){
10686                 return map;
10687             }
10688             actionVariables = actionVariablesParent.ActionVariable;
10689 
10690             if(actionVariables.length > 0){
10691                 for(i=0; i<actionVariables.length; i+=1){
10692                     actionVariable = actionVariables[i];
10693                     // escape nulls to empty string
10694                     actionVariable.name = actionVariable.name || "";
10695                     actionVariable.type = actionVariable.type || "";
10696                     actionVariable.node = actionVariable.node || "";
10697                     actionVariable.testValue = actionVariable.testValue || "";
10698                     map[actionVariable.name] = actionVariable;
10699                 }
10700             } else {
10701                 map[actionVariables.name] = actionVariables;
10702             }
10703 
10704             return map;
10705         },
10706 
10707         /** @private */
10708         createPutSuccessHandler: function(action, contentBody, successHandler){
10709             return function (rsp) {
10710                 // Update internal structure based on response. Here we
10711                 // inject the contentBody from the PUT request into the
10712                 // rsp.object element to mimic a GET as a way to take
10713                 // advantage of the existing _processResponse method.
10714                 rsp.object = contentBody;
10715                 action._processResponse(rsp);
10716 
10717                 //Remove the injected WorkflowAction object before cascading response
10718                 rsp.object = {};
10719                 
10720                 //cascade response back to consumer's response handler
10721                 successHandler(rsp);
10722             };
10723         },
10724 
10725         /** @private */
10726         createPostSuccessHandler: function (action, contentBody, successHandler) {
10727             return function (rsp) {
10728                 rsp.object = contentBody;
10729                 action._processResponse(rsp);
10730 
10731                 //Remove the injected WorkflowAction object before cascading response
10732                 rsp.object = {};
10733 
10734                 //cascade response back to consumer's response handler
10735                 successHandler(rsp);
10736             };
10737         },
10738 
10739         /**
10740          * @private
10741          * Build params array out of all the values coming into add or update methods
10742          * paramMap is a map of params.. we need to translate it into an array of Param objects
10743          * where path and windowName are params for the BROWSER_POP type
10744          */
10745         buildParamsForRest: function(paramMap){
10746             var params = {"Param": []},
10747                 i;
10748             for(i in paramMap){
10749                 if(paramMap.hasOwnProperty(i)){
10750                     params.Param.push({name: i, value: paramMap[i]});
10751                 }
10752             }
10753             return params;
10754         },
10755 
10756         /**
10757          * @private
10758          * Build actionVariables array out of all the values coming into add or update methods
10759          * actionVariableMap is a map of actionVariables.. we need to translate it into an array of ActionVariable objects
10760          * where path and windowName are params for the BROWSER_POP type
10761          */
10762         buildActionVariablesForRest: function(actionVariableMap){
10763             var actionVariables = {"ActionVariable": []},
10764                 i,
10765                 actionVariable;
10766             for(i in actionVariableMap){
10767                 if(actionVariableMap.hasOwnProperty(i)){
10768                     // {name: "callVariable1", type: "SYSTEM", node: "", testValue: "<blink>"}
10769                     actionVariable = {
10770                         "name": actionVariableMap[i].name,
10771                         "type": actionVariableMap[i].type,
10772                         "node": actionVariableMap[i].node,
10773                         "testValue": actionVariableMap[i].testValue
10774                     };
10775                     actionVariables.ActionVariable.push(actionVariable);
10776                 }
10777             }
10778             return actionVariables;
10779         },
10780 
10781         /**
10782          * Add
10783          */
10784         add: function (newValues, handlers) {
10785             var contentBody = {};
10786 
10787             contentBody[this.getRestType()] = {
10788                 "name": newValues.name,
10789                 "type": newValues.type,
10790                 "handledBy": newValues.handledBy,
10791                 "params": this.buildParamsForRest(newValues.params),
10792                 "actionVariables": this.buildActionVariablesForRest(newValues.actionVariables)
10793             };
10794 
10795             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
10796             handlers = handlers || {};
10797 
10798             this.restRequest(this.getRestUrl(), {
10799                 method: 'POST',
10800                 success: this.createPostSuccessHandler(this, contentBody, handlers.success),
10801                 error: handlers.error,
10802                 content: contentBody
10803             });
10804 
10805             return this; // Allow cascading
10806         },
10807 
10808         /**
10809          * @private
10810          * Update
10811          */
10812         update: function (newValues, handlers) {
10813             this.isLoaded();
10814             var contentBody = {};
10815             
10816             contentBody[this.getRestType()] = {
10817                 "uri": this.getId(),
10818                 "name": newValues.name,
10819                 "type": newValues.type,
10820                 "handledBy": newValues.handledBy,
10821                 "params": this.buildParamsForRest(newValues.params),
10822                 "actionVariables": this.buildActionVariablesForRest(newValues.actionVariables)
10823             };
10824 
10825             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
10826             handlers = handlers || {};
10827 
10828             this.restRequest(this.getRestUrl(), {
10829                 method: 'PUT',
10830                 success: this.createPutSuccessHandler(this, contentBody, handlers.success),
10831                 error: handlers.error,
10832                 content: contentBody
10833             });
10834 
10835             return this; // Allow cascading
10836         },
10837 
10838 
10839         /**
10840          * @private
10841          * Delete
10842          */
10843         "delete": function ( handlers) {
10844             this.isLoaded();
10845 
10846             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
10847             handlers = handlers || {};
10848 
10849             this.restRequest(this.getRestUrl(), {
10850                 method: 'DELETE',
10851                 success: this.createPutSuccessHandler(this, {}, handlers.success),
10852                 error: handlers.error,
10853                 content: undefined
10854             });
10855 
10856             return this; // Allow cascading
10857         }
10858 
10859 
10860 
10861     });
10862 
10863     window.finesse = window.finesse || {};
10864     window.finesse.restservices = window.finesse.restservices || {};
10865     window.finesse.restservices.WorkflowAction = WorkflowAction;
10866     
10867     return WorkflowAction;
10868 });
10869 
10870 /**
10871 * JavaScript representation of the Finesse WorkflowActions collection
10872 * object which contains a list of WorkflowAction objects.
10873  *
10874  * @requires finesse.clientservices.ClientServices
10875  * @requires Class
10876  * @requires finesse.FinesseBase
10877  * @requires finesse.restservices.RestBase
10878  * @requires finesse.restservices.Dialog
10879  * @requires finesse.restservices.RestCollectionBase
10880  */
10881 
10882 /** @private */
10883 define('restservices/WorkflowActions',[
10884     'restservices/RestCollectionBase',
10885     'restservices/RestBase',
10886     'restservices/WorkflowAction'
10887 ],
10888 function (RestCollectionBase, RestBase, WorkflowAction) {
10889 
10890     var WorkflowActions = RestCollectionBase.extend({
10891         
10892         /**
10893          * @class
10894          * JavaScript representation of a WorkflowActions collection object. 
10895          * @augments finesse.restservices.RestCollectionBase
10896          * @constructs
10897          * @see finesse.restservices.WorkflowAction
10898          * @see finesse.restservices.Workflow
10899          * @see finesse.restservices.Workflows
10900          * @example
10901          *  _workflowActions = _user.getWorkflowActions( {
10902          *      onCollectionAdd : _handleWorkflowActionAdd,
10903          *      onCollectionDelete : _handleWorkflowActionDelete,
10904          *      onLoad : _handleWorkflowActionsLoaded
10905          *  });
10906         */
10907         _fakeConstuctor: function () {
10908             /* This is here to hide the real init constructor from the public docs */
10909         },
10910         
10911         /**
10912          * @private
10913          * JavaScript representation of a WorkflowActions collection object. Also exposes
10914          * methods to operate on the object against the server.
10915          *
10916          * @param {Object} options
10917          *     An object with the following properties:<ul>
10918          *         <li><b>id:</b> The id of the object being constructed</li>
10919          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
10920          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
10921          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
10922          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
10923          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
10924          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
10925          *             <li><b>content:</b> {String} Raw string of response</li>
10926          *             <li><b>object:</b> {Object} Parsed object of response</li>
10927          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
10928          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
10929          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
10930          *             </ul></li>
10931          *         </ul></li>
10932          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
10933          **/
10934         init: function (options) {
10935             this._super(options);           
10936         },
10937 
10938         /**
10939          * @private
10940          * Gets the REST class for the current object - this is the WorkflowActions class.
10941          */
10942         getRestClass: function () {
10943             return WorkflowActions;
10944         },
10945 
10946         /**
10947          * @private
10948          * Gets the REST class for the objects that make up the collection. - this
10949          * is the WorkflowAction class.
10950          */
10951         getRestItemClass: function () {
10952             return WorkflowAction;
10953         },
10954 
10955         /**
10956          * @private
10957          * Gets the REST type for the current object - this is a "WorkflowActions".
10958          */
10959         getRestType: function () {
10960             return "WorkflowActions";
10961         },
10962         
10963         /**
10964          * @private
10965          * Gets the REST type for the objects that make up the collection - this is "WorkflowActions".
10966          */
10967         getRestItemType: function () {
10968             return "WorkflowAction";
10969         },
10970 
10971         /**
10972          * @private
10973          * Override default to indicates that the collection supports making
10974          * requests.
10975          */
10976         supportsRequests: true,
10977 
10978         /**
10979          * @private
10980          * Override default to indicates that the collection subscribes to its objects.
10981          */
10982         supportsRestItemSubscriptions: false,
10983         
10984         /**
10985          * @private
10986          * Retrieve the WorkflowActions.
10987          *
10988          * @returns {finesse.restservices.WorkflowActions}
10989          *     This WorkflowActions object to allow cascading.
10990          */
10991         get: function () {
10992             // set loaded to false so it will rebuild the collection after the get
10993             this._loaded = false;
10994             // reset collection
10995             this._collection = {};
10996             // perform get
10997             this._synchronize();
10998             return this;
10999         }
11000     });
11001 
11002     window.finesse = window.finesse || {};
11003     window.finesse.restservices = window.finesse.restservices || {};
11004     window.finesse.restservices.WorkflowActions = WorkflowActions;
11005         
11006     return WorkflowActions;
11007 });
11008 
11009 /**
11010  * JavaScript representation of the Finesse Workflow object.
11011  *
11012  * @requires finesse.clientservices.ClientServices
11013  * @requires Class
11014  * @requires finesse.FinesseBase
11015  * @requires finesse.restservices.RestBase
11016  */
11017 
11018 /*jslint browser: true, nomen: true, sloppy: true, forin: true */
11019 /*global define,finesse */
11020 
11021 /** @private */
11022 define('restservices/Workflow',[
11023     'restservices/RestBase',
11024     'restservices/WorkflowActions'
11025 ],
11026 function (RestBase, WorkflowActions) {
11027 
11028     var Workflow = RestBase.extend({
11029 
11030         /**
11031          * @class
11032          * JavaScript representation of a Workflow object. Also exposes
11033          * methods to operate on the object against the server.
11034          *
11035          * @param {Object} options
11036          *     An object with the following properties:<ul>
11037          *         <li><b>id:</b> The id of the object being constructed</li>
11038          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
11039          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
11040          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
11041          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
11042          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
11043          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
11044          *             <li><b>content:</b> {String} Raw string of response</li>
11045          *             <li><b>object:</b> {Object} Parsed object of response</li>
11046          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
11047          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
11048          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
11049          *             </ul></li>
11050          *         </ul></li>
11051          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
11052          * @constructs
11053          **/
11054         init: function (options) {
11055             this._super(options);
11056         },
11057 
11058         /**
11059          * @private
11060          * Gets the REST class for the current object - this is the Workflow class.
11061          * @returns {Object} The Workflow class.
11062          */
11063         getRestClass: function () {
11064             return Workflow;
11065         },
11066 
11067         /**
11068          * @private
11069          * Gets the REST type for the current object - this is a "Workflow".
11070          * @returns {String} The Workflow string.
11071          */
11072         getRestType: function () {
11073             return "Workflow";
11074         },
11075 
11076         /**
11077          * @private
11078          * Override default to indicate that this object doesn't support making
11079          * requests.
11080          */
11081         supportsRequests: false,
11082 
11083         /**
11084          * @private
11085          * Override default to indicate that this object doesn't support subscriptions.
11086          */
11087         supportsSubscriptions: false,
11088 
11089         /**
11090          * @private
11091          * Getter for the Uri value.
11092          * @returns {String} The Uri.
11093          */
11094         getUri: function () {
11095             this.isLoaded();
11096             return this.getData().uri;
11097         },
11098 
11099         /**
11100          * Getter for the name.
11101          * @returns {String} The name.
11102          */
11103         getName: function () {
11104             this.isLoaded();
11105             return this.getData().name;
11106         },
11107 
11108         /**
11109          * Getter for the description.
11110          * @returns {String} The description.
11111          */
11112         getDescription: function () {
11113             this.isLoaded();
11114             return this.getData().description;
11115         },
11116 
11117         /**
11118          * Getter for the trigger set.
11119          * @returns {String} The trigger set.
11120          */
11121         getTriggerSet: function () {
11122             this.isLoaded();
11123             return this.getData().TriggerSet;
11124         },
11125 
11126         /**
11127          * Getter for the condition set.
11128          * @returns {String} The condition set.
11129          */
11130         getConditionSet: function () {
11131             this.isLoaded();
11132             return this.getData().ConditionSet;
11133         },
11134         
11135         /**
11136          * Getter for the assigned workflowActions.
11137          * @returns {String} The workflowActions object.
11138          */
11139         getWorkflowActions: function () {
11140             this.isLoaded();
11141             var workflowActions = this.getData().workflowActions;
11142             if (workflowActions === null) {
11143                 workflowActions = "";
11144             }
11145             return workflowActions;
11146         },
11147 
11148         createPutSuccessHandler: function (workflow, contentBody, successHandler) {
11149             return function (rsp) {
11150                 // Update internal structure based on response. Here we
11151                 // inject the contentBody from the PUT request into the
11152                 // rsp.object element to mimic a GET as a way to take
11153                 // advantage of the existing _processResponse method.
11154                 rsp.object = contentBody;
11155                 workflow._processResponse(rsp);
11156 
11157                 //Remove the injected Workflow object before cascading response
11158                 rsp.object = {};
11159 
11160                 //cascade response back to consumer's response handler
11161                 successHandler(rsp);
11162             };
11163         },
11164 
11165         createPostSuccessHandler: function (workflow, contentBody, successHandler) {
11166             return function (rsp) {
11167                 rsp.object = contentBody;
11168                 workflow._processResponse(rsp);
11169 
11170                 //Remove the injected Workflow object before cascading response
11171                 rsp.object = {};
11172 
11173                 //cascade response back to consumer's response handler
11174                 successHandler(rsp);
11175             };
11176         },
11177 
11178         /**
11179          * @private
11180          * Add
11181          */
11182         add: function (newValues, handlers) {
11183             // this.isLoaded();
11184             var contentBody = {};
11185 
11186             contentBody[this.getRestType()] = {
11187                 "name": newValues.name,
11188                 "description": newValues.description,
11189                 "TriggerSet" : newValues.TriggerSet,
11190                 "ConditionSet" : newValues.ConditionSet,
11191                 "workflowActions" : newValues.workflowActions
11192             };
11193 
11194             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
11195             handlers = handlers || {};
11196 
11197             this.restRequest(this.getRestUrl(), {
11198                 method: 'POST',
11199                 success: this.createPostSuccessHandler(this, contentBody, handlers.success),
11200                 error: handlers.error,
11201                 content: contentBody
11202             });
11203 
11204             return this; // Allow cascading
11205         },
11206 
11207         /**
11208          * @private
11209          * Update
11210          */
11211         update: function (newValues, handlers) {
11212             this.isLoaded();
11213             var contentBody = {};
11214 
11215             contentBody[this.getRestType()] = {
11216                 "uri": this.getId(),
11217                 "name": newValues.name,
11218                 "description": newValues.description,
11219                 "TriggerSet" : newValues.TriggerSet,
11220                 "ConditionSet" : newValues.ConditionSet,
11221                 "workflowActions" : newValues.workflowActions
11222             };
11223 
11224             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
11225             handlers = handlers || {};
11226 
11227             this.restRequest(this.getRestUrl(), {
11228                 method: 'PUT',
11229                 success: this.createPutSuccessHandler(this, contentBody, handlers.success),
11230                 error: handlers.error,
11231                 content: contentBody
11232             });
11233 
11234             return this; // Allow cascading
11235         },
11236 
11237 
11238         /**
11239          * @private
11240          * Delete
11241          */
11242         "delete": function (handlers) {
11243             this.isLoaded();
11244 
11245             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
11246             handlers = handlers || {};
11247 
11248             this.restRequest(this.getRestUrl(), {
11249                 method: 'DELETE',
11250                 success: this.createPutSuccessHandler(this, {}, handlers.success),
11251                 error: handlers.error,
11252                 content: undefined
11253             });
11254 
11255             return this; // Allow cascading
11256         }
11257 
11258 
11259 
11260     });
11261 
11262     window.finesse = window.finesse || {};
11263     window.finesse.restservices = window.finesse.restservices || {};
11264     window.finesse.restservices.Workflow = Workflow;
11265 
11266     return Workflow;
11267 });
11268 
11269 /**
11270 * JavaScript representation of the Finesse workflows collection
11271 * object which contains a list of workflow objects.
11272  *
11273  * @requires finesse.clientservices.ClientServices
11274  * @requires Class
11275  * @requires finesse.FinesseBase
11276  * @requires finesse.restservices.RestBase
11277  * @requires finesse.restservices.Dialog
11278  * @requires finesse.restservices.RestCollectionBase
11279  */
11280 
11281 /** @private */
11282 define('restservices/Workflows',[
11283     'restservices/RestCollectionBase',
11284     'restservices/RestBase',
11285     'restservices/Workflow'
11286 ],
11287 function (RestCollectionBase, RestBase, Workflow) {
11288 
11289     var Workflows = RestCollectionBase.extend({
11290 
11291         /**
11292          * @class
11293          * JavaScript representation of a workflows collection object. Also exposes
11294          * methods to operate on the object against the server.
11295          *
11296          * @param {Object} options
11297          *     An object with the following properties:<ul>
11298          *         <li><b>id:</b> The id of the object being constructed</li>
11299          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
11300          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
11301          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
11302          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
11303          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
11304          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
11305          *             <li><b>content:</b> {String} Raw string of response</li>
11306          *             <li><b>object:</b> {Object} Parsed object of response</li>
11307          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
11308          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
11309          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
11310          *             </ul></li>
11311          *         </ul></li>
11312          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
11313          *  @constructs
11314          **/
11315         init: function (options) {
11316             this._super(options);
11317         },
11318 
11319         /**
11320          * @private
11321          * Gets the REST class for the current object - this is the workflows class.
11322          */
11323         getRestClass: function () {
11324             return Workflows;
11325         },
11326 
11327         /**
11328          * @private
11329          * Gets the REST class for the objects that make up the collection. - this
11330          * is the workflow class.
11331          */
11332         getRestItemClass: function () {
11333             return Workflow;
11334         },
11335 
11336         /**
11337          * @private
11338          * Gets the REST type for the current object - this is a "workflows".
11339          */
11340         getRestType: function () {
11341             return "Workflows";
11342         },
11343 
11344         /**
11345          * @private
11346          * Gets the REST type for the objects that make up the collection - this is "workflows".
11347          */
11348         getRestItemType: function () {
11349             return "Workflow";
11350         },
11351 
11352         /**
11353          * @private
11354          * Override default to indicates that the collection supports making requests.
11355          */
11356         supportsRequests: true,
11357 
11358         /**
11359          * @private
11360          * Override default to indicates that the collection does not subscribe to its objects.
11361          */
11362         supportsRestItemSubscriptions: false,
11363 
11364         /**
11365          * @private
11366          * Retrieve the workflows. This call will re-query the server and refresh the collection.
11367          *
11368          * @returns {finesse.restservices.workflows}
11369          *     This workflows object to allow cascading.
11370          */
11371         get: function () {
11372             // set loaded to false so it will rebuild the collection after the get
11373             this._loaded = false;
11374             // reset collection
11375             this._collection = {};
11376             // perform get
11377             this._synchronize();
11378             return this;
11379         }
11380     });
11381 
11382     window.finesse = window.finesse || {};
11383     window.finesse.restservices = window.finesse.restservices || {};
11384     window.finesse.restservices.Workflows = Workflows;
11385         
11386     return Workflows;
11387 });
11388 
11389 /**
11390  * JavaScript representation of the Finesse MediaPropertiesLayout object for the Admin webapp.
11391  * @requires finesse.clientservices.ClientServices
11392  * @requires Class
11393  * @requires finesse.FinesseBase
11394  * @requires finesse.restservices.RestBase
11395  */
11396 
11397 /** The following comment is to prevent jslint errors about 
11398  * using variables before they are defined.
11399  */
11400 /*global finesse*/
11401 
11402 /**
11403  * @class
11404  * JavaScript representation of a MediaPropertiesLayout object for the Admin webapp. Also exposes
11405  * methods to operate on the object against the server.
11406  *
11407  * @constructor
11408  * @param {String} id
11409  *     Not required...
11410  * @param {Object} callbacks
11411  *     An object containing callbacks for instantiation and runtime
11412  * @param {Function} callbacks.onLoad(this)
11413  *     Callback to invoke upon successful instantiation, passes in MediaPropertiesLayout object
11414  * @param {Function} callbacks.onLoadError(rsp)
11415  *     Callback to invoke on instantiation REST request error
11416  *     as passed by finesse.clientservices.ClientServices.ajax()
11417  *     {
11418  *         status: {Number} The HTTP status code returned
11419  *         content: {String} Raw string of response
11420  *         object: {Object} Parsed object of response
11421  *         error: {Object} Wrapped exception that was caught
11422  *         error.errorType: {String} Type of error that was caught
11423  *         error.errorMessage: {String} Message associated with error
11424  *     }
11425  * @param {Function} callbacks.onChange(this)
11426  *     Callback to invoke upon successful update, passes in MediaPropertiesLayout object
11427  * @param {Function} callbacks.onError(rsp)
11428  *     Callback to invoke on update error (refresh or event)
11429  *     as passed by finesse.clientservices.ClientServices.ajax()
11430  *     {
11431  *         status: {Number} The HTTP status code returned
11432  *         content: {String} Raw string of response
11433  *         object: {Object} Parsed object of response
11434  *         error: {Object} Wrapped exception that was caught
11435  *         error.errorType: {String} Type of error that was caught
11436  *         error.errorMessage: {String} Message associated with error
11437  *     }
11438  */
11439 
11440 /** @private */
11441 define('restservices/MediaPropertiesLayout',['restservices/RestBase'], function (RestBase) {
11442     var MediaPropertiesLayout = RestBase.extend(/** @lends finesse.restservices.MediaPropertiesLayout.prototype */{
11443 
11444         /**
11445          * @class
11446          * The MediaPropertiesLayout handles which call variables are associated with Dialogs.
11447          * 
11448          * @augments finesse.restservices.RestBase
11449          * @see finesse.restservices.Dialog#getMediaProperties
11450          * @see finesse.restservices.User#getMediaPropertiesLayout
11451          * @constructs
11452          */
11453         _fakeConstuctor: function () {
11454             /* This is here to hide the real init constructor from the public docs */
11455         },
11456         
11457         /**
11458          * @private
11459          * JavaScript representation of a MediaPropertiesLayout object. Also exposes
11460          * methods to operate on the object against the server.
11461          *
11462          * @param {Object} options
11463          *     An object with the following properties:<ul>
11464          *         <li><b>id:</b> The id of the object being constructed</li>
11465          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
11466          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
11467          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
11468          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
11469          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
11470          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
11471          *             <li><b>content:</b> {String} Raw string of response</li>
11472          *             <li><b>object:</b> {Object} Parsed object of response</li>
11473          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
11474          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
11475          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
11476          *             </ul></li>
11477          *         </ul></li>
11478          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
11479          **/
11480         init: function (options) {
11481             this._super(options);
11482         },
11483 
11484         /**
11485          * @private
11486          * Gets the REST class for the current object - this is the MediaPropertiesLayout object.
11487          */
11488         getRestClass: function () {
11489             return MediaPropertiesLayout;
11490         },
11491 
11492         /**
11493          * @private
11494          * Gets the REST type for the current object - this is a "MediaPropertiesLayout".
11495          */
11496         getRestType: function () {
11497             return "MediaPropertiesLayout";
11498         },
11499 
11500         /**
11501          * @private
11502          * Returns whether this object supports subscriptions
11503          */
11504         supportsSubscriptions: false,
11505 
11506         /**
11507          * Getter for the name.
11508          * @returns {String} The name.
11509          */
11510         getName: function () {
11511             this.isLoaded();
11512             return this._data.name;
11513         },
11514 
11515         /**
11516          * Getter for the description.
11517          * @returns {String} The description.
11518          */
11519         getDescription: function () {
11520             this.isLoaded();
11521             return this._data.description || "";
11522         },
11523 
11524         /**
11525          * Getter for the layout type (should be DEFAULT or CUSTOM).
11526          * @returns {String} The layout type.
11527          */
11528         getType: function () {
11529             this.isLoaded();
11530             return this._data.type || "";
11531         },
11532 
11533         /**
11534          * Retrieve the media properties layout. This call will re-query the server and refresh the layout object.
11535          * @returns {finesse.restservices.MediaPropertiesLayout}
11536          *     This MediaPropertiesLayout object to allow cascading
11537          */
11538         get: function () {
11539             this._synchronize();
11540 
11541             return this; //Allow cascading
11542         },
11543 
11544         /**
11545          * Gets the data for this object.
11546          * 
11547          * Performs safe conversion from raw API data to ensure that the returned layout object
11548          * always has a header with correct entry fields, and exactly two columns with lists of entries.
11549          *
11550          * @returns {finesse.restservices.MediaPropertiesLayout.Object} Data in columns (unless only one defined).
11551          */
11552         getData: function () {
11553 
11554             var layout = this._data, result, _addColumnData;
11555 
11556             result = this.getEmptyData();
11557             result.name = layout.name;
11558             result.description = layout.description;
11559             result.type = layout.type;
11560 
11561             /**
11562              * @private
11563              */
11564             _addColumnData = function (entryData, colIndex) {
11565 
11566                 if (!entryData) {
11567                     //If there's no entry data at all, rewrite entryData to be an empty collection of entries
11568                     entryData = {};
11569                 } else if (entryData.mediaProperty) {
11570                     //If entryData contains the keys for a single entry rather than being a collection of entries,
11571                     //rewrite it to be a collection containing a single entry
11572                     entryData = { "": entryData };
11573                 }
11574 
11575                 //Add each of the entries in the list to the column
11576                 jQuery.each(entryData, function (i, entryData) {
11577 
11578                     //If the entry has no displayName specified, explicitly set it to the empty string
11579                     if (!entryData.displayName) {
11580                         entryData.displayName = "";
11581                     }
11582 
11583                     result.columns[colIndex].push(entryData);
11584 
11585                 });
11586 
11587             };
11588 
11589             //The header should only contain a single entry
11590             if (layout.header && layout.header.entry) {
11591 
11592                 //If the entry has no displayName specified, explicitly set it to the empty string
11593                 if (!layout.header.entry.displayName) {
11594                     layout.header.entry.displayName = "";
11595                 }
11596 
11597                 result.header = layout.header.entry;
11598 
11599             } else {
11600 
11601                 throw "MediaPropertiesLayout.getData() - Header does not contain an entry";
11602 
11603             }
11604 
11605             //If the column object contains an entry object that wasn't part of a list of entries,
11606             //it must be a single right-hand entry object (left-hand entry object would be part of a list.)
11607             //Force the entry object to be the 2nd element in an otherwise-empty list.
11608             if (layout.column && layout.column.entry) {
11609                 layout.column = [
11610                     null,
11611                     { "entry": layout.column.entry }
11612                 ];
11613             }
11614 
11615             if (layout.column && layout.column.length > 0 && layout.column.length <= 2) {
11616 
11617                 //Render left column entries
11618                 if (layout.column[0] && layout.column[0].entry) {
11619                     _addColumnData(layout.column[0].entry, 0);
11620                 }
11621 
11622                 //Render right column entries
11623                 if (layout.column[1] && layout.column[1].entry) {
11624                     _addColumnData(layout.column[1].entry, 1);
11625                 }
11626 
11627             }
11628 
11629             return result;
11630 
11631         },
11632 
11633         /**
11634          * @private
11635          * Empty/template version of getData().
11636          *
11637          * Used by getData(), and by callers of getData() in error cases.
11638          */
11639         getEmptyData: function () {
11640 
11641             return {
11642                 header : {
11643                     displayName: null,
11644                     mediaProperty: null
11645                 },
11646                 columns : [[], []]
11647             };
11648 
11649         },
11650 
11651         /**
11652          * Update the layout of this MediaPropertiesLayout
11653          * @param {Object} layout
11654          *      The object representation of the layout you are setting
11655          * @param {finesse.interfaces.RequestHandlers} handlers
11656          *      An object containing the handlers for the request
11657          * @returns {finesse.restservices.MediaPropertiesLayout}
11658          *      This MediaPropertiesLayout object to allow cascading
11659          * @private
11660          */
11661         update: function (newLayoutObject, handlers) {
11662             var contentBody = {};
11663 
11664             // Make sure type is kept the same
11665             newLayoutObject.type = this.getType();
11666 
11667             contentBody[this.getRestType()] = newLayoutObject;
11668 
11669             //Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
11670             handlers = handlers || {};
11671 
11672             this.restRequest(this.getRestUrl(), {
11673                 method: 'PUT',
11674                 success: handlers.success,
11675                 error: handlers.error,
11676                 content: contentBody
11677             });
11678 
11679             return this; // Allow cascading
11680         },
11681 
11682         /**
11683          * Create a new MediaPropertiesLayout object with the layout passed in
11684          * @param {Object} layout
11685          *      The object representation of the layout you are creating
11686          * @param {finesse.interfaces.RequestHandlers} handlers
11687          *      An object containing the handlers for the request
11688          * @returns {finesse.restservices.MediaPropertiesLayout}
11689          *      This MediaPropertiesLayout object to allow cascading
11690          * @private
11691          */
11692         add: function (layout, handlers) {
11693             var contentBody = {};
11694 
11695             contentBody[this.getRestType()] = layout;
11696 
11697             handlers = handlers || {};
11698 
11699             this.restRequest(this.getRestUrl(), {
11700                 method: 'POST',
11701                 success: handlers.success,
11702                 error: handlers.error,
11703                 content: contentBody
11704             });
11705 
11706             return this; // Allow cascading
11707         },
11708 
11709         /**
11710          * Delete this MediaPropertiesLayout
11711          * @param {finesse.interfaces.RequestHandlers} handlers
11712          *      An object containing the handlers for the request
11713          * @returns {finesse.restservices.MediaPropertiesLayout}
11714          *      This MediaPropertiesLayout object to allow cascading
11715          * @private
11716          */
11717         "delete": function (handlers) {
11718             handlers = handlers || {};
11719 
11720             this.restRequest(this.getRestUrl(), {
11721                 method: 'DELETE',
11722                 success: handlers.success,
11723                 error: handlers.error,
11724                 content: undefined
11725             });
11726 
11727             return this; // Allow cascading
11728         }
11729 
11730     });
11731     
11732     MediaPropertiesLayout.Object = /** @lends finesse.restservices.MediaPropertiesLayout.Object.prototype */ {
11733         /**
11734          * @class Format of MediaPropertiesLayout Object.<br>
11735          * Object { <ul>
11736          *      <li>header : { <ul>
11737          *          <li>dispayName {String} 
11738          *          <li>mediaProperty {String}</ul>}
11739          *      <li>columns : { <ul>
11740          *          <li>[ [] , [] ]
11741          *          </ul>
11742          *      where column arrays consists of the same Object format as header.<br>
11743          *          }</ul>
11744          *      }<br>         
11745          * @constructs
11746          */
11747         _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
11748         
11749     };
11750 
11751 	window.finesse = window.finesse || {};
11752     window.finesse.restservices = window.finesse.restservices || {};
11753     window.finesse.restservices.MediaPropertiesLayout = MediaPropertiesLayout;
11754     
11755     return MediaPropertiesLayout;
11756 });
11757 
11758 /**
11759  * JavaScript representation of the Finesse MediaPropertiesLayout object for a User
11760  *
11761  * @requires MediaPropertiesLayout
11762  * @requires ClientServices
11763  * @requires finesse.FinesseBase
11764  * @requires finesse.restservices.RestBase
11765  */
11766 
11767 /** The following comment is to prevent jslint errors about 
11768  * using variables before they are defined.
11769  */
11770 /*global finesse*/
11771 
11772 /** @private */
11773 define('restservices/UserMediaPropertiesLayout',['restservices/MediaPropertiesLayout'], function (MediaPropertiesLayout) {
11774      var UserMediaPropertiesLayout = MediaPropertiesLayout.extend(/** @lends finesse.restservices.UserMediaPropertiesLayout.prototype */{
11775 
11776 		/**
11777 		 * @class
11778 		 * JavaScript representation of a UserMediaPropertiesLayout collection object. Also exposes
11779 		 * methods to operate on the object against the server.
11780 		 * 
11781 		 * @param {Object} options
11782 		 * An object with the following properties:<ul>
11783 		 *        <li><b>id:</b> The id of the object being constructed</li>
11784 		 *        <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
11785 		 *        <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
11786 		 *        <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
11787 		 *        <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
11788 		 *        <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
11789 		 *            <li><b>status:</b> {Number} The HTTP status code returned</li>
11790 		 *            <li><b>content:</b> {String} Raw string of response</li>
11791 		 *            <li><b>object:</b> {Object} Parsed object of response</li>
11792 		 *            <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
11793 		 *                <li><b>errorType:</b> {String} Type of error that was caught</li>
11794 		 *                <li><b>errorMessage:</b> {String} Message associated with error</li>
11795 		 *            </ul></li>
11796 		 *        </ul></li>
11797 		 *        <li><b>parentObj: (optional)</b> The parent object</li></ul>
11798 		 * @constructs
11799 		**/
11800 		init: function (options) {
11801 		    this._super(options);
11802 		},
11803 		
11804 		/**
11805 		 * @private
11806 		 * Gets the REST class for the current object - this is the UserMediaPropertiesLayout class.
11807 		 */
11808 		getRestClass: function () {
11809 		    return UserMediaPropertiesLayout;
11810 		},
11811 
11812         /**
11813          * Overrides the parent class.  Returns the url for the UserMediaPropertiesLayout resource
11814          */
11815         getRestUrl: function () {
11816             return ("/finesse/api/User/" + this.getId() + "/" + this.getRestType());
11817         },
11818 
11819         /**
11820          * @private
11821          * Override to throw an error because we cannot do an update on the User's
11822          * MediaPropertiesLayout node
11823          */
11824         update: function (layout, handlers) {
11825             throw new Error("update(): Cannot update layout for User's MediaPropertiesLayout");
11826         },
11827 
11828         /**
11829          * @private
11830          * Override to throw an error because we cannot create a new layout on the User's
11831          * MediaPropertiesLayout node
11832          */
11833         add: function (layout, handlers) {
11834             throw new Error("add(): Cannot create a new layout for User's MediaPropertiesLayout");
11835         },
11836 
11837         /**
11838          * @private
11839          * Override to throw an error because we cannot delete the layout on the User's
11840          * MediaPropertiesLayout node
11841          */
11842         "delete": function (layout, handlers) {
11843             throw new Error("delete(): Cannot delete the layout for User's MediaPropertiesLayout");
11844         }
11845 
11846     });
11847 	
11848 	window.finesse = window.finesse || {};
11849     window.finesse.restservices = window.finesse.restservices || {};
11850     window.finesse.restservices.UserMediaPropertiesLayout = UserMediaPropertiesLayout;
11851     
11852     return UserMediaPropertiesLayout;
11853 });
11854 
11855 /**
11856 * JavaScript representation of the Finesse mediaPropertiesLayouts collection
11857 * object which contains a list of mediaPropertiesLayout objects.
11858  *
11859  * @requires finesse.clientservices.ClientServices
11860  * @requires Class
11861  * @requires finesse.FinesseBase
11862  * @requires finesse.restservices.RestBase
11863  * @requires finesse.restservices.Dialog
11864  * @requires finesse.restservices.RestCollectionBase
11865  */
11866 
11867 /** @private */
11868 define('restservices/MediaPropertiesLayouts',[
11869     'restservices/RestCollectionBase',
11870     'restservices/RestBase',
11871     'restservices/MediaPropertiesLayout'
11872 ],
11873 function (RestCollectionBase, RestBase, MediaPropertiesLayout) {
11874 
11875     var MediaPropertiesLayouts = RestCollectionBase.extend({
11876 
11877         /**
11878          * @class
11879          * JavaScript representation of a mediaPropertiesLayouts collection object. Also exposes
11880          * methods to operate on the object against the server.
11881          *
11882          * @param {Object} options
11883          *     An object with the following properties:<ul>
11884          *         <li><b>id:</b> The id of the object being constructed</li>
11885          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
11886          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
11887          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
11888          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
11889          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
11890          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
11891          *             <li><b>content:</b> {String} Raw string of response</li>
11892          *             <li><b>object:</b> {Object} Parsed object of response</li>
11893          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
11894          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
11895          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
11896          *             </ul></li>
11897          *         </ul></li>
11898          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
11899          *  @constructs
11900          **/
11901         init: function (options) {
11902             this._super(options);
11903         },
11904 
11905         /**
11906          * @private
11907          * Gets the REST class for the current object - this is the mediaPropertiesLayouts class.
11908          */
11909         getRestClass: function () {
11910             return MediaPropertiesLayouts;
11911         },
11912 
11913         /**
11914          * @private
11915          * Gets the REST class for the objects that make up the collection. - this
11916          * is the mediaPropertiesLayout class.
11917          */
11918         getRestItemClass: function () {
11919             return MediaPropertiesLayout;
11920         },
11921 
11922         /**
11923          * @private
11924          * Gets the REST type for the current object - this is a "mediaPropertiesLayouts".
11925          */
11926         getRestType: function () {
11927             return "MediaPropertiesLayouts";
11928         },
11929 
11930         /**
11931          * @private
11932          * Gets the REST type for the objects that make up the collection - this is "mediaPropertiesLayouts".
11933          */
11934         getRestItemType: function () {
11935             return "MediaPropertiesLayout";
11936         },
11937 
11938         /**
11939          * @private
11940          * Override default to indicates that the collection supports making requests.
11941          */
11942         supportsRequests: true,
11943 
11944         /**
11945          * @private
11946          * Override default to indicates that the collection does not subscribe to its objects.
11947          */
11948         supportsRestItemSubscriptions: false,
11949 
11950         /**
11951          * @private
11952          * Retrieve the MediaPropertiesLayouts. This call will re-query the server and refresh the collection.
11953          *
11954          * @returns {finesse.restservices.MediaPropertiesLayouts}
11955          *     This MediaPropertiesLayouts object to allow cascading.
11956          */
11957         get: function () {
11958             // set loaded to false so it will rebuild the collection after the get
11959             this._loaded = false;
11960             // reset collection
11961             this._collection = {};
11962             // perform get
11963             this._synchronize();
11964             return this;
11965         }
11966     });
11967 
11968     window.finesse = window.finesse || {};
11969     window.finesse.restservices = window.finesse.restservices || {};
11970     window.finesse.restservices.MediaPropertiesLayouts = MediaPropertiesLayouts;
11971         
11972     return MediaPropertiesLayouts;
11973 });
11974 
11975 /**
11976  * JavaScript representation of the Finesse MediaPropertiesLayout object for a User
11977  *
11978  * @requires MediaPropertiesLayout
11979  * @requires ClientServices
11980  * @requires finesse.FinesseBase
11981  * @requires finesse.restservices.RestBase
11982  */
11983 
11984 /** The following comment is to prevent jslint errors about 
11985  * using variables before they are defined.
11986  */
11987 /*global finesse*/
11988 
11989 /** @private */
11990 define('restservices/UserMediaPropertiesLayouts',[
11991 	'restservices/MediaPropertiesLayouts',
11992 	'restservices/UserMediaPropertiesLayout'
11993 ],
11994 function (MediaPropertiesLayouts, UserMediaPropertiesLayout) {
11995      var UserMediaPropertiesLayouts = MediaPropertiesLayouts.extend(/** @lends finesse.restservices.UserMediaPropertiesLayouts.prototype */{
11996 
11997 		/**
11998 		 * @class
11999 		 * JavaScript representation of a UserMediaPropertiesLayouts collection object. Also exposes
12000 		 * methods to operate on the object against the server.
12001 		 * 
12002 		 * @param {Object} options
12003 		 * An object with the following properties:<ul>
12004 		 *        <li><b>id:</b> The id of the object being constructed</li>
12005 		 *        <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
12006 		 *        <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
12007 		 *        <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
12008 		 *        <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
12009 		 *        <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
12010 		 *            <li><b>status:</b> {Number} The HTTP status code returned</li>
12011 		 *            <li><b>content:</b> {String} Raw string of response</li>
12012 		 *            <li><b>object:</b> {Object} Parsed object of response</li>
12013 		 *            <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
12014 		 *                <li><b>errorType:</b> {String} Type of error that was caught</li>
12015 		 *                <li><b>errorMessage:</b> {String} Message associated with error</li>
12016 		 *            </ul></li>
12017 		 *        </ul></li>
12018 		 *        <li><b>parentObj: (optional)</b> The parent object</li></ul>
12019 		 * @constructs
12020 		**/
12021 		init: function (options) {
12022 		    this._super(options);
12023 		},
12024 		
12025 		/**
12026 		 * @private
12027 		 * Gets the REST class for the current object - this is the UserMediaPropertiesLayouts class.
12028 		 */
12029 		getRestClass: function () {
12030 		    return UserMediaPropertiesLayouts;
12031 		},
12032 
12033         /**
12034          * @private
12035          * Gets the REST class for the objects that make up the collection. - this
12036          * is the UserMediaPropertiesLayout class.
12037          */
12038 		getRestItemClass: function() {
12039 			return UserMediaPropertiesLayout;
12040 		}
12041     });
12042 	
12043 	window.finesse = window.finesse || {};
12044     window.finesse.restservices = window.finesse.restservices || {};
12045     window.finesse.restservices.UserMediaPropertiesLayouts = UserMediaPropertiesLayouts;
12046     
12047     return UserMediaPropertiesLayouts;
12048 });
12049 
12050 /**
12051  * JavaScript representation of the Finesse Dialog object for non-voice media.
12052  *
12053  * @requires finesse.restservices.DialogBase
12054  */
12055 
12056 /** @private */
12057 define('restservices/MediaDialog',[
12058         'restservices/DialogBase'
12059     ],
12060     function (DialogBase) {
12061         var MediaDialog = DialogBase.extend(/** @lends finesse.restservices.MediaDialog.prototype */{
12062 
12063             /**
12064              * @private
12065              *
12066              * Support requests so that applications can refresh non-voice dialogs when the media channel that the
12067              * dialog belongs to is interrupted. An event is not sent to update a dialog's actions when the media is
12068              * interrupted so a refresh is required so that the application can get an updated set of actions.
12069              */
12070             supportsRequests: true,
12071 
12072             /**
12073              * @class
12074              * A MediaDialog is an attempted connection between or among multiple participants,
12075              * for example, a chat or email.
12076              *
12077              * @augments finesse.restservices.DialogBase
12078              * @constructs
12079              */
12080             _fakeConstuctor: function () {
12081                 /* This is here to hide the real init constructor from the public docs */
12082             },
12083 
12084             /**
12085              * @private
12086              *
12087              * @param {Object} options
12088              *     An object with the following properties:<ul>
12089              *         <li><b>id:</b> The id of the object being constructed</li>
12090              *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
12091              *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
12092              *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
12093              *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
12094              *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
12095              *             <li><b>status:</b> {Number} The HTTP status code returned</li>
12096              *             <li><b>content:</b> {String} Raw string of response</li>
12097              *             <li><b>object:</b> {Object} Parsed object of response</li>
12098              *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
12099              *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
12100              *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
12101              *             </ul></li>
12102              *         </ul></li>
12103              *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
12104              **/
12105             init: function (options) {
12106                 this._super(options);
12107             },
12108 
12109             /**
12110              * @private
12111              * Gets the REST class for the current object - this is the MediaDialog class.
12112              * @returns {Object} The Dialog class.
12113              */
12114             getRestClass: function () {
12115                 return MediaDialog;
12116             },
12117 
12118             /**
12119              * Transfers a Media Dialog to the target specified
12120              * @param {String} target script selector
12121              *     The script selector to transfer the dialog.
12122              * @param {finesse.interfaces.RequestHandlers} handlers
12123              *     An object containing the handlers for the request
12124              */
12125             transfer: function(target, handlers) {
12126                 this.setTaskState(MediaDialog.TaskActions.TRANSFER, handlers, target);
12127             },
12128 
12129             /**
12130              * Set the state on a Media Dialog based on the action given.
12131              * @param {finesse.restservices.MediaDialog.TaskActions} action
12132              *     The action string indicating the action to invoke on a Media dialog.
12133              * @param {finesse.interfaces.RequestHandlers} handlers
12134              *     An object containing the handlers for the request
12135              * @param {String} target
12136              *    The target to transfer the dialog.  Pass null if not transfer
12137              */
12138             setTaskState: function (state,handlers,target) {
12139                 this.isLoaded();
12140 
12141                 var contentBody = {};
12142                 contentBody[this.getRestType()] = {
12143                     "requestedAction": state,
12144                     "target": target
12145                 };
12146                 // (nonexistent) keys to be read as undefined
12147                 handlers = handlers || {};
12148                 this.restRequest(this.getRestUrl(), {
12149                     method: 'PUT',
12150                     success: handlers.success,
12151                     error: handlers.error,
12152                     content: contentBody
12153                 });
12154                 return this; // Allow cascading
12155             }
12156 
12157         });
12158 
12159         MediaDialog.TaskActions = /** @lends finesse.restservices.MediaDialog.TaskActions.prototype */ {
12160             /**
12161              * Accept an incoming task.
12162              */
12163             ACCEPT: "ACCEPT",
12164             /**
12165              * Start work on a task.
12166              */
12167             START : "START",
12168             /**
12169              * Pause work on an active task.
12170              */
12171             PAUSE: "PAUSE",
12172             /**
12173              * Resume work on a paused task.
12174              */
12175             RESUME : "RESUME",
12176             /**
12177              * Wrap up work for a task.
12178              */
12179             WRAP_UP : "WRAP_UP",
12180             /**
12181              * Transfer task to another target.
12182              */
12183             TRANSFER : "TRANSFER",
12184             /**
12185              * End a task.
12186              */
12187             CLOSE : "CLOSE",
12188             /**
12189              * @class Set of action constants for a Media Dialog.  These should be used for
12190              * {@link finesse.restservices.MediaDialog#setTaskState}.
12191              * @constructs
12192              */
12193             _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
12194         };
12195 
12196 
12197 
12198         MediaDialog.States = /** @lends finesse.restservices.MediaDialog.States.prototype */ {
12199             /**
12200              * Indicates that the task has been offered to an agent.
12201              */
12202             OFFERED: "OFFERED",
12203             /**
12204              * Indicates that the user has started work on the task.
12205              */
12206             ACTIVE: "ACTIVE",
12207             /**
12208              * Indicates that the user has paused work on the task.
12209              */
12210             PAUSED: "PAUSED",
12211             /**
12212              * Indicates that the user is wrapping up the task.
12213              */
12214             WRAPPING_UP: "WRAPPING_UP",
12215             /**
12216              * Indicates that the task was interrupted.
12217              */
12218             INTERRUPTED: "INTERRUPTED",
12219             /**
12220              * Indicates that the task has ended.
12221              */
12222             CLOSED: "CLOSED",
12223             /**
12224              * Indicates that the user has accepted the task.
12225              */
12226             ACCEPTED: "ACCEPTED",
12227             /**
12228              * Finesse has recovered a task after a failure. It does not have enough information to build a complete set
12229              * of actions for the task so it only allows the user to end the task.
12230              */
12231             UNKNOWN: "UNKNOWN",
12232             /**
12233              * @class Possible Dialog State constants.
12234              * The State flow of a typical in-bound Dialog is as follows: OFFERED, ACCEPTED, ACTIVE, CLOSED.
12235              * @constructs
12236              */
12237             _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
12238         };
12239 
12240         MediaDialog.ParticipantStates = MediaDialog.States;
12241 
12242         window.finesse = window.finesse || {};
12243         window.finesse.restservices = window.finesse.restservices || {};
12244         window.finesse.restservices.MediaDialog = MediaDialog;
12245 
12246 
12247         return MediaDialog;
12248     });
12249 
12250 /* Simple JavaScript Inheritance
12251  * By John Resig http://ejohn.org/
12252  * MIT Licensed.
12253  */
12254 // Inspired by base2 and Prototype
12255 define('restservices/../../thirdparty/Class',[], function () {
12256         var initializing = false, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;
12257         // The base Class implementation (does nothing)
12258         /** @private */
12259         Class = function(){};
12260         
12261         // Create a new Class that inherits from this class
12262         /** @private */
12263         Class.extend = function(prop) {
12264           var _super = this.prototype;
12265           
12266           // Instantiate a base class (but only create the instance,
12267           // don't run the init constructor)
12268           initializing = true;
12269           var prototype = new this();
12270           initializing = false;
12271           
12272           // Copy the properties over onto the new prototype
12273           for (var name in prop) {
12274             // Check if we're overwriting an existing function
12275             prototype[name] = typeof prop[name] == "function" && 
12276               typeof _super[name] == "function" && fnTest.test(prop[name]) ?
12277               (function(name, fn){
12278                 return function() {
12279                   var tmp = this._super;
12280                   
12281                   // Add a new ._super() method that is the same method
12282                   // but on the super-class
12283                   this._super = _super[name];
12284                   
12285                   // The method only need to be bound temporarily, so we
12286                   // remove it when we're done executing
12287                   var ret = fn.apply(this, arguments);        
12288                   this._super = tmp;
12289                   
12290                   return ret;
12291                 };
12292               })(name, prop[name]) :
12293               prop[name];
12294           }
12295           
12296           // The dummy class constructor
12297           /** @private */
12298           function Class() {
12299             // All construction is actually done in the init method
12300             if ( !initializing && this.init )
12301               this.init.apply(this, arguments);
12302           }
12303           
12304           // Populate our constructed prototype object
12305           Class.prototype = prototype;
12306           
12307           // Enforce the constructor to be what we expect
12308           Class.prototype.constructor = Class;
12309 
12310           // And make this class extendable
12311           Class.extend = arguments.callee;
12312           
12313           return Class;
12314         };
12315     return Class;
12316 });
12317 
12318 /**
12319  * Class used to establish a Media/Dialogs subscription to be shared by MediaDialogs objects for non-voice
12320  * dialog events.
12321  *
12322  * @requires Class
12323  * @requires finesse.clientservices.ClientServices
12324  * @requires finesse.clientservices.Topics
12325  */
12326 /** @private */
12327 define('restservices/MediaDialogsSubscriptionManager',[
12328         "../../thirdparty/Class",
12329         "clientservices/ClientServices",
12330         "clientservices/Topics"
12331     ],
12332     function (Class, ClientServices, Topics) {
12333         var MediaDialogsSubscriptionManager = Class.extend(/** @lends finesse.restservices.MediaDialogsSubscriptionManager.prototype */{
12334 
12335             /**
12336              * Map used to track the MediaDialogs objects managed by this object.
12337              * @private
12338              */
12339             _mediaDialogsMap: {},
12340 
12341             /**
12342              * The regex used to match the source of BOSH/XMPP events. If an event matches this source, the event will
12343              * be processed by the subscription manager.
12344              * @private
12345              */
12346             _sourceRegEx: null,
12347 
12348             /**
12349              * The subscription ID/handle for Media/Dialogs events.
12350              * @private
12351              */
12352             _subscriptionId: null,
12353 
12354             _fakeConstuctor: function ()
12355             {
12356                 /* This is here to hide the real init constructor from the public docs */
12357             },
12358 
12359             /**
12360              * Create the regex used to match the source of BOSH/XMPP events. If an event matches this source, the event
12361              * will be processed by the subscription manager.
12362              *
12363              * @param {Object} restObj
12364              *     The restObj whose REST URL will be used as the base of the regex.
12365              *
12366              * @returns {RegExp}
12367              *     The regex used to match the source of XMPP events.
12368              * @private
12369              */
12370             _makeSourceRegEx: function(restObj)
12371             {
12372                 return new RegExp("^" + restObj.getRestUrl() + "/Media/[0-9]+/Dialogs$");
12373             },
12374 
12375             /**
12376              * Return the media ID associated with the update.
12377              *
12378              * @param {Object} update
12379              *     The content of the update event.
12380              *
12381              * @returns {String}
12382              *     The media ID associated with the update.
12383              * @private
12384              */
12385             _getMediaIdFromEventUpdate: function(update)
12386             {
12387                 var parts = update.object.Update.source.split("/");
12388                 return parts[MediaDialogsSubscriptionManager.MEDIA_ID_INDEX_IN_SOURCE];
12389             },
12390 
12391             /**
12392              * Handler for update events. This handler forwards the update to the MediaDialogs object associated with
12393              * the media ID carried in the update event.
12394              *
12395              * @param {Object} update
12396              *     The content of the update event.
12397              *
12398              * @private
12399              */
12400             _updateEventHandler: function(update)
12401             {
12402                 var mediaId = this._getMediaIdFromEventUpdate(update),
12403                     mediaDialogs = this._mediaDialogsMap[mediaId];
12404 
12405                 if ( mediaDialogs )
12406                 {
12407                     mediaDialogs._updateEventHandler(mediaDialogs, update);
12408                 }
12409             },
12410 
12411             /**
12412              * Return the media ID associated with the REST update.
12413              *
12414              * @param {Object} update
12415              *     The content of the REST update.
12416              *
12417              * @returns {String}
12418              *     The media ID associated with the update.
12419              * @private
12420              */
12421             _getMediaIdFromRestUpdate: function(update)
12422             {
12423                 return update.object.Update.data.dialog.mediaProperties.mediaId;
12424             },
12425 
12426             /**
12427              * Handler for REST updates. This handler forwards the update to the MediaDialogs object associated with
12428              * the media ID carried in the REST update.
12429              *
12430              * @param {Object} update
12431              *     The content of the REST update.
12432              *
12433              * @private
12434              */
12435             _processRestItemUpdate: function(update)
12436             {
12437                 var mediaId = this._getMediaIdFromRestUpdate(update),
12438                     mediaDialogs = this._mediaDialogsMap[mediaId];
12439 
12440                 if ( mediaDialogs )
12441                 {
12442                     mediaDialogs._processRestItemUpdate(update);
12443                 }
12444             },
12445 
12446             /**
12447              * Utility method to create a callback to be given to OpenAjax to invoke when a message
12448              * is published on the topic of our REST URL (also XEP-0060 node).
12449              * This needs to be its own defined method so that subclasses can have their own implementation.
12450              * @returns {Function} callback(update)
12451              *     The callback to be invoked when an update event is received. This callback will
12452              *     process the update by notifying the MediaDialogs object associated with the media ID in the update.
12453              *
12454              * @private
12455              */
12456             _createPubsubCallback: function ()
12457             {
12458                 var _this = this;
12459                 return function (update) {
12460                     //If the source of the update is our REST URL, this means the collection itself is modified
12461                     if (update.object.Update.source.match(_this._sourceRegEx)) {
12462                         _this._updateEventHandler(update);
12463                     } else {
12464                         //Otherwise, it is safe to assume that if we got an event on our topic, it must be a
12465                         //rest item update of one of our children that was published on our node (OpenAjax topic)
12466                         _this._processRestItemUpdate(update);
12467                     }
12468                 };
12469             },
12470 
12471             /**
12472              * Track the MediaDialogs object so that events and REST updates signalled to this subscription manager
12473              * can be forwarded to the given MediaDialogs object.
12474              * @param {finesse.restservices.MediaDialogs} mediaDialogs MediaDialogs object to be tracked by the
12475              *     subscription manager.
12476              * @private
12477              */
12478             _manage: function(mediaDialogs)
12479             {
12480                 this._mediaDialogsMap[mediaDialogs.getMedia().getMediaId()] = mediaDialogs;
12481             },
12482 
12483             /**
12484              * Stop tracking the MediaDialogs object. Events and REST updates signalled to this subscription manager
12485              * will no longer be forwarded to the given MediaDialogs object.
12486              * @param {finesse.restservices.MediaDialogs} mediaDialogs MediaDialogs object to no longer track.
12487              * @private
12488              */
12489             _unManage: function(mediaDialogs)
12490             {
12491                 var mediaId = mediaDialogs.getMedia().getMediaId();
12492                 if ( this._callbackMap[mediaId] )
12493                 {
12494                     delete this._callbackMap[mediaId];
12495                 }
12496             },
12497 
12498             /**
12499              * @class
12500              * An internal class used to establish a Media/Dialogs subscription to be shared by MediaDialogs objects
12501              * for non-voice dialog events.
12502              *
12503              * @constructor
12504              * @param {RestBase} restObj
12505              *     A RestBase object used to build the user portion of XMPP and REST paths.
12506              * @constructs
12507              */
12508             init: function (restObj)
12509             {
12510                 var _this;
12511 
12512                 this._sourceRegEx = this._makeSourceRegEx(restObj);
12513             },
12514 
12515             /**
12516              * Create the BOSH/XMPP subscription used for non-voice dialog events. Additionally, store the given
12517              * MediaDialogs object so that events for the object can be forwarded to it.
12518              *
12519              * @param {finesse.restservices.MediaDialogs} mediaDialogs a MediaDialogs object to manage (forward events)
12520              * @param {Object} callbacks an object containing success and error callbacks used to signal the result of
12521              *     the subscription.
12522              * @returns {MediaDialogsSubscriptionManager}
12523              * @private
12524              */
12525             subscribe: function (mediaDialogs, callbacks)
12526             {
12527                 var topic = Topics.getTopic(mediaDialogs.getXMPPNodePath()),
12528                     _this = this,
12529                     handlers,
12530                     successful;
12531 
12532                 callbacks = callbacks || {};
12533 
12534                 handlers = {
12535                     /** @private */
12536                     success: function () {
12537                         // Add item to the refresh list in ClientServices to refresh if
12538                         // we recover due to our resilient connection.
12539                         ClientServices.addToRefreshList(_this);
12540                         if (typeof callbacks.success === "function") {
12541                             callbacks.success();
12542                         }
12543                     },
12544                     /** @private */
12545                     error: function (err) {
12546                         if (successful) {
12547                             _this._unManage(mediaDialogs);
12548                             ClientServices.unsubscribe(topic);
12549                         }
12550 
12551                         if (typeof callbacks.error === "function") {
12552                             callbacks.error(err);
12553                         }
12554                     }
12555                 };
12556 
12557                 this._manage(mediaDialogs);
12558                 if ( this._subscriptionId )
12559                 {
12560                     successful = true;
12561                 }
12562                 else
12563                 {
12564                     successful = ClientServices.subscribe(topic, this._createPubsubCallback(), true);
12565                     if ( successful )
12566                     {
12567                         this._subscriptionId = "OpenAjaxOnly";
12568                     }
12569                 }
12570 
12571                 if (successful) {
12572                     handlers.success();
12573                 } else {
12574                     handlers.error();
12575                 }
12576 
12577                 return this;
12578             }
12579         });
12580 
12581         MediaDialogsSubscriptionManager.MEDIA_ID_INDEX_IN_SOURCE = 6;
12582 
12583         window.finesse = window.finesse || {};
12584         window.finesse.restservices = window.finesse.restservices || {};
12585         window.finesse.restservices.MediaDialogsSubscriptionManager = MediaDialogsSubscriptionManager;
12586 
12587         return MediaDialogsSubscriptionManager;
12588     });
12589 
12590 /**
12591  * JavaScript representation of the Finesse MediaDialogs collection
12592  * object which contains a list of Dialog objects.
12593  *
12594  * @requires finesse.clientservices.ClientServices
12595  * @requires Class
12596  * @requires finesse.FinesseBase
12597  * @requires finesse.restservices.RestBase
12598  * @requires finesse.restservices.Dialogs
12599  * @requires finesse.restservices.MediaDialogsSubscriptionManager
12600  */
12601 /** @private */
12602 define('restservices/MediaDialogs',[
12603     'restservices/RestCollectionBase',
12604     'restservices/RestBase',
12605     'restservices/Dialogs',
12606     'restservices/MediaDialog',
12607     'restservices/MediaDialogsSubscriptionManager'
12608 ],
12609 function (RestCollectionBase, RestBase, Dialogs, MediaDialog, MediaDialogsSubscriptionManager) {
12610     var MediaDialogs = Dialogs.extend(/** @lends finesse.restservices.MediaDialogs.prototype */{
12611 
12612         /**
12613          * @class
12614          * JavaScript representation of a collection of Dialogs for a specific non-voice Media.
12615          * @augments finesse.restservices.Dialogs
12616          * @constructs
12617          * @see finesse.restservices.Dialog
12618          * @example
12619          *  _MediaDialogs = _media.getMediaDialogs( {
12620          *      onCollectionAdd : _handleDialogAdd,
12621          *      onCollectionDelete : _handleDialogDelete,
12622          *      onLoad : _handleMediaDialogsLoaded
12623          *  });
12624          *  
12625          * _dialogCollection = _MediaDialogs.getCollection();
12626          * for (var dialogId in _dialogCollection) {
12627          *     if (_dialogCollection.hasOwnProperty(dialogId)) {
12628          *         _dialog = _dialogCollection[dialogId];
12629          *         etc...
12630          *     }
12631          * }
12632          */
12633         _fakeConstuctor: function () {
12634             /* This is here to hide the real init constructor from the public docs */
12635         },
12636         
12637         /**
12638          * @private
12639          * @param {Object} options
12640          *     An object with the following properties:<ul>
12641          *         <li><b>id:</b> The id of the object being constructed</li>
12642          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
12643          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
12644          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
12645          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
12646          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
12647          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
12648          *             <li><b>content:</b> {String} Raw string of response</li>
12649          *             <li><b>object:</b> {Object} Parsed object of response</li>
12650          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
12651          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
12652          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
12653          *             </ul></li>
12654          *         </ul></li>
12655          *         <li><b>parentObj:</b> The parent object</li></ul>
12656          *         <li><b>mediaObj:</b> The media object</li></ul>
12657          **/
12658         init: function (options) {
12659             this._mediaObj = options.mediaObj;
12660             this._super(options);
12661         },
12662 
12663         getMedia: function() {
12664             return this._mediaObj;
12665         },
12666 
12667         /**
12668          * @private
12669          * Gets the REST class for the objects that make up the collection. - this
12670          * is the Dialog class.
12671          */
12672         getRestItemClass: function () {
12673             return MediaDialog;
12674         },
12675 
12676         /**
12677          * @private
12678          * Gets the node path for the current object - this is the media node
12679          * @returns {String} The node path
12680          */
12681         getXMPPNodePath: function () {
12682             var
12683                 restObj = this._restObj,
12684                 nodePath = "";
12685 
12686             //Prepend the base REST object if one was provided.
12687             if (restObj instanceof RestBase) {
12688                 nodePath += restObj.getRestUrl();
12689             }
12690             //Otherwise prepend with the default webapp name.
12691             else {
12692                 nodePath += "/finesse/api";
12693             }
12694             
12695             //Append the REST type.
12696             nodePath += "/" + this.getRestType() + "/Media";
12697             return nodePath;
12698         },
12699         
12700         /**
12701          * The REST URL in which this object can be referenced.
12702          * @return {String}
12703          *     The REST URI for this object.
12704          * @private
12705          */
12706         getRestUrl: function () {
12707             var
12708             restObj = this._mediaObj,
12709             restUrl = "";
12710 
12711             //Prepend the base REST object if one was provided.
12712             if (restObj instanceof RestBase) {
12713                 restUrl += restObj.getRestUrl();
12714             }
12715             //Otherwise prepend with the default webapp name.
12716             else {
12717                 restUrl += "/finesse/api";
12718             }
12719 
12720             //Append the REST type.
12721             restUrl += "/" + this.getRestType();
12722 
12723             //Append ID if it is not undefined, null, or empty.
12724             if (this._id) {
12725                 restUrl += "/" + this._id;
12726             }
12727             return restUrl;
12728         },
12729 
12730         /**
12731          * Overridden so that MediaDialogsSubscriptionManager can be used to share events across media dialogs.
12732          *
12733          * @param {Object} callbacks
12734          *     An object containing success and error handlers for the subscription request.
12735          * @private
12736          */
12737         subscribe: function (callbacks)
12738         {
12739             if ( !MediaDialogs.subscriptionManager )
12740             {
12741                 MediaDialogs.subscriptionManager = new MediaDialogsSubscriptionManager(this._restObj);
12742             }
12743 
12744             MediaDialogs.subscriptionManager.subscribe(this, callbacks);
12745 
12746             return this;
12747         }
12748     });
12749 
12750     MediaDialogs.subscriptionManager = /** @lends finesse.restservices.MediaDialogsSubscriptionManager.prototype */ null;
12751 
12752     window.finesse = window.finesse || {};
12753     window.finesse.restservices = window.finesse.restservices || {};
12754     window.finesse.restservices.MediaDialogs = MediaDialogs;
12755     
12756     return MediaDialogs;
12757 });
12758 
12759 /**
12760  * Allows gadgets to call the log function to publish client logging messages over the hub.
12761  *
12762  * @requires OpenAjax
12763  */
12764 /** @private */
12765 define('cslogger/ClientLogger',[], function () {
12766 
12767     var ClientLogger = ( function () { /** @lends finesse.cslogger.ClientLogger.prototype */
12768         var _hub, _logTopic, _originId, _sessId, _host,
12769             MONTH = { 0 : "Jan", 1 : "Feb", 2 : "Mar", 3 : "Apr", 4 : "May", 5 : "Jun", 
12770                       6 : "Jul", 7 : "Aug", 8 : "Sep", 9 : "Oct", 10 : "Nov", 11 : "Dec"},
12771  
12772         /**
12773          * Gets timestamp drift stored in sessionStorage
12774          * @returns drift in seconds if it is set in sessionStorage otherwise returns undefined.
12775          * @private
12776         */
12777         getTsDrift = function() {
12778             if (window.sessionStorage.getItem('clientTimestampDrift') !== null) {
12779                 return parseInt(window.sessionStorage.getItem('clientTimestampDrift'), 10);
12780             }
12781             else { 
12782                 return undefined;
12783             }
12784         },
12785          
12786         /**
12787           * Sets timestamp drift in sessionStorage
12788           * @param delta is the timestamp drift between server.and client.
12789           * @private
12790          */
12791         setTsDrift = function(delta) {
12792              window.sessionStorage.setItem('clientTimestampDrift', delta.toString());
12793         },
12794           
12795         /**
12796          * Gets Finesse server timezone offset from GMT in seconds 
12797          * @returns offset in seconds if it is set in sessionStorage otherwise returns undefined.
12798          * @private
12799         */
12800         getServerOffset = function() {
12801             if (window.sessionStorage.getItem('serverTimezoneOffset') !== null) {
12802                 return parseInt(window.sessionStorage.getItem('serverTimezoneOffset'), 10);
12803             }
12804             else { 
12805                 return undefined;
12806             }
12807         },
12808          
12809         /**
12810           * Sets server timezone offset 
12811           * @param sec is the server timezone GMT offset in seconds.
12812           * @private
12813          */
12814         setServerOffset = function(sec) {
12815              window.sessionStorage.setItem('serverTimezoneOffset', sec.toString());
12816         },
12817  
12818         /**
12819          * Checks to see if we have a console.
12820          * @returns Whether the console object exists.
12821          * @private
12822          */
12823         hasConsole = function () {
12824             try {
12825                 if (window.console !== undefined) {
12826                     return true;
12827                 }
12828             } 
12829             catch (err) {
12830               // ignore and return false
12831             }
12832     
12833             return false;
12834         },
12835         
12836         /**
12837          * Gets a short form (6 character) session ID from sessionStorage
12838          * @private
12839         */
12840         getSessId = function() {
12841             if (!_sessId) {
12842                //when _sessId not defined yet, initiate it
12843                if (window.sessionStorage.getItem('enableLocalLog') === 'true') {
12844                   _sessId= " "+window.sessionStorage.getItem('finSessKey');
12845                }
12846                else {
12847                   _sessId=" ";
12848                }
12849             }
12850             return _sessId;
12851          },
12852 
12853         /**
12854          * Pads a single digit number for display purposes (e.g. '4' shows as '04')
12855          * @param num is the number to pad to 2 digits
12856          * @returns a two digit padded string
12857          * @private
12858          */
12859         padTwoDigits = function (num)
12860         {
12861             return (num < 10) ? '0' + num : num;
12862         },
12863         
12864         /**
12865          * Pads a single digit number for display purposes (e.g. '4' shows as '004')
12866          * @param num is the number to pad to 3 digits
12867          * @returns a three digit padded string
12868          * @private
12869          */
12870         padThreeDigits = function (num)
12871         {
12872             if (num < 10)
12873             {
12874               return '00'+num;
12875             }
12876             else if (num < 100)
12877             {
12878               return '0'+num;
12879             }
12880             else  
12881             {
12882                return num;
12883             }
12884         },
12885               
12886         /**
12887          * Compute the "hour"
12888          * 
12889          * @param s is time in seconds
12890          * @returns {String} which is the hour
12891          * @private
12892          */
12893         ho = function (s) {
12894              return ((s/60).toString()).split(".")[0];
12895         },
12896           
12897         /**
12898          * Gets local timezone offset string.
12899          * 
12900          * @param t is the time in seconds
12901          * @param s is the separator character between hours and minutes, e.g. ':'
12902          * @returns {String} is local timezone GMT offset in the following format: [+|-]hh[|:]MM
12903          * @private
12904          */
12905         getGmtOffString = function (min,s) {
12906             var t, sign;
12907             if (min<0) {
12908                t = -min;
12909                sign = "-";
12910             }
12911             else {
12912                t = min;
12913                sign = "+";
12914             }
12915             
12916             if (s===':') {
12917                 return sign+padTwoDigits(ho(t))+s+padTwoDigits(t%60);
12918             }
12919             else {
12920                 return sign+padTwoDigits(ho(t))+padTwoDigits(t%60);
12921             }    
12922         },
12923 
12924         /**
12925          * Gets short form of a month name in English 
12926          * 
12927          * @param monthNum is zero-based month number 
12928          * @returns {String} is short form of month name in English
12929          * @private
12930          */
12931         getMonthShortStr = function (monthNum) {
12932             var result;
12933             try {
12934                 result = MONTH[monthNum];
12935             } 
12936             catch (err) {
12937                 if (hasConsole()) {
12938                     window.console.log("Month must be between 0 and 11");
12939                 }
12940             }
12941             return result;
12942         },
12943           
12944         /**
12945           * Gets a timestamp.
12946           * @param aDate is a javascript Date object
12947           * @returns {String} is a timestamp in the following format: yyyy-mm-ddTHH:MM:ss.SSS [+|-]HH:MM
12948           * @private
12949           */
12950         getDateTimeStamp = function (aDate)
12951         {
12952             var date, off, timeStr;
12953             if (aDate === null) {
12954                 date = new Date();
12955             }
12956             else {
12957                 date = aDate;
12958             }
12959             off = -1*date.getTimezoneOffset();
12960             timeStr = date.getFullYear().toString() + "-" +
12961                       padTwoDigits(date.getMonth()+1) + "-" +
12962                       padTwoDigits (date.getDate()) + "T"+
12963                       padTwoDigits(date.getHours()) + ":" + 
12964                       padTwoDigits(date.getMinutes()) + ":" +
12965                       padTwoDigits(date.getSeconds())+"." + 
12966                       padThreeDigits(date.getMilliseconds()) + " "+
12967                       getGmtOffString(off, ':');
12968     
12969             return timeStr;
12970         },
12971         
12972         /**
12973          * Gets drift-adjusted timestamp.
12974          * @param aTimestamp is a timestamp in milliseconds
12975          * @param drift is a timestamp drift in milliseconds
12976          * @param serverOffset is a timezone GMT offset in minutes
12977          * @returns {String} is a timestamp in the Finesse server log format, e.g. Jan 07 2104 HH:MM:ss.SSS -0500
12978          * @private
12979          */
12980         getDriftedDateTimeStamp = function (aTimestamp, drift, serverOffset)
12981         {
12982            var date, timeStr, localOffset;
12983            if (aTimestamp === null) {
12984                return "--- -- ---- --:--:--.--- -----";
12985            }
12986            else if (drift === undefined || serverOffset === undefined) {
12987                if (hasConsole()) {
12988                    window.console.log("drift or serverOffset must be a number");
12989                }
12990                return "--- -- ---- --:--:--.--- -----";
12991            }
12992            else {
12993                //need to get a zone diff in minutes
12994                localOffset = (new Date()).getTimezoneOffset();
12995                date = new Date(aTimestamp+drift+(localOffset+serverOffset)*60000);
12996                timeStr = getMonthShortStr(date.getMonth()) + " "+
12997                          padTwoDigits (date.getDate())+ " "+
12998                          date.getFullYear().toString() + " "+
12999                          padTwoDigits(date.getHours()) + ":" + 
13000                          padTwoDigits(date.getMinutes()) + ":" +
13001                          padTwoDigits(date.getSeconds())+"." + 
13002                          padThreeDigits(date.getMilliseconds())+" "+
13003                          getGmtOffString(serverOffset, '');
13004                 return timeStr;
13005             }
13006         },
13007     
13008         /**
13009         * Logs a message to a hidden textarea element on the page
13010         *
13011         * @param msg is the string to log.
13012         * @private
13013         */
13014         writeToLogOutput = function (msg) {
13015             var logOutput = document.getElementById("finesseLogOutput");
13016     
13017             if (logOutput === null)
13018             {
13019                 logOutput = document.createElement("textarea");
13020                 logOutput.id = "finesseLogOutput";
13021                 logOutput.style.display = "none";
13022                 document.body.appendChild(logOutput);
13023             }
13024     
13025             if (logOutput.value === "")
13026             {
13027                 logOutput.value = msg;
13028             }
13029             else
13030             {
13031                 logOutput.value = logOutput.value + "\n" + msg;
13032             }
13033         },
13034 
13035         /*
13036          * Logs a message to console 
13037         * @param str is the string to log.         * @private
13038          */
13039         logToConsole = function (str)
13040         {
13041             var now, msg, timeStr, driftedTimeStr, sessKey=getSessId();
13042             now = new Date();
13043             timeStr = getDateTimeStamp(now);
13044             if (getTsDrift() !== undefined) {
13045                 driftedTimeStr = getDriftedDateTimeStamp(now.getTime(), getTsDrift(), getServerOffset());
13046             }
13047             else {
13048                driftedTimeStr = getDriftedDateTimeStamp(null, 0, 0);
13049             }
13050             msg = timeStr + ":"+sessKey+": "+ _host + ": "+driftedTimeStr+ ": " + str;
13051             // Log to console
13052             if (hasConsole()) {
13053                 window.console.log(msg);
13054             }
13055     
13056             //Uncomment to print logs to hidden textarea.
13057             //writeToLogOutput(msg);
13058     
13059             return msg;
13060         };
13061         return {
13062     
13063             /**
13064              * Publishes a Log Message over the hub.
13065              *
13066              * @param {String} message
13067              *     The string to log.
13068              * @example
13069              * _clientLogger.log("This is some important message for MyGadget");
13070              * 
13071              */
13072             log : function (message) {
13073                 if(_hub) {
13074                     _hub.publish(_logTopic, logToConsole(_originId + message));
13075                 }
13076             },
13077             
13078             /**
13079              * @class
13080              * Allows gadgets to call the log function to publish client logging messages over the hub.
13081              * 
13082              * @constructs
13083              */
13084             _fakeConstuctor: function () {
13085                 /* This is here so we can document init() as a method rather than as a constructor. */
13086             },
13087             
13088             /**
13089              * Initiates the client logger with a hub a gadgetId and gadget's config object.
13090              * @param {Object} hub
13091              *      The hub to communicate with.
13092              * @param {String} gadgetId
13093              *      A unique string to identify which gadget is doing the logging.
13094              * @param {finesse.gadget.Config} config
13095              *      The config object used to get host name for that thirdparty gadget
13096              * @example
13097              * var _clientLogger = finesse.cslogger.ClientLogger;
13098              * _clientLogger.init(gadgets.Hub, "MyGadgetId", config);
13099              * 
13100              */
13101             init: function (hub, gadgetId, config) {
13102                 _hub = hub;
13103                 _logTopic = "finesse.clientLogging." + gadgetId;
13104                 _originId = gadgetId + " : ";
13105                 if ((config === undefined) || (config === "undefined")) 
13106                 {
13107                      _host = ((finesse.container && finesse.container.Config && finesse.container.Config.host)?finesse.container.Config.host : "?.?.?.?");
13108                  } 
13109                 else 
13110                 {
13111                      _host = ((config && config.host)?config.host : "?.?.?.?");
13112                  }
13113             }
13114         };
13115     }());
13116     
13117     window.finesse = window.finesse || {};
13118     window.finesse.cslogger = window.finesse.cslogger || {};
13119     window.finesse.cslogger.ClientLogger = ClientLogger;
13120     
13121     finesse = finesse || {};
13122     /** @namespace Supports writing messages to a central log. */
13123     finesse.cslogger = finesse.cslogger || {};
13124 
13125     return ClientLogger;
13126 });
13127 
13128 /**
13129  * Utility class used to recover a media object after recovering from a connection or system failure.
13130  *
13131  * @requires Class
13132  * @requires finesse.restservices.Notifier
13133  * @requires finesse.clientservices.ClientServices
13134  * @requires finesse.restservices.Media
13135  */
13136 
13137 /** @private */
13138 define('restservices/MediaOptionsHelper',['../../thirdparty/Class',
13139         '../utilities/Utilities',
13140         '../clientservices/ClientServices',
13141         '../cslogger/ClientLogger'
13142 ],
13143 function (Class, Utilities, ClientServices, ClientLogger)
13144 {
13145     /**
13146      * Utility class used to synchronize media login options after recovering from a connection or system failure. This
13147      * class will ensure that the Finesse server that the application fails over to has the same maxDialogLimit,
13148      * interruptAction, and dialogLogoutAction as the previous Finesse server.
13149      */
13150     var MediaOptionsHelper = Class.extend(/** @lends finesse.restservices.MediaOptionsHelper.prototype */
13151     {
13152         /**
13153          * @private
13154          *
13155          * The media that this helper is responsible for recovering in case of failover.
13156          */
13157         _media: null,
13158 
13159         /**
13160          * @private
13161          *
13162          * The media options that this helper will ensure are set properly across failures.
13163          */
13164         _mediaOptions: null,
13165 
13166         /**
13167          * @private
13168          *
13169          * The current state of the failover recovery.
13170          */
13171         _state: null,
13172 
13173         /**
13174          * @class
13175          *
13176          * Utility class used to synchronize media login options after recovering from a connection or system failure. This
13177          * class will ensure that the Finesse server that the application fails over to has the same maxDialogLimit,
13178          * interruptAction, and dialogLogoutAction as the previous Finesse server.
13179          *
13180          * @constructs
13181          */
13182         _fakeConstuctor: function ()
13183         {
13184             /* This is here to hide the real init constructor from the public docs */
13185         },
13186 
13187         /**
13188          * Utility method to format a message logged by an instance of this class.
13189          *
13190          * @param {string} message the message to format
13191          * @returns {string} the given message prefixed with the name of this class and the ID of the Media object
13192          *      associated with this class.
13193          * @private
13194          */
13195         _formatLogMessage: function(message)
13196         {
13197             return "MediaOptionsHelper[" + this.media.getMediaId() + "]: " + message;
13198         },
13199 
13200         /**
13201          * Utility method to log an informational message.
13202          *
13203          * Note that this method piggy-backs on the logger setup by the gadget. If the gadget does not initialize
13204          * logger, this class will not log.
13205          *
13206          * @param {string} message the message to log
13207          * @private
13208          */
13209         _log: function(message)
13210         {
13211             ClientLogger.log(this._formatLogMessage(message));
13212         },
13213 
13214         /**
13215          * Utility method to log an error message.
13216          *
13217          * Note that this method piggy-backs on the logger setup by the gadget. If the gadget does not initialize
13218          * logger, this class will not log.
13219          *
13220          * @param {string} message the message to log
13221          * @private
13222          */
13223         _error: function(message)
13224         {
13225             ClientLogger.error(this._formatLogMessage(message));
13226         },
13227 
13228         /**
13229          * @private
13230          *
13231          * Set the running state of this failover helper.
13232          *
13233          * @param {String} newState the new state of the failover helper.
13234          */
13235         _setState: function(newState)
13236         {
13237             this._state = newState;
13238             this._log("changed state to " + this._state);
13239         },
13240 
13241         /**
13242          * Check the given media object to see if the maxDialogLimit, interruptAction, and dialogLogoutAction options
13243          * need to be reset. These options need to be reset if the application specified login options and any of the
13244          * following conditions are true:<ul>
13245          *     <li>the dialogLogoutAction in the given media object does not match the action set by the application</li>
13246          *     <li>the interruptAction in the given media object does not match the action set by the application</li>
13247          *     <li>the maxDialogLimit in the given media object does not match the limit set by the application</li></ul>
13248          *
13249          * @param {Object} media the media object to evaluate
13250          * @returns {*|{}|boolean} true if a login request should be sent to correct the media options
13251          * @private
13252          */
13253         _shouldLoginToFixOptions: function(media)
13254         {
13255             return this._mediaOptions
13256                 && media.isLoggedIn()
13257                 && (media.getDialogLogoutAction() !== this._mediaOptions.dialogLogoutAction
13258                     || media.getInterruptAction() !== this._mediaOptions.interruptAction
13259                     || media.getMaxDialogLimit() !== this._mediaOptions.maxDialogLimit);
13260         },
13261 
13262         /**
13263          * @private
13264          *
13265          * Determine if the given response is an "agent already logged in" error.
13266          *
13267          * @param {Object} response the response to evaluate
13268          *
13269          * @returns {boolean} true if
13270          */
13271         _agentIsAlreadyLoggedIn: function(response)
13272         {
13273             return response
13274                 && response.object
13275                 && response.object.ApiErrors
13276                 && response.object.ApiErrors.ApiError
13277                 && response.object.ApiErrors.ApiError.ErrorMessage === "E_ARM_STAT_AGENT_ALREADY_LOGGED_IN";
13278         },
13279 
13280         /**
13281          * Determine if the given response to a media login request is successful. A response is successful under these
13282          * conditions:<ul>
13283          *     <li>the response has a 202 status</li>
13284          *     <li>the response has a 400 status and the error indicates the agent is already logged in</li>
13285          *     </ul>
13286          *
13287          * @param {Object} loginResponse the response to evaluate
13288          *
13289          * @returns {*|boolean} true if the response status is 202 or if the response status is 400 and the error states
13290          *      that the agent is already logged in.
13291          * @private
13292          */
13293         _isSuccessfulLoginResponse: function(loginResponse)
13294         {
13295             return loginResponse && ((loginResponse.status === 202) || this._agentIsAlreadyLoggedIn(loginResponse));
13296         },
13297 
13298         /**
13299          * Process a media load or change while in the connected state. This involves checking the media options to
13300          * ensure they are the same as those set by the application.
13301          *
13302          * @param {Object} media the media object that was loaded or changed.
13303          * @private
13304          */
13305         _processConnectedState: function(media)
13306         {
13307             var _this = this, processResponse;
13308 
13309             if ( this._shouldLoginToFixOptions(media) )
13310             {
13311                 processResponse = function(response)
13312                 {
13313                     _this._setState(MediaOptionsHelper.States.MONITORING_OPTIONS);
13314 
13315                     if ( !_this._isSuccessfulLoginResponse(response) )
13316                     {
13317                         _this._error("failed to reset options: " + response.status + ": " + response.content);
13318                     }
13319                 };
13320 
13321                 this._setState(MediaOptionsHelper.States.SETTING_OPTIONS);
13322 
13323                 this._log("logging in to fix options");
13324 
13325                 this.media.login({
13326                     dialogLogoutAction: _this._mediaOptions.dialogLogoutAction,
13327                     interruptAction: _this._mediaOptions.interruptAction,
13328                     maxDialogLimit: _this._mediaOptions.maxDialogLimit,
13329                     handlers: {
13330                         success: processResponse,
13331                         error: processResponse
13332                     }
13333                 });
13334             }
13335         },
13336 
13337         /**
13338          * Process a media load or change while in the resetting options state. All that is done in this state is log a
13339          * message that a reset is already in progress.
13340          *
13341          * @param {Object} media the media object that was loaded or changed.
13342          * @private
13343          */
13344         _processResettingOptionsState: function(media)
13345         {
13346             this._log("Resetting options is in progress");
13347         },
13348 
13349         /**
13350          * Initialize a helper class used to recover media objects following connectivity or component failures related
13351          * to Finesse and/or CCE services.
13352          *
13353          * Initialize the failover helper to manage the recovery of the given media object.
13354          *
13355          * @param {Object} media the media object to recover
13356          * @param {Object} mediaOptions an object containing the media options used by the application:<ul>
13357          *         <li><b>maxDialogLimit:</b> The id of the object being constructed</li>
13358          *         <li><b>interruptAction:</b> Accept or ignore interrupts</li>
13359          *         <li><b>dialogLogoutAction:</b> transfer or close the task at logout time</li></ul>
13360          */
13361         init: function (media, mediaOptions)
13362         {
13363             var _this = this, processMediaStateChange = function(media)
13364             {
13365                 switch ( _this._state )
13366                 {
13367                     case MediaOptionsHelper.States.MONITORING_OPTIONS:
13368                         _this._processConnectedState(media);
13369                         break;
13370                     case MediaOptionsHelper.States.SETTING_OPTIONS:
13371                         _this._processResettingOptionsState(media);
13372                         break;
13373                     default:
13374                         _this._error("unexpected state: " + _this.state);
13375                         break;
13376                 }
13377             };
13378 
13379             this.media = media;
13380 
13381             this._mediaOptions = mediaOptions || {};
13382 
13383             // The maxDialogLimit is a string in media events. Ensure _mediaOptions.maxDialogLimit is a string to
13384             // make sure it can be compared to the maxDialogLimit field in media events.
13385             //
13386             if ( this._mediaOptions.maxDialogLimit )
13387             {
13388                 this._mediaOptions.maxDialogLimit = this._mediaOptions.maxDialogLimit.toString();
13389             }
13390 
13391             // Add the media object to the refresh list so that ClientServices calls refresh on the media object when
13392             // the connection is reestablished. This will trigger processMediaStateChange() which will trigger a login
13393             // to restore media options if media options are different.
13394             //
13395             ClientServices.addToRefreshList(this.media);
13396 
13397             this._setState(MediaOptionsHelper.States.MONITORING_OPTIONS);
13398 
13399             this.media.addHandler('load', processMediaStateChange);
13400             this.media.addHandler('change', processMediaStateChange);
13401 
13402             this._log("initialized");
13403         }
13404     });
13405 
13406     /**
13407      * @private
13408      *
13409      * The states that a running MediaOptionsHelper executes.
13410      *
13411      * @type {{CONNECTED: string, RECOVERING: string, RECOVERY_LOGIN: string, _fakeConstructor: _fakeConstructor}}
13412      */
13413     MediaOptionsHelper.States = /** @lends finesse.restservices.MediaOptionsHelper.States.prototype */ {
13414 
13415         /**
13416          * The media is synchronized with the Finesse server. Media options are being monitored for changes.
13417          */
13418         MONITORING_OPTIONS: "MONITORING_OPTIONS",
13419 
13420         /**
13421          * A media login request has been sent to Finesse to set the correct media options.
13422          */
13423         SETTING_OPTIONS: "SETTING_OPTIONS",
13424 
13425         /**
13426          * @class Possible MediaOptionsHelper state values.
13427          * @constructs
13428          */
13429         _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
13430     };
13431 
13432     window.finesse = window.finesse || {};
13433     window.finesse.restservices = window.finesse.restservices || {};
13434     window.finesse.restservices.MediaOptionsHelper = MediaOptionsHelper;
13435 
13436     return MediaOptionsHelper;
13437 });
13438 /**
13439  * JavaScript representation of the Finesse Media object
13440  *
13441  * @requires finesse.clientservices.ClientServices
13442  * @requires Class
13443  * @requires finesse.FinesseBase
13444  * @requires finesse.restservices.RestBase
13445  * @requires finesse.restservices.MediaDialogs
13446  * @requires finesse.restservices.MediaOptionsHelper
13447  */
13448 
13449 /** @private */
13450 define('restservices/Media',[
13451     'restservices/RestBase',
13452     'restservices/MediaDialogs',
13453     'restservices/MediaOptionsHelper'
13454 ],
13455 function (RestBase, MediaDialogs, MediaOptionsHelper) {
13456     var Media = RestBase.extend(/** @lends finesse.restservices.Media.prototype */{
13457 
13458         /**
13459          * Media objects support GET REST requests.
13460          */
13461         supportsRequests: true,
13462 
13463         /**
13464          * @private
13465          * The list of dialogs associated with this media.
13466          */
13467         _mdialogs : null,
13468 
13469         /**
13470          * @private
13471          * The options used to log into this media.
13472          */
13473         _mediaOptions: null,
13474 
13475         /**
13476          * @private
13477          * Object used to keep the maxDialogLimit, interruptAction, and dialogLogoutAction in-synch across fail-overs.
13478          */
13479         _optionsHelper: null,
13480 
13481         /**
13482          * @class
13483          * A Media represents a non-voice channel,
13484          * for example, a chat or a email.
13485          *
13486          * @augments finesse.restservices.RestBase
13487          * @constructs
13488          */
13489         _fakeConstuctor: function () {
13490             /* This is here to hide the real init constructor from the public docs */
13491         },
13492 
13493         /**
13494          * @private
13495          *
13496          * @param {Object} options
13497          *     An object with the following properties:<ul>
13498          *         <li><b>id:</b> The id of the object being constructed</li>
13499          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
13500          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
13501          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
13502          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
13503          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
13504          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
13505          *             <li><b>content:</b> {String} Raw string of response</li>
13506          *             <li><b>object:</b> {Object} Parsed object of response</li>
13507          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
13508          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
13509          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
13510          *             </ul></li>
13511          *         </ul></li>
13512          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
13513          **/
13514         init: function (options) {
13515             this._super(options);
13516         },
13517 
13518         /**
13519          * @private
13520          * Utility method used to retrieve an attribute from this media object's underlying data.
13521          *
13522          * @param {String} attributeName the name of the attribute to retrieve
13523          * @returns {String} the value of the attribute or undefined if the attribute is not found
13524          */
13525         _getDataAttribute: function(attributeName) {
13526             this.isLoaded();
13527             return this.getData()[attributeName];
13528         },
13529 
13530         /**
13531          * @private
13532          * Gets the REST class for the current object - this is the Media class.
13533          * @returns {Object} The Media class.
13534          */
13535         getRestClass: function () {
13536             return Media;
13537         },
13538         
13539         /**
13540          * @private
13541          * Gets the REST type for the current object - this is a "Media".
13542          * @returns {String} The Media string.
13543          */
13544         getRestType: function () {
13545             return "Media";
13546         },
13547 
13548         /**
13549          * @private
13550          * Getter for the uri.
13551          * @returns {String} The uri.
13552          */
13553         getMediaUri: function () {
13554             return this._getDataAttribute('uri');
13555         },
13556 
13557         /**
13558          * Getter for the id.
13559          * @returns {String} The id.
13560          */
13561         getId: function () {
13562             return this._getDataAttribute('id');
13563         },
13564 
13565         /**
13566          * Getter for the name.
13567          * @returns {String} The name.
13568          */
13569         getName: function () {
13570             return this._getDataAttribute('name');
13571         },
13572 
13573         /**
13574          * Getter for the reason code id.
13575          * @returns {String} The reason code id.
13576          */
13577         getReasonCodeId: function () {
13578             return this._getDataAttribute('reasonCodeId');
13579         },
13580 
13581         /**
13582          * Getter for the reason code label.
13583          * @returns {String} The reason code label.
13584          */
13585         getReasonCodeLabel: function() {
13586             this.isLoaded();
13587             if (this.getData().reasonCode) {
13588                 return this.getData.reasonCode.label;
13589             }
13590             return "";
13591         },
13592 
13593         /**
13594          * Getter for the action to be taken in the event this media is interrupted. The action will be one of the
13595          * following:<ul>
13596          *     <li><b>ACCEPT:</b> the interrupt will be accepted and the agent will not work on tasks in this media
13597          *     until the media is no longer interrupted.</li>
13598          *     <li><b>IGNORE:</b> the interrupt will be ignored and the agent is allowed to work on the task while the
13599          *     media is interrupted.</li></ul>
13600          * @returns {*|Object}
13601          */
13602         getInterruptAction: function() {
13603             return this._getDataAttribute('interruptAction');
13604         },
13605 
13606         /**
13607          * Getter for the action to be taken in the event the agent logs out with dialogs associated with this media.
13608          * The action will be one of the following:<ul>
13609          *     <li><b>CLOSE:</b> the dialog will be closed.</li>
13610          *     <li><b>TRANSFER:</b> the dialog will be transferred to another agent.</li></ul>
13611          * @returns {*|Object}
13612          */
13613         getDialogLogoutAction: function() {
13614             return this._getDataAttribute('dialogLogoutAction');
13615         },
13616 
13617         /**
13618          * Getter for the state of the User on this Media.
13619          * @returns {String}
13620          *     The current (or last fetched) state of the User on this Media
13621          * @see finesse.restservices.Media.States
13622          */
13623         getState: function() {
13624             return this._getDataAttribute('state');
13625         },
13626 
13627         /**
13628          * Getter for the Media id
13629          * @returns {String} The Media id
13630          */
13631         getMediaId: function() {
13632             return this._getDataAttribute('id');
13633         },
13634 
13635         /**
13636          * Getter for maximum number of dialogs allowed on this Media
13637          * @returns {String} The max number of Dialogs on this Media
13638          */
13639         getMaxDialogLimit: function() {
13640             return this._getDataAttribute('maxDialogLimit');
13641         },
13642 
13643         /**
13644          * Getter for whether or not this media is interruptible
13645          * @returns {Boolean} true if interruptible; false otherwise
13646          */
13647         getInterruptible: function() {
13648             var interruptible = this._getDataAttribute('interruptible');
13649             return interruptible === 'true';
13650         },
13651 
13652         /**
13653          * Is the user interruptible on this Media.
13654          * @returns {Boolean} true if interruptible; false otherwise
13655          */
13656         isInterruptible: function() {
13657             return this.getInterruptible();
13658         },
13659 
13660         /**
13661          * Getter for routable field on this Media
13662          * @returns {Boolean} true if routable, false otherwise
13663          */
13664         getRoutable: function() {
13665             var routable = this._getDataAttribute('routable');
13666             return routable === 'true';
13667         },
13668 
13669         /**
13670          * Is the user routable on this Media.
13671          * @returns {Boolean} true if routable, false otherwise
13672          */
13673         isRoutable: function() {
13674             return this.getRoutable();
13675         },
13676 
13677         /**
13678          * @param {Object} options
13679          *     An object with the following properties:<ul>
13680          *         <li><b>routable:</b> true if the agent is routable, false otherwise</li>
13681          *         <li><b>{finesse.interfaces.RequestHandlers} handlers:</b> An object containing the handlers for the request</li></ul>
13682          * @returns {finesse.restservices.Media}
13683          *     This Media object, to allow cascading
13684          */
13685         setRoutable: function(params) {
13686             var handlers, contentBody = {},
13687                 restType = this.getRestType(),
13688                 url = this.getRestUrl();
13689             params = params || {};
13690 
13691             contentBody[restType] = {
13692                 "routable": params.routable
13693             };
13694 
13695             handlers = params.handlers || {};
13696 
13697             this._makeRequest(contentBody, url, handlers);
13698 
13699             return this;
13700         },
13701 
13702         /**
13703          * @private
13704          * Invoke a request to the server given a content body and handlers.
13705          *
13706          * @param {Object} contentBody
13707          *     A JS object containing the body of the action request.
13708          * @param {finesse.interfaces.RequestHandlers} handlers
13709          *     An object containing the handlers for the request
13710          */
13711         _makeRequest: function (contentBody, url, handlers) {
13712             // Protect against null dereferencing of options allowing its
13713             // (nonexistent) keys to be read as undefined
13714             handlers = handlers || {};
13715 
13716             this.restRequest(url, {
13717                 method: 'PUT',
13718                 success: handlers.success,
13719                 error: handlers.error,
13720                 content: contentBody
13721             });
13722         },
13723 
13724         /**
13725          * Return true if the params object contains one of the following:<ul>
13726          *     <li>maxDialogLimit</li>
13727          *     <li>interruptAction</li>
13728          *     <li>dialogLogoutAction</li></ul>
13729          *
13730          * @param {Object} params the parameters to evaluate
13731          * @returns {*|Boolean}
13732          * @private
13733          */
13734         _containsLoginOptions: function(params) {
13735             return params.maxDialogLimit || params.interruptAction || params.dialogLogoutAction;
13736         },
13737 
13738         /**
13739          * Create login parameters using the given algorithm:<ul>
13740          *     <li>if loginOptions have not be set in the call to MediaList.getMedia(), use the params given by the application</li>
13741          *     <li>if no params were set by the application, use the loginOptions set in the call to MediaList.getMedia()</li>
13742          *     <li>if the parameters given by the application contains login options, use the parameters given by the application</li>
13743          *     <li>if login options were given by the application but callbacks were given, create new login params with the
13744          *     loginOptions set in the call to MediaList.getMedia() and the callbacks specified in the given login params</li></ul>
13745          *
13746          * @param params the parameters specified by the application in the call to Media.login()
13747          *
13748          * @see finesse.restservices.Media#login
13749          * @see finesse.restservices.MediaList#getMedia
13750          *
13751          * @returns {Object} login parameters built based on the algorithm listed above.
13752          * @private
13753          */
13754         _makeLoginOptions: function(params) {
13755             if ( !this._mediaOptions ) {
13756                 // If loginOptions have not be set, use the params given by the application.
13757                 //
13758                 return params;
13759             }
13760 
13761             if ( !params ) {
13762                 // If there were no params given by the application, use the loginOptions set in the call to
13763                 // MediaList.getMedia().
13764                 //
13765                 return this._mediaOptions;
13766             }
13767 
13768             if (  this._containsLoginOptions(params) ) {
13769                 // if the parameters given by the application contains login options, use the parameters given by the
13770                 // application.
13771                 //
13772                 return params;
13773             }
13774 
13775             // If login options were given by the application but callbacks were given, create new login params with the
13776             // loginOptions set in the call to MediaList.getMedia() and the callbacks specified in the given login
13777             // params.
13778             //
13779             return {
13780                 maxDialogLimit: this._mediaOptions.maxDialogLimit,
13781                 interruptAction: this._mediaOptions.interruptAction,
13782                 dialogLogoutAction: this._mediaOptions.dialogLogoutAction,
13783                 handlers: params.handlers
13784             };
13785         },
13786 
13787         /**
13788          * Ensure that the maxDialogLimit, interruptAction, and dialogLogoutAction options are kept in synch across
13789          * fail-overs for this media object.
13790          * @private
13791          */
13792         _ensureOptionsAreInSynch: function() {
13793             if ( !this._optionsHelper && this._mediaOptions ) {
13794                 this._optionsHelper = new MediaOptionsHelper(this, this._mediaOptions);
13795             }
13796         },
13797 
13798         /**
13799          * Log the agent into this media.
13800          *
13801          * @param {Object} options
13802          *     An object with the following properties:<ul>
13803          *         <li><b>maxDialogLimit:</b> The id of the object being constructed</li>
13804          *         <li><b>interruptAction:</b> Accept or ignore interrupts</li>
13805          *         <li><b>dialogLogoutAction:</b> transfer or close the task at logout time</li>
13806          *         <li><b>{finesse.interfaces.RequestHandlers} handlers:</b> An object containing the handlers for the request</li></ul>
13807          *
13808          *     If maxDialogLimit, interruptAction, and dialogLogoutAction are not present, loginOptions specified in the
13809          *     call to finesse.restservices.MediaList.getMedia() will be used.
13810          *
13811          * @see finesse.restservices.MediaList#getMedia
13812          *
13813          * @returns {finesse.restservices.Media}
13814          *     This Media object, to allow cascading
13815          */
13816         login: function(params) {
13817             this.setState(Media.States.LOGIN, null, this._makeLoginOptions(params));
13818             return this; // Allow cascading
13819         },
13820 
13821         /**
13822          * Perform a logout for a user on this media.
13823          * @param {String} reasonCode
13824          *     The reason this user is logging out of this media.  Pass null for no reason.
13825          * @param {finesse.interfaces.RequestHandlers} handlers
13826          *     An object containing the handlers for the request
13827          * @returns {finesse.restservices.Media}
13828          *     This Media object, to allow cascading
13829          */
13830         logout: function(reasonCode, params) {
13831             var state = Media.States.LOGOUT;
13832             return this.setState(state, reasonCode, params);
13833         },
13834 
13835         /**
13836          * Set the state of the user on this Media.
13837          * @param {String} newState
13838          *     The state you are setting
13839          * @param {ReasonCode} reasonCode
13840          *     The reason this user is changing state for this media.  Pass null for no reason.
13841          * @param {finesse.interfaces.RequestHandlers} handlers
13842          *     An object containing the handlers for the request
13843          * @see finesse.restservices.User.States
13844          * @returns {finesse.restservices.Media}
13845          *     This Media object, to allow cascading
13846          */
13847         setState: function(state, reasonCode, params) {
13848             var handlers, reasonCodeId, contentBody = {},
13849                 restType = this.getRestType(),
13850                 url = this.getRestUrl();
13851             params = params || {};
13852 
13853             if(reasonCode) {
13854                 reasonCodeId = reasonCode.id;
13855             }
13856 
13857             contentBody[restType] = {
13858                 "state": state,
13859                 "maxDialogLimit": params.maxDialogLimit,
13860                 "interruptAction": params.interruptAction,
13861                 "dialogLogoutAction": params.dialogLogoutAction,
13862                 "reasonCodeId": reasonCodeId
13863             };
13864 
13865             handlers = params.handlers || {};
13866 
13867             this._makeRequest(contentBody, url, handlers);
13868 
13869             return this;
13870         },
13871 
13872         /**
13873          * Getter for a MediaDialogs collection object that is associated with User on this Media.
13874          * @param {finesse.interfaces.RestObjectHandlers} [handlers] Object that sets callback handlers (only
13875          * applicable when Object has not been previously created).
13876          * @returns {finesse.restservices.MediaDialogs}
13877          *     A MediaDialogs collection object.
13878          */
13879         getMediaDialogs: function (callbacks) {
13880             var options = callbacks || {};
13881             options.parentObj = this._restObj;
13882             options.mediaObj = this;
13883             this.isLoaded();
13884 
13885             if (this._mdialogs === null) {
13886                 this._mdialogs = new MediaDialogs(options);
13887             }
13888 
13889             return this._mdialogs;
13890         },
13891 
13892         /**
13893          * Refresh the dialog collection associated with this media.
13894          */
13895         refreshMediaDialogs: function() {
13896             if ( this._mdialogs ) {
13897                 this._mdialogs.refresh();
13898             }
13899         },
13900 
13901         /**
13902          * Set the maxDialogLimit, interruptAction, and dialogLogoutAction settings that the application will use for
13903          * this media. In the event of a failure, these options will be set on the new Finesse server.
13904          *
13905          * @param {Object} mediaOptions an object with the following properties:<ul>
13906          *         <li><b>maxDialogLimit:</b> The id of the object being constructed</li>
13907          *         <li><b>interruptAction:</b> Accept or ignore interrupts</li>
13908          *         <li><b>dialogLogoutAction:</b> transfer or close the task at logout time</li></ul>
13909          */
13910         setMediaOptions: function(mediaOptions) {
13911             if ( mediaOptions ) {
13912                 this._mediaOptions = mediaOptions;
13913                 if ( !this._optionsHelper ) {
13914                     this._optionsHelper = new MediaOptionsHelper(this, this._mediaOptions);
13915                 }
13916             }
13917         },
13918 
13919         /**
13920          * Refresh this media object and optionally refresh the list of media dialogs associated with this object.
13921          *
13922          * @param {Integer} retries the number of times to retry synchronizing this media object.
13923          */
13924         refresh: function(retries) {
13925             retries = retries || 1;
13926             this._synchronize(retries);
13927             this.refreshMediaDialogs();
13928         },
13929 
13930         /**
13931          * Is the user in work state on this Media.
13932          * @returns {boolean} returns true if the media is in work state; false otherwise
13933          */
13934         isInWorkState: function() {
13935             return this.getState() === Media.States.WORK;
13936         },
13937 
13938         /**
13939          * Is the user in any state except LOGOUT on this Media.
13940          * @returns {boolean} returns true if the agent is in any state except LOGOUT in this media
13941          */
13942         isLoggedIn: function() {
13943          var state = this.getState();
13944          return state && state !== Media.States.LOGOUT;
13945         }
13946     });
13947 
13948     Media.States = /** @lends finesse.restservices.Media.States.prototype */ {
13949         /**
13950          * User Login on a non-voice Media.  Note that while this is an action, is not technically a state, since a
13951          * logged-in User will always be in a specific state (READY, NOT_READY, etc.).
13952          */
13953         LOGIN: "LOGIN",
13954         /**
13955          * User is logged out of this Media.
13956          */
13957         LOGOUT: "LOGOUT",
13958         /**
13959          * User is not ready on this Media.
13960          */
13961         NOT_READY: "NOT_READY",
13962         /**
13963          * User is ready for activity on this Media.
13964          */
13965         READY: "READY",
13966         /**
13967          * User has a dialog coming in, but has not accepted it.
13968          */
13969         RESERVED: "RESERVED",
13970         /**
13971          * The dialogs in this media have been interrupted by a dialog in a non-interruptible media.
13972          */
13973         INTERRUPTED: "INTERRUPTED",
13974         /**
13975          * User enters this state when failing over from one Finesse to the other or when failing over from one
13976          * PG side to the other.
13977          */
13978         WORK: "WORK",
13979         /**
13980          * @class Possible Media state values.
13981          * @constructs
13982          */
13983         _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
13984 
13985     };
13986 
13987     window.finesse = window.finesse || {};
13988     window.finesse.restservices = window.finesse.restservices || {};
13989     window.finesse.restservices.Media = Media;
13990 
13991     return Media;
13992 });
13993 /**
13994  * JavaScript representation of the Finesse Media collection
13995  * object which contains a list of Media objects.
13996  *
13997  * @requires finesse.clientservices.ClientServices
13998  * @requires Class
13999  * @requires finesse.FinesseBase
14000  * @requires finesse.restservices.RestBase
14001  * @requires finesse.restservices.Media
14002  */
14003 /** @private */
14004 define('restservices/MediaList',[
14005         'restservices/RestCollectionBase',
14006         'restservices/Media',
14007         'restservices/RestBase',
14008         'utilities/Utilities'
14009     ],
14010     function (RestCollectionBase, Media, RestBase, Utilities) {
14011         var MediaList = RestCollectionBase.extend(/** @lends finesse.restservices.MediaList.prototype */{
14012 
14013             /**
14014              * @class
14015              * JavaScript representation of a MediaList collection object.
14016              * @augments finesse.restservices.RestCollectionBase
14017              * @constructs
14018              * @see finesse.restservices.Media
14019              * @example
14020              *  mediaList = _user.getMediaList( {
14021              *      onCollectionAdd : _handleMediaAdd,
14022              *      onCollectionDelete : _handleMediaDelete,
14023              *      onLoad : _handleMediaListLoaded
14024              *  });
14025              *
14026              * _mediaCollection = mediaList.getCollection();
14027              * for (var mediaId in _mediaCollection) {
14028              *     if (_mediaCollection.hasOwnProperty(mediaId)) {
14029              *         media = _mediaCollection[mediaId];
14030              *         etc...
14031              *     }
14032              * }
14033              */
14034             _fakeConstuctor: function () {
14035                 /* This is here to hide the real init constructor from the public docs */
14036             },
14037 
14038             /**
14039              * @private
14040              * @param {Object} options
14041              *     An object with the following properties:<ul>
14042              *         <li><b>id:</b> The id of the object being constructed</li>
14043              *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
14044              *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
14045              *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
14046              *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
14047              *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
14048              *             <li><b>status:</b> {Number} The HTTP status code returned</li>
14049              *             <li><b>content:</b> {String} Raw string of response</li>
14050              *             <li><b>object:</b> {Object} Parsed object of response</li>
14051              *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
14052              *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
14053              *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
14054              *             </ul></li>
14055              *         </ul></li>
14056              *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
14057              **/
14058             init: function (options) {
14059                 this._super(options);
14060             },
14061 
14062             /**
14063              * @private
14064              * Gets the REST class for the current object - this is the MediaList class.
14065              */
14066             getRestClass: function () {
14067                 return MediaList;
14068             },
14069 
14070             /**
14071              * @private
14072              * Gets the REST class for the objects that make up the collection. - this
14073              * is the Media class.
14074              */
14075             getRestItemClass: function () {
14076                 return Media;
14077             },
14078 
14079             /**
14080              * @private
14081              * Gets the REST type for the current object - this is a "MediaList".
14082              */
14083             getRestType: function () {
14084                 return "MediaList";
14085             },
14086 
14087             /**
14088              * @private
14089              * Gets the REST type for the objects that make up the collection - this is "Media".
14090              */
14091             getRestItemType: function () {
14092                 return "Media";
14093             },
14094 
14095             /**
14096              * @private
14097              * Override default to indicates that the collection doesn't support making
14098              * requests.
14099              */
14100             supportsRequests: true,
14101 
14102             /**
14103              * @private
14104              * Override default to indicates that the collection subscribes to its objects.
14105              */
14106             supportsRestItemSubscriptions: true,
14107 
14108             /**
14109              * The REST URL in which this object can be referenced.
14110              * @return {String}
14111              *     The REST URI for this object.
14112              * @private
14113              */
14114             getRestUrl: function () {
14115                 var
14116                     restObj = this._restObj,
14117                     restUrl = "";
14118 
14119                 //Prepend the base REST object if one was provided.
14120                 if (restObj instanceof RestBase) {
14121                     restUrl += restObj.getRestUrl();
14122                 }
14123                 //Otherwise prepend with the default webapp name.
14124                 else {
14125                     restUrl += "/finesse/api";
14126                 }
14127 
14128                 //Append the REST type. (Media not MediaList)
14129                 restUrl += "/" + this.getRestItemType();
14130 
14131                 //Append ID if it is not undefined, null, or empty.
14132                 if (this._id) {
14133                     restUrl += "/" + this._id;
14134                 }
14135 
14136                 return restUrl;
14137             },
14138 
14139             /**
14140              * Getter for a Media from the MediaList collection.
14141              *  * @param {Object} options
14142              *     An object with the following properties:<ul>
14143              *         <li><b>id:</b> The id of the media to fetch</li>
14144              *         <li><b>onLoad(this): (optional)</b> callback handler for when the object is successfully loaded from the server</li>
14145              *         <li><b>onChange(this): (optional)</b> callback handler for when an update notification of the object is received</li>
14146              *         <li><b>onAdd(this): (optional)</b> callback handler for when a notification that the object is created is received</li>
14147              *         <li><b>onDelete(this): (optional)</b> callback handler for when a notification that the object is deleted is received</li>
14148              *         <li><b>onError(rsp): (optional)</b> callback handler for if loading of the object fails, invoked with the error response object:<ul>
14149              *             <li><b>status:</b> {Number} The HTTP status code returned</li>
14150              *             <li><b>content:</b> {String} Raw string of response</li>
14151              *             <li><b>object:</b> {Object} Parsed object of response</li>
14152              *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
14153              *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
14154              *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
14155              *             </ul></li>
14156              *         </ul></li>
14157              *         <li><b>mediaOptions:</b> {Object} An object with the following properties:<ul>
14158              *             <li><b>maxDialogLimit:</b> The id of the object being constructed</li>
14159              *             <li><b>interruptAction:</b> Accept or ignore interrupts</li>
14160              *             <li><b>dialogLogoutAction:</b> transfer or close the task at logout time</li></ul>
14161              *         </li></ul>
14162              *
14163              * @returns {finesse.restservices.Media}
14164              *     A Media object.
14165              */
14166             getMedia: function (options) {
14167                 this.isLoaded();
14168                 options = options || {};
14169                 var objectId = options.id,
14170                     media = this._collection[objectId];
14171 
14172                 //throw error if media not found
14173                 if(!media) {
14174                     throw new Error("No media found with id: " + objectId);
14175                 }
14176 
14177                 media.addHandler('load', options.onLoad);
14178                 media.addHandler('change', options.onChange);
14179                 media.addHandler('add', options.onAdd);
14180                 media.addHandler('delete', options.onDelete);
14181                 media.addHandler('error', options.onError);
14182 
14183                 media.setMediaOptions(options.mediaOptions);
14184 
14185                 return media;
14186             },
14187 
14188             /**
14189              * Utility method to build the internal collection data structure (object) based on provided data
14190              * @param {Object} data
14191              *     The data to build the internal collection from
14192              * @private
14193              */
14194             _buildCollection: function (data) {
14195                 //overriding this from RestBaseCollection because we need to pass in parentObj
14196                 //because restUrl is finesse/api/User/<useri>/Media/<mediaId>
14197                 //other objects like dialog have finesse/api/Dialog/<dialogId>
14198 
14199                 var i, object, objectId, dataArray;
14200                 if (data && this.getProperty(data, this.getRestItemType()) !== null) {
14201                     dataArray = Utilities.getArray(this.getProperty(data, this.getRestItemType()));
14202                     for (i = 0; i < dataArray.length; i += 1) {
14203 
14204                         object = {};
14205                         object[this.getRestItemType()] = dataArray[i];
14206 
14207                         //get id from object.id instead of uri, since uri is not there for some reason
14208                         objectId = object[this.getRestItemType()].id;//this._extractId(object);
14209 
14210                         //create the Media Object
14211                         this._collection[objectId] = new (this.getRestItemClass())({
14212                             id: objectId,
14213                             data: object,
14214                             parentObj: this._restObj
14215                         });
14216                         this.length += 1;
14217                     }
14218                 }
14219             }
14220         });
14221 
14222         window.finesse = window.finesse || {};
14223         window.finesse.restservices = window.finesse.restservices || {};
14224         window.finesse.restservices.MediaList = MediaList;
14225 
14226         return MediaList;
14227     });
14228 
14229 /**
14230 * JavaScript representation of the Finesse ReasonCodes collection
14231 * object which contains a list of ReasonCodes objects.
14232  *
14233  * @requires Class
14234  */
14235 
14236 /** @private */
14237 define('restservices/ReasonCodes',[], function () {
14238 
14239     var ReasonCodes = Class.extend(/** @lends finesse.restservices.ReasonCodes.prototype */{
14240         
14241         /**
14242          * @class
14243          * JavaScript representation of a ReasonCodes collection object. 
14244          * @augments Class
14245          * @constructs
14246          * @example
14247          *  _user.getNotReadyReasonCodes({
14248          *           success: handleNotReadyReasonCodesSuccess,
14249          *           error: handleNotReadyReasonCodesError
14250          *       });
14251          *  
14252          * handleNotReadyReasonCodesSuccess = function(reasonCodes) {
14253          *          for(var i = 0; i < reasonCodes.length; i++) {
14254          *             var reasonCode = reasonCodes[i];
14255          *             var reasonCodeId = reasonCode.id;
14256          *             var reasonCodeLabel = reasonCode.label;
14257          *          }
14258          *      }
14259          * }
14260         */
14261 
14262         _fakeConstuctor: function () {
14263             /* This is here to hide the real init constructor from the public docs */
14264         }
14265         
14266     });
14267  
14268     window.finesse = window.finesse || {};
14269     window.finesse.restservices = window.finesse.restservices || {};
14270     window.finesse.restservices.ReasonCodes = ReasonCodes;
14271        
14272     return ReasonCodes;
14273 });
14274 
14275 /**
14276  * JavaScript representation of the Finesse User object
14277  *
14278  * @requires finesse.clientservices.ClientServices
14279  * @requires Class
14280  * @requires finesse.FinesseBase
14281  * @requires finesse.restservices.RestBase
14282  */
14283 
14284 /** @private */
14285 define('restservices/User',[
14286     'restservices/RestBase',
14287     'restservices/Dialogs',
14288     'restservices/ClientLog',
14289     'restservices/Queues',
14290     'restservices/WrapUpReasons',
14291     'restservices/PhoneBooks',
14292     'restservices/Workflows',
14293     'restservices/UserMediaPropertiesLayout',
14294     'restservices/UserMediaPropertiesLayouts',
14295     'restservices/Media',
14296     'restservices/MediaList',
14297     'restservices/ReasonCodes',
14298     'utilities/Utilities'
14299 ],
14300 function (RestBase, Dialogs, ClientLog, Queues, WrapUpReasons, PhoneBooks, Workflows, UserMediaPropertiesLayout, UserMediaPropertiesLayouts, Media, MediaList, ReasonCodes, Utilities) {
14301 
14302     var User = RestBase.extend(/** @lends finesse.restservices.User.prototype */{
14303 
14304         _dialogs : null,
14305         _clientLogObj : null,
14306         _wrapUpReasons : null,
14307         _phoneBooks : null,
14308         _workflows : null,
14309         _mediaPropertiesLayout : null,
14310         _mediaPropertiesLayouts : null,
14311         _queues : null,
14312         media : null,
14313         mediaList : null,
14314 
14315         /**
14316          * @class
14317          * The User represents a Finesse Agent or Supervisor.
14318          *
14319          * @param {Object} options
14320          *     An object with the following properties:<ul>
14321          *         <li><b>id:</b> The id of the object being constructed</li>
14322          *         <li><b>onLoad(this): (optional)</b> callback handler for when the object is successfully loaded from the server</li>
14323          *         <li><b>onChange(this): (optional)</b> callback handler for when an update notification of the object is received</li>
14324          *         <li><b>onAdd(this): (optional)</b> callback handler for when a notification that the object is created is received</li>
14325          *         <li><b>onDelete(this): (optional)</b> callback handler for when a notification that the object is deleted is received</li>
14326          *         <li><b>onError(rsp): (optional)</b> callback handler for if loading of the object fails, invoked with the error response object:<ul>
14327          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
14328          *             <li><b>content:</b> {String} Raw string of response</li>
14329          *             <li><b>object:</b> {Object} Parsed object of response</li>
14330          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
14331          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
14332          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
14333          *             </ul></li>
14334          *         </ul></li>
14335          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
14336          * @augments finesse.restservices.RestBase
14337          * @constructs
14338          * @example
14339          *      _user = new finesse.restservices.User({
14340          *                      id: _id,
14341          *                      onLoad : _handleUserLoad,
14342          *                      onChange : _handleUserChange
14343          *      });
14344          **/
14345         init: function (options) {
14346             this._super(options);
14347         },
14348 
14349         Callbacks: {},
14350 
14351         /**
14352          * @private
14353          * Gets the REST class for the current object - this is the User object.
14354          */
14355         getRestClass: function () {
14356             return User;
14357         },
14358 
14359         /**
14360         * @private
14361          * Gets the REST type for the current object - this is a "User".
14362          */
14363         getRestType: function () {
14364             return "User";
14365         },
14366         /**
14367          * @private
14368          * overloading this to return URI
14369          */
14370         getXMPPNodePath: function () {
14371             return this.getRestUrl();
14372         },
14373         /**
14374         * @private
14375          * Returns whether this object supports subscriptions
14376          */
14377         supportsSubscriptions: function () {
14378             return true;
14379         },
14380 
14381         /**
14382          * Getter for the firstName of this User.
14383          * @returns {String}
14384          *     The firstName for this User
14385          */
14386         getFirstName: function () {
14387             this.isLoaded();
14388             return Utilities.convertNullToEmptyString(this.getData().firstName);
14389         },
14390 
14391 						        
14392         /**
14393 		 * Getter for the reasonCode of this User.
14394 		 * 
14395 		 * @returns {Object} The reasonCode for the state of the User<br>
14396 		 * The contents may include the following:<ul>
14397 		 * 		<li>uri: The URI for the reason code object.
14398 		 * 		<li>id: The unique ID for the reason code.
14399 		 * 		<li>category: The category. Can be either NOT_READY or LOGOUT.
14400 		 * 		<li>code: The numeric reason code value.
14401 		 * 		<li>label: The label for the reason code.
14402 		 * 		<li>forAll: Boolean flag that denotes the global status for the reason code.
14403 		 * 		<li>systemCode: Boolean flag which denotes whether the reason code is system generated or custom one.
14404 		 * 	</ul>
14405 		 * 
14406 		 */
14407 		getReasonCode : function() {
14408 			this.isLoaded();
14409 			return this.getData().reasonCode;
14410 		},
14411 		
14412 		/**
14413 		 * Getter for the pending state reasonCode of this User.
14414 		 * 
14415 		 * @returns {Object} The reasonCode for the pending state of the User.<br>
14416 		 * The contents may include the following:<ul>
14417 		 * 		<li>uri: The URI for the reason code object.
14418 		 * 		<li>id: The unique ID for the reason code.
14419 		 * 		<li>category: The category. Can be either NOT_READY or LOGOUT.
14420 		 * 		<li>code: The numeric reason code value.
14421 		 * 		<li>label: The label for the reason code.
14422 		 * 		<li>forAll: Boolean flag that denotes the global status for the reason code.
14423 		 * 		<li>systemCode: Boolean flag which denotes whether the reason code is system generated or custom one.
14424 		 * 	</ul>
14425 		 */
14426 		getPendingStateReasonCode : function() {
14427 			this.isLoaded();
14428 			return this.getData().pendingStateReasonCode;
14429 		},
14430 
14431 						        
14432         /**
14433 		 * Getter for the reasonCode of this User.
14434 		 * 
14435 		 * @returns {Boolean} True if the reason code for the state of the user
14436 		 * is a system generated reason code. 
14437 		 */
14438 		isReasonCodeReserved : function() {
14439 			var resonCode = this.getReasonCode();
14440 			if (resonCode) {
14441 				return resonCode.systemCode === "true" ? true
14442 						: false;
14443 			}
14444 			return false;
14445 		},
14446 
14447 
14448         /**
14449          * Getter for the lastName of this User.
14450          * @returns {String}
14451          *     The lastName for this User
14452          */
14453         getLastName: function () {
14454             this.isLoaded();
14455             return Utilities.convertNullToEmptyString(this.getData().lastName);
14456         },
14457 
14458         /**
14459          * Getter for the full name of this User.
14460          * The full name is of the format "FirstName LastName" (for example: "John Doe")
14461          * 
14462          * @returns {String}
14463          *     The full name of this User.
14464          */
14465         getFullName : function() {
14466             this.isLoaded();
14467 
14468             /*
14469              * Currently, the expected format is "FirstName LastName", but can differ later
14470              * to something "LastName, FirstName".
14471              * To accommodate such, we use formatString utility method so that, if required,
14472              * the same can be achieved just by flipping the format place holders as follows:
14473              * "{1}, {0}"
14474              * Also, the function could be enhanced to take the format parameter.
14475              */
14476             var fmt = "{0} {1}";
14477             return Utilities.formatString(fmt,
14478                 Utilities.convertNullToEmptyString(this.getData().firstName),
14479                 Utilities.convertNullToEmptyString(this.getData().lastName));
14480         },
14481 
14482         /**
14483          * Getter for the extension of this User.
14484          * @returns {String}
14485          *     The extension, if any, of this User
14486          */
14487         getExtension: function () {
14488             this.isLoaded();
14489             return Utilities.convertNullToEmptyString(this.getData().extension);
14490         },
14491 
14492         /**
14493          * Getter for the id of the Team of this User
14494          * @returns {String}
14495          *     The current (or last fetched) id of the Team of this User
14496          */
14497         getTeamId: function () {
14498             this.isLoaded();
14499             return this.getData().teamId;
14500         },
14501 
14502         /**
14503          * Getter for the name of the Team of this User
14504          * @returns {String}
14505          *     The current (or last fetched) name of the Team of this User
14506          */
14507         getTeamName: function () {
14508             this.isLoaded();
14509             return this.getData().teamName;
14510         },
14511 
14512         /**
14513          * Is user an agent?
14514          * @returns {Boolean} True if user has role of agent, else false.
14515          */
14516         hasAgentRole: function () {
14517             this.isLoaded();
14518             return this.hasRole("Agent");
14519         },
14520 
14521         /**
14522          * Is user a supervisor?
14523          * @returns {Boolean} True if user has role of supervisor, else false.
14524          */
14525         hasSupervisorRole: function () {
14526             this.isLoaded();
14527             return this.hasRole("Supervisor");
14528         },
14529 
14530         /**
14531          * @private
14532          * Checks to see if user has "theRole"
14533          * @returns {Boolean}
14534          */
14535         hasRole: function (theRole) {
14536             this.isLoaded();
14537             var result = false, i, roles, len;
14538 
14539             roles = this.getData().roles.role;
14540             len = roles.length;
14541             if (typeof roles === 'string') {
14542                 if (roles === theRole) {
14543                     result = true;
14544                 }
14545             } else {
14546                 for (i = 0; i < len ; i = i + 1) {
14547                     if (roles[i] === theRole) {
14548                         result = true;
14549                         break;
14550                     }
14551                 }
14552             }
14553 
14554             return result;
14555         },
14556 
14557         /**
14558          * Getter for the pending state of this User.
14559          * @returns {String}
14560          *     The pending state of this User
14561          * @see finesse.restservices.User.States
14562          */
14563         getPendingState: function () {
14564             this.isLoaded();
14565             return Utilities.convertNullToEmptyString(this.getData().pendingState);
14566         },
14567 
14568         /**
14569          * Getter for the state of this User.
14570          * @returns {String}
14571          *     The current (or last fetched) state of this User
14572          * @see finesse.restservices.User.States
14573          */
14574         getState: function () {
14575             this.isLoaded();
14576             return this.getData().state;
14577         },
14578         
14579         /**
14580          * Getter for the media state of this User.
14581          * @returns {String}
14582          *     The current (or last fetched) media state of this User
14583          *     Will be applicable only in CCX deployments
14584          *     When the agent is talking on a manual outbound call, it returns busy 
14585          *     The value will not be present in other cases.
14586          */
14587         getMediaState: function () {
14588             this.isLoaded();
14589             /* There is an assertion that the value should not be undefined while setting the value in datastore. Hence setting it to null*/
14590             if(this.getData().mediaState === undefined) {
14591                   this.getData().mediaState = null;
14592             }
14593             
14594             return this.getData().mediaState;
14595          },
14596         
14597         /**
14598          * Getter for the state change time of this User.
14599          * @returns {String}
14600          *     The state change time of this User
14601          */
14602         getStateChangeTime: function () {
14603             this.isLoaded();
14604             return this.getData().stateChangeTime;
14605         },
14606 
14607         /**
14608          * Getter for the wrap-up mode of this User.
14609          * @see finesse.restservices.User.WrapUpMode
14610          * @returns {String} The wrap-up mode of this user that is a value of {@link finesse.restservices.User.WrapUpMode}
14611          */
14612         getWrapUpOnIncoming: function () {
14613             this.isLoaded();
14614             return this.getData().settings.wrapUpOnIncoming;
14615         },
14616 
14617         /**
14618          * Is User required to go into wrap-up?
14619          * @see finesse.restservices.User.WrapUpMode
14620          * @return {Boolean}
14621          *      True if this agent is required to go into wrap-up.
14622          */
14623         isWrapUpRequired: function () {
14624             return (this.getWrapUpOnIncoming() === User.WrapUpMode.REQUIRED ||
14625                     this.getWrapUpOnIncoming() === User.WrapUpMode.REQUIRED_WITH_WRAP_UP_DATA);
14626         },
14627 
14628         /**
14629          * Checks to see if the user is considered a mobile agent by checking for
14630          * the existence of the mobileAgent node.
14631          * @returns {Boolean}
14632          *      True if this agent is a mobile agent.
14633          */
14634         isMobileAgent: function () {
14635             this.isLoaded();
14636             var ma = this.getData().mobileAgent;
14637             return ma !== null && typeof ma === "object";
14638         },
14639 
14640         /**
14641          * Getter for the mobile agent work mode.
14642          * @returns {finesse.restservices.User.WorkMode}
14643          *      If available, return the mobile agent work mode, otherwise null.
14644          */
14645         getMobileAgentMode: function () {
14646             this.isLoaded();
14647             if (this.isMobileAgent()) {
14648                 return this.getData().mobileAgent.mode;
14649             }
14650             return null;
14651         },
14652 
14653         /**
14654          * Getter for the mobile agent dial number.
14655          * @returns {String}
14656          *      If available, return the mobile agent dial number, otherwise null.
14657          */
14658         getMobileAgentDialNumber: function () {
14659             this.isLoaded();
14660             if (this.isMobileAgent()) {
14661                 return this.getData().mobileAgent.dialNumber;
14662             }
14663             return null;
14664         },
14665 
14666         /**
14667          * Getter for a Dialogs collection object that is associated with User.
14668          * @param {finesse.interfaces.RestObjectHandlers} [handlers] Object that sets callback handlers (only
14669          * applicable when Object has not been previously created).
14670          * @returns {finesse.restservices.Dialogs}
14671          *     A Dialogs collection object.
14672          */
14673         getDialogs: function (callbacks) {
14674             var options = callbacks || {};
14675             options.parentObj = this;
14676             this.isLoaded();
14677 
14678             if (this._dialogs === null) {
14679                 this._dialogs = new Dialogs(options);
14680             }
14681 
14682             return this._dialogs;
14683         },
14684 
14685         /**
14686          * Getter for Media collection object that is associated with User.
14687          * @param {finesse.interfaces.RestObjectHandlers} [handlers] Object that sets callback handlers (only
14688          * applicable when Object has not been previously created).
14689          * @returns {finesse.restservices.MediaList}
14690          *     A Media Dialogs collection object.
14691          */
14692         getMediaList: function (callbacks) {
14693             var options = callbacks || {};
14694             options.parentObj = this;
14695             this.isLoaded();
14696 
14697             if (this.mediaList === null) {
14698                 this.mediaList = new MediaList(options);
14699             }
14700 
14701             return this.mediaList;
14702         },
14703 
14704         /**
14705          * @private
14706          * Getter for a ClientLog object that is associated with User.
14707          * @param {finesse.interfaces.RestObjectHandlers} [handlers] Object that sets callback handlers (only 
14708          * applicable when Object has not been previously created).
14709          * @returns {finesse.restservices.ClientLog}
14710          *     A ClientLog collection object.
14711          */
14712         getClientLog: function (callbacks) {
14713             var options = callbacks || {};
14714             options.parentObj = this;
14715             this.isLoaded();
14716            
14717             if (this._clientLogObj === null) {
14718                 this._clientLogObj = new ClientLog(options);
14719             }
14720             else {
14721                 if(options.onLoad && typeof options.onLoad === "function") {
14722                 options.onLoad(this._clientLogObj);
14723                 }
14724             }
14725             return this._clientLogObj;
14726         },
14727        
14728         /**
14729          * Getter for a Queues collection object that is associated with User.
14730          * @param {finesse.interfaces.RestObjectHandlers} [handlers] Object that sets callback handlers (only 
14731          * applicable when Object has not been previously created).
14732          * @returns {finesse.restservices.Queues}
14733          *     A Queues collection object.
14734          */
14735         getQueues: function (callbacks) {
14736             var options = callbacks || {};
14737             options.parentObj = this;
14738             this.isLoaded();
14739     
14740             if (this._queues === null) {
14741                 this._queues = new Queues(options);
14742             }
14743     
14744             return this._queues;
14745         },
14746 
14747         /**
14748          * Getter for a WrapUpReasons collection object that is associated with User.
14749          * @param {finesse.interfaces.RestObjectHandlers} [handlers] Object that sets callback handlers (only 
14750          * applicable when Object has not been previously created).
14751          * @returns {finesse.restservices.WrapUpReasons}
14752          *     A WrapUpReasons collection object.
14753          */
14754         getWrapUpReasons: function (callbacks) {
14755             var options = callbacks || {};
14756             options.parentObj = this;
14757             this.isLoaded();
14758     
14759             if (this._wrapUpReasons === null) {
14760                 this._wrapUpReasons = new WrapUpReasons(options);
14761             }
14762     
14763             return this._wrapUpReasons;
14764         },
14765 
14766         /**
14767          * Getter for a PhoneBooks collection object that is associated with User.
14768          * @param {finesse.interfaces.RestObjectHandlers} [handlers] Object that sets callback handlers (only 
14769          * applicable when Object has not been previously created).
14770          * @returns {finesse.restservices.PhoneBooks}
14771          *     A PhoneBooks collection object.
14772          */
14773         getPhoneBooks: function (callbacks) {
14774             var options = callbacks || {};
14775             options.parentObj = this;
14776             this.isLoaded();
14777     
14778             if (this._phoneBooks === null) {
14779                 this._phoneBooks = new PhoneBooks(options);
14780             }
14781     
14782             return this._phoneBooks;
14783         },
14784 
14785         /**
14786          * @private
14787          * Loads the Workflows collection object that is associated with User and
14788          * 'returns' them to the caller via the handlers.
14789          * @param {finesse.interfaces.RestObjectHandlers} [handlers] Object that sets callback handlers (only 
14790          * applicable when Object has not been previously created).
14791          * @see finesse.restservices.Workflow
14792          * @see finesse.restservices.Workflows
14793          * @see finesse.restservices.RestCollectionBase
14794          */
14795         loadWorkflows: function (callbacks) {
14796             var options = callbacks || {};
14797             options.parentObj = this;
14798             this.isLoaded();
14799 
14800             if (this._workflows === null) {
14801                 this._workflows = new Workflows(options);
14802             } else {
14803                 this._workflows.refresh();
14804             }
14805 
14806         },
14807 
14808         /**
14809          * Getter for a UserMediaPropertiesLayout object that is associated with User.
14810          * @param {finesse.interfaces.RestObjectHandlers} [handlers] Object that sets callback handlers (only 
14811          * applicable when Object has not been previously created).
14812          * @returns {finesse.restservices.UserMediaPropertiesLayout}
14813          *     The UserMediaPropertiesLayout object associated with this user
14814          */
14815         getMediaPropertiesLayout: function (callbacks) {
14816             var options = callbacks || {};
14817             options.parentObj = this;
14818             options.id = this._id;
14819     
14820             this.isLoaded();
14821             if (this._mediaPropertiesLayout === null) {
14822                 this._mediaPropertiesLayout = new UserMediaPropertiesLayout(options);
14823             }
14824             return this._mediaPropertiesLayout;
14825         },
14826 
14827         /**
14828          * Getter for a UserMediaPropertiesLayouts object that is associated with User.
14829          * @param {finesse.interfaces.RestObjectHandlers} [handlers] Object that sets callback handlers (only 
14830          * applicable when Object has not been previously created).
14831          * @returns {finesse.restservices.UserMediaPropertiesLayout}
14832          *     The UserMediaPropertiesLayout object associated with this user
14833          */
14834         getMediaPropertiesLayouts: function (callbacks) {
14835             var options = callbacks || {};
14836             options.parentObj = this;
14837     
14838             this.isLoaded();
14839             if (this._mediaPropertiesLayouts === null) {
14840                 this._mediaPropertiesLayouts = new UserMediaPropertiesLayouts(options);
14841             }
14842             return this._mediaPropertiesLayouts;
14843         },
14844     
14845         /**
14846          * Getter for the supervised Teams this User (Supervisor) supervises, if any.
14847          * @see finesse.restservices.Team
14848          * @returns {Array}
14849          *     An array of Teams supervised by this User (Supervisor)
14850          */
14851         getSupervisedTeams: function () {
14852             this.isLoaded();
14853     
14854             try {
14855                 return Utilities.getArray(this.getData().teams.Team);
14856             } catch (e) {
14857                 return [];
14858             }
14859     
14860         },
14861     
14862         /**
14863          * Perform an agent login for this user, associating him with the
14864          * specified extension.
14865          * @param {Object} params
14866          *     An object containing properties for agent login.
14867          * @param {String} params.extension
14868          *     The extension to associate with this user
14869          * @param {Object} [params.mobileAgent]
14870          *     A mobile agent object containing the mode and dial number properties.
14871          * @param {finesse.interfaces.RequestHandlers} params.handlers
14872          * @see finesse.interfaces.RequestHandlers
14873          * @returns {finesse.restservices.User}
14874          *     This User object, to allow cascading
14875          * @private
14876          */
14877         _login: function (params) {
14878             var handlers, contentBody = {},
14879             restType = this.getRestType();
14880             
14881             // Protect against null dereferencing.
14882             params = params || {};
14883     
14884             contentBody[restType] = {
14885                 "state": User.States.LOGIN,
14886                 "extension": params.extension
14887             };
14888     
14889             // Create mobile agent node if available.
14890             if (typeof params.mobileAgent === "object") {
14891                 contentBody[restType].mobileAgent = {
14892                     "mode": params.mobileAgent.mode,
14893                     "dialNumber": params.mobileAgent.dialNumber
14894                 };
14895             }
14896     
14897             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
14898             handlers = params.handlers || {};
14899     
14900             this.restRequest(this.getRestUrl(), {
14901                 method: 'PUT',
14902                 success: handlers.success,
14903                 error: handlers.error,
14904                 content: contentBody
14905             });
14906     
14907             return this; // Allow cascading
14908         },
14909     
14910         /**
14911          * Perform an agent login for this user, associating him with the
14912          * specified extension.
14913          * @param {String} extension
14914          *     The extension to associate with this user
14915          * @param {finesse.interfaces.RequestHandlers} handlers
14916          *     An object containing the handlers for the request
14917          * @returns {finesse.restservices.User}
14918          *     This User object, to allow cascading
14919          */
14920         login: function (extension, handlers) {
14921             this.isLoaded();
14922             var params = {
14923                 "extension": extension,
14924                 "handlers": handlers
14925             };
14926             return this._login(params);
14927         },
14928         
14929         
14930 
14931     
14932         /**
14933          * Perform an agent login for this user, associating him with the
14934          * specified extension.
14935          * @param {String} extension
14936          *     The extension to associate with this user
14937          * @param {String} mode
14938          *     The mobile agent work mode as defined in finesse.restservices.User.WorkMode.
14939          * @param {String} extension
14940          *     The external dial number desired to be used by the mobile agent.
14941          * @param {finesse.interfaces.RequestHandlers} handlers
14942          *     An object containing the handlers for the request
14943          * @returns {finesse.restservices.User}
14944          *     This User object, to allow cascading
14945          */
14946         loginMobileAgent: function (extension, mode, dialNumber, handlers) {
14947             this.isLoaded();
14948             var params = {
14949                 "extension": extension,
14950                 "mobileAgent": {
14951                     "mode": mode,
14952                     "dialNumber": dialNumber
14953                 },
14954                 "handlers": handlers
14955             };
14956             return this._login(params);
14957         },
14958         
14959         
14960         _updateMobileAgent: function (params) {
14961             var handlers, contentBody = {},
14962             restType = this.getRestType();
14963 
14964             params = params || {};
14965 
14966             contentBody[restType] = {
14967             };
14968 
14969             if (typeof params.mobileAgent === "object") {
14970                 contentBody[restType].mobileAgent = {
14971                     "mode": params.mobileAgent.mode,
14972                     "dialNumber": params.mobileAgent.dialNumber
14973                 };
14974             }
14975 
14976             handlers = params.handlers || {};
14977 
14978             this.restRequest(this.getRestUrl(), {
14979                 method: 'PUT',
14980                 success: handlers.success,
14981                 error: handlers.error,
14982                 content: contentBody
14983             });
14984 
14985             return this;
14986         },
14987         
14988         /**
14989          * Update user object in Finesse with agent's mobile login information (Refer defect CSCvc35407)
14990          * @param {String} mode
14991          *      The mobile agent work mode as defined in finesse.restservices.User.WorkMode.
14992          * @param {String} dialNumber
14993          *      The external dial number desired to be used by the mobile agent.
14994          * @param {finesse.interfaces.RequestHandlers} handlers
14995          *     An object containing the handlers for the request
14996          * @returns {finesse.restservices.User}
14997          *     This User object, to allow cascading
14998          */
14999         updateToMobileAgent: function (mode, dialNumber, handlers) {
15000             this.isLoaded();
15001             var params = {
15002                 "mobileAgent": {
15003                     "mode": mode,
15004                     "dialNumber": dialNumber
15005                 },
15006                 "handlers": handlers
15007             };
15008             return this._updateMobileAgent(params);
15009         },
15010     
15011         /**
15012          * Perform an agent logout for this user.
15013          * @param {String} reasonCode
15014          *     The reason this user is logging out.  Pass null for no reason.
15015          * @param {finesse.interfaces.RequestHandlers} handlers
15016          *     An object containing the handlers for the request
15017          * @returns {finesse.restservices.User}
15018          *     This User object, to allow cascading
15019          */
15020         logout: function (reasonCode, handlers) {
15021             return this.setState("LOGOUT", reasonCode, handlers);
15022         },
15023     
15024         /**
15025          * Set the state of the user.
15026          * @param {String} newState
15027          *     The state you are setting
15028          * @param {ReasonCode} 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          * @see finesse.restservices.User.States
15033          * @returns {finesse.restservices.User}
15034          *     This User object, to allow cascading
15035          */
15036         setState: function (newState, reasonCode, handlers) {
15037             this.isLoaded();
15038     
15039             var options, contentBody = {};
15040     
15041             if (!reasonCode) {
15042                 if(newState === "LOGOUT"){
15043                         contentBody[this.getRestType()] = {
15044                             "state": newState,
15045                             "logoutAllMedia": "true"
15046                         };
15047                 }
15048                 else{
15049                         contentBody[this.getRestType()] = {
15050                             "state": newState
15051                         };
15052                 }
15053                 
15054             } else {
15055                   if(newState === "LOGOUT"){
15056                         contentBody[this.getRestType()] = {
15057                              "state": newState,
15058                              "reasonCodeId": reasonCode.id,
15059                              "logoutAllMedia": "true"
15060                          };
15061                 }
15062                 else{
15063                     contentBody[this.getRestType()] = {
15064                              "state": newState,
15065                              "reasonCodeId": reasonCode.id
15066                          };
15067                     }
15068                
15069             }
15070     
15071             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
15072             handlers = handlers || {};
15073     
15074             options = {
15075                 method: 'PUT',
15076                 success: handlers.success,
15077                 error: handlers.error,
15078                 content: contentBody
15079             };
15080     
15081             // After removing the selective 202 handling, we should be able to just use restRequest
15082             this.restRequest(this.getRestUrl(), options);
15083     
15084             return this; // Allow cascading
15085         },
15086     
15087         /**
15088          * Make call to a particular phone number.
15089          *
15090          * @param {String} 
15091          *     The number to call
15092          * @param {finesse.interfaces.RequestHandlers} handlers
15093          *     An object containing the handlers for the request
15094          * @returns {finesse.restservices.User}
15095          *     This User object, to allow cascading
15096          */ 
15097         makeCall: function (number, handlers) {
15098             this.isLoaded();
15099     
15100             this.getDialogs().createNewCallDialog(number, this.getExtension(), handlers);
15101     
15102             return this; // Allow cascading
15103         },
15104     
15105         /**
15106          * Make a silent monitor call to a particular agent's phone number.
15107          *
15108          * @param {String} 
15109          *     The number to call
15110          * @param {finesse.interfaces.RequestHandlers} handlers
15111          *     An object containing the handlers for the request
15112          * @returns {finesse.restservices.User}
15113          *     This User object, to allow cascading
15114          */
15115         makeSMCall: function (number, handlers) {
15116             this.isLoaded();
15117     
15118             var actionType = "SILENT_MONITOR";
15119     
15120             this.getDialogs().createNewSuperviseCallDialog(number, this.getExtension(), actionType, handlers);
15121     
15122             return this; // Allow cascading
15123         },
15124         
15125     
15126         /**
15127          * Make a silent monitor call to a particular agent's phone number.
15128          *
15129          * @param {String}
15130          *     The number to call
15131          * @param {String} dialogUri
15132          *     The associated dialog uri of SUPERVISOR_MONITOR call
15133          * @param {finesse.interfaces.RequestHandlers} handlers
15134          *     An object containing the handlers for the request
15135          * @see finesse.restservices.dialog
15136          * @returns {finesse.restservices.User}
15137          *     This User object, to allow cascading
15138          */
15139         makeBargeCall:function (number, dialogURI, handlers) {
15140             this.isLoaded();
15141             var actionType = "BARGE_CALL";
15142             this.getDialogs().createNewBargeCall( this.getExtension(), number, actionType, dialogURI,handlers);
15143     
15144             return this; // Allow cascading
15145         },
15146         
15147         /**
15148          * Returns true if the user's current state will result in a pending state change. A pending state
15149          * change is a request to change state that does not result in an immediate state change. For
15150          * example if an agent attempts to change to the NOT_READY state while in the TALKING state, the
15151          * agent will not change state until the call ends.
15152          *
15153          * The current set of states that result in pending state changes is as follows:
15154          *     TALKING
15155          *     HOLD
15156          *     RESERVED_OUTBOUND_PREVIEW
15157          *  @returns {Boolean} True if there is a pending state change.
15158          *  @see finesse.restservices.User.States
15159          */
15160         isPendingStateChange: function () {
15161             var state = this.getState();
15162             return state && ((state === User.States.TALKING) || (state === User.States.HOLD) || (state === User.States.RESERVED_OUTBOUND_PREVIEW));
15163         },
15164         
15165         /**
15166          * Returns true if the user's current state is WORK or WORK_READY. This is used so
15167          * that a pending state is not cleared when moving into wrap up (work) mode. 
15168          * Note that we don't add this as a pending state, since changes while in wrap up
15169          * occur immediately (and we don't want any "pending state" to flash on screen.
15170          * 
15171          * @see finesse.restservices.User.States
15172          * @returns {Boolean} True if user is in wrap-up mode.
15173          */
15174         isWrapUp: function () {
15175             var state = this.getState();
15176             return state && ((state === User.States.WORK) || (state === User.States.WORK_READY));
15177         },
15178     
15179         /**
15180          * @private
15181          * Parses a uriString to retrieve the id portion
15182          * @param {String} uriString
15183          * @return {String} id
15184          */
15185         _parseIdFromUriString : function (uriString) {
15186             return Utilities.getId(uriString);
15187         },
15188 						
15189         /**
15190 		 * Gets the user's Reason Code label. Works for both Not Ready and
15191 		 * Logout reason codes
15192 		 * 
15193 		 * @return {String} the reason code label, or empty string if none
15194 		 */
15195 		getReasonCodeLabel : function() {
15196 			this.isLoaded();
15197 
15198 			if (this.getData().reasonCode) {
15199 				return this.getData().reasonCode.label;
15200 			} else {
15201 				return "";
15202 			}
15203 		},
15204     
15205         /**
15206 		 * Gets the user's Not Ready reason code.
15207 		 * 
15208 		 * @return {String} Reason Code Id, or undefined if not set or
15209 		 *         indeterminate
15210 		 */
15211         getNotReadyReasonCodeId : function () {
15212             this.isLoaded();
15213     
15214             var reasoncodeIdResult, finesseServerReasonCodeId;
15215             finesseServerReasonCodeId = this.getData().reasonCodeId;
15216     
15217             //FinesseServer will give "-l" => we will set to undefined (for convenience)
15218             if (finesseServerReasonCodeId !== "-1") {
15219                 reasoncodeIdResult = finesseServerReasonCodeId;
15220             }
15221     
15222             return reasoncodeIdResult;
15223         },
15224     
15225         /**
15226          * Performs a GET against the Finesse server looking up the reasonCodeId specified.
15227          * Note that there is no return value; use the success handler to process a
15228          * valid return.
15229          * @param {finesse.interfaces.RequestHandlers} handlers
15230          *     An object containing the handlers for the request
15231          * @param {String} reasonCodeId The id for the reason code to lookup
15232          * 
15233          */
15234         getReasonCodeById : function (handlers, reasonCodeId)
15235         {
15236             var self = this, contentBody, reasonCode, url;
15237             contentBody = {};
15238     
15239             url = this.getRestUrl() + "/ReasonCode/" + reasonCodeId;
15240             this.restRequest(url, {
15241                 method: 'GET',
15242                 success: function (rsp) {
15243                     reasonCode = {
15244                         uri: rsp.object.ReasonCode.uri,
15245                         label: rsp.object.ReasonCode.label,
15246                         id: self._parseIdFromUriString(rsp.object.ReasonCode.uri)
15247                     };
15248                     handlers.success(reasonCode);
15249                 },
15250                 error: function (rsp) {
15251                     handlers.error(rsp);
15252                 },
15253                 content: contentBody
15254             });
15255         },
15256     
15257         /**
15258          * Performs a GET against Finesse server retrieving all the specified type of reason codes.
15259          * @param {String} type (LOGOUT or NOT_READY)
15260          * @param {finesse.interfaces.RequestHandlers} handlers
15261          *     An object containing the handlers for the request
15262          */
15263         _getReasonCodesByType : function (type, handlers)
15264         {
15265             var self = this, contentBody = {}, url, reasonCodes, i, reasonCodeArray;
15266     
15267             url = this.getRestUrl() + "/ReasonCodes?category=" + type;
15268             this.restRequest(url, {
15269                 method: 'GET',
15270                 success: function (rsp) {
15271                     reasonCodes = [];
15272     
15273                     reasonCodeArray = rsp.object.ReasonCodes.ReasonCode;
15274                     if (reasonCodeArray === undefined) {
15275                         reasonCodes = undefined;
15276                     } else if (reasonCodeArray[0] !== undefined) {
15277                         for (i = 0; i < reasonCodeArray.length; i = i + 1) {
15278                             reasonCodes[i] = {
15279                                 label: rsp.object.ReasonCodes.ReasonCode[i].label,
15280                                 id: self._parseIdFromUriString(rsp.object.ReasonCodes.ReasonCode[i].uri)
15281                             };
15282                         }
15283                     } else {
15284                         reasonCodes[0] = {
15285                             label: rsp.object.ReasonCodes.ReasonCode.label,
15286                             id: self._parseIdFromUriString(rsp.object.ReasonCodes.ReasonCode.uri)
15287                         };
15288                     }
15289                     handlers.success(reasonCodes);
15290                 },
15291                 error: function (rsp) {
15292                     handlers.error(rsp);
15293                 },
15294                 content: contentBody
15295             });
15296         },
15297     
15298         /**
15299          * Performs a GET against the Finesse server and retrieves all of the Not Ready
15300          * reason codes. Note that there is no return value; use the success handler to
15301          * process a valid return.
15302          *
15303          * <pre class="code">
15304          *      _user.getSignoutReasonCodes({
15305          *           success: handleSignoutReasonCodesSuccess,
15306          *           error: handleSignoutReasonCodesError
15307          *       });
15308          * </pre>
15309          *
15310          * @see finesse.restservices.ReasonCodes
15311          *
15312          * @param {finesse.interfaces.RequestHandlers} handlers
15313          *     An object containing the handlers for the request
15314          */
15315         getSignoutReasonCodes : function (handlers)
15316         {
15317             this._getReasonCodesByType("LOGOUT", handlers);
15318         },
15319     
15320         /**
15321          * Performs a GET against the Finesse server and retrieves all of the Not Ready
15322          * reason codes. Note that there is no return value; use the success handler to
15323          * process a valid return.
15324          *
15325          * <pre class="code">
15326          *      _user.getNotReadyReasonCodes({
15327          *           success: handleNotReadyReasonCodesSuccess,
15328          *           error: handleNotReadyReasonCodesError
15329          *       });
15330          * </pre>
15331          *
15332          * @see finesse.restservices.ReasonCodes
15333          *
15334          * @param {finesse.interfaces.RequestHandlers} handlers
15335          *     An object containing the handlers for the request
15336          */
15337         getNotReadyReasonCodes : function (handlers)
15338         {
15339             this._getReasonCodesByType("NOT_READY", handlers);
15340         }
15341     });
15342     User.MediaStates = /** @lends finesse.restservices.User.MediaStates.prototype */ {
15343          /**
15344          * Will be applicable only in CCX deployments
15345          * When the agent is talking on a manual outbound call
15346          */
15347          BUSY: "BUSY",
15348 			 /**
15349 	         * @class Possible Agent Media States.
15350 	         * @constructs
15351 	         */
15352 	        _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
15353 	        
15354     };
15355     User.States = /** @lends finesse.restservices.User.States.prototype */ {
15356             /**
15357              * User Login.  Note that while this is an action, is not technically a state, since a 
15358              * logged-in User will always be in a specific state (READY, NOT_READY, TALKING, etc.).
15359              */
15360             LOGIN: "LOGIN",
15361             /**
15362              * User is logged out.
15363              */
15364             LOGOUT: "LOGOUT",
15365             /**
15366              * User is not ready. Note that in UCCX implementations, the user is in this state while on a non-routed call.
15367              */
15368             NOT_READY: "NOT_READY",
15369             /**
15370              * User is ready for calls.
15371              */
15372             READY: "READY",
15373             /**
15374              * User has a call coming in, but has not answered it.
15375              */
15376             RESERVED: "RESERVED",
15377             /**
15378              * User has an outbound call being made, but has not been connected to it.
15379              */
15380             RESERVED_OUTBOUND: "RESERVED_OUTBOUND",
15381             /**
15382              * User has an outbound call's preview information being displayed, but has not acted on it.
15383              */
15384             RESERVED_OUTBOUND_PREVIEW: "RESERVED_OUTBOUND_PREVIEW",
15385             /**
15386              * User is on a call.  Note that in UCCX implementations, this is for routed calls only.
15387              */
15388             TALKING: "TALKING",
15389             /**
15390              * User is on hold.  Note that in UCCX implementations, the user remains in TALKING state while on hold.
15391              */
15392             HOLD: "HOLD",
15393             /**
15394              * User is wrap-up/work mode.  This mode is typically configured to time out, after which the user becomes NOT_READY.
15395              */
15396             WORK: "WORK",
15397             /**
15398              * This is the same as WORK, except that after time out user becomes READY.
15399              */
15400             WORK_READY: "WORK_READY",
15401             /**
15402              * @class Possible User state values.
15403              * @constructs
15404              */
15405             _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
15406           
15407         };
15408     
15409     User.WorkMode = { /** @lends finesse.restservices.User.WorkMode.prototype */
15410         /**
15411          * Mobile agent is connected (dialed) for each incoming call received.
15412          */
15413         CALL_BY_CALL: "CALL_BY_CALL",
15414         /**
15415          * Mobile agent is connected (dialed) at login.
15416          */
15417         NAILED_CONNECTION: "NAILED_CONNECTION",
15418         /**
15419          * @class Possible Mobile Agent Work Mode Types.
15420          * @constructs
15421          */
15422         _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
15423         
15424     };
15425 
15426     User.WrapUpMode = { /** @lends finesse.restservices.User.WrapUpMode.prototype */
15427         /**
15428          * Agent must go into wrap-up when call ends
15429          */
15430         REQUIRED: "REQUIRED",
15431         /**
15432          * Agent must go into wrap-up when call ends and must enter wrap-up data
15433          */
15434         REQUIRED_WITH_WRAP_UP_DATA: "REQUIRED_WITH_WRAP_UP_DATA",
15435         /**
15436          * Agent can choose to go into wrap-up on a call-by-call basis when the call ends
15437          */
15438         OPTIONAL: "OPTIONAL",
15439         /**
15440          * Agent is not allowed to go into wrap-up when call ends.
15441          */
15442         NOT_ALLOWED: "NOT_ALLOWED",
15443         /**
15444          * @class Possible Wrap-up Mode Types.
15445          * @constructs
15446          */
15447         _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
15448         
15449     };
15450 
15451     window.finesse = window.finesse || {};
15452     window.finesse.restservices = window.finesse.restservices || {};
15453     window.finesse.restservices.User = User;
15454         
15455     return User;
15456 });
15457 
15458 /**
15459  * JavaScript representation of the Finesse Users collection
15460  * object which contains a list of Users objects.
15461  *
15462  * @requires finesse.clientservices.ClientServices
15463  * @requires Class
15464  * @requires finesse.FinesseBase
15465  * @requires finesse.restservices.RestBase
15466  * @requires finesse.restservices.RestCollectionBase
15467  * @requires finesse.restservices.User
15468  */
15469 
15470 /** @private */
15471 define('restservices/Users',[
15472     'restservices/RestCollectionBase',
15473     'restservices/RestBase',
15474     'restservices/User'
15475 ],
15476 function (RestCollectionBase, RestBase, User) {
15477 
15478     var Users = RestCollectionBase.extend(/** @lends finesse.restservices.Users.prototype */{
15479 
15480     /**
15481      * @class
15482      * JavaScript representation of a Users collection object. 
15483      * While there is no method provided to retrieve all Users, this collection is
15484      * used to return the Users in a supervised Team.
15485      * @augments finesse.restservices.RestCollectionBase
15486      * @constructs
15487      * @see finesse.restservices.Team
15488      * @see finesse.restservices.User
15489      * @see finesse.restservices.User#getSupervisedTeams
15490      * @example
15491      *  // Note: The following method gets an Array of Teams, not a Collection.
15492      *  _teams = _user.getSupervisedTeams();
15493      *  if (_teams.length > 0) {
15494      *      _team0Users = _teams[0].getUsers();
15495      *  }
15496      */
15497     _fakeConstuctor: function () {
15498         /* This is here to hide the real init constructor from the public docs */
15499     },
15500         
15501     /**
15502      * @private
15503      * JavaScript representation of the Finesse Users collection
15504      * object which contains a list of Users objects.
15505      *
15506 	 * @param {Object} options
15507 	 *     An object with the following properties:<ul>
15508      *         <li><b>id:</b> The id of the object being constructed</li>
15509      *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
15510      *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
15511      *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
15512      *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
15513      *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
15514      *             <li><b>status:</b> {Number} The HTTP status code returned</li>
15515      *             <li><b>content:</b> {String} Raw string of response</li>
15516      *             <li><b>object:</b> {Object} Parsed object of response</li>
15517      *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
15518      *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
15519      *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
15520      *             </ul></li>
15521      *         </ul></li>
15522      *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
15523      **/
15524 	init: function (options) {
15525 		this._super(options);
15526 	},
15527 
15528 	/**
15529      * @private
15530 	 * Gets the REST class for the current object - this is the Users class.
15531 	 */
15532 	getRestClass: function () {
15533 	    return Users;
15534 	},
15535 
15536 	/**
15537      * @private
15538 	 * Gets the REST class for the objects that make up the collection. - this
15539 	 * is the User class.
15540 	 */
15541 	getRestItemClass: function () {
15542 		return User;
15543 	},
15544 
15545 	/**
15546      * @private
15547 	 * Gets the REST type for the current object - this is a "Users".
15548 	 */
15549 	getRestType: function () {
15550 	    return "Users";
15551 	},
15552 
15553 	/**
15554      * @private
15555 	 * Gets the REST type for the objects that make up the collection - this is "User".
15556 	 */
15557 	getRestItemType: function () {
15558 	    return "User";
15559 	},
15560 
15561 	/**
15562      * @private
15563      * Gets the node path for the current object - this is the team Users node
15564      * @returns {String} The node path
15565      */
15566     getXMPPNodePath: function () {
15567 		return this.getRestUrl();
15568     },
15569 
15570     /**
15571      * @private
15572      * Overloading _doGET to reroute the GET to /Team/id from /Team/id/Users
15573      * This needs to be done because the GET /Team/id/Users API is missing
15574      * @returns {Users} This Users (collection) object to allow cascading
15575      */
15576     _doGET: function (handlers) {
15577         var _this = this;
15578         handlers = handlers || {};
15579         // Only do this for /Team/id/Users
15580         if (this._restObj && this._restObj.getRestType() === "Team") {
15581             this._restObj._doGET({
15582                 success: function (rspObj) {
15583                     // Making sure the response was a valid Team
15584                     if (_this._restObj._validate(rspObj.object)) {
15585                         // Shimmying the response to look like a Users collection by extracting it from the Team response
15586                         rspObj.object[_this.getRestType()] = rspObj.object[_this._restObj.getRestType()][_this.getRestType().toLowerCase()];
15587                         handlers.success(rspObj);
15588                     } else {
15589                         handlers.error(rspObj);
15590                     }
15591                 },
15592                 error: handlers.error
15593             });
15594             return this; // Allow cascading
15595         } else {
15596             return this._super(handlers);
15597         }
15598     },
15599 
15600 	/**
15601      * @private
15602      * Override default to indicates that the collection doesn't support making
15603 	 * requests.
15604 	 */
15605 	supportsRequests: false,
15606 
15607     /**
15608      * @private
15609      * Indicates that this collection handles the subscription for its items
15610      */
15611     handlesItemSubscription: true,
15612 	
15613     /**
15614      * @private
15615      * Override default to indicate that we need to subscribe explicitly
15616      */
15617     explicitSubscription: true
15618     
15619 	});
15620 
15621     window.finesse = window.finesse || {};
15622     window.finesse.restservices = window.finesse.restservices || {};
15623     window.finesse.restservices.Users = Users;
15624 
15625     return Users;
15626 });
15627 
15628 /**
15629  * JavaScript representation of the Finesse Team Not Ready Reason Code Assignment object.
15630  *
15631  * @requires finesse.clientservices.ClientServices
15632  * @requires Class
15633  * @requires finesse.FinesseBase
15634  * @requires finesse.restservices.RestBase
15635  */
15636 
15637 /** @private */
15638 define('restservices/TeamNotReadyReasonCode',['restservices/RestBase'], function (RestBase) {
15639     
15640     var TeamNotReadyReasonCode = RestBase.extend( {
15641 
15642         /**
15643          * @class
15644          * JavaScript representation of a Team Not Ready ReasonCode object. Also exposes
15645          * methods to operate on the object against the server.
15646          *
15647          * @param {Object} options
15648          *     An object with the following properties:<ul>
15649          *         <li><b>id:</b> The id of the object being constructed</li>
15650          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
15651          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
15652          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
15653          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
15654          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
15655          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
15656          *             <li><b>content:</b> {String} Raw string of response</li>
15657          *             <li><b>object:</b> {Object} Parsed object of response</li>
15658          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
15659          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
15660          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
15661          *             </ul></li>
15662          *         </ul></li>
15663          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
15664          * @constructs
15665          **/
15666         init: function (options) {
15667             this._super(options);
15668         },
15669     
15670         /**
15671          * @private
15672          * Gets the REST class for the current object - this is the TeamNotReadyReasonCode class.
15673          * @returns {Object} The TeamNotReadyReasonCode class.
15674          */
15675         getRestClass: function () {
15676             return TeamNotReadyReasonCode;
15677         },
15678     
15679         /**
15680          * @private
15681          * Gets the REST type for the current object - this is a "ReasonCode".
15682          * @returns {String} The ReasonCode string.
15683          */
15684         getRestType: function () {
15685             return "ReasonCode";
15686         },
15687     
15688         /**
15689          * @private
15690          * Override default to indicate that this object doesn't support making
15691          * requests.
15692          */
15693         supportsRequests: false,
15694     
15695         /**
15696          * @private
15697          * Override default to indicate that this object doesn't support subscriptions.
15698          */
15699         supportsSubscriptions: false,
15700     
15701         /**
15702          * Getter for the category.
15703          * @returns {String} The category.
15704          */
15705         getCategory: function () {
15706             this.isLoaded();
15707             return this.getData().category;
15708         },
15709     
15710         /**
15711          * Getter for the code.
15712          * @returns {String} The code.
15713          */
15714         getCode: function () {
15715             this.isLoaded();
15716             return this.getData().code;
15717         },
15718     
15719         /**
15720          * Getter for the label.
15721          * @returns {String} The label.
15722          */
15723         getLabel: function () {
15724             this.isLoaded();
15725             return this.getData().label;
15726         },
15727     
15728         /**
15729          * Getter for the forAll value.
15730          * @returns {String} The forAll.
15731          */
15732         getForAll: function () {
15733             this.isLoaded();
15734             return this.getData().forAll;
15735         },
15736     
15737         /**
15738          * Getter for the Uri value.
15739          * @returns {String} The Uri.
15740          */
15741         getUri: function () {
15742             this.isLoaded();
15743             return this.getData().uri;
15744         }
15745 
15746     });
15747     
15748     window.finesse = window.finesse || {};
15749     window.finesse.restservices = window.finesse.restservices || {};
15750     window.finesse.restservices.TeamNotReadyReasonCode = TeamNotReadyReasonCode;
15751         
15752     return TeamNotReadyReasonCode;
15753 });
15754 
15755 /**
15756 * JavaScript representation of the Finesse TeamNotReadyReasonCodes collection
15757 * object which contains a list of TeamNotReadyReasonCode objects.
15758  *
15759  * @requires finesse.clientservices.ClientServices
15760  * @requires Class
15761  * @requires finesse.FinesseBase
15762  * @requires finesse.restservices.RestBase
15763  * @requires finesse.restservices.Dialog
15764  * @requires finesse.restservices.RestCollectionBase
15765  */
15766 
15767 /** @private */
15768 define('restservices/TeamNotReadyReasonCodes',[
15769     'restservices/RestCollectionBase',
15770     'restservices/RestBase',
15771     'restservices/TeamNotReadyReasonCode'
15772 ],
15773 function (RestCollectionBase, RestBase, TeamNotReadyReasonCode) {
15774 
15775     var TeamNotReadyReasonCodes = RestCollectionBase.extend( {
15776 
15777       /**
15778        * @class
15779        * JavaScript representation of a TeamNotReadyReasonCodes collection object. Also exposes
15780        * methods to operate on the object against the server.
15781        *
15782        * @param {Object} options
15783        *     An object with the following properties:<ul>
15784        *         <li><b>id:</b> The id of the object being constructed</li>
15785        *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
15786        *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
15787        *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
15788        *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
15789        *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
15790        *             <li><b>status:</b> {Number} The HTTP status code returned</li>
15791        *             <li><b>content:</b> {String} Raw string of response</li>
15792        *             <li><b>object:</b> {Object} Parsed object of response</li>
15793        *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
15794        *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
15795        *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
15796        *             </ul></li>
15797        *         </ul></li>
15798        *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
15799        * @augments finesse.restservices.RestCollectionBase
15800        * @constructs
15801        **/
15802       init: function (options) {
15803           this._super(options);
15804       },
15805     
15806       /**
15807        * @private
15808        * Gets the REST class for the current object - this is the TeamNotReadyReasonCodes class.
15809        */
15810       getRestClass: function () {
15811           return TeamNotReadyReasonCodes;
15812       },
15813     
15814       /**
15815        * @private
15816        * Gets the REST class for the objects that make up the collection. - this
15817        * is the TeamNotReadyReasonCode class.
15818        */
15819       getRestItemClass: function () {
15820           return TeamNotReadyReasonCode;
15821       },
15822     
15823       /**
15824        * @private
15825        * Gets the REST type for the current object - this is a "ReasonCodes".
15826        */
15827       getRestType: function () {
15828           return "ReasonCodes";
15829       },
15830     
15831       /**
15832        * @private
15833        * Overrides the parent class.  Returns the url for the NotReadyReasonCodes resource
15834        */
15835       getRestUrl: function () {
15836           // return ("/finesse/api/" + this.getRestType() + "?category=NOT_READY");
15837           var restObj = this._restObj,
15838               restUrl = "";
15839           //Prepend the base REST object if one was provided.
15840           //Otherwise prepend with the default webapp name.
15841           if (restObj instanceof RestBase) {
15842               restUrl += restObj.getRestUrl();
15843           }
15844           else {
15845               restUrl += "/finesse/api";
15846           }
15847           //Append the REST type.
15848           restUrl += "/ReasonCodes?category=NOT_READY";
15849           //Append ID if it is not undefined, null, or empty.
15850           if (this._id) {
15851               restUrl += "/" + this._id;
15852           }
15853           return restUrl;
15854       },
15855     
15856       /**
15857        * @private
15858        * Gets the REST type for the objects that make up the collection - this is "ReasonCode".
15859        */
15860       getRestItemType: function () {
15861           return "ReasonCode";
15862       },
15863     
15864       /**
15865        * @private
15866        * Override default to indicates that the collection supports making
15867        * requests.
15868        */
15869       supportsRequests: true,
15870     
15871       /**
15872        * @private
15873        * Override default to indicate that this object doesn't support subscriptions.
15874        */
15875       supportsRestItemSubscriptions: false,
15876     
15877       /**
15878        * @private
15879        * Retrieve the Not Ready Reason Codes.
15880        *
15881        * @returns {TeamNotReadyReasonCodes}
15882        *     This TeamNotReadyReasonCodes object to allow cascading.
15883        */
15884       get: function () {
15885           // set loaded to false so it will rebuild the collection after the get
15886           this._loaded = false;
15887           // reset collection
15888           this._collection = {};
15889           // perform get
15890           this._synchronize();
15891           return this;
15892       },
15893     
15894       /**
15895        * @private
15896        * Set up the PutSuccessHandler for TeamNotReadyReasonCodes
15897        * @param {Object} reasonCodes
15898        * @param {String} contentBody
15899        * @param successHandler    
15900        * @return {function}
15901        */
15902       createPutSuccessHandler: function (reasonCodes, contentBody, successHandler) {
15903           return function (rsp) {
15904               // Update internal structure based on response. Here we
15905               // inject the contentBody from the PUT request into the
15906               // rsp.object element to mimic a GET as a way to take
15907               // advantage of the existing _processResponse method.
15908               rsp.object = contentBody;
15909               reasonCodes._processResponse(rsp);
15910     
15911               //Remove the injected contentBody object before cascading response
15912               rsp.object = {};
15913     
15914               //cascade response back to consumer's response handler
15915               successHandler(rsp);
15916           };
15917       },
15918     
15919       /**
15920        * @private
15921        * Perform the REST API PUT call to update the reason code assignments for the team
15922        * @param {string[]} newValues
15923        * @param handlers     
15924        */
15925       update: function (newValues, handlers) {
15926           this.isLoaded();
15927           var contentBody = {}, contentBodyInner = [], i, innerObject = {};
15928     
15929           contentBody[this.getRestType()] = {
15930           };
15931     
15932           for (i in newValues) {
15933               if (newValues.hasOwnProperty(i)) {
15934                   innerObject = {
15935                       "uri": newValues[i]
15936                   };
15937                   contentBodyInner.push(innerObject);
15938               }
15939           }
15940     
15941           contentBody[this.getRestType()] = {
15942               "ReasonCode" : contentBodyInner
15943           };
15944     
15945           // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
15946           handlers = handlers || {};
15947     
15948           this.restRequest(this.getRestUrl(), {
15949               method: 'PUT',
15950               success: this.createPutSuccessHandler(this, contentBody, handlers.success),
15951               error: handlers.error,
15952               content: contentBody
15953           });
15954     
15955           return this; // Allow cascading
15956       }
15957   });
15958   
15959     window.finesse = window.finesse || {};
15960     window.finesse.restservices = window.finesse.restservices || {};
15961     window.finesse.restservices.TeamNotReadyReasonCodes = TeamNotReadyReasonCodes;
15962     
15963   return TeamNotReadyReasonCodes;
15964 });
15965 
15966 /**
15967  * JavaScript representation of the Finesse Team Wrap Up Reason object.
15968  *
15969  * @requires finesse.clientservices.ClientServices
15970  * @requires Class
15971  * @requires finesse.FinesseBase
15972  * @requires finesse.restservices.RestBase
15973  */
15974 /** @private */
15975 define('restservices/TeamWrapUpReason',['restservices/RestBase'], function (RestBase) {
15976 
15977     var TeamWrapUpReason = RestBase.extend({
15978 
15979     /**
15980      * @class
15981      * JavaScript representation of a TeamWrapUpReason object. Also exposes
15982      * methods to operate on the object against the server.
15983      *
15984      * @param {Object} options
15985      *     An object with the following properties:<ul>
15986      *         <li><b>id:</b> The id of the object being constructed</li>
15987      *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
15988      *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
15989      *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
15990      *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
15991      *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
15992      *             <li><b>status:</b> {Number} The HTTP status code returned</li>
15993      *             <li><b>content:</b> {String} Raw string of response</li>
15994      *             <li><b>object:</b> {Object} Parsed object of response</li>
15995      *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
15996      *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
15997      *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
15998      *             </ul></li>
15999      *         </ul></li>
16000      *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
16001      * @constructs
16002      **/
16003     init: function (options) {
16004         this._super(options);
16005     },
16006 
16007     /**
16008      * @private
16009      * Gets the REST class for the current object - this is the TeamWrapUpReason class.
16010      * @returns {Object} The TeamWrapUpReason class.
16011      */
16012     getRestClass: function () {
16013         return TeamWrapUpReason;
16014     },
16015 
16016     /**
16017      * @private
16018      * Gets the REST type for the current object - this is a "WrapUpReason".
16019      * @returns {String} The WrapUpReason string.
16020      */
16021     getRestType: function () {
16022         return "WrapUpReason";
16023     },
16024 
16025     /**
16026      * @private
16027      * Override default to indicate that this object doesn't support making
16028      * requests.
16029      */
16030     supportsRequests: false,
16031 
16032     /**
16033      * @private
16034      * Override default to indicate that this object doesn't support subscriptions.
16035      */
16036     supportsSubscriptions: false,
16037 
16038     /**
16039      * Getter for the label.
16040      * @returns {String} The label.
16041      */
16042     getLabel: function () {
16043         this.isLoaded();
16044         return this.getData().label;
16045     },
16046 
16047     /**
16048      * @private
16049      * Getter for the forAll value.
16050      * @returns {Boolean} True if global
16051      */
16052     getForAll: function () {
16053         this.isLoaded();
16054         return this.getData().forAll;
16055     },
16056 
16057     /**
16058      * @private
16059      * Getter for the Uri value.
16060      * @returns {String} The Uri.
16061      */
16062     getUri: function () {
16063         this.isLoaded();
16064         return this.getData().uri;
16065     }
16066 	});
16067 
16068     window.finesse = window.finesse || {};
16069     window.finesse.restservices = window.finesse.restservices || {};
16070     window.finesse.restservices.TeamWrapUpReason = TeamWrapUpReason;
16071 
16072     return TeamWrapUpReason;
16073 });
16074 
16075 /**
16076 * JavaScript representation of the Finesse Team Wrap-Up Reasons collection
16077 * object which contains a list of Wrap-Up Reasons objects.
16078  *
16079  * @requires finesse.clientservices.ClientServices
16080  * @requires Class
16081  * @requires finesse.FinesseBase
16082  * @requires finesse.restservices.RestBase
16083  * @requires finesse.restservices.Dialog
16084  * @requires finesse.restservices.RestCollectionBase
16085  */
16086 /** @private */
16087 define('restservices/TeamWrapUpReasons',[
16088     'restservices/RestCollectionBase',
16089     'restservices/RestBase',
16090     'restservices/TeamWrapUpReason'
16091 ],
16092 function (RestCollectionBase, RestBase, TeamWrapUpReason) {
16093 
16094     var TeamWrapUpReasons = RestCollectionBase.extend({
16095 
16096     /**
16097      * @class
16098      * JavaScript representation of a TeamWrapUpReasons collection object. Also exposes
16099      * methods to operate on the object against the server.
16100      *
16101      * @param {Object} options
16102      *     An object with the following properties:<ul>
16103      *         <li><b>id:</b> The id of the object being constructed</li>
16104      *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
16105      *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
16106      *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
16107      *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
16108      *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
16109      *             <li><b>status:</b> {Number} The HTTP status code returned</li>
16110      *             <li><b>content:</b> {String} Raw string of response</li>
16111      *             <li><b>object:</b> {Object} Parsed object of response</li>
16112      *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
16113      *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
16114      *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
16115      *             </ul></li>
16116      *         </ul></li>
16117      *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
16118      * @constructs
16119      **/
16120     init: function (options) {
16121         this._super(options);
16122     },
16123 
16124     /**
16125      * @private
16126      * Gets the REST class for the current object - this is the TeamWrapUpReasons class.
16127      */
16128     getRestClass: function () {
16129         return TeamWrapUpReasons;
16130     },
16131 
16132     /**
16133      * @private
16134      * Gets the REST class for the objects that make up the collection. - this
16135      * is the TeamWrapUpReason class.
16136      */
16137     getRestItemClass: function () {
16138         return TeamWrapUpReason;
16139     },
16140 
16141     /**
16142      * @private
16143      * Gets the REST type for the current object - this is a "WrapUpReasons".
16144      */
16145     getRestType: function () {
16146         return "WrapUpReasons";
16147     },
16148 
16149     /**
16150      * @private
16151      * Gets the REST type for the objects that make up the collection - this is "WrapUpReason".
16152      */
16153     getRestItemType: function () {
16154         return "WrapUpReason";
16155     },
16156 
16157     /**
16158      * @private
16159      * Override default to indicates that the collection supports making
16160      * requests.
16161      */
16162     supportsRequests: true,
16163 
16164     /**
16165      * @private
16166      * Override default to indicate that this object doesn't support subscriptions.
16167      */
16168     supportsRestItemSubscriptions: false,
16169 
16170     /**
16171      * Retrieve the Team Wrap Up Reasons.
16172      *
16173      * @returns {finesse.restservices.TeamWrapUpReasons}
16174      *     This TeamWrapUpReasons object to allow cascading.
16175      */
16176     get: function () {
16177         // set loaded to false so it will rebuild the collection after the get
16178         this._loaded = false;
16179         // reset collection
16180         this._collection = {};
16181         // perform get
16182         this._synchronize();
16183         return this;
16184     },
16185 
16186     /**
16187      * Set up the PutSuccessHandler for TeamWrapUpReasons
16188      * @param {Object} wrapUpReasons
16189      * @param {Object} contentBody
16190      * @param successHandler
16191      * @returns response
16192      */
16193     createPutSuccessHandler: function (wrapUpReasons, contentBody, successHandler) {
16194         return function (rsp) {
16195             // Update internal structure based on response. Here we
16196             // inject the contentBody from the PUT request into the
16197             // rsp.object element to mimic a GET as a way to take
16198             // advantage of the existing _processResponse method.
16199             rsp.object = contentBody;
16200             
16201             wrapUpReasons._processResponse(rsp);
16202 
16203             //Remove the injected contentBody object before cascading response
16204             rsp.object = {};
16205 
16206             //cascade response back to consumer's response handler
16207             successHandler(rsp);
16208         };
16209     },
16210 
16211     /**    
16212      * Perform the REST API PUT call to update the reason code assignments for the team
16213      * @param {String Array} newValues
16214      * @param handlers
16215      * @returns {Object} this
16216      */
16217     update: function (newValues, handlers) {
16218         this.isLoaded();
16219         var contentBody = {}, contentBodyInner = [], i, innerObject = {};
16220 
16221         contentBody[this.getRestType()] = {
16222         };
16223 
16224         for (i in newValues) {
16225             if (newValues.hasOwnProperty(i)) {
16226                 innerObject = {
16227                     "uri": newValues[i]
16228                 };
16229                 contentBodyInner.push(innerObject);
16230             }
16231         }
16232 
16233         contentBody[this.getRestType()] = {
16234             "WrapUpReason" : contentBodyInner
16235         };
16236 
16237         // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
16238         handlers = handlers || {};
16239 
16240         this.restRequest(this.getRestUrl(), {
16241             method: 'PUT',
16242             success: this.createPutSuccessHandler(this, contentBody, handlers.success),
16243             error: handlers.error,
16244             content: contentBody
16245         });
16246 
16247         return this; // Allow cascading
16248     }
16249 	});
16250 
16251     window.finesse = window.finesse || {};
16252     window.finesse.restservices = window.finesse.restservices || {};
16253     window.finesse.restservices.TeamWrapUpReasons = TeamWrapUpReasons;
16254 
16255     return TeamWrapUpReasons;
16256 });
16257 
16258 /**
16259  * JavaScript representation of a TeamSignOutReasonCode.
16260  *
16261  * @requires finesse.clientservices.ClientServices
16262  * @requires Class
16263  * @requires finesse.FinesseBase
16264  * @requires finesse.restservices.RestBase
16265  */
16266 
16267 /** @private */
16268 define('restservices/TeamSignOutReasonCode',['restservices/RestBase'], function (RestBase) {
16269     var TeamSignOutReasonCode = RestBase.extend({
16270 
16271         /**
16272          * @class
16273          * JavaScript representation of a TeamSignOutReasonCode object. Also exposes
16274          * methods to operate on the object against the server.
16275          *
16276          * @param {Object} options
16277          *     An object with the following properties:<ul>
16278          *         <li><b>id:</b> The id of the object being constructed</li>
16279          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
16280          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
16281          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
16282          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
16283          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
16284          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
16285          *             <li><b>content:</b> {String} Raw string of response</li>
16286          *             <li><b>object:</b> {Object} Parsed object of response</li>
16287          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
16288          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
16289          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
16290          *             </ul></li>
16291          *         </ul></li>
16292          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
16293          * @constructs
16294          * @ignore
16295          **/
16296         init: function (options) {
16297             this._super(options);
16298         },
16299 
16300         /**
16301          * @private
16302          * Gets the REST class for the current object - this is the TeamSignOutReasonCode class.
16303          * @returns {Object} The TeamSignOutReasonCode class.
16304          */
16305         getRestClass: function () {
16306             return TeamSignOutReasonCode;
16307         },
16308 
16309         /**
16310          * @private
16311          * Gets the REST type for the current object - this is a "ReasonCode".
16312          * @returns {String} The ReasonCode string.
16313          */
16314         getRestType: function () {
16315             return "ReasonCode";
16316         },
16317 
16318         /**
16319          * @private
16320          * Override default to indicate that this object doesn't support making
16321          * requests.
16322          */
16323         supportsRequests: false,
16324 
16325         /**
16326          * @private
16327          * Override default to indicate that this object doesn't support subscriptions.
16328          */
16329         supportsSubscriptions: false,
16330 
16331         /**
16332          * Getter for the category.
16333          * @returns {String} The category.
16334          */
16335         getCategory: function () {
16336             this.isLoaded();
16337             return this.getData().category;
16338         },
16339 
16340         /**
16341          * Getter for the code.
16342          * @returns {String} The code.
16343          */
16344         getCode: function () {
16345             this.isLoaded();
16346             return this.getData().code;
16347         },
16348 
16349         /**
16350          * Getter for the label.
16351          * @returns {String} The label.
16352          */
16353         getLabel: function () {
16354             this.isLoaded();
16355             return this.getData().label;
16356         },
16357 
16358         /**
16359          * Getter for the forAll value.
16360          * @returns {String} The forAll.
16361          */
16362         getForAll: function () {
16363             this.isLoaded();
16364             return this.getData().forAll;
16365         },
16366 
16367         /**
16368          * Getter for the Uri value.
16369          * @returns {String} The Uri.
16370          */
16371         getUri: function () {
16372             this.isLoaded();
16373             return this.getData().uri;
16374         }
16375 
16376     });
16377 
16378     window.finesse = window.finesse || {};
16379     window.finesse.restservices = window.finesse.restservices || {};
16380     window.finesse.restservices.TeamSignOutReasonCode = TeamSignOutReasonCode;
16381     
16382     return TeamSignOutReasonCode;
16383 });
16384 
16385 /**
16386 * JavaScript representation of the TeamSignOutReasonCodes collection
16387 * object which contains a list of TeamSignOutReasonCode objects.
16388  *
16389  * @requires finesse.clientservices.ClientServices
16390  * @requires Class
16391  * @requires finesse.FinesseBase
16392  * @requires finesse.restservices.RestBase
16393  * @requires finesse.restservices.Dialog
16394  * @requires finesse.restservices.RestCollectionBase
16395  */
16396 
16397 /** @private */
16398 define('restservices/TeamSignOutReasonCodes',[
16399     'restservices/RestCollectionBase',
16400     'restservices/RestBase',
16401     'restservices/TeamSignOutReasonCode'
16402 ],
16403 function (RestCollectionBase, RestBase, TeamSignOutReasonCode) {
16404     
16405     var TeamSignOutReasonCodes = RestCollectionBase.extend({
16406         /**
16407          * @class
16408          * JavaScript representation of a TeamSignOutReasonCodes collection object. Also exposes
16409          * methods to operate on the object against the server.
16410          *
16411          * @param {Object} options
16412          *     An object with the following properties:<ul>
16413          *         <li><b>id:</b> The id of the object being constructed</li>
16414          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
16415          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
16416          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
16417          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
16418          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
16419          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
16420          *             <li><b>content:</b> {String} Raw string of response</li>
16421          *             <li><b>object:</b> {Object} Parsed object of response</li>
16422          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
16423          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
16424          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
16425          *             </ul></li>
16426          *         </ul></li>
16427          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
16428          * @constructs
16429          **/
16430         init: function (options) {
16431             this._super(options);
16432         },
16433 
16434         /**
16435          * @private
16436          * Gets the REST class for the current object - this is the TeamSignOutReasonCodes class.
16437          */
16438         getRestClass: function () {
16439             return TeamSignOutReasonCodes;
16440         },
16441 
16442         /**
16443          * @private
16444          * Gets the REST class for the objects that make up the collection. - this
16445          * is the TeamSignOutReasonCode class.
16446          */
16447         getRestItemClass: function () {
16448             return TeamSignOutReasonCode;
16449         },
16450 
16451         /**
16452          * @private
16453          * Gets the REST type for the current object - this is a "ReasonCodes".
16454          */
16455         getRestType: function () {
16456             return "ReasonCodes";
16457         },
16458 
16459         /**
16460          * Overrides the parent class.  Returns the url for the SignOutReasonCodes resource
16461          */
16462         getRestUrl: function () {
16463             var restObj = this._restObj, restUrl = "";
16464 
16465             //Prepend the base REST object if one was provided.
16466             //Otherwise prepend with the default webapp name.
16467             if (restObj instanceof RestBase) {
16468                 restUrl += restObj.getRestUrl();
16469             } else {
16470                 restUrl += "/finesse/api";
16471             }
16472             //Append the REST type.
16473             restUrl += "/ReasonCodes?category=LOGOUT";
16474             //Append ID if it is not undefined, null, or empty.
16475             if (this._id) {
16476                 restUrl += "/" + this._id;
16477             }
16478             return restUrl;
16479         },
16480 
16481         /**
16482          * @private
16483          * Gets the REST type for the objects that make up the collection - this is "ReasonCode".
16484          */
16485         getRestItemType: function () {
16486             return "ReasonCode";
16487         },
16488 
16489         /**
16490          * @private
16491          * Override default to indicates that the collection supports making requests.
16492          */
16493         supportsRequests: true,
16494 
16495         /**
16496          * @private
16497          * Override default to indicates that the collection does not subscribe to its objects.
16498          */
16499         supportsRestItemSubscriptions: false,
16500 
16501         /**
16502          * Retrieve the Sign Out Reason Codes.
16503          *
16504          * @returns {finesse.restservices.TeamSignOutReasonCodes}
16505          *     This TeamSignOutReasonCodes object to allow cascading.
16506          */
16507         get: function () {
16508             // set loaded to false so it will rebuild the collection after the get
16509             this._loaded = false;
16510             // reset collection
16511             this._collection = {};
16512             // perform get
16513             this._synchronize();
16514             return this;
16515         },
16516 
16517         /* We only use PUT and GET on Reason Code team assignments
16518          * @param {Object} contact
16519          * @param {Object} contentBody
16520          * @param {Function} successHandler
16521          */
16522         createPutSuccessHandler: function (contact, contentBody, successHandler) {
16523             return function (rsp) {
16524                 // Update internal structure based on response. Here we
16525                 // inject the contentBody from the PUT request into the
16526                 // rsp.object element to mimic a GET as a way to take
16527                 // advantage of the existing _processResponse method.
16528                 rsp.object = contentBody;
16529                 contact._processResponse(rsp);
16530 
16531                 //Remove the injected contentBody object before cascading response
16532                 rsp.object = {};
16533 
16534                 //cascade response back to consumer's response handler
16535                 successHandler(rsp);
16536             };
16537         },
16538 
16539         /**
16540          * Update - This should be all that is needed.
16541          * @param {Object} newValues
16542          * @param {Object} handlers
16543          * @returns {finesse.restservices.TeamSignOutReasonCodes}
16544          *     This TeamSignOutReasonCodes object to allow cascading.
16545          */
16546         update: function (newValues, handlers) {
16547             this.isLoaded();
16548             var contentBody = {}, contentBodyInner = [], i, innerObject = {};
16549 
16550             contentBody[this.getRestType()] = {
16551             };
16552 
16553             for (i in newValues) {
16554                 if (newValues.hasOwnProperty(i)) {
16555                     innerObject = {
16556                         "uri": newValues[i]
16557                     };
16558                     contentBodyInner.push(innerObject);
16559                 }
16560             }
16561 
16562             contentBody[this.getRestType()] = {
16563                 "ReasonCode" : contentBodyInner
16564             };
16565 
16566             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
16567             handlers = handlers || {};
16568 
16569             this.restRequest(this.getRestUrl(), {
16570                 method: 'PUT',
16571                 success: this.createPutSuccessHandler(this, contentBody, handlers.success),
16572                 error: handlers.error,
16573                 content: contentBody
16574             });
16575 
16576             return this; // Allow cascading
16577         }
16578 
16579     });
16580     
16581     window.finesse = window.finesse || {};
16582     window.finesse.restservices = window.finesse.restservices || {};
16583     window.finesse.restservices.TeamSignOutReasonCodes = TeamSignOutReasonCodes;
16584     
16585     return TeamSignOutReasonCodes;
16586 });
16587 
16588 /**
16589  * JavaScript representation of the Finesse PhoneBook Assignment object.
16590  *
16591  * @requires finesse.clientservices.ClientServices
16592  * @requires Class
16593  * @requires finesse.FinesseBase
16594  * @requires finesse.restservices.RestBase
16595  */
16596 
16597 /**
16598  * The following comment prevents JSLint errors concerning undefined global variables.
16599  * It tells JSLint that these identifiers are defined elsewhere.
16600  */
16601 /*jslint bitwise:true, browser:true, nomen:true, regexp:true, sloppy:true, white:true */
16602 
16603 /** The following comment is to prevent jslint errors about 
16604  * using variables before they are defined.
16605  */
16606 /*global $, jQuery, Handlebars, dojox, dojo, finesse */
16607 
16608 /** @private */
16609 define('restservices/TeamPhoneBook',['restservices/RestBase'], function (RestBase) {
16610     var TeamPhoneBook = RestBase.extend({
16611 
16612         /**
16613          * @class
16614          * JavaScript representation of a PhoneBook object. Also exposes
16615          * methods to operate on the object against the server.
16616          *
16617          * @param {Object} options
16618          *     An object with the following properties:<ul>
16619          *         <li><b>id:</b> The id of the object being constructed</li>
16620          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
16621          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
16622          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
16623          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
16624          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
16625          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
16626          *             <li><b>content:</b> {String} Raw string of response</li>
16627          *             <li><b>object:</b> {Object} Parsed object of response</li>
16628          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
16629          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
16630          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
16631          *             </ul></li>
16632          *         </ul></li>
16633          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
16634          * @constructs
16635          **/
16636         init: function (options) {
16637             this._super(options);
16638         },
16639 
16640         /**
16641          * @private
16642          * Gets the REST class for the current object - this is the PhoneBooks class.
16643          * @returns {Object} The PhoneBooks class.
16644          */
16645         getRestClass: function () {
16646             return TeamPhoneBook;
16647         },
16648 
16649         /**
16650          * @private
16651          * Gets the REST type for the current object - this is a "PhoneBook".
16652          * @returns {String} The PhoneBook string.
16653          */
16654         getRestType: function () {
16655             return "PhoneBook";
16656         },
16657 
16658         /**
16659          * @private
16660          * Override default to indicate that this object doesn't support making
16661          * requests.
16662          */
16663         supportsRequests: false,
16664 
16665         /**
16666          * @private
16667          * Override default to indicate that this object doesn't support subscriptions.
16668          */
16669         supportsSubscriptions: false,
16670 
16671         /**
16672          * Getter for the name.
16673          * @returns {String} The name.
16674          */
16675         getName: function () {
16676             this.isLoaded();
16677             return this.getData().name;
16678         },
16679 
16680         /**
16681          * Getter for the Uri value.
16682          * @returns {String} The Uri.
16683          */
16684         getUri: function () {
16685             this.isLoaded();
16686             return this.getData().uri;
16687         }
16688 
16689     });
16690 
16691     window.finesse = window.finesse || {};
16692     window.finesse.restservices = window.finesse.restservices || {};
16693     window.finesse.restservices.TeamPhoneBook = TeamPhoneBook;
16694     
16695     return TeamPhoneBook;
16696 });
16697 
16698 /**
16699 * JavaScript representation of the Finesse PhoneBook Assignments collection
16700 * object which contains a list of Not Ready Reason Codes objects.
16701  *
16702  * @requires finesse.clientservices.ClientServices
16703  * @requires Class
16704  * @requires finesse.FinesseBase
16705  * @requires finesse.restservices.RestBase
16706  * @requires finesse.restservices.Dialog
16707  * @requires finesse.restservices.RestCollectionBase
16708  */
16709 
16710 /**
16711  * The following comment prevents JSLint errors concerning undefined global variables.
16712  * It tells JSLint that these identifiers are defined elsewhere.
16713  */
16714 /*jslint bitwise:true, browser:true, nomen:true, regexp:true, sloppy:true, white:true */
16715 
16716 /** The following comment is to prevent jslint errors about 
16717  * using variables before they are defined.
16718  */
16719 /*global $, jQuery, Handlebars, dojox, dojo, finesse */
16720 
16721 /** @private */
16722 define('restservices/TeamPhoneBooks',[
16723     'restservices/RestCollectionBase',
16724     'restservices/RestBase',
16725     'restservices/TeamPhoneBook'
16726 ],
16727 function (RestCollectionBase, RestBase, TeamPhoneBook) {
16728     var TeamPhoneBooks = RestCollectionBase.extend({
16729         
16730         /**
16731          * @class
16732          * JavaScript representation of a TeamPhoneBooks collection object. Also exposes
16733          * methods to operate on the object against the server.
16734          *
16735          * @param {Object} options
16736          *     An object with the following properties:<ul>
16737          *         <li><b>id:</b> The id of the object being constructed</li>
16738          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
16739          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
16740          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
16741          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
16742          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
16743          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
16744          *             <li><b>content:</b> {String} Raw string of response</li>
16745          *             <li><b>object:</b> {Object} Parsed object of response</li>
16746          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
16747          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
16748          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
16749          *             </ul></li>
16750          *         </ul></li>
16751          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
16752          * @constructs
16753          **/
16754         init: function (options) {
16755             this._super(options);           
16756         },
16757 
16758         /**
16759          * @private
16760          * Gets the REST class for the current object - this is the TeamPhoneBooks class.
16761          */
16762         getRestClass: function () {
16763             return TeamPhoneBooks;
16764         },
16765 
16766         /**
16767          * @private
16768          * Gets the REST class for the objects that make up the collection. - this
16769          * is the TeamPhoneBooks class.
16770          */
16771         getRestItemClass: function () {
16772             return TeamPhoneBook;
16773         },
16774 
16775         /**
16776          * @private
16777          * Gets the REST type for the current object - this is a "ReasonCodes".
16778          */
16779         getRestType: function () {
16780             return "PhoneBooks";
16781         },
16782         
16783         /**
16784          * Overrides the parent class.  Returns the url for the PhoneBooks resource
16785          */
16786         getRestUrl: function () {
16787             // return ("/finesse/api/" + this.getRestType() + "?category=NOT_READY");
16788             var restObj = this._restObj,
16789             restUrl = "";
16790             //Prepend the base REST object if one was provided.
16791             if (restObj instanceof RestBase) {
16792                 restUrl += restObj.getRestUrl();
16793             }
16794             //Otherwise prepend with the default webapp name.
16795             else {
16796                 restUrl += "/finesse/api";
16797             }
16798             //Append the REST type.
16799             restUrl += "/PhoneBooks";
16800             //Append ID if it is not undefined, null, or empty.
16801             if (this._id) {
16802                 restUrl += "/" + this._id;
16803             }
16804             return restUrl;        
16805         },
16806         
16807         /**
16808          * @private
16809          * Gets the REST type for the objects that make up the collection - this is "ReasonCode".
16810          */
16811         getRestItemType: function () {
16812             return "PhoneBook";
16813         },
16814 
16815         /**
16816          * @private
16817          * Override default to indicates that the collection supports making
16818          * requests.
16819          */
16820         supportsRequests: true,
16821 
16822         /**
16823          * @private
16824          * Override default to indicates that the collection subscribes to its objects.
16825          */
16826         supportsRestItemSubscriptions: false,
16827         
16828         /**
16829          * Retrieve the Not Ready Reason Codes.
16830          *
16831          * @returns {finesse.restservices.TeamPhoneBooks}
16832          *     This TeamPhoneBooks object to allow cascading.
16833          */
16834         get: function () {
16835             // set loaded to false so it will rebuild the collection after the get
16836             /** @private */
16837             this._loaded = false;
16838             // reset collection
16839             /** @private */
16840             this._collection = {};
16841             // perform get
16842             this._synchronize();
16843             return this;
16844         },
16845 
16846         /* We only use PUT and GET on Reason Code team assignments 
16847          */
16848         createPutSuccessHandler: function(contact, contentBody, successHandler){
16849             return function (rsp) {
16850                 // Update internal structure based on response. Here we
16851                 // inject the contentBody from the PUT request into the
16852                 // rsp.object element to mimic a GET as a way to take
16853                 // advantage of the existing _processResponse method.
16854                 rsp.object = contentBody;
16855                 contact._processResponse(rsp);
16856 
16857                 //Remove the injected Contact object before cascading response
16858                 rsp.object = {};
16859                 
16860                 //cascade response back to consumer's response handler
16861                 successHandler(rsp);
16862             };
16863         },
16864 
16865         /**
16866          * Update - This should be all that is needed.
16867          */
16868         update: function (newValues, handlers) {
16869             this.isLoaded();
16870             var contentBody = {}, contentBodyInner = [], i, innerObject;
16871 
16872             contentBody[this.getRestType()] = {
16873             };
16874         
16875             for (i in newValues) {
16876                 if (newValues.hasOwnProperty(i)) {
16877                     innerObject = {};
16878                     innerObject = {
16879                         "uri": newValues[i]
16880                     };
16881                     contentBodyInner.push(innerObject);
16882                 }
16883             }
16884 
16885             contentBody[this.getRestType()] = {
16886                 "PhoneBook" : contentBodyInner
16887             };
16888 
16889             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
16890             handlers = handlers || {};
16891 
16892             this.restRequest(this.getRestUrl(), {
16893                 method: 'PUT',
16894                 success: this.createPutSuccessHandler(this, contentBody, handlers.success),
16895                 error: handlers.error,
16896                 content: contentBody
16897             });
16898 
16899             return this; // Allow cascading
16900         }       
16901         
16902     });
16903         
16904     window.finesse = window.finesse || {};
16905     window.finesse.restservices = window.finesse.restservices || {};
16906     window.finesse.restservices.TeamPhoneBooks = TeamPhoneBooks;
16907     
16908     return TeamPhoneBooks;
16909 });
16910 
16911 /**
16912  * JavaScript representation of the Finesse LayoutConfig object
16913  * @requires ClientServices
16914  * @requires finesse.FinesseBase
16915  * @requires finesse.restservices.RestBase
16916  */
16917 
16918 /** @private */
16919 define('restservices/LayoutConfig',['restservices/RestBase'], function (RestBase) {
16920     /** @private */
16921 	var LayoutConfig = RestBase.extend({
16922 
16923 		/**
16924 		 * @class
16925 		 * JavaScript representation of a LayoutConfig object. Also exposes methods to operate
16926 		 * on the object against the server.
16927 		 *
16928 		 * @param {String} id
16929 		 *     Not required...
16930 		 * @param {Object} callbacks
16931 		 *     An object containing callbacks for instantiation and runtime
16932 		 * @param {Function} callbacks.onLoad(this)
16933 		 *     Callback to invoke upon successful instantiation
16934 		 * @param {Function} callbacks.onLoadError(rsp)
16935 		 *     Callback to invoke on instantiation REST request error
16936 		 *     as passed by finesse.clientservices.ClientServices.ajax()
16937 		 *     {
16938 		 *         status: {Number} The HTTP status code returned
16939 		 *         content: {String} Raw string of response
16940 		 *         object: {Object} Parsed object of response
16941 		 *         error: {Object} Wrapped exception that was caught
16942 		 *         error.errorType: {String} Type of error that was caught
16943 		 *         error.errorMessage: {String} Message associated with error
16944 		 *     }
16945 		 * @param {Function} callbacks.onChange(this)
16946 		 *     Callback to invoke upon successful update
16947 		 * @param {Function} callbacks.onError(rsp)
16948 		 *     Callback to invoke on update error (refresh or event)
16949 		 *     as passed by finesse.clientservices.ClientServices.ajax()
16950 		 *     {
16951 		 *         status: {Number} The HTTP status code returned
16952 		 *         content: {String} Raw string of response
16953 		 *         object: {Object} Parsed object of response
16954 		 *         error: {Object} Wrapped exception that was caught
16955 		 *         error.errorType: {String} Type of error that was caught
16956 		 *         error.errorMessage: {String} Message associated with error
16957 		 *     }
16958 		 *  
16959 	     * @constructs
16960 		 */
16961 		init: function (callbacks) {
16962 			this._super("", callbacks);
16963 			//when post is performed and id is empty
16964 			/*if (id === "") {
16965 				this._loaded = true;
16966 			}*/
16967 	        this._layoutxml = {};
16968 		},
16969 	
16970 		/**
16971 		 * Returns REST class of LayoutConfig object
16972 		 */
16973 		getRestClass: function () {
16974 			return LayoutConfig;
16975 		},
16976 	
16977 		/**
16978 		 * The type of this REST object is LayoutConfig
16979 		 */
16980 		getRestType: function () {
16981 			return "LayoutConfig";
16982 		},
16983 
16984 		/**
16985 		 * Gets the REST URL of this object.
16986 		 * 
16987 		 * If the parent has an id, the id is appended.
16988 		 * On occasions of POST, it will not have an id.
16989 		 */
16990 		getRestUrl: function () {
16991 			var layoutUri = "/finesse/api/" + this.getRestType() + "/default";
16992 			/*if (this._id) {
16993 				layoutUri = layoutUri + "/" + this._id;
16994 			}*/
16995 			return layoutUri;
16996 		},
16997 	
16998 		/**
16999 		 * This API does not support subscription
17000 		 */
17001 		supportsSubscriptions: false,
17002 		
17003 		keepRestResponse: true,
17004 
17005 
17006 		/**
17007 		 * Gets finesselayout.xml retrieved from the API call
17008 		 */
17009 		getLayoutxml: function () {
17010 			this.isLoaded();
17011 			var layoutxml = this.getData().layoutxml;
17012 
17013             // We need to unescape everything that is unallowed in xml so consumers don't have to deal with it (used in tandem with update())
17014             layoutxml = layoutxml.replace(/&/g,"&");
17015 
17016             return layoutxml;
17017 		},
17018 	
17019 		/**
17020 		 * Gets the type of this LayoutConfig object
17021 		 */
17022 		/*
17023 		getType: function () {
17024 			this.isLoaded();
17025 			return this.getData().type;
17026 		},*/
17027 	
17028 		/**
17029 		 * Retrieve the LayoutConfig settings.
17030 		 * If the id is not provided the API call will fail.
17031 		 * @returns {LayoutConfig}
17032 		 *     This LayoutConfig object to allow cascading.
17033 		 */
17034 		get: function () {      
17035 			this._synchronize();
17036 			return this;
17037 		},
17038 
17039 		/**
17040 		 * Closure handle updating of the internal data for the LayoutConfig object
17041 		 * upon a successful update (PUT) request before calling the intended
17042 		 * success handler provided by the consumer
17043 		 * 
17044 		 * @param {Object}
17045 		 *            layoutconfig Reference to this LayoutConfig object
17046 		 * @param {Object}
17047 		 *            LayoutConfig Object that contains the  settings to be
17048 		 *            submitted in the api request
17049 		 * @param {Function}
17050 		 *            successHandler The success handler specified by the consumer
17051 		 *            of this object
17052 		 * @returns {LayoutConfig} This LayoutConfig object to allow cascading
17053 		 */
17054 	
17055 		createPutSuccessHandler: function (layoutconfig, contentBody, successHandler) {
17056 			return function (rsp) {			
17057 				// Update internal structure based on response. Here we
17058 				// inject the contentBody from the PUT request into the
17059 				// rsp.object element to mimic a GET as a way to take
17060 				// advantage of the existing _processResponse method.
17061 				rsp.content = contentBody;
17062 				rsp.object.LayoutConfig = {};
17063 				rsp.object.LayoutConfig.finesseLayout = contentBody;
17064 				layoutconfig._processResponse(rsp);
17065 	
17066 				//Remove the injected layoutConfig object before cascading response
17067 				rsp.object.LayoutConfig = {};
17068 	
17069 				//cascade response back to consumer's response handler
17070 				successHandler(rsp);
17071 			};
17072 		},
17073 	
17074 		/**
17075 		 *  Update LayoutConfig
17076 		 * @param {Object} finesselayout
17077 		 *     The XML for FinesseLayout being stored
17078 		 * 
17079 		 * @param {Object} handlers
17080 		 *     An object containing callback handlers for the request. Optional.
17081 		 * @param {Function} options.success(rsp)
17082 		 *     A callback function to be invoked for a successful request.
17083 		 *     {
17084 		 *         status: {Number} The HTTP status code returned
17085 		 *         content: {String} Raw string of response
17086 		 *         object: {Object} Parsed object of response
17087 		 *     }
17088 		 * @param {Function} options.error(rsp)
17089 		 *     A callback function to be invoked for an unsuccessful request.
17090 		 *     {
17091 		 *         status: {Number} The HTTP status code returned
17092 		 *         content: {String} Raw string of response
17093 		 *         object: {Object} Parsed object of response (HTTP errors)
17094 		 *         error: {Object} Wrapped exception that was caught
17095 		 *         error.errorType: {String} Type of error that was caught
17096 		 *         error.errorMessage: {String} Message associated with error
17097 		 *     }
17098 		 * @returns {finesse.restservices.LayoutConfig}
17099 		 *     This LayoutConfig object to allow cascading
17100 		 */
17101 	
17102 		update: function (layoutxml, handlers) {
17103 			this.isLoaded();
17104 
17105 			
17106 			var contentBody = {}, 
17107 			//Created a regex (re) to scoop out just the gadget URL (without before and after whitespace characters)
17108 			re = /<gadget>\s*(\S+)\s*<\/gadget>/g;
17109 
17110 			// We need to escape everything that is unallowed in xml so consumers don't have to deal with it (used in tandem with getLayoutxml())
17111 			layoutxml = layoutxml.replace(/&(?!amp;)/g, "&");
17112 
17113 			//used the regex (re) to the update and save the layoutxml with the improved gadget URL (without before/after whitespace)
17114 			layoutxml = layoutxml.replace(re, "<gadget>$1</gadget>");
17115 
17116 			contentBody[this.getRestType()] = {
17117 				"layoutxml": finesse.utilities.Utilities.translateHTMLEntities(layoutxml, true)
17118 			};
17119 
17120 			// Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
17121 			handlers = handlers || {};
17122 
17123 			this.restRequest(this.getRestUrl(), {
17124 				method: 'PUT',
17125 				success: this.createPutSuccessHandler(this, layoutxml, handlers.success),
17126 				error: handlers.error,
17127 				content: contentBody
17128 			});
17129 
17130 			return this; // Allow cascading
17131 		}
17132 	
17133 		/**
17134 		 *TODO createPostSuccessHandler needs to be debugged to make it working
17135 		 * Closure handle creating new  LayoutConfig object
17136 		 * upon a successful create (POST) request before calling the intended
17137 		 * success handler provided by the consumer
17138 		 * 
17139 		 * @param {Object}
17140 		 *            layoutconfig Reference to this LayoutConfig object
17141 		 * @param {Object}
17142 		 *            LayoutConfig Object that contains the  settings to be
17143 		 *            submitted in the api request
17144 		 * @param {Function}
17145 		 *            successHandler The success handler specified by the consumer
17146 		 *            of this object
17147 		 * @returns {finesse.restservices.LayoutConfig} This LayoutConfig object to allow cascading
17148 		 */
17149 	/*
17150 		createPostSuccessHandler: function (layoutconfig, contentBody, successHandler) {
17151 			return function (rsp) {
17152 	
17153 				rsp.object = contentBody;
17154 				layoutconfig._processResponse(rsp);
17155 	
17156 				//Remove the injected layoutConfig object before cascading response
17157 				rsp.object = {};
17158 	
17159 				//cascade response back to consumer's response handler
17160 				successHandler(rsp);
17161 			};
17162 		}, */
17163 	
17164 		/**
17165 		 * TODO Method needs to be debugged to make POST working
17166 		 *  Add LayoutConfig
17167 		 * @param {Object} finesselayout
17168 		 *     The XML for FinesseLayout being stored
17169 		 * 
17170 		 * @param {Object} handlers
17171 		 *     An object containing callback handlers for the request. Optional.
17172 		 * @param {Function} options.success(rsp)
17173 		 *     A callback function to be invoked for a successful request.
17174 		 *     {
17175 		 *         status: {Number} The HTTP status code returned
17176 		 *         content: {String} Raw string of response
17177 		 *         object: {Object} Parsed object of response
17178 		 *     }
17179 		 * @param {Function} options.error(rsp)
17180 		 *     A callback function to be invoked for an unsuccessful request.
17181 		 *     {
17182 		 *         status: {Number} The HTTP status code returned
17183 		 *         content: {String} Raw string of response
17184 		 *         object: {Object} Parsed object of response (HTTP errors)
17185 		 *         error: {Object} Wrapped exception that was caught
17186 		 *         error.errorType: {String} Type of error that was caught
17187 		 *         error.errorMessage: {String} Message associated with error
17188 		 *     }
17189 		 * @returns {finesse.restservices.LayoutConfig}
17190 		 *     This LayoutConfig object to allow cascading
17191 		 */
17192 	/*
17193 		add: function (layoutxml, handlers) {
17194 			this.isLoaded();
17195 			var contentBody = {};
17196 	
17197 	
17198 			contentBody[this.getRestType()] = {
17199 					"layoutxml": layoutxml,
17200 					"type": "current"
17201 			    };
17202 	
17203 			// Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
17204 			handlers = handlers || {};
17205 	
17206 			this.restRequest(this.getRestUrl(), {
17207 				method: 'POST',
17208 				success: this.createPostSuccessHandler(this, contentBody, handlers.success),
17209 				error: handlers.error,
17210 				content: contentBody
17211 			});
17212 	
17213 			return this; // Allow cascading
17214 		} */
17215 	});
17216 	
17217 	window.finesse = window.finesse || {};
17218     window.finesse.restservices = window.finesse.restservices || {};
17219     window.finesse.restservices.LayoutConfig = LayoutConfig;
17220     
17221 	return LayoutConfig;
17222 	
17223 });
17224 
17225 /**
17226  * JavaScript representation of the Finesse LayoutConfig object for a Team.
17227  *
17228  * @requires finesse.clientservices.ClientServices
17229  * @requires Class
17230  * @requires finesse.FinesseBase
17231  * @requires finesse.restservices.RestBase
17232  * @requires finesse.utilities.Utilities
17233  * @requires finesse.restservices.LayoutConfig
17234  */
17235 
17236 /** The following comment is to prevent jslint errors about 
17237  * using variables before they are defined.
17238  */
17239 /*global Exception */
17240 
17241 /** @private */
17242 define('restservices/TeamLayoutConfig',[
17243     'restservices/RestBase',
17244     'utilities/Utilities',
17245     'restservices/LayoutConfig'
17246 ],
17247 function (RestBase, Utilities, LayoutConfig) {
17248     
17249     var TeamLayoutConfig = RestBase.extend({
17250       // Keep the restresponse so we can parse the layoutxml out of it in getLayoutXML()
17251       keepRestResponse: true,
17252     
17253       /**
17254        * @class
17255        * JavaScript representation of a LayoutConfig object for a Team. Also exposes
17256        * methods to operate on the object against the server.
17257        *
17258        * @param {Object} options
17259        *     An object with the following properties:<ul>
17260        *         <li><b>id:</b> The id of the object being constructed</li>
17261        *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
17262        *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
17263        *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
17264        *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
17265        *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
17266        *             <li><b>status:</b> {Number} The HTTP status code returned</li>
17267        *             <li><b>content:</b> {String} Raw string of response</li>
17268        *             <li><b>object:</b> {Object} Parsed object of response</li>
17269        *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
17270        *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
17271        *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
17272        *             </ul></li>
17273        *         </ul></li>
17274        *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
17275        * @constructs
17276        **/
17277       init: function (options) {
17278           this._super(options);
17279       },
17280     
17281       /**
17282        * @private
17283        * Gets the REST class for the current object - this is the LayoutConfigs class.
17284        * @returns {Object} The LayoutConfigs class.
17285        */
17286       getRestClass: function () {
17287           return TeamLayoutConfig;
17288       },
17289     
17290       /**
17291        * @private
17292        * Gets the REST type for the current object - this is a "LayoutConfig".
17293        * @returns {String} The LayoutConfig string.
17294        */
17295       getRestType: function () {
17296           return "TeamLayoutConfig";
17297       },
17298     
17299       /**
17300        * @private
17301        * Override default to indicate that this object doesn't support making
17302        * requests.
17303        */
17304       supportsRequests: false,
17305     
17306       /**
17307        * @private
17308        * Override default to indicate that this object doesn't support subscriptions.
17309        */
17310       supportsSubscriptions: false,
17311     
17312       /**
17313        * Getter for the category.
17314        * @returns {String} The category.
17315        */
17316       getLayoutXML: function () {
17317           this.isLoaded();
17318           var layoutxml = this.getData().layoutxml;
17319 
17320           // We need to unescape everything that is unallowed in xml so consumers don't have to deal with it (used in tandem with put())
17321           layoutxml = layoutxml.replace(/&/g,"&");
17322 
17323           return layoutxml;
17324       },
17325     
17326       /**
17327        * Getter for the code.
17328        * @returns {String} The code.
17329        */
17330       getUseDefault: function () {
17331           this.isLoaded();
17332           return this.getData().useDefault;
17333       },
17334       
17335       /**
17336        * Retrieve the TeamLayoutConfig.
17337        *
17338        * @returns {finesse.restservices.TeamLayoutConfig}
17339        */
17340       get: function () {
17341           // this._id is needed, but is not used in this object.. we're overriding getRestUrl anyway
17342           this._id = "0";
17343           // set loaded to false so it will rebuild the collection after the get
17344           this._loaded = false;
17345           // reset collection
17346           this._collection = {};
17347           // perform get
17348           this._synchronize();
17349           return this;
17350       },
17351     
17352       createPutSuccessHandler: function(contact, contentBody, successHandler){
17353           return function (rsp) {
17354               // Update internal structure based on response. Here we
17355               // inject the contentBody from the PUT request into the
17356               // rsp.object element to mimic a GET as a way to take
17357               // advantage of the existing _processResponse method.
17358               rsp.object = contentBody;
17359               contact._processResponse(rsp);
17360     
17361               //Remove the injected Contact object before cascading response
17362               rsp.object = {};
17363               
17364               //cascade response back to consumer's response handler
17365               successHandler(rsp);
17366           };
17367       },
17368       
17369       put: function (newValues, handlers) {
17370           // this._id is needed, but is not used in this object.. we're overriding getRestUrl anyway
17371           this._id = "0";
17372           this.isLoaded();
17373 
17374           // We need to escape everything that is unallowed in xml so consumers don't have to deal with it (used in tandem with getLayoutxml())
17375           var layoutxml = newValues.layoutXML.replace(/&(?!amp;)/g, "&"),
17376               contentBody = {}, 
17377               //Created a regex (re) to scoop out just the gadget URL (without before and after whitespace characters)
17378               re = /<gadget>\s*(\S+)\s*<\/gadget>/g;
17379 
17380           //used the regex (re) to the update and save the layoutxml with the improved gadget URL (without before/after whitespace)
17381           layoutxml = layoutxml.replace(re, "<gadget>$1</gadget>");
17382           
17383           contentBody[this.getRestType()] = {
17384               "useDefault": newValues.useDefault,
17385               // The LayoutConfig restservice javascript class only translates ampersands, so we'll do that also
17386               "layoutxml": finesse.utilities.Utilities.translateHTMLEntities(layoutxml, true)
17387           };
17388     
17389           // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
17390           handlers = handlers || {};
17391     
17392           this.restRequest(this.getRestUrl(), {
17393               method: 'PUT',
17394               success: this.createPutSuccessHandler(this, contentBody, handlers.success),
17395               error: handlers.error,
17396               content: contentBody
17397           });
17398     
17399           return this; // Allow cascading
17400       },
17401     
17402       getRestUrl: function(){
17403           // return team's url + /LayoutConfig
17404           // eg: /api/Team/1/LayoutConfig
17405           if(this._restObj === undefined){
17406               throw new Exception("TeamLayoutConfig instances must have a parent team object.");
17407           }
17408           return this._restObj.getRestUrl() + '/LayoutConfig';
17409       }
17410     
17411       });
17412         
17413     window.finesse = window.finesse || {};
17414     window.finesse.restservices = window.finesse.restservices || {};
17415     window.finesse.restservices.TeamLayoutConfig = TeamLayoutConfig;
17416       
17417     return TeamLayoutConfig;
17418 });
17419 
17420 /**
17421  * JavaScript representation of a TeamWorkflow.
17422  *
17423  * @requires finesse.clientservices.ClientServices
17424  * @requires Class
17425  * @requires finesse.FinesseBase
17426  * @requires finesse.restservices.RestBase
17427  */
17428 /** @private */
17429 define('restservices/TeamWorkflow',['restservices/RestBase'], function (RestBase) {
17430 
17431     var TeamWorkflow = RestBase.extend({
17432 
17433         /**
17434          * @class
17435          * JavaScript representation of a TeamWorkflow object. Also exposes
17436          * methods to operate on the object against the server.
17437          *
17438          * @param {Object} options
17439          *     An object with the following properties:<ul>
17440          *         <li><b>id:</b> The id of the object being constructed</li>
17441          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
17442          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
17443          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
17444          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
17445          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
17446          *             <li><b>status:</b> {Number} The HTTP status description returned</li>
17447          *             <li><b>content:</b> {String} Raw string of response</li>
17448          *             <li><b>object:</b> {Object} Parsed object of response</li>
17449          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
17450          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
17451          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
17452          *             </ul></li>
17453          *         </ul></li>
17454          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
17455          * @constructs
17456          **/
17457         init: function (options) {
17458             this._super(options);
17459         },
17460 
17461         /**
17462          * @private
17463          * Gets the REST class for the current object - this is the TeamWorkflow class.
17464          * @returns {Object} The TeamWorkflow class.
17465          */
17466         getRestClass: function () {
17467             return TeamWorkflow;
17468         },
17469 
17470         /**
17471          * @private
17472          * Gets the REST type for the current object - this is a "Workflow".
17473          * @returns {String} The Workflow string.
17474          */
17475         getRestType: function () {
17476             return "Workflow";
17477         },
17478 
17479         /**
17480          * @private
17481          * Override default to indicate that this object doesn't support making
17482          * requests.
17483          */
17484         supportsRequests: false,
17485 
17486         /**
17487          * @private
17488          * Override default to indicate that this object doesn't support subscriptions.
17489          */
17490         supportsSubscriptions: false,
17491 
17492         /**
17493          * Getter for the name.
17494          * @returns {String} The name.
17495          */
17496         getName: function () {
17497             this.isLoaded();
17498             return this.getData().name;
17499         },
17500 
17501         /**
17502          * Getter for the description.
17503          * @returns {String} The description.
17504          */
17505         getDescription: function () {
17506             this.isLoaded();
17507             return this.getData().description;
17508         },
17509 
17510         /**
17511          * Getter for the Uri value.
17512          * @returns {String} The Uri.
17513          */
17514         getUri: function () {
17515             this.isLoaded();
17516             return this.getData().uri;
17517         }
17518 
17519     });
17520     
17521 	window.finesse = window.finesse || {};
17522     window.finesse.restservices = window.finesse.restservices || {};
17523     window.finesse.restservices.TeamWorkflow = TeamWorkflow;
17524 
17525     return TeamWorkflow;
17526 });
17527 
17528 /**
17529 * JavaScript representation of the TeamWorkflows collection
17530 * object which contains a list of TeamWorkflow objects.
17531  *
17532  * @requires finesse.clientservices.ClientServices
17533  * @requires Class
17534  * @requires finesse.FinesseBase
17535  * @requires finesse.restservices.RestBase
17536  * @requires finesse.restservices.Dialog
17537  * @requires finesse.restservices.RestCollectionBase
17538  */
17539 /** @private */
17540 define('restservices/TeamWorkflows',[
17541     'restservices/RestCollectionBase',
17542     'restservices/TeamWorkflow',
17543     'restservices/RestBase'
17544 ],
17545 function (RestCollectionBase, TeamWorkflow, RestBase) {
17546 
17547     var TeamWorkflows = RestCollectionBase.extend({
17548     
17549         /**
17550          * @class
17551          * JavaScript representation of a TeamWorkflows collection object. Also exposes
17552          * methods to operate on the object against the server.
17553          *
17554          * @param {Object} options
17555          *     An object with the following properties:<ul>
17556          *         <li><b>id:</b> The id of the object being constructed</li>
17557          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
17558          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
17559          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
17560          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
17561          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
17562          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
17563          *             <li><b>content:</b> {String} Raw string of response</li>
17564          *             <li><b>object:</b> {Object} Parsed object of response</li>
17565          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
17566          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
17567          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
17568          *             </ul></li>
17569          *         </ul></li>
17570          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
17571          * @constructs
17572          **/
17573         init: function (options) {
17574             this._super(options);
17575         },
17576 
17577         /**
17578          * @private
17579          * Gets the REST class for the current object - this is the TeamWorkflows class.
17580          */
17581         getRestClass: function () {
17582             return TeamWorkflows;
17583         },
17584 
17585         /**
17586          * @private
17587          * Gets the REST class for the objects that make up the collection. - this
17588          * is the TeamWorkflow class.
17589          */
17590         getRestItemClass: function () {
17591             return TeamWorkflow;
17592         },
17593 
17594         /**
17595          * @private
17596          * Gets the REST type for the current object - this is a "Workflows".
17597          */
17598         getRestType: function () {
17599             return "Workflows";
17600         },
17601 
17602         /**
17603          * Overrides the parent class.  Returns the url for the Workflows resource
17604          */
17605         getRestUrl: function () {
17606             var restObj = this._restObj, restUrl = "";
17607 
17608             //Prepend the base REST object if one was provided.
17609             //Otherwise prepend with the default webapp name.
17610             if (restObj instanceof RestBase) {
17611                 restUrl += restObj.getRestUrl();
17612             } else {
17613                 restUrl += "/finesse/api/Team";
17614             }
17615             //Append ID if it is not undefined, null, or empty.
17616             if (this._id) {
17617                 restUrl += "/" + this._id;
17618             }
17619             //Append the REST type.
17620             restUrl += "/Workflows";
17621             
17622             return restUrl;
17623         },
17624 
17625         /**
17626          * @private
17627          * Gets the REST type for the objects that make up the collection - this is "Workflow".
17628          */
17629         getRestItemType: function () {
17630             return "Workflow";
17631         },
17632 
17633         /**
17634          * @private
17635          * Override default to indicates that the collection supports making requests.
17636          */
17637         supportsRequests: true,
17638 
17639         /**
17640          * @private
17641          * Override default to indicates that the collection does not subscribe to its objects.
17642          */
17643         supportsRestItemSubscriptions: false,
17644 
17645         /**
17646          * Retrieve the Sign Out Reason Codes.
17647          *
17648          * @returns {finesse.restservices.TeamWorkflows}
17649          *     This TeamWorkflows object to allow cascading.
17650          */
17651         get: function () {
17652             // set loaded to false so it will rebuild the collection after the get
17653             this._loaded = false;
17654             // reset collection
17655             this._collection = {};
17656             // perform get
17657             this._synchronize();
17658             return this;
17659         },
17660 
17661         /* We only use PUT and GET on Reason Code team assignments
17662          * @param {Object} contact
17663          * @param {Object} contentBody
17664          * @param {Function} successHandler
17665          */
17666         createPutSuccessHandler: function (contact, contentBody, successHandler) {
17667             return function (rsp) {
17668                 // Update internal structure based on response. Here we
17669                 // inject the contentBody from the PUT request into the
17670                 // rsp.object element to mimic a GET as a way to take
17671                 // advantage of the existing _processResponse method.
17672                 rsp.object = contentBody;
17673                 contact._processResponse(rsp);
17674 
17675                 //Remove the injected contentBody object before cascading response
17676                 rsp.object = {};
17677 
17678                 //cascade response back to consumer's response handler
17679                 successHandler(rsp);
17680             };
17681         },
17682 
17683         /**
17684          * Update - This should be all that is needed.
17685          * @param {Object} newValues
17686          * @param {Object} handlers
17687          * @returns {finesse.restservices.TeamWorkflows}
17688          *     This TeamWorkflows object to allow cascading.
17689          */
17690         update: function (newValues, handlers) {
17691             this.isLoaded();
17692             var contentBody = {}, contentBodyInner = [], i, innerObject = {};
17693 
17694             contentBody[this.getRestType()] = {
17695             };
17696 
17697             for (i in newValues) {
17698                 if (newValues.hasOwnProperty(i)) {
17699                     innerObject = {
17700                         "uri": newValues[i]
17701                     };
17702                     contentBodyInner.push(innerObject);
17703                 }
17704             }
17705 
17706             contentBody[this.getRestType()] = {
17707                 "Workflow" : contentBodyInner
17708             };
17709 
17710             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
17711             handlers = handlers || {};
17712 
17713             this.restRequest(this.getRestUrl(), {
17714                 method: 'PUT',
17715                 success: this.createPutSuccessHandler(this, contentBody, handlers.success),
17716                 error: handlers.error,
17717                 content: contentBody
17718             });
17719 
17720             return this; // Allow cascading
17721         }
17722 
17723     });
17724     
17725 	window.finesse = window.finesse || {};
17726     window.finesse.restservices = window.finesse.restservices || {};
17727     window.finesse.restservices.TeamWorkflows = TeamWorkflows;
17728     
17729     return TeamWorkflows;
17730 });
17731 
17732 /**
17733  * JavaScript representation of the Finesse Team REST object.
17734  *
17735  * @requires finesse.clientservices.ClientServices
17736  * @requires Class
17737  * @requires finesse.FinesseBase
17738  * @requires finesse.restservices.RestBase
17739  * @requires finesse.restservices.RestCollectionBase
17740  * @requires finesse.restservices.User
17741  * @requires finesse.restservices.Users
17742  */
17743 
17744 /**
17745  * The following comment prevents JSLint errors concerning undefined global variables.
17746  * It tells JSLint that these identifiers are defined elsewhere.
17747  */
17748 /*jslint bitwise:true, browser:true, nomen:true, regexp:true, sloppy:true, white:true */
17749 
17750 /** The following comment is to prevent jslint errors about 
17751  * using variables before they are defined.
17752  */
17753 /*global $, jQuery, Handlebars, dojox, dojo, finesse */
17754 
17755 /** @private */
17756 define('restservices/Team',[
17757     'restservices/RestBase',
17758     'utilities/Utilities',
17759     'restservices/Users',
17760     'restservices/TeamNotReadyReasonCodes',
17761     'restservices/TeamWrapUpReasons',
17762     'restservices/TeamSignOutReasonCodes',
17763     'restservices/TeamPhoneBooks',
17764     'restservices/TeamLayoutConfig',
17765     'restservices/TeamWorkflows'
17766 ],
17767 function (RestBase, Utilities, Users, TeamNotReadyReasonCodes, TeamWrapUpReasons, TeamSignOutReasonCodes, TeamPhoneBooks, TeamLayoutConfig, TeamWorkflows) {
17768     var Team = RestBase.extend(/** @lends finesse.restservices.Team.prototype */{
17769         
17770         _teamLayoutConfig: null,
17771 
17772         /**
17773          * @class
17774          * A Team is a set of Agent Users, typically supervised by one or more Supervisor Users.
17775          *
17776          * @augments finesse.restservices.RestBase
17777          * @see finesse.restservices.User#getSupervisedTeams
17778          * @see finesse.restservices.Users
17779          * @constructs
17780          */
17781         _fakeConstuctor: function () {
17782             /* This is here to hide the real init constructor from the public docs */
17783         },
17784         
17785         /**
17786          * @private
17787          * @class
17788          * JavaScript representation of a Team object. Also exposes methods to operate
17789          * on the object against the server.
17790          *
17791          * @param {Object} options
17792          *     An object with the following properties:<ul>
17793          *         <li><b>id:</b> The id of the object being constructed</li>
17794          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
17795          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
17796          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
17797          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
17798          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
17799          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
17800          *             <li><b>content:</b> {String} Raw string of response</li>
17801          *             <li><b>object:</b> {Object} Parsed object of response</li>
17802          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
17803          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
17804          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
17805          *             </ul></li>
17806          *         </ul></li>
17807          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
17808          **/
17809         init: function (options) {
17810             this._super(options);
17811         },
17812     
17813         /**
17814          * @private
17815          * Gets the REST class for the current object - this is the Team class.
17816          * @returns {Object} The Team constructor.
17817          */
17818         getRestClass: function () {
17819             return finesse.restesrvices.Team;
17820         },
17821     
17822         /**
17823          * @private
17824          * Gets the REST type for the current object - this is a "Team".
17825          * @returns {String} The Team string.
17826          */
17827         getRestType: function () {
17828             return "Team";
17829         },
17830     
17831         /**
17832          * @private
17833          * Override default to indicate that this object doesn't support making
17834          * requests.
17835          */
17836         supportsSubscriptions: false,
17837     
17838         /**
17839          * Getter for the team id.
17840          * @returns {String} The team id.
17841          */
17842         getId: function () {
17843             this.isLoaded();
17844             return this.getData().id;
17845         },
17846     
17847         /**
17848          * Getter for the team name.
17849          * @returns {String} The team name
17850          */
17851         getName: function () {
17852             this.isLoaded();
17853             return this.getData().name;
17854         },
17855     
17856         /**
17857          * @private
17858          * Getter for the team uri.
17859          * @returns {String} The team uri
17860          */
17861         getUri: function () {
17862             this.isLoaded();
17863             return this.getData().uri;        
17864         },
17865     
17866         /**
17867          * Constructs and returns a collection of Users.
17868          * @param {finesse.interfaces.RestObjectHandlers} [handlers] Object that sets callback handlers.
17869          * @returns {finesse.restservices.Users} Users collection of User objects.
17870          */
17871         getUsers: function (options) {
17872             this.isLoaded();
17873             options = options || {};
17874     
17875             options.parentObj = this;
17876             // We are using getData() instead of getData.Users because the superclass (RestCollectionBase)
17877             // for Users needs the "Users" key to validate the provided payload matches the class type.
17878             options.data = this.getData();
17879     
17880             return new Users(options);
17881         },
17882     
17883         /**
17884          * @private
17885          * Getter for a teamNotReadyReasonCodes collection object that is associated with Team.
17886          * @param callbacks
17887          * @returns {teamNotReadyReasonCodes}
17888          *     A teamNotReadyReasonCodes collection object.
17889          */
17890         getTeamNotReadyReasonCodes: function (callbacks) {
17891             var options = callbacks || {};
17892             options.parentObj = this;
17893             this.isLoaded();
17894     
17895             if (!this._teamNotReadyReasonCodes) {
17896                 this._teamNotReadyReasonCodes = new TeamNotReadyReasonCodes(options);
17897             }
17898     
17899             return this._teamNotReadyReasonCodes;
17900         },
17901     
17902         /**
17903          * @private
17904          * Getter for a teamWrapUpReasons collection object that is associated with Team.
17905          * @param callbacks
17906          * @returns {teamWrapUpReasons}
17907          *     A teamWrapUpReasons collection object.
17908          */
17909         getTeamWrapUpReasons: function (callbacks) {
17910             var options = callbacks || {};
17911             options.parentObj = this;
17912             this.isLoaded();
17913     
17914             if (!this._teamWrapUpReasons) {
17915                 this._teamWrapUpReasons = new TeamWrapUpReasons(options);
17916             }
17917     
17918             return this._teamWrapUpReasons;
17919         },
17920     
17921         /**
17922          * @private
17923          * Getter for a teamSignOutReasonCodes collection object that is associated with Team.
17924          * @param callbacks
17925          * @returns {teamSignOutReasonCodes}
17926          *     A teamSignOutReasonCodes collection object.
17927          */
17928     
17929         getTeamSignOutReasonCodes: function (callbacks) {
17930             var options = callbacks || {};
17931             options.parentObj = this;
17932             this.isLoaded();
17933     
17934             if (!this._teamSignOutReasonCodes) {
17935                 this._teamSignOutReasonCodes = new TeamSignOutReasonCodes(options);
17936             }
17937     
17938             return this._teamSignOutReasonCodes;
17939         },
17940     
17941         /**
17942          * @private
17943          * Getter for a teamPhoneBooks collection object that is associated with Team.
17944          * @param callbacks
17945          * @returns {teamPhoneBooks}
17946          *     A teamPhoneBooks collection object.
17947          */
17948         getTeamPhoneBooks: function (callbacks) {
17949             var options = callbacks || {};
17950             options.parentObj = this;
17951             this.isLoaded();
17952     
17953             if (!this._phonebooks) {
17954                 this._phonebooks = new TeamPhoneBooks(options);
17955             }
17956     
17957             return this._phonebooks;
17958         },
17959     
17960         /**
17961          * @private
17962          * Getter for a teamWorkflows collection object that is associated with Team.
17963          * @param callbacks
17964          * @returns {teamWorkflows}
17965          *     A teamWorkflows collection object.
17966          */
17967         getTeamWorkflows: function (callbacks) {
17968             var options = callbacks || {};
17969             options.parentObj = this;
17970             this.isLoaded();
17971     
17972             if (!this._workflows) {
17973                 this._workflows = new TeamWorkflows(options);
17974             }
17975     
17976             return this._workflows;
17977         },
17978     
17979         /**
17980          * @private
17981          * Getter for a teamLayoutConfig object that is associated with Team.
17982          * @param callbacks
17983          * @returns {teamLayoutConfig}
17984          */
17985         getTeamLayoutConfig: function (callbacks) {
17986             var options = callbacks || {};
17987             options.parentObj = this;
17988             this.isLoaded();
17989     
17990             if (this._teamLayoutConfig === null) {
17991                 this._teamLayoutConfig = new TeamLayoutConfig(options);
17992             }
17993     
17994             return this._teamLayoutConfig;
17995         }
17996     
17997     });
17998     
17999     window.finesse = window.finesse || {};
18000     window.finesse.restservices = window.finesse.restservices || {};
18001     window.finesse.restservices.Team = Team;
18002     
18003     return Team;    
18004 });
18005 
18006 /**
18007  * JavaScript representation of the Finesse Teams collection.
18008  * object which contains a list of Team objects
18009  * @requires finesse.clientservices.ClientServices
18010  * @requires Class
18011  * @requires finesse.FinesseBase
18012  * @requires finesse.restservices.RestBase
18013  * @requires finesse.restservices.RestCollectionBase
18014  */
18015 
18016 /** @private */
18017 define('restservices/Teams',[
18018     'restservices/RestCollectionBase',
18019     'restservices/Team'
18020 ],
18021 function (RestCollectionBase, Team) {
18022     /** @private */
18023     var Teams = RestCollectionBase.extend({
18024 
18025         /**
18026          * @class
18027          * JavaScript representation of a Teams collection object. Also exposes methods to operate
18028          * on the object against the server.
18029          *
18030          * @param {Object} options
18031          *     An object with the following properties:<ul>
18032          *         <li><b>id:</b> The id of the object being constructed</li>
18033          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
18034          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
18035          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
18036          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
18037          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
18038          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
18039          *             <li><b>content:</b> {String} Raw string of response</li>
18040          *             <li><b>object:</b> {Object} Parsed object of response</li>
18041          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
18042          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
18043          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
18044          *             </ul></li>
18045          *         </ul></li>
18046          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
18047          * @constructs
18048          **/
18049         init: function (options) {
18050             this._super(options);
18051         },
18052 
18053         /**
18054          * @private
18055          * Gets the REST class for the current object - this is the Teams class.
18056          * @returns {Object} The Teams constructor.
18057          */
18058         getRestClass: function () {
18059             return Teams;
18060         },
18061 
18062         /**
18063          * @private
18064          * Gets the REST class for the objects that make up the collection. - this
18065          * is the Team class.
18066          */
18067         getRestItemClass: function () {
18068             return Team;
18069         },
18070 
18071         /**
18072          * @private
18073          * Gets the REST type for the current object - this is a "Teams".
18074          * @returns {String} The Teams string.
18075          */
18076         getRestType: function () {
18077             return "Teams";
18078         },
18079         
18080         /**
18081          * @private
18082          * Gets the REST type for the objects that make up the collection - this is "Team".
18083          */
18084         getRestItemType: function () {
18085             return "Team";
18086         },
18087 
18088         /**
18089          * @private
18090          * Override default to indicates that the collection supports making
18091          * requests.
18092          */
18093         supportsRequests: true,
18094 
18095         /**
18096          * @private
18097          * Override default to indicate that this object doesn't support subscriptions.
18098          */
18099         supportsRestItemSubscriptions: false,
18100         
18101         /**
18102          * @private
18103          * Retrieve the Teams.  This call will re-query the server and refresh the collection.
18104          *
18105          * @returns {finesse.restservices.Teams}
18106          *     This Teams object to allow cascading.
18107          */
18108         get: function () {
18109             // set loaded to false so it will rebuild the collection after the get
18110             this._loaded = false;
18111             // reset collection
18112             this._collection = {};
18113             // perform get
18114             this._synchronize();
18115             return this;
18116         }
18117 
18118     });
18119 
18120     window.finesse = window.finesse || {};
18121     window.finesse.restservices = window.finesse.restservices || {};
18122     window.finesse.restservices.Teams = Teams;
18123     
18124     return Teams;
18125 });
18126 
18127 /**
18128  * JavaScript representation of the Finesse SystemInfo object
18129  *
18130  * @requires finesse.clientservices.ClientServices
18131  * @requires Class
18132  * @requires finesse.FinesseBase
18133  * @requires finesse.restservices.RestBase
18134  */
18135 
18136 /** @private */
18137 define('restservices/SystemInfo',['restservices/RestBase'], function (RestBase) {
18138     
18139     var SystemInfo = RestBase.extend(/** @lends finesse.restservices.SystemInfo.prototype */{
18140         /**
18141          * @private
18142          * Returns whether this object supports subscriptions
18143          */
18144         supportsSubscriptions: false,
18145 
18146         doNotRefresh: true,
18147       
18148         /**
18149          * @class
18150          * JavaScript representation of a SystemInfo object.
18151          * 
18152          * @augments finesse.restservices.RestBase
18153          * @see finesse.restservices.SystemInfo.Statuses
18154          * @constructs
18155          */
18156         _fakeConstuctor: function () {
18157             /* This is here to hide the real init constructor from the public docs */
18158         },
18159         
18160          /**
18161          * @private
18162          * JavaScript representation of a SystemInfo object. Also exposes methods to operate
18163          * on the object against the server.
18164          *
18165          * @param {Object} options
18166          *     An object with the following properties:<ul>
18167          *         <li><b>id:</b> The id of the object being constructed</li>
18168          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
18169          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
18170          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
18171          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
18172          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
18173          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
18174          *             <li><b>content:</b> {String} Raw string of response</li>
18175          *             <li><b>object:</b> {Object} Parsed object of response</li>
18176          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
18177          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
18178          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
18179          *             </ul></li>
18180          *         </ul></li>
18181          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
18182          **/
18183         init: function (id, callbacks, restObj)
18184         {
18185             this._super(id, callbacks, restObj);
18186         },
18187 
18188         /**
18189          * @private
18190          * Gets the REST class for the current object - this is the SystemInfo object.
18191          */
18192         getRestClass: function () {
18193             return SystemInfo;
18194         },
18195 
18196         /**
18197          * @private
18198          * Gets the REST type for the current object - this is a "SystemInfo".
18199          */
18200         getRestType: function ()
18201         {
18202             return "SystemInfo";
18203         },
18204         
18205         _validate: function (obj)
18206         {
18207             return true;
18208         },
18209         
18210         /**
18211          * Returns the status of the Finesse system.
18212          *   IN_SERVICE if the Finesse API reports that it is in service,
18213          *   OUT_OF_SERVICE otherwise.
18214          * @returns {finesse.restservices.SystemInfo.Statuses} System Status
18215          */
18216         getStatus: function () {
18217             this.isLoaded();
18218             return this.getData().status;
18219         },
18220         
18221         /**
18222          * Returns the reason due to which Finesse is OUT OF SERVICE.
18223          * It returns empty string when Finesse status is IN_SERVICE.
18224          * @returns {String} statusReason if finesse is OUT OF SERVICE , or empty string otherwise.
18225          */
18226         getStatusReason: function () {
18227             this.isLoaded();
18228             return this.getData().statusReason;
18229         },
18230         
18231         /**
18232          * Returns the current timestamp from this SystemInfo object.
18233          *   This is used to calculate time drift delta between server and client.
18234          *  @returns {String} Time (GMT): yyyy-MM-dd'T'HH:mm:ss'Z'
18235          */
18236         getCurrentTimestamp: function () {
18237             this.isLoaded();
18238             return this.getData().currentTimestamp;
18239         },
18240         
18241         /**
18242          * Getter for the xmpp domain of the system.
18243          * @returns {String} The xmpp domain corresponding to this SystemInfo object.
18244          */
18245         getXmppDomain: function () {
18246             this.isLoaded();
18247             return this.getData().xmppDomain;
18248         },
18249         
18250         /**
18251          * Getter for the xmpp pubsub domain of the system.
18252          * @returns {String} The xmpp pubsub domain corresponding to this SystemInfo object.
18253          */
18254         getXmppPubSubDomain: function () {
18255             this.isLoaded();
18256             return this.getData().xmppPubSubDomain;
18257         },
18258 
18259         /**
18260          * Getter for the deployment type (UCCE or UCCX).
18261          * @returns {String} "UCCE" or "UCCX"
18262          */ 
18263         getDeploymentType: function () {
18264             this.isLoaded();
18265             return this.getData().deploymentType;
18266         },
18267 
18268         /**
18269          * Returns whether this is a single node deployment or not by checking for the existence of the secondary node in SystemInfo.
18270          * @returns {Boolean} True for single node deployments, false otherwise.
18271          */ 
18272         isSingleNode: function () {
18273             var secondary = this.getData().secondaryNode;
18274             if (secondary && secondary.host) {
18275                 return false;
18276             }
18277             return true;
18278         },
18279 
18280         /**
18281          * Checks all arguments against the primary and secondary hosts (FQDN) and returns the match.
18282          * This is useful for getting the FQDN of the current Finesse server.
18283          * @param {String} ...arguments[]... - any number of arguments to match against
18284          * @returns {String} FQDN (if properly configured) of the matched host of the primary or secondary node, or undefined if no match is found.
18285          */ 
18286         getThisHost: function () {
18287             var i,
18288             primary = this.getData().primaryNode,
18289             secondary = this.getData().secondaryNode;
18290 
18291             for (i = 0; (i < arguments.length); i = i + 1) {
18292                 if (primary && arguments[i] === primary.host) {
18293                     return primary.host;
18294                 } else if (secondary && arguments[i] === secondary.host) {
18295                     return secondary.host;
18296                 }
18297             }
18298         },
18299 
18300         /**
18301          * Checks all arguments against the primary and secondary hosts (FQDN) and returns the other node.
18302          * This is useful for getting the FQDN of the other Finesse server, i.e. for failover purposes.
18303          * @param {String} arguments - any number of arguments to match against
18304          * @returns {String} FQDN (if properly configured) of the alternate node, defaults to primary if no match is found, undefined for single node deployments.
18305          */ 
18306         getAlternateHost: function () {
18307             var i,
18308             isPrimary = false,
18309             primary = this.getData().primaryNode,
18310             secondary = this.getData().secondaryNode,
18311             xmppDomain = this.getData().xmppDomain,
18312             alternateHost;
18313 
18314             if (primary && primary.host) {
18315                     if (xmppDomain === primary.host) {
18316                         isPrimary = true;
18317                     }
18318                 if (secondary && secondary.host) {
18319                     if (isPrimary) {
18320                         return secondary.host;
18321                     }
18322                     return primary.host;
18323                 }
18324             }
18325         },
18326         
18327         /**
18328          * Gets the peripheral ID that Finesse is connected to. The peripheral
18329          * ID is the ID of the PG Routing Client (PIM).
18330          * 
18331          * @returns {String} The peripheral Id if UCCE, or empty string otherwise.
18332          */
18333         getPeripheralId : function () {
18334              this.isLoaded();
18335              
18336              var peripheralId = this.getData().peripheralId;
18337              if (peripheralId === null) {
18338                   return "";
18339              } else {
18340                   return this.getData().peripheralId;
18341              }
18342         },
18343 
18344          /**
18345          * Gets the license. Only apply to UCCX.
18346          * 
18347          * @returns {String} The license if UCCX, or empty string otherwise.
18348          */
18349         getlicense : function () {
18350              this.isLoaded();
18351              return this.getData().license || "";
18352         },
18353         
18354         /**
18355          * Gets the systemAuthMode for the current deployment
18356          * 
18357          * @returns {String} The System auth mode for current deployment
18358          */
18359         getSystemAuthMode : function() {
18360             this.isLoaded();
18361             return this.getData().systemAuthMode;
18362         }
18363     });
18364     
18365     SystemInfo.Statuses = /** @lends finesse.restservices.SystemInfo.Statuses.prototype */ { 
18366         /** 
18367          * Finesse is in service. 
18368          */
18369         IN_SERVICE: "IN_SERVICE",
18370         /** 
18371          * Finesse is not in service. 
18372          */
18373         OUT_OF_SERVICE: "OUT_OF_SERVICE",
18374         /**
18375          * @class SystemInfo status values.
18376          * @constructs
18377          */
18378         _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
18379 
18380     };
18381     
18382     window.finesse = window.finesse || {};
18383     window.finesse.restservices = window.finesse.restservices || {};
18384     window.finesse.restservices.SystemInfo = SystemInfo;
18385     
18386     return SystemInfo;
18387 });
18388 
18389 define('restservices/DialogLogoutActions',[], function ()
18390 {
18391     var DialogLogoutActions = /** @lends finesse.restservices.DialogLogoutActions.prototype */ {
18392 
18393         /**
18394          * Set this action to close active dialogs when the agent logs out.
18395          */
18396         CLOSE: "CLOSE",
18397 
18398         /**
18399          * Set this action to transfer active dialogs when the agent logs out.
18400          */
18401         TRANSFER: "TRANSFER",
18402 
18403         /**
18404          * @class Actions used to handle tasks that are associated with a given media at logout time.
18405          *
18406          * @constructs
18407          */
18408         _fakeConstructor: function ()
18409         {
18410         }, // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
18411 
18412         /**
18413          * Is the given action a valid dialog logout action.
18414          *
18415          * @param {String} action the action to evaluate
18416          * @returns {Boolean} true if the action is valid; false otherwise
18417          */
18418         isValidAction: function(action)
18419         {
18420             if ( !action )
18421             {
18422                 return false;
18423             }
18424 
18425             return DialogLogoutActions.hasOwnProperty(action.toUpperCase());
18426         }
18427     };
18428 
18429     window.finesse = window.finesse || {};
18430     window.finesse.restservices = window.finesse.restservices || {};
18431     window.finesse.restservices.DialogLogoutActions = DialogLogoutActions;
18432 
18433     return DialogLogoutActions;
18434 });
18435 define('restservices/InterruptActions',[], function ()
18436 {
18437     var InterruptActions = /** @lends finesse.restservices.InterruptActions.prototype */
18438     {
18439         /**
18440          * The interrupt will be accepted and the agent will not work on dialogs in this media until the media is no longer interrupted.
18441          */
18442         ACCEPT: "ACCEPT",
18443 
18444         /**
18445          * the interrupt will be ignored and the agent is allowed to work on dialogs while the media is interrupted.
18446          */
18447         IGNORE: "IGNORE",
18448 
18449         /**
18450          * @class
18451          *
18452          * The action to be taken in the event this media is interrupted. The action will be one of the following:<ul>
18453          *     <li><b>ACCEPT:</b> the interrupt will be accepted and the agent will not work on dialogs in this media
18454          *     until the media is no longer interrupted.</li>
18455          *     <li><b>IGNORE:</b> the interrupt will be ignored and the agent is allowed to work on dialogs while the
18456          *     media is interrupted.</li></ul>
18457          *
18458          * @constructs
18459          */
18460         _fakeConstructor: function ()
18461         {
18462         }, // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
18463 
18464         /**
18465          * Is the given action a valid dialog logout action.
18466          *
18467          * @param {String} action the action to evaluate
18468          * @returns {Boolean} true if the action is valid; false otherwise
18469          */
18470         isValidAction: function(action)
18471         {
18472             if ( !action )
18473             {
18474                 return false;
18475             }
18476 
18477             return InterruptActions.hasOwnProperty(action.toUpperCase());
18478         }
18479     };
18480 
18481     window.finesse = window.finesse || {};
18482     window.finesse.restservices = window.finesse.restservices || {};
18483     window.finesse.restservices.InterruptActions = InterruptActions;
18484 
18485     return InterruptActions;
18486 });
18487 /**
18488  * JavaScript representation of the ReasonCode lookup object.
18489  * This has got reason code related APIs like looking up a reason code with
18490  * its code value and category.
18491  *
18492  */
18493 
18494 /** @private */
18495 define('restservices/ReasonCodeLookup',['restservices/RestBase', 'utilities/Utilities'], function (RestBase, Utilities) {
18496     
18497     var ReasonCodeLookup = RestBase.extend(/** @lends finesse.restservices.ReasonCodeLookup.prototype */{
18498         /**
18499          * @private
18500          * Returns whether this object supports subscriptions
18501          */
18502         supportsSubscriptions: false,
18503 
18504         doNotRefresh: true,
18505         
18506         autoSubscribe: false,
18507         
18508         supportsRequests: false,
18509       
18510         /**
18511          * @class
18512          * JavaScript representation of a ReasonCodeLookup object.
18513          * 
18514          * @constructs
18515          */
18516         _fakeConstuctor: function () {
18517             /* This is here to hide the real init constructor from the public docs */
18518         },
18519         
18520          /**
18521          * @private
18522          * JavaScript representation of a ReasonCodeLookup object. Also exposes methods to operate
18523          * on the object against the server.
18524          *
18525          * @param {Object} options
18526          *     An object with the following properties:<ul>
18527          *         <li><b>id:</b> The id of the object being constructed</li>
18528          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
18529          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
18530          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
18531          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
18532          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
18533          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
18534          *             <li><b>content:</b> {String} Raw string of response</li>
18535          *             <li><b>object:</b> {Object} Parsed object of response</li>
18536          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
18537          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
18538          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
18539          *             </ul></li>
18540          *         </ul></li>
18541          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
18542          **/
18543         init: function (options){
18544             this._super(options);
18545         },
18546         
18547         /**
18548          * Do get disabled.
18549          */
18550         doGet: function(handlers) {
18551         	return;
18552         },
18553 
18554         /**
18555          * @private
18556          * Gets the REST class for the current object - this is the ReasonCodeLookup object.
18557          */
18558         getRestClass: function () {
18559             return ReasonCodeLookup;
18560         },
18561 
18562         /**
18563          * @private
18564          * Gets the REST type for the current object - this is a "ReasonCodeLookup".
18565          */
18566         getRestType: function () {
18567             return "ReasonCode";
18568         },
18569         
18570         
18571         /**
18572          * @private
18573          * Parses a uriString to retrieve the id portion
18574          * @param {String} uriString
18575          * @return {String} id
18576          */
18577         _parseIdFromUriString : function (uriString) {
18578             return Utilities.getId(uriString);
18579         },
18580         
18581         /**
18582          * Performs a GET against the Finesse server looking up the reason code 
18583          * with its reason code value, and category specified.
18584          * Note that there is no return value; use the success handler to process a
18585          * valid return.
18586          * 
18587          * @param {finesse.interfaces.RequestHandlers} handlers
18588          *     An object containing the handlers for the request
18589          * @param {String} reasonCodeValue The code for the reason code to lookup
18590          * @param {String} reasonCodeCategory The category for the reason code to lookup
18591          * 
18592          */
18593         lookupReasonCode : function (handlers, reasonCodeValue, reasonCodeCategory) {
18594             var self = this, contentBody, reasonCode, url;
18595             contentBody = {};
18596             
18597             url = this.getRestUrl();
18598             url = url + "?category=" + reasonCodeCategory + "&code=" + reasonCodeValue;
18599             this.restRequest(url, {
18600                 method: 'GET',
18601                 success: function (rsp) {
18602                     reasonCode = {
18603                         uri: rsp.object.ReasonCode.uri,
18604                         label: rsp.object.ReasonCode.label,
18605                         id: self._parseIdFromUriString(rsp.object.ReasonCode.uri)
18606                     };
18607                     handlers.success(reasonCode);
18608                 },
18609                 error: function (rsp) {
18610                     handlers.error(rsp);
18611                 },
18612                 content: contentBody
18613             });
18614         },
18615         
18616     });
18617     
18618         
18619     window.finesse = window.finesse || {};
18620     window.finesse.restservices = window.finesse.restservices || {};
18621     window.finesse.restservices.ReasonCodeLookup = ReasonCodeLookup;
18622     
18623     return ReasonCodeLookup;
18624 });
18625 
18626 /**
18627  * Provides standard way resolve message keys with substitution
18628  *
18629  * @requires finesse.container.I18n or gadgets.Prefs
18630  */
18631 
18632 // Add Utilities to the finesse.utilities namespace
18633 define('utilities/I18n',['utilities/Utilities'], function (Utilities) {
18634     var I18n = (function () {
18635 
18636         /**
18637          * Shortcut to finesse.container.I18n.getMsg or gadgets.Prefs.getMsg
18638          * @private
18639          */
18640         var _getMsg;
18641 
18642         return {
18643             /**
18644              * Provides a message resolver for this utility singleton.
18645              * @param {Function} getMsg
18646              *     A function that returns a string given a message key.
18647              *     If the key is not found, this function must return 
18648              *     something that tests false (i.e. undefined or "").
18649              */
18650             setGetter : function (getMsg) {
18651                 _getMsg = getMsg;
18652             },
18653 
18654             /**
18655              * Resolves the given message key, also performing substitution.
18656              * This generic utility will use a custom function to resolve the key
18657              * provided by finesse.utilities.I18n.setGetter. Otherwise, it will 
18658              * discover either finesse.container.I18n.getMsg or gadgets.Prefs.getMsg
18659              * upon the first invocation and store that reference for efficiency.
18660              * 
18661              * Since this will construct a new gadgets.Prefs object, it is recommended
18662              * for gadgets to explicitly provide the setter to prevent duplicate
18663              * gadgets.Prefs objects. This does not apply if your gadget does not need
18664              * access to gadgets.Prefs other than getMsg. 
18665              * 
18666              * @param {String} key
18667              *     The key to lookup
18668              * @param {String} arguments
18669              *     Arguments for substitution
18670              * @returns {String/Function}
18671              *     The resolved string if successful, otherwise a function that returns
18672              *     a '???' string that can also be casted into a string.
18673              */
18674             getString : function (key) {
18675                 var prefs, i, retStr, noMsg, getFailed = "", args;
18676                 if (!_getMsg) {
18677                     if (finesse.container && finesse.container.I18n) {
18678                         _getMsg = finesse.container.I18n.getMsg;
18679                     } else if (gadgets) {
18680                         prefs = new gadgets.Prefs();
18681                         _getMsg = prefs.getMsg;
18682                     }
18683                 }
18684                 
18685                 try {
18686                     retStr = _getMsg(key);
18687                 } catch (e) {
18688                     getFailed = "finesse.utilities.I18n.getString(): invalid _getMsg";
18689                 }
18690 
18691                 if (retStr) {
18692                     // Lookup was successful, perform substitution (if any)
18693                     args = [ retStr ];
18694                     for (i = 1; i < arguments.length; i += 1) {
18695                         args.push(arguments[i]);
18696                     }
18697                     return Utilities.formatString.apply(this, args);
18698                 }
18699                 // We want a function because jQuery.html() and jQuery.text() is smart enough to invoke it.
18700                 /** @private */
18701                 noMsg = function () {
18702                     return "???" + key + "???" + getFailed;
18703                 };
18704                 // We overload the toString() of this "function" to allow JavaScript to cast it into a string
18705                 // For example, var myMsg = "something " + finesse.utilities.I18n.getMsg("unresolvable.key");
18706                 /** @private */
18707                 noMsg.toString = function () {
18708                     return "???" + key + "???" + getFailed;
18709                 };
18710                 return noMsg;
18711 
18712             }
18713         };
18714     }());
18715     
18716     window.finesse = window.finesse || {};
18717     window.finesse.utilities = window.finesse.utilities || {};
18718     window.finesse.utilities.I18n = I18n;
18719 
18720     return I18n;
18721 });
18722 
18723 /**
18724  * Logging.js: provides simple logging for clients to use and overrides synchronous native methods: alert(), confirm(), and prompt().
18725  * 
18726  * On Firefox, it will hook into console for logging.  On IE, it will log to the status bar. 
18727  */
18728 // Add Utilities to the finesse.utilities namespace
18729 define('utilities/Logger',[], function () {
18730     var Logger = (function () {
18731         
18732         var
18733         
18734         /** @private **/
18735         debugOn,
18736         
18737         /**
18738          * Pads a single digit number for display purposes (e.g. '4' shows as '04')
18739          * @param num is the number to pad to 2 digits
18740          * @returns a two digit padded string
18741          * @private
18742          */
18743         padTwoDigits = function (num) {        
18744             return (num < 10) ? '0' + num : num;  
18745         },
18746         
18747         /**
18748          * Checks to see if we have a console - this allows us to support Firefox or IE.
18749          * @returns {Boolean} True for Firefox, False for IE
18750          * @private
18751          */
18752         hasConsole = function () {
18753             var retval = false;
18754             try
18755             {
18756                 if (window.console !== undefined) 
18757                 {
18758                     retval = true;
18759                 }
18760             } 
18761             catch (err)
18762             {
18763                 retval = false;
18764             }
18765               
18766             return retval;
18767         },
18768         
18769         /**
18770          * Gets a timestamp.
18771          * @returns {String} is a timestamp in the following format: HH:MM:SS
18772          * @private
18773          */
18774         getTimeStamp = function () {
18775             var date = new Date(), timeStr;
18776             timeStr = padTwoDigits(date.getHours()) + ":" + padTwoDigits(date.getMinutes()) + ":" + padTwoDigits(date.getSeconds());
18777 
18778             return timeStr;
18779         };
18780         
18781         return {
18782             /**
18783              * Enable debug mode. Debug mode may impact performance on the UI.
18784              *
18785              * @param {Boolean} enable
18786              *      True to enable debug logging.
18787              * @private
18788              */
18789             setDebug : function (enable) {
18790                 debugOn = enable;
18791             },
18792             
18793             /**
18794              * Logs a string as DEBUG.
18795              * 
18796              * @param str is the string to log. 
18797              * @private
18798              */
18799             log : function (str) {
18800                 var timeStr = getTimeStamp();
18801                 
18802                 if (debugOn) {
18803                     if (hasConsole())
18804                     {
18805                         window.console.log(timeStr + ": " + "DEBUG" + " - " + str);
18806                     }
18807                 }
18808             },
18809             
18810             /**
18811              * Logs a string as INFO.
18812              * 
18813              * @param str is the string to log. 
18814              * @private
18815              */
18816             info : function (str) {
18817                 var timeStr = getTimeStamp();
18818                 
18819                 if (hasConsole())
18820                 {
18821                     window.console.info(timeStr + ": " + "INFO" + " - " + str);
18822                 }
18823             },
18824             
18825             /**
18826              * Logs a string as WARN.
18827              * 
18828              * @param str is the string to log. 
18829              * @private
18830              */
18831             warn : function (str) {
18832                 var timeStr = getTimeStamp();
18833                 
18834                 if (hasConsole())
18835                 {
18836                     window.console.warn(timeStr + ": " + "WARN" + " - " + str);
18837                 }
18838             },
18839             /**
18840              * Logs a string as ERROR.
18841              * 
18842              * @param str is the string to log. 
18843              * @private
18844              */
18845             error : function (str) {
18846                 var timeStr = getTimeStamp();
18847                 
18848                 if (hasConsole())
18849                 {
18850                     window.console.error(timeStr + ": " + "ERROR" + " - " + str);
18851                 }
18852             }
18853         };
18854     }());
18855     
18856     return Logger;
18857 });
18858 
18859 /**
18860  * BackSpaceHandler.js: provides functionality to prevent the page from navigating back and hence losing the unsaved data when backspace is pressed.
18861  * 
18862  */
18863 define('utilities/BackSpaceHandler',[], function () {
18864 			var eventCallback = function(event) {
18865 				var doPrevent = false, d = event.srcElement || event.target;
18866 				if (event.keyCode === 8) {
18867 					if ((d.tagName.toUpperCase() === 'INPUT' && (d.type
18868 							.toUpperCase() === 'TEXT'
18869 							|| d.type.toUpperCase() === 'PASSWORD'
18870 							|| d.type.toUpperCase() === 'FILE'
18871 							|| d.type.toUpperCase() === 'SEARCH'
18872 							|| d.type.toUpperCase() === 'EMAIL'
18873 							|| d.type.toUpperCase() === 'NUMBER' || d.type
18874 							.toUpperCase() === 'DATE'))
18875 							|| d.tagName.toUpperCase() === 'TEXTAREA') {
18876 						doPrevent = d.readOnly || d.disabled;
18877 					} else {
18878 						doPrevent = true;
18879 					}
18880 				}
18881 
18882 				if (doPrevent) {
18883 					event.preventDefault();
18884 				}
18885 			};
18886 
18887 			if (window.addEventListener) {
18888 				window.addEventListener('keydown', eventCallback);
18889 			} else if (window.attachEvent) {
18890 				window.attachEvent('onkeydown', eventCallback);
18891 			} else {
18892 				window.console.error("Unable to attach backspace handler event ");
18893 			}
18894 });
18895 /* using variables before they are defined.
18896  */
18897 /*global navigator,unescape,sessionStorage,localStorage,_initSessionList,_initSessionListComplete */
18898 
18899 /**
18900  * Allows each gadget to communicate with the server to send logs.
18901  */
18902 
18903 /**
18904  * @class
18905  * @private
18906  * Allows each product to initialize its method of storage
18907  */
18908 define('cslogger/FinesseLogger',["clientservices/ClientServices", "utilities/Utilities"], function (ClientServices, Utilities) {
18909     
18910     var FinesseLogger = (function () { 
18911 
18912         var
18913 
18914         /**
18915          * Array use to collect ongoing logs in memory
18916          * @private
18917          */
18918         _logArray = [],
18919 
18920         /**
18921          * The final data string sent to the server, =_logArray.join
18922          * @private
18923          */
18924         _logStr = "",
18925 
18926         /**
18927          * Keep track of size of log
18928          * @private
18929          */
18930         _logSize = 0,
18931 
18932         /**
18933          * Flag to keep track show/hide of send log link
18934          * @private
18935          */
18936         _sendLogShown = false,
18937 
18938         /**
18939          * Flag to keep track if local log initialized
18940          * @private
18941          */
18942         _loggingInitialized = false,
18943         
18944 
18945         /**
18946          * local log size limit
18947          * @private
18948          */
18949         _maxLocalStorageSize = 5000000,
18950 
18951         /**
18952          * half local log size limit
18953          * @private
18954          */
18955         _halfMaxLocalStorageSize = 0.5*_maxLocalStorageSize,
18956 
18957         
18958         /**
18959          * threshold for purge 
18960          * @private
18961          */
18962         _purgeStartPercent = 0.75,
18963         
18964         /**
18965          * log item prefix 
18966          * @private
18967          */
18968         _linePrefix = null,
18969         
18970         /**
18971          * locallog session 
18972          * @private
18973          */
18974         _session = null,
18975         
18976         /**
18977          * Flag to keep track show/hide of send log link
18978          * @private
18979          */
18980         _sessionKey = null,
18981         /**
18982          * Log session metadata 
18983          * @private
18984          */
18985         _logInfo = {},
18986         
18987         /**
18988          * Flag to find sessions 
18989          * @private
18990          */
18991         _findSessionsObj = null,
18992 
18993         /**
18994          * Wrap up console.log esp. for IE9 
18995          * @private
18996          */
18997         _myConsoleLog = function (str) {
18998             if (window.console !== undefined) {
18999               window.console.log(str);
19000             }
19001         },
19002         /**
19003          * Initialize the Local Logging
19004          * @private
19005          */
19006         _initLogging = function () {
19007             if (_loggingInitialized) {
19008                 return;
19009             }
19010             //Build a new store
19011             _session = sessionStorage.getItem("finSessKey");
19012             //if the _session is null or empty, skip the init
19013             if (!_session) {
19014               return;
19015             }
19016             _sessionKey = "Fi"+_session;
19017             _linePrefix = _sessionKey + "_";
19018             _logInfo = {};
19019             _logInfo.name = _session;
19020             _logInfo.size = 0;
19021             _logInfo.head = 0;
19022             _logInfo.tail = 0;
19023             _logInfo.startTime = new Date().getTime();
19024             _loggingInitialized = true;
19025             _initSessionList();
19026         },
19027         
19028         /**
19029          * get total data size 
19030          *
19031          * @return {Integer} which is the amount of data stored in local storage.
19032          * @private
19033          */
19034         _getTotalData = function ()
19035         {
19036             var sessName, sessLogInfoStr,sessLogInfoObj, sessionsInfoObj, totalData = 0,
19037             sessionsInfoStr = localStorage.getItem("FinesseSessionsInfo");
19038             if (!sessionsInfoStr) {
19039                  return 0;
19040             }
19041             sessionsInfoObj = JSON.parse(sessionsInfoStr);
19042 
19043             for (sessName in sessionsInfoObj.sessions)
19044             {
19045                 if (sessionsInfoObj.sessions.hasOwnProperty(sessName)) {
19046                     sessLogInfoStr = localStorage.getItem("Fi" + sessName);
19047                     if (!sessLogInfoStr) {
19048                         _myConsoleLog("_getTotalData failed to get log info for "+sessName);
19049                     }
19050                     else {
19051                        sessLogInfoObj = JSON.parse(sessLogInfoStr);
19052                        totalData = totalData + sessLogInfoObj.size;
19053                     }
19054                 }
19055             }
19056 
19057               return totalData;
19058         },
19059         
19060         /**
19061          * Remove lines from tail up until store size decreases to half of max size limit.
19062          *
19063          * @private
19064          */
19065         _purgeCurrentSession = function() {
19066             var curStoreSize, purgedSize=0, line, tailKey, secLogInfoStr, logInfoStr, theLogInfo;
19067             curStoreSize = _getTotalData();
19068             if (curStoreSize < _halfMaxLocalStorageSize) {
19069                return;
19070             }
19071             logInfoStr = localStorage.getItem(_sessionKey);
19072             if (!logInfoStr) {
19073                return;
19074             }
19075             theLogInfo = JSON.parse(logInfoStr);
19076             //_myConsoleLog("Starting _purgeCurrentSession() - currentStoreSize=" + curStoreSize);
19077             while(curStoreSize > _halfMaxLocalStorageSize) {
19078                try {
19079                    tailKey = _sessionKey+"_"+theLogInfo.tail;
19080                    line = localStorage.getItem(tailKey);
19081                    if (line) {
19082                        purgedSize = purgedSize +line.length;
19083                        localStorage.removeItem(tailKey);
19084                        curStoreSize = curStoreSize - line.length;
19085                        theLogInfo.size = theLogInfo.size - line.length;
19086                    }
19087                }
19088                catch (err) {
19089                    _myConsoleLog("purgeCurrentSession encountered err="+err);
19090                }
19091                if (theLogInfo.tail < theLogInfo.head) {
19092                    theLogInfo.tail = theLogInfo.tail  + 1;
19093                }
19094                else {
19095                    break;
19096                }
19097             }
19098             //purge stops here, we need to update session's meta data in storage
19099             secLogInfoStr = localStorage.getItem(_sessionKey);
19100             if (!secLogInfoStr) {
19101                 //somebody cleared the localStorage
19102                 return;
19103             }
19104             
19105             //_myConsoleLog("In _purgeCurrentSession() - after purging current session, currentStoreSize=" + curStoreSize);
19106             //_myConsoleLog("In _purgeCurrentSession() - after purging purgedSize=" + purgedSize);
19107             //_myConsoleLog("In _purgeCurrentSession() - after purging logInfo.size=" + theLogInfo.size);
19108             //_myConsoleLog("In _purgeCurrentSession() - after purging logInfo.tail=" + theLogInfo.tail);
19109             localStorage.setItem(_sessionKey, JSON.stringify(theLogInfo));
19110             _myConsoleLog("Done _purgeCurrentSession() - currentStoreSize=" + curStoreSize);
19111         },
19112        
19113         /**
19114          * Purge a session 
19115          *
19116          * @param sessionName is the name of the session
19117          * @return {Integer} which is the current amount of data purged
19118          * @private
19119          */
19120         _purgeSession = function (sessionName) {
19121               var theLogInfo, logInfoStr, sessionsInfoStr, sessionsInfoObj;
19122               //Get the session logInfo
19123               logInfoStr = localStorage.getItem("Fi" + sessionName);
19124               if (!logInfoStr) {
19125                  _myConsoleLog("_purgeSession failed to get logInfo for "+sessionName);
19126                  return 0;
19127               }
19128               theLogInfo = JSON.parse(logInfoStr);
19129               
19130               //Note: This assumes that we don't crash in the middle of purging
19131               //=> if we do then it should get deleted next time
19132               //Purge tail->head
19133               while (theLogInfo.tail <= theLogInfo.head)
19134               {
19135                   try {
19136                       localStorage.removeItem("Fi" + sessionName + "_" + theLogInfo.tail);
19137                       theLogInfo.tail = theLogInfo.tail + 1;
19138                   }
19139                   catch (err) {
19140                       _myConsoleLog("In _purgeSession err="+err);
19141                       break;
19142                   }
19143               }
19144 
19145               //Remove the entire session
19146               localStorage.removeItem("Fi" + sessionName);
19147 
19148               //Update FinesseSessionsInfo
19149               sessionsInfoStr = localStorage.getItem("FinesseSessionsInfo");
19150               if (!sessionsInfoStr) {
19151                  _myConsoleLog("_purgeSession could not get sessions Info, it was cleared?");
19152                  return 0;
19153               }
19154               sessionsInfoObj = JSON.parse(sessionsInfoStr);
19155               if (sessionsInfoObj.sessions !== null)
19156               {
19157                  delete sessionsInfoObj.sessions[sessionName];
19158               
19159                  sessionsInfoObj.total = sessionsInfoObj.total - 1;
19160                  sessionsInfoObj.lastWrittenBy = _session;
19161                  localStorage.setItem("FinesseSessionsInfo", JSON.stringify(sessionsInfoObj));
19162               }
19163               
19164               return theLogInfo.size;
19165         },
19166         
19167          /**
19168           * purge old sessions
19169           * 
19170           * @param storeSize
19171 	  * @return {Boolean} whether purging reaches its target
19172           * @private
19173          */
19174          _purgeOldSessions = function (storeSize) {
19175              var sessionsInfoStr, purgedSize = 0, sessName, sessions, curStoreSize, activeSession, sessionsInfoObj;
19176              sessionsInfoStr = localStorage.getItem("FinesseSessionsInfo");
19177              if (!sessionsInfoStr) {
19178                 _myConsoleLog("Could not get FinesseSessionsInfo");
19179                 return true;
19180              }
19181              sessionsInfoObj = JSON.parse(sessionsInfoStr);
19182              curStoreSize = _getTotalData();
19183              
19184              activeSession = _session;
19185              sessions = sessionsInfoObj.sessions;
19186              for (sessName in sessions) {
19187                 if (sessions.hasOwnProperty(sessName)) {
19188                     if (sessName !== activeSession) {
19189                         purgedSize = purgedSize + _purgeSession(sessName);
19190                         if ((curStoreSize-purgedSize) < _halfMaxLocalStorageSize) {
19191                             return true;
19192                         }
19193                     }
19194                 }
19195              }
19196             //purge is not done, so return false
19197             return false;
19198          },
19199          
19200        /**
19201         * handle insert error
19202         *
19203         * @param error
19204         * @private
19205         */
19206         _insertLineHandleError = function (error) {
19207             _myConsoleLog(error);
19208         },
19209 
19210         /**
19211          * check storage data size and if need purge
19212          * @private
19213          */
19214         _checkSizeAndPurge = function () {
19215             var purgeIsDone=false, totalSize = _getTotalData();
19216             if (totalSize > 0.75*_maxLocalStorageSize) {
19217                _myConsoleLog("in _checkSizeAndPurge, totalSize ("+totalSize+") exceeds limit");
19218                purgeIsDone = _purgeOldSessions(totalSize);
19219                if (purgeIsDone) {
19220                   _myConsoleLog("in _checkSizeAndPurge after purging old session, purge is done");
19221                }
19222                else {
19223                   //after all old sessions purged, still need purge
19224                   totalSize = _getTotalData();
19225                   if (totalSize > 0.75*_maxLocalStorageSize) {
19226                       _myConsoleLog("in _checkSizeAndPurge after purging old session,still needs purging, now storeSize ("+totalSize+")");
19227                      _purgeCurrentSession();
19228                      _myConsoleLog("in _checkSizeAndPurge done purging current session.");
19229                   }
19230                }
19231             }
19232         },
19233         
19234         /**
19235          * check if the session is already in meta data  
19236          * 
19237          * @param metaData
19238          * @param sessionName
19239          * @return {Boolean} true if session has metaData (false otherwise)
19240          * @private
19241          */
19242         _sessionsInfoContains = function (metaData, sessionName) {
19243            if (metaData && metaData.sessions && metaData.sessions.hasOwnProperty(sessionName)) {
19244               return true;
19245            }
19246            return false;
19247         },
19248         
19249         
19250         /**
19251          * setup sessions in local storage 
19252          * 
19253          * @param logInfo
19254          * @private
19255          */
19256         _getAndSetNumberOfSessions = function (logInfo) {
19257             var numOfSessionsPass1, numOfSessionsPass2, l;
19258             numOfSessionsPass1 = localStorage.getItem("FinesseSessionsInfo");
19259             if (numOfSessionsPass1 === null) {
19260                 //Init first time
19261                 numOfSessionsPass1 = {};
19262                 numOfSessionsPass1.total = 1;
19263                 numOfSessionsPass1.sessions = {};
19264                 numOfSessionsPass1.sessions[logInfo.name] = logInfo.startTime;
19265                 numOfSessionsPass1.lastWrittenBy = logInfo.name;
19266                 localStorage.setItem("FinesseSessionsInfo", JSON.stringify(numOfSessionsPass1));
19267             }
19268             else {
19269                 numOfSessionsPass1 = JSON.parse(numOfSessionsPass1);
19270                 //check if the session is already in the FinesseSessionSInfo
19271                 if (_sessionsInfoContains(numOfSessionsPass1, logInfo.name)) {
19272                     return;
19273                 }             
19274                 //Save numOfSessionsPass1
19275                 numOfSessionsPass1.total = parseInt(numOfSessionsPass1.total, 10) + 1;
19276                 numOfSessionsPass1.sessions[logInfo.name] = logInfo.startTime;
19277                 numOfSessionsPass1.lastWrittenBy = logInfo.name;
19278                 localStorage.setItem("FinesseSessionsInfo", JSON.stringify(numOfSessionsPass1));
19279                 numOfSessionsPass2 = localStorage.getItem("FinesseSessionsInfo");
19280                 if (!numOfSessionsPass2) {
19281                    _myConsoleLog("Could not get FinesseSessionsInfo");
19282                    return;
19283                 }
19284                 numOfSessionsPass2 = JSON.parse(numOfSessionsPass2);
19285                 //in future we need to confirm the numOfSessionsPass2 is the same as numOfSessionsPass1
19286                 ////if (numOfSessionsPass1.lastWrittenBy !== numOfSessionsPass2.lastWrittenBy) {
19287                 ////    _myConsoleLog("Rebuild sessions");
19288                 ////    _sessionTimerId = setTimeout(_initSessionList, 10000);
19289                 ////}
19290                 ////else {
19291                 ////    _sessionTimerId = null;
19292                 ////callback(numOfSessionsPass2.sessions);
19293                 ////}
19294             }
19295             if (!localStorage.getItem(_sessionKey)) {
19296                 localStorage.setItem(_sessionKey, JSON.stringify(_logInfo));
19297             }
19298         },
19299         
19300         
19301         /**
19302          * init session list 
19303          * @private
19304          */
19305         _initSessionList = function () {
19306             _getAndSetNumberOfSessions(_logInfo);
19307         },
19308         
19309        /**
19310         * do the real store of log line
19311         * 
19312         * @param line
19313         * @private
19314         */
19315         _persistLine = function (line) {
19316             var key, logInfoStr;
19317             logInfoStr = localStorage.getItem(_sessionKey);
19318             if (logInfoStr === null) {
19319                return;
19320             }
19321             _logInfo = JSON.parse(logInfoStr);
19322             _logInfo.head = _logInfo.head + 1;
19323             key = _linePrefix + _logInfo.head;
19324             localStorage.setItem(key, line);
19325             //Save the size
19326             _logInfo.size = _logInfo.size + line.length;
19327             if (_logInfo.tail === 0) {
19328                 _logInfo.tail = _logInfo.head;
19329             }
19330         
19331             localStorage.setItem(_sessionKey, JSON.stringify(_logInfo));
19332             _checkSizeAndPurge();
19333         },
19334         
19335         /**
19336          * Insert a line into the localStorage.
19337          *
19338          * @param line line to be inserted 
19339          * @private
19340         */
19341         _insertLine = function (line) {
19342             //_myConsoleLog("_insertLine: [" + line + "]");
19343             //Write the next line to localStorage
19344             try {
19345                //Persist the line 
19346                _persistLine(line);
19347             }
19348             catch (err) {
19349                _myConsoleLog("error in _insertLine(), err="+err);
19350                //_insertLineHandleError(err);
19351             }
19352         },
19353          
19354         
19355         /**
19356          * Clear the local storage
19357          * @private
19358          */
19359         _clearLocalStorage = function() {
19360             localStorage.clear();
19361 
19362         },
19363 
19364         /**
19365          * Collect logs when onCollect called
19366          *
19367          * @param data
19368          * @private
19369          */
19370         _collectMethod = function(data) {
19371           //Size of log should not exceed 1.5MB
19372           var info, maxLength = 1572864;
19373           
19374           //add size buffer equal to the size of info to be added when publish
19375           info = Utilities.getSanitizedUserAgentString() + "
";
19376           info = escape(info);
19377 
19378             //If log was empty previously, fade in buttons
19379             if (!_sendLogShown) {
19380                 //call the fadeInSendLog() in Footer
19381                 finesse.modules.Footer.sendLogAppear();
19382                 _sendLogShown = true;
19383                 _logSize = info.length;
19384             }
19385             
19386             //if local storage logging is enabled, then insert the log into local storage
19387             if (window.sessionStorage.getItem('enableLocalLog')==='true') {
19388                 if (data) {
19389                    if (data.length>0 && data.substring(0,1) === '\n') {
19390                       _insertLine(data.substring(1));
19391                    }
19392                    else {
19393                       _insertLine(data);
19394                    }
19395                 }
19396             }
19397               
19398             //escape all data to get accurate size (shindig will escape when it builds request)
19399             //escape 6 special chars for XML: &<>"'\n
19400             data = data.replace(/&/g, "&").replace(/"/g, """).replace(/'/g, "'").replace(/>/g, ">").replace(/</g, "<").replace(/\n/g, "
");
19401             data = escape(data+"\n");
19402 
19403             if (data.length < maxLength){
19404                 //make room for new data if log is exceeding max length
19405                 while (_logSize + data.length > maxLength) {
19406                     _logSize -= (_logArray.shift()).length;
19407                 }
19408             }
19409 
19410             //Else push the log into memory, increment the log size
19411             _logArray.push(data);
19412 
19413             //inc the size accordingly
19414             _logSize+=data.length;
19415 
19416         };
19417 
19418         return {
19419 
19420             /**
19421              * @private
19422              * Initiate FinesseLogger.
19423              */
19424             init: function () {
19425                 ClientServices.subscribe("finesse.clientLogging.*", _collectMethod);
19426                 _initLogging();
19427             },
19428 
19429             /**
19430              * @private
19431              * Clear all items stored in localStorage.
19432             */
19433             clear : function () {
19434                _clearLocalStorage();
19435             },
19436 
19437             /**
19438              * @private
19439              * Initialize the local storage logging.
19440             */
19441             initLocalLog: function () {
19442                _initLogging();
19443             },
19444 
19445             /**
19446              * @private
19447              * Inserts a line into the localStorage.
19448              * @param line to insert
19449             */
19450             localLog : function (line) {
19451                _insertLine(line);
19452             },
19453 
19454            /**
19455             * @ignore
19456             * Publish logs to server and clear the memory
19457             *
19458             * @param userObj
19459             * @param options
19460             * @param callBack
19461             */
19462             publish: function(userObj, options, callBack) {
19463                 // Avoid null references.
19464                 options = options || {};
19465                 callBack = callBack || {};
19466 
19467                 if (callBack.sending === "function") {
19468                     callBack.sending();
19469                 }
19470 
19471                 //logs the basic version and machine info and escaped new line
19472                 _logStr = Utilities.getSanitizedUserAgentString() + "
";
19473                 
19474                 //join the logs to correct string format
19475                 _logStr += unescape(_logArray.join(""));
19476 
19477                 //turning log string to JSON obj
19478                 var logObj = {
19479                         ClientLog: {
19480                         logData : _logStr //_logStr
19481                     }
19482                 },
19483                 tmpOnAdd = (options.onAdd && typeof options.onAdd === "function")? options.onAdd : function(){};
19484                 /** @private */
19485                 options.onAdd = function(){
19486                     tmpOnAdd();
19487                     _logArray.length = 0; _logSize =0;
19488                     _sendLogShown = false;
19489                     };
19490                 //adding onLoad to the callbacks, this is the subscribe success case for the first time user subscribe to the client log node
19491                 /** @private */
19492                 options.onLoad = function (clientLogObj) {
19493                     clientLogObj.sendLogs(logObj,{
19494                             error: callBack.error
19495                         });
19496                     };
19497 
19498                 userObj.getClientLog(options);
19499             }
19500         };
19501     }());
19502 
19503     window.finesse = window.finesse || {};
19504     window.finesse.cslogger = window.finesse.cslogger || {};
19505     /** @private */
19506     window.finesse.cslogger.FinesseLogger = FinesseLogger;
19507 
19508     return FinesseLogger;
19509 });
19510 
19511 /**
19512  *  Contains a list of topics used for containerservices pubsub.
19513  *
19514  */
19515 
19516 /**
19517  * @class
19518  * Contains a list of topics with some utility functions.
19519  */
19520 /** @private */
19521 define('containerservices/Topics',[], function () {
19522 
19523     var Topics = (function () {
19524 
19525     /**
19526      * The namespace prepended to all Finesse topics.
19527      */
19528     this.namespace = "finesse.containerservices";
19529 
19530     /**
19531      * @private
19532      * Gets the full topic name with the ContainerServices namespace prepended.
19533      * @param {String} topic
19534      *     The topic category.
19535      * @returns {String}
19536      *     The full topic name with prepended namespace.
19537      */
19538     var _getNSTopic = function (topic) {
19539         return this.namespace + "." + topic;
19540     };
19541 
19542 
19543 
19544     /** @scope finesse.containerservices.Topics */
19545     return {
19546         /** 
19547          * @private
19548          * request channel. */
19549         REQUESTS: _getNSTopic("requests"),
19550 
19551         /** 
19552          * @private
19553          * reload gadget channel. */
19554         RELOAD_GADGET: _getNSTopic("reloadGadget"),
19555 
19556         /**
19557          * @private
19558          * Convert a Finesse REST URI to a OpenAjax compatible topic name.
19559          */
19560         getTopic: function (restUri) {
19561             //The topic should not start with '/' else it will get replaced with
19562             //'.' which is invalid.
19563             //Thus, remove '/' if it is at the beginning of the string
19564             if (restUri.indexOf('/') === 0) {
19565                 restUri = restUri.substr(1);
19566             }
19567 
19568             //Replace every instance of "/" with ".". This is done to follow the
19569             //OpenAjaxHub topic name convention.
19570             return restUri.replace(/\//g, ".");
19571         }
19572     };
19573 	}());
19574 	
19575 	window.finesse = window.finesse || {};
19576     window.finesse.containerservices = window.finesse.containerservices || {};
19577     window.finesse.containerservices.Topics = Topics;
19578     
19579     /** @namespace JavaScript class objects and methods to handle gadget container services.*/
19580     finesse.containerservices = finesse.containerservices || {};
19581 
19582     return Topics;
19583  });
19584 
19585 /** The following comment is to prevent jslint errors about 
19586  * using variables before they are defined.
19587  */
19588 /*global finesse*/
19589 
19590 /**
19591  * Per containerservices request, publish to the OpenAjax gadget pubsub infrastructure.
19592  *
19593  * @requires OpenAjax, finesse.containerservices.Topics
19594  */
19595 
19596 /** @private */
19597 define('containerservices/MasterPublisher',[
19598     "utilities/Utilities",
19599     "containerservices/Topics"
19600 ],
19601 function (Utilities, Topics) {
19602 
19603     var MasterPublisher = function () {
19604 
19605     var
19606     
19607     /**
19608      * Reference to the gadget pubsub Hub instance.
19609      * @private
19610      */
19611     _hub = gadgets.Hub,
19612 
19613     /**
19614      * Reference to the Topics class.
19615      * @private
19616      */
19617     _topics = Topics,
19618     
19619     /**
19620      * Reference to conversion utilities class.
19621      * @private
19622      */
19623     _utils = Utilities,
19624     
19625     /**
19626      * References to ClientServices logger methods
19627      * @private
19628      */
19629     _logger = {
19630         log: finesse.clientservices.ClientServices.log
19631     },
19632     
19633    /**
19634      * The types of possible request types supported when listening to the
19635      * requests channel. Each request type could result in different operations.
19636      * @private
19637      */
19638     _REQTYPES = {
19639 		ACTIVETAB: "ActiveTabReq",
19640 		SET_ACTIVETAB: "SetActiveTabReq",
19641         RELOAD_GADGET: "ReloadGadgetReq"
19642     },
19643 
19644     /**
19645      * Handles client requests made to the request topic. The type of the
19646      * request is described in the "type" property within the data payload. Each
19647      * type can result in a different operation.
19648      * @param {String} topic
19649      *     The topic which data was published to.
19650      * @param {Object} data
19651      *     The data containing requests information published by clients.
19652      * @param {String} data.type
19653      *     The type of the request. Supported: "ActiveTabReq", "SetActiveTabReq", "ReloadGadgetReq"
19654      * @param {Object} data.data
19655      *     May contain data relevant for the particular requests.
19656      * @param {String} [data.invokeID]
19657      *     The ID used to identify the request with the response. The invoke ID
19658      *     will be included in the data in the publish to the topic. It is the
19659      *     responsibility of the client to correlate the published data to the
19660      *     request made by using the invoke ID.
19661      * @private
19662      */
19663     _clientRequestHandler = function (topic, data) {
19664     
19665         //Ensure a valid data object with "type" and "data" properties.
19666         if (typeof data === "object" &&
19667                 typeof data.type === "string" &&
19668                 typeof data.data === "object") {
19669 			switch (data.type) {
19670 			case _REQTYPES.ACTIVETAB:
19671                 _hub.publish("finesse.containerservices.activeTab", finesse.container.Tabs.getActiveTab());
19672                 break;
19673             case _REQTYPES.SET_ACTIVETAB:
19674                 if (typeof data.data.id === "string") {
19675                     _logger.log("Handling request to activate tab: " + data.data.id);
19676                     if (!finesse.container.Tabs.activateTab(data.data.id)) {
19677                         _logger.log("No tab found with id: " + data.data.id);
19678                     }
19679                 }
19680                 break;
19681             case _REQTYPES.RELOAD_GADGET:
19682                 _hub.publish("finesse.containerservices.reloadGadget", data.data);
19683                 break;
19684 			default:
19685 				break;
19686 			}
19687         }
19688     };
19689 
19690     (function () {
19691 
19692         //Listen to a request channel to respond to any requests made by other
19693         //clients because the Master may have access to useful information.
19694         _hub.subscribe(_topics.REQUESTS, _clientRequestHandler);
19695     }());
19696 
19697     //BEGIN TEST CODE//
19698     /**
19699      * Test code added to expose private functions that are used by unit test
19700      * framework. This section of code is removed during the build process
19701      * before packaging production code. The [begin|end]TestSection are used
19702      * by the build to identify the section to strip.
19703      * @ignore
19704      */
19705     this.beginTestSection = 0;
19706 
19707     /**
19708      * @ignore
19709      */
19710     this.getTestObject = function () {
19711         //Load mock dependencies.
19712         var _mock = new MockControl();
19713         _hub = _mock.createMock(gadgets.Hub);
19714 
19715         return {
19716             //Expose mock dependencies
19717             mock: _mock,
19718             hub: _hub,
19719 			
19720             //Expose internal private functions
19721             reqtypes: _REQTYPES,
19722             
19723             clientRequestHandler: _clientRequestHandler
19724 
19725         };
19726     };
19727 
19728 
19729     /**
19730      * @ignore
19731      */
19732     this.endTestSection = 0;
19733     //END TEST CODE//
19734 	};
19735 	
19736 	window.finesse = window.finesse || {};
19737     window.finesse.containerservices = window.finesse.containerservices || {};
19738     window.finesse.containerservices.MasterPublisher = MasterPublisher;
19739 	
19740     return MasterPublisher;
19741 });
19742 
19743 /**
19744  * JavaScript representation of the Finesse WorkflowActionEvent object.
19745  *
19746  * @requires finesse.FinesseBase
19747  */
19748 
19749 /** The following comment is to prevent jslint errors about 
19750  * using variables before they are defined.
19751  */
19752 /*global FinesseBase: true, publisher:true, define:true, finesse:true, window:true */
19753 /** @private */
19754 define('containerservices/WorkflowActionEvent', ["FinesseBase"], function (FinesseBase) {
19755     var WorkflowActionEvent = FinesseBase.extend(/** @lends finesse.containerservices.WorkflowActionEvent.prototype */{
19756         /**
19757          * Reference to the WorkflowActionEvent name
19758          * This will be set by setWorkflowActionEvent
19759          * @private
19760          */
19761         _name: null,
19762 
19763         /**
19764          * Reference to the WorkflowActionEvent type
19765          * This will be set by setWorkflowActionEvent
19766          * @private
19767          */
19768         _type: null,
19769 
19770         /**
19771          * Reference to the WorkflowActionEvent handledBy value
19772          * This will be set by setWorkflowActionEvent
19773          * @private
19774          */
19775         _handledBy: null,
19776 
19777         /**
19778          * Reference to the WorkflowActionEvent params array
19779          * This will be set by setWorkflowActionEvent
19780          * @private
19781          */
19782         _params: [],
19783 
19784         /**
19785          * Reference to the WorkflowActionEvent actionVariables array
19786          * This will be set by setWorkflowActionEvent
19787          * @private
19788          */            
19789         _actionVariables: [], 
19790         
19791         /**
19792          * @class
19793          * JavaScript representation of a WorkflowActionEvent object.
19794          * The WorkflowActionEvent object is delivered as the payload of
19795          * a WorkflowAction callback.  This can be subscribed to by using
19796          * {@link finesse.containerservices.ContainerServices#addHandler} with a 
19797          * topic of {@link finesse.containerservices.ContainerServices.Topics#WORKFLOW_ACTION_EVENT}. 
19798          * Gadgets should key on events with a handleBy value of "OTHER".
19799          * 
19800          * @constructs
19801          **/
19802         init: function () {
19803             this._super();
19804         },        
19805 
19806         /**
19807 	     * Validate that the passed in object is a WorkflowActionEvent object
19808 	     * and sets the variables if it is
19809 	     * @param maybeWorkflowActionEvent A possible WorkflowActionEvent object to be evaluated and set if 
19810 	     *                                 it validates successfully.
19811 	     * @returns {Boolean} Whether it is valid or not.
19812          * @private
19813 	     */
19814 	    setWorkflowActionEvent: function(maybeWorkflowActionEvent) {
19815 	        var returnValue;
19816 	
19817 	        if (maybeWorkflowActionEvent.hasOwnProperty("name") === true &&
19818 	                maybeWorkflowActionEvent.hasOwnProperty("type") === true &&
19819                     maybeWorkflowActionEvent.hasOwnProperty("handledBy") === true &&
19820 	                maybeWorkflowActionEvent.hasOwnProperty("params") === true &&
19821 	                maybeWorkflowActionEvent.hasOwnProperty("actionVariables") === true) {
19822 	            this._name = maybeWorkflowActionEvent.name;
19823 	            this._type = maybeWorkflowActionEvent.type;
19824                 this._handledBy = maybeWorkflowActionEvent.handledBy;
19825 	            this._params = maybeWorkflowActionEvent.params;
19826 	            this._actionVariables = maybeWorkflowActionEvent.actionVariables;
19827 	            returnValue = true;
19828 	        } else {
19829 	            returnValue = false;
19830 	        }
19831 	
19832 	        return returnValue;
19833 	    },
19834 	
19835 	    /**
19836 	     * Getter for the WorkflowActionEvent name.
19837 	     * @returns {String} The name of the WorkflowAction.
19838 	     */
19839 	    getName: function () {
19840 	        // escape nulls to empty string
19841 	        return this._name || "";
19842 	    },
19843 	
19844 	    /**
19845 	     * Getter for the WorkflowActionEvent type.
19846 	     * @returns {String} The type of the WorkflowAction (BROWSER_POP, HTTP_REQUEST).
19847 	     */
19848 	    getType: function () {
19849 	        // escape nulls to empty string
19850 	        return this._type || "";
19851 	    },
19852 	
19853         /**
19854          * Getter for the WorkflowActionEvent handledBy value. Gadgets should look for
19855          * events with a handleBy of "OTHER".
19856          * @see finesse.containerservices.WorkflowActionEvent.HandledBy
19857          * @returns {String} The handledBy value of the WorkflowAction that is a value of {@link finesse.containerservices.WorkflowActionEvent.HandledBy}.
19858          */
19859         getHandledBy: function () {
19860             // escape nulls to empty string
19861             return this._handledBy || "";
19862         },
19863 
19864 
19865 	    /**
19866 	     * Getter for the WorkflowActionEvent Params map.
19867 	     * @returns {Object} key = param name, value = Object{name, value, expandedValue}
19868 	     * BROWSER_POP<ul>
19869 	     * <li>windowName : Name of window to pop into, or blank to always open new window.
19870 	     * <li>path : URL to open.</ul>
19871 	     * HTTP_REQUEST<ul>
19872 	     * <li>method : "PUT" or "POST".
19873 	     * <li>location : "FINESSE" or "OTHER".
19874 	     * <li>contentType : MIME type of request body, if applicable, e.g. "text/plain".
19875 	     * <li>path : Request URL.
19876 	     * <li>body : Request content for POST requests.</ul>
19877 	     */
19878 	    getParams: function () {
19879 	        var map = {},
19880 	            params = this._params,
19881 	            i,
19882 	            param;
19883 	
19884 	        if (params === null || params.length === 0) {
19885 	            return map;
19886 	        }
19887 	
19888 	        for (i = 0; i < params.length; i += 1) {
19889 	            param = params[i];
19890 	            // escape nulls to empty string
19891 	            param.name = param.name || "";
19892 	            param.value = param.value || "";
19893 	            param.expandedValue = param.expandedValue || "";
19894 	            map[param.name] = param;
19895 	        }
19896 	
19897 	        return map;
19898 	    },
19899 	    
19900 	    /**
19901 	     * Getter for the WorkflowActionEvent ActionVariables map
19902 	     * @returns {Object} key = action variable name, value = Object{name, type, node, testValue, actualValue}
19903 	     */
19904 	    getActionVariables: function() {
19905 	        var map = {},
19906 	            actionVariables = this._actionVariables,
19907 	            i,
19908 	            actionVariable;
19909 	
19910 	        if (actionVariables === null || actionVariables.length === 0) {
19911 	            return map;
19912 	        }
19913 	
19914 	        for (i = 0; i < actionVariables.length; i += 1) {
19915 	            actionVariable = actionVariables[i];
19916 	            // escape nulls to empty string
19917 	            actionVariable.name = actionVariable.name || "";
19918 	            actionVariable.type = actionVariable.type || "";
19919 	            actionVariable.node = actionVariable.node || "";
19920 	            actionVariable.testValue = actionVariable.testValue || "";
19921 	            actionVariable.actualValue = actionVariable.actualValue || "";
19922 	            map[actionVariable.name] = actionVariable;
19923 	        }
19924 	
19925 	        return map;
19926 	    }
19927     }); 
19928     
19929     
19930     WorkflowActionEvent.HandledBy = /** @lends finesse.containerservices.WorkflowActionEvent.HandledBy.prototype */ {
19931         /**
19932          * This specifies that Finesse will handle this WorkflowActionEvent.  A 3rd Party can do additional processing
19933          * with the action, but first and foremost Finesse will handle this WorkflowAction.
19934          */
19935         FINESSE: "FINESSE",
19936 
19937         /**
19938          * This specifies that a 3rd Party will handle this WorkflowActionEvent.  Finesse's Workflow Engine Executor will 
19939          * ignore this action and expects Gadget Developers to take action.
19940          */
19941         OTHER: "OTHER",
19942         
19943         /**
19944          * @class This is the set of possible HandledBy values used for WorkflowActionEvent from ContainerServices.  This
19945          * is provided from the {@link finesse.containerservices.WorkflowActionEvent#getHandledBy} method.
19946          * @constructs
19947          */
19948         _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
19949     };    
19950     
19951     window.finesse = window.finesse || {};
19952     window.finesse.containerservices = window.finesse.containerservices || {};
19953     window.finesse.containerservices.WorkflowActionEvent = WorkflowActionEvent;
19954     
19955     return WorkflowActionEvent;
19956 });
19957 
19958 /**
19959  * JavaScript representation of the Finesse TimerTickEvent
19960  *
19961  * @requires finesse.FinesseBase
19962  */
19963 
19964 /** The following comment is to prevent jslint errors about 
19965  * using variables before they are defined.
19966  */
19967 /*global FinesseBase: true, publisher:true, define:true, finesse:true, window:true */
19968 /** @private */
19969 define('containerservices/TimerTickEvent',[
19970     "FinesseBase"
19971 ],
19972 function (FinesseBase) {
19973     var TimerTickEvent = FinesseBase.extend(/** @lends finesse.containerservices.TimerTickEvent.prototype */{
19974         /**
19975          * date the TimerTickEvent was queued 
19976          * @private
19977          */
19978         _dateQueued: null,
19979 
19980         /**
19981          * the frequency of the timer tick (in miiliseconds)
19982          * @private
19983          */
19984         _tickFrequency: 1000,
19985 
19986         /**
19987          * @class
19988          * JavaScript representation of a TimerTickEvent object.
19989          * The TimerTickEvent object is delivered as the payload of
19990          * a TimerTickEvent callback.  This can be subscribed to by using
19991          * {@link finesse.containerservices.ContainerServices#addHandler} with a 
19992          * topic of {@link finesse.containerservices.ContainerServices.Topics#TIMER_TICK_EVENT}. 
19993          * 
19994          * @constructs
19995          **/
19996         init: function (tickFrequency, dateQueued) {
19997             this._super();
19998             
19999             this._tickFrequency = tickFrequency;
20000             this._dateQueued = dateQueued;
20001         },
20002 
20003        /**
20004          * Get the "tickFrequency" field
20005          * @param {int} which is the "TickFrequency" field
20006          * @private
20007          */
20008         getTickFrequency: function () {
20009             return this._tickFrequency;
20010         },
20011 
20012         /**
20013          * Getter for the TimerTickEvent "DateQueued" field. 
20014          * @returns {Date} which is a Date object when the TimerTickEvent was queued
20015          */
20016         getDateQueued: function () {
20017             return this._dateQueued;
20018         }
20019 
20020     });
20021     
20022     window.finesse = window.finesse || {};
20023     window.finesse.containerservices = window.finesse.containerservices || {};
20024     window.finesse.containerservices.TimerTickEvent = TimerTickEvent;
20025     
20026     return TimerTickEvent;
20027 });
20028 
20029 /**
20030  * JavaScript representation of the Finesse GadgetViewChangedEvent object.
20031  *
20032  * @requires finesse.FinesseBase
20033  */
20034 
20035 /** The following comment is to prevent jslint errors about 
20036  * using variables before they are defined.
20037  */
20038 /*global FinesseBase: true, publisher:true, define:true, finesse:true, window:true */
20039 /** @private */
20040 define('containerservices/GadgetViewChangedEvent',[
20041     "FinesseBase"
20042 ],
20043 function (FinesseBase) {
20044     var GadgetViewChangedEvent = FinesseBase.extend(/** @lends finesse.containerservices.GadgetViewChangedEvent.prototype */{
20045         /**
20046          * Reference to the gadget id
20047          * @private
20048          */
20049         _gadgetId: null,
20050 
20051         /**
20052          * Reference to the tab id
20053          * @private
20054          */
20055         _tabId: null,
20056 
20057         /**
20058          * Reference to the maxAvailableHeight
20059          * @private
20060          */
20061         _maxAvailableHeight: null,
20062 
20063         /**
20064          * Reference to the view
20065          * E.g. 'default' or 'canvas'
20066          * @private
20067          */
20068         _view: null,
20069         
20070         /**
20071          * @class
20072          * JavaScript representation of a GadgetViewChangedEvent object.
20073          * The GadgetViewChangedEvent object is delivered as the payload of
20074          * a GadgetViewChangedEvent callback.  This can be subscribed to by using
20075          * {@link finesse.containerservices.ContainerServices#addHandler} with a 
20076          * topic of {@link finesse.containerservices.ContainerServices.Topics#GADGET_VIEW_CHANGED_EVENT}. 
20077          * 
20078          * @constructs
20079          **/
20080         init: function (gadgetId, tabId, maxAvailableHeight, view) {
20081             this._super();
20082 
20083             this._gadgetId = gadgetId;
20084             this._tabId = tabId;
20085             this._maxAvailableHeight = maxAvailableHeight;
20086             this._view = view;
20087         },
20088     
20089         /**
20090          * Getter for the gadget id.
20091          * @returns {String} The identifier for the gadget changing view.
20092          */
20093         getGadgetId: function () {
20094             // escape nulls to empty string
20095             return this._gadgetId || "";
20096         },
20097     
20098         /**
20099          * Getter for the maximum available height.
20100          * @returns {String} The maximum available height for the gadget's view.
20101          */
20102         getMaxAvailableHeight: function () {
20103             // escape nulls to empty string
20104             return this._maxAvailableHeight || "";
20105         },
20106 
20107         /**
20108          * Getter for the tab id.
20109          * @returns {String} The identifier for the tab where the gadget changing view resides.
20110          */
20111         getTabId: function () {
20112             // escape nulls to empty string
20113             return this._tabId || "";
20114         },
20115 
20116         /**
20117          * Getter for the view.
20118          * @returns {String} The view type the gadget is changing to.
20119          */
20120         getView: function () {
20121             // escape nulls to empty string
20122             return this._view || "";
20123         }
20124     });
20125     
20126     window.finesse = window.finesse || {};
20127     window.finesse.containerservices = window.finesse.containerservices || {};
20128     window.finesse.containerservices.GadgetViewChangedEvent = GadgetViewChangedEvent;
20129     
20130     return GadgetViewChangedEvent;
20131 });
20132 
20133 /**
20134  * JavaScript representation of the Finesse MaxAvailableHeightChangedEvent object.
20135  *
20136  * @requires finesse.FinesseBase
20137  */
20138 
20139 /** The following comment is to prevent jslint errors about 
20140  * using variables before they are defined.
20141  */
20142 /*global FinesseBase: true, publisher:true, define:true, finesse:true, window:true */
20143 /** @private */
20144 define('containerservices/MaxAvailableHeightChangedEvent',[
20145     "FinesseBase"
20146 ],
20147 function (FinesseBase) {
20148     var MaxAvailableHeightChangedEvent = FinesseBase.extend(/** @lends finesse.containerservices.MaxAvailableHeightChangedEvent.prototype */{
20149 
20150         /**
20151          * Reference to the maxAvailableHeight
20152          * @private
20153          */
20154         _maxAvailableHeight: null,
20155         
20156         /**
20157          * @class
20158          * JavaScript representation of a MaxAvailableHeightChangedEvent object.
20159          * The MaxAvailableHeightChangedEvent object is delivered as the payload of
20160          * a MaxAvailableHeightChangedEvent callback.  This can be subscribed to by using
20161          * {@link finesse.containerservices.ContainerServices#addHandler} with a 
20162          * topic of {@link finesse.containerservices.ContainerServices.Topics#MAX_AVAILABLE_HEIGHT_CHANGED_EVENT}. 
20163          * 
20164          * @constructs
20165          **/
20166         init: function (maxAvailableHeight) {
20167             this._super();
20168 
20169             this._maxAvailableHeight = maxAvailableHeight;
20170         },
20171     
20172         /**
20173          * Getter for the maximum available height.
20174          * @returns {String} The maximum available height for a gadget in canvas view
20175          */
20176         getMaxAvailableHeight: function () {
20177             // escape nulls to empty string
20178             return this._maxAvailableHeight || "";
20179         }
20180     });
20181     
20182     window.finesse = window.finesse || {};
20183     window.finesse.containerservices = window.finesse.containerservices || {};
20184     window.finesse.containerservices.MaxAvailableHeightChangedEvent = MaxAvailableHeightChangedEvent;
20185     
20186     return MaxAvailableHeightChangedEvent;
20187 });
20188 
20189 /**
20190  * Exposes a set of API wrappers that will hide the dirty work of
20191  *     constructing Finesse API requests and consuming Finesse events.
20192  *
20193  * @requires OpenAjax, jQuery 1.5, finesse.utilities.Utilities
20194  */
20195 
20196 /** The following comment is to prevent jslint errors about using variables before they are defined. */
20197 /*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 */
20198 /*jslint nomen: true, unparam: true, sloppy: true, white: true */
20199 /** @private */
20200 define('containerservices/ContainerServices',[
20201     "utilities/Utilities",
20202     "restservices/Notifier",
20203     "containerservices/Topics",
20204     "containerservices/MasterPublisher",
20205     "containerservices/WorkflowActionEvent",
20206     "containerservices/TimerTickEvent",
20207     "containerservices/GadgetViewChangedEvent",
20208     "containerservices/MaxAvailableHeightChangedEvent"
20209 ],
20210 function (Utilities, Notifier, Topics, MasterPublisher, WorkflowActionEvent) {
20211 
20212     var ContainerServices = ( function () { /** @lends finesse.containerservices.ContainerServices.prototype */
20213 
20214     var
20215 
20216     /**
20217      * Shortcut reference to the Utilities singleton
20218      * This will be set by init()
20219      * @private
20220      */
20221     _util,
20222 
20223     /**
20224      * Shortcut reference to the gadget pubsub Hub instance.
20225      * This will be set by init()
20226      * @private
20227      */
20228     _hub,
20229 
20230     /**
20231      * Boolean whether this instance is master or not
20232      * @private
20233      */
20234     _master = false,
20235 
20236     /**
20237      * Whether the Client Services have been initiated yet.
20238      * @private
20239      */
20240     _inited = false,
20241     
20242     /**
20243      * References to ClientServices logger methods
20244      * @private
20245      */
20246     _logger = {
20247         log: finesse.clientservices.ClientServices.log
20248     },
20249     
20250      /**
20251      * Stores the list of subscription IDs for all subscriptions so that it
20252      * could be retrieve for unsubscriptions.
20253      * @private
20254      */
20255     _subscriptionID = {},
20256     
20257     /**
20258      * Reference to the gadget's parent container
20259      * @private
20260      */
20261     _container,
20262 
20263     /**
20264      * Reference to the MasterPublisher
20265      * @private
20266      */
20267     _publisher,
20268     
20269     /**
20270      * Object that will contain the Notifiers
20271      * @private
20272      */
20273     _notifiers = {},
20274 
20275     /**
20276      * Reference to the tabId that is associated with the gadget
20277      * @private
20278      */
20279     _myTab = null,
20280     
20281     /**
20282      * Reference to the visibility of current gadget
20283      * @private
20284      */
20285     _visible = false,
20286     
20287     /**
20288      * Reference for auth modes constants.
20289      * @private
20290      */
20291     _authModes,
20292     
20293     /**
20294      * Shortcut reference to the Topics class.
20295      * This will be set by init()
20296      * @private
20297      */
20298     _topics,
20299 
20300     /**
20301      * Associates a topic name with the private handler function.
20302      * Adding a new topic requires that you add this association here 
20303      *  in to keep addHandler generic.
20304      * @param {String} topic : Specifies the callback to retrieve
20305      * @return {Function} The callback function associated with the topic param.
20306      * @private
20307      */
20308     _topicCallback = function (topic) {
20309         var callback, notifier;
20310         switch (topic)
20311         {
20312             case finesse.containerservices.ContainerServices.Topics.ACTIVE_TAB:
20313                 callback = _tabTracker;
20314                 break;
20315             case finesse.containerservices.ContainerServices.Topics.WORKFLOW_ACTION_EVENT:
20316                 callback = _workflowActionEventTracker;
20317                 break;
20318             case finesse.containerservices.ContainerServices.Topics.RELOAD_GADGET_EVENT:
20319                 callback = _masterReloader;
20320                 break;
20321             case finesse.containerservices.ContainerServices.Topics.GADGET_VIEW_CHANGED_EVENT:
20322                 callback = _gadgetViewChanged;
20323                 break;
20324             case finesse.containerservices.ContainerServices.Topics.MAX_AVAILABLE_HEIGHT_CHANGED_EVENT:
20325                 callback = _maxAvailableHeightChanged;
20326                 break;
20327             case finesse.containerservices.ContainerServices.Topics.ACCESS_TOKEN_REFRESHED_EVENT:
20328                 callback = _accessTokenRefreshed;
20329                 break;
20330             default:
20331                 callback = function (param) {
20332                      var data = null;
20333                      
20334                      notifier = _getNotifierReference(topic);
20335                      
20336                      if (arguments.length === 1) {
20337                         data = param;
20338                      } else {
20339                         data = arguments;
20340                      }
20341                      notifier.notifyListeners(data);
20342                 };
20343         }
20344         return callback;
20345     },
20346 
20347     /**
20348      * Ensure that ClientServices have been inited.
20349      * @private
20350      */
20351     _isInited = function () {
20352         if (!_inited) {
20353             throw new Error("ContainerServices needs to be inited.");
20354         }
20355     },
20356 
20357     /**
20358      * Retrieves a Notifier reference to a particular topic, and creates one if it doesn't exist.
20359      * @param {String} topic : Specifies the notifier to retrieve
20360      * @return {Notifier} The notifier object.
20361      * @private
20362      */
20363     _getNotifierReference = function (topic) {
20364         if (!_notifiers.hasOwnProperty(topic))
20365         {
20366             _notifiers[topic] = new Notifier();
20367         }
20368 
20369         return _notifiers[topic];
20370     },
20371 
20372     /**
20373      * Utility function to make a subscription to a particular topic. Only one
20374      * callback function is registered to a particular topic at any time.
20375      * @param {String} topic
20376      *     The full topic name. The topic name should follow the OpenAjax
20377      *     convention using dot notation (ex: finesse.api.User.1000).
20378      * @param {Function} callback
20379      *     The function that should be invoked with the data when an event
20380      *     is delivered to the specific topic.
20381      * @returns {Boolean}
20382      *     True if the subscription was made successfully and the callback was
20383      *     been registered. False if the subscription already exist, the
20384      *     callback was not overwritten.
20385      * @private
20386      */
20387     _subscribe = function (topic, callback) {
20388         _isInited();
20389 
20390         //Ensure that the same subscription isn't made twice.
20391         if (!_subscriptionID[topic]) {
20392             //Store the subscription ID using the topic name as the key.
20393             _subscriptionID[topic] = _hub.subscribe(topic,
20394                 //Invoke the callback just with the data object.
20395                 function (topic, data) {
20396                     callback(data);
20397                 });
20398             return true;
20399         }
20400         return false;
20401     },
20402 
20403     /**
20404      * Unsubscribe from a particular topic.
20405      * @param {String} topic : The full topic name.
20406      * @private
20407      */
20408     _unsubscribe = function (topic) {
20409         _isInited();
20410 
20411         //Unsubscribe from the topic using the subscription ID recorded when
20412         //the subscription was made, then delete the ID from data structure.
20413         _hub.unsubscribe(_subscriptionID[topic]);
20414         delete _subscriptionID[topic];
20415     },
20416 
20417     /**
20418      * Get my tab id.
20419      * @returns {String} tabid : The tabid of this container/gadget.
20420      * @private
20421      */
20422     _getMyTab = function () {
20423         if (_myTab === null)
20424         {
20425             try {
20426             _myTab = $(frameElement).closest("div.tab-panel").attr("id").replace("panel_", "");
20427             } catch (err) {
20428                 _logger.log("Error accessing current tab: " + err.message);
20429                _myTab = null;
20430             }
20431         }
20432         return _myTab;
20433     },
20434     
20435     /**
20436      * Callback function that is called when an activeTab message is posted to the Hub.
20437      * Notifies listener functions if this tab is the one that was just made active.
20438      * @param {String} tabId : The tabId which was just made visible.
20439      * @private
20440      */
20441     _tabTracker = function(tabId) {
20442         if (tabId === _getMyTab()) {
20443             if(!_visible) {
20444                 _visible = true;
20445                 _notifiers[finesse.containerservices.ContainerServices.Topics.ACTIVE_TAB].notifyListeners(this);
20446             }
20447         } else {
20448             _visible = false;
20449         }
20450     },
20451     
20452     /**
20453      * Make a request to set a particular tab active. This
20454      * method should be called after {@link finesse.containerservices.ContainerServices#addHandler}
20455      * to ensure the gadget gets properly initialized.
20456      * @param {String} tabId
20457      *    The tabId (not the label text) of the tab to make active.  If the id is invalid, no action will occur.
20458      * @private
20459      */
20460     _activateTab = function ( tabId ) {
20461         _logger.log("Sending request to activate tab: " + tabId);
20462         if(_hub){
20463             var data = {
20464                 type: "SetActiveTabReq",
20465                 data: { id: tabId },
20466                 invokeID: (new Date()).getTime()          
20467             };
20468             _hub.publish(_topics.REQUESTS, data);
20469         } else {
20470             throw new Error("Hub is not defined.");
20471         }
20472         
20473     },
20474 
20475     /**
20476      * Callback function that is called when a gadget view changed message is posted to the Hub.
20477      * @private
20478      */
20479     _gadgetViewChanged = function (data) {
20480         if (data) {
20481             var gadgetViewChangedEvent = new finesse.containerservices.GadgetViewChangedEvent(
20482                 data.gadgetId,
20483                 data.tabId,
20484                 data.maxAvailableHeight,
20485                 data.view);
20486 
20487             _notifiers[finesse.containerservices.ContainerServices.Topics.GADGET_VIEW_CHANGED_EVENT].notifyListeners(gadgetViewChangedEvent);
20488         }
20489     },
20490 
20491     /**
20492      * Callback function that is called when a max available height changed message is posted to the Hub.
20493      * @private
20494      */
20495     _maxAvailableHeightChanged = function (data) {
20496         if (data) {
20497             var maxAvailableHeightChangedEvent = new finesse.containerservices.MaxAvailableHeightChangedEvent(
20498                 data.maxAvailableHeight);
20499 
20500             _notifiers[finesse.containerservices.ContainerServices.Topics.MAX_AVAILABLE_HEIGHT_CHANGED_EVENT].notifyListeners(maxAvailableHeightChangedEvent);
20501         }
20502     },
20503 
20504     /**
20505      * Callback function that is called when a workflowActionEvent message is posted to the Hub.
20506      * Notifies listener functions if the posted object can be converted to a proper WorkflowActionEvent object.
20507      * @param {String} workflowActionEvent : The workflowActionEvent that was posted to the Hub
20508      * @private
20509      */
20510     _workflowActionEventTracker = function(workflowActionEvent) {
20511         var vWorkflowActionEvent = new finesse.containerservices.WorkflowActionEvent();
20512                 
20513         if (vWorkflowActionEvent.setWorkflowActionEvent(workflowActionEvent)) {
20514             _notifiers[finesse.containerservices.ContainerServices.Topics.WORKFLOW_ACTION_EVENT].notifyListeners(vWorkflowActionEvent);
20515         }
20516         // else
20517         // {
20518             //?console.log("Error in ContainerServices : _workflowActionEventTracker - could not map published HUB object to WorkflowActionEvent");
20519         // }
20520 
20521     },
20522 
20523     /**
20524      * Callback function that is called when a reloadGadget event message is posted to the Hub.
20525      *
20526      * Grabs the id of the gadget we want to reload from the data and reload it!
20527      *
20528      * @param {String} topic
20529      *      which topic the event came on (unused)
20530      * @param {Object} data
20531      *      the data published with the event
20532      * @private
20533      */
20534     _masterReloader = function (topic, data) {
20535         var gadgetId = data.gadgetId;
20536         if (gadgetId) {
20537             _container.reloadGadget(gadgetId);
20538         }
20539     },
20540     
20541     /**
20542      * Pulls the gadget id from the url parameters
20543      * @return {String} id of the gadget
20544      * @private
20545      */
20546     _findMyGadgetId = function () {
20547         if (gadgets && gadgets.util && gadgets.util.getUrlParameters()) {
20548             return gadgets.util.getUrlParameters().mid;
20549         }
20550     };
20551 
20552     return {
20553         /**
20554          * @class
20555          * This class provides container-level services for gadget developers, exposing container events by
20556          * calling a set of exposed functions. Gadgets can utilize the container dialogs and 
20557          * event handling (add/remove).
20558          * @example
20559          *    containerServices = finesse.containerservices.ContainerServices.init();
20560          *    containerServices.addHandler(
20561          *      finesse.containerservices.ContainerServices.Topics.ACTIVE_TAB, 
20562          *      function() {
20563          *          clientLogs.log("Gadget is now visible");  // log to Finesse logger
20564          *          // automatically adjust the height of the gadget to show the html
20565          *          gadgets.window.adjustHeight();
20566          *      });
20567          *    containerServices.makeActiveTabReq();
20568          *    
20569          * @constructs
20570          */
20571         _fakeConstuctor: function () {
20572             /* This is here so we can document init() as a method rather than as a constructor. */
20573         },
20574         
20575         /**
20576          * Initialize ContainerServices for use in gadget.
20577          * @param {Boolean} [master=false] Do not use this parameter from your gadget.
20578          * @returns ContainerServices instance.
20579          */
20580         init: function (master) {
20581             if (!_inited) {
20582                 _inited = true;
20583                 // Set shortcuts
20584                 _util = Utilities;
20585                 _authModes = _util.getAuthModes();
20586 
20587                 //init the hub only when it's available
20588                 if(gadgets.Hub) {
20589                     _hub = gadgets.Hub;
20590                 }
20591 
20592                 if(Topics) {
20593                     _topics = Topics;
20594                 }
20595 
20596                 if (master) {
20597                     _master = true;
20598                     _container = finesse.container.Container;
20599                     _publisher = new MasterPublisher();
20600 
20601                     // subscribe for reloading gadget events
20602                     // we only want the master ContainerServices handling these events
20603                     _hub.subscribe(_topics.RELOAD_GADGET, _topicCallback(_topics.RELOAD_GADGET));
20604                 } else {
20605                     _container = parent.finesse.container.Container;
20606                 }
20607             }
20608             
20609             this.makeActiveTabReq();
20610             
20611             //Return the CS object for object chaining.
20612             return this;
20613         },
20614 
20615         /**
20616          * Shows the jQuery UI Dialog with the specified parameters. The following are the
20617          * default parameters: <ul>
20618          *     <li> Title of "Cisco Finesse".</li>
20619          *     <li>Message of "A generic error has occured".</li>
20620          *     <li>The only button, "Ok", closes the dialog.</li>
20621          *     <li>Modal (blocks other dialogs).</li>
20622          *     <li>Not draggable.</li>
20623          *     <li>Fixed size.</li></ul>
20624          * @param {Object} options
20625          *  An object containing additional options for the dialog.
20626          * @param {String/Boolean} options.title
20627          *  Title to use. undefined defaults to "Cisco Finesse". false to hide
20628          * @param {Function} options.close
20629          *  A function to invoke when the dialog is closed.
20630          * @param {String} options.message
20631          *  The message to display in the dialog.
20632          *  Defaults to "A generic error has occurred."
20633          * @param {Boolean} options.isBlocking
20634          *  Flag indicating whether this dialog will block other dialogs from being shown (Modal).
20635          * @returns {jQuery} JQuery wrapped object of the dialog DOM element.
20636          * @see finesse.containerservices.ContainerServices#hideDialog
20637          */
20638         showDialog: function(options) {
20639             if ((_container.showDialog !== undefined) && (_container.showDialog !== this.showDialog)) {
20640                 return _container.showDialog(options);
20641             }
20642         },
20643         
20644         /**
20645          * Hides the jQuery UI Dialog.
20646          * @returns {jQuery} jQuery wrapped object of the dialog DOM element
20647          * @see finesse.containerservices.ContainerServices#showDialog
20648          */
20649         hideDialog: function() {
20650             if ((_container.hideDialog !== undefined) && (_container.hideDialog !== this.hideDialog)) {
20651                 return _container.hideDialog();
20652             }
20653         },
20654 
20655         /**
20656          *  Reloads the current gadget. 
20657          *  For use from within a gadget only.
20658          */
20659         reloadMyGadget: function () {
20660             var topic, gadgetId, data;
20661 
20662             if (!_master) {
20663                 // first unsubscribe this gadget from all topics on the hub
20664                 for (topic in _notifiers) {
20665                     if (_notifiers.hasOwnProperty(topic)) {
20666                         _unsubscribe(topic);
20667                         delete _notifiers[topic];
20668                     }
20669                 }
20670 
20671                 // send an asynch request to the hub to tell the master container
20672                 // services that we want to refresh this gadget
20673                 gadgetId = _findMyGadgetId();
20674                 data = {
20675                     type: "ReloadGadgetReq",
20676                     data: {gadgetId: gadgetId},
20677                     invokeID: (new Date()).getTime()          
20678                 };
20679                 _hub.publish(_topics.REQUESTS, data);
20680             }            
20681         },
20682 
20683         /**
20684          * Updates the url for this gadget and then reload it.
20685          * 
20686          * This allows the gadget to be reloaded from a different location
20687          * than what is uploaded to the current server. For example, this
20688          * would be useful for 3rd party gadgets to implement their own failover
20689          * mechanisms.
20690          *
20691          * For use from within a gadget only.
20692          *
20693          * @param {String} url
20694          *      url from which to reload gadget
20695          */
20696         reloadMyGadgetFromUrl: function (url) {
20697             if (!_master) {
20698                 var gadgetId = _findMyGadgetId();
20699 
20700                 // update the url in the container
20701                 _container.modifyGadgetUrl(gadgetId, url);
20702 
20703                 // reload it
20704                 this.reloadMyGadget();
20705             }
20706         },
20707         
20708         /**
20709          * Adds a handler for one of the supported topics provided by ContainerServices.  The callbacks provided
20710          * will be invoked when that topic is notified.  
20711          * @param {String} topic
20712          *  The Hub topic to which we are listening.
20713          * @param {Function} callback
20714          *  The callback function to invoke.
20715          * @see finesse.containerservices.ContainerServices.Topics
20716          * @see finesse.containerservices.ContainerServices#removeHandler
20717          */
20718         addHandler: function (topic, callback) {
20719             _isInited();
20720             var notifier = null;
20721             
20722             try {    
20723                 // For backwards compatibility...
20724                 if (topic === "tabVisible") {
20725                     if (window.console && typeof window.console.log === "function") {
20726                         window.console.log("WARNING - Using tabVisible as topic.  This is deprecated.  Use finesse.containerservices.ContainerServices.Topics.ACTIVE_TAB now!");
20727                     }
20728                     
20729                     topic = finesse.containerservices.ContainerServices.Topics.ACTIVE_TAB;
20730                 }
20731                 
20732                 // Add the callback to the notifier.
20733                 _util.validateHandler(callback);
20734             
20735                 notifier = _getNotifierReference(topic);
20736             
20737                 notifier.addListener(callback);
20738             
20739                 // Subscribe to the topic. _subscribe ensures that a topic is only subscribed to once,
20740                 // so attempt to subscribe each time a handler is added. This ensures that a topic is subscribed
20741                 // to only when necessary.
20742                 _subscribe(topic, _topicCallback(topic));
20743             
20744             } catch (err) {
20745                 throw new Error("addHandler(): " + err);
20746             }
20747         }, 
20748         
20749         /**
20750          * Removes a previously-added handler for one of the supported topics.
20751          * @param {String} topic
20752          *  The Hub topic from which we are removing the callback.
20753          * @param {Function} callback
20754          *  The name of the callback function to remove.
20755          * @see finesse.containerservices.ContainerServices.Topics
20756          * @see finesse.containerservices.ContainerServices#addHandler
20757          */
20758         removeHandler: function(topic, callback) {
20759             var notifier = null;
20760             
20761             try {
20762                 _util.validateHandler(callback);
20763     
20764                 notifier = _getNotifierReference(topic);
20765     
20766                 notifier.removeListener(callback);
20767             } catch (err) {
20768                 throw new Error("removeHandler(): " + err);
20769             }
20770         },
20771         
20772         /**
20773          * Wrapper API for publishing data on the Openajax hub
20774          * @param {String} topic
20775          *  The Hub topic to which we are publishing.
20776          * @param {Object} data
20777          *  The data to be published on the hub.
20778          */
20779         publish : function(topic , data){
20780             if(_hub){
20781                 _hub.publish(topic, data);
20782             } else {
20783                 throw new Error("Hub is not defined.");
20784             }
20785         },
20786 
20787         /**
20788          * Returns the visibility of current gadget.  Note that this 
20789          * will not be set until after the initialization of the gadget.
20790          * @return {Boolean} The visibility of current gadget.
20791          */
20792         tabVisible: function(){
20793             return _visible;
20794         },
20795         
20796         /**
20797          * Make a request to check the current tab.  The 
20798          * activeTab event will be invoked if on the active tab.  This
20799          * method should be called after {@link finesse.containerservices.ContainerServices#addHandler}
20800          * to ensure the gadget gets properly initialized.
20801          */
20802         makeActiveTabReq : function () {
20803             if(_hub){
20804                 var data = {
20805                     type: "ActiveTabReq",
20806                     data: {},
20807                     invokeID: (new Date()).getTime()          
20808                 };
20809                 _hub.publish(_topics.REQUESTS, data);
20810             } else {
20811                 throw new Error("Hub is not defined.");
20812             }
20813             
20814         },
20815 
20816         /**
20817          * Make a request to set a particular tab active. This
20818          * method should be called after {@link finesse.containerservices.ContainerServices#addHandler}
20819          * to ensure the gadget gets properly initialized.
20820          * @param {String} tabId
20821          *    The tabId (not the label text) of the tab to make active.  If the id is invalid, no action will occur.
20822          */
20823         activateTab : function (tabId) {
20824             _activateTab(tabId);
20825         },
20826         
20827         /**
20828          * Make a request to set this container's tab active. This
20829          * method should be called after {@link finesse.containerservices.ContainerServices#addHandler}
20830          * to ensure the gadget gets properly initialized.
20831          */
20832         activateMyTab : function () {
20833             _activateTab( _getMyTab() );
20834         },
20835         
20836         /**
20837          * Get the tabId of my container/gadget.
20838          * @returns {String} tabid : The tabid of this container/gadget.
20839          */
20840         getMyTabId : function () {
20841             return _getMyTab();
20842         },
20843 
20844         /**
20845          * Gets the id of the gadget.
20846          * @returns {number} the id of the gadget
20847          */
20848         getMyGadgetId : function () {
20849             return _findMyGadgetId();
20850         },
20851 
20852         //BEGIN TEST CODE//
20853         /**
20854          * Test code added to expose private functions that are used by unit test
20855          * framework. This section of code is removed during the build process
20856          * before packaging production code. The [begin|end]TestSection are used
20857          * by the build to identify the section to strip.
20858          * @ignore
20859          */
20860         beginTestSection : 0,
20861 
20862         /**
20863          * @ignore
20864          */
20865         getTestObject: function () {
20866             //Load mock dependencies.
20867             var _mock = new MockControl();
20868             _util = _mock.createMock(Utilities);
20869             _hub = _mock.createMock(gadgets.Hub);
20870             _inited = true;
20871             return {
20872                 //Expose mock dependencies
20873                 mock: _mock,
20874                 hub: _hub,
20875                 util: _util,
20876                 addHandler: this.addHandler,
20877                 removeHandler: this.removeHandler
20878             };
20879         },
20880 
20881         /**
20882          * @ignore
20883          */
20884        endTestSection: 0
20885         //END TEST CODE//
20886     };
20887     }());
20888     
20889     ContainerServices.Topics = /** @lends finesse.containerservices.ContainerServices.Topics.prototype */ {
20890         /**
20891          * Topic for subscribing to be notified when the active tab changes.
20892          * The provided callback will be invoked when the tab that the gadget 
20893          * that subscribes with this becomes active.  To ensure code is called
20894          * when the gadget is already on the active tab use the 
20895          * {@link finesse.containerservices.ContainerServices#makeActiveTabReq}
20896          * method.
20897          */
20898         ACTIVE_TAB: "finesse.containerservices.activeTab",
20899 
20900         /**
20901          * Topic for WorkflowAction events traffic.
20902          * The provided callback will be invoked when a WorkflowAction needs
20903          * to be handled.  The callback will be passed a {@link finesse.containerservices.WorkflowActionEvent}
20904          * that can be used to interrogate the WorkflowAction and determine to use or not.
20905          */
20906         WORKFLOW_ACTION_EVENT: "finesse.containerservices.workflowActionEvent",
20907         
20908         /**
20909          * Topic for Timer Tick event.
20910          * The provided callback will be invoked when this event is fired.
20911          * The callback will be passed a {@link finesse.containerservices.TimerTickEvent}.
20912          */
20913         TIMER_TICK_EVENT : "finesse.containerservices.timerTickEvent",
20914 
20915         /**
20916          * Topic for Reload Gadget events traffic.
20917          * Only the master ContainerServices instance will handle this event.
20918          */
20919         RELOAD_GADGET_EVENT: "finesse.containerservices.reloadGadget",
20920         
20921         /**
20922          * Topic for listening to gadget view changed events.
20923          * The provided callback will be invoked when a gadget changes view.
20924          * The callback will be passed a {@link finesse.containerservices.GadgetViewChangedEvent}.
20925          */
20926         GADGET_VIEW_CHANGED_EVENT: "finesse.containerservices.gadgetViewChangedEvent",
20927 
20928         /**
20929          * Topic for listening to max available height changed events.
20930          * The provided callback will be invoked when the maximum height available to a maximized gadget changes.
20931          * This event is only meant for maximized gadgets and will not be published unless a maximized gadget exists.
20932          * The callback will be passed a {@link finesse.containerservices.MaxAvailableHeightChangedEvent}.
20933          */
20934         MAX_AVAILABLE_HEIGHT_CHANGED_EVENT: "finesse.containerservices.maxAvailableHeightChangedEvent",
20935         
20936         /**
20937          * @class This is the set of Topics used for subscribing for events from ContainerServices.
20938          * Use {@link finesse.containerservices.ContainerServices#addHandler} to subscribe to the topic.
20939          * 
20940          * @constructs
20941          */
20942         _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
20943     };
20944     
20945     window.finesse = window.finesse || {};
20946     window.finesse.containerservices = window.finesse.containerservices || {};
20947     window.finesse.containerservices.ContainerServices = ContainerServices;
20948 
20949     return ContainerServices;
20950  });
20951 /**
20952  * FinesseToaster is a utility class to show toaster notification in Finesse.
20953  * FinesseToaster leverages HTML5 Notification API to display Toaster
20954  * Notification.
20955  * 
20956  */
20957 
20958 define('containerservices/FinesseToaster',[],function() {
20959 
20960 	var FinesseToaster = (function() {
20961 		/** @lends finesse.containerservices.FinesseToaster.prototype */
20962 
20963 		var
20964 
20965 		/** How long the toaster will be displayed by default. Default timeout is 8 seconds */
20966 		AUTO_CLOSE_TIME = 8000,
20967 
20968 		/** PERMISSION_GRANTED constant for granted string */
20969 		PERMISSION_GRANTED = 'granted',
20970 
20971 		/** PERMISSION_DEFAULT constant for default string */
20972 		PERMISSION_DEFAULT = 'default',
20973 
20974 		/** PERMISSION_DENIED constant for denied string */
20975 		PERMISSION_DENIED = 'denied',
20976 
20977 		/** ICON_PATH constant for holding path icon images */
20978 		ICON_PATH = '/desktop/theme/finesse/images/modules/',
20979 
20980 		/**
20981 		 * Shortcut reference to finesse.cslogger.ClientLogger singleton This
20982 		 * will be set by init(), it should already be initialized by
20983 		 * PageServices
20984 		 *
20985 		 * @private
20986 		 */
20987 		_logger,
20988 		
20989 		/**
20990 		 * Boolean variable to determine if finesse toaster is enabled 
20991 		 * @private
20992 		 */
20993 		_isToasterDisabled = false,
20994 		
20995 		/**
20996 		 * Boolean variable to determine if finesse toaster is initialized 
20997 		 * @private
20998 		 */
20999 		_isToasterInitialized,
21000 		
21001 		/**
21002 		 * this function check of provided parameter is a javascript function.
21003 		 * 
21004 		 * @private
21005 		 */
21006 		isFunction = function(param) {
21007 			if (typeof param === "function") {
21008 				return true;
21009 			}
21010 			return false;
21011 		},
21012 
21013 		/**
21014 		 * _createNotification creates Notification instance
21015 		 *
21016 		 * @param {String}
21017 		 *            title title string should be displayed in the Toaster
21018 		 * @param {Object}
21019 		 *            options JSON object for notification options.
21020 		 */
21021 		_createNotification = function(title, options) {
21022 			var notification = new window.Notification(title, options);
21023 			return notification;
21024 		},
21025 
21026 		/**
21027 		 * _setAutoClose set the auto close time for toaster, it checks if
21028 		 * client code passed custom time out for the toaster, otherwise it set
21029 		 * the AUTO_CLOSE_TIME
21030 		 *
21031 		 * @param {Object}
21032 		 *            notification window.Notification that creates Html5
21033 		 *            Notification.
21034 		 * @param {String}
21035 		 *            autoClose autoClose time of the Toaster
21036 		 * @return toasterTimeout Toaster Timeout
21037 		 */
21038 		_setAutoClose = function(notification, autoClose) {
21039 
21040 			// check if custom close time passed other wise set
21041 			// DEFAULT_AUTO_CLOSE
21042 			var autoCloseTime = (autoClose && !isNaN(autoClose)) ? autoClose
21043 					: AUTO_CLOSE_TIME,
21044 			// set the time out for notification toaster
21045 			toasterTimer = setTimeout(function() {
21046 				notification.close();
21047 			}, autoCloseTime);
21048 
21049 			return toasterTimer;
21050 
21051 		},
21052 
21053 		/** This method will request permission to display Toaster. */
21054 		_requestPermission = function() {
21055 			// If they are not denied (i.e. default)
21056 			if (window.Notification
21057 					&& window.Notification.permission !== PERMISSION_DENIED) {
21058 				// Request permission
21059 				window.Notification
21060 						.requestPermission(function(status) {
21061 
21062 							// Change based on user's decision
21063 							if (window.Notification.permission !== status) {
21064 								window.Notification.permission = status;
21065 							}
21066 							_logger
21067 									.log("FinesseToaster.requestPermission(): request permission status "
21068 											+ status);
21069 
21070 						});
21071 
21072 			} else {
21073 				_logger
21074 						.log("FinesseToaster.requestPermission(): Notification not supported or permission denied.");
21075 			}
21076 
21077 		},
21078 
21079 		/**
21080 		 * This method will add onclick and onerror listener to Notification.
21081 		 * on click of toaster the gadget which originally had focus may loose
21082 		 * the focus. To get the back the focus on any element inside the gadget
21083 		 * use the on click callback handler.
21084 		 * 
21085 		 * @param {Object}
21086 		 *            notification window.Notification that creates Html5
21087 		 *            Notification.
21088 		 * @param {Object}
21089 		 *            options JSON object for notification options.
21090 		 */
21091 		_addToasterListeners = function(notification, options, toasterTimer) {
21092 			// this is onlcik handler of toaster. this handler will be invoked
21093 			// on click of toaster
21094 			notification.onclick = function() {
21095 				// in case of manually closed toaster, stop the notification
21096 				// auto close method to be invoked
21097 				clearTimeout(toasterTimer);
21098 				// This will maximize/activate chrome browser on click of
21099 				// toaster. this handling required only in case of Chrome
21100 				if (window.chrome) {
21101 					parent.focus();
21102 				}
21103 
21104 				if (options && options.onclick) {
21105 					if (isFunction(options.onclick)) {
21106 						options.onclick();
21107 					} else {
21108 						throw new Error("onclick callback must be a function");
21109 					}
21110 				}
21111 
21112 				//close toaster upon click
21113 				this.close();
21114 
21115 			};
21116 
21117 			// this is onerror handler of toaster, if there is any error while
21118 			// loading toaster this hadnler will be invoked
21119 			notification.onerror = function() {
21120 				if (options && options.onerror) {
21121 					if (isFunction(options.onerror)) {
21122 						options.onerror();
21123 					} else {
21124 						throw new Error("onerror callback must be a function");
21125 					}
21126 				}
21127 			};
21128 		};
21129 		
21130 		return {
21131 
21132 			/**
21133 			 * @class
21134 			 *  FinesseToaster is a utility class to show toaster
21135 			 *        notification in Finesse. FinesseToaster leverages <a
21136 			 *        href="https://www.w3.org/TR/notifications/">HTML5
21137 			 *        Notification</a> API to display Toaster Notification. 
21138 			 *       <p> <a
21139 			 *        href="https://developer.mozilla.org/en/docs/Web/API/notification#Browser_compatibility">For
21140 			 *        HTML5 Notification API and browser compatibility, please click
21141 			 *        here.</a></p>
21142 			 * 
21143 			 * @constructs
21144 			 */
21145 			_fakeConstuctor : function() {
21146 
21147 			},
21148 			/**
21149 			 * TOASTER_DEFAULT_ICONS constants has list of predefined icons (e.g INCOMING_CALL_ICON).
21150 			 * <p><b>Constant list</b></p>
21151 			 * <ul>
21152 			 * <li>TOASTER_DEFAULT_ICONS.INCOMING_CALL_ICON</li>
21153 			 * </ul>
21154 			 */
21155 			TOASTER_DEFAULT_ICONS : {
21156 				INCOMING_CALL_ICON : ICON_PATH + "incoming_call.png"
21157 			},
21158 
21159 			/**
21160 			 * <b>showToaster </b>: shows Toaster Notification.
21161 			 *
21162 			 * @param {String}
21163 			 *            <b>title</b> : title string should be displayed in the Toaster
21164 			 * @param {Object}
21165 			 *            options is JSON object for notification options. 
21166 			 *            <ul>
21167 			 *            <li><b>options</b> = { </li>
21168 			 *            <li><b>body</b> : The body string of the notification as
21169 			 *            specified in the options parameter of the constructor.</li>
21170 			 *            <li><b>icon</b>: The URL of the image used as an icon of the
21171 			 *            notification as specified in the options parameter of
21172 			 *            the constructor.</li>
21173 			 *            <li><b>autoClose</b> : custom auto close time of the toaster</li>
21174 			 *            <li><b>showWhenVisible</b> : 'true' toaster shows up even when page is 
21175 			 *            visible,'false' toaster  shows up only when page is invisible </li>
21176 			 *           <li> }</li>
21177 			 *            </ul>
21178 			 *
21179 			 */
21180 			showToaster : function(title, options) {
21181 				
21182 				if(!_isToasterInitialized){
21183 					throw new Error("FinesseToaster.showToaster() : Finesse toaster is not initialized");
21184 				}
21185 				
21186 				if(_isToasterDisabled){
21187 					_logger.log("FinesseToaster.showToaster() : FinesseToaster is disabled");
21188 					return;
21189 				}
21190 
21191 				var notification, toasterTimer;
21192 
21193 				// If notifications are granted show the notification
21194 				if (window.Notification
21195 						&& window.Notification.permission === PERMISSION_GRANTED) {
21196 
21197 					// document.hasFocus() used over document.hidden to keep the consistent behavior across mozilla/chrome
21198 					if (document.hasFocus() === false
21199 							|| options.showWhenVisible) {
21200 						if (_logger && AUTO_CLOSE_TIME > -1) {
21201 							notification = _createNotification(title, options);
21202 
21203 							// set the auto close time out of the toaster
21204 							toasterTimer = _setAutoClose(notification,
21205 									options.autoClose);
21206 
21207 							// and Toaster Event listeners. eg. onclick , onerror.
21208 							_addToasterListeners(notification, options,
21209 									toasterTimer);
21210 						} 
21211 					} else {
21212 						_logger
21213 								.log("FinesseToaster supressed : Page is visible and  FineeseToaster.options.showWhenVisible is false");
21214 					}
21215 
21216 				}
21217 
21218 				return notification;
21219 			},
21220 
21221 			/**
21222 			 * initialize FininseToaster and inject dependencies. this method
21223 			 * will also request permission in browser from user to display
21224 			 * Toaster Notification.
21225 			 *
21226 			 *@param {Object}
21227 			 *          Could be finesse.container.Config or finesse.gadget.Config based on where it is getting initialized from.
21228 			 *            
21229 			 * @param {Object}
21230 			 *            finesse.cslogger.ClientLogger
21231 			 * @return finesse.containerservices.FinesseToaster
21232 			 */
21233 			init : function(config , logger) {
21234 				
21235 				_isToasterInitialized = true;
21236 				
21237 				// This is for injecting mocked logger.
21238 				if (logger) {
21239 					_logger = logger;
21240 				} else {
21241 					_logger = finesse.cslogger.ClientLogger;
21242 				}
21243 				
21244 				//set default toaster notification timeout
21245 				if (config && config.toasterNotificationTimeout) {
21246 					AUTO_CLOSE_TIME = Number(config.toasterNotificationTimeout) * 1000;
21247 					
21248 					if(AUTO_CLOSE_TIME === 0){
21249 						//Finesse toaster has been disabled
21250 						_isToasterDisabled = true;
21251 					}
21252 				} 
21253 
21254 				// Request permission
21255 				_requestPermission();
21256 				return finesse.containerservices.FinesseToaster;
21257 			}
21258 		};
21259 
21260 	}());
21261 
21262 	window.finesse = window.finesse || {};
21263 	window.finesse.containerservices = window.finesse.containerservices || {};
21264 	window.finesse.containerservices.FinesseToaster = FinesseToaster;
21265 
21266 	return FinesseToaster;
21267 });
21268 
21269 /**
21270  * This "interface" is just a way to easily jsdoc the Object callback handlers.
21271  *
21272  * @requires finesse.clientservices.ClientServices
21273  * @requires Class
21274  */
21275 /** @private */
21276 define('interfaces/RestObjectHandlers',[
21277     "FinesseBase",
21278      "utilities/Utilities",
21279      "restservices/Notifier",
21280      "clientservices/ClientServices",
21281      "clientservices/Topics"
21282 ],
21283 function () {
21284 
21285     var RestObjectHandlers = ( function () { /** @lends finesse.interfaces.RestObjectHandlers.prototype */
21286         
21287         return {
21288 
21289             /**
21290              * @class
21291              * This "interface" defines REST Object callback handlers, passed as an argument to
21292              * Object getter methods in cases where the Object is going to be created.
21293              * 
21294              * @param {Object} [handlers]
21295              *     An object containing callback handlers for instantiation and runtime
21296              *     Callback to invoke upon successful instantiation, passes in REST object.
21297              * @param {Function} [handlers.onLoad(this)]
21298              *     Callback to invoke upon loading the data for the first time.
21299              * @param {Function} [handlers.onChange(this)]
21300              *     Callback to invoke upon successful update object (PUT)
21301              * @param {Function} [handlers.onAdd(this)]
21302              *     Callback to invoke upon successful update to add object (POST)
21303              * @param {Function} [handlers.onDelete(this)]
21304              *     Callback to invoke upon successful update to delete object (DELETE)
21305              * @param {Function} [handlers.onError(rsp)]
21306              *     Callback to invoke on update error (refresh or event)
21307              *     as passed by finesse.restservices.RestBase.restRequest()<br>
21308              *     {<br>
21309              *         status: {Number} The HTTP status code returned<br>
21310              *         content: {String} Raw string of response<br>
21311              *         object: {Object} Parsed object of response<br>
21312              *         error: {Object} Wrapped exception that was caught<br>
21313              *         error.errorType: {String} Type of error that was caught<br>
21314              *         error.errorMessage: {String} Message associated with error<br>
21315              *     }<br>
21316              *     <br>
21317              * Note that RestCollections have two additional callback handlers:<br>
21318              * <br>
21319              * @param {Function} [handlers.onCollectionAdd(this)]: when an object is added to this collection
21320              * @param {Function} [handlers.onCollectionDelete(this)]: when an object is removed from this collection
21321 
21322              * @constructs
21323              */
21324             _fakeConstuctor: function () {
21325                 /* This is here to enable jsdoc to document this as a class. */
21326             }
21327         };
21328     }());
21329 
21330 window.finesse = window.finesse || {};
21331 window.finesse.interfaces = window.finesse.interfaces || {};
21332 window.finesse.interfaces.RestObjectHandlers = RestObjectHandlers;
21333 
21334 return RestObjectHandlers;
21335 
21336 });
21337 
21338 
21339 /**
21340  * This "interface" is just a way to easily jsdoc the REST request handlers.
21341  *
21342  * @requires finesse.clientservices.ClientServices
21343  * @requires Class
21344  */
21345 /** @private */
21346 define('interfaces/RequestHandlers',[
21347     "FinesseBase",
21348      "utilities/Utilities",
21349      "restservices/Notifier",
21350      "clientservices/ClientServices",
21351      "clientservices/Topics"
21352 ],
21353 function () {
21354 
21355     var RequestHandlers = ( function () { /** @lends finesse.interfaces.RequestHandlers.prototype */
21356         
21357         return {
21358 
21359             /**
21360              * @class
21361              * This "interface" defines REST Object callback handlers, passed as an argument to
21362              * Object getter methods in cases where the Object is going to be created.
21363              * 
21364              * @param {Object} handlers
21365              *     An object containing the following (optional) handlers for the request:<ul>
21366              *         <li><b>success(rsp):</b> A callback function for a successful request to be invoked with the following
21367              *         response object as its only parameter:<ul>
21368              *             <li><b>status:</b> {Number} The HTTP status code returned</li>
21369              *             <li><b>content:</b> {String} Raw string of response</li>
21370              *             <li><b>object:</b> {Object} Parsed object of response</li></ul>
21371              *         <li><b>error(rsp):</b> An error callback function for an unsuccessful request to be invoked with the
21372              *         error response object as its only parameter:<ul>
21373              *             <li><b>status:</b> {Number} The HTTP status code returned</li>
21374              *             <li><b>content:</b> {String} Raw string of response</li>
21375              *             <li><b>object:</b> {Object} Parsed object of response (HTTP errors)</li>
21376              *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
21377              *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
21378              *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
21379              *             </ul></li>
21380              *         </ul>
21381 
21382              * @constructs 
21383              */
21384             _fakeConstuctor: function () {
21385                 /* This is here to enable jsdoc to document this as a class. */
21386             }
21387         };
21388     }());
21389 
21390 window.finesse = window.finesse || {};
21391 window.finesse.interfaces = window.finesse.interfaces || {};
21392 window.finesse.interfaces.RequestHandlers = RequestHandlers;
21393 
21394 finesse = finesse || {};
21395 /** @namespace These interfaces are just a convenience for documenting common parameter structures. */
21396 finesse.interfaces = finesse.interfaces || {};
21397 
21398 return RequestHandlers;
21399 
21400 });
21401 
21402 
21403 
21404 define('gadget/Config',[
21405 	"utilities/Utilities"
21406 ], function (Utilities) {  
21407 	var Config = (function () { /** @lends finesse.gadget.Config.prototype */
21408 		
21409 		if (gadgets && gadgets.Prefs) {
21410 		
21411 			var _prefs = new gadgets.Prefs();
21412 		
21413 			return {
21414 				/**
21415 				 * The base64 encoded "id:password" string used for authentication.
21416 				 */
21417 				authorization: Utilities.getUserAuthString(),
21418 				
21419 				/**
21420 				 * The  auth token string used for authentication in SSO deployments.
21421 				 */
21422 				authToken: Utilities.getToken(),
21423 				
21424 				/**
21425 				 * The country code of the client (derived from locale).
21426 				 */
21427 				country: _prefs.getString("country"),
21428 				
21429 				/**
21430 				 * The language code of the client (derived from locale).
21431 				 */
21432 				language: _prefs.getString("language"),
21433 				
21434 				/**
21435 				 * The locale of the client.
21436 				 */
21437 				locale: _prefs.getString("locale"),
21438 				
21439 				/**
21440 				 * The Finesse server IP/host as reachable from the browser.
21441 				 */
21442 				host: _prefs.getString("host"),
21443 				
21444 				/**
21445 				 * The Finesse server host's port reachable from the browser.
21446 				 */
21447 				hostPort: _prefs.getString("hostPort"),
21448 				
21449 				/**
21450 				 * The extension of the user.
21451 				 */
21452 				extension: _prefs.getString("extension"),
21453 				
21454 				/**
21455 				 * One of the work modes found in {@link finesse.restservices.User.WorkMode}, or something false (undefined) for a normal login.
21456 				 */
21457 				mobileAgentMode: _prefs.getString("mobileAgentMode"),
21458 				
21459 				/**
21460 				 * The dial number to use for mobile agent, or something false (undefined) for a normal login.
21461 				 */
21462 				mobileAgentDialNumber: _prefs.getString("mobileAgentDialNumber"),
21463 				
21464 				/**
21465 				 * The domain of the XMPP server.
21466 				 */
21467 				xmppDomain: _prefs.getString("xmppDomain"),
21468 				
21469 				/**
21470 				 * The pub sub domain where the pub sub service is running.
21471 				 */
21472 				pubsubDomain: _prefs.getString("pubsubDomain"),
21473 				
21474 				/**
21475 				 * The Finesse API IP/host as reachable from the gadget container.
21476 				 */
21477 				restHost: _prefs.getString("restHost"),
21478 				
21479 				/**
21480 				 * The type of HTTP protocol (http or https).
21481 				 */
21482 				scheme: _prefs.getString("scheme"),
21483 				
21484 				/**
21485 				 * The localhost fully qualified domain name.
21486 				 */
21487 				localhostFQDN: _prefs.getString("localhostFQDN"),
21488 				
21489 				/**
21490 				 * The localhost port.
21491 				 */
21492 				localhostPort: _prefs.getString("localhostPort"),
21493 				
21494 				/**
21495 				 * The id of the team the user belongs to.
21496 				 */
21497 				teamId: _prefs.getString("teamId"),
21498 				
21499 				/**
21500 				 * The name of the team the user belongs to.
21501 				 */
21502 				teamName: _prefs.getString("teamName"),
21503 				
21504 				/**
21505 				 * The drift time between the client and the server in milliseconds.
21506 				 */
21507 				clientDriftInMillis: _prefs.getInt("clientDriftInMillis"),
21508 				
21509 				/**
21510 				 * The client compatibility mode configuration (true if it is or false otherwise).
21511 				 */
21512 				compatibilityMode: _prefs.getString("compatibilityMode"),
21513 				
21514 				/**
21515 				 * The peripheral Id that Finesse is connected to.
21516 				 */
21517 				peripheralId: _prefs.getString("peripheralId"),
21518 				
21519 				/**
21520 				 * The auth mode of the finesse deployment.
21521 				 */
21522 				systemAuthMode: _prefs.getString("systemAuthMode"),
21523 				
21524 				
21525 				/**
21526 				 * The time for which fineese toaster stay on the browser.
21527 				 */
21528 				toasterNotificationTimeout: _prefs.getString("toasterNotificationTimeout"),
21529 				
21530 				/**
21531 				* @class
21532 				* The Config object for gadgets within the Finesse desktop container which
21533 				* contains configuration data provided by the container page.
21534 				* @constructs
21535 				*/
21536 				_fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
21537 				
21538 			};
21539 		} else {
21540 			return {};
21541 		}
21542 	}());
21543 	
21544 	/** Assign to container and gadget namespace to have config available in both  */
21545 	window.finesse = window.finesse || {};
21546 	window.finesse.container = window.finesse.container || {};
21547 	window.finesse.container.Config = window.finesse.container.Config || Config;
21548 
21549 	window.finesse.gadget = window.finesse.gadget || {};
21550 	window.finesse.gadget.Config = Config;
21551 
21552 	return Config;
21553 });
21554 define('finesse',[
21555     'restservices/Users',
21556     'restservices/Teams',
21557     'restservices/SystemInfo',
21558     'restservices/Media',
21559     'restservices/MediaDialogs',
21560     'restservices/DialogLogoutActions',
21561     'restservices/InterruptActions',
21562     'restservices/ReasonCodeLookup',
21563     'utilities/I18n',
21564     'utilities/Logger',
21565     'utilities/SaxParser',
21566     'utilities/BackSpaceHandler',
21567     'cslogger/ClientLogger',
21568     'cslogger/FinesseLogger',
21569     'containerservices/ContainerServices',
21570     'containerservices/FinesseToaster',
21571     'interfaces/RestObjectHandlers',
21572     'interfaces/RequestHandlers',
21573     'gadget/Config'
21574 ],
21575 function () {
21576     return window.finesse;
21577 });
21578 
21579 require(["finesse"]);
21580 return require('finesse'); }));
21581 
21582 // Prevent other JS files from wiping out window.finesse from the namespace
21583 var finesse = window.finesse;