1 /**
  2  * Cisco Finesse - JavaScript Library
  3  * Version 11.5(1)
  4  * Cisco Systems, Inc.
  5  * http://www.cisco.com/
  6  *
  7  * Portions created or assigned to Cisco Systems, Inc. are
  8  * Copyright (c) 2016 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     window.finesse = window.finesse || {};
3176     window.finesse.utilities = window.finesse.utilities || {};
3177     window.finesse.utilities.Utilities = Utilities;
3178     
3179     return Utilities;
3180 });
3181 
3182 /** The following comment is to prevent jslint errors about 
3183  * using variables before they are defined.
3184  */
3185 /*global finesse*/
3186 
3187 /**
3188  * Initiated by the Master to create a shared BOSH connection.
3189  *
3190  * @requires Utilities
3191  */
3192 
3193 /**
3194  * @class
3195  * Establishes a shared event connection by creating a communication tunnel
3196  * with the notification server and consume events which could be published.
3197  * Public functions are exposed to register to the connection status information
3198  * and events.
3199  * @constructor
3200  * @param {String} host
3201  *     The host name/ip of the Finesse server.
3202  * @throws {Error} If required constructor parameter is missing.
3203  */
3204 /** @private */
3205 define('clientservices/MasterTunnel',["utilities/Utilities"], function (Utilities) {
3206      var MasterTunnel = function (host, scheme) { 
3207         if (typeof host !== "string" || host.length === 0) {
3208             throw new Error("Required host parameter missing.");
3209         }
3210 
3211         var
3212 
3213         /**
3214          * Flag to indicate whether the tunnel frame is loaded.
3215          * @private
3216          */
3217         _isTunnelLoaded = false,
3218 
3219         /**
3220          * Short reference to the Finesse utility.
3221          * @private
3222          */
3223         _util = Utilities,
3224 
3225         /**
3226          * The URL with host and port to the Finesse server.
3227          * @private
3228          */
3229         _tunnelOrigin,
3230 
3231         /**
3232          * Location of the tunnel HTML URL.
3233          * @private
3234          */
3235         _tunnelURL,
3236         
3237         /**
3238          * The port on which to connect to the Finesse server to load the eventing resources.
3239          * @private
3240          */
3241         _tunnelOriginPort,
3242         
3243         /**
3244          * Flag to indicate whether we have processed the tunnel config yet.
3245          * @private
3246          */
3247         _isTunnelConfigInit = false,
3248 
3249         /**
3250          * The tunnel frame window object.
3251          * @private
3252          */
3253         _tunnelFrame,
3254 
3255         /**
3256          * The handler registered with the object to be invoked when an event is
3257          * delivered by the notification server.
3258          * @private
3259          */
3260         _eventHandler,
3261         
3262         /**
3263          * The handler registered with the object to be invoked when presence is
3264          * delivered by the notification server.
3265          * @private
3266          */
3267         _presenceHandler,
3268 
3269         /**
3270          * The handler registered with the object to be invoked when the BOSH
3271          * connection has changed states. The object will contain the "status"
3272          * property and a "resourceID" property only if "status" is "connected".
3273          * @private
3274          */
3275         _connInfoHandler,
3276 
3277         /**
3278          * The last connection status published by the JabberWerx library.
3279          * @private
3280          */
3281         _statusCache,
3282 
3283         /**
3284          * The last event sent by notification server.
3285          * @private
3286          */
3287         _eventCache,
3288 
3289         /**
3290          * The ID of the user logged into notification server.
3291          * @private
3292          */
3293         _id,
3294 
3295         /**
3296          * The domain of the XMPP server, representing the portion of the JID
3297          * following '@': userid@domain.com
3298          * @private
3299          */
3300         _xmppDomain,
3301 
3302         /**
3303          * The password of the user logged into notification server.
3304          * @private
3305          */
3306         _password,
3307 
3308         /**
3309          * The jid of the pubsub service on the XMPP server
3310          * @private
3311          */
3312         _pubsubDomain,
3313 
3314         /**
3315          * The resource to use for the BOSH connection.
3316          * @private
3317          */
3318         _resource,
3319 
3320         /**
3321          * The resource ID identifying the client device (that we receive from the server).
3322          * @private
3323          */
3324         _resourceID,
3325 
3326         /**
3327          * The different types of messages that could be sent to the parent frame.
3328          * The types here should be understood by the parent frame and used to
3329          * identify how the message is formatted.
3330          * @private
3331          */
3332         _TYPES = {
3333             EVENT: 0,
3334             ID: 1,
3335             PASSWORD: 2,
3336             RESOURCEID: 3,
3337             STATUS: 4,
3338             XMPPDOMAIN: 5,
3339             PUBSUBDOMAIN: 6,
3340             SUBSCRIBE: 7,
3341             UNSUBSCRIBE: 8,
3342             PRESENCE: 9,
3343             CONNECT_REQ: 10
3344         },
3345 
3346         _handlers = {
3347             subscribe: {},
3348             unsubscribe: {}
3349         },
3350         
3351 
3352         /**
3353          * Create a connection info object.
3354          * @returns {Object}
3355          *     A connection info object containing a "status" and "resourceID".
3356          * @private
3357          */
3358         _createConnInfoObj = function () {
3359             return {
3360                 status: _statusCache,
3361                 resourceID: _resourceID
3362             };
3363         },
3364 
3365         /**
3366          * Utility function which sends a message to the dynamic tunnel frame
3367          * event frame formatted as follows: "type|message".
3368          * @param {Number} type
3369          *     The category type of the message.
3370          * @param {String} message
3371          *     The message to be sent to the tunnel frame.
3372          * @private
3373          */
3374         _sendMessage = function (type, message) {
3375             message = type + "|" + message;
3376             _util.sendMessage(message, _tunnelFrame, _tunnelOrigin);
3377         },
3378 
3379         /**
3380          * Utility to process the response of a subscribe request from
3381          * the tunnel frame, then invoking the stored callback handler
3382          * with the respective data (error, when applicable)
3383          * @param {String} data
3384          *     The response in the format of "node[|error]"
3385          * @private
3386          */
3387         _processSubscribeResponse = function (data) {
3388             var dataArray = data.split("|"),
3389             node = dataArray[0],
3390             err;
3391             
3392             //Error is optionally the second item in the array
3393             if (dataArray.length) {
3394                 err = dataArray[1];
3395             }
3396             
3397             // These response handlers are short lived and should be removed and cleaned up immediately after invocation.
3398             if (_handlers.subscribe[node]) {
3399                 _handlers.subscribe[node](err);
3400                 delete _handlers.subscribe[node];
3401             }
3402         },
3403 
3404         /**
3405          * Utility to process the response of an unsubscribe request from
3406          * the tunnel frame, then invoking the stored callback handler
3407          * with the respective data (error, when applicable)
3408          * @param {String} data
3409          *     The response in the format of "node[|error]"
3410          * @private
3411          */
3412         _processUnsubscribeResponse = function (data) {
3413             var dataArray = data.split("|"),
3414             node = dataArray[0],
3415             err;
3416             
3417             //Error is optionally the second item in the array
3418             if (dataArray.length) {
3419                 err = dataArray[1];
3420             }
3421             
3422             // These response handlers are short lived and should be removed and cleaned up immediately after invocation.
3423             if (_handlers.unsubscribe[node]) {
3424                 _handlers.unsubscribe[node](err);
3425                 delete _handlers.unsubscribe[node];
3426             }
3427         },
3428 
3429         /**
3430          * Handler for messages delivered by window.postMessage. Listens for events
3431          * published by the notification server, connection status published by
3432          * the JabberWerx library, and the resource ID created when the BOSH
3433          * connection has been established.
3434          * @param {Object} e
3435          *     The message object as provided by the window.postMessage feature.
3436          * @private
3437          */
3438         _messageHandler = function (e) {
3439             var
3440 
3441             //Extract the message type and message data. The expected format is
3442             //"type|data" where type is a number represented by the TYPES object.
3443             delimPos = e.data.indexOf("|"),
3444             type = Number(e.data.substr(0, delimPos)),
3445             data =  e.data.substr(delimPos + 1);
3446             
3447             //Accepts messages and invoke the correct registered handlers.
3448             switch (type) {
3449             case _TYPES.EVENT:
3450                 _eventCache = data;
3451                 if (typeof _eventHandler === "function") {
3452                     _eventHandler(data);
3453                 }
3454                 break;
3455             case _TYPES.STATUS:
3456                 _statusCache = data;
3457 
3458                 //A "loaded" status means that the frame is ready to accept
3459                 //credentials for establishing a BOSH connection.
3460                 if (data === "loaded") {
3461                     _isTunnelLoaded = true;
3462                     if(_resource) {
3463                         _sendMessage(_TYPES.RESOURCEID, _resource);
3464                     }
3465                     _sendMessage(_TYPES.ID, _id);
3466                     _sendMessage(_TYPES.XMPPDOMAIN, _xmppDomain);
3467                     _sendMessage(_TYPES.PASSWORD, _password);
3468                     _sendMessage(_TYPES.PUBSUBDOMAIN, _pubsubDomain);
3469                 } else if (typeof _connInfoHandler === "function") {
3470                     _connInfoHandler(_createConnInfoObj());
3471                 }
3472                 break;
3473             case _TYPES.RESOURCEID:
3474                 _resourceID = data;
3475                 break;
3476             case _TYPES.SUBSCRIBE:
3477                 _processSubscribeResponse(data);
3478                 break;
3479             case _TYPES.UNSUBSCRIBE:
3480                 _processUnsubscribeResponse(data);
3481                 break;
3482             case _TYPES.PRESENCE:
3483                 if (typeof _presenceHandler === "function") {
3484                     _presenceHandler(data);
3485                 }
3486                 break;
3487             default:
3488                 break;
3489             }
3490         },
3491 
3492         /**
3493          * Initialize the tunnel config so that the url can be http or https with the appropriate port
3494          * @private
3495          */
3496         _initTunnelConfig = function () {
3497             if (_isTunnelConfigInit === true) {
3498                 return;
3499             }
3500             
3501             //Initialize tunnel origin
3502             //Determine tunnel origin based on host and scheme
3503             _tunnelOriginPort = (scheme && scheme.indexOf("https") !== -1) ? "7443" : "7071";
3504             if (scheme) {
3505                 _tunnelOrigin = scheme + "://" + host + ":" + _tunnelOriginPort;
3506             } else {
3507                 _tunnelOrigin = "http://" + host + ":" + _tunnelOriginPort;
3508             }
3509             _tunnelURL = _tunnelOrigin + "/tunnel/";
3510             
3511             _isTunnelConfigInit = true;
3512         },
3513 
3514         /**
3515          * Create the tunnel iframe which establishes the shared BOSH connection.
3516          * Messages are sent across frames using window.postMessage.
3517          * @private
3518          */
3519         _createTunnel = function () {
3520             var tunnelID = ((self === parent) ? "tunnel-frame" : "autopilot-tunnel-frame"),
3521             iframe = document.createElement("iframe");         
3522             iframe.style.display = "none";
3523             iframe.setAttribute("id", tunnelID);
3524             iframe.setAttribute("name", tunnelID);
3525             iframe.setAttribute("src", _tunnelURL);
3526             document.body.appendChild(iframe);
3527             _tunnelFrame = window.frames[tunnelID];
3528         };
3529 
3530         /**
3531          * Sends a message via postmessage to the EventTunnel to attempt to connect to the XMPP server
3532          * @private
3533          */
3534         this.makeConnectReq = function () {
3535             _sendMessage(_TYPES.PASSWORD, _password);
3536         };
3537         
3538         /**
3539          * @private
3540          * Returns the host of the Finesse server.
3541          * @returns {String}
3542          *     The host specified during the creation of the object.
3543          */
3544         this.getHost = function () {
3545             return host;
3546         };
3547 
3548         /**
3549          * @private
3550          * The resource ID of the user who is logged into the notification server.
3551          * @returns {String}
3552          *     The resource ID generated by the notification server.
3553          */
3554         this.getResourceID = function () {
3555             return _resourceID;
3556         };
3557 
3558         /**
3559          * @private
3560          * Indicates whether the tunnel frame is loaded.
3561          * @returns {Boolean}
3562          *     True if the tunnel frame is loaded, false otherwise.
3563          */
3564         this.isTunnelLoaded = function () {
3565             return _isTunnelLoaded;
3566         };
3567 
3568         /**
3569          * @private
3570          * The location of the tunnel HTML URL.
3571          * @returns {String}
3572          *     The location of the tunnel HTML URL.
3573          */
3574         this.getTunnelURL = function () {
3575             return _tunnelURL;
3576         };
3577 
3578         /**
3579          * @private
3580          * Tunnels a subscribe request to the eventing iframe.
3581          * @param {String} node
3582          *     The node to subscribe to
3583          * @param {Function} handler
3584          *     Handler to invoke upon success or failure
3585          */
3586         this.subscribe = function (node, handler) {
3587             if (handler && typeof handler !== "function") {
3588                 throw new Error("Parameter is not a function.");
3589             }
3590             _handlers.subscribe[node] = handler;
3591             _sendMessage(_TYPES.SUBSCRIBE, node);
3592         };
3593 
3594         /**
3595          * @private
3596          * Tunnels an unsubscribe request to the eventing iframe.
3597          * @param {String} node
3598          *     The node to unsubscribe from
3599          * @param {Function} handler
3600          *     Handler to invoke upon success or failure
3601          */
3602         this.unsubscribe = function (node, handler) {
3603             if (handler && typeof handler !== "function") {
3604                 throw new Error("Parameter is not a function.");
3605             }
3606             _handlers.unsubscribe[node] = handler;
3607             _sendMessage(_TYPES.UNSUBSCRIBE, node);
3608         };
3609 
3610         /**
3611          * @private
3612          * Registers a handler to be invoked when an event is delivered. Only one
3613          * is registered at a time. If there has already been an event that was
3614          * delivered, the handler will be invoked immediately.
3615          * @param {Function} handler
3616          *     Invoked when an event is delivered through the event connection.
3617          */
3618         this.registerEventHandler = function (handler) {
3619             if (typeof handler !== "function") {
3620                 throw new Error("Parameter is not a function.");
3621             }
3622             _eventHandler = handler;
3623             if (_eventCache) {
3624                 handler(_eventCache);
3625             }
3626         };
3627 
3628         /**
3629          * @private
3630          * Unregisters the event handler completely.
3631          */
3632         this.unregisterEventHandler = function () {
3633             _eventHandler = undefined;
3634         };
3635         
3636         /**
3637          * @private
3638          * Registers a handler to be invoked when a presence event is delivered. Only one
3639          * is registered at a time. 
3640          * @param {Function} handler
3641          *     Invoked when a presence event is delivered through the event connection.
3642          */
3643         this.registerPresenceHandler = function (handler) {
3644             if (typeof handler !== "function") {
3645                 throw new Error("Parameter is not a function.");
3646             }
3647             _presenceHandler = handler;
3648         };
3649         
3650         /**
3651          * @private
3652          * Unregisters the presence event handler completely.
3653          */
3654         this.unregisterPresenceHandler = function () {
3655             _presenceHandler = undefined;
3656         };
3657 
3658         /**
3659          * @private
3660          * Registers a handler to be invoked when a connection status changes. The
3661          * object passed will contain a "status" property, and a "resourceID"
3662          * property, which will contain the most current resource ID assigned to
3663          * the client. If there has already been an event that was delivered, the
3664          * handler will be invoked immediately.
3665          * @param {Function} handler
3666          *     Invoked when a connection status changes.
3667          */
3668         this.registerConnectionInfoHandler = function (handler) {
3669             if (typeof handler !== "function") {
3670                 throw new Error("Parameter is not a function.");
3671             }
3672             _connInfoHandler = handler;
3673             if (_statusCache) {
3674                 handler(_createConnInfoObj());
3675             }
3676         };
3677 
3678         /**
3679          * @private
3680          * Unregisters the connection information handler.
3681          */
3682         this.unregisterConnectionInfoHandler = function () {
3683             _connInfoHandler = undefined;
3684         };
3685 
3686         /**
3687          * @private
3688          * Start listening for events and create a event tunnel for the shared BOSH
3689          * connection.
3690          * @param {String} id
3691          *     The ID of the user for the notification server.
3692          * @param {String} password
3693          *     The password of the user for the notification server.
3694          * @param {String} xmppDomain
3695          *     The XMPP domain of the notification server
3696          * @param {String} pubsubDomain
3697          *     The location (JID) of the XEP-0060 PubSub service
3698          * @param {String} resource
3699          *     The resource to connect to the notification servier with.
3700          */
3701         this.init = function (id, password, xmppDomain, pubsubDomain, resource) {
3702             
3703             if (typeof id !== "string" || typeof password !== "string" || typeof xmppDomain !== "string" || typeof pubsubDomain !== "string") {
3704                 throw new Error("Invalid or missing required parameters.");
3705             }
3706 
3707             _initTunnelConfig();
3708             
3709             _id = id;
3710             _password = password;
3711             _xmppDomain = xmppDomain;
3712             _pubsubDomain = pubsubDomain;
3713             _resource = resource;
3714 
3715             //Attach a listener for messages sent from tunnel frame.
3716             _util.receiveMessage(_messageHandler, _tunnelOrigin);
3717 
3718             //Create the tunnel iframe which will establish the shared connection.
3719             _createTunnel();
3720         };
3721 
3722         //BEGIN TEST CODE//
3723 //        /**
3724 //         * Test code added to expose private functions that are used by unit test
3725 //         * framework. This section of code is removed during the build process
3726 //         * before packaging production code. The [begin|end]TestSection are used
3727 //         * by the build to identify the section to strip.
3728 //         * @ignore
3729 //         */
3730 //        this.beginTestSection = 0;
3731 //
3732 //        /**
3733 //         * @ignore
3734 //         */
3735 //        this.getTestObject = function () {
3736 //            //Load mock dependencies.
3737 //            var _mock = new MockControl();
3738 //            _util = _mock.createMock(finesse.utilities.Utilities);
3739 //
3740 //            return {
3741 //                //Expose mock dependencies
3742 //                mock: _mock,
3743 //                util: _util,
3744 //
3745 //                //Expose internal private functions
3746 //                types: _TYPES,
3747 //                createConnInfoObj: _createConnInfoObj,
3748 //                sendMessage: _sendMessage,
3749 //                messageHandler: _messageHandler,
3750 //                createTunnel: _createTunnel,
3751 //                handlers: _handlers,
3752 //                initTunnelConfig : _initTunnelConfig
3753 //            };
3754 //        };
3755 //
3756 //        /**
3757 //         * @ignore
3758 //         */
3759 //        this.endTestSection = 0;
3760 //        //END TEST CODE//
3761     };
3762     
3763     /** @namespace JavaScript class objects and methods to handle the subscription to Finesse events.*/
3764     finesse.clientservices = finesse.clientservices || {};
3765 
3766     window.finesse = window.finesse || {};
3767     window.finesse.clientservices = window.finesse.clientservices || {};
3768     window.finesse.clientservices.MasterTunnel = MasterTunnel;
3769 
3770     return MasterTunnel;
3771 
3772 });
3773 
3774 /**
3775  * Contains a list of topics used for client side pubsub.
3776  *
3777  */
3778 
3779 /** @private */
3780 define('clientservices/Topics',[], function () {
3781     
3782    var Topics = (function () {
3783 
3784         /**
3785          * @private
3786          * The namespace prepended to all Finesse topics.
3787          */
3788         this.namespace = "finesse.info";
3789     
3790         /**
3791          * @private
3792          * Gets the full topic name with the Finesse namespace prepended.
3793          * @param {String} topic
3794          *     The topic category.
3795          * @returns {String}
3796          *     The full topic name with prepended namespace.
3797          */
3798         var _getNSTopic = function (topic) {
3799             return this.namespace + "." + topic;
3800         };
3801         
3802         /** @scope finesse.clientservices.Topics */
3803         return {
3804             /** 
3805              * @private
3806              * Client side request channel. 
3807              */
3808             REQUESTS: _getNSTopic("requests"),
3809     
3810             /** 
3811              * @private
3812              * Client side response channel. 
3813              */
3814             RESPONSES: _getNSTopic("responses"),
3815 
3816             /** 
3817              * @private
3818              * Connection status. 
3819              */
3820             EVENTS_CONNECTION_INFO: _getNSTopic("connection"),
3821             
3822             /** 
3823              * @private
3824              * Presence channel 
3825              */
3826             PRESENCE: _getNSTopic("presence"),
3827     
3828             /**
3829              * @private
3830              * Convert a Finesse REST URI to a OpenAjax compatible topic name.
3831              */
3832             getTopic: function (restUri) {
3833                 //The topic should not start with '/' else it will get replaced with
3834                 //'.' which is invalid.
3835                 //Thus, remove '/' if it is at the beginning of the string
3836                 if (restUri.indexOf('/') === 0) {
3837                     restUri = restUri.substr(1);
3838                 }
3839     
3840                 //Replace every instance of "/" with ".". This is done to follow the
3841                 //OpenAjaxHub topic name convention.
3842                 return restUri.replace(/\//g, ".");
3843             }
3844         };
3845     }());
3846     window.finesse = window.finesse || {};
3847     window.finesse.clientservices = window.finesse.clientservices || {};
3848     /** @private */
3849     window.finesse.clientservices.Topics = Topics;
3850     
3851     return Topics;
3852 });
3853 
3854 /** The following comment is to prevent jslint errors about 
3855  * using variables before they are defined.
3856  */
3857 /*global finesse*/
3858 
3859 /**
3860  * Registers with the MasterTunnel to receive events, which it
3861  *     could publish to the OpenAjax gadget pubsub infrastructure.
3862  *
3863  * @requires OpenAjax, finesse.clientservices.MasterTunnel, finesse.clientservices.Topics
3864  */
3865 
3866 /** @private */
3867 define('clientservices/MasterPublisher',[
3868     "clientservices/MasterTunnel",
3869     "clientservices/Topics",
3870     "utilities/Utilities"
3871 ],
3872 function (MasterTunnel, Topics, Utilities) {
3873     
3874      var MasterPublisher = function (tunnel, hub) {
3875         if (!(tunnel instanceof MasterTunnel)) {
3876             throw new Error("Required tunnel object missing or invalid.");
3877         }
3878 
3879         var
3880         
3881         ClientServices = finesse.clientservices.ClientServices,
3882 
3883         /**
3884          * Reference to the gadget pubsub Hub instance.
3885          * @private
3886          */
3887         _hub = hub,
3888 
3889         /**
3890          * Reference to the Topics class.
3891          * @private
3892          */
3893         _topics = Topics,
3894         
3895         /**
3896          * Reference to conversion utilities class.
3897          * @private
3898          */
3899         _utils = Utilities,
3900         
3901         /**
3902          * References to ClientServices logger methods
3903          * @private
3904          */
3905         _logger = {
3906             log: ClientServices.log
3907         },
3908         
3909         /**
3910          * Store the passed in tunnel.
3911          * @private
3912          */
3913         _tunnel = tunnel,
3914 
3915         /**
3916          * Caches the connection info event so that it could be published if there
3917          * is a request for it.
3918          * @private
3919          */
3920         _connInfoCache = {},
3921 
3922         /**
3923          * The types of possible request types supported when listening to the
3924          * requests channel. Each request type could result in different operations.
3925          * @private
3926          */
3927         _REQTYPES = {
3928             CONNECTIONINFO: "ConnectionInfoReq",
3929             SUBSCRIBE: "SubscribeNodeReq",
3930             UNSUBSCRIBE: "UnsubscribeNodeReq",
3931             CONNECT: "ConnectionReq"
3932         },
3933 
3934         /**
3935          * Will store list of nodes that have OF subscriptions created
3936          *     _nodesList[node][subscribing].reqIds[subid]
3937          *     _nodesList[node][active].reqIds[subid]
3938          *     _nodesList[node][unsubscribing].reqIds[subid]
3939          *     _nodesList[node][holding].reqIds[subid]
3940          * @private
3941          */
3942         _nodesList = {},
3943         
3944         /**
3945          * The states that a subscription can be in
3946          * @private
3947          */
3948         _CHANNELSTATES = {
3949             UNINITIALIZED: "Uninitialized",
3950             PENDING: "Pending",
3951             OPERATIONAL: "Operational"
3952         },
3953 
3954         /**
3955           * Checks if the payload is JSON 
3956           * @returns {Boolean}
3957           * @private
3958           */
3959         _isJsonPayload = function(event) {
3960             var delimStart, delimEnd, retval = false;
3961             
3962             try { 
3963               delimStart = event.indexOf('{');
3964               delimEnd = event.lastIndexOf('}');
3965 
3966               if ((delimStart !== -1 ) && (delimEnd === (event.length - 1))) {
3967                 retval = true;  //event contains JSON payload
3968               }
3969             } catch (err) {
3970               _logger.log("MasterPublisher._isJsonPayload() - Caught error: " + err);
3971             }
3972             return retval;
3973         },
3974         
3975                 /**
3976           * Parses a JSON event and then publishes.
3977           *
3978           * @param {String} event
3979           *     The full event payload.
3980           * @throws {Error} If the payload object is malformed.
3981           * @private
3982           */
3983         _parseAndPublishJSONEvent = function(event) {
3984             var topic, eventObj, publishEvent,
3985             delimPos = event.indexOf("{"),
3986             node, parser,
3987             eventJson = event,
3988             returnObj = {node: null, data: null};
3989 
3990             try {
3991                //Extract and strip the node path from the message
3992                if (delimPos > 0) 
3993                {
3994                   //We need to decode the URI encoded node path
3995                   //TODO: make sure this is kosher with OpenAjax topic naming
3996                   node = decodeURI(event.substr(0, delimPos));
3997                   eventJson = event.substr(delimPos);
3998                   
3999                   //Converting the node path to openAjaxhub topic
4000                   topic = _topics.getTopic(node);
4001                   
4002                   returnObj.node = node;
4003                   returnObj.payload = eventJson;
4004                } 
4005                else 
4006                {
4007                   _logger.log("MasterPublisher._parseAndPublishJSONEvent() - [ERROR] node is not given in postMessage: " + eventJson);
4008                   throw new Error("node is not given in postMessage: " + eventJson);
4009                }
4010 
4011                parser = _utils.getJSONParser();
4012 
4013                eventObj = parser.parse(eventJson);
4014                returnObj.data = eventObj;
4015 
4016             } catch (err) {
4017                _logger.log("MasterPublisher._parseAndPublishJSONEvent() - [ERROR] Malformed event payload: " + err);
4018                throw new Error("Malformed event payload : " + err);
4019             }
4020             
4021             _logger.log("MasterPublisher._parseAndPublishJSONEvent() - Received JSON event on node '" + node + "': " + eventJson); 
4022             
4023             publishEvent = {content : event, object : eventObj };
4024 
4025             //Publish event to proper event topic.
4026             if (topic && eventObj) {
4027                _hub.publish(topic, publishEvent);
4028             }
4029         },
4030         
4031         /**
4032           * Parses an XML event and then publishes.
4033           *
4034           * @param {String} event
4035           *     The full event payload.
4036           * @throws {Error} If the payload object is malformed.
4037           * @private
4038           */
4039         _parseAndPublishXMLEvent = function(event) {
4040             var topic, eventObj, publishEvent, restTopic,
4041             delimPos = event.indexOf("<"),
4042             node,
4043             eventXml = event;
4044             
4045             try {
4046                //Extract and strip the node path from the message
4047                if (delimPos > 0) {
4048                   //We need to decode the URI encoded node path
4049                   //TODO: make sure this is kosher with OpenAjax topic naming
4050                   node = decodeURI(event.substr(0, delimPos));
4051                   eventXml = event.substr(delimPos);
4052                   //Converting the node path to openAjaxhub topic
4053                   topic = _topics.getTopic(node);
4054                } else {
4055                   _logger.log("MasterPublisher._parseAndPublishXMLEvent() - [ERROR] node is not given in postMessage: " + eventXml);
4056                   throw new Error("node is not given in postMessage: " + eventXml);
4057                }
4058 
4059                eventObj = _utils.xml2JsObj(eventXml);
4060                   
4061            } catch (err) {
4062                _logger.log("MasterPublisher._parseAndPublishXMLEvent() - [ERROR] Malformed event payload: " + err);
4063                throw new Error("Malformed event payload : " + err);
4064            }
4065            
4066            _logger.log("MasterPublisher._parseAndPublishXMLEvent() - Received XML event on node '" + node + "': " + eventXml);
4067            
4068            publishEvent = {content : event, object : eventObj };
4069 
4070            //Publish event to proper event topic.
4071            if (topic && eventObj) {
4072                _hub.publish(topic, publishEvent);
4073            }
4074         },
4075         
4076         /**
4077          * Publishes events to the appropriate topic. The topic name is determined
4078          * by fetching the source value from the event.
4079          * @param {String} event
4080          *     The full event payload.
4081          * @throws {Error} If the payload object is malformed.
4082          * @private
4083          */
4084         _eventHandler = function (event) {
4085             
4086             //Handle JSON or XML events
4087             if (!_isJsonPayload(event))
4088             {
4089                //XML
4090                _parseAndPublishXMLEvent(event);
4091             }
4092             else
4093             {
4094                //JSON
4095                _parseAndPublishJSONEvent(event);
4096             }
4097         },
4098         
4099         
4100         /**
4101          * Handler for when presence events are sent through the MasterTunnel.
4102          * @returns {Object}
4103          *     A presence xml event.
4104          * @private
4105          */
4106         _presenceHandler = function (event) {
4107             var eventObj = _utils.xml2JsObj(event), publishEvent;
4108             
4109             publishEvent = {content : event, object : eventObj};
4110             
4111             if (eventObj) {
4112                 _hub.publish(_topics.PRESENCE, publishEvent);
4113             }
4114         },
4115 
4116         /**
4117          * Clone the connection info object from cache.
4118          * @returns {Object}
4119          *     A connection info object containing a "status" and "resourceID".
4120          * @private
4121          */
4122         _cloneConnInfoObj = function () {
4123             if (_connInfoCache) {
4124                 return {
4125                     status: _connInfoCache.status,
4126                     resourceID: _connInfoCache.resourceID
4127                 };
4128             } else {
4129                 return null;
4130             }
4131         },
4132 
4133         /**
4134          * Cleans up any outstanding subscribe/unsubscribe requests and notifies them of errors.
4135          * This is done if we get disconnected because we cleanup explicit subscriptions on disconnect.
4136          * @private
4137          */
4138         _cleanupPendingRequests = function () {
4139             var node, curSubid, errObj = {
4140                 error: {
4141                     errorType: "Disconnected",
4142                     errorMessage: "Outstanding request will never complete."
4143                 }
4144             };
4145 
4146             // Iterate through all outstanding subscribe requests to notify them that it will never return
4147             for (node in _nodesList) {
4148                 if (_nodesList.hasOwnProperty(node)) {
4149                     for (curSubid in _nodesList[node].subscribing.reqIds) {
4150                         if (_nodesList[node].subscribing.reqIds.hasOwnProperty(curSubid)) {
4151                             // Notify this outstanding subscribe request to give up and error out
4152                             _hub.publish(_topics.RESPONSES + "." + curSubid, errObj); 
4153                         }
4154                     }
4155                     for (curSubid in _nodesList[node].unsubscribing.reqIds) {
4156                         if (_nodesList[node].unsubscribing.reqIds.hasOwnProperty(curSubid)) {
4157                             // Notify this outstanding unsubscribe request to give up and error out
4158                             _hub.publish(_topics.RESPONSES + "." + curSubid, errObj); 
4159                         }
4160                     }
4161                 }
4162             }
4163         },
4164 
4165         /**
4166          * Publishes the connection info to the connection info topic.
4167          * @param {Object} connInfo
4168          *     The connection info object containing the status and resource ID.
4169          * @private
4170          */
4171         _connInfoHandler = function (connInfo) {
4172             var before = _connInfoCache.status;
4173             _connInfoCache = connInfo;
4174             _logger.log("MasterPublisher._connInfoHandler() - Connection status: " + connInfo.status);
4175             _hub.publish(_topics.EVENTS_CONNECTION_INFO, _cloneConnInfoObj());
4176             if (before === "connected" && connInfo.status !== "connected") {
4177                 // Fail all pending requests when we transition to disconnected
4178                 _cleanupPendingRequests();
4179             }
4180         },
4181 
4182         
4183         /**
4184          * Utility method to bookkeep node subscription requests and determine
4185          * whehter it is necessary to tunnel the request to JabberWerx.
4186          * @param {String} node
4187          *     The node of interest
4188          * @param {String} reqId
4189          *     A unique string identifying the request/subscription
4190          * @private
4191          */
4192         _subscribeNode = function (node, subid) {
4193             if (_connInfoCache.status !== "connected") {
4194                 _hub.publish(_topics.RESPONSES + "." + subid, {
4195                     error: {
4196                         errorType: "Not connected",
4197                         errorMessage: "Cannot subscribe without connection."
4198                     }
4199                 });
4200                 return;
4201             }
4202             // NODE DOES NOT YET EXIST
4203             if (!_nodesList[node]) {
4204                 _nodesList[node] = {
4205                     "subscribing": {
4206                         "reqIds": {},
4207                         "length": 0
4208                     },
4209                     "active": {
4210                         "reqIds": {},
4211                         "length": 0
4212                     },
4213                     "unsubscribing": {
4214                         "reqIds": {},
4215                         "length": 0
4216                     },
4217                     "holding": {
4218                         "reqIds": {},
4219                         "length": 0
4220                     }
4221                 };
4222             }
4223             if (_nodesList[node].active.length === 0) {
4224                 if (_nodesList[node].unsubscribing.length === 0) {
4225                     if (_nodesList[node].subscribing.length === 0) {
4226                         _nodesList[node].subscribing.reqIds[subid] = true;
4227                         _nodesList[node].subscribing.length += 1;
4228 
4229                         _logger.log("MasterPublisher._subscribeNode() - Attempting to subscribe to node '" + node + "'");
4230                         _tunnel.subscribe(node, function (err) {
4231                             var errObj, curSubid;
4232                             if (err) {
4233                                 errObj = {
4234                                     subscribe: {
4235                                         content: err
4236                                     }
4237                                 };
4238 
4239                                 try {
4240                                     errObj.subscribe.object = gadgets.json.parse((_utils.xml2json(jQuery.parseXML(err), "")));
4241                                 } catch (e) {
4242                                     errObj.error = {
4243                                         errorType: "parseError",
4244                                         errorMessage: "Could not serialize XML: " + e
4245                                     };
4246                                 }
4247                                 _logger.log("MasterPublisher._subscribeNode() - Error subscribing to node '" + node + "': " + err);
4248                             } else {
4249                                 _logger.log("MasterPublisher._subscribeNode() - Subscribed to node '" + node + "'");
4250                             }
4251 
4252                             for (curSubid in _nodesList[node].subscribing.reqIds) {
4253                                 if (_nodesList[node].subscribing.reqIds.hasOwnProperty(curSubid)) {
4254                                     _hub.publish(_topics.RESPONSES + "." + curSubid, errObj);
4255                                     if (!err) {
4256                                         _nodesList[node].active.reqIds[curSubid] = true;
4257                                         _nodesList[node].active.length += 1;
4258                                     }
4259                                     delete _nodesList[node].subscribing.reqIds[curSubid];
4260                                     _nodesList[node].subscribing.length -= 1;
4261                                 }
4262                             }
4263                         });
4264                         
4265                     } else { //other ids are subscribing
4266                         _nodesList[node].subscribing.reqIds[subid] = true;
4267                         _nodesList[node].subscribing.length += 1;
4268                     }                       
4269                 } else { //An unsubscribe request is pending, hold onto these subscribes until it is done
4270                     _nodesList[node].holding.reqIds[subid] = true;
4271                     _nodesList[node].holding.length += 1;
4272                 }
4273             } else { // The node has active subscriptions; add this subid and return successful response
4274                 _nodesList[node].active.reqIds[subid] = true;
4275                 _nodesList[node].active.length += 1;
4276                 _hub.publish(_topics.RESPONSES + "." + subid, undefined); 
4277             }
4278         },
4279 
4280         /**
4281          * Utility method to bookkeep node unsubscribe requests and determine
4282          * whehter it is necessary to tunnel the request to JabberWerx.
4283          * @param {String} node
4284          *     The node to unsubscribe from
4285          * @param {String} reqId
4286          *     A unique string identifying the subscription to remove
4287          * @private
4288          */
4289         _unsubscribeNode = function (node, subid) {
4290             if (!_nodesList[node]) { //node DNE, publish success response
4291                 _hub.publish(_topics.RESPONSES + "." + subid, undefined); 
4292             } else {
4293                 if (_connInfoCache.status !== "connected") {
4294                     _hub.publish(_topics.RESPONSES + "." + subid, {
4295                         error: {
4296                             errorType: "Not connected",
4297                             errorMessage: "Cannot unsubscribe without connection."
4298                         }
4299                     });
4300                     return;
4301                 }
4302                 if (_nodesList[node].active.length > 1) {
4303                     delete _nodesList[node].active.reqIds[subid];
4304                     _hub.publish(_topics.RESPONSES + "." + subid, undefined); 
4305                     _nodesList[node].active.length -= 1;
4306                 } else if (_nodesList[node].active.length === 1) { // transition subid from active category to unsubscribing category
4307                     _nodesList[node].unsubscribing.reqIds[subid] = true;
4308                     _nodesList[node].unsubscribing.length += 1;
4309                     delete _nodesList[node].active.reqIds[subid];
4310                     _nodesList[node].active.length -= 1;
4311 
4312                     _logger.log("MasterPublisher._unsubscribeNode() - Attempting to unsubscribe from node '" + node + "'");
4313                     _tunnel.unsubscribe(node, function (err) {
4314                         var errObj, curSubid;
4315                         if (err) {
4316                             errObj = {
4317                                 subscribe: {
4318                                     content: err
4319                                 }
4320                             };
4321 
4322                             try {
4323                                 errObj.subscribe.object = gadgets.json.parse((_utils.xml2json(jQuery.parseXML(err), "")));
4324                             } catch (e) {
4325                                 errObj.error = {
4326                                     errorType: "parseError",
4327                                     errorMessage: "Could not serialize XML: " + e
4328                                 };
4329                             }
4330                             _logger.log("MasterPublisher._unsubscribeNode() - Error unsubscribing from node '" + node + "': " + err);
4331                         } else {
4332                             _logger.log("MasterPublisher._unsubscribeNode() - Unsubscribed from node '" + node + "'");
4333                         }
4334 
4335                         for (curSubid in _nodesList[node].unsubscribing.reqIds) {
4336                             if (_nodesList[node].unsubscribing.reqIds.hasOwnProperty(curSubid)) {
4337                                 // publish to all subids whether unsubscribe failed or succeeded
4338                                 _hub.publish(_topics.RESPONSES + "." + curSubid, errObj); 
4339                                 if (!err) {
4340                                     delete _nodesList[node].unsubscribing.reqIds[curSubid];
4341                                     _nodesList[node].unsubscribing.length -= 1;
4342                                 } else { // Just remove the subid from unsubscribing; the next subscribe request will operate with node already created
4343                                     delete _nodesList[node].unsubscribing.reqIds[curSubid];
4344                                     _nodesList[node].unsubscribing.length -= 1;
4345                                 }   
4346                             }
4347                         }
4348                         
4349                         if (!err && _nodesList[node].holding.length > 0) { // if any subscribe requests came in while unsubscribing from OF, now transition from holding to subscribing
4350                             for (curSubid in _nodesList[node].holding.reqIds) {
4351                                 if (_nodesList[node].holding.reqIds.hasOwnProperty(curSubid)) {
4352                                     delete _nodesList[node].holding.reqIds[curSubid];
4353                                     _nodesList[node].holding.length -= 1;
4354                                     _subscribeNode(node, curSubid);                             
4355                                 }
4356                             }
4357                         }
4358                     });
4359                 } else { // length <= 0?
4360                     _hub.publish(_topics.RESPONSES + "." + subid, undefined);
4361                 }
4362             }
4363         },
4364         
4365         /**
4366          * Handles client requests to establish a BOSH connection.
4367          * @param {String} id
4368          *     id of the xmpp user
4369          * @param {String} password
4370          *     password of the xmpp user
4371          * @param {String} xmppDomain
4372          *     xmppDomain of the xmpp user account
4373          * @private
4374          */
4375         _connect = function (id, password, xmppDomain) {
4376             _tunnel.makeConnectReq(id, password, xmppDomain);
4377         },
4378 
4379         /**
4380          * Handles client requests made to the request topic. The type of the
4381          * request is described in the "type" property within the data payload. Each
4382          * type can result in a different operation.
4383          * @param {String} topic
4384          *     The topic which data was published to.
4385          * @param {Object} data
4386          *     The data containing requests information published by clients.
4387          * @param {String} data.type
4388          *     The type of the request. Supported: "ConnectionInfoReq"
4389          * @param {Object} data.data
4390          *     May contain data relevant for the particular requests.
4391          * @param {String} [data.invokeID]
4392          *     The ID used to identify the request with the response. The invoke ID
4393          *     will be included in the data in the publish to the topic. It is the
4394          *     responsibility of the client to correlate the published data to the
4395          *     request made by using the invoke ID.
4396          * @private
4397          */
4398         _clientRequestHandler = function (topic, data) {
4399             var dataCopy;
4400 
4401             //Ensure a valid data object with "type" and "data" properties.
4402             if (typeof data === "object" &&
4403                     typeof data.type === "string" &&
4404                     typeof data.data === "object") {
4405                 switch (data.type) {
4406                 case _REQTYPES.CONNECTIONINFO:
4407                     //It is possible that Slave clients come up before the Master
4408                     //client. If that is the case, the Slaves will need to make a
4409                     //request for the Master to send the latest connection info to the
4410                     //connectionInfo topic.
4411                     dataCopy = _cloneConnInfoObj();
4412                     if (dataCopy) {
4413                         if (data.invokeID !== undefined) {
4414                             dataCopy.invokeID = data.invokeID;
4415                         }
4416                         _hub.publish(_topics.EVENTS_CONNECTION_INFO, dataCopy);
4417                     }
4418                     break;
4419                 case _REQTYPES.SUBSCRIBE:
4420                     if (typeof data.data.node === "string") {
4421                         _subscribeNode(data.data.node, data.invokeID);
4422                     }
4423                     break;
4424                 case _REQTYPES.UNSUBSCRIBE:
4425                     if (typeof data.data.node === "string") {
4426                         _unsubscribeNode(data.data.node, data.invokeID);
4427                     }
4428                     break;
4429                 case _REQTYPES.CONNECT:
4430                     // Deprecated: Disallow others (non-masters) from administering BOSH connections that are not theirs
4431                     _logger.log("MasterPublisher._clientRequestHandler(): F403: Access denied. Non-master ClientService instances do not have the clearance level to make this request: makeConnectionReq");
4432                     break;
4433                 default:
4434                     break;
4435                 }
4436             }
4437         };
4438 
4439         (function () {
4440             //Register to receive events and connection status from tunnel.
4441             _tunnel.registerEventHandler(_eventHandler);
4442             _tunnel.registerPresenceHandler(_presenceHandler);
4443             _tunnel.registerConnectionInfoHandler(_connInfoHandler);
4444 
4445             //Listen to a request channel to respond to any requests made by other
4446             //clients because the Master may have access to useful information.
4447             _hub.subscribe(_topics.REQUESTS, _clientRequestHandler);
4448         }());
4449 
4450         /**
4451          * @private
4452          * Handles client requests to establish a BOSH connection.
4453          * @param {String} id
4454          *     id of the xmpp user
4455          * @param {String} password
4456          *     password of the xmpp user
4457          * @param {String} xmppDomain
4458          *     xmppDomain of the xmpp user account
4459          */
4460         this.connect = function (id, password, xmppDomain) {
4461           _connect(id, password, xmppDomain);
4462         };
4463 
4464         /**
4465          * @private
4466          * Resets the list of explicit subscriptions
4467          */
4468         this.wipeout = function () {
4469             _cleanupPendingRequests();
4470             _nodesList = {};
4471        };
4472 
4473         //BEGIN TEST CODE//
4474        /**
4475         * Test code added to expose private functions that are used by unit test
4476         * framework. This section of code is removed during the build process
4477         * before packaging production code. The [begin|end]TestSection are used
4478         * by the build to identify the section to strip.
4479         * @ignore
4480         */
4481        this.beginTestSection = 0;
4482 
4483        /**
4484         * @ignore
4485         */
4486        this.getTestObject = function () {
4487            //Load mock dependencies.
4488            var _mock = new MockControl();
4489            _hub = _mock.createMock(gadgets.Hub);
4490            _tunnel = _mock.createMock();
4491 
4492            return {
4493                //Expose mock dependencies
4494                mock: _mock,
4495                hub: _hub,
4496                tunnel: _tunnel,
4497                setTunnel: function (tunnel) {
4498                    _tunnel = tunnel;
4499                },
4500                getTunnel: function () {
4501                    return _tunnel;
4502                },
4503 
4504                //Expose internal private functions
4505                reqtypes: _REQTYPES,
4506                eventHandler: _eventHandler,
4507                presenceHandler: _presenceHandler,
4508                
4509                subscribeNode: _subscribeNode,
4510                unsubscribeNode: _unsubscribeNode,
4511                
4512                getNodeList: function () {
4513                    return _nodesList;
4514                },
4515                setNodeList: function (nodelist) {
4516                    _nodesList = nodelist;
4517                },
4518                
4519                cloneConnInfoObj: _cloneConnInfoObj,
4520                connInfoHandler: _connInfoHandler,
4521                clientRequestHandler: _clientRequestHandler
4522 
4523            };
4524        };
4525 
4526 
4527        /**
4528         * @ignore
4529         */
4530        this.endTestSection = 0;
4531        //END TEST CODE//
4532 
4533     };
4534     
4535     window.finesse = window.finesse || {};
4536     window.finesse.clientservices = window.finesse.clientservices || {};
4537     window.finesse.clientservices.MasterPublisher = MasterPublisher;
4538 
4539     return MasterPublisher;
4540 });
4541 
4542 /** The following comment is to prevent jslint errors about 
4543  * using variables before they are defined.
4544  */
4545 /*global publisher:true */
4546 
4547 /**
4548  * Exposes a set of API wrappers that will hide the dirty work of
4549  *     constructing Finesse API requests and consuming Finesse events.
4550  *
4551  * @requires OpenAjax, jQuery 1.5, finesse.utilities.Utilities
4552  */
4553 
4554 
4555 /**
4556  * Allow clients to make Finesse API requests and consume Finesse events by
4557  * calling a set of exposed functions. The Services layer will do the dirty
4558  * work of establishing a shared BOSH connection (for designated Master
4559  * modules), consuming events for client subscriptions, and constructing API
4560  * requests.
4561  */
4562 /** @private */
4563 define('clientservices/ClientServices',[
4564     "clientservices/MasterTunnel",
4565     "clientservices/MasterPublisher",
4566     "clientservices/Topics",
4567     "utilities/Utilities"
4568 ],
4569 function (MasterTunnel, MasterPublisher, Topics, Utilities) {
4570     
4571      var ClientServices = (function () {/** @lends finesse.clientservices.ClientServices.prototype */
4572         var
4573 
4574         /**
4575          * Shortcut reference to the master tunnel
4576          * @private
4577          */
4578         _tunnel,
4579 
4580         _publisher,
4581 
4582         /**
4583          * Shortcut reference to the finesse.utilities.Utilities singleton
4584          * This will be set by init()
4585          * @private
4586          */
4587         _util,
4588 
4589         /**
4590          * Shortcut reference to the gadgets.io object.
4591          * This will be set by init()
4592          * @private
4593          */
4594         _io,
4595 
4596         /**
4597          * Shortcut reference to the gadget pubsub Hub instance.
4598          * This will be set by init()
4599          * @private
4600          */
4601         _hub,
4602 
4603         /**
4604          * Logger object set externally by setLogger, defaults to nothing.
4605          * @private
4606          */
4607         _logger = {},
4608 
4609         /**
4610          * Shortcut reference to the Topics class.
4611          * This will be set by init()
4612          * @private
4613          */
4614         _topics,
4615 
4616         /**
4617          * Config object needed to initialize this library
4618          * This must be set by init()
4619          * @private
4620          */
4621         _config,
4622 
4623         /**
4624          * @private
4625          * Whether or not this ClientService instance is a Master.
4626          */
4627         _isMaster = false,
4628 
4629         /**
4630          * @private
4631          * Whether the Client Services have been initiated yet.
4632          */
4633         _inited = false,
4634 
4635         /**
4636          * Stores the list of subscription IDs for all subscriptions so that it
4637          * could be retrieve for unsubscriptions.
4638          * @private
4639          */
4640         _subscriptionID = {},
4641 
4642         /**
4643          * The possible states of the JabberWerx BOSH connection.
4644          * @private
4645          */
4646         _STATUS = {
4647             CONNECTING: "connecting",
4648             CONNECTED: "connected",
4649             DISCONNECTED: "disconnected",
4650             DISCONNECTED_CONFLICT: "conflict",
4651             DISCONNECTED_UNAUTHORIZED: "unauthorized",
4652             DISCONNECTING: "disconnecting",
4653             RECONNECTING: "reconnecting",
4654             UNLOADING: "unloading",
4655             FAILING: "failing",
4656             RECOVERED: "recovered"
4657         },
4658         
4659         /**
4660          * Local reference for authMode enum object.
4661          * @private
4662          */
4663         _authModes,
4664 
4665         _failoverMode = false,
4666 
4667         /**
4668          * Handler function to be invoked when BOSH connection is connecting.
4669          * @private
4670          */
4671         _onConnectingHandler,
4672 
4673         /**
4674          * Handler function to be invoked when BOSH connection is connected
4675          * @private
4676          */
4677         _onConnectHandler,
4678 
4679         /**
4680          * Handler function to be invoked when BOSH connection is disconnecting.
4681          * @private
4682          */
4683         _onDisconnectingHandler,
4684 
4685         /**
4686          * Handler function to be invoked when the BOSH is disconnected.
4687          * @private
4688          */
4689         _onDisconnectHandler,
4690 
4691         /**
4692          * Handler function to be invoked when the BOSH is reconnecting.
4693          * @private
4694          */
4695         _onReconnectingHandler,
4696         
4697         /**
4698          * Handler function to be invoked when the BOSH is unloading.
4699          * @private
4700          */
4701         _onUnloadingHandler,
4702 
4703         /**
4704          * Contains a cache of the latest connection info containing the current
4705          * state of the BOSH connection and the resource ID.
4706          * @private
4707          */
4708         _connInfo,
4709 
4710         /**
4711          * Keeps track of all the objects that need to be refreshed when we recover
4712          * due to our resilient connection. Only objects that we subscribe to will
4713          * be added to this list.
4714          * @private
4715          */
4716         _refreshList = [],
4717         
4718         /**
4719          * Needs to be passed as authorization header inside makeRequest wrapper function
4720          */
4721         _authHeaderString,
4722 
4723         /**
4724          * @private
4725          * Centralized logger.log method for external logger
4726          * @param {String} msg
4727          */
4728         _log = function (msg) {
4729             // If the external logger throws up, it stops here.
4730             try {
4731                 if (_logger.log) {
4732                     _logger.log("[ClientServices] " + msg);
4733                 }
4734             } catch (e) { }
4735         },
4736 
4737         /**
4738          * Go through each object in the _refreshList and call its refresh() function
4739          * @private
4740          */
4741         _refreshObjects = function () {
4742             var i;
4743 
4744             // wipe out the explicit subscription list before we refresh objects
4745             if (_publisher) {
4746                 _publisher.wipeout();
4747             }
4748 
4749             // refresh each item in the refresh list
4750             for (i = _refreshList.length - 1; i >= 0; i -= 1) {
4751                 _log("Refreshing " + _refreshList[i].getRestUrl());
4752                 _refreshList[i].refresh(10);
4753             }
4754         },
4755 
4756         /**
4757          * Handler to process connection info publishes.
4758          * @param {Object} data
4759          *     The connection info data object.
4760          * @param {String} data.status
4761          *     The BOSH connection status.
4762          * @param {String} data.resourceID
4763          *     The resource ID for the connection.
4764          * @private
4765          */
4766         _connInfoHandler =  function (data) {
4767 
4768             //Invoke registered handler depending on status received. Due to the
4769             //request topic where clients can make request for the Master to publish
4770             //the connection info, there is a chance that duplicate connection info
4771             //events may be sent, so ensure that there has been a state change
4772             //before invoking the handlers.
4773             if (_connInfo === undefined || _connInfo.status !== data.status) {
4774                 _connInfo = data;
4775                 switch (data.status) {
4776                 case _STATUS.CONNECTING:
4777                     if (_isMaster && _onConnectingHandler) {
4778                         _onConnectingHandler();
4779                     }
4780                     break;
4781                 case _STATUS.CONNECTED:
4782                     if ((_isMaster || !_failoverMode) && _onConnectHandler) {
4783                         _onConnectHandler();
4784                     }
4785                     break;
4786                 case _STATUS.DISCONNECTED:
4787                     if (_isMaster && _onDisconnectHandler) {
4788                         _onDisconnectHandler();
4789                     }
4790                     break;
4791                 case _STATUS.DISCONNECTED_CONFLICT:
4792                     if (_isMaster && _onDisconnectHandler) {
4793                         _onDisconnectHandler("conflict");
4794                     }
4795                     break;
4796                 case _STATUS.DISCONNECTED_UNAUTHORIZED:
4797                     if (_isMaster && _onDisconnectHandler) {
4798                         _onDisconnectHandler("unauthorized");
4799                     }
4800                     break;
4801                 case _STATUS.DISCONNECTING:
4802                     if (_isMaster && _onDisconnectingHandler) {
4803                         _onDisconnectingHandler();
4804                     }
4805                     break;
4806                 case _STATUS.RECONNECTING:
4807                     if (_isMaster && _onReconnectingHandler) {
4808                         _onReconnectingHandler();
4809                     }
4810                     break;
4811                 case _STATUS.UNLOADING:
4812                     if (_isMaster && _onUnloadingHandler) {
4813                         _onUnloadingHandler();
4814                     }
4815                     break;
4816                 case _STATUS.FAILING:
4817                     if (!_isMaster) {
4818                         // Stop
4819                         _failoverMode = true;
4820                         if (_onDisconnectHandler) {
4821                             _onDisconnectHandler();
4822                         }
4823                     }
4824                     break;
4825                 case _STATUS.RECOVERED:
4826                     if (!_isMaster) {
4827                         _failoverMode = false;
4828                         if (_onConnectHandler) {
4829                             _onConnectHandler();
4830                         }
4831                     }
4832                     // Whenever we are recovered, we need to refresh any objects
4833                     // that are stored.
4834                     _refreshObjects();
4835                     break;
4836                 }
4837             }
4838         },
4839 
4840         /**
4841          * Ensure that ClientServices have been inited.
4842          * @private
4843          */
4844         _isInited = function () {
4845             if (!_inited) {
4846                 throw new Error("ClientServices needs to be inited.");
4847             }
4848         },
4849 
4850         /**
4851          * Have the client become the Master by initiating a tunnel to a shared
4852          * event BOSH connection. The Master is responsible for publishing all
4853          * events to the pubsub infrastructure.
4854          * 
4855          * TODO: Currently we only check the global auth mode. This code has to 
4856          * handle mixed mode - in this case the user specfic SSO mode has to be
4857          * exposed via an API.
4858          * 
4859          * @private
4860          */
4861         _becomeMaster = function () {
4862             var creds , id;
4863             _tunnel = new MasterTunnel(_config.host, _config.scheme);
4864             _publisher = new MasterPublisher(_tunnel, _hub);
4865             if(_authModes.SSO === _config.systemAuthMode ) {
4866                 creds = _config.authToken;
4867             } else {
4868                 creds = _config.password;
4869             }
4870             _util = Utilities;
4871              id = _util.encodeNodeName(_config.id);
4872             _tunnel.init(id, creds, _config.xmppDomain, _config.pubsubDomain, _config.resource);
4873             _isMaster = true;
4874         },
4875 
4876         /**
4877          * Make a request to the request channel to have the Master publish the
4878          * connection info object.
4879          * @private
4880          */
4881         _makeConnectionInfoReq = function () {
4882             var data = {
4883                 type: "ConnectionInfoReq",
4884                 data: {},
4885                 invokeID: (new Date()).getTime()
4886             };
4887             _hub.publish(_topics.REQUESTS, data);
4888         },
4889 
4890         /**
4891          * Utility method to register a handler which is associated with a
4892          * particular connection status.
4893          * @param {String} status
4894          *     The connection status string.
4895          * @param {Function} handler
4896          *     The handler to associate with a particular connection status.
4897          * @throws {Error}
4898          *     If the handler provided is not a function.
4899          * @private
4900          */
4901         _registerHandler = function (status, handler) {
4902             if (typeof handler === "function") {
4903                 if (_connInfo && _connInfo.status === status) {
4904                     handler();
4905                 }
4906                 switch (status) {
4907                 case _STATUS.CONNECTING:
4908                     _onConnectingHandler = handler;
4909                     break;
4910                 case _STATUS.CONNECTED:
4911                     _onConnectHandler = handler;
4912                     break;
4913                 case _STATUS.DISCONNECTED:
4914                     _onDisconnectHandler = handler;
4915                     break;
4916                 case _STATUS.DISCONNECTING:
4917                     _onDisconnectingHandler = handler;
4918                     break;
4919                 case _STATUS.RECONNECTING:
4920                     _onReconnectingHandler = handler;
4921                     break;
4922                 case _STATUS.UNLOADING:
4923                     _onUnloadingHandler = handler;
4924                     break;
4925                 }
4926 
4927             } else {
4928                 throw new Error("Callback is not a function");
4929             }
4930         },
4931         
4932         /**
4933          * @private
4934          * Retrieves systemAuthMode from parent Finesse Container. If parent is not available, mode will be retrieved from the systemInfo rest object
4935          * @throws {Error}
4936          *     If unable to retrieve systemAuthMode
4937          */
4938         _getSystemAuthMode = function(){
4939           var parentFinesse , sysInfo;
4940           // For gadgets hosted outside of finesse container , finesse parent object will not be available
4941             try{
4942                 parentFinesse = window.parent.finesse;
4943             } catch (e){
4944                 parentFinesse = undefined;
4945             }
4946 
4947             if( parentFinesse ){
4948                _config.systemAuthMode =  parentFinesse.container.Config.systemAuthMode; 
4949             } else {
4950                sysInfo = new finesse.restservices.SystemInfo({
4951                    id: _config.id,
4952                    onLoad: function (systemInfo) {
4953                         _config.systemAuthMode = systemInfo.getSystemAuthMode();
4954                    },
4955                    onError: function (errRsp) {
4956                         throw new Error("Unable to retrieve systemAuthMode from config. Initialization failed......");
4957                    }
4958               });
4959                        
4960             }
4961        };
4962 
4963         return {
4964 
4965             /**
4966              * @private
4967              * Adds an item to the list to be refreshed upon reconnect
4968              * @param {RestBase} object - rest object to be refreshed
4969              */
4970             addToRefreshList: function (object) {
4971                 _refreshList.push(object);
4972             },
4973 
4974             /**
4975              * @private
4976              * Removes the given item from the refresh list
4977              * @param  {RestBase} object - rest object to be removed
4978              */
4979             removeFromRefreshList: function (object) {
4980                 var i;
4981                 for (i = _refreshList.length - 1; i >= 0; i -= 1) {
4982                     if (_refreshList[i] === object) {
4983                         _refreshList.splice(i, 1);
4984                         break;
4985                     }
4986                 }
4987             },
4988 
4989             /**
4990              * @private
4991              * The location of the tunnel HTML URL.
4992              * @returns {String}
4993              *     The location of the tunnel HTML URL.
4994              */
4995             getTunnelURL: function () {
4996                 return _tunnel.getTunnelURL();            
4997             },
4998             
4999             /**
5000              * @private
5001              * Indicates whether the tunnel frame is loaded.
5002              * @returns {Boolean}
5003              *     True if the tunnel frame is loaded, false otherwise.
5004              */
5005             isTunnelLoaded: function () {
5006                 return _tunnel.isTunnelLoaded();            
5007             },
5008             
5009             /**
5010              * @private
5011              * Indicates whether the ClientServices instance is a Master.
5012              * @returns {Boolean}
5013              *     True if this instance of ClientServices is a Master, false otherwise.
5014              */
5015             isMaster: function () {
5016                 return _isMaster;
5017             },
5018 
5019             /**
5020              * @private
5021              * Get the resource ID. An ID is only available if the BOSH connection has
5022              * been able to connect successfully.
5023              * @returns {String}
5024              *     The resource ID string. Null if the BOSH connection was never
5025              *     successfully created and/or the resource ID has not been associated.
5026              */
5027             getResourceID: function () {
5028                 if (_connInfo !== undefined) {
5029                     return _connInfo.resourceID;
5030                 }
5031                 return null;
5032             },
5033             
5034             /*
5035             getHub: function () {
5036                 return _hub;
5037             },
5038         */
5039             /**
5040              * @private
5041              * Add a callback to be invoked when the BOSH connection is attempting
5042              * to connect. If the connection is already trying to connect, the
5043              * callback will be invoked immediately.
5044              * @param {Function} handler
5045              *      An empty param function to be invoked on connecting. Only one
5046              *      handler can be registered at a time. Handlers already registered
5047              *      will be overwritten.
5048              */
5049             registerOnConnectingHandler: function (handler) {
5050                 _registerHandler(_STATUS.CONNECTING, handler);
5051             },
5052 
5053             /**
5054              * @private
5055              * Removes the on connecting callback that was registered.
5056              */
5057             unregisterOnConnectingHandler: function () {
5058                 _onConnectingHandler = undefined;
5059             },
5060 
5061             /**
5062              * Add a callback to be invoked when all of the following conditions are met:
5063              * <ul>
5064              *   <li>When Finesse goes IN_SERVICE</li>
5065              *   <li>The BOSH connection is established</li>
5066              *   <li>The Finesse user presence becomes available</li>
5067              * </ul>
5068              * If all these conditions are met at the time this function is called, then
5069              * the handler will be invoked immediately.
5070              * @param {Function} handler
5071              *      An empty param function to be invoked on connect. Only one handler
5072              *      can be registered at a time. Handlers already registered will be
5073              *      overwritten.
5074              * @example
5075              *      finesse.clientservices.ClientServices.registerOnConnectHandler(gadget.myCallback);
5076              */
5077             registerOnConnectHandler: function (handler) {
5078                 _registerHandler(_STATUS.CONNECTED, handler);
5079             },
5080 
5081             /**
5082              * @private
5083              * Removes the on connect callback that was registered.
5084              */
5085             unregisterOnConnectHandler: function () {
5086                 _onConnectHandler = undefined;
5087             },
5088 
5089             /**
5090              * Add a callback to be invoked when any of the following occurs:
5091              * <ul>
5092              *   <li>Finesse is no longer IN_SERVICE</li>
5093              *   <li>The BOSH connection is lost</li>
5094              *   <li>The presence of the Finesse user is no longer available</li>
5095              * </ul>
5096              * If any of these conditions are met at the time this function is
5097              * called, the callback will be invoked immediately.
5098              * @param {Function} handler
5099              *      An empty param function to be invoked on disconnected. Only one
5100              *      handler can be registered at a time. Handlers already registered
5101              *      will be overwritten.
5102              * @example
5103              *      finesse.clientservices.ClientServices.registerOnDisconnectHandler(gadget.myCallback);
5104              */
5105             registerOnDisconnectHandler: function (handler) {
5106                 _registerHandler(_STATUS.DISCONNECTED, handler);
5107             },
5108 
5109             /**
5110              * @private
5111              * Removes the on disconnect callback that was registered.
5112              */
5113             unregisterOnDisconnectHandler: function () {
5114                 _onDisconnectHandler = undefined;
5115             },
5116 
5117             /**
5118              * @private
5119              * Add a callback to be invoked when the BOSH is currently disconnecting. If
5120              * the connection is already disconnecting, invoke the callback immediately.
5121              * @param {Function} handler
5122              *      An empty param function to be invoked on disconnected. Only one
5123              *      handler can be registered at a time. Handlers already registered
5124              *      will be overwritten.
5125              */
5126             registerOnDisconnectingHandler: function (handler) {
5127                 _registerHandler(_STATUS.DISCONNECTING, handler);
5128             },
5129 
5130             /**
5131              * @private
5132              * Removes the on disconnecting callback that was registered.
5133              */
5134             unregisterOnDisconnectingHandler: function () {
5135                 _onDisconnectingHandler = undefined;
5136             },
5137 
5138             /**
5139              * @private
5140              * Add a callback to be invoked when the BOSH connection is attempting
5141              * to connect. If the connection is already trying to connect, the
5142              * callback will be invoked immediately.
5143              * @param {Function} handler
5144              *      An empty param function to be invoked on connecting. Only one
5145              *      handler can be registered at a time. Handlers already registered
5146              *      will be overwritten.
5147              */
5148             registerOnReconnectingHandler: function (handler) {
5149                 _registerHandler(_STATUS.RECONNECTING, handler);
5150             },
5151 
5152             /**
5153              * @private
5154              * Removes the on reconnecting callback that was registered.
5155              */
5156             unregisterOnReconnectingHandler: function () {
5157                 _onReconnectingHandler = undefined;
5158             },
5159             
5160             /**
5161              * @private
5162              * Add a callback to be invoked when the BOSH connection is unloading
5163              * 
5164              * @param {Function} handler
5165              *      An empty param function to be invoked on connecting. Only one
5166              *      handler can be registered at a time. Handlers already registered
5167              *      will be overwritten.
5168              */
5169             registerOnUnloadingHandler: function (handler) {
5170                 _registerHandler(_STATUS.UNLOADING, handler);
5171             },
5172             
5173             /**
5174              * @private
5175              * Removes the on unloading callback that was registered.
5176              */
5177             unregisterOnUnloadingHandler: function () {
5178                 _onUnloadingHandler = undefined;
5179             },
5180 
5181             /**
5182              * @private
5183              * Proxy method for gadgets.io.makeRequest. The will be identical to gadgets.io.makeRequest
5184              * ClientServices will mixin the BASIC Auth string, locale, and host, since the
5185              * configuration is encapsulated in here anyways.
5186              * This removes the dependency
5187              * @param {String} url
5188              *     The relative url to make the request to (the host from the passed in config will be
5189              *     appended). It is expected that any encoding to the URL is already done.
5190              * @param {Function} handler
5191              *     Callback handler for makeRequest to invoke when the response returns.
5192              *     Completely passed through to gadgets.io.makeRequest
5193              * @param {Object} params
5194              *     The params object that gadgets.io.makeRequest expects. Authorization and locale
5195              *     headers are mixed in.
5196              */
5197             makeRequest: function (url, handler, params) {
5198                 var requestedScheme, scheme = "http";
5199                 
5200                 // ClientServices needs to be initialized with a config for restHost, auth, and locale
5201                 _isInited();
5202                 
5203                 // Allow mixin of auth and locale headers
5204                 params = params || {};
5205 
5206                 // Override refresh interval to 0 instead of default 3600 as a way to workaround makeRequest 
5207                 // using GET http method because then the params are added to the url as query params, which 
5208                 // exposes the authorization string in the url. This is a placeholder until oauth comes in
5209                 params[gadgets.io.RequestParameters.REFRESH_INTERVAL] = params[gadgets.io.RequestParameters.REFRESH_INTERVAL] || 0;
5210                 
5211                 params[gadgets.io.RequestParameters.HEADERS] = params[gadgets.io.RequestParameters.HEADERS] || {};
5212                 
5213                 // Add Basic auth to request header
5214                 params[gadgets.io.RequestParameters.HEADERS].Authorization = _util.getAuthHeaderString(_config);
5215 
5216                 //Locale
5217                 params[gadgets.io.RequestParameters.HEADERS].locale = _config.locale;
5218 
5219                 //Allow clients to override the scheme:
5220                 //  - If not specified  => we use HTTP
5221                 //  - If null specified => we use _config.scheme
5222                 //  - Otherwise         => we use whatever they provide
5223                 requestedScheme = params.SCHEME; 
5224                 if (!(requestedScheme === undefined || requestedScheme === "undefined")) {
5225                     if (requestedScheme === null) {
5226                        scheme = _config.scheme;
5227                     } else {
5228                        scheme = requestedScheme;
5229                     }
5230                 }
5231                 scheme = _config.restScheme || scheme;
5232                 
5233                 _log("RequestedScheme: " + requestedScheme + "; Scheme: " + scheme);
5234                 gadgets.io.makeRequest(encodeURI(scheme + "://" + _config.restHost + ":" + _config.localhostPort) + url, handler, params);
5235             },
5236 
5237             /**
5238              * @private
5239              * Utility function to make a subscription to a particular topic. Only one
5240              * callback function is registered to a particular topic at any time.
5241              * @param {String} topic
5242              *     The full topic name. The topic name should follow the OpenAjax
5243              *     convention using dot notation (ex: finesse.api.User.1000).
5244              * @param {Function} callback
5245              *     The function that should be invoked with the data when an event
5246              *     is delivered to the specific topic.
5247              * @returns {Boolean}
5248              *     True if the subscription was made successfully and the callback was
5249              *     been registered. False if the subscription already exist, the
5250              *     callback was not overwritten.
5251              */
5252             subscribe: function (topic, callback, disableDuringFailover) {
5253                 _isInited();
5254 
5255                 //Ensure that the same subscription isn't made twice.
5256                 if (!_subscriptionID[topic]) {
5257                     //Store the subscription ID using the topic name as the key.
5258                     _subscriptionID[topic] = _hub.subscribe(topic,
5259                         //Invoke the callback just with the data object.
5260                         function (topic, data) {
5261                             if (!disableDuringFailover || _isMaster || !_failoverMode) {
5262                                 // Halt all intermediate event processing while the master instance attempts to rebuild the connection. This only occurs:
5263                                 // - For RestBase objects (which pass the disableDuringFailover flag), so that intermediary events while recovering are hidden away until everything is all good
5264                                 //    - We shouldn't be halting anything else because we have infrastructure requests that use the hub pub sub
5265                                 // - Master instance will get all events regardless, because it is responsible for managing failover
5266                                 // - If we are not in a failover mode, everything goes
5267                                 // _refreshObjects will reconcile anything that was missed once we are back in action
5268                                 callback(data);
5269                             } 
5270                         });
5271                     return true;
5272                 }
5273                 return false;
5274             },
5275 
5276             /**
5277              * @private
5278              * Unsubscribe from a particular topic.
5279              * @param {String} topic
5280              *     The full topic name.
5281              */
5282             unsubscribe: function (topic) {
5283                 _isInited();
5284 
5285                 //Unsubscribe from the topic using the subscription ID recorded when
5286                 //the subscription was made, then delete the ID from data structure.
5287                 if (_subscriptionID[topic]) {
5288                     _hub.unsubscribe(_subscriptionID[topic]);
5289                     delete _subscriptionID[topic];
5290                 }
5291             },
5292 
5293             /**
5294              * @private
5295              * Make a request to the request channel to have the Master subscribe
5296              * to a node.
5297              * @param {String} node
5298              *     The node to subscribe to.
5299              */
5300             subscribeNode: function (node, handler) {
5301                 if (handler && typeof handler !== "function") {
5302                     throw new Error("ClientServices.subscribeNode: handler is not a function");
5303                 }
5304                 
5305                 // Construct the request to send to MasterPublisher through the OpenAjax Hub
5306                 var data = {
5307                     type: "SubscribeNodeReq",
5308                     data: {node: node},
5309                     invokeID: _util.generateUUID()
5310                 },
5311                 responseTopic = _topics.RESPONSES + "." + data.invokeID,
5312                 _this = this;
5313 
5314                 // We need to first subscribe to the response channel
5315                 this.subscribe(responseTopic, function (rsp) {
5316                     // Since this channel is only used for this singular request,
5317                     // we are not interested anymore.
5318                     // This is also critical to not leaking memory by having OpenAjax
5319                     // store a bunch of orphaned callback handlers that enclose on
5320                     // our entire ClientServices singleton
5321                     _this.unsubscribe(responseTopic);
5322                     if (handler) {
5323                         handler(data.invokeID, rsp);
5324                     }
5325                 });
5326                 // Then publish the request on the request channel
5327                 _hub.publish(_topics.REQUESTS, data);
5328             },
5329 
5330             /**
5331              * @private
5332              * Make a request to the request channel to have the Master unsubscribe
5333              * from a node.
5334              * @param {String} node
5335              *     The node to unsubscribe from.
5336              */
5337             unsubscribeNode: function (node, subid, handler) {
5338                 if (handler && typeof handler !== "function") {
5339                     throw new Error("ClientServices.unsubscribeNode: handler is not a function");
5340                 }
5341                 
5342                 // Construct the request to send to MasterPublisher through the OpenAjax Hub
5343                 var data = {
5344                     type: "UnsubscribeNodeReq",
5345                     data: {
5346                         node: node,
5347                         subid: subid
5348                     },
5349                     invokeID: _util.generateUUID()
5350                 },
5351                 responseTopic = _topics.RESPONSES + "." + data.invokeID,
5352                 _this = this;
5353 
5354                 // We need to first subscribe to the response channel
5355                 this.subscribe(responseTopic, function (rsp) {
5356                     // Since this channel is only used for this singular request,
5357                     // we are not interested anymore.
5358                     // This is also critical to not leaking memory by having OpenAjax
5359                     // store a bunch of orphaned callback handlers that enclose on
5360                     // our entire ClientServices singleton
5361                     _this.unsubscribe(responseTopic);
5362                     if (handler) {
5363                         handler(rsp);
5364                     }
5365                 });
5366                 // Then publish the request on the request channel
5367                 _hub.publish(_topics.REQUESTS, data);
5368             },
5369             
5370             /**
5371              * @private
5372              * Make a request to the request channel to have the Master connect to the XMPP server via BOSH
5373              */
5374             makeConnectionReq : function () {
5375                 // Disallow others (non-masters) from administering BOSH connections that are not theirs
5376                 if (_isMaster && _publisher) {
5377                     _publisher.connect(_config.id, _config.password, _config.xmppDomain);
5378                 } else {
5379                     _log("F403: Access denied. Non-master ClientService instances do not have the clearance level to make this request: makeConnectionReq");
5380                 }
5381             },
5382         
5383             /**
5384              * @private
5385              * Set's the global logger for this Client Services instance.
5386              * @param {Object} logger
5387              *     Logger object with the following attributes defined:<ul>
5388              *         <li><b>log:</b> function (msg) to simply log a message
5389              *     </ul>
5390              */
5391             setLogger: function (logger) {
5392                 // We want to check the logger coming in so we don't have to check every time it is called.
5393                 if (logger && typeof logger === "object" && typeof logger.log === "function") {
5394                     _logger = logger;
5395                 } else {
5396                     // We are resetting it to an empty object so that _logger.log in .log is falsy.
5397                     _logger = {};
5398                 }
5399             },
5400             
5401             /**
5402              * @private
5403              * Centralized logger.log method for external logger
5404              * @param {String} msg
5405              *     Message to log
5406              */
5407             log: _log,
5408 
5409             /**
5410              * @class
5411              * Allow clients to make Finesse API requests and consume Finesse events by
5412              * calling a set of exposed functions. The Services layer will do the dirty
5413              * work of establishing a shared BOSH connection (for designated Master
5414              * modules), consuming events for client subscriptions, and constructing API
5415              * requests.
5416              * 
5417              * @constructs
5418              */
5419             _fakeConstuctor: function () {
5420                 /* This is here so we can document init() as a method rather than as a constructor. */
5421             },
5422             
5423             /**
5424              * Initiates the Client Services with the specified config parameters.
5425              * Enabling the Client Services as Master will trigger the establishment
5426              * of a BOSH event connection.
5427              * @param {finesse.gadget.Config} config
5428              *     Configuration object containing properties used for making REST requests:<ul>
5429              *         <li><b>host:</b> The Finesse server IP/host as reachable from the browser
5430              *         <li><b>restHost:</b> The Finesse API IP/host as reachable from the gadget container
5431              *         <li><b>id:</b> The ID of the user. This is an optional param as long as the
5432              *         appropriate authorization string is provided, otherwise it is
5433              *         required.</li>
5434              *         <li><b>password:</b> The password belonging to the user. This is an optional param as
5435              *         long as the appropriate authorization string is provided,
5436              *         otherwise it is required.</li>
5437              *         <li><b>authorization:</b> The base64 encoded "id:password" authentication string. This
5438              *         param is provided to allow the ability to hide the password
5439              *         param. If provided, the id and the password extracted from this
5440              *         string will be used over the config.id and config.password.</li>
5441              *     </ul>
5442              * @throws {Error} If required constructor parameter is missing.
5443              * @example
5444              *      finesse.clientservices.ClientServices.init(finesse.gadget.Config);
5445              */
5446             init: function (config) {
5447                 if (!_inited) {
5448                     //Validate the properties within the config object if one is provided.
5449                     if (!(typeof config === "object" &&
5450                          typeof config.host === "string" && config.host.length > 0 && config.restHost && 
5451                          (typeof config.authorization === "string" ||
5452                                  (typeof config.id === "string")))) {
5453                         throw new Error("Config object contains invalid properties.");
5454                     }
5455 
5456                     // Initialize configuration
5457                     _config = config;
5458 
5459                     // Set shortcuts
5460                     _util = Utilities;
5461                     _authModes = _util.getAuthModes();
5462                     _topics = Topics;
5463                     
5464                     //TODO: document when this is properly supported
5465                     // Allows hub and io dependencies to be passed in. Currently only used for unit tests.
5466                     _hub = config.hub || gadgets.Hub;
5467                     _io = config.io || gadgets.io;
5468 
5469                     //If the authorization string is provided, then use that to
5470                     //extract the ID and the password. Otherwise use the ID and
5471                     //password from the respective ID and password params.
5472                     if (_config.authorization) {
5473                         var creds = _util.getCredentials(_config.authorization);
5474                         _config.id = creds.id;
5475                         _config.password = creds.password;
5476                     }
5477                     else {
5478                         _config.authorization = _util.b64Encode(
5479                                 _config.id + ":" + _config.password);
5480                     }
5481                     
5482                     //In case if gadgets create their own config instance , add systemAuthMode property inside config object
5483                     if(!_config.systemAuthMode || _config.systemAuthMode === ""){
5484                         _getSystemAuthMode();
5485                     }
5486                     
5487                     if(!_config.authToken && _config.systemAuthMode === _authModes.SSO){
5488                           _config.authToken = _util.getToken();
5489                           if(!_config.authToken){
5490                               throw new Error("Access token is unavailable inside Config object.");
5491                           }
5492                     }
5493                     
5494                     _inited = true;
5495 
5496                     if (_hub) {
5497                         //Subscribe to receive connection information. Since it is possible that
5498                         //the client comes up after the Master comes up, the client will need
5499                         //to make a request to have the Master send the latest connection info.
5500                         //It would be possible that all clients get connection info again.
5501                         this.subscribe(_topics.EVENTS_CONNECTION_INFO, _connInfoHandler);
5502                         _makeConnectionInfoReq();
5503                     }
5504                 }
5505 
5506                 //Return the CS object for object chaining.
5507                 return this;
5508             },
5509 
5510             /**
5511              * @private
5512              * Initializes the BOSH component of this ClientServices instance. This establishes
5513              * the BOSH connection and will trigger the registered handlers as the connection
5514              * status changes respectively:<ul>
5515              *     <li>registerOnConnectingHandler</li>
5516              *     <li>registerOnConnectHandler</li>
5517              *     <li>registerOnDisconnectHandler</li>
5518              *     <li>registerOnDisconnectingHandler</li>
5519              *     <li>registerOnReconnectingHandler</li>
5520              *     <li>registerOnUnloadingHandler</li>
5521              * <ul>
5522              *
5523              * @param {Object} config
5524              *     An object containing the following (optional) handlers for the request:<ul>
5525              *         <li><b>xmppDomain:</b> {String} The domain of the XMPP server. Available from the SystemInfo object.
5526              *         This is used to construct the JID: user@domain.com</li>
5527              *         <li><b>pubsubDomain:</b> {String} The pub sub domain where the pub sub service is running.
5528              *         Available from the SystemInfo object.
5529              *         This is used for creating or removing subscriptions.</li>
5530              *         <li><b>resource:</b> {String} The resource to connect to the notification server with.</li>
5531              *     </ul>
5532              */
5533             initBosh: function (config) {
5534                 //Validate the properties within the config object if one is provided.
5535                 if (!(typeof config === "object" && typeof config.xmppDomain === "string" && typeof config.pubsubDomain === "string")) {
5536                     throw new Error("Config object contains invalid properties.");
5537                 }
5538                 
5539                 // Mixin the required information for establishing the BOSH connection
5540                 _config.xmppDomain = config.xmppDomain;
5541                 _config.pubsubDomain = config.pubsubDomain;
5542                 _config.resource = config.resource;
5543                 
5544                 //Initiate Master launch sequence
5545                 _becomeMaster(); 
5546             },
5547 
5548             /**
5549              * @private
5550              * Sets the failover mode to either FAILING or RECOVERED. This will only occur in the master instance and is meant to be
5551              * used by a stereotypical failover monitor type module to notify non-master instances (i.e. in gadgets)
5552              * @param {Object} failoverMode
5553              *     true if failing, false or something falsy when recovered
5554              */
5555             setFailoverMode: function (failoverMode) {
5556                 if (_isMaster) {
5557                     _hub.publish(_topics.EVENTS_CONNECTION_INFO, {
5558                         status: (failoverMode ? _STATUS.FAILING : _STATUS.RECOVERED)
5559                     });
5560                 }
5561             },
5562 
5563             /**
5564              * @private
5565              * Private accessor used to inject mocked private dependencies for unit testing
5566              */
5567             _getTestObj: function () {
5568                 return {
5569                     setPublisher: function (publisher) {
5570                         _publisher = publisher;
5571                     }
5572                 };
5573             }
5574         };
5575     }());
5576      
5577     window.finesse = window.finesse || {};
5578     window.finesse.clientservices = window.finesse.clientservices || {};
5579     window.finesse.clientservices.ClientServices = ClientServices;
5580     
5581     return ClientServices;
5582 
5583 });
5584 
5585 /**
5586  * The following comment prevents JSLint errors concerning undefined global variables.
5587  * It tells JSLint that these identifiers are defined elsewhere.
5588  */
5589 /*jslint bitwise:true, browser:true, nomen:true, regexp:true, sloppy:true, white:true */
5590 
5591 /** The following comment is to prevent jslint errors about 
5592  * using variables before they are defined.
5593  */
5594 /*global Handlebars */
5595 
5596 /**
5597  * JavaScript class to implement common notification
5598  *               functionality.
5599  * 
5600  * @requires Class
5601  * @requires finesse.FinesseBase
5602  */
5603 /** @private */
5604 define('restservices/Notifier',[
5605     'FinesseBase',
5606     'clientservices/ClientServices'
5607 ],
5608 function (FinesseBase, ClientServices) {
5609     var Notifier = FinesseBase.extend({
5610 		/**
5611          * Initializes the notifier object.
5612          */
5613         init : function () {
5614             this._super();
5615             this._listenerCallback = [];
5616         },
5617 
5618         /**
5619          * Add a listener.
5620          * 
5621          * @param callback_function
5622          * @param scope
5623          *            is the callback function to add
5624          */
5625         addListener : function (callback_function, scope) {
5626             var len = this._listenerCallback.length, i, cb, add = true;
5627             for (i = 0; i < len; i += 1) {
5628                 cb = this._listenerCallback[i].callback;
5629                 if (cb === callback_function) {
5630                     // this callback already exists
5631                     add = false;
5632                     break;
5633                 }
5634             }
5635             if (add) {
5636                 this._listenerCallback.push({ "callback": this._isAFunction(callback_function), "scope": (scope || window) });
5637             }            
5638         },
5639 
5640         /**
5641          * Remove a listener.
5642          * 
5643          * @param callback_function
5644          *            is the callback function to remove
5645          * @return {Boolean} true if removed
5646          */
5647         removeListener : function (callback_function) {
5648 
5649             var result = false, len = this._listenerCallback.length, i, cb;
5650             for (i = len - 1; i >= 0; i -=1) {
5651                 cb = this._listenerCallback[i].callback;
5652                 if (cb === callback_function) {
5653                     this._listenerCallback[i] = undefined;
5654                     this._listenerCallback.splice(i, 1);
5655                     result = true;
5656                     break;
5657                 }
5658             }
5659             
5660             return result;
5661         },
5662 
5663         /**
5664 	 * Removes all listeners
5665 	 * @return {undefined}
5666 	 */
5667 	reset: function () {
5668 		this._listenerCallback = [];
5669 	},
5670 
5671 	/**
5672          * Notify all listeners.
5673          * 
5674          * @param obj
5675          *            is the object that has changed
5676          */
5677         notifyListeners : function (obj) {
5678             var len = this._listenerCallback.length, i, callbackFunction, scope;
5679 
5680             for (i = 0; i < len; i += 1) {
5681                 // Be sure that one bad callback does not prevent other listeners
5682                 // from receiving.
5683                 try {
5684                     callbackFunction = this._listenerCallback[i].callback;
5685                     scope = this._listenerCallback[i].scope;
5686                     if (typeof callbackFunction === 'function') {
5687                         callbackFunction.call(scope, obj);
5688                     }
5689                 } catch (err) {
5690                     ClientServices.log("Exception caught: " + err);
5691                 }
5692             }
5693         },
5694 
5695         /**
5696          * Gets a copy of the listeners.
5697          * @return changeListenerCopy (array of callbacks)
5698          */
5699         getListeners : function () {
5700             var changeListenerCopy = [], len = this._listenerCallback.length, i;
5701 
5702             for (i = 0; i < len; i += 1) {
5703                 changeListenerCopy.push(this._listenerCallback[i].callback);
5704             }
5705 
5706             return changeListenerCopy;
5707         },
5708         
5709         /**
5710          * Verifies that the handler is function.
5711          * @param handler to verify
5712          * @return the handler 
5713          * @throws Error if not a function
5714          */
5715         _isAFunction : function (handler) {
5716             if (handler === undefined || typeof handler === "function") {
5717                 return handler;
5718             } else {
5719                 throw new Error("handler must be a function");
5720             }
5721         }
5722 	});
5723 	
5724 	window.finesse = window.finesse || {};
5725 	window.finesse.restservices = window.finesse.restservices || {};
5726 	window.finesse.restservices.Notifier = Notifier;
5727 
5728 	/** @namespace JavaScript classes and methods that represent REST objects and collections. */
5729     finesse.restservices = finesse.restservices || {};
5730 	
5731     return Notifier;
5732 });
5733 
5734 /**
5735  * JavaScript base object that all REST objects should inherit
5736  * from because it encapsulates and provides the common functionality that
5737  * all REST objects need.
5738  *
5739  * @requires finesse.clientservices.ClientServices
5740  * @requires Class
5741  */
5742 
5743 /** @private */
5744 define('restservices/RestBase',[
5745     "FinesseBase",
5746     "utilities/Utilities",
5747     "restservices/Notifier",
5748     "clientservices/ClientServices",
5749     "clientservices/Topics"
5750 ],
5751 function (FinesseBase, Utilities, Notifier, ClientServices, Topics) {
5752     
5753     var RestBase = FinesseBase.extend(/** @lends finesse.restservices.RestBase.prototype */{
5754 
5755         doNotLog: false,        
5756 
5757         /**
5758          * Used by _processUpdate() and restRequest().
5759          * Maps requestIds to object-wrapped callbacks passed to restRequest(),
5760          * so that one of the callbacks can be fired when a corresponding event is
5761          * received inside _processUpdate().
5762          * @private
5763          */
5764         _pendingCallbacks: {},
5765         
5766         /**
5767          * Gets the REST class for the current object.  This object throws an
5768          * exception because subtype must implement.
5769          * @throws {Error} because subtype must implement
5770          * @private
5771          */
5772         getRestClass: function () {
5773             throw new Error("getRestClass(): Not implemented in subtype.");
5774         },
5775 
5776         /**
5777          * Gets the REST type for the current object.  This object throws an
5778          * exception because subtype must implement.
5779          * @throws {Error} because subtype must implement.
5780          * @private
5781          */
5782         getRestType: function () {
5783             throw new Error("getRestType(): Not implemented in subtype.");
5784         },
5785 
5786         /**
5787          * Gets the node path for the current object.
5788          * @private
5789          */
5790         getXMPPNodePath: function () {
5791             return this.getRestUrl();
5792         },
5793         
5794         /**
5795          * Boolean function that specifies whether the REST object supports
5796          * requests. True by default. Subclasses should override if false.
5797          * @private
5798          */
5799         supportsRequests: true,
5800 
5801         /**
5802          * Boolean function that specifies whether the REST object supports
5803          * subscriptions. True by default. Subclasses should override if false.
5804          * @private
5805          */
5806         supportsSubscriptions: true,
5807         
5808         /**
5809          * Boolean function that specifies whether the REST object should retain
5810          * a copy of the REST response. False by default. Subclasses should override if true.
5811          * @private
5812          */
5813         keepRestResponse: false,
5814 
5815         /**
5816          * Number that represents the REST Response status that is returned. Only
5817          * set if keepRestResponse is set to true.
5818          * @public
5819          */
5820         restResponseStatus: null,
5821 
5822         /**
5823          * Object to store additional headers to be sent with the REST Request, null by default.
5824          * @private
5825          */
5826         extraHeaders: null,
5827 
5828         /**
5829          * Boolean function that specifies whether the REST object explicitly
5830          * subscribes. False by default. Subclasses should override if true.
5831          * @private
5832          */
5833         explicitSubscription: false,
5834 
5835         /**
5836          * Boolean function that specifies whether subscribing should be
5837          * automatically done at construction. Defaults to true.
5838          * This be overridden at object construction, not by implementing subclasses
5839          * @private
5840          */
5841         autoSubscribe: true,
5842         
5843         /**
5844          * Private reference to default logger
5845          * @private
5846          */
5847         _logger: {
5848             log: ClientServices.log,
5849             error: ClientServices.log
5850         },
5851 
5852         /**
5853          * @class
5854          * JavaScript representation of a REST object. Also exposes methods to operate
5855          * on the object against the server.  This object is typically extended into individual
5856          * REST Objects (like Dialog, User, etc...), and shouldn't be used directly.
5857          *
5858          * @constructor
5859          * @param {String} id
5860          *     The ID that uniquely identifies the REST object.
5861          * @param {Object} callbacks
5862          *     An object containing callbacks for instantiation and runtime
5863          *     Callback to invoke upon successful instantiation, passes in REST object.
5864          * @param {Function} callbacks.onLoad(this)
5865          *     Callback to invoke upon loading the data for the first time.
5866          * @param {Function} callbacks.onChange(this)
5867          *     Callback to invoke upon successful update object (PUT)
5868          * @param {Function} callbacks.onAdd(this)
5869          *     Callback to invoke upon successful update to add object (POST)
5870          * @param {Function} callbacks.onDelete(this)
5871          *     Callback to invoke upon successful update to delete object (DELETE)
5872          * @param {Function} callbacks.onError(rsp)
5873          *     Callback to invoke on update error (refresh or event)
5874          *     as passed by finesse.restservices.RestBase.restRequest()
5875          *     {
5876          *         status: {Number} The HTTP status code returned
5877          *         content: {String} Raw string of response
5878          *         object: {Object} Parsed object of response
5879          *         error: {Object} Wrapped exception that was caught
5880          *         error.errorType: {String} Type of error that was caught
5881          *         error.errorMessage: {String} Message associated with error
5882          *     }
5883          * @param {RestBase} [restObj]
5884          *     A RestBase parent object which this object has an association with.
5885          * @constructs
5886          */
5887         init: function (options, callbacks, restObj) {
5888             /**
5889               * Initialize the base class
5890               */
5891             var _this = this;
5892 
5893             this._super();
5894 
5895             if (typeof options === "object") {
5896                 this._id = options.id;
5897                 this._restObj = options.parentObj;
5898                 this.autoSubscribe = (options.autoSubscribe === false) ? false : true;
5899                 this.doNotSubscribe = options.doNotSubscribe;
5900                 this.doNotRefresh = this.doNotRefresh || options.doNotRefresh;
5901                 callbacks = {
5902                     onLoad: options.onLoad,
5903                     onChange: options.onChange,
5904                     onAdd: options.onAdd,
5905                     onDelete: options.onDelete,
5906                     onError: options.onError
5907                 };
5908             } else {
5909                 this._id = options;
5910                 this._restObj = restObj;
5911             }
5912             
5913             // Common stuff
5914             
5915             this._data = {};
5916             
5917             //Contains the full rest response to be processed by upper layers if needed
5918             this._restResponse = undefined;
5919 
5920             this._lastUpdate = {};
5921 
5922             this._util = Utilities;
5923 
5924             //Should be correctly initialized in either a window OR gadget context
5925             this._config = finesse.container.Config;
5926 
5927             // Setup all the notifiers - change, load and error.
5928             this._changeNotifier = new Notifier();
5929             this._loadNotifier = new Notifier();
5930             this._addNotifier = new Notifier();
5931             this._deleteNotifier = new Notifier();
5932             this._errorNotifier = new Notifier();
5933 
5934             this._loaded = false;
5935 
5936             // Protect against null dereferencing of options allowing its
5937             // (nonexistent) keys to be read as undefined
5938             callbacks = callbacks || {};
5939 
5940             this.addHandler('load', callbacks.onLoad);
5941             this.addHandler('change', callbacks.onChange);
5942             this.addHandler('add', callbacks.onAdd);
5943             this.addHandler('delete', callbacks.onDelete);
5944             this.addHandler('error', callbacks.onError);
5945 
5946             // Attempt to get the RestType then synchronize
5947             try {
5948                 this.getRestType();
5949 
5950                 // Only subscribe if this REST object supports subscriptions
5951                 // and autoSubscribe was not requested to be disabled as a construction option
5952                 if (this.supportsSubscriptions && this.autoSubscribe && !this.doNotSubscribe) {
5953                     this.subscribe({
5954                         success: function () {
5955                             //TODO: figure out how to use Function.call() or Function.apply() here...
5956                             //this is exactly the same as the below else case other than the scope of "this"
5957                             if (typeof options === "object" && options.data) {
5958                                 if (!_this._processObject(_this._normalize(options.data))) {
5959                                     // notify of error if we fail to construct
5960                                     _this._errorNotifier.notifyListeners(_this);
5961                                 }
5962                             } else {
5963                                 // Only subscribe if this REST object supports requests
5964                                 if (_this.supportsRequests) {
5965                                     _this._synchronize();
5966                                 }
5967                             }
5968                         },
5969                         error: function (err) {
5970                             _this._errorNotifier.notifyListeners(err);
5971                         }
5972                     });
5973                 } else {
5974                     if (typeof options === "object" && options.data) {
5975                         if (!this._processObject(this._normalize(options.data))) {
5976                             // notify of error if we fail to construct
5977                             this._errorNotifier.notifyListeners(this);
5978                         }
5979                     } else {
5980                         // Only subscribe if this REST object supports requests
5981                         if (this.supportsRequests) {
5982                             this._synchronize();
5983                         }
5984                     }
5985                 }
5986 
5987             } catch (err) {
5988                 this._logger.error('id=' + this._id + ': ' + err);
5989             }
5990         },
5991 
5992         /**
5993          * Determines if the object has a particular property.
5994          * @param obj is the object to examine
5995          * @param property is the property to check for
5996          * @returns {Boolean}
5997          */
5998         hasProperty: function (obj, prop) {
5999             return (obj !== null) && (obj.hasOwnProperty(prop));
6000         },
6001 
6002         /**
6003          * Gets a property from the object.
6004          * @param obj is the object to examine
6005          * @param property is the property to get
6006          * @returns {Property Value} or {Null} if not found
6007          */
6008         getProperty: function (obj, property) {
6009             var result = null;
6010 
6011             if (this.hasProperty(obj, property) === false) {
6012                 result = null;
6013             } else {
6014                 result = obj[property];
6015             }
6016             return result;
6017         },
6018 
6019         /**
6020          * Utility to extracts the ID from the specified REST URI. This is with the
6021          * assumption that the ID is always the last element in the URI after the
6022          * "/" delimiter.
6023          * @param {String} restUri
6024          *     The REST uri (i.e. /finesse/api/User/1000).
6025          * @private
6026          */
6027         _extractId: function (restObj) {
6028             var obj, restUri = "", strLoc;
6029             for (obj in restObj) {
6030                 if (restObj.hasOwnProperty(obj)) {
6031                     restUri = restObj[obj].uri;
6032                     break;
6033                 }
6034             }
6035             return Utilities.getId(restUri);
6036         },
6037 
6038         /**
6039          * Gets the data for this object.
6040          * @returns {Object} which is contained in data
6041          */
6042         getData: function () {
6043             return this._data;
6044         },
6045         
6046         /**
6047          * Gets the complete REST response to the request made
6048          * @returns {Object} which is contained in data
6049          * @private
6050          */
6051         getRestResponse: function () {
6052             return this._restResponse;
6053         },
6054 
6055         /**
6056          * The REST URL in which this object can be referenced.
6057          * @return {String}
6058          *     The REST URI for this object.
6059          * @private
6060          */
6061         getRestUrl: function () {
6062             var
6063             restObj = this._restObj,
6064             restUrl = "";
6065 
6066             //Prepend the base REST object if one was provided.
6067             if (restObj instanceof RestBase) {
6068                 restUrl += restObj.getRestUrl();
6069             }
6070             //Otherwise prepend with the default webapp name.
6071             else {
6072                 restUrl += "/finesse/api";
6073             }
6074 
6075             //Append the REST type.
6076             restUrl += "/" + this.getRestType();
6077 
6078             //Append ID if it is not undefined, null, or empty.
6079             if (this._id) {
6080                 restUrl += "/" + this._id;
6081             }
6082             return restUrl;
6083         },
6084 
6085         /**
6086          * Getter for the id of this RestBase
6087          * @returns {String}
6088          *     The id of this RestBase
6089          */
6090         getId: function () {
6091             return this._id;
6092         },
6093 
6094         /**
6095          * Synchronize this object with the server using REST GET request.
6096          * @returns {Object}
6097          *     {
6098          *         abort: {function} Function that signifies the callback handler to NOT process the response of the rest request
6099          *     }
6100          * @private
6101          */
6102         _synchronize: function (retries) {
6103             // Fetch this REST object
6104             if (typeof this._id === "string") {
6105                 var _this = this, isLoaded = this._loaded, _RETRY_INTERVAL = 10 * 1000;
6106 
6107                 return this._doGET(
6108                     {
6109                         success: function (rsp) {
6110                             if (!_this._processResponse(rsp)) {
6111                                 if (retries > 0) {
6112                                     setTimeout(function () {
6113                                         _this._synchronize(retries - 1);
6114                                     }, _RETRY_INTERVAL);
6115                                 } else {
6116                                     _this._errorNotifier.notifyListeners(_this);
6117                                 }
6118                             } else {
6119                                 // If this object was already "loaded" prior to
6120                                 // the _doGET request, then call the
6121                                 // changeNotifier
6122                                 if (isLoaded) {
6123                                     _this._changeNotifier.notifyListeners(_this);
6124                                 }
6125                             }
6126                         },
6127                         error: function (rsp) {
6128                             if (retries > 0) {
6129                                 setTimeout(function () {
6130                                     _this._synchronize(retries - 1);
6131                                 }, _RETRY_INTERVAL);
6132                                 
6133                             } else {
6134                                 _this._errorNotifier.notifyListeners(rsp);
6135                             }
6136                         }
6137                     }
6138                 );
6139 
6140             } else {
6141                 throw new Error("Can't construct a <" + this.getRestType() + "> due to invalid id type.");
6142             }
6143         },
6144 
6145         /**
6146          * Adds an handler to this object.
6147          * If notifierType is 'load' and the object has already loaded, the callback is invoked immediately
6148          * @param {String} notifierType
6149          *     The type of notifier to add to ('load', 'change', 'add', 'delete', 'error')
6150          * @param {Function} callback
6151          *     The function callback to invoke.
6152          * @example
6153          *   // Handler for additions to the Dialogs collection object.  
6154          *   // When Dialog (a RestBase object) is created, add a change handler.
6155          *   _handleDialogAdd = function(dialog) {
6156          *      dialog.addHandler('change', _handleDialogChange);
6157          *   }
6158          */
6159         addHandler: function (notifierType, callback, scope) {
6160             var notifier = null;
6161             try {
6162                 Utilities.validateHandler(callback);
6163 
6164                 notifier = this._getNotifierReference(notifierType);
6165 
6166                 notifier.addListener(callback, scope);
6167                 
6168                 // If load handler is added and object has
6169                 // already been loaded, invoke callback
6170                 // immediately
6171                 if (notifierType === 'load' && this._loaded) {
6172                     callback.call((scope || window), this);
6173                 }
6174             } catch (err) {
6175                 this._logger.error('id=' + this._id + ': ' + err);
6176             }
6177         },
6178 
6179         /**
6180          * Removes a handler from this object.
6181          * @param {String} notifierType
6182          *     The type of notifier to remove ('load', 'change', 'add', 'delete', 'error')
6183          * @param {Function} callback
6184          *     The function to remove.
6185          */
6186         removeHandler: function (notifierType, callback) {
6187             var notifier = null;
6188             try {
6189                 Utilities.validateHandler(callback);
6190 
6191                 notifier = this._getNotifierReference(notifierType);
6192 
6193                 if (typeof(callback) === "undefined")
6194                 {
6195                     // Remove all listeners for the type
6196                     notifier.reset();
6197                 }
6198                 else
6199                 {
6200                     // Remove the specified listener
6201                     finesse.utilities.Utilities.validateHandler(callback);
6202                     notifier.removeListener(callback);
6203                 }
6204             } catch (err) {
6205                 this._logger.error('id=' + this._id + ': ' + err);
6206             }
6207         },
6208 
6209         /**
6210          * Utility method gating any operations that require complete instantiation
6211          * @throws Error
6212          *     If this object was not fully instantiated yet
6213          * @returns {finesse.restservices.RestBase}
6214          *     This RestBase object to allow cascading
6215          */
6216         isLoaded: function () {
6217             if (!this._loaded) {
6218                 throw new Error("Cannot operate on object that is not fully instantiated, use onLoad/onLoadError handlers");
6219             }
6220             return this; // Allow cascading
6221         },
6222 
6223 
6224 
6225         /**
6226          * Force an update on this object. Since an asynchronous GET is performed,
6227          * it is necessary to have an onChange handler registered in order to be
6228          * notified when the response of this returns.
6229          * @param {Integer} retries
6230          *    The number or retry attempts to make.
6231          * @returns {Object}
6232          *     {
6233          *         abort: {function} Function that signifies the callback handler to NOT process the response of the asynchronous request
6234          *     }
6235          */
6236         refresh: function (retries) {
6237             var _this = this;
6238 
6239             if (this.explicitSubscription) {
6240                 this._subscribeNode({
6241                     success: function () {
6242                         //Disallow GETs if object doesn't support it.
6243                         if (!_this.supportsRequests) {
6244                             throw new Error("Object doesn't support request operations.");
6245                         }
6246 
6247                         _this._synchronize(retries);
6248 
6249                         return this; // Allow cascading
6250                     },
6251                     error: function (err) {
6252                         _this._errorNotifier.notifyListeners(err);
6253                     }
6254                 });
6255             } else {
6256                 //Disallow GETs if object doesn't support it.
6257                 if (!this.supportsRequests) {
6258                     throw new Error("Object doesn't support request operations.");
6259                 }
6260 
6261                 return this._synchronize(retries);
6262             }
6263         },
6264 
6265         /**
6266          * Utility method to validate against the known schema of this RestBase
6267          * @param {Object} obj
6268          *     The object to validate
6269          * @returns {Boolean}
6270          *     True if the object fits the schema of this object. This usually
6271          *     means all required keys or nested objects are present.
6272          *     False otherwise.
6273          * @private
6274          */
6275         _validate: function (obj) {
6276             var valid = (typeof obj === "object" && this.hasProperty(obj, this.getRestType()));
6277             if (!valid)
6278             {
6279                 this._logger.error(this.getRestType() + " failed validation! Does your JS REST class return the correct string from getRestType()?");
6280             }
6281             return valid;
6282         },
6283 
6284         /**
6285          * Utility method to fetch this RestBase from the server
6286          * @param {finesse.interfaces.RequestHandlers} handlers
6287          *     An object containing the handlers for the request
6288          * @returns {Object}
6289          *     {
6290          *         abort: {function} Function that signifies the callback handler to NOT process the response of the rest request
6291          *     }
6292          * @private
6293          */
6294         _doGET: function (handlers) {
6295             return this.restRequest(this.getRestUrl(), handlers);
6296         },
6297 
6298         /**
6299          * Common update event handler used by the pubsub callback closure.
6300          * Processes the update event then notifies listeners.
6301          * @param {Object} scope
6302          *     An object containing callbacks to handle the asynchronous get
6303          * @param {Object} update
6304          *     An object containing callbacks to handle the asynchronous get
6305          * @private
6306          */
6307         _updateEventHandler: function (scope, update) {
6308             if (scope._processUpdate(update)) {
6309                 switch (update.object.Update.event) {
6310                 case "POST":
6311                     scope._addNotifier.notifyListeners(scope);
6312                     break;
6313                 case "PUT":
6314                     scope._changeNotifier.notifyListeners(scope);
6315                     break;
6316                 case "DELETE":
6317                     scope._deleteNotifier.notifyListeners(scope);
6318                     break;
6319                 }
6320             }   
6321         },
6322 
6323         /**
6324          * Utility method to create a callback to be given to OpenAjax to invoke when a message
6325          * is published on the topic of our REST URL (also XEP-0060 node).
6326          * This needs to be its own defined method so that subclasses can have their own implementation.
6327          * @returns {Function} callback(update)
6328          *     The callback to be invoked when an update event is received. This callback will
6329          *     process the update and notify listeners.
6330          * @private
6331          */
6332         _createPubsubCallback: function () {
6333             var _this = this;
6334             return function (update) {
6335                 _this._updateEventHandler(_this, update);
6336             };
6337         },
6338 
6339         /**
6340          * Subscribe to pubsub infra using the REST URL as the topic name.
6341          * @param {finesse.interfaces.RequestHandlers} handlers
6342          *     An object containing the handlers for the request
6343          * @private
6344          */
6345         subscribe: function (callbacks) {
6346             // Only need to do a subscription to client pubsub. No need to trigger
6347             // a subscription on the Finesse server due to implicit subscribe (at
6348             // least for now).
6349             var _this = this,
6350             topic = Topics.getTopic(this.getXMPPNodePath()),
6351             handlers,
6352             successful = ClientServices.subscribe(topic, this._createPubsubCallback(), true);
6353 
6354             callbacks = callbacks || {};
6355 
6356             handlers = {
6357                 /** @private */
6358                 success: function () {
6359                     // Add item to the refresh list in ClientServices to refresh if
6360                     // we recover due to our resilient connection. However, do
6361                     // not add if doNotRefresh flag is set.
6362                     if (!_this.doNotRefresh) {
6363                         ClientServices.addToRefreshList(_this);
6364                     }
6365 
6366                     if (typeof callbacks.success === "function") {
6367                         callbacks.success();
6368                     }
6369                 },
6370                 /** @private */
6371                 error: function (err) {
6372                     if (successful) {
6373                         ClientServices.unsubscribe(topic);
6374                     }
6375 
6376                     if (typeof callbacks.error === "function") {
6377                         callbacks.error(err);
6378                     }
6379                 }
6380             };
6381 
6382             // Request a node subscription only if this object requires explicit subscriptions
6383             if (this.explicitSubscription === true) {
6384                 this._subscribeNode(handlers);
6385             } else {
6386                 if (successful) {
6387                     this._subid = "OpenAjaxOnly";
6388                     handlers.success();
6389                 } else {
6390                     handlers.error();
6391                 }
6392             }
6393 
6394             return this;
6395         },
6396 
6397         /**
6398          * Unsubscribe to pubsub infra using the REST URL as the topic name.
6399          * @param {finesse.interfaces.RequestHandlers} handlers
6400          *     An object containing the handlers for the request
6401          * @private
6402          */
6403         unsubscribe: function (callbacks) {
6404             // Only need to do a subscription to client pubsub. No need to trigger
6405             // a subscription on the Finesse server due to implicit subscribe (at
6406             // least for now).
6407             var _this = this,
6408             topic = Topics.getTopic(this.getRestUrl()),
6409             handlers;
6410 
6411             // no longer keep track of object to refresh on reconnect
6412             ClientServices.removeFromRefreshList(_this);
6413 
6414             callbacks = callbacks || {};
6415 
6416             handlers = {
6417                 /** @private */
6418                 success: function () {
6419                     if (typeof callbacks.success === "function") {
6420                         callbacks.success();
6421                     }
6422                 },
6423                 /** @private */
6424                 error: function (err) {
6425                     if (typeof callbacks.error === "function") {
6426                         callbacks.error(err);
6427                     }
6428                 }
6429             };
6430 
6431             if (this._subid) {
6432                 ClientServices.unsubscribe(topic);
6433                 // Request a node unsubscribe only if this object requires explicit subscriptions
6434                 if (this.explicitSubscription === true) {
6435                     this._unsubscribeNode(handlers);
6436                 } else {
6437                     this._subid = undefined;
6438                     handlers.success();
6439                 }
6440             } else {
6441                 handlers.success();
6442             }
6443 
6444             return this;
6445         },
6446 
6447         /**
6448          * Private utility to perform node subscribe requests for explicit subscriptions
6449          * @param {finesse.interfaces.RequestHandlers} handlers
6450          *     An object containing the handlers for the request
6451          * @private
6452          */
6453         _subscribeNode: function (callbacks) {
6454             var _this = this;
6455 
6456             // Protect against null dereferencing of callbacks allowing its (nonexistent) keys to be read as undefined
6457             callbacks = callbacks || {};
6458 
6459             ClientServices.subscribeNode(this.getXMPPNodePath(), function (subid, err) {
6460                 if (err) {
6461                     if (typeof callbacks.error === "function") {
6462                         callbacks.error(err);
6463                     }
6464                 } else {
6465                     // Store the subid on a successful subscribe
6466                     _this._subid = subid;
6467                     if (typeof callbacks.success === "function") {
6468                         callbacks.success();
6469                     }
6470                 }
6471             });
6472         },
6473 
6474         /**
6475          * Private utility to perform node unsubscribe requests for explicit subscriptions
6476          * @param {finesse.interfaces.RequestHandlers} handlers
6477          *     An object containing the handlers for the request
6478          * @private
6479          */
6480         _unsubscribeNode: function (callbacks) {
6481             var _this = this;
6482 
6483             // Protect against null dereferencing of callbacks allowing its (nonexistent) keys to be read as undefined
6484             callbacks = callbacks || {};
6485 
6486             ClientServices.unsubscribeNode(this.getXMPPNodePath(), this._subid, function (err) {
6487                 _this._subid = undefined;
6488                 if (err) {
6489                     if (typeof callbacks.error === "function") {
6490                         callbacks.error(err);
6491                     }
6492                 } else {
6493                     if (typeof callbacks.success === "function") {
6494                         callbacks.success();
6495                     }
6496                 }
6497             });
6498         },
6499 
6500         /**
6501          * Validate and store the object into the internal data store.
6502          * @param {Object} object
6503          *     The JavaScript object that should match of schema of this REST object.
6504          * @returns {Boolean}
6505          *     True if the object was validated and stored successfully.
6506          * @private
6507          */
6508         _processObject: function (object) {
6509             if (this._validate(object)) {
6510                 this._data = this.getProperty(object, this.getRestType()); // Should clone the object here?
6511 
6512                 // If loaded for the first time, call the load notifiers.
6513                 if (!this._loaded) {
6514                     this._loaded = true;
6515                     this._loadNotifier.notifyListeners(this);
6516                 }
6517 
6518                 return true;
6519             }
6520             return false;
6521         },
6522 
6523         /**
6524          * Normalize the object to mitigate the differences between the backend
6525          * and what this REST object should hold. For example, the backend sends
6526          * send an event with the root property name being lower case. In order to
6527          * match the GET, the property should be normalized to an upper case.
6528          * @param {Object} object
6529          *     The object which should be normalized.
6530          * @returns {Object}
6531          *     Return the normalized object.
6532          * @private
6533          */
6534         _normalize: function (object) {
6535             var
6536             restType = this.getRestType(),
6537             // Get the REST object name with first character being lower case.
6538             objRestType = restType.charAt(0).toLowerCase() + restType.slice(1);
6539 
6540             // Normalize payload to match REST object. The payload for an update
6541             // use a lower case object name as oppose to upper case. Only normalize
6542             // if necessary.
6543             if (!this.hasProperty(object, restType) && this.hasProperty(object, objRestType)) {
6544                 //Since the object is going to be modified, clone the object so that
6545                 //it doesn't affect others (due to OpenAjax publishing to other
6546                 //subscriber.
6547                 object = jQuery.extend(true, {}, object);
6548 
6549                 object[restType] = object[objRestType];
6550                 delete(object[objRestType]);
6551             }
6552             return object;
6553         },
6554 
6555         /**
6556          * Utility method to process the response of a successful get
6557          * @param {Object} rsp
6558          *     The response of a successful get
6559          * @returns {Boolean}
6560          *     True if the update was successfully processed (the response object
6561          *     passed the schema validation) and updated the internal data cache,
6562          *     false otherwise.
6563          * @private
6564          */
6565         _processResponse: function (rsp) {
6566             try {
6567                 if (this.keepRestResponse) {
6568                     this._restResponse = rsp.content;
6569                     this.restResponseStatus = rsp.status;
6570                 }
6571                 return this._processObject(rsp.object);
6572             }
6573             catch (err) {
6574                 this._logger.error(this.getRestType() + ': ' + err);
6575             }
6576             return false;
6577         },
6578 
6579         /**
6580          * Method that is called at the end of _processUpdate() which by default
6581          * will just delete the requestId-to-callbacks mapping but can be overridden.
6582          * @param  {String} requestId The requestId of the event
6583          */
6584         _postProcessUpdateStrategy: function (requestId) {
6585             //Clean up _pendingCallbacks now that we fired a callback corresponding to the received requestId.
6586             delete this._pendingCallbacks[requestId];
6587         },
6588 
6589         /**
6590          * Utility method to process the update notification.
6591          * @param {Object} update
6592          *     The payload of an update notification.
6593          * @returns {Boolean}
6594          *     True if the update was successfully processed (the update object
6595          *     passed the schema validation) and updated the internal data cache,
6596          *     false otherwise.
6597          * @private
6598          */
6599         _processUpdate: function (update) {
6600             try {
6601                 var updateObj, requestId, fakeResponse, receivedError;
6602 
6603                 // The backend will send the data object with a lower case. To be
6604                 // consistent with what should be represented in this object, the
6605                 // object name should be upper case. This will normalize the object.
6606                 updateObj = this._normalize(update.object.Update.data);
6607 
6608                 // Store the last event.
6609                 this._lastUpdate = update.object;
6610 
6611                 requestId = this._lastUpdate.Update ? this._lastUpdate.Update.requestId : undefined;
6612 
6613                 if (requestId && this._pendingCallbacks[requestId]) {
6614 
6615                     /*
6616                      * The passed success/error callbacks are expecting to be passed an AJAX response, so construct
6617                      * a simulated/"fake" AJAX response object from the information in the received event.
6618                      * The constructed object should conform to the contract for response objects specified
6619                      * in _createAjaxHandler().
6620                      */
6621                     fakeResponse = {};
6622 
6623                     //The contract says that rsp.content should contain the raw text of the response so we simulate that here.
6624                     //For some reason json2xml has trouble with the native update object, so we serialize a clone of it by
6625                     //doing a parse(stringify(update)).
6626                     fakeResponse.content = this._util.json2xml(gadgets.json.parse(gadgets.json.stringify(update)));
6627 
6628                     fakeResponse.object = {};
6629 
6630                     if (updateObj.apiErrors && updateObj.apiErrors.apiError) { //Error case
6631 
6632                         //TODO: The lowercase -> uppercase ApiErrors translation method below is undesirable, can it be improved?
6633                         receivedError = updateObj.apiErrors.apiError;
6634                         fakeResponse.object.ApiErrors = {};
6635                         fakeResponse.object.ApiErrors.ApiError = {};
6636                         fakeResponse.object.ApiErrors.ApiError.ErrorData = receivedError.errorData || undefined;
6637                         fakeResponse.object.ApiErrors.ApiError.ErrorMessage = receivedError.errorMessage || undefined;
6638                         fakeResponse.object.ApiErrors.ApiError.ErrorType = receivedError.errorType || undefined;
6639 
6640                         /*
6641                          * Since this is the error case, supply the error callback with a '400 BAD REQUEST' status code. We don't know what the real
6642                          * status code should be since the event we're constructing fakeResponse from doesn't include a status code.
6643                          * This is just to conform to the contract for the error callback in _createAjaxHandler().
6644                          **/
6645                         fakeResponse.status = 400;
6646 
6647                     } else { //Success case
6648 
6649                         fakeResponse.object = this._lastUpdate;
6650 
6651                         /*
6652                          * Since this is the success case, supply the success callback with a '200 OK' status code. We don't know what the real
6653                          * status code should be since the event we're constructing fakeResponse from doesn't include a status code.
6654                          * This is just to conform to the contract for the success callback in _createAjaxHandler().
6655                          **/
6656                         fakeResponse.status = 200;
6657                     }
6658 
6659                     try {
6660 
6661                         if (fakeResponse.object.ApiErrors && this._pendingCallbacks[requestId].error) {
6662                             this._pendingCallbacks[requestId].error(fakeResponse);
6663                         } 
6664                         // HTTP 202 is handled as a success, besides, we cannot infer that a non-error is a success.
6665                         /*else if (this._pendingCallbacks[requestId].success) {
6666                             this._pendingCallbacks[requestId].success(fakeResponse);
6667                         }*/
6668 
6669                     } catch (callbackErr) {
6670 
6671                         this._logger.error(this.getRestType() + ": Caught error while firing callback: " + callbackErr);
6672 
6673                     }
6674 
6675                     this._postProcessUpdateStrategy(requestId);
6676 
6677                 } 
6678 
6679                 return this._processObject(updateObj);
6680             }
6681             catch (err) {
6682                 this._logger.error(this.getRestType() + ': ' + err);
6683             }
6684             return false;
6685         },
6686 
6687         /**
6688          * Utility method to create ajax response handler closures around the
6689          * provided callbacks. Callbacks should be passed through from .ajax().
6690          * makeRequest is responsible for garbage collecting these closures.
6691          * @param {finesse.interfaces.RequestHandlers} handlers
6692          *     An object containing the handlers for the request
6693          * @returns {Object}
6694          *     {
6695          *         abort: {function} Function that signifies the callback handler to NOT process the response when the response returns
6696          *         callback: {function} Callback handler to be invoked when the response returns
6697          *     }
6698          * @private
6699          */
6700         _createAjaxHandler: function (options) {
6701             //We should not need to check this again since it has already been done in .restRequest()
6702             //options = options || {};
6703 
6704             //Flag to indicate whether or not to process the response
6705             var abort = false,
6706 
6707             //Get a reference to the parent User object
6708             _this = this;
6709 
6710             return {
6711 
6712                 abort: function () {
6713                     abort = true;
6714                 },
6715 
6716                 callback: function (rsp) {
6717 
6718                     if (abort) {
6719                         // Do not process the response
6720                         return;
6721                     }
6722 
6723                     var requestId, error = false, rspObj;
6724 
6725                     if (options.success || options.error) {
6726                         rspObj = {
6727                             status: rsp.rc,
6728                             content: rsp.text
6729                         };
6730 
6731                         if (!_this.doNotLog) {
6732                             _this._logger.log(_this.getRestType() + ": requestId='" + options.uuid + "', Returned with status=" + rspObj.status + ", content='" + rspObj.content + "'");
6733                         }
6734 
6735                         //Some responses may not have a body.
6736                         if (rsp.text && rsp.text.length > 0) {
6737                             try {
6738                                 rspObj.object = _this._util.xml2js(rsp.text);
6739                             } catch (e) {
6740                                 error = true;
6741                                 rspObj.error = {
6742                                     errorType: "parseError",
6743                                     errorMessage: "Could not serialize XML: " + e
6744                                 };
6745                             }
6746                         } else {
6747                             rspObj.object = {};
6748                         }
6749 
6750                         if (!error && rspObj.status >= 200 && rspObj.status < 300) {
6751                             if (options.success) {
6752                                 options.success(rspObj);
6753                             }
6754                         } else {
6755                             if (options.error) {
6756                                 options.error(rspObj);
6757                             }
6758                         }
6759 
6760                         /*
6761                          * If a synchronous error happened after a non-GET request (usually a validation error), we
6762                          * need to clean up the request's entry in _pendingCallbacks since no corresponding event
6763                          * will arrive later. The corresponding requestId should be present in the response headers.
6764                          *
6765                          * It appears Shindig changes the header keys to lower case, hence 'requestid' instead of
6766                          * 'requestId' below.
6767                          **/
6768                         if (rspObj.status !== 202 && rsp.headers && rsp.headers.requestid) {
6769                             requestId = rsp.headers.requestid[0];
6770                             if (_this._pendingCallbacks[requestId]) {
6771                                 delete _this._pendingCallbacks[requestId];
6772                             }
6773                         }
6774                     }
6775                 }
6776             };
6777         },
6778 
6779         /**
6780          * Utility method to make an asynchronous request
6781          * @param {String} url
6782          *     The unencoded URL to which the request is sent (will be encoded)
6783          * @param {Object} options
6784          *     An object containing additional options for the request.
6785          * @param {Object} options.content
6786          *     An object to send in the content body of the request. Will be
6787          *     serialized into XML before sending.
6788          * @param {String} options.method
6789          *     The type of request. Defaults to "GET" when none is specified.
6790          * @param {Function} options.success(rsp)
6791          *     A callback function to be invoked for a successful request.
6792          *     {
6793          *         status: {Number} The HTTP status code returned
6794          *         content: {String} Raw string of response
6795          *         object: {Object} Parsed object of response
6796          *     }
6797          * @param {Function} options.error(rsp)
6798          *     A callback function to be invoked for an unsuccessful request.
6799          *     {
6800          *         status: {Number} The HTTP status code returned
6801          *         content: {String} Raw string of response
6802          *         object: {Object} Parsed object of response
6803          *         error: {Object} Wrapped exception that was caught
6804          *         error.errorType: {String} Type of error that was caught
6805          *         error.errorMessage: {String} Message associated with error
6806          *     }
6807          * @returns {Object}
6808          *     {
6809          *         abort: {function} Function that signifies the callback handler to NOT process the response of this asynchronous request
6810          *     }
6811          * @private
6812         */
6813 			        restRequest : function(url, options) {
6814 			    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;
6815 			    // Protect against null dereferencing of options
6816 			    // allowing its (nonexistent) keys to be read as
6817 			    // undefined
6818 			    options = options || {};
6819 			    options.success = this._util
6820 				    .validateHandler(options.success);
6821 			    options.error = this._util
6822 				    .validateHandler(options.error);
6823 
6824 			    // true if this should be a GET request, false
6825 			    // otherwise
6826 			    if (!options.method || options.method === "GET") {
6827 				// Disable caching for GETs
6828 				if (url.indexOf("?") > -1) {
6829 				    url += "&";
6830 				} else {
6831 				    url += "?";
6832 				}
6833 				url += "nocache="
6834 					+ this._util.currentTimeMillis();
6835 			    } else {
6836 				/**
6837 				 * If not GET, generate a requestID and add it
6838 				 * to the headers, then wrap callbacks into an
6839 				 * object and store it in _pendingCallbacks. If
6840 				 * we receive a synchronous error response
6841 				 * instead of a 202 as expected, the AJAX
6842 				 * handler will clean up _pendingCallbacks.
6843 				 */
6844 				/*
6845 				 * TODO: Clean up _pendingCallbacks if an entry
6846 				 * persists after a certain amount of time has
6847 				 * passed. In the block below, can store the
6848 				 * current time (new Date().getTime()) alongside
6849 				 * the callbacks in the new _pendingCallbacks
6850 				 * entry. Then iterate through a copty of
6851 				 * _pendingCallbacks, deleting all entries
6852 				 * inside _pendingCallbacks that are older than
6853 				 * a certain threshold (2 minutes for example.)
6854 				 * This solves a potential memory leak issue if
6855 				 * we never receive an event for a given stored
6856 				 * requestId; we don't want to store unfired
6857 				 * callbacks forever.
6858 				 */
6859 				/** @private */
6860 				options.uuid = this._util.generateUUID();
6861 				// params[gadgets.io.RequestParameters.HEADERS].requestId
6862 				// = options.uuid;
6863 				// By default, Shindig strips nearly all of the
6864 				// response headers, but this parameter tells
6865 				// Shindig
6866 				// to send the headers through unmodified; we
6867 				// need to be able to read the 'requestId'
6868 				// header if we
6869 				// get a synchronous error as a result of a
6870 				// non-GET request. (See the bottom of
6871 				// _createAjaxHandler().)
6872 				// params[gadgets.io.RequestParameters.GET_FULL_HEADERS]
6873 				// = "true";
6874 				this._pendingCallbacks[options.uuid] = {};
6875 				this._pendingCallbacks[options.uuid].success = options.success;
6876 				this._pendingCallbacks[options.uuid].error = options.error;
6877 			    }
6878 			    encodedUrl = encodeURI(url)
6879 				    + (window.errorOnRestRequest ? "ERROR" : "");
6880 			    ajaxHandler = this._createAjaxHandler(options);
6881 			    // ClientServices.makeRequest(encodedUrl,
6882 			    // ajaxHandler.callback, params);
6883 			    mtype = options.method || 'GET';
6884 			    encodedUrl = scheme + "//" + host + ":" + port
6885 				    + encodedUrl;
6886 
6887 			    if (typeof options.content === "object"
6888 				    && typeof options.content !== "undefined") {
6889 				// Except get, all the other operations accepts
6890 				// application/xml only.
6891 				// @Consumes in all the webservices except GET
6892 				// are having this.
6893 				postdata = this._util.js2xml(options.content);
6894 				if (postdata !== null && postdata !== "") {
6895 				    contentTypeAX = "application/xml";
6896 				} else {
6897 				    // in desktop, reason code GET request was
6898 				    // called with empty object content
6899 				    // if not able to parse any content to post
6900 				    // data, then it is GET only
6901 				    contentTypeAX = "";
6902 				    dataTypeAX = "text";
6903 				}
6904 			    } else {
6905 				// No content type for GET operation, by default
6906 				// it will take text/plain || application/xml.
6907 				// for dataType - GET will result plain text,
6908 				// which we are taking as xml.
6909 				// Queried list of @Produces from code base, all
6910 				// the GET operations accepts text/plain OR
6911 				// application/xml and produces application/xml
6912 				contentTypeAX = "";
6913 				dataTypeAX = "text";
6914 			    }
6915 			    auth = this._util.getAuthHeaderString(this._config);
6916 
6917 			    if (!this.doNotLog) {
6918 				this._logger.log(this.getRestType()
6919 					+ ": requestId='" + options.uuid
6920 					+ "', Making REST request: method="
6921 					+ (options.method || "GET") + ", url='"
6922 					+ encodedUrl + "'");
6923 			    }
6924 			    $
6925 				    .ajax({
6926 					url : encodedUrl,
6927 					headers : this.extraHeaders || {},
6928 					beforeSend : function(xhr) {
6929 					    xhr.setRequestHeader(
6930 						    "Authorization", auth);
6931 					    xhr.setRequestHeader("locale",
6932 						    locale);
6933 					    if (options.uuid) {
6934 						xhr.setRequestHeader(
6935 							"requestId",
6936 							options.uuid);
6937 					    }
6938 					},
6939 					dataType : dataTypeAX, // xml or json?
6940 					contentType : contentTypeAX,
6941 					type : mtype,
6942 					data : postdata,
6943 					timeout : 5000,
6944 					success : function(response, status,
6945 						xhr) {
6946 					    var rspObj = {
6947 						rc : xhr.status,
6948 						text : response,
6949 						headers : {
6950 						    requestid : xhr
6951 							    .getResponseHeader("requestId")
6952 						}
6953 					    };
6954 					    ajaxHandler.callback(rspObj);
6955 					},
6956 					error : function(jqXHR, request, error) {
6957 					    var resObj = {
6958 						rc : jqXHR.status,
6959 						text : jqXHR.responseText,
6960 						headers : {
6961 						    requestid : jqXHR
6962 							    .getResponseHeader("requestId")
6963 						}
6964 					    };
6965 					    ajaxHandler.callback(resObj);
6966 					}
6967 				    });
6968 			    return {
6969 				abort : ajaxHandler.abort
6970 			    };
6971 			},
6972 
6973 
6974         /**
6975 	 * Retrieves a reference to a particular notifierType.
6976 	 * 
6977 	 * @param notifierType
6978 	 *                is a string which indicates the notifier to retrieve
6979 	 *                ('load', 'change', 'add', 'delete', 'error')
6980 	 * @return {Notifier}
6981 	 * @private
6982 	 */
6983         _getNotifierReference: function (notifierType) {
6984             var notifierReference = null;
6985             if (notifierType === 'load') {
6986                 notifierReference = this._loadNotifier;
6987             } else if (notifierType === 'change') {
6988                 notifierReference = this._changeNotifier;
6989             } else if (notifierType === 'add') {
6990                 notifierReference = this._addNotifier;
6991             } else if (notifierType === 'delete') {
6992                 notifierReference = this._deleteNotifier;
6993             } else if (notifierType === 'error') {
6994                 notifierReference = this._errorNotifier;
6995             } else {
6996                 throw new Error("_getNotifierReference(): Trying to get unknown notifier(notifierType=" + notifierType + ")");
6997             }
6998 
6999             return notifierReference;
7000         }
7001     });
7002 
7003     window.finesse = window.finesse || {};
7004     window.finesse.restservices = window.finesse.restservices || {};
7005     window.finesse.restservices.RestBase = RestBase;
7006     
7007     return RestBase;
7008 });
7009 
7010 /** The following comment is to prevent jslint errors about 
7011  * using variables before they are defined.
7012  */
7013 /*global finesse*/
7014 
7015 /**
7016  * JavaScript base object that all REST collection objects should
7017  * inherit from because it encapsulates and provides the common functionality
7018  * that all REST objects need.
7019  *
7020  * @requires finesse.clientservices.ClientServices
7021  * @requires Class
7022  * @requires finesse.FinesseBase
7023  * @requires finesse.restservices.RestBase
7024  */
7025 
7026 /**
7027  * @class
7028  * JavaScript representation of a REST collection object.
7029  *
7030  * @constructor
7031  * @param {Function} callbacks.onCollectionAdd(this)
7032  *     Callback to invoke upon successful item addition to the collection.
7033  * @param {Function} callbacks.onCollectionDelete(this)
7034  *     Callback to invoke upon successful item deletion from the collection.
7035  * @borrows finesse.restservices.RestBase as finesse.restservices.RestCollectionBase
7036  */
7037 /** @private */
7038 define('restservices/RestCollectionBase',[
7039     'restservices/RestBase',
7040     'utilities/Utilities',
7041     'restservices/Notifier'
7042 ],
7043 function (RestBase, Utilities, Notifier) {
7044     var RestCollectionBase = RestBase.extend(/** @lends finesse.restservices.RestCollectionBase.prototype */{
7045 
7046         /**
7047          * Boolean function that specifies whether the collection handles subscribing
7048          * and propagation of events for the individual REST object items the
7049          * collection holds. False by default. Subclasses should override if true.
7050          * @private
7051          */
7052         supportsRestItemSubscriptions: false,
7053 
7054         /**
7055          * Gets the constructor the individual items that make of the collection.
7056          * For example, a Dialogs collection object will hold a list of Dialog items.
7057          * @throws Error because subtype must implement.
7058          * @private
7059          */
7060         getRestItemClass: function () {
7061             throw new Error("getRestItemClass(): Not implemented in subtype.");
7062         },
7063 
7064         /**
7065          * Gets the REST type of the individual items that make of the collection.
7066          * For example, a Dialogs collection object will hold a list of Dialog items.
7067          * @throws Error because subtype must implement.
7068          * @private
7069          */
7070         getRestItemType: function () {
7071             throw new Error("getRestItemType(): Not implemented in subtype.");
7072         },
7073 
7074         /**
7075          * The base REST URL in which items this object contains can be referenced.
7076          * @return {String}
7077          *     The REST URI for items this object contains.
7078          * @private
7079          */
7080         getRestItemBaseUrl: function () {
7081             var
7082             restUrl = "/finesse/api";
7083 
7084             //Append the REST type.
7085             restUrl += "/" + this.getRestItemType();
7086 
7087             return restUrl;
7088         },
7089 
7090          /*
7091          * Creates a new object from the given data
7092          * @param data - data object
7093          * @private
7094          */
7095         _objectCreator: function (data) {
7096             var objectId = this._extractId(data),
7097             newRestObj = this._collection[objectId],
7098             _this = this;
7099 
7100             //Prevent duplicate entries into collection.
7101             if (!newRestObj) {
7102                 //Create a new REST object using the subtype defined by the
7103                 //overridden method.
7104                 newRestObj = new (this.getRestItemClass())({
7105                     doNotSubscribe: this.handlesItemSubscription,
7106                     doNotRefresh: this.handlesItemRefresh,
7107                     id: objectId,
7108                     data: data,
7109                     onLoad: function (newObj) {
7110                         //Normalize and add  REST object to collection datastore.
7111                         _this._collection[objectId] = newObj;
7112                         _this._collectionAddNotifier.notifyListeners(newObj);
7113                         _this.length += 1;
7114                     }
7115                 });
7116             }
7117             else {
7118                 //If entry already exist in collection, process the new event,
7119                 //and notify all change listeners since an existing object has
7120                 //change. This could happen in the case when the Finesse server
7121                 //cycles, and sends a snapshot of the user's calls.
7122                 newRestObj._processObject(data);
7123                 newRestObj._changeNotifier.notifyListeners(newRestObj);
7124             }
7125         },
7126 
7127         /*
7128          * Deletes and object and notifies its handlers
7129          * @param data - data object
7130          * @private
7131          */
7132         _objectDeleter: function (data) {
7133             var objectId = this._extractId(data),
7134             object = this._collection[objectId];
7135             if (object) {
7136                 //Even though this is a delete, let's make sure the object we are passing has got good data
7137                 object._processObject(data);
7138                 //Notify listeners and delete from internal datastore.
7139                 this._collectionDeleteNotifier.notifyListeners(object);
7140                 delete this._collection[objectId];
7141                 this.length -= 1;
7142             }
7143         },
7144 
7145          /**
7146           * Creates an anonymous function for notifiying error listeners of a particular object
7147           * data.
7148           * @param obj - the objects whose error listeners to notify
7149           * @returns {Function}
7150           *     Callback for notifying of errors
7151           * @private
7152           */
7153         _createErrorNotifier: function (obj) {
7154             return function (err) {
7155                 obj._errorNotifier.notifyListeners(err);
7156             };
7157         },
7158 
7159          /**
7160           * Replaces the collection with a refreshed list using the passed in
7161           * data.
7162           * @param data - data object (usually this._data)
7163           * @private
7164           */
7165          _buildRefreshedCollection: function (data) {
7166             var i, dataObject, object, objectId, dataArray, newIds = [], foundFlag;
7167             if (data && this.getProperty(data, this.getRestItemType()) !== null) {
7168                 dataArray = Utilities.getArray(this.getProperty(data, this.getRestItemType()));
7169             } else {
7170                 dataArray = [];
7171             }
7172 
7173             // iterate through each item in the new data and add to or update collection
7174             for (i = 0; i < dataArray.length; i += 1) {
7175                 dataObject = {};
7176                 dataObject[this.getRestItemType()] = dataArray[i];
7177                 objectId = this._extractId(dataObject);
7178 
7179                 this._objectCreator(dataObject);
7180                 newIds.push(objectId);
7181 
7182                 // resubscribe if the object requires an explicit subscription
7183                 object = this._collection[objectId];
7184                 if (this.handlesItemRefresh && object.explicitSubscription) {
7185                     object._subscribeNode({
7186                         error: this._createErrorNotifier(object)
7187                     });
7188                 }
7189             }
7190 
7191             // now clean up items (if any) that were removed
7192             for (objectId in this._collection) {
7193                 if (this._collection.hasOwnProperty(objectId)) {
7194                     foundFlag = false;
7195                     for (i = newIds.length - 1; i >= 0; i -= 1) {
7196                         if (newIds[i] === objectId) {
7197                             foundFlag = true;
7198                             break;
7199                         }
7200                     }
7201                     // did not find in updated list, so delete it
7202                     if (!foundFlag) {
7203                         this._objectDeleter({'data': this._collection[objectId]._data});
7204                     }
7205                 }
7206             }
7207         },
7208 
7209          /**
7210           * The actual refresh operation, refactored out so we don't have to repeat code
7211           * @private
7212           */
7213         _RESTRefresh: function () {
7214             var _this = this;
7215             this._doGET({
7216                 success: function(rsp) {
7217                     if (_this._processResponse(rsp)) {
7218                         _this._buildRefreshedCollection(_this._data);
7219                     } else {
7220                         _this._errorNotifier.notifyListeners(_this);
7221                     }
7222                 },
7223                 error: function(rsp) {
7224                     _this._errorNotifier.notifyListeners(rsp);
7225                 }
7226             });            
7227         },
7228 
7229         /**
7230          * Force an update on this object. Since an asynchronous GET is performed,
7231          * it is necessary to have an onChange handler registered in order to be
7232          * notified when the response of this returns.
7233          * @returns {finesse.restservices.RestBaseCollection}
7234          *     This RestBaseCollection object to allow cascading
7235          */
7236          refresh: function() {
7237             var _this = this, isLoaded = this._loaded;
7238 
7239             // resubscribe if the collection requires an explicit subscription
7240             if (this.explicitSubscription) {
7241                 this._subscribeNode({
7242                     success: function () {
7243                         _this._RESTRefresh();
7244                     },
7245                     error: function (err) {
7246                         _this._errorNotifier.notifyListeners(err);
7247                     }
7248                 });
7249             } else {
7250                 this._RESTRefresh();
7251             }
7252 
7253             return this; // Allow cascading
7254          },
7255 
7256         /**
7257          * @private
7258          * The _addHandlerCb and _deleteHandlerCb require that data be passed in the
7259          * format of an array of {(Object Type): object} objects. For example, a
7260          * queues object would return [{Queue: queue1}, {Queue: queue2}, ...].
7261          * @param skipOuterObject If {true} is passed in for this param, then the "data"
7262          *                           property is returned instead of an object with the
7263          *                           data appended.
7264          * @return {Array}
7265          */
7266         extractCollectionData: function (skipOuterObject) {
7267             var restObjs,
7268             obj,
7269             result = [],
7270             _this = this;
7271             
7272             if (this._data)
7273             {
7274                 restObjs = this._data[this.getRestItemType()];
7275     
7276                 if (restObjs)
7277                 {
7278                     // check if there are multiple objects to pass
7279                     if (!$.isArray(restObjs))
7280                     {
7281                         restObjs = [restObjs];
7282                     }
7283     
7284                     // if so, create an object for each and add to result array
7285                     $.each(restObjs, function (id, object) {
7286                         if (skipOuterObject === true)
7287                         {
7288                             obj = object;
7289                         }
7290                         else
7291                         {
7292                             obj = {};
7293                             obj[_this.getRestItemType()] = object;
7294                         }
7295                         result.push(obj);
7296                     });
7297                 }
7298                 
7299             }
7300             
7301             return result;
7302         },
7303 
7304         /**
7305          * For Finesse, collections are handled uniquely on a POST and
7306          * doesn't necessary follow REST conventions. A POST on a collection
7307          * doesn't mean that the collection has been created, it means that an
7308          * item has been added to the collection. This function will generate
7309          * a closure which will handle this logic appropriately.
7310          * @param {Object} scope
7311          *     The scope of where the callback should be invoked.
7312          * @private
7313          */
7314         _addHandlerCb: function (scope) {
7315             return function (restItem) {
7316                 var data = restItem.extractCollectionData();               
7317 
7318                 $.each(data, function (id, object) {
7319                     scope._objectCreator(object);
7320                 });
7321             };
7322         },
7323 
7324         /**
7325          * For Finesse, collections are handled uniquely on a DELETE and
7326          * doesn't necessary follow REST conventions. A DELETE on a collection
7327          * doesn't mean that the collection has been deleted, it means that an
7328          * item has been deleted from the collection. This function will generate
7329          * a closure which will handle this logic appropriately.
7330          * @param {Object} scope
7331          *     The scope of where the callback should be invoked.
7332          * @private
7333          */
7334         _deleteHandlerCb: function (scope) {
7335             return function (restItem) {
7336                 var data = restItem.extractCollectionData();
7337 
7338                 $.each(data, function (id, obj) {
7339                     scope._objectDeleter(obj);
7340                 });
7341             };
7342         },
7343 
7344         /**
7345          * Utility method to process the update notification for Rest Items
7346          * that are children of the collection whose events are published to
7347          * the collection's node.
7348          * @param {Object} update
7349          *     The payload of an update notification.
7350          * @returns {Boolean}
7351          *     True if the update was successfully processed (the update object
7352          *     passed the schema validation) and updated the internal data cache,
7353          *     false otherwise.
7354          * @private
7355          */
7356         _processRestItemUpdate: function (update) {
7357             var object, objectId, updateObj = update.object.Update;
7358 
7359             //Extract the ID from the source if the Update was an error.
7360             if (updateObj.data.apiErrors) {
7361                 objectId = Utilities.getId(updateObj.source);
7362             }
7363             //Otherwise extract from the data object itself.
7364             else {
7365                 objectId = this._extractId(updateObj.data);
7366             }
7367 
7368             object = this._collection[objectId];
7369             if (object) {
7370                 if (object._processUpdate(update)) {
7371                     switch (updateObj.event) {
7372                     case "POST":
7373                         object._addNotifier.notifyListeners(object);
7374                         break;
7375                     case "PUT":
7376                         object._changeNotifier.notifyListeners(object);
7377                         break;
7378                     case "DELETE":
7379                         object._deleteNotifier.notifyListeners(object);
7380                         break;
7381                     }
7382                 }
7383             }
7384         },
7385 
7386         /**
7387          * SUBCLASS IMPLEMENTATION (override):
7388          * For collections, this callback has the additional responsibility of passing events
7389          * of collection item updates to the item objects themselves. The collection needs to
7390          * do this because item updates are published to the collection's node.
7391          * @returns {Function}
7392          *     The callback to be invoked when an update event is received
7393          * @private
7394          */
7395         _createPubsubCallback: function () {
7396             var _this = this;
7397             return function (update) {
7398                 //If the source of the update is our REST URL, this means the collection itself is modified
7399                 if (update.object.Update.source === _this.getRestUrl()) {
7400                     _this._updateEventHandler(_this, update);
7401                 } else {
7402                     //Otherwise, it is safe to assume that if we got an event on our topic, it must be a
7403                     //rest item update of one of our children that was published on our node (OpenAjax topic)
7404                     _this._processRestItemUpdate(update);
7405                 }
7406             };
7407         },
7408 
7409         /**
7410          * @class
7411          * This is the base collection object. 
7412          *
7413          * @constructs
7414          * @augments finesse.restservices.RestBase
7415          * @see finesse.restservices.Contacts
7416          * @see finesse.restservices.Dialogs
7417          * @see finesse.restservices.PhoneBooks
7418          * @see finesse.restservices.Queues
7419          * @see finesse.restservices.WorkflowActions
7420          * @see finesse.restservices.Workflows
7421          * @see finesse.restservices.WrapUpReasons
7422          */
7423         _fakeConstuctor: function () {
7424             /* This is here to hide the real init constructor from the public docs */
7425         },
7426         
7427        /**
7428          * @private
7429          * @param {Object} options
7430          *     An object with the following properties:<ul>
7431          *         <li><b>id:</b> The id of the object being constructed</li>
7432          *         <li><b>onCollectionAdd(this): (optional)</b> when an object is added to this collection</li>
7433          *         <li><b>onCollectionDelete(this): (optional)</b> when an object is removed from this collection</li>
7434          *         <li><b>onLoad(this): (optional)</b> when the collection is successfully loaded from the server</li>
7435          *         <li><b>onChange(this): (optional)</b> when an update notification of the collection is received. 
7436          *         This does not include adding and deleting members of the collection</li>
7437          *         <li><b>onAdd(this): (optional)</b> when a notification that the collection is created is received</li>
7438          *         <li><b>onDelete(this): (optional)</b> when a notification that the collection is deleted is received</li>
7439          *         <li><b>onError(rsp): (optional)</b> if loading of the collection fails, invoked with the error response object:<ul>
7440          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
7441          *             <li><b>content:</b> {String} Raw string of response</li>
7442          *             <li><b>object:</b> {Object} Parsed object of response</li>
7443          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
7444          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
7445          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
7446          *             </ul></li>
7447          *         </ul></li>
7448          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
7449          **/
7450         init: function (options) {
7451 
7452             options = options || {};
7453             options.id = "";
7454 
7455             //Make internal datastore collection to hold a list of objects.
7456             this._collection = {};
7457             this.length = 0;
7458 
7459             //Collections will have additional callbacks that will be invoked when
7460             //an item has been added/deleted.
7461             this._collectionAddNotifier = new Notifier();
7462             this._collectionDeleteNotifier = new Notifier();
7463 
7464             //Initialize the base class.
7465             this._super(options);
7466 
7467             this.addHandler('collectionAdd', options.onCollectionAdd);
7468             this.addHandler('collectionDelete', options.onCollectionDelete);
7469 
7470             //For Finesse, collections are handled uniquely on a POST/DELETE and
7471             //doesn't necessary follow REST conventions. A POST on a collection
7472             //doesn't mean that the collection has been created, it means that an
7473             //item has been added to the collection. A DELETE means that an item has
7474             //been removed from the collection. Due to this, we are attaching
7475             //special callbacks to the add/delete that will handle this logic.
7476             this.addHandler("add", this._addHandlerCb(this));
7477             this.addHandler("delete", this._deleteHandlerCb(this));
7478         },
7479 
7480         /**
7481          * Returns the collection.
7482          * @returns {Object}
7483          *     The collection as an object
7484          */
7485         getCollection: function () {
7486             //TODO: is this safe? or should we instead return protected functions such as .each(function)?
7487             return this._collection;
7488         },
7489 
7490         /**
7491          * Utility method to build the internal collection data structure (object) based on provided data
7492          * @param {Object} data
7493          *     The data to build the internal collection from
7494          * @private
7495          */
7496         _buildCollection: function (data) {
7497             var i, object, objectId, dataArray;
7498             if (data && this.getProperty(data, this.getRestItemType()) !== null) {
7499                 dataArray = Utilities.getArray(this.getProperty(data, this.getRestItemType()));
7500                 for (i = 0; i < dataArray.length; i += 1) {
7501 
7502                     object = {};
7503                     object[this.getRestItemType()] = dataArray[i];
7504                     objectId = this._extractId(object);
7505                     this._collection[objectId] = new (this.getRestItemClass())({
7506                         doNotSubscribe: this.handlesItemSubscription,
7507                         doNotRefresh: this.handlesItemRefresh,
7508                         id: objectId,
7509                         data: object
7510                     });
7511                     this.length += 1;
7512                 }
7513             }
7514         },
7515 
7516         /**
7517          * Called to know whether to include an item in the _collection and _data. Return false to keep it, true to filter out (discard) it.
7518          * Override this in subclasses if you need only object with certain attribute values.
7519          * @param  {Object} item Item to test.
7520          * @return {Boolean} False to keep, true to filter out (discard);
7521          */
7522         _filterOutItem: function (item) {
7523             return false;
7524         },
7525     
7526         /**
7527          * Validate and store the object into the internal data store.
7528          * SUBCLASS IMPLEMENTATION (override):
7529          * Performs collection specific logic to _buildCollection internally based on provided data
7530          * @param {Object} object
7531          *     The JavaScript object that should match of schema of this REST object.
7532          * @returns {Boolean}
7533          *     True if the object was validated and stored successfully.
7534          * @private
7535          */
7536         _processObject: function (object) {
7537             var i,
7538                 restItemType = this.getRestItemType(),
7539                 items;
7540             if (this._validate(object)) {
7541                 this._data = this.getProperty(object, this.getRestType()); // Should clone the object here?
7542     
7543                 // If a subclass has overriden _filterOutItem then we'll need to run through the items and remove them
7544                 if (this._data)
7545                 {
7546                     items = this._data[restItemType];
7547     
7548                     if (typeof(items) !== "undefined")
7549                     {
7550                         if (typeof(items.length) === "undefined")
7551                         {
7552                             // Single object
7553                             if (this._filterOutItem(items))
7554                             {
7555                                 this._data[restItemType] = items = [];
7556                             }
7557                             
7558                         }
7559                         else
7560                         {
7561                             // filter out objects
7562                             for (i = items.length - 1; i !== -1; i = i - 1)
7563                             {
7564                                 if (this._filterOutItem(items[i]))
7565                                 {
7566                                     items.splice(i, 1);
7567                                 }
7568                             }
7569                         }
7570                     }
7571                 }
7572     
7573                 // If loaded for the first time, call the load notifiers.
7574                 if (!this._loaded) {
7575                     this._buildCollection(this._data);
7576                     this._loaded = true;
7577                     this._loadNotifier.notifyListeners(this);
7578                 }
7579                 
7580                 return true;
7581                 
7582             }
7583             return false;
7584         },
7585 
7586         /**
7587          * Retrieves a reference to a particular notifierType.
7588          * @param {String} notifierType
7589          *      Specifies the notifier to retrieve (load, change, error, add, delete)
7590          * @return {Notifier} The notifier object.
7591          */
7592         _getNotifierReference: function (notifierType) {
7593             var notifierReference;
7594 
7595             try {
7596                 //Use the base method to get references for load/change/error.
7597                 notifierReference = this._super(notifierType);
7598             } catch (err) {
7599                 //Check for add/delete
7600                 if (notifierType === "collectionAdd") {
7601                     notifierReference = this._collectionAddNotifier;
7602                 } else if (notifierType === "collectionDelete") {
7603                     notifierReference = this._collectionDeleteNotifier;
7604                 } else {
7605                     //Rethrow exception from base class.
7606                     throw err;
7607                 }
7608             }
7609             return notifierReference;
7610         }
7611     });
7612     
7613     window.finesse = window.finesse || {};
7614     window.finesse.restservices = window.finesse.restservices || {};
7615     window.finesse.restservices.RestCollectionBase = RestCollectionBase;
7616     
7617     return RestCollectionBase;
7618 });
7619 
7620 /**
7621  * JavaScript representation of the Finesse Dialog object.
7622  *
7623  * @requires finesse.clientservices.ClientServices
7624  * @requires Class
7625  * @requires finesse.FinesseBase
7626  * @requires finesse.restservices.RestBase
7627  */
7628 
7629 /** @private */
7630 define('restservices/DialogBase',[
7631         'restservices/RestBase',
7632         'utilities/Utilities'
7633     ],
7634     function (RestBase, Utilities) {
7635         var DialogBase = RestBase.extend(/** @lends finesse.restservices.DialogBase.prototype */{
7636 
7637             /**
7638              * @class
7639              * A DialogBase is an attempted connection between or among multiple participants,
7640              * for example, a regular phone call, a chat, or an email.
7641              *
7642              * This object is typically extended into individual
7643              * REST Objects (like Dialog, MediaDialog, etc...), and shouldn't be used directly.
7644              *
7645              * @augments finesse.restservices.RestBase
7646              * @constructs
7647              */
7648             _fakeConstuctor: function () {
7649                 /* This is here to hide the real init constructor from the public docs */
7650             },
7651 
7652             /**
7653              * @private
7654              *
7655              * @param {Object} options
7656              *     An object with the following properties:<ul>
7657              *         <li><b>id:</b> The id of the object being constructed</li>
7658              *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
7659              *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
7660              *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
7661              *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
7662              *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
7663              *             <li><b>status:</b> {Number} The HTTP status code returned</li>
7664              *             <li><b>content:</b> {String} Raw string of response</li>
7665              *             <li><b>object:</b> {Object} Parsed object of response</li>
7666              *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
7667              *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
7668              *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
7669              *             </ul></li>
7670              *         </ul></li>
7671              *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
7672              **/
7673             init: function (options) {
7674                 this._super(options);
7675             },
7676 
7677             /**
7678              * @private
7679              * Gets the REST class for the current object - this is the Dialog class.
7680              * @returns {Object} The Dialog class.
7681              */
7682             getRestClass: function () {
7683                 throw new Error("getRestClass(): Not implemented in subtype.");
7684             },
7685 
7686             /**
7687              * @private
7688              * The constant for agent device.
7689              */
7690             _agentDeviceType: "AGENT_DEVICE",
7691 
7692             /**
7693              * @private
7694              * Gets the REST type for the current object - this is a "Dialog".
7695              * @returns {String} The Dialog string.
7696              */
7697             getRestType: function () {
7698                 return "Dialog";
7699             },
7700 
7701             /**
7702              * @private
7703              * Override default to indicate that this object doesn't support making
7704              * requests.
7705              */
7706             supportsRequests: false,
7707 
7708             /**
7709              * @private
7710              * Override default to indicate that this object doesn't support subscriptions.
7711              */
7712             supportsSubscriptions: false,
7713 
7714 
7715             /**
7716              * Getter for the media type.
7717              * @returns {String} The media type.
7718              */
7719             getMediaType: function () {
7720                 this.isLoaded();
7721                 return this.getData().mediaType;
7722             },
7723 
7724             /**
7725              * @private
7726              * Getter for the uri.
7727              * @returns {String} The uri.
7728              */
7729             getDialogUri: function () {
7730                 this.isLoaded();
7731                 return this.getData().uri;
7732             },
7733 
7734             /**
7735              * Getter for the callType.
7736              * @deprecated Use getMediaProperties().callType instead.
7737              * @returns {String} The callType.
7738              */
7739             getCallType: function () {
7740                 this.isLoaded();
7741                 return this.getData().mediaProperties.callType;
7742             },
7743 
7744 
7745             /**
7746              * Getter for the Dialog state.
7747              * @returns {String} The Dialog state.
7748              */
7749             getState: function () {
7750                 this.isLoaded();
7751                 return this.getData().state;
7752             },
7753 
7754             /**
7755              * Retrieves a list of participants within the Dialog object.
7756              * @returns {Object} Array list of participants.
7757              * Participant entity properties are as follows:<ul>
7758              *     <li>state - The state of the Participant. 
7759              *     <li>stateCause - The state cause of the Participant.
7760              *     <li>mediaAddress - The media address of the Participant.
7761              *     <li>startTime - The start Time of the Participant.
7762              *     <li>stateChangeTime - The time when participant state has changed.
7763              *     <li>actions - These are the actions that a Participant can perform</ul>
7764              */
7765             getParticipants: function () {
7766                 this.isLoaded();
7767                 var participants = this.getData().participants.Participant;
7768                 //Due to the nature of the XML->JSO converter library, a single
7769                 //element in the XML array will be considered to an object instead of
7770                 //a real array. This will handle those cases to ensure that an array is
7771                 //always returned.
7772 
7773                 return Utilities.getArray(participants);
7774             },
7775 
7776             /**
7777              * This method retrieves the participant timer counters
7778              *
7779              * @param {String} participantExt Extension of participant.
7780              * @returns {Object} Array of Participants which contains properties :<ul>
7781              *     <li>state - The state of the Participant. 
7782              *     <li>startTime - The start Time of the Participant.
7783              *     <li>stateChangeTime - The time when participant state has changed.</ul>
7784              * 
7785              */
7786             getParticipantTimerCounters : function (participantExt) {
7787                 var part, participantTimerCounters = {}, idx, participants;
7788 
7789                 participants = this.getParticipants();
7790 
7791 
7792                 //Loop through all the participants and find the right participant (based on participantExt)
7793                 for(idx=0;idx<participants.length;idx=idx+1)
7794                 {
7795                     part = participants[idx];
7796 
7797                     if (part.mediaAddress === participantExt)
7798                     {
7799                         participantTimerCounters.startTime= part.startTime;
7800                         participantTimerCounters.stateChangeTime= part.stateChangeTime;
7801                         participantTimerCounters.state= part.state;
7802                         break;
7803                     }
7804                 }
7805 
7806                 return participantTimerCounters;
7807             },
7808 
7809 
7810             /**
7811              * Retrieves a list of media properties from the dialog object.
7812              * @returns {Object} Map of call variables; names mapped to values.
7813              * Variables may include the following:<ul>
7814              * <li>dialedNumber: The number dialed.
7815              * <li>callType: The type of call. Call types include:<ul>
7816              *     <li>ACD_IN
7817              *     <li>PREROUTE_ACD_IN
7818              *     <li>PREROUTE_DIRECT_AGENT
7819              *     <li>TRANSFER
7820              *     <li>OTHER_IN
7821              *     <li>OUT
7822              *     <li>AGENT_INSIDE
7823              *     <li>CONSULT
7824              *     <li>CONFERENCE
7825              *     <li>SUPERVISOR_MONITOR
7826              *     <li>OUTBOUND
7827              *     <li>OUTBOUND_PREVIEW</ul>
7828              * <li>DNIS: The DNIS provided. For routed calls, this is the route point.
7829              * <li>wrapUpReason: A description of the call.
7830              * <li>Call Variables, by name.  The name indicates whether it is a call variable or ECC variable.
7831              * Call variable names start with callVariable#, where # is 1-10. ECC variable names (both scalar and array) are prepended with "user".
7832              * ECC variable arrays include an index enclosed within square brackets located at the end of the ECC array name.
7833              * <li>The following call variables provide additional details about an Outbound Option call:<ul>
7834              *     <li>BACampaign
7835              *     <li>BAAccountNumber
7836              *     <li>BAResponse
7837              *     <li>BAStatus<ul>
7838              *         <li>PREDICTIVE_OUTBOUND: A predictive outbound call.
7839              *         <li>PROGRESSIVE_OUTBOUND: A progressive outbound call.
7840              *         <li>PREVIEW_OUTBOUND_RESERVATION: Agent is reserved for a preview outbound call.
7841              *         <li>PREVIEW_OUTBOUND: Agent is on a preview outbound call.</ul>
7842              *     <li>BADialedListID
7843              *     <li>BATimeZone
7844              *     <li>BABuddyName</ul></ul>
7845              *
7846              */
7847             getMediaProperties: function () {
7848                 var mpData, currentMediaPropertiesMap, thisMediaPropertiesJQuery;
7849 
7850                 this.isLoaded();
7851 
7852                 // We have to convert to jQuery object to do a proper compare
7853                 thisMediaPropertiesJQuery = jQuery(this.getData().mediaProperties);
7854 
7855                 if ((this._lastMediaPropertiesJQuery !== undefined)
7856                     && (this._lastMediaPropertiesMap !== undefined)
7857                     && (this._lastMediaPropertiesJQuery.is(thisMediaPropertiesJQuery))) {
7858 
7859                     return this._lastMediaPropertiesMap;
7860                 }
7861 
7862                 currentMediaPropertiesMap = {};
7863 
7864                 mpData = this.getData().mediaProperties;
7865 
7866                 if (mpData) {
7867                     if (mpData.callvariables && mpData.callvariables.CallVariable) {
7868                         if (mpData.callvariables.CallVariable.length === undefined) {
7869                             mpData.callvariables.CallVariable = [mpData.callvariables.CallVariable];
7870                         }
7871                         jQuery.each(mpData.callvariables.CallVariable, function (i, callVariable) {
7872                             currentMediaPropertiesMap[callVariable.name] = callVariable.value;
7873                         });
7874                     }
7875 
7876                     jQuery.each(mpData, function (key, value) {
7877                         if (key !== 'callvariables') {
7878                             currentMediaPropertiesMap[key] = value;
7879                         }
7880                     });
7881                 }
7882 
7883                 this._lastMediaPropertiesMap = currentMediaPropertiesMap;
7884                 this._lastMediaPropertiesJQuery = thisMediaPropertiesJQuery;
7885 
7886                 return this._lastMediaPropertiesMap;
7887             },
7888 
7889 
7890 
7891             /**
7892              * @private
7893              * Invoke a request to the server given a content body and handlers.
7894              *
7895              * @param {Object} contentBody
7896              *     A JS object containing the body of the action request.
7897              * @param {finesse.interfaces.RequestHandlers} handlers
7898              *     An object containing the handlers for the request
7899              */
7900             _makeRequest: function (contentBody, handlers) {
7901                 // Protect against null dereferencing of options allowing its
7902                 // (nonexistent) keys to be read as undefined
7903                 handlers = handlers || {};
7904 
7905                 this.restRequest(this.getRestUrl(), {
7906                     method: 'PUT',
7907                     success: handlers.success,
7908                     error: handlers.error,
7909                     content: contentBody
7910                 });
7911             }
7912 
7913         });
7914 
7915         window.finesse = window.finesse || {};
7916         window.finesse.restservices = window.finesse.restservices || {};
7917         window.finesse.restservices.DialogBase = DialogBase;
7918 
7919 
7920         return DialogBase;
7921     });
7922 
7923 /**
7924  * JavaScript representation of the Finesse Dialog object.
7925  *
7926  * @requires finesse.clientservices.ClientServices
7927  * @requires Class
7928  * @requires finesse.FinesseBase
7929  * @requires finesse.restservices.RestBase
7930  */
7931 
7932 /** @private */
7933 define('restservices/Dialog',[
7934     'restservices/DialogBase',
7935     'utilities/Utilities'
7936 ],
7937 function (DialogBase, Utilities) {
7938     var Dialog = DialogBase.extend(/** @lends finesse.restservices.Dialog.prototype */{
7939 
7940         /**
7941          * @class
7942          * A Dialog is an attempted connection between or among multiple participants,
7943          * for example, a regular phone call, a conference, or a silent monitor session.
7944          * 
7945          * @augments finesse.restservices.DialogBase
7946          * @constructs
7947          */
7948         _fakeConstuctor: function () {
7949             /* This is here to hide the real init constructor from the public docs */
7950         },
7951         
7952         /**
7953          * @private
7954          *
7955          * @param {Object} options
7956          *     An object with the following properties:<ul>
7957          *         <li><b>id:</b> The id of the object being constructed</li>
7958          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
7959          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
7960          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
7961          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
7962          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
7963          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
7964          *             <li><b>content:</b> {String} Raw string of response</li>
7965          *             <li><b>object:</b> {Object} Parsed object of response</li>
7966          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
7967          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
7968          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
7969          *             </ul></li>
7970          *         </ul></li>
7971          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
7972          **/
7973         init: function (options) {
7974             this._super(options);
7975         },
7976 
7977         /**
7978          * @private
7979          * Gets the REST class for the current object - this is the Dialog class.
7980          * @returns {Object} The Dialog class.
7981          */
7982         getRestClass: function () {
7983             return Dialog;
7984         },
7985 
7986         /**
7987          * The requestId reaper timeout in ms
7988          */
7989         REQUESTID_REAPER_TIMEOUT: 5000,
7990 
7991         /**
7992          * Getter for the from address.
7993          * @returns {String} The from address.
7994          */
7995         getFromAddress: function () {
7996             this.isLoaded();
7997             return this.getData().fromAddress;
7998         },
7999 
8000         /**
8001          * Getter for the to address.
8002          * @returns {String} The to address.
8003          */
8004         getToAddress: function () {
8005             this.isLoaded();
8006             return this.getData().toAddress;
8007         },
8008         
8009 
8010        /**
8011          * gets the participant timer counters 
8012          *
8013          * @param {String} participantExt Extension of participant.
8014          * @returns {Object} Array of Participants which contains properties :<ul>
8015          *     <li>state - The state of the Participant. 
8016          *     <li>startTime - The start Time of the Participant.
8017          *     <li>stateChangeTime - The time when participant state has changed.</ul>
8018          */
8019         getParticipantTimerCounters : function (participantExt) {
8020           var part, participantTimerCounters = {}, idx, participants;
8021           
8022           participants = this.getParticipants();
8023 
8024 
8025           //Loop through all the participants and find the right participant (based on participantExt)
8026           for(idx=0;idx<participants.length;idx=idx+1)
8027           {
8028             part = participants[idx];
8029             
8030             if (part.mediaAddress === participantExt)
8031             {
8032                 participantTimerCounters.startTime= part.startTime;
8033                 participantTimerCounters.stateChangeTime= part.stateChangeTime;
8034                 participantTimerCounters.state= part.state;
8035                 break;
8036             }
8037           }
8038           
8039           return participantTimerCounters;
8040         },
8041         
8042         /**
8043          * Determines the droppable participants.  A droppable participant is a participant that is an agent extension.   
8044          * (It is not a CTI Route Point, IVR Port, or the caller)
8045          * 
8046          * @param {String} filterExtension used to remove a single extension from the list
8047          * @returns {Object} Array of Participants that can be dropped.
8048          * Participant entity properties are as follows:<ul>
8049          *     <li>state - The state of the Participant. 
8050          *     <li>stateCause - The state cause of the Participant.
8051          *     <li>mediaAddress - The media address of the Participant.
8052          *     <li>startTime - The start Time of the Participant.
8053          *     <li>stateChangeTime - The time when participant state has changed.
8054          *     <li>actions - These are the actions that a Participant can perform</ul>
8055          */
8056         getDroppableParticipants: function (filterExtension) {
8057           this.isLoaded();
8058           var droppableParticipants = [], participants, index, idx, filterExtensionToRemove = "", callStateOk, part;
8059 
8060           participants = this.getParticipants();
8061 
8062           if (filterExtension)
8063           {
8064             filterExtensionToRemove = filterExtension;
8065           }
8066 
8067           //Loop through all the participants to remove non-agents & remove filterExtension
8068           //We could have removed filterExtension using splice, but we have to iterate through
8069           //the list anyway.
8070           for(idx=0;idx<participants.length;idx=idx+1)
8071           {
8072             part = participants[idx];
8073 
8074             //Skip the filterExtension
8075             if (part.mediaAddress !== filterExtensionToRemove)
8076             {
8077                 callStateOk = this._isParticipantStateDroppable(part);
8078 
8079                 //Remove non-agents & make sure callstate 
8080                 if (callStateOk === true && part.mediaAddressType === this._agentDeviceType)
8081                 {
8082                   droppableParticipants.push(part);
8083                 }
8084             }
8085         }
8086 
8087         return Utilities.getArray(droppableParticipants);
8088         },
8089 
8090         _isParticipantStateDroppable : function (part)
8091         {
8092           var isParticipantStateDroppable = false;
8093           if (part.state === Dialog.ParticipantStates.ACTIVE || part.state === Dialog.ParticipantStates.ACCEPTED || part.state === Dialog.ParticipantStates.HELD)
8094           {
8095             isParticipantStateDroppable = true;
8096           }
8097           
8098           return isParticipantStateDroppable;
8099         },
8100         
8101         /**
8102          * Is the participant droppable
8103          *
8104          * @param {String} participantExt Extension of participant.
8105          * @returns {Boolean} True is droppable.
8106          */
8107         isParticipantDroppable : function (participantExt) {
8108           var droppableParticipants = null, isDroppable = false, idx, part, callStateOk;
8109           
8110           droppableParticipants = this.getDroppableParticipants();
8111           
8112           if (droppableParticipants) 
8113           {
8114             for(idx=0;idx<droppableParticipants.length;idx=idx+1)
8115             {
8116               part = droppableParticipants[idx];
8117              
8118               if (part.mediaAddress === participantExt)
8119               {
8120                 callStateOk = this._isParticipantStateDroppable(part);
8121 
8122                 //Remove non-agents & make sure callstate 
8123                 if (callStateOk === true && part.mediaAddressType === this._agentDeviceType)
8124                 {
8125                   isDroppable = true;
8126                   break;
8127                 }
8128               }
8129             }
8130           }
8131           
8132           return isDroppable;
8133         },
8134 
8135         /**
8136          * Retrieves information about the currently scheduled callback, if any.
8137          * @returns {Object} If no callback has been set, will return undefined. If 
8138          * a callback has been set, it will return a map with one or more of the 
8139          * following entries, depending on what values have been set. 
8140          *    callbackTime   - the callback time, if it has been set.
8141          *    callbackNumber - the callback number, if it has been set.
8142          */
8143         getCallbackInfo: function() {
8144             this.isLoaded();
8145             return this.getData().scheduledCallbackInfo;
8146         },
8147 
8148         /**
8149          * Invoke a consult call out to a destination.
8150          *
8151          * @param {String} mediaAddress
8152          *     The media address of the user performing the consult call.
8153          * @param {String} toAddress
8154          *     The destination address of the consult call.
8155          * @param {finesse.interfaces.RequestHandlers} handlers
8156          *     An object containing the handlers for the request
8157          */
8158         makeConsultCall: function (mediaAddress, toAddress, handlers) {
8159             this.isLoaded();
8160             var contentBody = {};
8161             contentBody[this.getRestType()] = {
8162                 "targetMediaAddress": mediaAddress,
8163                 "toAddress": toAddress,
8164                 "requestedAction": Dialog.Actions.CONSULT_CALL
8165             };
8166             this._makeRequest(contentBody, handlers);
8167             return this; // Allow cascading
8168         },
8169         
8170         /**
8171          * Invoke a single step transfer request.
8172          *
8173          * @param {String} mediaAddress
8174          *     The media address of the user performing the single step transfer.
8175          * @param {String} toAddress
8176          *     The destination address of the single step transfer.
8177          * @param {finesse.interfaces.RequestHandlers} handlers
8178          *     An object containing the handlers for the request
8179          */
8180         initiateDirectTransfer: function (mediaAddress, toAddress, handlers) {
8181             this.isLoaded();
8182             var contentBody = {};
8183             contentBody[this.getRestType()] = {
8184                 "targetMediaAddress": mediaAddress,
8185                 "toAddress": toAddress,
8186                 "requestedAction": Dialog.Actions.TRANSFER_SST
8187             };
8188             this._makeRequest(contentBody, handlers);
8189             return this; // Allow cascading
8190         },
8191 
8192         /**
8193          * Update this dialog's wrap-up reason.
8194          *
8195          * @param {String} wrapUpReason
8196          *     The new wrap-up reason for this dialog
8197          * @param {finesse.interfaces.RequestHandlers} handlers
8198          *     An object containing the handlers for the request
8199          */
8200         updateWrapUpReason: function (wrapUpReason, options)
8201         {
8202             this.isLoaded();
8203             var mediaProperties =
8204             {
8205                 "wrapUpReason": wrapUpReason
8206             };
8207 
8208             options = options || {};
8209             options.content = {};
8210             options.content[this.getRestType()] =
8211             {
8212                 "mediaProperties": mediaProperties,
8213                 "requestedAction": Dialog.Actions.UPDATE_CALL_DATA
8214             };
8215             options.method = "PUT";
8216             this.restRequest(this.getRestUrl(), options);
8217 
8218             return this;
8219         },
8220 
8221         /**
8222          * Invoke a request to server based on the action given.
8223          * @param {String} mediaAddress
8224          *     The media address of the user performing the action.
8225          * @param {finesse.restservices.Dialog.Actions} action
8226          *     The action string indicating the action to invoke on dialog.
8227          * @param {finesse.interfaces.RequestHandlers} handlers
8228          *     An object containing the handlers for the request
8229          */
8230         requestAction: function (mediaAddress, action, handlers) {
8231             this.isLoaded();
8232             var contentBody = {};
8233             contentBody[this.getRestType()] = {
8234                 "targetMediaAddress": mediaAddress,
8235                 "requestedAction": action
8236             };
8237             this._makeRequest(contentBody, handlers);
8238             return this; // Allow cascading
8239         },
8240         
8241         /**
8242          * Wrapper around "requestAction" to request PARTICIPANT_DROP action.
8243          *
8244          * @param targetMediaAddress is the address to drop
8245          * @param {finesse.interfaces.RequestHandlers} handlers
8246          *     An object containing the handlers for the request
8247          */
8248         dropParticipant: function (targetMediaAddress, handlers) {
8249             this.requestAction(targetMediaAddress, Dialog.Actions.PARTICIPANT_DROP, handlers);
8250         },
8251         
8252         /**
8253          * Invoke a request to server to send DTMF digit tones.
8254          * @param {String} mediaAddress
8255          * @param {String} action
8256          *     The action string indicating the action to invoke on dialog.
8257          * @param {finesse.interfaces.RequestHandlers} handlers
8258          *     An object containing the handlers for the request
8259          */
8260         sendDTMFRequest: function (mediaAddress, handlers, digit) {
8261             this.isLoaded();
8262             var contentBody = {};
8263             contentBody[this.getRestType()] = {
8264                 "targetMediaAddress": mediaAddress,
8265                 "requestedAction": "SEND_DTMF",
8266                 "actionParams": {
8267                     "ActionParam": {
8268                         "name": "dtmfString",
8269                         "value": digit
8270                     }
8271                 }
8272             };
8273             this._makeRequest(contentBody, handlers);
8274             return this; // Allow cascading
8275         },
8276 
8277         /**
8278          * Invoke a request to server to set the time for a callback.
8279          * @param {String} mediaAddress
8280          * @param {String} callbackTime 
8281          *     The requested time for the callback, in YYYY-MM-DDTHH:MM format
8282          *     (ex: 2013-12-24T23:59)
8283          * @param {finesse.interfaces.RequestHandlers} handlers
8284          *     An object containing the handlers for the request
8285          */
8286         updateCallbackTime: function (mediaAddress, callbackTime, handlers) {
8287             this.isLoaded();
8288             var contentBody = {};
8289             contentBody[this.getRestType()] = {
8290                 "targetMediaAddress": mediaAddress,
8291                 "requestedAction": Dialog.Actions.UPDATE_SCHEDULED_CALLBACK,
8292                 "actionParams": {
8293                     "ActionParam": {
8294                         "name": "callbackTime",
8295                         "value": callbackTime
8296                     }
8297                 }
8298             };
8299             this._makeRequest(contentBody, handlers);
8300             return this; // Allow cascading
8301         },
8302 
8303         /**
8304          * Invoke a request to server to set the number for a callback.
8305          * @param {String} mediaAddress
8306          * @param {String} callbackNumber
8307          *     The requested number to call for the callback
8308          * @param {finesse.interfaces.RequestHandlers} handlers
8309          *     An object containing the handlers for the request
8310          */
8311         updateCallbackNumber: function (mediaAddress, callbackNumber, handlers) {
8312             this.isLoaded();
8313             var contentBody = {};
8314             contentBody[this.getRestType()] = {
8315                 "targetMediaAddress": mediaAddress,
8316                 "requestedAction": Dialog.Actions.UPDATE_SCHEDULED_CALLBACK,
8317                 "actionParams": {
8318                     "ActionParam": {
8319                         "name": "callbackNumber",
8320                         "value": callbackNumber
8321                     }
8322                 }
8323             };
8324             this._makeRequest(contentBody, handlers);
8325             return this; // Allow cascading
8326         },
8327 
8328         /**
8329          * Invoke a request to server to cancel a callback.
8330          * @param {String} mediaAddress
8331          * @param {finesse.interfaces.RequestHandlers} handlers
8332          *     An object containing the handlers for the request
8333          */
8334         cancelCallback: function (mediaAddress, handlers) {
8335             this.isLoaded();
8336             var contentBody = {};
8337             contentBody[this.getRestType()] = {
8338                 "targetMediaAddress": mediaAddress,
8339                 "requestedAction": Dialog.Actions.CANCEL_SCHEDULED_CALLBACK
8340             };
8341             this._makeRequest(contentBody, handlers);
8342             return this; // Allow cascading
8343         },
8344 
8345         /**
8346          * Invoke a request to server to reclassify the call type.
8347          * @param {String} mediaAddress
8348          *     The media address of the user performing the consult call.
8349          * @param {String} classification
8350          *     The classification to assign to the call. Valid values are "VOICE", "FAX",
8351          *     "ANS_MACHINE", "INVALID", "BUSY" (CCX only), and "DO_NOT_CALL".
8352          * @param {finesse.interfaces.RequestHandlers} handlers
8353          *     An object containing the handlers for the request
8354          */
8355         reclassifyCall: function (mediaAddress, classification, handlers) {
8356             this.isLoaded();
8357             var contentBody = {};
8358             contentBody[this.getRestType()] = {
8359                 "targetMediaAddress": mediaAddress,
8360                 "requestedAction": Dialog.Actions.RECLASSIFY,
8361                 "actionParams": {
8362                     "ActionParam": {
8363                         "name": "outboundClassification",
8364                         "value": classification
8365                     }
8366                 }
8367             };
8368             this._makeRequest(contentBody, handlers);
8369             return this; // Allow cascading
8370         },
8371 
8372         /**
8373          * Utility method to create a closure containing the requestId and the Dialogs object so 
8374          * that the _pendingCallbacks map can be manipulated when the timer task is executed.
8375          * @param  {String} requestId The requestId of the event
8376          * @return {Function}           The function to be executed by setTimeout
8377          */
8378         _createRequestIdReaper: function (requestId) {
8379             var that = this;
8380             return function () {
8381                 that._logger.log("Dialog: clearing the requestId-to-callbacks mapping for requestId=" + requestId);
8382                 delete that._pendingCallbacks[requestId];
8383             };
8384         },
8385 
8386         /**
8387          * Overriding implementation of the one in RestBase.js
8388          * This determines the strategy that Dialogs will take after processing an event that contains a requestId.
8389          * @param  {String} requestId The requestId of the event
8390          */
8391         _postProcessUpdateStrategy: function (requestId) {
8392             this._logger.log("Dialog: determining whether to set timeout for clearing requestId-to-callbacks mapping requestId=" + requestId);
8393             var callbacksObj = this._pendingCallbacks[requestId];
8394             if (callbacksObj && !callbacksObj.used) {
8395                 this._logger.log("Dialog: setting timeout for clearing requestId-to-callbacks mapping requestId=" + requestId);
8396                 setTimeout(this._createRequestIdReaper(requestId), this.REQUESTID_REAPER_TIMEOUT);
8397                 callbacksObj.used = true;
8398             }            
8399         }
8400 
8401     });
8402 
8403     Dialog.Actions = /** @lends finesse.restservices.Dialog.Actions.prototype */ {
8404             /**
8405              * Drops the Participant from the Dialog.
8406              */
8407             DROP: "DROP",
8408             /**
8409              * Answers a Dialog.
8410              */
8411             ANSWER: "ANSWER",
8412             /**
8413              * Holds the Dialog.
8414              */
8415             HOLD: "HOLD",
8416             /**
8417              * Barges into a Call Dialog.
8418              */
8419             BARGE_CALL: "BARGE_CALL",
8420             /**
8421              * Allow as Supervisor to Drop a Participant from the Dialog.
8422              */
8423             PARTICIPANT_DROP: "PARTICIPANT_DROP",
8424             /**
8425              * Makes a new Call Dialog.
8426              */
8427             MAKE_CALL: "MAKE_CALL",
8428             /**
8429              * Retrieves a Dialog that is on Hold.
8430              */
8431             RETRIEVE: "RETRIEVE",
8432             /**
8433              * Sets the time or number for a callback. Can be
8434              * either a new callback, or updating an existing one.
8435              */
8436             UPDATE_SCHEDULED_CALLBACK: "UPDATE_SCHEDULED_CALLBACK",
8437             /**
8438              * Cancels a callback.
8439              */
8440             CANCEL_SCHEDULED_CALLBACK: "CANCEL_SCHEDULED_CALLBACK",
8441             /**
8442              * Initiates a Consult Call.
8443              */
8444             CONSULT_CALL: "CONSULT_CALL",
8445             /**
8446              * Initiates a Transfer of a Dialog.
8447              */
8448             TRANSFER: "TRANSFER",
8449             /**
8450              * Initiates a Single-Step Transfer of a Dialog.
8451              */
8452             TRANSFER_SST: "TRANSFER_SST",
8453             /**
8454              * Initiates a Conference of a Dialog.
8455              */
8456             CONFERENCE: "CONFERENCE",
8457             /**
8458              * Changes classification for a call
8459              */
8460             RECLASSIFY: "RECLASSIFY", 
8461             /**
8462              * Updates data on a Call Dialog.
8463              */
8464             UPDATE_CALL_DATA: "UPDATE_CALL_DATA",
8465             /**
8466              * Initiates a Recording on a Call Dialog.
8467              */
8468             START_RECORDING : "START_RECORDING",
8469             /**
8470              * Sends DTMF (dialed digits) to a Call Dialog.
8471              */
8472             DTMF : "SEND_DTMF",            
8473             /**
8474              * Accepts a Dialog that is being Previewed.
8475              */
8476             ACCEPT: "ACCEPT",
8477             /**
8478              * Rejects a Dialog.
8479              */
8480             REJECT: "REJECT",
8481             /**
8482              * Closes a Dialog.
8483              */
8484             CLOSE : "CLOSE",
8485             /**
8486              * @class Set of action constants for a Dialog.  These should be used for
8487              * {@link finesse.restservices.Dialog#requestAction}.
8488              * @constructs
8489              */
8490             _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
8491         };
8492 
8493     Dialog.States = /** @lends finesse.restservices.Dialog.States.prototype */ {
8494        /**
8495          * Indicates that the call is ringing at a device.
8496          */
8497         ALERTING: "ALERTING",
8498         /**
8499          * Indicates that the phone is off the hook at a device.
8500          */
8501         INITIATING: "INITIATING",
8502         /**
8503          * Indicates that the dialog has a least one active participant.
8504          */
8505         ACTIVE: "ACTIVE",
8506         /**
8507          * Indicates that the dialog has no active participants.
8508          */
8509         DROPPED: "DROPPED",
8510         /**
8511          * Indicates that the phone is dialing at the device.
8512          */
8513         INITIATED: "INITIATED",
8514         /**
8515          * Indicates that the dialog has failed.
8516          * @see Dialog.ReasonStates
8517          */
8518         FAILED: "FAILED",
8519         /**
8520          * Indicates that the user has accepted an OUTBOUND_PREVIEW dialog.
8521          */
8522         ACCEPTED: "ACCEPTED",
8523         /**
8524          * @class Possible Dialog State constants.
8525          * The State flow of a typical in-bound Dialog is as follows: INITIATING, INITIATED, ALERTING, ACTIVE, DROPPED.
8526          * @constructs
8527          */
8528         _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
8529     };
8530 
8531     Dialog.ParticipantStates = /** @lends finesse.restservices.Dialog.ParticipantStates.prototype */ {
8532         /**
8533           * Indicates that an incoming call is ringing on the device.
8534           */
8535          ALERTING: "ALERTING",
8536          /**
8537           * Indicates that an outgoing call, not yet active, exists on the device.
8538           */
8539          INITIATING: "INITIATING",
8540          /**
8541           * Indicates that the participant is active on the call.
8542           */
8543          ACTIVE: "ACTIVE",
8544          /**
8545           * Indicates that the participant has dropped from the call.
8546           */
8547          DROPPED: "DROPPED",
8548          /**
8549           * Indicates that the participant has held their connection to the call.
8550           */
8551          HELD: "HELD",
8552          /**
8553           * Indicates that the phone is dialing at a device.
8554           */
8555          INITIATED: "INITIATED",
8556          /**
8557           * Indicates that the call failed.
8558           * @see Dialog.ReasonStates
8559           */
8560          FAILED: "FAILED",
8561          /**
8562           * Indicates that the participant is not in active state on the call, but is wrapping up after the participant has dropped from the call.
8563           */
8564          WRAP_UP: "WRAP_UP",
8565          /**
8566           * Indicates that the participant has accepted the dialog.  This state is applicable to OUTBOUND_PREVIEW dialogs.
8567           */
8568          ACCEPTED: "ACCEPTED",
8569          /**
8570           * @class Possible Dialog Participant State constants.
8571           * @constructs
8572           */
8573          _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
8574      };
8575 
8576     Dialog.ReasonStates = /** @lends finesse.restservices.Dialog.ReasonStates.prototype */ {
8577        /**
8578         * Dialog was Busy.  This will typically be for a Failed Dialog.
8579         */
8580         BUSY: "BUSY",
8581         /**
8582          * Dialog reached a Bad Destination.  This will typically be for a Failed Dialog.
8583          */
8584         BAD_DESTINATION: "BAD_DESTINATION",
8585         /**
8586          * All Other Reasons.  This will typically be for a Failed Dialog.
8587          */
8588         OTHER: "OTHER",
8589         /**
8590          * The Device Resource for the Dialog was not available.
8591          */
8592         DEVICE_RESOURCE_NOT_AVAILABLE : "DEVICE_RESOURCE_NOT_AVAILABLE",
8593         /**
8594          * @class Possible dialog state reasons code constants.
8595              * @constructs
8596              */
8597             _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
8598     };
8599 
8600     window.finesse = window.finesse || {};
8601     window.finesse.restservices = window.finesse.restservices || {};
8602     window.finesse.restservices.Dialog = Dialog;
8603     
8604     
8605     return Dialog;
8606 });
8607 
8608 /**
8609  * JavaScript representation of the Finesse Dialogs collection
8610  * object which contains a list of Dialog objects.
8611  *
8612  * @requires finesse.clientservices.ClientServices
8613  * @requires Class
8614  * @requires finesse.FinesseBase
8615  * @requires finesse.restservices.RestBase
8616  * @requires finesse.restservices.Dialog
8617  */
8618 /** @private */
8619 define('restservices/Dialogs',[
8620     'restservices/RestCollectionBase',
8621     'restservices/Dialog'
8622 ],
8623 function (RestCollectionBase, Dialog) {
8624     var Dialogs = RestCollectionBase.extend(/** @lends finesse.restservices.Dialogs.prototype */{
8625 
8626         /**
8627          * @class
8628          * JavaScript representation of a Dialogs collection object. Also exposes
8629          * methods to operate on the object against the server.
8630          * @augments finesse.restservices.RestCollectionBase
8631          * @constructs
8632          * @see finesse.restservices.Dialog
8633          * @example
8634          *  _dialogs = _user.getDialogs( {
8635          *      onCollectionAdd : _handleDialogAdd,
8636          *      onCollectionDelete : _handleDialogDelete,
8637          *      onLoad : _handleDialogsLoaded
8638          *  });
8639          *  
8640          * _dialogCollection = _dialogs.getCollection();
8641          * for (var dialogId in _dialogCollection) {
8642          *     if (_dialogCollection.hasOwnProperty(dialogId)) {
8643          *         _dialog = _dialogCollection[dialogId];
8644          *         etc...
8645          *     }
8646          * }
8647          */
8648         _fakeConstuctor: function () {
8649             /* This is here to hide the real init constructor from the public docs */
8650         },
8651         
8652         /**
8653          * @private
8654          * @param {Object} options
8655          *     An object with the following properties:<ul>
8656          *         <li><b>id:</b> The id of the object being constructed</li>
8657          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
8658          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
8659          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
8660          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
8661          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
8662          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
8663          *             <li><b>content:</b> {String} Raw string of response</li>
8664          *             <li><b>object:</b> {Object} Parsed object of response</li>
8665          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
8666          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
8667          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
8668          *             </ul></li>
8669          *         </ul></li>
8670          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
8671          **/
8672         init: function (options) {
8673             this._super(options);
8674         },
8675 
8676         /**
8677          * @private
8678          * Gets the REST class for the current object - this is the Dialogs class.
8679          */
8680         getRestClass: function () {
8681             return Dialogs;
8682         },
8683 
8684         /**
8685          * @private
8686          * Gets the REST class for the objects that make up the collection. - this
8687          * is the Dialog class.
8688          */
8689         getRestItemClass: function () {
8690             return Dialog;
8691         },
8692 
8693         /**
8694          * @private
8695          * Gets the REST type for the current object - this is a "Dialogs".
8696          */
8697         getRestType: function () {
8698             return "Dialogs";
8699         },
8700 
8701         /**
8702          * @private
8703          * Gets the REST type for the objects that make up the collection - this is "Dialogs".
8704          */
8705         getRestItemType: function () {
8706             return "Dialog";
8707         },
8708 
8709         /**
8710          * @private
8711          * Override default to indicates that the collection doesn't support making
8712          * requests.
8713          */
8714         supportsRequests: true,
8715 
8716         /**
8717          * @private
8718          * Override default to indicates that the collection subscribes to its objects.
8719          */
8720         supportsRestItemSubscriptions: true,
8721 
8722         /**
8723          * The requestId reaper timeout in ms
8724          */
8725         REQUESTID_REAPER_TIMEOUT: 5000,
8726 
8727         /**
8728          * @private
8729          * Create a new Dialog in this collection
8730          *
8731          * @param {String} toAddress
8732          *     The to address of the new Dialog
8733          * @param {String} fromAddress
8734          *     The from address of the new Dialog
8735          * @param {finesse.interfaces.RequestHandlers} handlers
8736          *     An object containing the (optional) handlers for the request.
8737          * @return {finesse.restservices.Dialogs}
8738          *     This Dialogs object, to allow cascading.
8739          */
8740         createNewCallDialog: function (toAddress, fromAddress, handlers)
8741         {
8742             var contentBody = {};
8743             contentBody[this.getRestItemType()] = {
8744                 "requestedAction": "MAKE_CALL",
8745                 "toAddress": toAddress,
8746                 "fromAddress": fromAddress
8747             };
8748 
8749             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
8750             handlers = handlers || {};
8751 
8752             this.restRequest(this.getRestUrl(), {
8753                 method: 'POST',
8754                 success: handlers.success,
8755                 error: handlers.error,
8756                 content: contentBody
8757             });
8758             return this; // Allow cascading
8759         },
8760 
8761         /**
8762          * @private
8763          * Create a new Dialog in this collection as a result of a requested action
8764          *
8765          * @param {String} toAddress
8766          *     The to address of the new Dialog
8767          * @param {String} fromAddress
8768          *     The from address of the new Dialog
8769          * @param {finesse.restservices.Dialog.Actions} actionType
8770          *     The associated action to request for creating this new dialog
8771          * @param {finesse.interfaces.RequestHandlers} handlers
8772          *     An object containing the (optional) handlers for the request.
8773          * @return {finesse.restservices.Dialogs}
8774          *     This Dialogs object, to allow cascading.
8775          */
8776         createNewSuperviseCallDialog: function (toAddress, fromAddress, actionType, handlers)
8777         {
8778             var contentBody = {};
8779             this._isLoaded = true;
8780 
8781             contentBody[this.getRestItemType()] = {
8782                 "requestedAction": actionType,
8783                 "toAddress": toAddress,
8784                 "fromAddress": fromAddress
8785             };
8786 
8787             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
8788             handlers = handlers || {};
8789 
8790             this.restRequest(this.getRestUrl(), {
8791                 method: 'POST',
8792                 success: handlers.success,
8793                 error: handlers.error,
8794                 content: contentBody
8795             });
8796             return this; // Allow cascading
8797         },
8798         
8799         /**
8800          * @private
8801          * Create a new Dialog in this collection as a result of a requested action
8802          * @param {String} fromAddress
8803          *     The from address of the new Dialog
8804          * @param {String} toAddress
8805          *     The to address of the new Dialog
8806          * @param {finesse.restservices.Dialog.Actions} actionType
8807          *     The associated action to request for creating this new dialog
8808          * @param {String} dialogUri
8809          *     The associated uri of SUPERVISOR_MONITOR call
8810          * @param {finesse.interfaces.RequestHandlers} handlers
8811          *     An object containing the (optional) handlers for the request.
8812          * @return {finesse.restservices.Dialogs}
8813          *     This Dialogs object, to allow cascading.
8814          */
8815         createNewBargeCall: function (fromAddress, toAddress, actionType, dialogURI, handlers) {
8816             this.isLoaded();
8817          
8818             var contentBody = {};
8819             contentBody[this.getRestItemType()] = {
8820                 "fromAddress": fromAddress,
8821                 "toAddress": toAddress,
8822                 "requestedAction": actionType,
8823                 "associatedDialogUri": dialogURI
8824                 
8825             };
8826             // (nonexistent) keys to be read as undefined
8827             handlers = handlers || {};  
8828             this.restRequest(this.getRestUrl(), {
8829                 method: 'POST',
8830                 success: handlers.success,
8831                 error: handlers.error,
8832                 content: contentBody
8833             });
8834             return this; // Allow cascading
8835         },
8836 
8837         /**
8838          * Utility method to get the number of dialogs in this collection.
8839          * 'excludeSilentMonitor' flag is provided as an option to exclude calls with type
8840          * 'SUPERVISOR_MONITOR' from the count.
8841          * @param  {Boolean} excludeSilentMonitor If true, calls with type of 'SUPERVISOR_MONITOR' will be excluded from the count.
8842          * @return {Number} The number of dialogs in this collection.
8843          */
8844         getDialogCount: function (excludeSilentMonitor) {
8845             this.isLoaded();
8846 
8847             var dialogId, count = 0;
8848             if (excludeSilentMonitor) {
8849                 for (dialogId in this._collection) {
8850                     if (this._collection.hasOwnProperty(dialogId)) {
8851                         if (this._collection[dialogId].getCallType() !== 'SUPERVISOR_MONITOR') {
8852                             count += 1;
8853                         }
8854                     }
8855                 }
8856 
8857                 return count;
8858             } else {
8859                 return this.length;
8860             }        
8861         },
8862 
8863         /**
8864          * Utility method to create a closure containing the requestId and the Dialogs object so 
8865          * that the _pendingCallbacks map can be manipulated when the timer task is executed.
8866          * @param  {String} requestId The requestId of the event
8867          * @return {Function}           The function to be executed by setTimeout
8868          */
8869         _createRequestIdReaper: function (requestId) {
8870             var that = this;
8871             return function () {
8872                 that._logger.log("Dialogs: clearing the requestId-to-callbacks mapping for requestId=" + requestId);
8873                 delete that._pendingCallbacks[requestId];
8874             };
8875         },
8876 
8877         /**
8878          * Overriding implementation of the one in RestBase.js
8879          * This determines the strategy that Dialogs will take after processing an event that contains a requestId.
8880          * @param  {String} requestId The requestId of the event
8881          */
8882         _postProcessUpdateStrategy: function (requestId) {
8883             this._logger.log("Dialogs: determining whether to set timeout for clearing requestId-to-callbacks mapping requestId=" + requestId);
8884             var callbacksObj = this._pendingCallbacks[requestId];
8885             if (callbacksObj && !callbacksObj.used) {
8886                 this._logger.log("Dialogs: setting timeout for clearing requestId-to-callbacks mapping requestId=" + requestId);
8887                 setTimeout(this._createRequestIdReaper(requestId), this.REQUESTID_REAPER_TIMEOUT);
8888                 callbacksObj.used = true;
8889             }            
8890         }
8891 
8892     });
8893     
8894     window.finesse = window.finesse || {};
8895     window.finesse.restservices = window.finesse.restservices || {};
8896     window.finesse.restservices.Dialogs = Dialogs;
8897     
8898     return Dialogs;
8899 });
8900 
8901 /**
8902  * JavaScript representation of the Finesse ClientLog object
8903  *
8904  * @requires finesse.clientservices.ClientServices
8905  * @requires Class
8906  * @requires finesse.FinesseBase
8907  * @requires finesse.restservices.RestBase
8908  */
8909 
8910 /** The following comment is to prevent jslint errors about 
8911  * using variables before they are defined.
8912  */
8913 /** @private */
8914 /*global finesse*/
8915 
8916 define('restservices/ClientLog',["restservices/RestBase"], function (RestBase) {
8917     
8918     var ClientLog = RestBase.extend(/** @lends finesse.restservices.ClientLog.prototype */{    
8919         /**
8920          * @private
8921          * Returns whether this object supports transport logs
8922          */
8923         doNotLog : true,
8924         
8925         explicitSubscription : true,
8926         
8927         /**
8928          * @class
8929          * @private
8930          * JavaScript representation of a ClientLog object. Also exposes methods to operate
8931          * on the object against the server.
8932          *
8933          * @param {Object} options
8934          *     An object with the following properties:<ul>
8935          *         <li><b>id:</b> The id of the object being constructed</li>
8936          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
8937          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
8938          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
8939          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
8940          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
8941          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
8942          *             <li><b>content:</b> {String} Raw string of response</li>
8943          *             <li><b>object:</b> {Object} Parsed object of response</li>
8944          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
8945          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
8946          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
8947          *             </ul></li>
8948          *         </ul></li>
8949          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
8950          * @constructs
8951          * @augments finesse.restservices.RestBase
8952          **/
8953         init: function (options) {
8954             this._super({
8955                 id: "", 
8956                 data: {clientLog : null},
8957                 onAdd: options.onAdd,
8958                 onChange: options.onChange,
8959                 onLoad: options.onLoad,
8960                 onError: options.onError,
8961                 parentObj: options.parentObj
8962                 });
8963         },
8964 
8965         /**
8966          * @private
8967          * Gets the REST class for the current object - this is the ClientLog object.
8968          */
8969         getRestClass: function () {
8970             return ClientLog;
8971         },
8972 
8973         /**
8974          * @private
8975          * Gets the REST type for the current object - this is a "ClientLog".
8976          */
8977         getRestType: function ()
8978         {
8979             return "ClientLog";
8980         },
8981         
8982         /**
8983          * @private
8984          * Gets the node path for the current object
8985          * @returns {String} The node path
8986          */
8987         getXMPPNodePath: function () {
8988             return this.getRestUrl();
8989         },
8990 
8991         /**
8992          * @private
8993          * Utility method to fetch this object from the server, however we
8994          * override it for ClientLog to not do anything because GET is not supported
8995          * for ClientLog object.
8996          */
8997         _doGET: function (handlers) {
8998             return;
8999         },
9000            
9001         /**
9002          * @private
9003          * Invoke a request to the server given a content body and handlers.
9004          *
9005          * @param {Object} contentBody
9006          *     A JS object containing the body of the action request.
9007          * @param {Object} handlers
9008          *     An object containing the following (optional) handlers for the request:<ul>
9009          *         <li><b>success(rsp):</b> A callback function for a successful request to be invoked with the following
9010          *         response object as its only parameter:<ul>
9011          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
9012          *             <li><b>content:</b> {String} Raw string of response</li>
9013          *             <li><b>object:</b> {Object} Parsed object of response</li></ul>
9014          *         <li>A error callback function for an unsuccessful request to be invoked with the
9015          *         error response object as its only parameter:<ul>
9016          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
9017          *             <li><b>content:</b> {String} Raw string of response</li>
9018          *             <li><b>object:</b> {Object} Parsed object of response (HTTP errors)</li>
9019          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
9020          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
9021          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
9022          *             </ul></li>
9023          *         </ul>
9024          */
9025         sendLogs: function (contentBody, handlers) {
9026             // Protect against null dereferencing of options allowing its
9027             // (nonexistent) keys to be read as undefined
9028             handlers = handlers || {};
9029 
9030             this.restRequest(this.getRestUrl(), {
9031                 method: 'POST',
9032                 //success: handlers.success,
9033                 error: handlers.error,
9034                 content: contentBody
9035             });
9036         }
9037     });
9038     
9039     window.finesse = window.finesse || {};
9040     window.finesse.restservices = window.finesse.restservices || {};
9041     window.finesse.restservices.ClientLog = ClientLog;
9042     
9043     return ClientLog;
9044 });
9045 
9046 /**
9047  * JavaScript representation of the Finesse Queue object
9048  * @requires finesse.clientservices.ClientServices
9049  * @requires Class
9050  * @requires finesse.FinesseBase
9051  * @requires finesse.restservices.RestBase
9052  */
9053 
9054 /** @private */
9055 define('restservices/Queue',[
9056     'restservices/RestBase',
9057     'utilities/Utilities'
9058 ],
9059 function (RestBase, Utilities) {
9060     var Queue = RestBase.extend(/** @lends finesse.restservices.Queue.prototype */{
9061 
9062         /**
9063          * @class
9064          * A Queue is a list of Contacts available to a User for quick dial.
9065          * 
9066          * @augments finesse.restservices.RestBase
9067          * @constructs
9068          */
9069         _fakeConstuctor: function () {
9070             /* This is here to hide the real init constructor from the public docs */
9071         },
9072         
9073 		/**
9074 		 * @private
9075 		 * JavaScript representation of a Queue object. Also exposes methods to operate
9076 		 * on the object against the server.
9077 		 *
9078 		 * @constructor
9079 		 * @param {String} id
9080 		 *     Not required...
9081 		 * @param {Object} callbacks
9082 		 *     An object containing callbacks for instantiation and runtime
9083 		 * @param {Function} callbacks.onLoad(this)
9084 		 *     Callback to invoke upon successful instantiation
9085 		 * @param {Function} callbacks.onLoadError(rsp)
9086 		 *     Callback to invoke on instantiation REST request error
9087 		 *     as passed by finesse.clientservices.ClientServices.ajax()
9088 		 *     {
9089 		 *         status: {Number} The HTTP status code returned
9090 		 *         content: {String} Raw string of response
9091 		 *         object: {Object} Parsed object of response
9092 		 *         error: {Object} Wrapped exception that was caught
9093 		 *         error.errorType: {String} Type of error that was caught
9094 		 *         error.errorMessage: {String} Message associated with error
9095 		 *     }
9096 		 * @param {Function} callbacks.onChange(this)
9097 		 *     Callback to invoke upon successful update
9098 		 * @param {Function} callbacks.onError(rsp)
9099 		 *     Callback to invoke on update error (refresh or event)
9100 		 *     as passed by finesse.clientservices.ClientServices.ajax()
9101 		 *     {
9102 		 *         status: {Number} The HTTP status code returned
9103 		 *         content: {String} Raw string of response
9104 		 *         object: {Object} Parsed object of response
9105 		 *         error: {Object} Wrapped exception that was caught
9106 		 *         error.errorType: {String} Type of error that was caught
9107 		 *         error.errorMessage: {String} Message associated with error
9108 		 *     }
9109 		 *  
9110 		 */
9111         init: function (id, callbacks, restObj) {
9112             this._super(id, callbacks, restObj);
9113         },
9114 
9115         /**
9116          * @private
9117          * Gets the REST class for the current object - this is the Queue object.
9118          */
9119         getRestClass: function () {
9120             return Queue;
9121         },
9122 
9123         /**
9124          * @private
9125          * Gets the REST type for the current object - this is a "Queue".
9126          */
9127         getRestType: function () {
9128             return "Queue";
9129         },
9130 
9131         /**
9132          * @private
9133          * Returns whether this object supports subscriptions
9134          */
9135         supportsSubscriptions: function () {
9136             return true;
9137         },
9138         
9139         /**
9140          * @private
9141          * Specifies whether this object's subscriptions need to be explicitly requested
9142          */
9143         explicitSubscription: true,
9144         
9145         /**
9146          * @private
9147          * Gets the node path for the current object - this is the team Users node
9148          * @returns {String} The node path
9149          */
9150         getXMPPNodePath: function () {
9151             return this.getRestUrl();
9152         },
9153         
9154         /**
9155          * Getter for the queue id
9156          * @returns {String}
9157          *     The id of the Queue
9158          */
9159         getId: function () {
9160             this.isLoaded();
9161             return this._id;
9162         },
9163         
9164         /**
9165          * Getter for the queue name
9166          * @returns {String}
9167          *      The name of the Queue
9168          */
9169         getName: function () {
9170             this.isLoaded();
9171             return this.getData().name;
9172         },
9173         
9174         /**
9175          * Getter for the queue statistics.
9176          * Supported statistics include:<br>
9177          *  - callsInQueue<br>
9178          *  - startTimeOfLongestCallInQueue<br>
9179          *  <br>
9180          *  These statistics can be accessed via dot notation:<br>
9181          *  i.e.: getStatistics().callsInQueue
9182          * @returns {Object}
9183          *      The Object with different statistics as properties.
9184          */
9185         getStatistics: function () {
9186             this.isLoaded();
9187             return this.getData().statistics;       
9188         },
9189 
9190         /**
9191          * Parses a uriString to retrieve the id portion
9192          * @param {String} uriString
9193          * @return {String} id
9194          */
9195         _parseIdFromUriString : function (uriString) {
9196             return Utilities.getId(uriString);
9197         }
9198 
9199     });
9200 	
9201 	window.finesse = window.finesse || {};
9202     window.finesse.restservices = window.finesse.restservices || {};
9203     window.finesse.restservices.Queue = Queue;
9204     
9205     return Queue;
9206 });
9207 
9208 /**
9209  * JavaScript representation of the Finesse Queues collection
9210  * object which contains a list of Queue objects.
9211  * @requires finesse.clientservices.ClientServices
9212  * @requires Class
9213  * @requires finesse.FinesseBase
9214  * @requires finesse.restservices.RestBase
9215  * @requires finesse.restservices.RestCollectionBase
9216  */
9217 
9218 /**
9219  * @class
9220  * JavaScript representation of a Queues collection object.
9221  *
9222  * @constructor
9223  * @borrows finesse.restservices.RestCollectionBase as finesse.restservices.Queues
9224  */
9225 
9226 /** @private */
9227 define('restservices/Queues',[
9228     'restservices/RestCollectionBase',
9229     'restservices/Queue'
9230 ],
9231 function (RestCollectionBase, Queue) {
9232     var Queues = RestCollectionBase.extend(/** @lends finesse.restservices.Queues.prototype */{
9233 
9234         /**
9235          * @class
9236          * JavaScript representation of a Queues collection object. 
9237          * @augments finesse.restservices.RestCollectionBase
9238          * @constructs
9239          * @see finesse.restservices.Queue
9240          * @example
9241          *  _queues = _user.getQueues( {
9242          *      onCollectionAdd : _handleQueueAdd,
9243          *      onCollectionDelete : _handleQueueDelete,
9244          *      onLoad : _handleQueuesLoaded
9245          *  });
9246          *  
9247          * _queueCollection = _queues.getCollection();
9248          * for (var queueId in _queueCollection) {
9249          *     if (_queueCollection.hasOwnProperty(queueId)) {
9250          *         _queue = _queueCollection[queueId];
9251          *         etc...
9252          *     }
9253          * }
9254          */
9255         _fakeConstuctor: function () {
9256             /* This is here to hide the real init constructor from the public docs */
9257         },
9258 	    
9259          /**
9260          * @private
9261          * JavaScript representation of a Queues object. Also exposes
9262          * methods to operate on the object against the server.
9263          *
9264          * @param {Object} options
9265          *     An object with the following properties:<ul>
9266          *         <li><b>id:</b> The id of the object being constructed</li>
9267          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
9268          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
9269          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
9270          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
9271          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
9272          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
9273          *             <li><b>content:</b> {String} Raw string of response</li>
9274          *             <li><b>object:</b> {Object} Parsed object of response</li>
9275          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
9276          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
9277          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
9278          *             </ul></li>
9279          *         </ul></li>
9280          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
9281          **/
9282         init: function (options) {
9283             this._super(options);           
9284         },
9285 
9286         /**
9287          * @private
9288          * Gets xmpp node path.
9289          */
9290         getXMPPNodePath: function () {
9291             return this.getRestUrl();
9292         },
9293 
9294         /**
9295          * @private
9296          * Gets the REST class for the current object - this is the Queues class.
9297          */
9298         getRestClass: function () {
9299             return Queues;
9300         },
9301 
9302         /**
9303          * @private
9304          * Gets the REST class for the objects that make up the collection. - this
9305          * is the Queue class.
9306          */
9307         getRestItemClass: function () {
9308             return Queue;
9309         },
9310 
9311         /**
9312          * @private
9313          * Gets the REST type for the current object - this is a "Queues".
9314          */
9315         getRestType: function () {
9316             return "Queues";
9317         },
9318         
9319         /**
9320          * @private
9321          * Gets the REST type for the objects that make up the collection - this is "Queue".
9322          */
9323         getRestItemType: function () {
9324             return "Queue";
9325         },
9326 
9327         explicitSubscription: true,
9328         
9329         handlesItemRefresh: true
9330     });
9331     
9332     window.finesse = window.finesse || {};
9333     window.finesse.restservices = window.finesse.restservices || {};
9334     window.finesse.restservices.Queues = Queues;
9335     
9336     return Queues;
9337 });
9338 
9339 /**
9340  * JavaScript representation of the Finesse WrapUpReason object.
9341  *
9342  * @requires finesse.clientservices.ClientServices
9343  * @requires Class
9344  * @requires finesse.FinesseBase
9345  * @requires finesse.restservices.RestBase
9346  */
9347 
9348 /** @private */
9349 define('restservices/WrapUpReason',['restservices/RestBase'], function (RestBase) {
9350 
9351     var WrapUpReason = RestBase.extend(/** @lends finesse.restservices.WrapUpReason.prototype */{
9352 
9353         /**
9354          * @class
9355          * A WrapUpReason is a code and description identifying a particular reason that a
9356          * User is in WORK (WrapUp) mode.
9357          * 
9358          * @augments finesse.restservices.RestBase
9359          * @see finesse.restservices.User
9360          * @see finesse.restservices.User.States#WORK
9361          * @constructs
9362          */
9363         _fakeConstuctor: function () {
9364             /* This is here to hide the real init constructor from the public docs */
9365         },
9366         
9367         /** 
9368          * @private
9369          * JavaScript representation of a WrapUpReason object. Also exposes
9370          * methods to operate on the object against the server.
9371          *
9372          * @param {Object} options
9373          *     An object with the following properties:<ul>
9374          *         <li><b>id:</b> The id of the object being constructed</li>
9375          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
9376          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
9377          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
9378          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
9379          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
9380          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
9381          *             <li><b>content:</b> {String} Raw string of response</li>
9382          *             <li><b>object:</b> {Object} Parsed object of response</li>
9383          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
9384          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
9385          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
9386          *             </ul></li>
9387          *         </ul></li>
9388          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
9389          **/
9390         init: function (options) {
9391             this._super(options);
9392         },
9393 
9394         /**
9395          * @private
9396          * Gets the REST class for the current object - this is the WrapUpReason class.
9397          * @returns {Object} The WrapUpReason class.
9398          */
9399         getRestClass: function () {
9400             return WrapUpReason;
9401         },
9402 
9403         /**
9404          * @private
9405          * Gets the REST type for the current object - this is a "WrapUpReason".
9406          * @returns {String} The WrapUpReason string.
9407          */
9408         getRestType: function () {
9409             return "WrapUpReason";
9410         },
9411 
9412         /**
9413          * @private
9414          * Gets the REST type for the current object - this is a "WrapUpReasons".
9415          * @returns {String} The WrapUpReasons string.
9416          */
9417         getParentRestType: function () {
9418             return "WrapUpReasons";
9419         },
9420 
9421         /**
9422          * @private
9423          * Override default to indicate that this object doesn't support making
9424          * requests.
9425          */
9426         supportsRequests: false,
9427 
9428         /**
9429          * @private
9430          * Override default to indicate that this object doesn't support subscriptions.
9431          */
9432         supportsSubscriptions: false,
9433 
9434         /**
9435          * Getter for the label.
9436          * @returns {String} The label.
9437          */
9438         getLabel: function () {
9439             this.isLoaded();
9440             return this.getData().label;
9441         },
9442 
9443         /**
9444          * @private
9445          * Getter for the forAll flag.
9446          * @returns {Boolean} True if global.
9447          */
9448         getForAll: function () {
9449             this.isLoaded();
9450             return this.getData().forAll;
9451         },
9452 
9453         /**
9454          * @private
9455          * Getter for the Uri value.
9456          * @returns {String} The Uri.
9457          */
9458         getUri: function () {
9459             this.isLoaded();
9460             return this.getData().uri;
9461         }
9462     });
9463 
9464     window.finesse = window.finesse || {};
9465     window.finesse.restservices = window.finesse.restservices || {};
9466     window.finesse.restservices.WrapUpReason = WrapUpReason;
9467         
9468     return WrapUpReason;
9469 });
9470 
9471 /**
9472 * JavaScript representation of the Finesse WrapUpReasons collection
9473 * object which contains a list of WrapUpReason objects.
9474  *
9475  * @requires finesse.clientservices.ClientServices
9476  * @requires Class
9477  * @requires finesse.FinesseBase
9478  * @requires finesse.restservices.RestBase
9479  * @requires finesse.restservices.Dialog
9480  * @requires finesse.restservices.RestCollectionBase
9481  */
9482 
9483 /** @private */
9484 define('restservices/WrapUpReasons',[
9485     'restservices/RestCollectionBase',
9486     'restservices/WrapUpReason'
9487 ],
9488 function (RestCollectionBase, WrapUpReason) {
9489 
9490     var WrapUpReasons = RestCollectionBase.extend(/** @lends finesse.restservices.WrapUpReasons.prototype */{
9491         
9492         /**
9493          * @class
9494          * JavaScript representation of a WrapUpReasons collection object. 
9495          * @augments finesse.restservices.RestCollectionBase
9496          * @constructs
9497          * @see finesse.restservices.WrapUpReason
9498          * @example
9499          *  _wrapUpReasons = _user.getWrapUpReasons ( {
9500          *      onCollectionAdd : _handleWrapUpReasonAdd,
9501          *      onCollectionDelete : _handleWrapUpReasonDelete,
9502          *      onLoad : _handleWrapUpReasonsLoaded
9503          *  });
9504          *  
9505          * _wrapUpReasonCollection = _wrapUpReasons.getCollection();
9506          * for (var wrapUpReasonId in _wrapUpReasonCollection) {
9507          *     if (_wrapUpReasonCollection.hasOwnProperty(wrapUpReasonId)) {
9508          *         _wrapUpReason = _wrapUpReasonCollection[wrapUpReasonId];
9509          *         etc...
9510          *     }
9511          * }
9512         */
9513         _fakeConstuctor: function () {
9514             /* This is here to hide the real init constructor from the public docs */
9515         },
9516         
9517         /** 
9518          * @private
9519          * JavaScript representation of a WrapUpReasons collection object. Also exposes
9520          * methods to operate on the object against the server.
9521          *
9522          * @param {Object} options
9523          *     An object with the following properties:<ul>
9524          *         <li><b>id:</b> The id of the object being constructed</li>
9525          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
9526          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
9527          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
9528          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
9529          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
9530          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
9531          *             <li><b>content:</b> {String} Raw string of response</li>
9532          *             <li><b>object:</b> {Object} Parsed object of response</li>
9533          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
9534          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
9535          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
9536          *             </ul></li>
9537          *         </ul></li>
9538          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
9539          **/
9540         init: function (options) {
9541             this._super(options);           
9542         },
9543 
9544         /**
9545          * @private
9546          * Gets the REST class for the current object - this is the WrapUpReasons class.
9547          */
9548         getRestClass: function () {
9549             return WrapUpReasons;
9550         },
9551 
9552         /**
9553          * @private
9554          * Gets the REST class for the objects that make up the collection. - this
9555          * is the WrapUpReason class.
9556          */
9557         getRestItemClass: function () {
9558             return WrapUpReason;
9559         },
9560 
9561         /**
9562          * @private
9563          * Gets the REST type for the current object - this is a "WrapUpReasons".
9564          */
9565         getRestType: function () {
9566             return "WrapUpReasons";
9567         },
9568         
9569         /**
9570          * @private
9571          * Gets the REST type for the objects that make up the collection - this is "WrapUpReason".
9572          */
9573         getRestItemType: function () {
9574             return "WrapUpReason";
9575         },
9576 
9577         /**
9578          * @private
9579          * Override default to indicates that the collection supports making
9580          * requests.
9581          */
9582         supportsRequests: true,
9583 
9584         /**
9585          * @private
9586          * Override default to indicate that this object doesn't support subscriptions.
9587          */
9588         supportsRestItemSubscriptions: false,
9589 
9590         /**
9591          * @private
9592          * Retrieve the Wrap-Up Reason Codes. This call will re-query the server and refresh the collection.
9593          *
9594          * @returns {finesse.restservices.WrapUpReasons}
9595          *     This ReadyReasonCodes object to allow cascading.
9596          */
9597         get: function () {
9598             // set loaded to false so it will rebuild the collection after the get
9599             this._loaded = false;
9600             // reset collection
9601             this._collection = {};
9602             // perform get
9603             this._synchronize();
9604             return this;
9605         }
9606         
9607     });
9608  
9609     window.finesse = window.finesse || {};
9610     window.finesse.restservices = window.finesse.restservices || {};
9611     window.finesse.restservices.WrapUpReasons = WrapUpReasons;
9612        
9613     return WrapUpReasons;
9614 });
9615 
9616 /**
9617  * JavaScript representation of the Finesse Contact object.
9618  * @requires finesse.clientservices.ClientServices
9619  * @requires Class
9620  * @requires finesse.FinesseBase
9621  * @requires finesse.restservices.RestBase
9622  */
9623 /** @private */
9624 define('restservices/Contact',['restservices/RestBase'], function (RestBase) {
9625 
9626     var Contact = RestBase.extend(/** @lends finesse.restservices.Contact.prototype */{
9627 
9628         /**
9629          * @class
9630          * A Contact is a single entry in a PhoneBook, consisting of a First and Last Name,
9631          * a Phone Number, and a Description.
9632          * 
9633          * @augments finesse.restservices.RestBase
9634          * @see finesse.restservices.PhoneBook
9635          * @constructs
9636          */
9637         _fakeConstuctor: function () {
9638             /* This is here to hide the real init constructor from the public docs */
9639         },
9640         
9641         /**
9642          * @private
9643          * @param {Object} options
9644          *     An object with the following properties:<ul>
9645          *         <li><b>id:</b> The id of the object being constructed</li>
9646          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
9647          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
9648          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
9649          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
9650          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
9651          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
9652          *             <li><b>content:</b> {String} Raw string of response</li>
9653          *             <li><b>object:</b> {Object} Parsed object of response</li>
9654          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
9655          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
9656          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
9657          *             </ul></li>
9658          *         </ul></li>
9659          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
9660          **/
9661         init: function (options) {
9662             this._super(options);
9663         },
9664 
9665         /**
9666          * @private
9667          * Gets the REST class for the current object - this is the Contact class.
9668          * @returns {Object} The Contact class.
9669          */
9670         getRestClass: function () {
9671             return Contact;
9672         },
9673 
9674         /**
9675          * @private
9676          * Gets the REST type for the current object - this is a "Contact".
9677          * @returns {String} The Contact string.
9678          */
9679         getRestType: function () {
9680             return "Contact";
9681         },
9682 
9683         /**
9684          * @private
9685          * Override default to indicate that this object doesn't support making
9686          * requests.
9687          */
9688         supportsRequests: false,
9689 
9690         /**
9691          * @private
9692          * Override default to indicate that this object doesn't support subscriptions.
9693          */
9694         supportsSubscriptions: false,
9695 
9696         /**
9697          * Getter for the firstName.
9698          * @returns {String} The firstName.
9699          */
9700         getFirstName: function () {
9701             this.isLoaded();
9702             return this.getData().firstName;
9703         },
9704 
9705         /**
9706          * Getter for the lastName.
9707          * @returns {String} The lastName.
9708          */
9709         getLastName: function () {
9710             this.isLoaded();
9711             return this.getData().lastName;
9712         },
9713 
9714         /**
9715          * Getter for the phoneNumber.
9716          * @returns {String} The phoneNumber.
9717          */
9718         getPhoneNumber: function () {
9719             this.isLoaded();
9720             return this.getData().phoneNumber;
9721         },
9722 
9723         /**
9724          * Getter for the description.
9725          * @returns {String} The description.
9726          */
9727         getDescription: function () {
9728             this.isLoaded();
9729             return this.getData().description;
9730         },
9731 
9732         /** @private */
9733         createPutSuccessHandler: function(contact, contentBody, successHandler){
9734             return function (rsp) {
9735                 // Update internal structure based on response. Here we
9736                 // inject the contentBody from the PUT request into the
9737                 // rsp.object element to mimic a GET as a way to take
9738                 // advantage of the existing _processResponse method.
9739                 rsp.object = contentBody;
9740                 contact._processResponse(rsp);
9741 
9742                 //Remove the injected Contact object before cascading response
9743                 rsp.object = {};
9744                 
9745                 //cascade response back to consumer's response handler
9746                 successHandler(rsp);
9747             };
9748         },
9749 
9750         /** @private */
9751         createPostSuccessHandler: function (contact, contentBody, successHandler) {
9752             return function (rsp) {
9753                 rsp.object = contentBody;
9754                 contact._processResponse(rsp);
9755 
9756                 //Remove the injected Contact object before cascading response
9757                 rsp.object = {};
9758 
9759                 //cascade response back to consumer's response handler
9760                 successHandler(rsp);
9761             };
9762         },
9763 
9764         /**
9765          * Add
9766          * @private
9767          */
9768         add: function (newValues, handlers) {
9769             // this.isLoaded();
9770             var contentBody = {};
9771 
9772             contentBody[this.getRestType()] = {
9773                 "firstName": newValues.firstName,
9774                 "lastName": newValues.lastName,
9775                 "phoneNumber": newValues.phoneNumber,
9776                 "description": newValues.description
9777             };
9778 
9779             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
9780             handlers = handlers || {};
9781 
9782             this.restRequest(this.getRestUrl(), {
9783                 method: 'POST',
9784                 success: this.createPostSuccessHandler(this, contentBody, handlers.success),
9785                 error: handlers.error,
9786                 content: contentBody
9787             });
9788 
9789             return this; // Allow cascading
9790         },
9791 
9792         /**
9793          * Update
9794          * @private
9795          */
9796         update: function (newValues, handlers) {
9797             this.isLoaded();
9798             var contentBody = {};
9799 
9800             contentBody[this.getRestType()] = {
9801                 "uri": this.getId(),
9802                 "firstName": newValues.firstName,
9803                 "lastName": newValues.lastName,
9804                 "phoneNumber": newValues.phoneNumber,
9805                 "description": newValues.description
9806             };
9807 
9808             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
9809             handlers = handlers || {};
9810 
9811             this.restRequest(this.getRestUrl(), {
9812                 method: 'PUT',
9813                 success: this.createPutSuccessHandler(this, contentBody, handlers.success),
9814                 error: handlers.error,
9815                 content: contentBody
9816             });
9817 
9818             return this; // Allow cascading
9819         },
9820 
9821 
9822         /**
9823          * Delete
9824          * @private
9825          */
9826         "delete": function ( handlers) {
9827             this.isLoaded();
9828 
9829             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
9830             handlers = handlers || {};
9831 
9832             this.restRequest(this.getRestUrl(), {
9833                 method: 'DELETE',
9834                 success: this.createPutSuccessHandler(this, {}, handlers.success),
9835                 error: handlers.error,
9836                 content: undefined
9837             });
9838 
9839             return this; // Allow cascading
9840         }
9841     });
9842 
9843     window.finesse = window.finesse || {};
9844     window.finesse.restservices = window.finesse.restservices || {};
9845     window.finesse.restservices.Contact = Contact;
9846     
9847     return Contact;
9848 });
9849 
9850 /**
9851 * JavaScript representation of the Finesse Contacts collection
9852 * object which contains a list of Contact objects.
9853  *
9854  * @requires finesse.clientservices.ClientServices
9855  * @requires Class
9856  * @requires finesse.FinesseBase
9857  * @requires finesse.restservices.RestBase
9858  * @requires finesse.restservices.Dialog
9859  * @requires finesse.restservices.RestCollectionBase
9860  */
9861 /** @private */
9862 define('restservices/Contacts',[
9863     'restservices/RestCollectionBase',
9864     'restservices/Contact'
9865 ],
9866 function (RestCollectionBase, Contact) {
9867     var Contacts = RestCollectionBase.extend(/** @lends finesse.restservices.Contacts.prototype */{
9868         
9869         /**
9870          * @class
9871          * JavaScript representation of a Contacts collection object. Also exposes
9872          * methods to operate on the object against the server.
9873          * @augments finesse.restservices.RestCollectionBase
9874          * @constructs
9875          * @see finesse.restservices.Contact
9876          * @see finesse.restservices.PhoneBook
9877          * @example
9878          *  _contacts = _phonebook.getContacts( {
9879          *      onCollectionAdd : _handleContactAdd,
9880          *      onCollectionDelete : _handleContactDelete,
9881          *      onLoad : _handleContactsLoaded
9882          *  });
9883          *  
9884          * _contactCollection = _contacts.getCollection();
9885          * for (var contactId in _contactCollection) {
9886          *     if (_contactCollection.hasOwnProperty(contactId)) {
9887          *         _contact = _contactCollection[contactId];
9888          *         etc...
9889          *     }
9890          * }
9891          */
9892         _fakeConstuctor: function () {
9893             /* This is here to hide the real init constructor from the public docs */
9894         },
9895         
9896         /** 
9897          * @private
9898          * JavaScript representation of a Contacts collection object. Also exposes
9899          * methods to operate on the object against the server.
9900          *
9901          * @param {Object} options
9902          *     An object with the following properties:<ul>
9903          *         <li><b>id:</b> The id of the object being constructed</li>
9904          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
9905          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
9906          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
9907          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
9908          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
9909          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
9910          *             <li><b>content:</b> {String} Raw string of response</li>
9911          *             <li><b>object:</b> {Object} Parsed object of response</li>
9912          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
9913          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
9914          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
9915          *             </ul></li>
9916          *         </ul></li>
9917          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
9918          **/
9919         init: function (options) {
9920             this._super(options);           
9921         },
9922 
9923         /**
9924          * @private
9925          * Gets the REST class for the current object - this is the Contacts class.
9926          */
9927         getRestClass: function () {
9928             return Contacts;
9929         },
9930 
9931         /**
9932          * @private
9933          * Gets the REST class for the objects that make up the collection. - this
9934          * is the Contact class.
9935          */
9936         getRestItemClass: function () {
9937             return Contact;
9938         },
9939 
9940         /**
9941          * @private
9942          * Gets the REST type for the current object - this is a "Contacts".
9943          */
9944         getRestType: function () {
9945             return "Contacts";
9946         },
9947         
9948         /**
9949          * @private
9950          * Gets the REST type for the objects that make up the collection - this is "Contacts".
9951          */
9952         getRestItemType: function () {
9953             return "Contact";
9954         },
9955 
9956         /**
9957          * @private
9958          * Override default to indicates that the collection supports making
9959          * requests.
9960          */
9961         supportsRequests: true,
9962 
9963         /**
9964          * @private
9965          * Override default to indicates that the collection subscribes to its objects.
9966          */
9967         supportsRestItemSubscriptions: false,
9968         
9969         /**
9970          * @private
9971          * Retrieve the Contacts.  This call will re-query the server and refresh the collection.
9972          *
9973          * @returns {finesse.restservices.Contacts}
9974          *     This Contacts object, to allow cascading.
9975          */
9976         get: function () {
9977             // set loaded to false so it will rebuild the collection after the get
9978             this._loaded = false;
9979             // reset collection
9980             this._collection = {};
9981             // perform get
9982             this._synchronize();
9983             return this;
9984         }
9985         
9986     });
9987     
9988     window.finesse = window.finesse || {};
9989     window.finesse.restservices = window.finesse.restservices || {};
9990     window.finesse.restservices.Contacts = Contacts;
9991     
9992     
9993     return Contacts;
9994 });
9995 
9996 /**
9997  * JavaScript representation of the Finesse PhoneBook object.
9998  *
9999  * @requires finesse.clientservices.ClientServices
10000  * @requires Class
10001  * @requires finesse.FinesseBase
10002  * @requires finesse.restservices.RestBase
10003  */
10004 
10005 /** @private */
10006 define('restservices/PhoneBook',[
10007     'restservices/RestBase',
10008     'restservices/Contacts'
10009 ],
10010 function (RestBase, Contacts) {
10011     var PhoneBook = RestBase.extend(/** @lends finesse.restservices.PhoneBook.prototype */{
10012 
10013         _contacts: null,
10014 
10015         /**
10016          * @class
10017          * A PhoneBook is a list of Contacts available to a User for quick dial.
10018          * 
10019          * @augments finesse.restservices.RestBase
10020          * @see finesse.restservices.Contacts
10021          * @constructs
10022          */
10023         _fakeConstuctor: function () {
10024             /* This is here to hide the real init constructor from the public docs */
10025         },
10026         
10027         /** 
10028          * @private
10029          * JavaScript representation of a PhoneBook object. Also exposes
10030          * methods to operate on the object against the server.
10031          *
10032          * @param {Object} options
10033          *     An object with the following properties:<ul>
10034          *         <li><b>id:</b> The id of the object being constructed</li>
10035          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
10036          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
10037          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
10038          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
10039          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
10040          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
10041          *             <li><b>content:</b> {String} Raw string of response</li>
10042          *             <li><b>object:</b> {Object} Parsed object of response</li>
10043          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
10044          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
10045          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
10046          *             </ul></li>
10047          *         </ul></li>
10048          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
10049          **/
10050         init: function (options) {
10051             this._super(options);
10052         },
10053 
10054         /**
10055          * @private
10056          * Gets the REST class for the current object - this is the PhoneBook class.
10057          * @returns {Object} The PhoneBook class.
10058          */
10059         getRestClass: function () {
10060             return PhoneBook;
10061         },
10062 
10063         /**
10064          * @private
10065          * Gets the REST type for the current object - this is a "PhoneBook".
10066          * @returns {String} The PhoneBook string.
10067          */
10068         getRestType: function () {
10069             return "PhoneBook";
10070         },
10071 
10072         /**
10073          * @private
10074          * Override default to indicate that this object doesn't support making
10075          * requests.
10076          */
10077         supportsRequests: false,
10078 
10079         /**
10080          * @private
10081          * Override default to indicate that this object doesn't support subscriptions.
10082          */
10083         supportsSubscriptions: false,
10084 
10085         /**
10086          * Getter for the name of the Phone Book.
10087          * @returns {String} The name.
10088          */
10089         getName: function () {
10090             this.isLoaded();
10091             return this.getData().name;
10092         },
10093 
10094         /**
10095          * Getter for the type flag.
10096          * @returns {String} The type.
10097          */
10098         getType: function () {
10099             this.isLoaded();
10100             return this.getData().type;
10101         },
10102 
10103         /**
10104          * @private
10105          * Getter for the Uri value.
10106          * @returns {String} The Uri.
10107          */
10108         getUri: function () {
10109             this.isLoaded();
10110             return this.getData().uri;
10111         },
10112 
10113         /**
10114          * Getter for a Contacts collection object that is associated with PhoneBook.
10115          * @param {finesse.interfaces.RequestHandlers} handlers
10116          *     An object containing the handlers for the request
10117          * @returns {finesse.restservices.Contacts}
10118          *     A Contacts collection object.
10119          */
10120         getContacts: function (callbacks) {
10121             var options = callbacks || {};
10122             options.parentObj = this;
10123             this.isLoaded();
10124 
10125             if (this._contacts === null) {
10126                 this._contacts = new Contacts(options);
10127             }
10128 
10129             return this._contacts;
10130         },
10131 
10132         /**
10133          * Getter for <contacts> node within PhoneBook - sometimes it's just a URI, sometimes it is a Contacts collection
10134          * @returns {String} uri to contacts
10135          *          or {finesse.restservices.Contacts} collection
10136          */
10137         getEmbeddedContacts: function(){
10138             this.isLoaded();
10139             return this.getData().contacts;
10140         },
10141 
10142         /** @private */
10143         createPutSuccessHandler: function(phonebook, contentBody, successHandler){
10144             return function (rsp) {
10145                 // Update internal structure based on response. Here we
10146                 // inject the contentBody from the PUT request into the
10147                 // rsp.object element to mimic a GET as a way to take
10148                 // advantage of the existing _processResponse method.
10149                 rsp.object = contentBody;
10150                 phonebook._processResponse(rsp);
10151 
10152                 //Remove the injected PhoneBook object before cascading response
10153                 rsp.object = {};
10154                 
10155                 //cascade response back to consumer's response handler
10156                 successHandler(rsp);
10157             };
10158         },
10159 
10160         /** @private */
10161         createPostSuccessHandler: function (phonebook, contentBody, successHandler) {
10162             return function (rsp) {
10163                 rsp.object = contentBody;
10164                 phonebook._processResponse(rsp);
10165 
10166                 //Remove the injected PhoneBook object before cascading response
10167                 rsp.object = {};
10168 
10169                 //cascade response back to consumer's response handler
10170                 successHandler(rsp);
10171             };
10172         },
10173 
10174         /**
10175          * @private
10176          * Add a PhoneBook.
10177          * @param {Object} newValues
10178          * @param {String} newValues.name Name of PhoneBook
10179          * @param {String} newValues.type Type of PhoneBook
10180          * @param {finesse.interfaces.RequestHandlers} handlers
10181          *     An object containing the handlers for the request
10182          * @returns {finesse.restservices.PhoneBook}
10183          *     This PhoneBook object, to allow cascading
10184          */
10185         add: function (newValues, handlers) {
10186             // this.isLoaded();
10187             var contentBody = {};
10188 
10189             contentBody[this.getRestType()] = {
10190                 "name": newValues.name,
10191                 "type": newValues.type
10192             };
10193 
10194             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
10195             handlers = handlers || {};
10196 
10197             this.restRequest(this.getRestUrl(), {
10198                 method: 'POST',
10199                 success: this.createPostSuccessHandler(this, contentBody, handlers.success),
10200                 error: handlers.error,
10201                 content: contentBody
10202             });
10203 
10204             return this; // Allow cascading
10205         },
10206 
10207         /**
10208          * @private
10209          * Update a PhoneBook.
10210          * @param {Object} newValues
10211          * @param {String} newValues.name Name of PhoneBook
10212          * @param {String} newValues.type Type of PhoneBook
10213          * @param {finesse.interfaces.RequestHandlers} handlers
10214          *     An object containing the handlers for the request
10215          * @returns {finesse.restservices.PhoneBook}
10216          *     This PhoneBook object, to allow cascading
10217          */
10218         update: function (newValues, handlers) {
10219             this.isLoaded();
10220             var contentBody = {};
10221 
10222             contentBody[this.getRestType()] = {
10223                 "uri": this.getId(),
10224                 "name": newValues.name,
10225                 "type": newValues.type
10226             };
10227 
10228             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
10229             handlers = handlers || {};
10230 
10231             this.restRequest(this.getRestUrl(), {
10232                 method: 'PUT',
10233                 success: this.createPutSuccessHandler(this, contentBody, handlers.success),
10234                 error: handlers.error,
10235                 content: contentBody
10236             });
10237 
10238             return this; // Allow cascading
10239         },
10240 
10241 
10242         /**
10243          * Delete a PhoneBook.
10244          * @param {finesse.interfaces.RequestHandlers} handlers
10245          *     An object containing the handlers for the request
10246          * @returns {finesse.restservices.PhoneBook}
10247          *     This PhoneBook object, to allow cascading
10248          */
10249         "delete": function ( handlers) {
10250             this.isLoaded();
10251 
10252             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
10253             handlers = handlers || {};
10254 
10255             this.restRequest(this.getRestUrl(), {
10256                 method: 'DELETE',
10257                 success: this.createPutSuccessHandler(this, {}, handlers.success),
10258                 error: handlers.error,
10259                 content: undefined
10260             });
10261 
10262             return this; // Allow cascading
10263         }
10264 
10265 
10266 
10267     });
10268     
10269     window.finesse = window.finesse || {};
10270     window.finesse.restservices = window.finesse.restservices || {};
10271     window.finesse.restservices.PhoneBook = PhoneBook;
10272     
10273     return PhoneBook;
10274 });
10275 
10276 /**
10277 * JavaScript representation of the Finesse PhoneBooks collection
10278 * object which contains a list of PhoneBook objects.
10279  *
10280  * @requires finesse.clientservices.ClientServices
10281  * @requires Class
10282  * @requires finesse.FinesseBase
10283  * @requires finesse.restservices.RestBase
10284  * @requires finesse.restservices.Dialog
10285  * @requires finesse.restservices.RestCollectionBase
10286  */
10287 /** @private */
10288 define('restservices/PhoneBooks',[
10289     'restservices/RestCollectionBase',
10290     'restservices/PhoneBook'
10291 ],
10292 function (RestCollectionBase, PhoneBook) {
10293     var PhoneBooks = RestCollectionBase.extend(/** @lends finesse.restservices.PhoneBooks.prototype */{
10294         
10295         /**
10296          * @class
10297          * JavaScript representation of a PhoneBooks collection object. 
10298          * @augments finesse.restservices.RestCollectionBase
10299          * @constructs
10300          * @see finesse.restservices.PhoneBook
10301          * @see finesse.restservices.Contacts
10302          * @see finesse.restservices.Contact
10303          * @example
10304          *  _phoneBooks = _user.getPhoneBooks( {
10305          *      onCollectionAdd : _handlePhoneBookAdd,
10306          *      onCollectionDelete : _handlePhoneBookDelete,
10307          *      onLoad : _handlePhoneBooksLoaded
10308          *  });
10309          *  
10310          * _phoneBookCollection = _phoneBooks.getCollection();
10311          * for (var phoneBookId in _phoneBookCollection) {
10312          *     if (_phoneBookCollection.hasOwnProperty(phoneBookId)) {
10313          *         _phoneBook = _phoneBookCollection[phoneBookId];
10314          *         etc...
10315          *     }
10316          * }
10317         */
10318         _fakeConstuctor: function () {
10319             /* This is here to hide the real init constructor from the public docs */
10320         },
10321         
10322        /**
10323          * @private
10324          *
10325          * @param {Object} options
10326          *     An object with the following properties:<ul>
10327          *         <li><b>id:</b> The id of the object being constructed</li>
10328          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
10329          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
10330          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
10331          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
10332          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
10333          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
10334          *             <li><b>content:</b> {String} Raw string of response</li>
10335          *             <li><b>object:</b> {Object} Parsed object of response</li>
10336          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
10337          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
10338          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
10339          *             </ul></li>
10340          *         </ul></li>
10341          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
10342          **/
10343         init: function (options) {
10344             // Keep the REST response for PhoneBooks to check for 206 Partial Content.
10345             this.keepRestResponse = true;
10346             // Add in the Range header which is required for PhoneBooks API.
10347             this.extraHeaders = { "Range": "objects=1-1500" };
10348             this._super(options);
10349         },
10350 
10351         /**
10352          * @private
10353          * Gets the REST class for the current object - this is the PhoneBooks class.
10354          */
10355         getRestClass: function () {
10356             return PhoneBooks;
10357         },
10358 
10359         /**
10360          * @private
10361          * Gets the REST class for the objects that make up the collection. - this
10362          * is the PhoneBook class.
10363          */
10364         getRestItemClass: function () {
10365             return PhoneBook;
10366         },
10367 
10368         /**
10369          * @private
10370          * Gets the REST type for the current object - this is a "PhoneBooks".
10371          */
10372         getRestType: function () {
10373             return "PhoneBooks";
10374         },
10375         
10376         /**
10377          * @private
10378          * Gets the REST type for the objects that make up the collection - this is "PhoneBooks".
10379          */
10380         getRestItemType: function () {
10381             return "PhoneBook";
10382         },
10383 
10384         /**
10385          * @private
10386          * Override default to indicates that the collection supports making
10387          * requests.
10388          */
10389         supportsRequests: true,
10390 
10391         /**
10392          * @private
10393          * Override default to indicates that the collection subscribes to its objects.
10394          */
10395         supportsRestItemSubscriptions: false,
10396         
10397         /**
10398          * @private
10399          * Retrieve the PhoneBooks.  This call will re-query the server and refresh the collection.
10400          *
10401          * @returns {finesse.restservices.PhoneBooks}
10402          *     This PhoneBooks object, to allow cascading.
10403          */
10404         get: function () {
10405             // set loaded to false so it will rebuild the collection after the get
10406             this._loaded = false;
10407             // reset collection
10408             this._collection = {};
10409             // perform get
10410             this._synchronize();
10411             return this;
10412         }
10413         
10414     });
10415     
10416     window.finesse = window.finesse || {};
10417     window.finesse.restservices = window.finesse.restservices || {};
10418     window.finesse.restservices.PhoneBooks = PhoneBooks;
10419     
10420     return PhoneBooks;
10421 });
10422 
10423 /**
10424  * JavaScript representation of the Finesse WorkflowAction object.
10425  *
10426  * @requires finesse.clientservices.ClientServices
10427  * @requires Class
10428  * @requires finesse.FinesseBase
10429  * @requires finesse.restservices.RestBase
10430  */
10431 
10432 /*jslint browser: true, nomen: true, sloppy: true, forin: true */
10433 /*global define,finesse */
10434 
10435 /** @private */
10436 define('restservices/WorkflowAction',['restservices/RestBase'], function (RestBase) {
10437 
10438     var WorkflowAction = RestBase.extend({
10439 
10440         _contacts: null,
10441 
10442         actionTypes: [
10443             {
10444                 name: 'BROWSER_POP',
10445                 params: [
10446                     {
10447                         name: 'windowName',
10448                         type: 'text'
10449                     },
10450                     {
10451                         name: 'path',
10452                         type: 'systemVariableSingleLineEditor'
10453                     }
10454                 ]
10455             },
10456             {
10457                 name: 'HTTP_REQUEST',
10458                 params: [
10459                     {
10460                         name: 'method',
10461                         type: 'dropdown',
10462                         values: ['POST', 'PUT']
10463                     },
10464                     {
10465                         name: 'location',
10466                         type: 'dropdown',
10467                         values: ['FINESSE', 'OTHER']
10468                     },
10469                     {
10470                         name: 'contentType',
10471                         type: 'text'
10472                     },
10473                     {
10474                         name: 'path',
10475                         type: 'systemVariableSingleLineEditor'
10476                     },
10477                     {
10478                         name: 'body',
10479                         type: 'systemVariableMultiLineEditor'
10480                     }
10481                 ]
10482             }            
10483             // more action type definitions here
10484         ],
10485 
10486         /**
10487          * @class
10488          * A WorkflowAction is an action (e.g. Browser Pop, Rest Request) defined in a
10489          * Workflow and triggered by a system event (Call Received, Call Ended, etc.).
10490          * 
10491          * @augments finesse.restservices.RestBase
10492          * @see finesse.restservices.Workflow
10493          * @constructs
10494          */
10495         _fakeConstuctor: function () {
10496             /* This is here to hide the real init constructor from the public docs */
10497         },
10498         
10499         /**
10500          * @private
10501          * JavaScript representation of a WorkflowAction object. Also exposes
10502          * methods to operate on the object against the server.
10503          *
10504          * @param {Object} options
10505          *     An object with the following properties:<ul>
10506          *         <li><b>id:</b> The id of the object being constructed</li>
10507          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
10508          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
10509          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
10510          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
10511          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
10512          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
10513          *             <li><b>content:</b> {String} Raw string of response</li>
10514          *             <li><b>object:</b> {Object} Parsed object of response</li>
10515          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
10516          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
10517          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
10518          *             </ul></li>
10519          *         </ul></li>
10520          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
10521          **/
10522         init: function (options) {
10523             this._super(options);
10524         },
10525 
10526         /**
10527          * @private
10528          * Gets the REST class for the current object - this is the WorkflowAction class.
10529          * @returns {Object} The WorkflowAction class.
10530          */
10531         getRestClass: function () {
10532             return finesse.restservices.WorkflowAction;
10533         },
10534 
10535         /**
10536          * @private
10537          * Gets the REST type for the current object - this is a "WorkflowAction".
10538          * @returns {String} The WorkflowAction string.
10539          */
10540         getRestType: function () {
10541             return "WorkflowAction";
10542         },
10543 
10544         /**
10545          * @private
10546          * Override default to indicate that this object doesn't support making
10547          * requests.
10548          */
10549         supportsRequests: false,
10550 
10551         /**
10552          * @private
10553          * Override default to indicate that this object doesn't support subscriptions.
10554          */
10555         supportsSubscriptions: false,
10556 
10557         /**
10558          * Getter for the name.
10559          * @returns {String} The name.
10560          */
10561         getName: function () {
10562             this.isLoaded();
10563             return this.getData().name;
10564         },
10565 
10566         /**
10567          * Getter for the type flag.
10568          * @returns {String} The type.
10569          */
10570         getType: function () {
10571             this.isLoaded();
10572             return this.getData().type;
10573         },
10574 
10575         /**
10576          * @private
10577          * Getter for the Uri value.
10578          * @returns {String} The Uri.
10579          */
10580         getUri: function () {
10581             this.isLoaded();
10582             return this.getData().uri;
10583         },
10584 
10585         /**
10586          * @private
10587          * Getter for the handledBy value.
10588          * @returns {String} handledBy.
10589          */
10590         getHandledBy: function () {
10591             this.isLoaded();
10592             return this.getData().handledBy;
10593         },
10594 
10595         /**
10596          * Getter for the parameters.
10597          * @returns {Object} key = param name, value = param value
10598          */
10599         getParams: function () {
10600             var map = {},
10601                 params = this.getData().params.Param,
10602                 i,
10603                 param;
10604 
10605             for(i=0; i<params.length; i+=1){
10606                 param = params[i];
10607                 map[param.name] = param.value || "";
10608             }
10609 
10610             return map;
10611         },
10612 
10613         /**
10614          * Getter for the ActionVariables
10615          * @returns {Object} key = action variable name, value = Object{name, type, node, testValue}
10616          */
10617         getActionVariables: function() {
10618             var map = {},
10619                 actionVariablesParent = this.getData().actionVariables,
10620                 actionVariables,
10621                 i,
10622                 actionVariable;
10623 
10624             if (actionVariablesParent === null ||  typeof(actionVariablesParent) === "undefined" || actionVariablesParent.length === 0){
10625                 return map;
10626             }
10627             actionVariables = actionVariablesParent.ActionVariable;
10628 
10629             if(actionVariables.length > 0){
10630                 for(i=0; i<actionVariables.length; i+=1){
10631                     actionVariable = actionVariables[i];
10632                     // escape nulls to empty string
10633                     actionVariable.name = actionVariable.name || "";
10634                     actionVariable.type = actionVariable.type || "";
10635                     actionVariable.node = actionVariable.node || "";
10636                     actionVariable.testValue = actionVariable.testValue || "";
10637                     map[actionVariable.name] = actionVariable;
10638                 }
10639             } else {
10640                 map[actionVariables.name] = actionVariables;
10641             }
10642 
10643             return map;
10644         },
10645 
10646         /** @private */
10647         createPutSuccessHandler: function(action, contentBody, successHandler){
10648             return function (rsp) {
10649                 // Update internal structure based on response. Here we
10650                 // inject the contentBody from the PUT request into the
10651                 // rsp.object element to mimic a GET as a way to take
10652                 // advantage of the existing _processResponse method.
10653                 rsp.object = contentBody;
10654                 action._processResponse(rsp);
10655 
10656                 //Remove the injected WorkflowAction object before cascading response
10657                 rsp.object = {};
10658                 
10659                 //cascade response back to consumer's response handler
10660                 successHandler(rsp);
10661             };
10662         },
10663 
10664         /** @private */
10665         createPostSuccessHandler: function (action, contentBody, successHandler) {
10666             return function (rsp) {
10667                 rsp.object = contentBody;
10668                 action._processResponse(rsp);
10669 
10670                 //Remove the injected WorkflowAction object before cascading response
10671                 rsp.object = {};
10672 
10673                 //cascade response back to consumer's response handler
10674                 successHandler(rsp);
10675             };
10676         },
10677 
10678         /**
10679          * @private
10680          * Build params array out of all the values coming into add or update methods
10681          * paramMap is a map of params.. we need to translate it into an array of Param objects
10682          * where path and windowName are params for the BROWSER_POP type
10683          */
10684         buildParamsForRest: function(paramMap){
10685             var params = {"Param": []},
10686                 i;
10687             for(i in paramMap){
10688                 if(paramMap.hasOwnProperty(i)){
10689                     params.Param.push({name: i, value: paramMap[i]});
10690                 }
10691             }
10692             return params;
10693         },
10694 
10695         /**
10696          * @private
10697          * Build actionVariables array out of all the values coming into add or update methods
10698          * actionVariableMap is a map of actionVariables.. we need to translate it into an array of ActionVariable objects
10699          * where path and windowName are params for the BROWSER_POP type
10700          */
10701         buildActionVariablesForRest: function(actionVariableMap){
10702             var actionVariables = {"ActionVariable": []},
10703                 i,
10704                 actionVariable;
10705             for(i in actionVariableMap){
10706                 if(actionVariableMap.hasOwnProperty(i)){
10707                     // {name: "callVariable1", type: "SYSTEM", node: "", testValue: "<blink>"}
10708                     actionVariable = {
10709                         "name": actionVariableMap[i].name,
10710                         "type": actionVariableMap[i].type,
10711                         "node": actionVariableMap[i].node,
10712                         "testValue": actionVariableMap[i].testValue
10713                     };
10714                     actionVariables.ActionVariable.push(actionVariable);
10715                 }
10716             }
10717             return actionVariables;
10718         },
10719 
10720         /**
10721          * Add
10722          */
10723         add: function (newValues, handlers) {
10724             var contentBody = {};
10725 
10726             contentBody[this.getRestType()] = {
10727                 "name": newValues.name,
10728                 "type": newValues.type,
10729                 "handledBy": newValues.handledBy,
10730                 "params": this.buildParamsForRest(newValues.params),
10731                 "actionVariables": this.buildActionVariablesForRest(newValues.actionVariables)
10732             };
10733 
10734             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
10735             handlers = handlers || {};
10736 
10737             this.restRequest(this.getRestUrl(), {
10738                 method: 'POST',
10739                 success: this.createPostSuccessHandler(this, contentBody, handlers.success),
10740                 error: handlers.error,
10741                 content: contentBody
10742             });
10743 
10744             return this; // Allow cascading
10745         },
10746 
10747         /**
10748          * @private
10749          * Update
10750          */
10751         update: function (newValues, handlers) {
10752             this.isLoaded();
10753             var contentBody = {};
10754             
10755             contentBody[this.getRestType()] = {
10756                 "uri": this.getId(),
10757                 "name": newValues.name,
10758                 "type": newValues.type,
10759                 "handledBy": newValues.handledBy,
10760                 "params": this.buildParamsForRest(newValues.params),
10761                 "actionVariables": this.buildActionVariablesForRest(newValues.actionVariables)
10762             };
10763 
10764             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
10765             handlers = handlers || {};
10766 
10767             this.restRequest(this.getRestUrl(), {
10768                 method: 'PUT',
10769                 success: this.createPutSuccessHandler(this, contentBody, handlers.success),
10770                 error: handlers.error,
10771                 content: contentBody
10772             });
10773 
10774             return this; // Allow cascading
10775         },
10776 
10777 
10778         /**
10779          * @private
10780          * Delete
10781          */
10782         "delete": function ( handlers) {
10783             this.isLoaded();
10784 
10785             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
10786             handlers = handlers || {};
10787 
10788             this.restRequest(this.getRestUrl(), {
10789                 method: 'DELETE',
10790                 success: this.createPutSuccessHandler(this, {}, handlers.success),
10791                 error: handlers.error,
10792                 content: undefined
10793             });
10794 
10795             return this; // Allow cascading
10796         }
10797 
10798 
10799 
10800     });
10801 
10802     window.finesse = window.finesse || {};
10803     window.finesse.restservices = window.finesse.restservices || {};
10804     window.finesse.restservices.WorkflowAction = WorkflowAction;
10805     
10806     return WorkflowAction;
10807 });
10808 
10809 /**
10810 * JavaScript representation of the Finesse WorkflowActions collection
10811 * object which contains a list of WorkflowAction objects.
10812  *
10813  * @requires finesse.clientservices.ClientServices
10814  * @requires Class
10815  * @requires finesse.FinesseBase
10816  * @requires finesse.restservices.RestBase
10817  * @requires finesse.restservices.Dialog
10818  * @requires finesse.restservices.RestCollectionBase
10819  */
10820 
10821 /** @private */
10822 define('restservices/WorkflowActions',[
10823     'restservices/RestCollectionBase',
10824     'restservices/RestBase',
10825     'restservices/WorkflowAction'
10826 ],
10827 function (RestCollectionBase, RestBase, WorkflowAction) {
10828 
10829     var WorkflowActions = RestCollectionBase.extend({
10830         
10831         /**
10832          * @class
10833          * JavaScript representation of a WorkflowActions collection object. 
10834          * @augments finesse.restservices.RestCollectionBase
10835          * @constructs
10836          * @see finesse.restservices.WorkflowAction
10837          * @see finesse.restservices.Workflow
10838          * @see finesse.restservices.Workflows
10839          * @example
10840          *  _workflowActions = _user.getWorkflowActions( {
10841          *      onCollectionAdd : _handleWorkflowActionAdd,
10842          *      onCollectionDelete : _handleWorkflowActionDelete,
10843          *      onLoad : _handleWorkflowActionsLoaded
10844          *  });
10845         */
10846         _fakeConstuctor: function () {
10847             /* This is here to hide the real init constructor from the public docs */
10848         },
10849         
10850         /**
10851          * @private
10852          * JavaScript representation of a WorkflowActions collection object. Also exposes
10853          * methods to operate on the object against the server.
10854          *
10855          * @param {Object} options
10856          *     An object with the following properties:<ul>
10857          *         <li><b>id:</b> The id of the object being constructed</li>
10858          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
10859          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
10860          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
10861          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
10862          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
10863          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
10864          *             <li><b>content:</b> {String} Raw string of response</li>
10865          *             <li><b>object:</b> {Object} Parsed object of response</li>
10866          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
10867          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
10868          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
10869          *             </ul></li>
10870          *         </ul></li>
10871          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
10872          **/
10873         init: function (options) {
10874             this._super(options);           
10875         },
10876 
10877         /**
10878          * @private
10879          * Gets the REST class for the current object - this is the WorkflowActions class.
10880          */
10881         getRestClass: function () {
10882             return WorkflowActions;
10883         },
10884 
10885         /**
10886          * @private
10887          * Gets the REST class for the objects that make up the collection. - this
10888          * is the WorkflowAction class.
10889          */
10890         getRestItemClass: function () {
10891             return WorkflowAction;
10892         },
10893 
10894         /**
10895          * @private
10896          * Gets the REST type for the current object - this is a "WorkflowActions".
10897          */
10898         getRestType: function () {
10899             return "WorkflowActions";
10900         },
10901         
10902         /**
10903          * @private
10904          * Gets the REST type for the objects that make up the collection - this is "WorkflowActions".
10905          */
10906         getRestItemType: function () {
10907             return "WorkflowAction";
10908         },
10909 
10910         /**
10911          * @private
10912          * Override default to indicates that the collection supports making
10913          * requests.
10914          */
10915         supportsRequests: true,
10916 
10917         /**
10918          * @private
10919          * Override default to indicates that the collection subscribes to its objects.
10920          */
10921         supportsRestItemSubscriptions: false,
10922         
10923         /**
10924          * @private
10925          * Retrieve the WorkflowActions.
10926          *
10927          * @returns {finesse.restservices.WorkflowActions}
10928          *     This WorkflowActions object to allow cascading.
10929          */
10930         get: function () {
10931             // set loaded to false so it will rebuild the collection after the get
10932             this._loaded = false;
10933             // reset collection
10934             this._collection = {};
10935             // perform get
10936             this._synchronize();
10937             return this;
10938         }
10939     });
10940 
10941     window.finesse = window.finesse || {};
10942     window.finesse.restservices = window.finesse.restservices || {};
10943     window.finesse.restservices.WorkflowActions = WorkflowActions;
10944         
10945     return WorkflowActions;
10946 });
10947 
10948 /**
10949  * JavaScript representation of the Finesse Workflow object.
10950  *
10951  * @requires finesse.clientservices.ClientServices
10952  * @requires Class
10953  * @requires finesse.FinesseBase
10954  * @requires finesse.restservices.RestBase
10955  */
10956 
10957 /*jslint browser: true, nomen: true, sloppy: true, forin: true */
10958 /*global define,finesse */
10959 
10960 /** @private */
10961 define('restservices/Workflow',[
10962     'restservices/RestBase',
10963     'restservices/WorkflowActions'
10964 ],
10965 function (RestBase, WorkflowActions) {
10966 
10967     var Workflow = RestBase.extend({
10968 
10969         /**
10970          * @class
10971          * JavaScript representation of a Workflow object. Also exposes
10972          * methods to operate on the object against the server.
10973          *
10974          * @param {Object} options
10975          *     An object with the following properties:<ul>
10976          *         <li><b>id:</b> The id of the object being constructed</li>
10977          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
10978          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
10979          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
10980          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
10981          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
10982          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
10983          *             <li><b>content:</b> {String} Raw string of response</li>
10984          *             <li><b>object:</b> {Object} Parsed object of response</li>
10985          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
10986          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
10987          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
10988          *             </ul></li>
10989          *         </ul></li>
10990          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
10991          * @constructs
10992          **/
10993         init: function (options) {
10994             this._super(options);
10995         },
10996 
10997         /**
10998          * @private
10999          * Gets the REST class for the current object - this is the Workflow class.
11000          * @returns {Object} The Workflow class.
11001          */
11002         getRestClass: function () {
11003             return Workflow;
11004         },
11005 
11006         /**
11007          * @private
11008          * Gets the REST type for the current object - this is a "Workflow".
11009          * @returns {String} The Workflow string.
11010          */
11011         getRestType: function () {
11012             return "Workflow";
11013         },
11014 
11015         /**
11016          * @private
11017          * Override default to indicate that this object doesn't support making
11018          * requests.
11019          */
11020         supportsRequests: false,
11021 
11022         /**
11023          * @private
11024          * Override default to indicate that this object doesn't support subscriptions.
11025          */
11026         supportsSubscriptions: false,
11027 
11028         /**
11029          * @private
11030          * Getter for the Uri value.
11031          * @returns {String} The Uri.
11032          */
11033         getUri: function () {
11034             this.isLoaded();
11035             return this.getData().uri;
11036         },
11037 
11038         /**
11039          * Getter for the name.
11040          * @returns {String} The name.
11041          */
11042         getName: function () {
11043             this.isLoaded();
11044             return this.getData().name;
11045         },
11046 
11047         /**
11048          * Getter for the description.
11049          * @returns {String} The description.
11050          */
11051         getDescription: function () {
11052             this.isLoaded();
11053             return this.getData().description;
11054         },
11055 
11056         /**
11057          * Getter for the trigger set.
11058          * @returns {String} The trigger set.
11059          */
11060         getTriggerSet: function () {
11061             this.isLoaded();
11062             return this.getData().TriggerSet;
11063         },
11064 
11065         /**
11066          * Getter for the condition set.
11067          * @returns {String} The condition set.
11068          */
11069         getConditionSet: function () {
11070             this.isLoaded();
11071             return this.getData().ConditionSet;
11072         },
11073         
11074         /**
11075          * Getter for the assigned workflowActions.
11076          * @returns {String} The workflowActions object.
11077          */
11078         getWorkflowActions: function () {
11079             this.isLoaded();
11080             var workflowActions = this.getData().workflowActions;
11081             if (workflowActions === null) {
11082                 workflowActions = "";
11083             }
11084             return workflowActions;
11085         },
11086 
11087         createPutSuccessHandler: function (workflow, contentBody, successHandler) {
11088             return function (rsp) {
11089                 // Update internal structure based on response. Here we
11090                 // inject the contentBody from the PUT request into the
11091                 // rsp.object element to mimic a GET as a way to take
11092                 // advantage of the existing _processResponse method.
11093                 rsp.object = contentBody;
11094                 workflow._processResponse(rsp);
11095 
11096                 //Remove the injected Workflow object before cascading response
11097                 rsp.object = {};
11098 
11099                 //cascade response back to consumer's response handler
11100                 successHandler(rsp);
11101             };
11102         },
11103 
11104         createPostSuccessHandler: function (workflow, contentBody, successHandler) {
11105             return function (rsp) {
11106                 rsp.object = contentBody;
11107                 workflow._processResponse(rsp);
11108 
11109                 //Remove the injected Workflow object before cascading response
11110                 rsp.object = {};
11111 
11112                 //cascade response back to consumer's response handler
11113                 successHandler(rsp);
11114             };
11115         },
11116 
11117         /**
11118          * @private
11119          * Add
11120          */
11121         add: function (newValues, handlers) {
11122             // this.isLoaded();
11123             var contentBody = {};
11124 
11125             contentBody[this.getRestType()] = {
11126                 "name": newValues.name,
11127                 "description": newValues.description,
11128                 "TriggerSet" : newValues.TriggerSet,
11129                 "ConditionSet" : newValues.ConditionSet,
11130                 "workflowActions" : newValues.workflowActions
11131             };
11132 
11133             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
11134             handlers = handlers || {};
11135 
11136             this.restRequest(this.getRestUrl(), {
11137                 method: 'POST',
11138                 success: this.createPostSuccessHandler(this, contentBody, handlers.success),
11139                 error: handlers.error,
11140                 content: contentBody
11141             });
11142 
11143             return this; // Allow cascading
11144         },
11145 
11146         /**
11147          * @private
11148          * Update
11149          */
11150         update: function (newValues, handlers) {
11151             this.isLoaded();
11152             var contentBody = {};
11153 
11154             contentBody[this.getRestType()] = {
11155                 "uri": this.getId(),
11156                 "name": newValues.name,
11157                 "description": newValues.description,
11158                 "TriggerSet" : newValues.TriggerSet,
11159                 "ConditionSet" : newValues.ConditionSet,
11160                 "workflowActions" : newValues.workflowActions
11161             };
11162 
11163             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
11164             handlers = handlers || {};
11165 
11166             this.restRequest(this.getRestUrl(), {
11167                 method: 'PUT',
11168                 success: this.createPutSuccessHandler(this, contentBody, handlers.success),
11169                 error: handlers.error,
11170                 content: contentBody
11171             });
11172 
11173             return this; // Allow cascading
11174         },
11175 
11176 
11177         /**
11178          * @private
11179          * Delete
11180          */
11181         "delete": function (handlers) {
11182             this.isLoaded();
11183 
11184             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
11185             handlers = handlers || {};
11186 
11187             this.restRequest(this.getRestUrl(), {
11188                 method: 'DELETE',
11189                 success: this.createPutSuccessHandler(this, {}, handlers.success),
11190                 error: handlers.error,
11191                 content: undefined
11192             });
11193 
11194             return this; // Allow cascading
11195         }
11196 
11197 
11198 
11199     });
11200 
11201     window.finesse = window.finesse || {};
11202     window.finesse.restservices = window.finesse.restservices || {};
11203     window.finesse.restservices.Workflow = Workflow;
11204 
11205     return Workflow;
11206 });
11207 
11208 /**
11209 * JavaScript representation of the Finesse workflows collection
11210 * object which contains a list of workflow objects.
11211  *
11212  * @requires finesse.clientservices.ClientServices
11213  * @requires Class
11214  * @requires finesse.FinesseBase
11215  * @requires finesse.restservices.RestBase
11216  * @requires finesse.restservices.Dialog
11217  * @requires finesse.restservices.RestCollectionBase
11218  */
11219 
11220 /** @private */
11221 define('restservices/Workflows',[
11222     'restservices/RestCollectionBase',
11223     'restservices/RestBase',
11224     'restservices/Workflow'
11225 ],
11226 function (RestCollectionBase, RestBase, Workflow) {
11227 
11228     var Workflows = RestCollectionBase.extend({
11229 
11230         /**
11231          * @class
11232          * JavaScript representation of a workflows collection object. Also exposes
11233          * methods to operate on the object against the server.
11234          *
11235          * @param {Object} options
11236          *     An object with the following properties:<ul>
11237          *         <li><b>id:</b> The id of the object being constructed</li>
11238          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
11239          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
11240          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
11241          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
11242          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
11243          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
11244          *             <li><b>content:</b> {String} Raw string of response</li>
11245          *             <li><b>object:</b> {Object} Parsed object of response</li>
11246          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
11247          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
11248          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
11249          *             </ul></li>
11250          *         </ul></li>
11251          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
11252          *  @constructs
11253          **/
11254         init: function (options) {
11255             this._super(options);
11256         },
11257 
11258         /**
11259          * @private
11260          * Gets the REST class for the current object - this is the workflows class.
11261          */
11262         getRestClass: function () {
11263             return Workflows;
11264         },
11265 
11266         /**
11267          * @private
11268          * Gets the REST class for the objects that make up the collection. - this
11269          * is the workflow class.
11270          */
11271         getRestItemClass: function () {
11272             return Workflow;
11273         },
11274 
11275         /**
11276          * @private
11277          * Gets the REST type for the current object - this is a "workflows".
11278          */
11279         getRestType: function () {
11280             return "Workflows";
11281         },
11282 
11283         /**
11284          * @private
11285          * Gets the REST type for the objects that make up the collection - this is "workflows".
11286          */
11287         getRestItemType: function () {
11288             return "Workflow";
11289         },
11290 
11291         /**
11292          * @private
11293          * Override default to indicates that the collection supports making requests.
11294          */
11295         supportsRequests: true,
11296 
11297         /**
11298          * @private
11299          * Override default to indicates that the collection does not subscribe to its objects.
11300          */
11301         supportsRestItemSubscriptions: false,
11302 
11303         /**
11304          * @private
11305          * Retrieve the workflows. This call will re-query the server and refresh the collection.
11306          *
11307          * @returns {finesse.restservices.workflows}
11308          *     This workflows object to allow cascading.
11309          */
11310         get: function () {
11311             // set loaded to false so it will rebuild the collection after the get
11312             this._loaded = false;
11313             // reset collection
11314             this._collection = {};
11315             // perform get
11316             this._synchronize();
11317             return this;
11318         }
11319     });
11320 
11321     window.finesse = window.finesse || {};
11322     window.finesse.restservices = window.finesse.restservices || {};
11323     window.finesse.restservices.Workflows = Workflows;
11324         
11325     return Workflows;
11326 });
11327 
11328 /**
11329  * JavaScript representation of the Finesse MediaPropertiesLayout object for the Admin webapp.
11330  * @requires finesse.clientservices.ClientServices
11331  * @requires Class
11332  * @requires finesse.FinesseBase
11333  * @requires finesse.restservices.RestBase
11334  */
11335 
11336 /** The following comment is to prevent jslint errors about 
11337  * using variables before they are defined.
11338  */
11339 /*global finesse*/
11340 
11341 /**
11342  * @class
11343  * JavaScript representation of a MediaPropertiesLayout object for the Admin webapp. Also exposes
11344  * methods to operate on the object against the server.
11345  *
11346  * @constructor
11347  * @param {String} id
11348  *     Not required...
11349  * @param {Object} callbacks
11350  *     An object containing callbacks for instantiation and runtime
11351  * @param {Function} callbacks.onLoad(this)
11352  *     Callback to invoke upon successful instantiation, passes in MediaPropertiesLayout object
11353  * @param {Function} callbacks.onLoadError(rsp)
11354  *     Callback to invoke on instantiation REST request error
11355  *     as passed by finesse.clientservices.ClientServices.ajax()
11356  *     {
11357  *         status: {Number} The HTTP status code returned
11358  *         content: {String} Raw string of response
11359  *         object: {Object} Parsed object of response
11360  *         error: {Object} Wrapped exception that was caught
11361  *         error.errorType: {String} Type of error that was caught
11362  *         error.errorMessage: {String} Message associated with error
11363  *     }
11364  * @param {Function} callbacks.onChange(this)
11365  *     Callback to invoke upon successful update, passes in MediaPropertiesLayout object
11366  * @param {Function} callbacks.onError(rsp)
11367  *     Callback to invoke on update error (refresh or event)
11368  *     as passed by finesse.clientservices.ClientServices.ajax()
11369  *     {
11370  *         status: {Number} The HTTP status code returned
11371  *         content: {String} Raw string of response
11372  *         object: {Object} Parsed object of response
11373  *         error: {Object} Wrapped exception that was caught
11374  *         error.errorType: {String} Type of error that was caught
11375  *         error.errorMessage: {String} Message associated with error
11376  *     }
11377  */
11378 
11379 /** @private */
11380 define('restservices/MediaPropertiesLayout',['restservices/RestBase'], function (RestBase) {
11381     var MediaPropertiesLayout = RestBase.extend(/** @lends finesse.restservices.MediaPropertiesLayout.prototype */{
11382 
11383         /**
11384          * @class
11385          * The MediaPropertiesLayout handles which call variables are associated with Dialogs.
11386          * 
11387          * @augments finesse.restservices.RestBase
11388          * @see finesse.restservices.Dialog#getMediaProperties
11389          * @see finesse.restservices.User#getMediaPropertiesLayout
11390          * @constructs
11391          */
11392         _fakeConstuctor: function () {
11393             /* This is here to hide the real init constructor from the public docs */
11394         },
11395         
11396         /**
11397          * @private
11398          * JavaScript representation of a MediaPropertiesLayout object. Also exposes
11399          * methods to operate on the object against the server.
11400          *
11401          * @param {Object} options
11402          *     An object with the following properties:<ul>
11403          *         <li><b>id:</b> The id of the object being constructed</li>
11404          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
11405          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
11406          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
11407          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
11408          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
11409          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
11410          *             <li><b>content:</b> {String} Raw string of response</li>
11411          *             <li><b>object:</b> {Object} Parsed object of response</li>
11412          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
11413          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
11414          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
11415          *             </ul></li>
11416          *         </ul></li>
11417          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
11418          **/
11419         init: function (options) {
11420             this._super(options);
11421         },
11422 
11423         /**
11424          * @private
11425          * Gets the REST class for the current object - this is the MediaPropertiesLayout object.
11426          */
11427         getRestClass: function () {
11428             return MediaPropertiesLayout;
11429         },
11430 
11431         /**
11432          * @private
11433          * Gets the REST type for the current object - this is a "MediaPropertiesLayout".
11434          */
11435         getRestType: function () {
11436             return "MediaPropertiesLayout";
11437         },
11438 
11439         /**
11440          * @private
11441          * Returns whether this object supports subscriptions
11442          */
11443         supportsSubscriptions: false,
11444 
11445         /**
11446          * Getter for the name.
11447          * @returns {String} The name.
11448          */
11449         getName: function () {
11450             this.isLoaded();
11451             return this._data.name;
11452         },
11453 
11454         /**
11455          * Getter for the description.
11456          * @returns {String} The description.
11457          */
11458         getDescription: function () {
11459             this.isLoaded();
11460             return this._data.description || "";
11461         },
11462 
11463         /**
11464          * Getter for the layout type (should be DEFAULT or CUSTOM).
11465          * @returns {String} The layout type.
11466          */
11467         getType: function () {
11468             this.isLoaded();
11469             return this._data.type || "";
11470         },
11471 
11472         /**
11473          * Retrieve the media properties layout. This call will re-query the server and refresh the layout object.
11474          * @returns {finesse.restservices.MediaPropertiesLayout}
11475          *     This MediaPropertiesLayout object to allow cascading
11476          */
11477         get: function () {
11478             this._synchronize();
11479 
11480             return this; //Allow cascading
11481         },
11482 
11483         /**
11484          * Gets the data for this object.
11485          * 
11486          * Performs safe conversion from raw API data to ensure that the returned layout object
11487          * always has a header with correct entry fields, and exactly two columns with lists of entries.
11488          *
11489          * @returns {finesse.restservices.MediaPropertiesLayout.Object} Data in columns (unless only one defined).
11490          */
11491         getData: function () {
11492 
11493             var layout = this._data, result, _addColumnData;
11494 
11495             result = this.getEmptyData();
11496             result.name = layout.name;
11497             result.description = layout.description;
11498             result.type = layout.type;
11499 
11500             /**
11501              * @private
11502              */
11503             _addColumnData = function (entryData, colIndex) {
11504 
11505                 if (!entryData) {
11506                     //If there's no entry data at all, rewrite entryData to be an empty collection of entries
11507                     entryData = {};
11508                 } else if (entryData.mediaProperty) {
11509                     //If entryData contains the keys for a single entry rather than being a collection of entries,
11510                     //rewrite it to be a collection containing a single entry
11511                     entryData = { "": entryData };
11512                 }
11513 
11514                 //Add each of the entries in the list to the column
11515                 jQuery.each(entryData, function (i, entryData) {
11516 
11517                     //If the entry has no displayName specified, explicitly set it to the empty string
11518                     if (!entryData.displayName) {
11519                         entryData.displayName = "";
11520                     }
11521 
11522                     result.columns[colIndex].push(entryData);
11523 
11524                 });
11525 
11526             };
11527 
11528             //The header should only contain a single entry
11529             if (layout.header && layout.header.entry) {
11530 
11531                 //If the entry has no displayName specified, explicitly set it to the empty string
11532                 if (!layout.header.entry.displayName) {
11533                     layout.header.entry.displayName = "";
11534                 }
11535 
11536                 result.header = layout.header.entry;
11537 
11538             } else {
11539 
11540                 throw "MediaPropertiesLayout.getData() - Header does not contain an entry";
11541 
11542             }
11543 
11544             //If the column object contains an entry object that wasn't part of a list of entries,
11545             //it must be a single right-hand entry object (left-hand entry object would be part of a list.)
11546             //Force the entry object to be the 2nd element in an otherwise-empty list.
11547             if (layout.column && layout.column.entry) {
11548                 layout.column = [
11549                     null,
11550                     { "entry": layout.column.entry }
11551                 ];
11552             }
11553 
11554             if (layout.column && layout.column.length > 0 && layout.column.length <= 2) {
11555 
11556                 //Render left column entries
11557                 if (layout.column[0] && layout.column[0].entry) {
11558                     _addColumnData(layout.column[0].entry, 0);
11559                 }
11560 
11561                 //Render right column entries
11562                 if (layout.column[1] && layout.column[1].entry) {
11563                     _addColumnData(layout.column[1].entry, 1);
11564                 }
11565 
11566             }
11567 
11568             return result;
11569 
11570         },
11571 
11572         /**
11573          * @private
11574          * Empty/template version of getData().
11575          *
11576          * Used by getData(), and by callers of getData() in error cases.
11577          */
11578         getEmptyData: function () {
11579 
11580             return {
11581                 header : {
11582                     displayName: null,
11583                     mediaProperty: null
11584                 },
11585                 columns : [[], []]
11586             };
11587 
11588         },
11589 
11590         /**
11591          * Update the layout of this MediaPropertiesLayout
11592          * @param {Object} layout
11593          *      The object representation of the layout you are setting
11594          * @param {finesse.interfaces.RequestHandlers} handlers
11595          *      An object containing the handlers for the request
11596          * @returns {finesse.restservices.MediaPropertiesLayout}
11597          *      This MediaPropertiesLayout object to allow cascading
11598          * @private
11599          */
11600         update: function (newLayoutObject, handlers) {
11601             var contentBody = {};
11602 
11603             // Make sure type is kept the same
11604             newLayoutObject.type = this.getType();
11605 
11606             contentBody[this.getRestType()] = newLayoutObject;
11607 
11608             //Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
11609             handlers = handlers || {};
11610 
11611             this.restRequest(this.getRestUrl(), {
11612                 method: 'PUT',
11613                 success: handlers.success,
11614                 error: handlers.error,
11615                 content: contentBody
11616             });
11617 
11618             return this; // Allow cascading
11619         },
11620 
11621         /**
11622          * Create a new MediaPropertiesLayout object with the layout passed in
11623          * @param {Object} layout
11624          *      The object representation of the layout you are creating
11625          * @param {finesse.interfaces.RequestHandlers} handlers
11626          *      An object containing the handlers for the request
11627          * @returns {finesse.restservices.MediaPropertiesLayout}
11628          *      This MediaPropertiesLayout object to allow cascading
11629          * @private
11630          */
11631         add: function (layout, handlers) {
11632             var contentBody = {};
11633 
11634             contentBody[this.getRestType()] = layout;
11635 
11636             handlers = handlers || {};
11637 
11638             this.restRequest(this.getRestUrl(), {
11639                 method: 'POST',
11640                 success: handlers.success,
11641                 error: handlers.error,
11642                 content: contentBody
11643             });
11644 
11645             return this; // Allow cascading
11646         },
11647 
11648         /**
11649          * Delete this MediaPropertiesLayout
11650          * @param {finesse.interfaces.RequestHandlers} handlers
11651          *      An object containing the handlers for the request
11652          * @returns {finesse.restservices.MediaPropertiesLayout}
11653          *      This MediaPropertiesLayout object to allow cascading
11654          * @private
11655          */
11656         "delete": function (handlers) {
11657             handlers = handlers || {};
11658 
11659             this.restRequest(this.getRestUrl(), {
11660                 method: 'DELETE',
11661                 success: handlers.success,
11662                 error: handlers.error,
11663                 content: undefined
11664             });
11665 
11666             return this; // Allow cascading
11667         }
11668 
11669     });
11670     
11671     MediaPropertiesLayout.Object = /** @lends finesse.restservices.MediaPropertiesLayout.Object.prototype */ {
11672         /**
11673          * @class Format of MediaPropertiesLayout Object.<br>
11674          * Object { <ul>
11675          *      <li>header : { <ul>
11676          *          <li>dispayName {String} 
11677          *          <li>mediaProperty {String}</ul>}
11678          *      <li>columns : { <ul>
11679          *          <li>[ [] , [] ]
11680          *          </ul>
11681          *      where column arrays consists of the same Object format as header.<br>
11682          *          }</ul>
11683          *      }<br>         
11684          * @constructs
11685          */
11686         _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
11687         
11688     };
11689 
11690 	window.finesse = window.finesse || {};
11691     window.finesse.restservices = window.finesse.restservices || {};
11692     window.finesse.restservices.MediaPropertiesLayout = MediaPropertiesLayout;
11693     
11694     return MediaPropertiesLayout;
11695 });
11696 
11697 /**
11698  * JavaScript representation of the Finesse MediaPropertiesLayout object for a User
11699  *
11700  * @requires MediaPropertiesLayout
11701  * @requires ClientServices
11702  * @requires finesse.FinesseBase
11703  * @requires finesse.restservices.RestBase
11704  */
11705 
11706 /** The following comment is to prevent jslint errors about 
11707  * using variables before they are defined.
11708  */
11709 /*global finesse*/
11710 
11711 /** @private */
11712 define('restservices/UserMediaPropertiesLayout',['restservices/MediaPropertiesLayout'], function (MediaPropertiesLayout) {
11713      var UserMediaPropertiesLayout = MediaPropertiesLayout.extend(/** @lends finesse.restservices.UserMediaPropertiesLayout.prototype */{
11714 
11715 		/**
11716 		 * @class
11717 		 * JavaScript representation of a UserMediaPropertiesLayout collection object. Also exposes
11718 		 * methods to operate on the object against the server.
11719 		 * 
11720 		 * @param {Object} options
11721 		 * An object with the following properties:<ul>
11722 		 *        <li><b>id:</b> The id of the object being constructed</li>
11723 		 *        <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
11724 		 *        <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
11725 		 *        <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
11726 		 *        <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
11727 		 *        <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
11728 		 *            <li><b>status:</b> {Number} The HTTP status code returned</li>
11729 		 *            <li><b>content:</b> {String} Raw string of response</li>
11730 		 *            <li><b>object:</b> {Object} Parsed object of response</li>
11731 		 *            <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
11732 		 *                <li><b>errorType:</b> {String} Type of error that was caught</li>
11733 		 *                <li><b>errorMessage:</b> {String} Message associated with error</li>
11734 		 *            </ul></li>
11735 		 *        </ul></li>
11736 		 *        <li><b>parentObj: (optional)</b> The parent object</li></ul>
11737 		 * @constructs
11738 		**/
11739 		init: function (options) {
11740 		    this._super(options);
11741 		},
11742 		
11743 		/**
11744 		 * @private
11745 		 * Gets the REST class for the current object - this is the UserMediaPropertiesLayout class.
11746 		 */
11747 		getRestClass: function () {
11748 		    return UserMediaPropertiesLayout;
11749 		},
11750 
11751         /**
11752          * Overrides the parent class.  Returns the url for the UserMediaPropertiesLayout resource
11753          */
11754         getRestUrl: function () {
11755             return ("/finesse/api/User/" + this.getId() + "/" + this.getRestType());
11756         },
11757 
11758         /**
11759          * @private
11760          * Override to throw an error because we cannot do an update on the User's
11761          * MediaPropertiesLayout node
11762          */
11763         update: function (layout, handlers) {
11764             throw new Error("update(): Cannot update layout for User's MediaPropertiesLayout");
11765         },
11766 
11767         /**
11768          * @private
11769          * Override to throw an error because we cannot create a new layout on the User's
11770          * MediaPropertiesLayout node
11771          */
11772         add: function (layout, handlers) {
11773             throw new Error("add(): Cannot create a new layout for User's MediaPropertiesLayout");
11774         },
11775 
11776         /**
11777          * @private
11778          * Override to throw an error because we cannot delete the layout on the User's
11779          * MediaPropertiesLayout node
11780          */
11781         "delete": function (layout, handlers) {
11782             throw new Error("delete(): Cannot delete the layout for User's MediaPropertiesLayout");
11783         }
11784 
11785     });
11786 	
11787 	window.finesse = window.finesse || {};
11788     window.finesse.restservices = window.finesse.restservices || {};
11789     window.finesse.restservices.UserMediaPropertiesLayout = UserMediaPropertiesLayout;
11790     
11791     return UserMediaPropertiesLayout;
11792 });
11793 
11794 /**
11795 * JavaScript representation of the Finesse mediaPropertiesLayouts collection
11796 * object which contains a list of mediaPropertiesLayout objects.
11797  *
11798  * @requires finesse.clientservices.ClientServices
11799  * @requires Class
11800  * @requires finesse.FinesseBase
11801  * @requires finesse.restservices.RestBase
11802  * @requires finesse.restservices.Dialog
11803  * @requires finesse.restservices.RestCollectionBase
11804  */
11805 
11806 /** @private */
11807 define('restservices/MediaPropertiesLayouts',[
11808     'restservices/RestCollectionBase',
11809     'restservices/RestBase',
11810     'restservices/MediaPropertiesLayout'
11811 ],
11812 function (RestCollectionBase, RestBase, MediaPropertiesLayout) {
11813 
11814     var MediaPropertiesLayouts = RestCollectionBase.extend({
11815 
11816         /**
11817          * @class
11818          * JavaScript representation of a mediaPropertiesLayouts collection object. Also exposes
11819          * methods to operate on the object against the server.
11820          *
11821          * @param {Object} options
11822          *     An object with the following properties:<ul>
11823          *         <li><b>id:</b> The id of the object being constructed</li>
11824          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
11825          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
11826          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
11827          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
11828          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
11829          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
11830          *             <li><b>content:</b> {String} Raw string of response</li>
11831          *             <li><b>object:</b> {Object} Parsed object of response</li>
11832          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
11833          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
11834          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
11835          *             </ul></li>
11836          *         </ul></li>
11837          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
11838          *  @constructs
11839          **/
11840         init: function (options) {
11841             this._super(options);
11842         },
11843 
11844         /**
11845          * @private
11846          * Gets the REST class for the current object - this is the mediaPropertiesLayouts class.
11847          */
11848         getRestClass: function () {
11849             return MediaPropertiesLayouts;
11850         },
11851 
11852         /**
11853          * @private
11854          * Gets the REST class for the objects that make up the collection. - this
11855          * is the mediaPropertiesLayout class.
11856          */
11857         getRestItemClass: function () {
11858             return MediaPropertiesLayout;
11859         },
11860 
11861         /**
11862          * @private
11863          * Gets the REST type for the current object - this is a "mediaPropertiesLayouts".
11864          */
11865         getRestType: function () {
11866             return "MediaPropertiesLayouts";
11867         },
11868 
11869         /**
11870          * @private
11871          * Gets the REST type for the objects that make up the collection - this is "mediaPropertiesLayouts".
11872          */
11873         getRestItemType: function () {
11874             return "MediaPropertiesLayout";
11875         },
11876 
11877         /**
11878          * @private
11879          * Override default to indicates that the collection supports making requests.
11880          */
11881         supportsRequests: true,
11882 
11883         /**
11884          * @private
11885          * Override default to indicates that the collection does not subscribe to its objects.
11886          */
11887         supportsRestItemSubscriptions: false,
11888 
11889         /**
11890          * @private
11891          * Retrieve the MediaPropertiesLayouts. This call will re-query the server and refresh the collection.
11892          *
11893          * @returns {finesse.restservices.MediaPropertiesLayouts}
11894          *     This MediaPropertiesLayouts object to allow cascading.
11895          */
11896         get: function () {
11897             // set loaded to false so it will rebuild the collection after the get
11898             this._loaded = false;
11899             // reset collection
11900             this._collection = {};
11901             // perform get
11902             this._synchronize();
11903             return this;
11904         }
11905     });
11906 
11907     window.finesse = window.finesse || {};
11908     window.finesse.restservices = window.finesse.restservices || {};
11909     window.finesse.restservices.MediaPropertiesLayouts = MediaPropertiesLayouts;
11910         
11911     return MediaPropertiesLayouts;
11912 });
11913 
11914 /**
11915  * JavaScript representation of the Finesse MediaPropertiesLayout object for a User
11916  *
11917  * @requires MediaPropertiesLayout
11918  * @requires ClientServices
11919  * @requires finesse.FinesseBase
11920  * @requires finesse.restservices.RestBase
11921  */
11922 
11923 /** The following comment is to prevent jslint errors about 
11924  * using variables before they are defined.
11925  */
11926 /*global finesse*/
11927 
11928 /** @private */
11929 define('restservices/UserMediaPropertiesLayouts',[
11930 	'restservices/MediaPropertiesLayouts',
11931 	'restservices/UserMediaPropertiesLayout'
11932 ],
11933 function (MediaPropertiesLayouts, UserMediaPropertiesLayout) {
11934      var UserMediaPropertiesLayouts = MediaPropertiesLayouts.extend(/** @lends finesse.restservices.UserMediaPropertiesLayouts.prototype */{
11935 
11936 		/**
11937 		 * @class
11938 		 * JavaScript representation of a UserMediaPropertiesLayouts collection object. Also exposes
11939 		 * methods to operate on the object against the server.
11940 		 * 
11941 		 * @param {Object} options
11942 		 * An object with the following properties:<ul>
11943 		 *        <li><b>id:</b> The id of the object being constructed</li>
11944 		 *        <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
11945 		 *        <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
11946 		 *        <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
11947 		 *        <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
11948 		 *        <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
11949 		 *            <li><b>status:</b> {Number} The HTTP status code returned</li>
11950 		 *            <li><b>content:</b> {String} Raw string of response</li>
11951 		 *            <li><b>object:</b> {Object} Parsed object of response</li>
11952 		 *            <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
11953 		 *                <li><b>errorType:</b> {String} Type of error that was caught</li>
11954 		 *                <li><b>errorMessage:</b> {String} Message associated with error</li>
11955 		 *            </ul></li>
11956 		 *        </ul></li>
11957 		 *        <li><b>parentObj: (optional)</b> The parent object</li></ul>
11958 		 * @constructs
11959 		**/
11960 		init: function (options) {
11961 		    this._super(options);
11962 		},
11963 		
11964 		/**
11965 		 * @private
11966 		 * Gets the REST class for the current object - this is the UserMediaPropertiesLayouts class.
11967 		 */
11968 		getRestClass: function () {
11969 		    return UserMediaPropertiesLayouts;
11970 		},
11971 
11972         /**
11973          * @private
11974          * Gets the REST class for the objects that make up the collection. - this
11975          * is the UserMediaPropertiesLayout class.
11976          */
11977 		getRestItemClass: function() {
11978 			return UserMediaPropertiesLayout;
11979 		}
11980     });
11981 	
11982 	window.finesse = window.finesse || {};
11983     window.finesse.restservices = window.finesse.restservices || {};
11984     window.finesse.restservices.UserMediaPropertiesLayouts = UserMediaPropertiesLayouts;
11985     
11986     return UserMediaPropertiesLayouts;
11987 });
11988 
11989 /**
11990  * JavaScript representation of the Finesse Dialog object for non-voice media.
11991  *
11992  * @requires finesse.restservices.DialogBase
11993  */
11994 
11995 /** @private */
11996 define('restservices/MediaDialog',[
11997         'restservices/DialogBase'
11998     ],
11999     function (DialogBase) {
12000         var MediaDialog = DialogBase.extend(/** @lends finesse.restservices.MediaDialog.prototype */{
12001 
12002             /**
12003              * @private
12004              *
12005              * Support requests so that applications can refresh non-voice dialogs when the media channel that the
12006              * dialog belongs to is interrupted. An event is not sent to update a dialog's actions when the media is
12007              * interrupted so a refresh is required so that the application can get an updated set of actions.
12008              */
12009             supportsRequests: true,
12010 
12011             /**
12012              * @class
12013              * A MediaDialog is an attempted connection between or among multiple participants,
12014              * for example, a chat or email.
12015              *
12016              * @augments finesse.restservices.DialogBase
12017              * @constructs
12018              */
12019             _fakeConstuctor: function () {
12020                 /* This is here to hide the real init constructor from the public docs */
12021             },
12022 
12023             /**
12024              * @private
12025              *
12026              * @param {Object} options
12027              *     An object with the following properties:<ul>
12028              *         <li><b>id:</b> The id of the object being constructed</li>
12029              *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
12030              *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
12031              *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
12032              *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
12033              *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
12034              *             <li><b>status:</b> {Number} The HTTP status code returned</li>
12035              *             <li><b>content:</b> {String} Raw string of response</li>
12036              *             <li><b>object:</b> {Object} Parsed object of response</li>
12037              *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
12038              *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
12039              *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
12040              *             </ul></li>
12041              *         </ul></li>
12042              *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
12043              **/
12044             init: function (options) {
12045                 this._super(options);
12046             },
12047 
12048             /**
12049              * @private
12050              * Gets the REST class for the current object - this is the MediaDialog class.
12051              * @returns {Object} The Dialog class.
12052              */
12053             getRestClass: function () {
12054                 return MediaDialog;
12055             },
12056 
12057             /**
12058              * Transfers a Media Dialog to the target specified
12059              * @param {String} target script selector
12060              *     The script selector to transfer the dialog.
12061              * @param {finesse.interfaces.RequestHandlers} handlers
12062              *     An object containing the handlers for the request
12063              */
12064             transfer: function(target, handlers) {
12065                 this.setTaskState(MediaDialog.TaskActions.TRANSFER, handlers, target);
12066             },
12067 
12068             /**
12069              * Set the state on a Media Dialog based on the action given.
12070              * @param {finesse.restservices.MediaDialog.TaskActions} action
12071              *     The action string indicating the action to invoke on a Media dialog.
12072              * @param {finesse.interfaces.RequestHandlers} handlers
12073              *     An object containing the handlers for the request
12074              * @param {String} target
12075              *    The target to transfer the dialog.  Pass null if not transfer
12076              */
12077             setTaskState: function (state,handlers,target) {
12078                 this.isLoaded();
12079 
12080                 var contentBody = {};
12081                 contentBody[this.getRestType()] = {
12082                     "requestedAction": state,
12083                     "target": target
12084                 };
12085                 // (nonexistent) keys to be read as undefined
12086                 handlers = handlers || {};
12087                 this.restRequest(this.getRestUrl(), {
12088                     method: 'PUT',
12089                     success: handlers.success,
12090                     error: handlers.error,
12091                     content: contentBody
12092                 });
12093                 return this; // Allow cascading
12094             }
12095 
12096         });
12097 
12098         MediaDialog.TaskActions = /** @lends finesse.restservices.MediaDialog.TaskActions.prototype */ {
12099             /**
12100              * Accept an incoming task.
12101              */
12102             ACCEPT: "ACCEPT",
12103             /**
12104              * Start work on a task.
12105              */
12106             START : "START",
12107             /**
12108              * Pause work on an active task.
12109              */
12110             PAUSE: "PAUSE",
12111             /**
12112              * Resume work on a paused task.
12113              */
12114             RESUME : "RESUME",
12115             /**
12116              * Wrap up work for a task.
12117              */
12118             WRAP_UP : "WRAP_UP",
12119             /**
12120              * Transfer task to another target.
12121              */
12122             TRANSFER : "TRANSFER",
12123             /**
12124              * End a task.
12125              */
12126             CLOSE : "CLOSE",
12127             /**
12128              * @class Set of action constants for a Media Dialog.  These should be used for
12129              * {@link finesse.restservices.MediaDialog#setTaskState}.
12130              * @constructs
12131              */
12132             _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
12133         };
12134 
12135 
12136 
12137         MediaDialog.States = /** @lends finesse.restservices.MediaDialog.States.prototype */ {
12138             /**
12139              * Indicates that the task has been offered to an agent.
12140              */
12141             OFFERED: "OFFERED",
12142             /**
12143              * Indicates that the user has started work on the task.
12144              */
12145             ACTIVE: "ACTIVE",
12146             /**
12147              * Indicates that the user has paused work on the task.
12148              */
12149             PAUSED: "PAUSED",
12150             /**
12151              * Indicates that the user is wrapping up the task.
12152              */
12153             WRAPPING_UP: "WRAPPING_UP",
12154             /**
12155              * Indicates that the task was interrupted.
12156              */
12157             INTERRUPTED: "INTERRUPTED",
12158             /**
12159              * Indicates that the task has ended.
12160              */
12161             CLOSED: "CLOSED",
12162             /**
12163              * Indicates that the user has accepted the task.
12164              */
12165             ACCEPTED: "ACCEPTED",
12166             /**
12167              * Finesse has recovered a task after a failure. It does not have enough information to build a complete set
12168              * of actions for the task so it only allows the user to end the task.
12169              */
12170             UNKNOWN: "UNKNOWN",
12171             /**
12172              * @class Possible Dialog State constants.
12173              * The State flow of a typical in-bound Dialog is as follows: OFFERED, ACCEPTED, ACTIVE, CLOSED.
12174              * @constructs
12175              */
12176             _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
12177         };
12178 
12179         MediaDialog.ParticipantStates = MediaDialog.States;
12180 
12181         window.finesse = window.finesse || {};
12182         window.finesse.restservices = window.finesse.restservices || {};
12183         window.finesse.restservices.MediaDialog = MediaDialog;
12184 
12185 
12186         return MediaDialog;
12187     });
12188 
12189 /* Simple JavaScript Inheritance
12190  * By John Resig http://ejohn.org/
12191  * MIT Licensed.
12192  */
12193 // Inspired by base2 and Prototype
12194 define('restservices/../../thirdparty/Class',[], function () {
12195         var initializing = false, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;
12196         // The base Class implementation (does nothing)
12197         /** @private */
12198         Class = function(){};
12199         
12200         // Create a new Class that inherits from this class
12201         /** @private */
12202         Class.extend = function(prop) {
12203           var _super = this.prototype;
12204           
12205           // Instantiate a base class (but only create the instance,
12206           // don't run the init constructor)
12207           initializing = true;
12208           var prototype = new this();
12209           initializing = false;
12210           
12211           // Copy the properties over onto the new prototype
12212           for (var name in prop) {
12213             // Check if we're overwriting an existing function
12214             prototype[name] = typeof prop[name] == "function" && 
12215               typeof _super[name] == "function" && fnTest.test(prop[name]) ?
12216               (function(name, fn){
12217                 return function() {
12218                   var tmp = this._super;
12219                   
12220                   // Add a new ._super() method that is the same method
12221                   // but on the super-class
12222                   this._super = _super[name];
12223                   
12224                   // The method only need to be bound temporarily, so we
12225                   // remove it when we're done executing
12226                   var ret = fn.apply(this, arguments);        
12227                   this._super = tmp;
12228                   
12229                   return ret;
12230                 };
12231               })(name, prop[name]) :
12232               prop[name];
12233           }
12234           
12235           // The dummy class constructor
12236           /** @private */
12237           function Class() {
12238             // All construction is actually done in the init method
12239             if ( !initializing && this.init )
12240               this.init.apply(this, arguments);
12241           }
12242           
12243           // Populate our constructed prototype object
12244           Class.prototype = prototype;
12245           
12246           // Enforce the constructor to be what we expect
12247           Class.prototype.constructor = Class;
12248 
12249           // And make this class extendable
12250           Class.extend = arguments.callee;
12251           
12252           return Class;
12253         };
12254     return Class;
12255 });
12256 
12257 /**
12258  * Class used to establish a Media/Dialogs subscription to be shared by MediaDialogs objects for non-voice
12259  * dialog events.
12260  *
12261  * @requires Class
12262  * @requires finesse.clientservices.ClientServices
12263  * @requires finesse.clientservices.Topics
12264  */
12265 /** @private */
12266 define('restservices/MediaDialogsSubscriptionManager',[
12267         "../../thirdparty/Class",
12268         "clientservices/ClientServices",
12269         "clientservices/Topics"
12270     ],
12271     function (Class, ClientServices, Topics) {
12272         var MediaDialogsSubscriptionManager = Class.extend(/** @lends finesse.restservices.MediaDialogsSubscriptionManager.prototype */{
12273 
12274             /**
12275              * Map used to track the MediaDialogs objects managed by this object.
12276              * @private
12277              */
12278             _mediaDialogsMap: {},
12279 
12280             /**
12281              * The regex used to match the source of BOSH/XMPP events. If an event matches this source, the event will
12282              * be processed by the subscription manager.
12283              * @private
12284              */
12285             _sourceRegEx: null,
12286 
12287             /**
12288              * The subscription ID/handle for Media/Dialogs events.
12289              * @private
12290              */
12291             _subscriptionId: null,
12292 
12293             _fakeConstuctor: function ()
12294             {
12295                 /* This is here to hide the real init constructor from the public docs */
12296             },
12297 
12298             /**
12299              * Create the regex used to match the source of BOSH/XMPP events. If an event matches this source, the event
12300              * will be processed by the subscription manager.
12301              *
12302              * @param {Object} restObj
12303              *     The restObj whose REST URL will be used as the base of the regex.
12304              *
12305              * @returns {RegExp}
12306              *     The regex used to match the source of XMPP events.
12307              * @private
12308              */
12309             _makeSourceRegEx: function(restObj)
12310             {
12311                 return new RegExp("^" + restObj.getRestUrl() + "/Media/[0-9]+/Dialogs$");
12312             },
12313 
12314             /**
12315              * Return the media ID associated with the update.
12316              *
12317              * @param {Object} update
12318              *     The content of the update event.
12319              *
12320              * @returns {String}
12321              *     The media ID associated with the update.
12322              * @private
12323              */
12324             _getMediaIdFromEventUpdate: function(update)
12325             {
12326                 var parts = update.object.Update.source.split("/");
12327                 return parts[MediaDialogsSubscriptionManager.MEDIA_ID_INDEX_IN_SOURCE];
12328             },
12329 
12330             /**
12331              * Handler for update events. This handler forwards the update to the MediaDialogs object associated with
12332              * the media ID carried in the update event.
12333              *
12334              * @param {Object} update
12335              *     The content of the update event.
12336              *
12337              * @private
12338              */
12339             _updateEventHandler: function(update)
12340             {
12341                 var mediaId = this._getMediaIdFromEventUpdate(update),
12342                     mediaDialogs = this._mediaDialogsMap[mediaId];
12343 
12344                 if ( mediaDialogs )
12345                 {
12346                     mediaDialogs._updateEventHandler(mediaDialogs, update);
12347                 }
12348             },
12349 
12350             /**
12351              * Return the media ID associated with the REST update.
12352              *
12353              * @param {Object} update
12354              *     The content of the REST update.
12355              *
12356              * @returns {String}
12357              *     The media ID associated with the update.
12358              * @private
12359              */
12360             _getMediaIdFromRestUpdate: function(update)
12361             {
12362                 return update.object.Update.data.dialog.mediaProperties.mediaId;
12363             },
12364 
12365             /**
12366              * Handler for REST updates. This handler forwards the update to the MediaDialogs object associated with
12367              * the media ID carried in the REST update.
12368              *
12369              * @param {Object} update
12370              *     The content of the REST update.
12371              *
12372              * @private
12373              */
12374             _processRestItemUpdate: function(update)
12375             {
12376                 var mediaId = this._getMediaIdFromRestUpdate(update),
12377                     mediaDialogs = this._mediaDialogsMap[mediaId];
12378 
12379                 if ( mediaDialogs )
12380                 {
12381                     mediaDialogs._processRestItemUpdate(update);
12382                 }
12383             },
12384 
12385             /**
12386              * Utility method to create a callback to be given to OpenAjax to invoke when a message
12387              * is published on the topic of our REST URL (also XEP-0060 node).
12388              * This needs to be its own defined method so that subclasses can have their own implementation.
12389              * @returns {Function} callback(update)
12390              *     The callback to be invoked when an update event is received. This callback will
12391              *     process the update by notifying the MediaDialogs object associated with the media ID in the update.
12392              *
12393              * @private
12394              */
12395             _createPubsubCallback: function ()
12396             {
12397                 var _this = this;
12398                 return function (update) {
12399                     //If the source of the update is our REST URL, this means the collection itself is modified
12400                     if (update.object.Update.source.match(_this._sourceRegEx)) {
12401                         _this._updateEventHandler(update);
12402                     } else {
12403                         //Otherwise, it is safe to assume that if we got an event on our topic, it must be a
12404                         //rest item update of one of our children that was published on our node (OpenAjax topic)
12405                         _this._processRestItemUpdate(update);
12406                     }
12407                 };
12408             },
12409 
12410             /**
12411              * Track the MediaDialogs object so that events and REST updates signalled to this subscription manager
12412              * can be forwarded to the given MediaDialogs object.
12413              * @param {finesse.restservices.MediaDialogs} mediaDialogs MediaDialogs object to be tracked by the
12414              *     subscription manager.
12415              * @private
12416              */
12417             _manage: function(mediaDialogs)
12418             {
12419                 this._mediaDialogsMap[mediaDialogs.getMedia().getMediaId()] = mediaDialogs;
12420             },
12421 
12422             /**
12423              * Stop tracking the MediaDialogs object. Events and REST updates signalled to this subscription manager
12424              * will no longer be forwarded to the given MediaDialogs object.
12425              * @param {finesse.restservices.MediaDialogs} mediaDialogs MediaDialogs object to no longer track.
12426              * @private
12427              */
12428             _unManage: function(mediaDialogs)
12429             {
12430                 var mediaId = mediaDialogs.getMedia().getMediaId();
12431                 if ( this._callbackMap[mediaId] )
12432                 {
12433                     delete this._callbackMap[mediaId];
12434                 }
12435             },
12436 
12437             /**
12438              * @class
12439              * An internal class used to establish a Media/Dialogs subscription to be shared by MediaDialogs objects
12440              * for non-voice dialog events.
12441              *
12442              * @constructor
12443              * @param {RestBase} restObj
12444              *     A RestBase object used to build the user portion of XMPP and REST paths.
12445              * @constructs
12446              */
12447             init: function (restObj)
12448             {
12449                 var _this;
12450 
12451                 this._sourceRegEx = this._makeSourceRegEx(restObj);
12452             },
12453 
12454             /**
12455              * Create the BOSH/XMPP subscription used for non-voice dialog events. Additionally, store the given
12456              * MediaDialogs object so that events for the object can be forwarded to it.
12457              *
12458              * @param {finesse.restservices.MediaDialogs} mediaDialogs a MediaDialogs object to manage (forward events)
12459              * @param {Object} callbacks an object containing success and error callbacks used to signal the result of
12460              *     the subscription.
12461              * @returns {MediaDialogsSubscriptionManager}
12462              * @private
12463              */
12464             subscribe: function (mediaDialogs, callbacks)
12465             {
12466                 var topic = Topics.getTopic(mediaDialogs.getXMPPNodePath()),
12467                     _this = this,
12468                     handlers,
12469                     successful;
12470 
12471                 callbacks = callbacks || {};
12472 
12473                 handlers = {
12474                     /** @private */
12475                     success: function () {
12476                         // Add item to the refresh list in ClientServices to refresh if
12477                         // we recover due to our resilient connection.
12478                         ClientServices.addToRefreshList(_this);
12479                         if (typeof callbacks.success === "function") {
12480                             callbacks.success();
12481                         }
12482                     },
12483                     /** @private */
12484                     error: function (err) {
12485                         if (successful) {
12486                             _this._unManage(mediaDialogs);
12487                             ClientServices.unsubscribe(topic);
12488                         }
12489 
12490                         if (typeof callbacks.error === "function") {
12491                             callbacks.error(err);
12492                         }
12493                     }
12494                 };
12495 
12496                 this._manage(mediaDialogs);
12497                 if ( this._subscriptionId )
12498                 {
12499                     successful = true;
12500                 }
12501                 else
12502                 {
12503                     successful = ClientServices.subscribe(topic, this._createPubsubCallback(), true);
12504                     if ( successful )
12505                     {
12506                         this._subscriptionId = "OpenAjaxOnly";
12507                     }
12508                 }
12509 
12510                 if (successful) {
12511                     handlers.success();
12512                 } else {
12513                     handlers.error();
12514                 }
12515 
12516                 return this;
12517             }
12518         });
12519 
12520         MediaDialogsSubscriptionManager.MEDIA_ID_INDEX_IN_SOURCE = 6;
12521 
12522         window.finesse = window.finesse || {};
12523         window.finesse.restservices = window.finesse.restservices || {};
12524         window.finesse.restservices.MediaDialogsSubscriptionManager = MediaDialogsSubscriptionManager;
12525 
12526         return MediaDialogsSubscriptionManager;
12527     });
12528 
12529 /**
12530  * JavaScript representation of the Finesse MediaDialogs collection
12531  * object which contains a list of Dialog objects.
12532  *
12533  * @requires finesse.clientservices.ClientServices
12534  * @requires Class
12535  * @requires finesse.FinesseBase
12536  * @requires finesse.restservices.RestBase
12537  * @requires finesse.restservices.Dialogs
12538  * @requires finesse.restservices.MediaDialogsSubscriptionManager
12539  */
12540 /** @private */
12541 define('restservices/MediaDialogs',[
12542     'restservices/RestCollectionBase',
12543     'restservices/RestBase',
12544     'restservices/Dialogs',
12545     'restservices/MediaDialog',
12546     'restservices/MediaDialogsSubscriptionManager'
12547 ],
12548 function (RestCollectionBase, RestBase, Dialogs, MediaDialog, MediaDialogsSubscriptionManager) {
12549     var MediaDialogs = Dialogs.extend(/** @lends finesse.restservices.MediaDialogs.prototype */{
12550 
12551         /**
12552          * @class
12553          * JavaScript representation of a collection of Dialogs for a specific non-voice Media.
12554          * @augments finesse.restservices.Dialogs
12555          * @constructs
12556          * @see finesse.restservices.Dialog
12557          * @example
12558          *  _MediaDialogs = _media.getMediaDialogs( {
12559          *      onCollectionAdd : _handleDialogAdd,
12560          *      onCollectionDelete : _handleDialogDelete,
12561          *      onLoad : _handleMediaDialogsLoaded
12562          *  });
12563          *  
12564          * _dialogCollection = _MediaDialogs.getCollection();
12565          * for (var dialogId in _dialogCollection) {
12566          *     if (_dialogCollection.hasOwnProperty(dialogId)) {
12567          *         _dialog = _dialogCollection[dialogId];
12568          *         etc...
12569          *     }
12570          * }
12571          */
12572         _fakeConstuctor: function () {
12573             /* This is here to hide the real init constructor from the public docs */
12574         },
12575         
12576         /**
12577          * @private
12578          * @param {Object} options
12579          *     An object with the following properties:<ul>
12580          *         <li><b>id:</b> The id of the object being constructed</li>
12581          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
12582          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
12583          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
12584          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
12585          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
12586          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
12587          *             <li><b>content:</b> {String} Raw string of response</li>
12588          *             <li><b>object:</b> {Object} Parsed object of response</li>
12589          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
12590          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
12591          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
12592          *             </ul></li>
12593          *         </ul></li>
12594          *         <li><b>parentObj:</b> The parent object</li></ul>
12595          *         <li><b>mediaObj:</b> The media object</li></ul>
12596          **/
12597         init: function (options) {
12598             this._mediaObj = options.mediaObj;
12599             this._super(options);
12600         },
12601 
12602         getMedia: function() {
12603             return this._mediaObj;
12604         },
12605 
12606         /**
12607          * @private
12608          * Gets the REST class for the objects that make up the collection. - this
12609          * is the Dialog class.
12610          */
12611         getRestItemClass: function () {
12612             return MediaDialog;
12613         },
12614 
12615         /**
12616          * @private
12617          * Gets the node path for the current object - this is the media node
12618          * @returns {String} The node path
12619          */
12620         getXMPPNodePath: function () {
12621             var
12622                 restObj = this._restObj,
12623                 nodePath = "";
12624 
12625             //Prepend the base REST object if one was provided.
12626             if (restObj instanceof RestBase) {
12627                 nodePath += restObj.getRestUrl();
12628             }
12629             //Otherwise prepend with the default webapp name.
12630             else {
12631                 nodePath += "/finesse/api";
12632             }
12633             
12634             //Append the REST type.
12635             nodePath += "/" + this.getRestType() + "/Media";
12636             return nodePath;
12637         },
12638         
12639         /**
12640          * The REST URL in which this object can be referenced.
12641          * @return {String}
12642          *     The REST URI for this object.
12643          * @private
12644          */
12645         getRestUrl: function () {
12646             var
12647             restObj = this._mediaObj,
12648             restUrl = "";
12649 
12650             //Prepend the base REST object if one was provided.
12651             if (restObj instanceof RestBase) {
12652                 restUrl += restObj.getRestUrl();
12653             }
12654             //Otherwise prepend with the default webapp name.
12655             else {
12656                 restUrl += "/finesse/api";
12657             }
12658 
12659             //Append the REST type.
12660             restUrl += "/" + this.getRestType();
12661 
12662             //Append ID if it is not undefined, null, or empty.
12663             if (this._id) {
12664                 restUrl += "/" + this._id;
12665             }
12666             return restUrl;
12667         },
12668 
12669         /**
12670          * Overridden so that MediaDialogsSubscriptionManager can be used to share events across media dialogs.
12671          *
12672          * @param {Object} callbacks
12673          *     An object containing success and error handlers for the subscription request.
12674          * @private
12675          */
12676         subscribe: function (callbacks)
12677         {
12678             if ( !MediaDialogs.subscriptionManager )
12679             {
12680                 MediaDialogs.subscriptionManager = new MediaDialogsSubscriptionManager(this._restObj);
12681             }
12682 
12683             MediaDialogs.subscriptionManager.subscribe(this, callbacks);
12684 
12685             return this;
12686         }
12687     });
12688 
12689     MediaDialogs.subscriptionManager = /** @lends finesse.restservices.MediaDialogsSubscriptionManager.prototype */ null;
12690 
12691     window.finesse = window.finesse || {};
12692     window.finesse.restservices = window.finesse.restservices || {};
12693     window.finesse.restservices.MediaDialogs = MediaDialogs;
12694     
12695     return MediaDialogs;
12696 });
12697 
12698 /**
12699  * Allows gadgets to call the log function to publish client logging messages over the hub.
12700  *
12701  * @requires OpenAjax
12702  */
12703 /** @private */
12704 define('cslogger/ClientLogger',[], function () {
12705 
12706     var ClientLogger = ( function () { /** @lends finesse.cslogger.ClientLogger.prototype */
12707         var _hub, _logTopic, _originId, _sessId, _host,
12708             MONTH = { 0 : "Jan", 1 : "Feb", 2 : "Mar", 3 : "Apr", 4 : "May", 5 : "Jun", 
12709                       6 : "Jul", 7 : "Aug", 8 : "Sep", 9 : "Oct", 10 : "Nov", 11 : "Dec"},
12710  
12711         /**
12712          * Gets timestamp drift stored in sessionStorage
12713          * @returns drift in seconds if it is set in sessionStorage otherwise returns undefined.
12714          * @private
12715         */
12716         getTsDrift = function() {
12717             if (window.sessionStorage.getItem('clientTimestampDrift') !== null) {
12718                 return parseInt(window.sessionStorage.getItem('clientTimestampDrift'), 10);
12719             }
12720             else { 
12721                 return undefined;
12722             }
12723         },
12724          
12725         /**
12726           * Sets timestamp drift in sessionStorage
12727           * @param delta is the timestamp drift between server.and client.
12728           * @private
12729          */
12730         setTsDrift = function(delta) {
12731              window.sessionStorage.setItem('clientTimestampDrift', delta.toString());
12732         },
12733           
12734         /**
12735          * Gets Finesse server timezone offset from GMT in seconds 
12736          * @returns offset in seconds if it is set in sessionStorage otherwise returns undefined.
12737          * @private
12738         */
12739         getServerOffset = function() {
12740             if (window.sessionStorage.getItem('serverTimezoneOffset') !== null) {
12741                 return parseInt(window.sessionStorage.getItem('serverTimezoneOffset'), 10);
12742             }
12743             else { 
12744                 return undefined;
12745             }
12746         },
12747          
12748         /**
12749           * Sets server timezone offset 
12750           * @param sec is the server timezone GMT offset in seconds.
12751           * @private
12752          */
12753         setServerOffset = function(sec) {
12754              window.sessionStorage.setItem('serverTimezoneOffset', sec.toString());
12755         },
12756  
12757         /**
12758          * Checks to see if we have a console.
12759          * @returns Whether the console object exists.
12760          * @private
12761          */
12762         hasConsole = function () {
12763             try {
12764                 if (window.console !== undefined) {
12765                     return true;
12766                 }
12767             } 
12768             catch (err) {
12769               // ignore and return false
12770             }
12771     
12772             return false;
12773         },
12774         
12775         /**
12776          * Gets a short form (6 character) session ID from sessionStorage
12777          * @private
12778         */
12779         getSessId = function() {
12780             if (!_sessId) {
12781                //when _sessId not defined yet, initiate it
12782                if (window.sessionStorage.getItem('enableLocalLog') === 'true') {
12783                   _sessId= " "+window.sessionStorage.getItem('finSessKey');
12784                }
12785                else {
12786                   _sessId=" ";
12787                }
12788             }
12789             return _sessId;
12790          },
12791 
12792         /**
12793          * Pads a single digit number for display purposes (e.g. '4' shows as '04')
12794          * @param num is the number to pad to 2 digits
12795          * @returns a two digit padded string
12796          * @private
12797          */
12798         padTwoDigits = function (num)
12799         {
12800             return (num < 10) ? '0' + num : num;
12801         },
12802         
12803         /**
12804          * Pads a single digit number for display purposes (e.g. '4' shows as '004')
12805          * @param num is the number to pad to 3 digits
12806          * @returns a three digit padded string
12807          * @private
12808          */
12809         padThreeDigits = function (num)
12810         {
12811             if (num < 10)
12812             {
12813               return '00'+num;
12814             }
12815             else if (num < 100)
12816             {
12817               return '0'+num;
12818             }
12819             else  
12820             {
12821                return num;
12822             }
12823         },
12824               
12825         /**
12826          * Compute the "hour"
12827          * 
12828          * @param s is time in seconds
12829          * @returns {String} which is the hour
12830          * @private
12831          */
12832         ho = function (s) {
12833              return ((s/60).toString()).split(".")[0];
12834         },
12835           
12836         /**
12837          * Gets local timezone offset string.
12838          * 
12839          * @param t is the time in seconds
12840          * @param s is the separator character between hours and minutes, e.g. ':'
12841          * @returns {String} is local timezone GMT offset in the following format: [+|-]hh[|:]MM
12842          * @private
12843          */
12844         getGmtOffString = function (min,s) {
12845             var t, sign;
12846             if (min<0) {
12847                t = -min;
12848                sign = "-";
12849             }
12850             else {
12851                t = min;
12852                sign = "+";
12853             }
12854             
12855             if (s===':') {
12856                 return sign+padTwoDigits(ho(t))+s+padTwoDigits(t%60);
12857             }
12858             else {
12859                 return sign+padTwoDigits(ho(t))+padTwoDigits(t%60);
12860             }    
12861         },
12862 
12863         /**
12864          * Gets short form of a month name in English 
12865          * 
12866          * @param monthNum is zero-based month number 
12867          * @returns {String} is short form of month name in English
12868          * @private
12869          */
12870         getMonthShortStr = function (monthNum) {
12871             var result;
12872             try {
12873                 result = MONTH[monthNum];
12874             } 
12875             catch (err) {
12876                 if (hasConsole()) {
12877                     window.console.log("Month must be between 0 and 11");
12878                 }
12879             }
12880             return result;
12881         },
12882           
12883         /**
12884           * Gets a timestamp.
12885           * @param aDate is a javascript Date object
12886           * @returns {String} is a timestamp in the following format: yyyy-mm-ddTHH:MM:ss.SSS [+|-]HH:MM
12887           * @private
12888           */
12889         getDateTimeStamp = function (aDate)
12890         {
12891             var date, off, timeStr;
12892             if (aDate === null) {
12893                 date = new Date();
12894             }
12895             else {
12896                 date = aDate;
12897             }
12898             off = -1*date.getTimezoneOffset();
12899             timeStr = date.getFullYear().toString() + "-" +
12900                       padTwoDigits(date.getMonth()+1) + "-" +
12901                       padTwoDigits (date.getDate()) + "T"+
12902                       padTwoDigits(date.getHours()) + ":" + 
12903                       padTwoDigits(date.getMinutes()) + ":" +
12904                       padTwoDigits(date.getSeconds())+"." + 
12905                       padThreeDigits(date.getMilliseconds()) + " "+
12906                       getGmtOffString(off, ':');
12907     
12908             return timeStr;
12909         },
12910         
12911         /**
12912          * Gets drift-adjusted timestamp.
12913          * @param aTimestamp is a timestamp in milliseconds
12914          * @param drift is a timestamp drift in milliseconds
12915          * @param serverOffset is a timezone GMT offset in minutes
12916          * @returns {String} is a timestamp in the Finesse server log format, e.g. Jan 07 2104 HH:MM:ss.SSS -0500
12917          * @private
12918          */
12919         getDriftedDateTimeStamp = function (aTimestamp, drift, serverOffset)
12920         {
12921            var date, timeStr, localOffset;
12922            if (aTimestamp === null) {
12923                return "--- -- ---- --:--:--.--- -----";
12924            }
12925            else if (drift === undefined || serverOffset === undefined) {
12926                if (hasConsole()) {
12927                    window.console.log("drift or serverOffset must be a number");
12928                }
12929                return "--- -- ---- --:--:--.--- -----";
12930            }
12931            else {
12932                //need to get a zone diff in minutes
12933                localOffset = (new Date()).getTimezoneOffset();
12934                date = new Date(aTimestamp+drift+(localOffset+serverOffset)*60000);
12935                timeStr = getMonthShortStr(date.getMonth()) + " "+
12936                          padTwoDigits (date.getDate())+ " "+
12937                          date.getFullYear().toString() + " "+
12938                          padTwoDigits(date.getHours()) + ":" + 
12939                          padTwoDigits(date.getMinutes()) + ":" +
12940                          padTwoDigits(date.getSeconds())+"." + 
12941                          padThreeDigits(date.getMilliseconds())+" "+
12942                          getGmtOffString(serverOffset, '');
12943                 return timeStr;
12944             }
12945         },
12946     
12947         /**
12948         * Logs a message to a hidden textarea element on the page
12949         *
12950         * @param msg is the string to log.
12951         * @private
12952         */
12953         writeToLogOutput = function (msg) {
12954             var logOutput = document.getElementById("finesseLogOutput");
12955     
12956             if (logOutput === null)
12957             {
12958                 logOutput = document.createElement("textarea");
12959                 logOutput.id = "finesseLogOutput";
12960                 logOutput.style.display = "none";
12961                 document.body.appendChild(logOutput);
12962             }
12963     
12964             if (logOutput.value === "")
12965             {
12966                 logOutput.value = msg;
12967             }
12968             else
12969             {
12970                 logOutput.value = logOutput.value + "\n" + msg;
12971             }
12972         },
12973 
12974         /*
12975          * Logs a message to console 
12976         * @param str is the string to log.         * @private
12977          */
12978         logToConsole = function (str)
12979         {
12980             var now, msg, timeStr, driftedTimeStr, sessKey=getSessId();
12981             now = new Date();
12982             timeStr = getDateTimeStamp(now);
12983             if (getTsDrift() !== undefined) {
12984                 driftedTimeStr = getDriftedDateTimeStamp(now.getTime(), getTsDrift(), getServerOffset());
12985             }
12986             else {
12987                driftedTimeStr = getDriftedDateTimeStamp(null, 0, 0);
12988             }
12989             msg = timeStr + ":"+sessKey+": "+ _host + ": "+driftedTimeStr+ ": " + str;
12990             // Log to console
12991             if (hasConsole()) {
12992                 window.console.log(msg);
12993             }
12994     
12995             //Uncomment to print logs to hidden textarea.
12996             //writeToLogOutput(msg);
12997     
12998             return msg;
12999         };
13000         return {
13001     
13002             /**
13003              * Publishes a Log Message over the hub.
13004              *
13005              * @param {String} message
13006              *     The string to log.
13007              * @example
13008              * _clientLogger.log("This is some important message for MyGadget");
13009              * 
13010              */
13011             log : function (message) {
13012                 if(_hub) {
13013                     _hub.publish(_logTopic, logToConsole(_originId + message));
13014                 }
13015             },
13016             
13017             /**
13018              * @class
13019              * Allows gadgets to call the log function to publish client logging messages over the hub.
13020              * 
13021              * @constructs
13022              */
13023             _fakeConstuctor: function () {
13024                 /* This is here so we can document init() as a method rather than as a constructor. */
13025             },
13026             
13027             /**
13028              * Initiates the client logger with a hub a gadgetId and gadget's config object.
13029              * @param {Object} hub
13030              *      The hub to communicate with.
13031              * @param {String} gadgetId
13032              *      A unique string to identify which gadget is doing the logging.
13033              * @param {finesse.gadget.Config} config
13034              *      The config object used to get host name for that thirdparty gadget
13035              * @example
13036              * var _clientLogger = finesse.cslogger.ClientLogger;
13037              * _clientLogger.init(gadgets.Hub, "MyGadgetId", config);
13038              * 
13039              */
13040             init: function (hub, gadgetId, config) {
13041                 _hub = hub;
13042                 _logTopic = "finesse.clientLogging." + gadgetId;
13043                 _originId = gadgetId + " : ";
13044                 if ((config === undefined) || (config === "undefined")) 
13045                 {
13046                      _host = ((finesse.container && finesse.container.Config && finesse.container.Config.host)?finesse.container.Config.host : "?.?.?.?");
13047                  } 
13048                 else 
13049                 {
13050                      _host = ((config && config.host)?config.host : "?.?.?.?");
13051                  }
13052             }
13053         };
13054     }());
13055     
13056     window.finesse = window.finesse || {};
13057     window.finesse.cslogger = window.finesse.cslogger || {};
13058     window.finesse.cslogger.ClientLogger = ClientLogger;
13059     
13060     finesse = finesse || {};
13061     /** @namespace Supports writing messages to a central log. */
13062     finesse.cslogger = finesse.cslogger || {};
13063 
13064     return ClientLogger;
13065 });
13066 
13067 /**
13068  * Utility class used to recover a media object after recovering from a connection or system failure.
13069  *
13070  * @requires Class
13071  * @requires finesse.restservices.Notifier
13072  * @requires finesse.clientservices.ClientServices
13073  * @requires finesse.restservices.Media
13074  */
13075 
13076 /** @private */
13077 define('restservices/MediaOptionsHelper',['../../thirdparty/Class',
13078         '../utilities/Utilities',
13079         '../clientservices/ClientServices',
13080         '../cslogger/ClientLogger'
13081 ],
13082 function (Class, Utilities, ClientServices, ClientLogger)
13083 {
13084     /**
13085      * Utility class used to synchronize media login options after recovering from a connection or system failure. This
13086      * class will ensure that the Finesse server that the application fails over to has the same maxDialogLimit,
13087      * interruptAction, and dialogLogoutAction as the previous Finesse server.
13088      */
13089     var MediaOptionsHelper = Class.extend(/** @lends finesse.restservices.MediaOptionsHelper.prototype */
13090     {
13091         /**
13092          * @private
13093          *
13094          * The media that this helper is responsible for recovering in case of failover.
13095          */
13096         _media: null,
13097 
13098         /**
13099          * @private
13100          *
13101          * The media options that this helper will ensure are set properly across failures.
13102          */
13103         _mediaOptions: null,
13104 
13105         /**
13106          * @private
13107          *
13108          * The current state of the failover recovery.
13109          */
13110         _state: null,
13111 
13112         /**
13113          * @class
13114          *
13115          * Utility class used to synchronize media login options after recovering from a connection or system failure. This
13116          * class will ensure that the Finesse server that the application fails over to has the same maxDialogLimit,
13117          * interruptAction, and dialogLogoutAction as the previous Finesse server.
13118          *
13119          * @constructs
13120          */
13121         _fakeConstuctor: function ()
13122         {
13123             /* This is here to hide the real init constructor from the public docs */
13124         },
13125 
13126         /**
13127          * Utility method to format a message logged by an instance of this class.
13128          *
13129          * @param {string} message the message to format
13130          * @returns {string} the given message prefixed with the name of this class and the ID of the Media object
13131          *      associated with this class.
13132          * @private
13133          */
13134         _formatLogMessage: function(message)
13135         {
13136             return "MediaOptionsHelper[" + this.media.getMediaId() + "]: " + message;
13137         },
13138 
13139         /**
13140          * Utility method to log an informational message.
13141          *
13142          * Note that this method piggy-backs on the logger setup by the gadget. If the gadget does not initialize
13143          * logger, this class will not log.
13144          *
13145          * @param {string} message the message to log
13146          * @private
13147          */
13148         _log: function(message)
13149         {
13150             ClientLogger.log(this._formatLogMessage(message));
13151         },
13152 
13153         /**
13154          * Utility method to log an error message.
13155          *
13156          * Note that this method piggy-backs on the logger setup by the gadget. If the gadget does not initialize
13157          * logger, this class will not log.
13158          *
13159          * @param {string} message the message to log
13160          * @private
13161          */
13162         _error: function(message)
13163         {
13164             ClientLogger.error(this._formatLogMessage(message));
13165         },
13166 
13167         /**
13168          * @private
13169          *
13170          * Set the running state of this failover helper.
13171          *
13172          * @param {String} newState the new state of the failover helper.
13173          */
13174         _setState: function(newState)
13175         {
13176             this._state = newState;
13177             this._log("changed state to " + this._state);
13178         },
13179 
13180         /**
13181          * Check the given media object to see if the maxDialogLimit, interruptAction, and dialogLogoutAction options
13182          * need to be reset. These options need to be reset if the application specified login options and any of the
13183          * following conditions are true:<ul>
13184          *     <li>the dialogLogoutAction in the given media object does not match the action set by the application</li>
13185          *     <li>the interruptAction in the given media object does not match the action set by the application</li>
13186          *     <li>the maxDialogLimit in the given media object does not match the limit set by the application</li></ul>
13187          *
13188          * @param {Object} media the media object to evaluate
13189          * @returns {*|{}|boolean} true if a login request should be sent to correct the media options
13190          * @private
13191          */
13192         _shouldLoginToFixOptions: function(media)
13193         {
13194             return this._mediaOptions
13195                 && media.isLoggedIn()
13196                 && (media.getDialogLogoutAction() !== this._mediaOptions.dialogLogoutAction
13197                     || media.getInterruptAction() !== this._mediaOptions.interruptAction
13198                     || media.getMaxDialogLimit() !== this._mediaOptions.maxDialogLimit);
13199         },
13200 
13201         /**
13202          * @private
13203          *
13204          * Determine if the given response is an "agent already logged in" error.
13205          *
13206          * @param {Object} response the response to evaluate
13207          *
13208          * @returns {boolean} true if
13209          */
13210         _agentIsAlreadyLoggedIn: function(response)
13211         {
13212             return response
13213                 && response.object
13214                 && response.object.ApiErrors
13215                 && response.object.ApiErrors.ApiError
13216                 && response.object.ApiErrors.ApiError.ErrorMessage === "E_ARM_STAT_AGENT_ALREADY_LOGGED_IN";
13217         },
13218 
13219         /**
13220          * Determine if the given response to a media login request is successful. A response is successful under these
13221          * conditions:<ul>
13222          *     <li>the response has a 202 status</li>
13223          *     <li>the response has a 400 status and the error indicates the agent is already logged in</li>
13224          *     </ul>
13225          *
13226          * @param {Object} loginResponse the response to evaluate
13227          *
13228          * @returns {*|boolean} true if the response status is 202 or if the response status is 400 and the error states
13229          *      that the agent is already logged in.
13230          * @private
13231          */
13232         _isSuccessfulLoginResponse: function(loginResponse)
13233         {
13234             return loginResponse && ((loginResponse.status === 202) || this._agentIsAlreadyLoggedIn(loginResponse));
13235         },
13236 
13237         /**
13238          * Process a media load or change while in the connected state. This involves checking the media options to
13239          * ensure they are the same as those set by the application.
13240          *
13241          * @param {Object} media the media object that was loaded or changed.
13242          * @private
13243          */
13244         _processConnectedState: function(media)
13245         {
13246             var _this = this, processResponse;
13247 
13248             if ( this._shouldLoginToFixOptions(media) )
13249             {
13250                 processResponse = function(response)
13251                 {
13252                     _this._setState(MediaOptionsHelper.States.MONITORING_OPTIONS);
13253 
13254                     if ( !_this._isSuccessfulLoginResponse(response) )
13255                     {
13256                         _this._error("failed to reset options: " + response.status + ": " + response.content);
13257                     }
13258                 };
13259 
13260                 this._setState(MediaOptionsHelper.States.SETTING_OPTIONS);
13261 
13262                 this._log("logging in to fix options");
13263 
13264                 this.media.login({
13265                     dialogLogoutAction: _this._mediaOptions.dialogLogoutAction,
13266                     interruptAction: _this._mediaOptions.interruptAction,
13267                     maxDialogLimit: _this._mediaOptions.maxDialogLimit,
13268                     handlers: {
13269                         success: processResponse,
13270                         error: processResponse
13271                     }
13272                 });
13273             }
13274         },
13275 
13276         /**
13277          * Process a media load or change while in the resetting options state. All that is done in this state is log a
13278          * message that a reset is already in progress.
13279          *
13280          * @param {Object} media the media object that was loaded or changed.
13281          * @private
13282          */
13283         _processResettingOptionsState: function(media)
13284         {
13285             this._log("Resetting options is in progress");
13286         },
13287 
13288         /**
13289          * Initialize a helper class used to recover media objects following connectivity or component failures related
13290          * to Finesse and/or CCE services.
13291          *
13292          * Initialize the failover helper to manage the recovery of the given media object.
13293          *
13294          * @param {Object} media the media object to recover
13295          * @param {Object} mediaOptions an object containing the media options used by the application:<ul>
13296          *         <li><b>maxDialogLimit:</b> The id of the object being constructed</li>
13297          *         <li><b>interruptAction:</b> Accept or ignore interrupts</li>
13298          *         <li><b>dialogLogoutAction:</b> transfer or close the task at logout time</li></ul>
13299          */
13300         init: function (media, mediaOptions)
13301         {
13302             var _this = this, processMediaStateChange = function(media)
13303             {
13304                 switch ( _this._state )
13305                 {
13306                     case MediaOptionsHelper.States.MONITORING_OPTIONS:
13307                         _this._processConnectedState(media);
13308                         break;
13309                     case MediaOptionsHelper.States.SETTING_OPTIONS:
13310                         _this._processResettingOptionsState(media);
13311                         break;
13312                     default:
13313                         _this._error("unexpected state: " + _this.state);
13314                         break;
13315                 }
13316             };
13317 
13318             this.media = media;
13319 
13320             this._mediaOptions = mediaOptions || {};
13321 
13322             // The maxDialogLimit is a string in media events. Ensure _mediaOptions.maxDialogLimit is a string to
13323             // make sure it can be compared to the maxDialogLimit field in media events.
13324             //
13325             if ( this._mediaOptions.maxDialogLimit )
13326             {
13327                 this._mediaOptions.maxDialogLimit = this._mediaOptions.maxDialogLimit.toString();
13328             }
13329 
13330             // Add the media object to the refresh list so that ClientServices calls refresh on the media object when
13331             // the connection is reestablished. This will trigger processMediaStateChange() which will trigger a login
13332             // to restore media options if media options are different.
13333             //
13334             ClientServices.addToRefreshList(this.media);
13335 
13336             this._setState(MediaOptionsHelper.States.MONITORING_OPTIONS);
13337 
13338             this.media.addHandler('load', processMediaStateChange);
13339             this.media.addHandler('change', processMediaStateChange);
13340 
13341             this._log("initialized");
13342         }
13343     });
13344 
13345     /**
13346      * @private
13347      *
13348      * The states that a running MediaOptionsHelper executes.
13349      *
13350      * @type {{CONNECTED: string, RECOVERING: string, RECOVERY_LOGIN: string, _fakeConstructor: _fakeConstructor}}
13351      */
13352     MediaOptionsHelper.States = /** @lends finesse.restservices.MediaOptionsHelper.States.prototype */ {
13353 
13354         /**
13355          * The media is synchronized with the Finesse server. Media options are being monitored for changes.
13356          */
13357         MONITORING_OPTIONS: "MONITORING_OPTIONS",
13358 
13359         /**
13360          * A media login request has been sent to Finesse to set the correct media options.
13361          */
13362         SETTING_OPTIONS: "SETTING_OPTIONS",
13363 
13364         /**
13365          * @class Possible MediaOptionsHelper state values.
13366          * @constructs
13367          */
13368         _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
13369     };
13370 
13371     window.finesse = window.finesse || {};
13372     window.finesse.restservices = window.finesse.restservices || {};
13373     window.finesse.restservices.MediaOptionsHelper = MediaOptionsHelper;
13374 
13375     return MediaOptionsHelper;
13376 });
13377 /**
13378  * JavaScript representation of the Finesse Media object
13379  *
13380  * @requires finesse.clientservices.ClientServices
13381  * @requires Class
13382  * @requires finesse.FinesseBase
13383  * @requires finesse.restservices.RestBase
13384  * @requires finesse.restservices.MediaDialogs
13385  * @requires finesse.restservices.MediaOptionsHelper
13386  */
13387 
13388 /** @private */
13389 define('restservices/Media',[
13390     'restservices/RestBase',
13391     'restservices/MediaDialogs',
13392     'restservices/MediaOptionsHelper'
13393 ],
13394 function (RestBase, MediaDialogs, MediaOptionsHelper) {
13395     var Media = RestBase.extend(/** @lends finesse.restservices.Media.prototype */{
13396 
13397         /**
13398          * Media objects support GET REST requests.
13399          */
13400         supportsRequests: true,
13401 
13402         /**
13403          * @private
13404          * The list of dialogs associated with this media.
13405          */
13406         _mdialogs : null,
13407 
13408         /**
13409          * @private
13410          * The options used to log into this media.
13411          */
13412         _mediaOptions: null,
13413 
13414         /**
13415          * @private
13416          * Object used to keep the maxDialogLimit, interruptAction, and dialogLogoutAction in-synch across fail-overs.
13417          */
13418         _optionsHelper: null,
13419 
13420         /**
13421          * @class
13422          * A Media represents a non-voice channel,
13423          * for example, a chat or a email.
13424          *
13425          * @augments finesse.restservices.RestBase
13426          * @constructs
13427          */
13428         _fakeConstuctor: function () {
13429             /* This is here to hide the real init constructor from the public docs */
13430         },
13431 
13432         /**
13433          * @private
13434          *
13435          * @param {Object} options
13436          *     An object with the following properties:<ul>
13437          *         <li><b>id:</b> The id of the object being constructed</li>
13438          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
13439          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
13440          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
13441          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
13442          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
13443          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
13444          *             <li><b>content:</b> {String} Raw string of response</li>
13445          *             <li><b>object:</b> {Object} Parsed object of response</li>
13446          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
13447          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
13448          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
13449          *             </ul></li>
13450          *         </ul></li>
13451          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
13452          **/
13453         init: function (options) {
13454             this._super(options);
13455         },
13456 
13457         /**
13458          * @private
13459          * Utility method used to retrieve an attribute from this media object's underlying data.
13460          *
13461          * @param {String} attributeName the name of the attribute to retrieve
13462          * @returns {String} the value of the attribute or undefined if the attribute is not found
13463          */
13464         _getDataAttribute: function(attributeName) {
13465             this.isLoaded();
13466             return this.getData()[attributeName];
13467         },
13468 
13469         /**
13470          * @private
13471          * Gets the REST class for the current object - this is the Media class.
13472          * @returns {Object} The Media class.
13473          */
13474         getRestClass: function () {
13475             return Media;
13476         },
13477         
13478         /**
13479          * @private
13480          * Gets the REST type for the current object - this is a "Media".
13481          * @returns {String} The Media string.
13482          */
13483         getRestType: function () {
13484             return "Media";
13485         },
13486 
13487         /**
13488          * @private
13489          * Getter for the uri.
13490          * @returns {String} The uri.
13491          */
13492         getMediaUri: function () {
13493             return this._getDataAttribute('uri');
13494         },
13495 
13496         /**
13497          * Getter for the id.
13498          * @returns {String} The id.
13499          */
13500         getId: function () {
13501             return this._getDataAttribute('id');
13502         },
13503 
13504         /**
13505          * Getter for the name.
13506          * @returns {String} The name.
13507          */
13508         getName: function () {
13509             return this._getDataAttribute('name');
13510         },
13511 
13512         /**
13513          * Getter for the reason code id.
13514          * @returns {String} The reason code id.
13515          */
13516         getReasonCodeId: function () {
13517             return this._getDataAttribute('reasonCodeId');
13518         },
13519 
13520         /**
13521          * Getter for the reason code label.
13522          * @returns {String} The reason code label.
13523          */
13524         getReasonCodeLabel: function() {
13525             this.isLoaded();
13526             if (this.getData().reasonCode) {
13527                 return this.getData.reasonCode.label;
13528             }
13529             return "";
13530         },
13531 
13532         /**
13533          * Getter for the action to be taken in the event this media is interrupted. The action will be one of the
13534          * following:<ul>
13535          *     <li><b>ACCEPT:</b> the interrupt will be accepted and the agent will not work on tasks in this media
13536          *     until the media is no longer interrupted.</li>
13537          *     <li><b>IGNORE:</b> the interrupt will be ignored and the agent is allowed to work on the task while the
13538          *     media is interrupted.</li></ul>
13539          * @returns {*|Object}
13540          */
13541         getInterruptAction: function() {
13542             return this._getDataAttribute('interruptAction');
13543         },
13544 
13545         /**
13546          * Getter for the action to be taken in the event the agent logs out with dialogs associated with this media.
13547          * The action will be one of the following:<ul>
13548          *     <li><b>CLOSE:</b> the dialog will be closed.</li>
13549          *     <li><b>TRANSFER:</b> the dialog will be transferred to another agent.</li></ul>
13550          * @returns {*|Object}
13551          */
13552         getDialogLogoutAction: function() {
13553             return this._getDataAttribute('dialogLogoutAction');
13554         },
13555 
13556         /**
13557          * Getter for the state of the User on this Media.
13558          * @returns {String}
13559          *     The current (or last fetched) state of the User on this Media
13560          * @see finesse.restservices.Media.States
13561          */
13562         getState: function() {
13563             return this._getDataAttribute('state');
13564         },
13565 
13566         /**
13567          * Getter for the Media id
13568          * @returns {String} The Media id
13569          */
13570         getMediaId: function() {
13571             return this._getDataAttribute('id');
13572         },
13573 
13574         /**
13575          * Getter for maximum number of dialogs allowed on this Media
13576          * @returns {String} The max number of Dialogs on this Media
13577          */
13578         getMaxDialogLimit: function() {
13579             return this._getDataAttribute('maxDialogLimit');
13580         },
13581 
13582         /**
13583          * Getter for whether or not this media is interruptible
13584          * @returns {Boolean} true if interruptible; false otherwise
13585          */
13586         getInterruptible: function() {
13587             var interruptible = this._getDataAttribute('interruptible');
13588             return interruptible === 'true';
13589         },
13590 
13591         /**
13592          * Is the user interruptible on this Media.
13593          * @returns {Boolean} true if interruptible; false otherwise
13594          */
13595         isInterruptible: function() {
13596             return this.getInterruptible();
13597         },
13598 
13599         /**
13600          * Getter for routable field on this Media
13601          * @returns {Boolean} true if routable, false otherwise
13602          */
13603         getRoutable: function() {
13604             var routable = this._getDataAttribute('routable');
13605             return routable === 'true';
13606         },
13607 
13608         /**
13609          * Is the user routable on this Media.
13610          * @returns {Boolean} true if routable, false otherwise
13611          */
13612         isRoutable: function() {
13613             return this.getRoutable();
13614         },
13615 
13616         /**
13617          * @param {Object} options
13618          *     An object with the following properties:<ul>
13619          *         <li><b>routable:</b> true if the agent is routable, false otherwise</li>
13620          *         <li><b>{finesse.interfaces.RequestHandlers} handlers:</b> An object containing the handlers for the request</li></ul>
13621          * @returns {finesse.restservices.Media}
13622          *     This Media object, to allow cascading
13623          */
13624         setRoutable: function(params) {
13625             var handlers, contentBody = {},
13626                 restType = this.getRestType(),
13627                 url = this.getRestUrl();
13628             params = params || {};
13629 
13630             contentBody[restType] = {
13631                 "routable": params.routable
13632             };
13633 
13634             handlers = params.handlers || {};
13635 
13636             this._makeRequest(contentBody, url, handlers);
13637 
13638             return this;
13639         },
13640 
13641         /**
13642          * @private
13643          * Invoke a request to the server given a content body and handlers.
13644          *
13645          * @param {Object} contentBody
13646          *     A JS object containing the body of the action request.
13647          * @param {finesse.interfaces.RequestHandlers} handlers
13648          *     An object containing the handlers for the request
13649          */
13650         _makeRequest: function (contentBody, url, handlers) {
13651             // Protect against null dereferencing of options allowing its
13652             // (nonexistent) keys to be read as undefined
13653             handlers = handlers || {};
13654 
13655             this.restRequest(url, {
13656                 method: 'PUT',
13657                 success: handlers.success,
13658                 error: handlers.error,
13659                 content: contentBody
13660             });
13661         },
13662 
13663         /**
13664          * Return true if the params object contains one of the following:<ul>
13665          *     <li>maxDialogLimit</li>
13666          *     <li>interruptAction</li>
13667          *     <li>dialogLogoutAction</li></ul>
13668          *
13669          * @param {Object} params the parameters to evaluate
13670          * @returns {*|Boolean}
13671          * @private
13672          */
13673         _containsLoginOptions: function(params) {
13674             return params.maxDialogLimit || params.interruptAction || params.dialogLogoutAction;
13675         },
13676 
13677         /**
13678          * Create login parameters using the given algorithm:<ul>
13679          *     <li>if loginOptions have not be set in the call to MediaList.getMedia(), use the params given by the application</li>
13680          *     <li>if no params were set by the application, use the loginOptions set in the call to MediaList.getMedia()</li>
13681          *     <li>if the parameters given by the application contains login options, use the parameters given by the application</li>
13682          *     <li>if login options were given by the application but callbacks were given, create new login params with the
13683          *     loginOptions set in the call to MediaList.getMedia() and the callbacks specified in the given login params</li></ul>
13684          *
13685          * @param params the parameters specified by the application in the call to Media.login()
13686          *
13687          * @see finesse.restservices.Media#login
13688          * @see finesse.restservices.MediaList#getMedia
13689          *
13690          * @returns {Object} login parameters built based on the algorithm listed above.
13691          * @private
13692          */
13693         _makeLoginOptions: function(params) {
13694             if ( !this._mediaOptions ) {
13695                 // If loginOptions have not be set, use the params given by the application.
13696                 //
13697                 return params;
13698             }
13699 
13700             if ( !params ) {
13701                 // If there were no params given by the application, use the loginOptions set in the call to
13702                 // MediaList.getMedia().
13703                 //
13704                 return this._mediaOptions;
13705             }
13706 
13707             if (  this._containsLoginOptions(params) ) {
13708                 // if the parameters given by the application contains login options, use the parameters given by the
13709                 // application.
13710                 //
13711                 return params;
13712             }
13713 
13714             // If login options were given by the application but callbacks were given, create new login params with the
13715             // loginOptions set in the call to MediaList.getMedia() and the callbacks specified in the given login
13716             // params.
13717             //
13718             return {
13719                 maxDialogLimit: this._mediaOptions.maxDialogLimit,
13720                 interruptAction: this._mediaOptions.interruptAction,
13721                 dialogLogoutAction: this._mediaOptions.dialogLogoutAction,
13722                 handlers: params.handlers
13723             };
13724         },
13725 
13726         /**
13727          * Ensure that the maxDialogLimit, interruptAction, and dialogLogoutAction options are kept in synch across
13728          * fail-overs for this media object.
13729          * @private
13730          */
13731         _ensureOptionsAreInSynch: function() {
13732             if ( !this._optionsHelper && this._mediaOptions ) {
13733                 this._optionsHelper = new MediaOptionsHelper(this, this._mediaOptions);
13734             }
13735         },
13736 
13737         /**
13738          * Log the agent into this media.
13739          *
13740          * @param {Object} options
13741          *     An object with the following properties:<ul>
13742          *         <li><b>maxDialogLimit:</b> The id of the object being constructed</li>
13743          *         <li><b>interruptAction:</b> Accept or ignore interrupts</li>
13744          *         <li><b>dialogLogoutAction:</b> transfer or close the task at logout time</li>
13745          *         <li><b>{finesse.interfaces.RequestHandlers} handlers:</b> An object containing the handlers for the request</li></ul>
13746          *
13747          *     If maxDialogLimit, interruptAction, and dialogLogoutAction are not present, loginOptions specified in the
13748          *     call to finesse.restservices.MediaList.getMedia() will be used.
13749          *
13750          * @see finesse.restservices.MediaList#getMedia
13751          *
13752          * @returns {finesse.restservices.Media}
13753          *     This Media object, to allow cascading
13754          */
13755         login: function(params) {
13756             this.setState(Media.States.LOGIN, null, this._makeLoginOptions(params));
13757             return this; // Allow cascading
13758         },
13759 
13760         /**
13761          * Perform a logout for a user on this media.
13762          * @param {String} reasonCode
13763          *     The reason this user is logging out of this media.  Pass null for no reason.
13764          * @param {finesse.interfaces.RequestHandlers} handlers
13765          *     An object containing the handlers for the request
13766          * @returns {finesse.restservices.Media}
13767          *     This Media object, to allow cascading
13768          */
13769         logout: function(reasonCode, params) {
13770             var state = Media.States.LOGOUT;
13771             return this.setState(state, reasonCode, params);
13772         },
13773 
13774         /**
13775          * Set the state of the user on this Media.
13776          * @param {String} newState
13777          *     The state you are setting
13778          * @param {ReasonCode} reasonCode
13779          *     The reason this user is changing state for this media.  Pass null for no reason.
13780          * @param {finesse.interfaces.RequestHandlers} handlers
13781          *     An object containing the handlers for the request
13782          * @see finesse.restservices.User.States
13783          * @returns {finesse.restservices.Media}
13784          *     This Media object, to allow cascading
13785          */
13786         setState: function(state, reasonCode, params) {
13787             var handlers, reasonCodeId, contentBody = {},
13788                 restType = this.getRestType(),
13789                 url = this.getRestUrl();
13790             params = params || {};
13791 
13792             if(reasonCode) {
13793                 reasonCodeId = reasonCode.id;
13794             }
13795 
13796             contentBody[restType] = {
13797                 "state": state,
13798                 "maxDialogLimit": params.maxDialogLimit,
13799                 "interruptAction": params.interruptAction,
13800                 "dialogLogoutAction": params.dialogLogoutAction,
13801                 "reasonCodeId": reasonCodeId
13802             };
13803 
13804             handlers = params.handlers || {};
13805 
13806             this._makeRequest(contentBody, url, handlers);
13807 
13808             return this;
13809         },
13810 
13811         /**
13812          * Getter for a MediaDialogs collection object that is associated with User on this Media.
13813          * @param {finesse.interfaces.RestObjectHandlers} [handlers] Object that sets callback handlers (only
13814          * applicable when Object has not been previously created).
13815          * @returns {finesse.restservices.MediaDialogs}
13816          *     A MediaDialogs collection object.
13817          */
13818         getMediaDialogs: function (callbacks) {
13819             var options = callbacks || {};
13820             options.parentObj = this._restObj;
13821             options.mediaObj = this;
13822             this.isLoaded();
13823 
13824             if (this._mdialogs === null) {
13825                 this._mdialogs = new MediaDialogs(options);
13826             }
13827 
13828             return this._mdialogs;
13829         },
13830 
13831         /**
13832          * Refresh the dialog collection associated with this media.
13833          */
13834         refreshMediaDialogs: function() {
13835             if ( this._mdialogs ) {
13836                 this._mdialogs.refresh();
13837             }
13838         },
13839 
13840         /**
13841          * Set the maxDialogLimit, interruptAction, and dialogLogoutAction settings that the application will use for
13842          * this media. In the event of a failure, these options will be set on the new Finesse server.
13843          *
13844          * @param {Object} mediaOptions an object with the following properties:<ul>
13845          *         <li><b>maxDialogLimit:</b> The id of the object being constructed</li>
13846          *         <li><b>interruptAction:</b> Accept or ignore interrupts</li>
13847          *         <li><b>dialogLogoutAction:</b> transfer or close the task at logout time</li></ul>
13848          */
13849         setMediaOptions: function(mediaOptions) {
13850             if ( mediaOptions ) {
13851                 this._mediaOptions = mediaOptions;
13852                 if ( !this._optionsHelper ) {
13853                     this._optionsHelper = new MediaOptionsHelper(this, this._mediaOptions);
13854                 }
13855             }
13856         },
13857 
13858         /**
13859          * Refresh this media object and optionally refresh the list of media dialogs associated with this object.
13860          *
13861          * @param {Integer} retries the number of times to retry synchronizing this media object.
13862          */
13863         refresh: function(retries) {
13864             retries = retries || 1;
13865             this._synchronize(retries);
13866             this.refreshMediaDialogs();
13867         },
13868 
13869         /**
13870          * Is the user in work state on this Media.
13871          * @returns {boolean} returns true if the media is in work state; false otherwise
13872          */
13873         isInWorkState: function() {
13874             return this.getState() === Media.States.WORK;
13875         },
13876 
13877         /**
13878          * Is the user in any state except LOGOUT on this Media.
13879          * @returns {boolean} returns true if the agent is in any state except LOGOUT in this media
13880          */
13881         isLoggedIn: function() {
13882             return this.getState() !== Media.States.LOGOUT;
13883         }
13884     });
13885 
13886     Media.States = /** @lends finesse.restservices.Media.States.prototype */ {
13887         /**
13888          * User Login on a non-voice Media.  Note that while this is an action, is not technically a state, since a
13889          * logged-in User will always be in a specific state (READY, NOT_READY, etc.).
13890          */
13891         LOGIN: "LOGIN",
13892         /**
13893          * User is logged out of this Media.
13894          */
13895         LOGOUT: "LOGOUT",
13896         /**
13897          * User is not ready on this Media.
13898          */
13899         NOT_READY: "NOT_READY",
13900         /**
13901          * User is ready for activity on this Media.
13902          */
13903         READY: "READY",
13904         /**
13905          * User has a dialog coming in, but has not accepted it.
13906          */
13907         RESERVED: "RESERVED",
13908         /**
13909          * The dialogs in this media have been interrupted by a dialog in a non-interruptible media.
13910          */
13911         INTERRUPTED: "INTERRUPTED",
13912         /**
13913          * User enters this state when failing over from one Finesse to the other or when failing over from one
13914          * PG side to the other.
13915          */
13916         WORK: "WORK",
13917         /**
13918          * @class Possible Media state values.
13919          * @constructs
13920          */
13921         _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
13922 
13923     };
13924 
13925     window.finesse = window.finesse || {};
13926     window.finesse.restservices = window.finesse.restservices || {};
13927     window.finesse.restservices.Media = Media;
13928 
13929     return Media;
13930 });
13931 /**
13932  * JavaScript representation of the Finesse Media collection
13933  * object which contains a list of Media objects.
13934  *
13935  * @requires finesse.clientservices.ClientServices
13936  * @requires Class
13937  * @requires finesse.FinesseBase
13938  * @requires finesse.restservices.RestBase
13939  * @requires finesse.restservices.Media
13940  */
13941 /** @private */
13942 define('restservices/MediaList',[
13943         'restservices/RestCollectionBase',
13944         'restservices/Media',
13945         'restservices/RestBase',
13946         'utilities/Utilities'
13947     ],
13948     function (RestCollectionBase, Media, RestBase, Utilities) {
13949         var MediaList = RestCollectionBase.extend(/** @lends finesse.restservices.MediaList.prototype */{
13950 
13951             /**
13952              * @class
13953              * JavaScript representation of a MediaList collection object.
13954              * @augments finesse.restservices.RestCollectionBase
13955              * @constructs
13956              * @see finesse.restservices.Media
13957              * @example
13958              *  mediaList = _user.getMediaList( {
13959              *      onCollectionAdd : _handleMediaAdd,
13960              *      onCollectionDelete : _handleMediaDelete,
13961              *      onLoad : _handleMediaListLoaded
13962              *  });
13963              *
13964              * _mediaCollection = mediaList.getCollection();
13965              * for (var mediaId in _mediaCollection) {
13966              *     if (_mediaCollection.hasOwnProperty(mediaId)) {
13967              *         media = _mediaCollection[mediaId];
13968              *         etc...
13969              *     }
13970              * }
13971              */
13972             _fakeConstuctor: function () {
13973                 /* This is here to hide the real init constructor from the public docs */
13974             },
13975 
13976             /**
13977              * @private
13978              * @param {Object} options
13979              *     An object with the following properties:<ul>
13980              *         <li><b>id:</b> The id of the object being constructed</li>
13981              *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
13982              *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
13983              *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
13984              *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
13985              *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
13986              *             <li><b>status:</b> {Number} The HTTP status code returned</li>
13987              *             <li><b>content:</b> {String} Raw string of response</li>
13988              *             <li><b>object:</b> {Object} Parsed object of response</li>
13989              *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
13990              *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
13991              *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
13992              *             </ul></li>
13993              *         </ul></li>
13994              *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
13995              **/
13996             init: function (options) {
13997                 this._super(options);
13998             },
13999 
14000             /**
14001              * @private
14002              * Gets the REST class for the current object - this is the MediaList class.
14003              */
14004             getRestClass: function () {
14005                 return MediaList;
14006             },
14007 
14008             /**
14009              * @private
14010              * Gets the REST class for the objects that make up the collection. - this
14011              * is the Media class.
14012              */
14013             getRestItemClass: function () {
14014                 return Media;
14015             },
14016 
14017             /**
14018              * @private
14019              * Gets the REST type for the current object - this is a "MediaList".
14020              */
14021             getRestType: function () {
14022                 return "MediaList";
14023             },
14024 
14025             /**
14026              * @private
14027              * Gets the REST type for the objects that make up the collection - this is "Media".
14028              */
14029             getRestItemType: function () {
14030                 return "Media";
14031             },
14032 
14033             /**
14034              * @private
14035              * Override default to indicates that the collection doesn't support making
14036              * requests.
14037              */
14038             supportsRequests: true,
14039 
14040             /**
14041              * @private
14042              * Override default to indicates that the collection subscribes to its objects.
14043              */
14044             supportsRestItemSubscriptions: true,
14045 
14046             /**
14047              * The REST URL in which this object can be referenced.
14048              * @return {String}
14049              *     The REST URI for this object.
14050              * @private
14051              */
14052             getRestUrl: function () {
14053                 var
14054                     restObj = this._restObj,
14055                     restUrl = "";
14056 
14057                 //Prepend the base REST object if one was provided.
14058                 if (restObj instanceof RestBase) {
14059                     restUrl += restObj.getRestUrl();
14060                 }
14061                 //Otherwise prepend with the default webapp name.
14062                 else {
14063                     restUrl += "/finesse/api";
14064                 }
14065 
14066                 //Append the REST type. (Media not MediaList)
14067                 restUrl += "/" + this.getRestItemType();
14068 
14069                 //Append ID if it is not undefined, null, or empty.
14070                 if (this._id) {
14071                     restUrl += "/" + this._id;
14072                 }
14073 
14074                 return restUrl;
14075             },
14076 
14077             /**
14078              * Getter for a Media from the MediaList collection.
14079              *  * @param {Object} options
14080              *     An object with the following properties:<ul>
14081              *         <li><b>id:</b> The id of the media to fetch</li>
14082              *         <li><b>onLoad(this): (optional)</b> callback handler for when the object is successfully loaded from the server</li>
14083              *         <li><b>onChange(this): (optional)</b> callback handler for when an update notification of the object is received</li>
14084              *         <li><b>onAdd(this): (optional)</b> callback handler for when a notification that the object is created is received</li>
14085              *         <li><b>onDelete(this): (optional)</b> callback handler for when a notification that the object is deleted is received</li>
14086              *         <li><b>onError(rsp): (optional)</b> callback handler for if loading of the object fails, invoked with the error response object:<ul>
14087              *             <li><b>status:</b> {Number} The HTTP status code returned</li>
14088              *             <li><b>content:</b> {String} Raw string of response</li>
14089              *             <li><b>object:</b> {Object} Parsed object of response</li>
14090              *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
14091              *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
14092              *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
14093              *             </ul></li>
14094              *         </ul></li>
14095              *         <li><b>mediaOptions:</b> {Object} An object with the following properties:<ul>
14096              *             <li><b>maxDialogLimit:</b> The id of the object being constructed</li>
14097              *             <li><b>interruptAction:</b> Accept or ignore interrupts</li>
14098              *             <li><b>dialogLogoutAction:</b> transfer or close the task at logout time</li></ul>
14099              *         </li></ul>
14100              *
14101              * @returns {finesse.restservices.Media}
14102              *     A Media object.
14103              */
14104             getMedia: function (options) {
14105                 this.isLoaded();
14106                 options = options || {};
14107                 var objectId = options.id,
14108                     media = this._collection[objectId];
14109 
14110                 //throw error if media not found
14111                 if(!media) {
14112                     throw new Error("No media found with id: " + objectId);
14113                 }
14114 
14115                 media.addHandler('load', options.onLoad);
14116                 media.addHandler('change', options.onChange);
14117                 media.addHandler('add', options.onAdd);
14118                 media.addHandler('delete', options.onDelete);
14119                 media.addHandler('error', options.onError);
14120 
14121                 media.setMediaOptions(options.mediaOptions);
14122 
14123                 return media;
14124             },
14125 
14126             /**
14127              * Utility method to build the internal collection data structure (object) based on provided data
14128              * @param {Object} data
14129              *     The data to build the internal collection from
14130              * @private
14131              */
14132             _buildCollection: function (data) {
14133                 //overriding this from RestBaseCollection because we need to pass in parentObj
14134                 //because restUrl is finesse/api/User/<useri>/Media/<mediaId>
14135                 //other objects like dialog have finesse/api/Dialog/<dialogId>
14136 
14137                 var i, object, objectId, dataArray;
14138                 if (data && this.getProperty(data, this.getRestItemType()) !== null) {
14139                     dataArray = Utilities.getArray(this.getProperty(data, this.getRestItemType()));
14140                     for (i = 0; i < dataArray.length; i += 1) {
14141 
14142                         object = {};
14143                         object[this.getRestItemType()] = dataArray[i];
14144 
14145                         //get id from object.id instead of uri, since uri is not there for some reason
14146                         objectId = object[this.getRestItemType()].id;//this._extractId(object);
14147 
14148                         //create the Media Object
14149                         this._collection[objectId] = new (this.getRestItemClass())({
14150                             id: objectId,
14151                             data: object,
14152                             parentObj: this._restObj
14153                         });
14154                         this.length += 1;
14155                     }
14156                 }
14157             }
14158         });
14159 
14160         window.finesse = window.finesse || {};
14161         window.finesse.restservices = window.finesse.restservices || {};
14162         window.finesse.restservices.MediaList = MediaList;
14163 
14164         return MediaList;
14165     });
14166 
14167 /**
14168  * JavaScript representation of the Finesse User object
14169  *
14170  * @requires finesse.clientservices.ClientServices
14171  * @requires Class
14172  * @requires finesse.FinesseBase
14173  * @requires finesse.restservices.RestBase
14174  */
14175 
14176 /** @private */
14177 define('restservices/User',[
14178     'restservices/RestBase',
14179     'restservices/Dialogs',
14180     'restservices/ClientLog',
14181     'restservices/Queues',
14182     'restservices/WrapUpReasons',
14183     'restservices/PhoneBooks',
14184     'restservices/Workflows',
14185     'restservices/UserMediaPropertiesLayout',
14186     'restservices/UserMediaPropertiesLayouts',
14187     'restservices/Media',
14188     'restservices/MediaList',
14189     'utilities/Utilities'
14190 ],
14191 function (RestBase, Dialogs, ClientLog, Queues, WrapUpReasons, PhoneBooks, Workflows, UserMediaPropertiesLayout, UserMediaPropertiesLayouts, Media, MediaList, Utilities) {
14192 
14193     var User = RestBase.extend(/** @lends finesse.restservices.User.prototype */{
14194 
14195         _dialogs : null,
14196         _clientLogObj : null,
14197         _wrapUpReasons : null,
14198         _phoneBooks : null,
14199         _workflows : null,
14200         _mediaPropertiesLayout : null,
14201         _mediaPropertiesLayouts : null,
14202         _queues : null,
14203         media : null,
14204         mediaList : null,
14205 
14206         /**
14207          * @class
14208          * The User represents a Finesse Agent or Supervisor.
14209          *
14210          * @param {Object} options
14211          *     An object with the following properties:<ul>
14212          *         <li><b>id:</b> The id of the object being constructed</li>
14213          *         <li><b>onLoad(this): (optional)</b> callback handler for when the object is successfully loaded from the server</li>
14214          *         <li><b>onChange(this): (optional)</b> callback handler for when an update notification of the object is received</li>
14215          *         <li><b>onAdd(this): (optional)</b> callback handler for when a notification that the object is created is received</li>
14216          *         <li><b>onDelete(this): (optional)</b> callback handler for when a notification that the object is deleted is received</li>
14217          *         <li><b>onError(rsp): (optional)</b> callback handler for if loading of the object fails, invoked with the error response object:<ul>
14218          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
14219          *             <li><b>content:</b> {String} Raw string of response</li>
14220          *             <li><b>object:</b> {Object} Parsed object of response</li>
14221          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
14222          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
14223          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
14224          *             </ul></li>
14225          *         </ul></li>
14226          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
14227          * @augments finesse.restservices.RestBase
14228          * @constructs
14229          * @example
14230          *      _user = new finesse.restservices.User({
14231          *                      id: _id,
14232          *                      onLoad : _handleUserLoad,
14233          *                      onChange : _handleUserChange
14234          *      });
14235          **/
14236         init: function (options) {
14237             this._super(options);
14238         },
14239 
14240         Callbacks: {},
14241 
14242         /**
14243          * @private
14244          * Gets the REST class for the current object - this is the User object.
14245          */
14246         getRestClass: function () {
14247             return User;
14248         },
14249 
14250         /**
14251         * @private
14252          * Gets the REST type for the current object - this is a "User".
14253          */
14254         getRestType: function () {
14255             return "User";
14256         },
14257         /**
14258          * @private
14259          * overloading this to return URI
14260          */
14261         getXMPPNodePath: function () {
14262             return this.getRestUrl();
14263         },
14264         /**
14265         * @private
14266          * Returns whether this object supports subscriptions
14267          */
14268         supportsSubscriptions: function () {
14269             return true;
14270         },
14271 
14272         /**
14273          * Getter for the firstName of this User.
14274          * @returns {String}
14275          *     The firstName for this User
14276          */
14277         getFirstName: function () {
14278             this.isLoaded();
14279             return Utilities.convertNullToEmptyString(this.getData().firstName);
14280         },
14281 
14282         /**
14283          * Getter for the lastName of this User.
14284          * @returns {String}
14285          *     The lastName for this User
14286          */
14287         getLastName: function () {
14288             this.isLoaded();
14289             return Utilities.convertNullToEmptyString(this.getData().lastName);
14290         },
14291 
14292         /**
14293          * Getter for the extension of this User.
14294          * @returns {String}
14295          *     The extension, if any, of this User
14296          */
14297         getExtension: function () {
14298             this.isLoaded();
14299             return Utilities.convertNullToEmptyString(this.getData().extension);
14300         },
14301 
14302         /**
14303          * Getter for the id of the Team of this User
14304          * @returns {String}
14305          *     The current (or last fetched) id of the Team of this User
14306          */
14307         getTeamId: function () {
14308             this.isLoaded();
14309             return this.getData().teamId;
14310         },
14311 
14312         /**
14313          * Getter for the name of the Team of this User
14314          * @returns {String}
14315          *     The current (or last fetched) name of the Team of this User
14316          */
14317         getTeamName: function () {
14318             this.isLoaded();
14319             return this.getData().teamName;
14320         },
14321 
14322         /**
14323          * Is user an agent?
14324          * @returns {Boolean} True if user has role of agent, else false.
14325          */
14326         hasAgentRole: function () {
14327             this.isLoaded();
14328             return this.hasRole("Agent");
14329         },
14330 
14331         /**
14332          * Is user a supervisor?
14333          * @returns {Boolean} True if user has role of supervisor, else false.
14334          */
14335         hasSupervisorRole: function () {
14336             this.isLoaded();
14337             return this.hasRole("Supervisor");
14338         },
14339 
14340         /**
14341          * @private
14342          * Checks to see if user has "theRole"
14343          * @returns {Boolean}
14344          */
14345         hasRole: function (theRole) {
14346             this.isLoaded();
14347             var result = false, i, roles, len;
14348 
14349             roles = this.getData().roles.role;
14350             len = roles.length;
14351             if (typeof roles === 'string') {
14352                 if (roles === theRole) {
14353                     result = true;
14354                 }
14355             } else {
14356                 for (i = 0; i < len ; i = i + 1) {
14357                     if (roles[i] === theRole) {
14358                         result = true;
14359                         break;
14360                     }
14361                 }
14362             }
14363 
14364             return result;
14365         },
14366 
14367         /**
14368          * Getter for the pending state of this User.
14369          * @returns {String}
14370          *     The pending state of this User
14371          * @see finesse.restservices.User.States
14372          */
14373         getPendingState: function () {
14374             this.isLoaded();
14375             return Utilities.convertNullToEmptyString(this.getData().pendingState);
14376         },
14377 
14378         /**
14379          * Getter for the state of this User.
14380          * @returns {String}
14381          *     The current (or last fetched) state of this User
14382          * @see finesse.restservices.User.States
14383          */
14384         getState: function () {
14385             this.isLoaded();
14386             return this.getData().state;
14387         },
14388 
14389         /**
14390          * Getter for the state change time of this User.
14391          * @returns {String}
14392          *     The state change time of this User
14393          */
14394         getStateChangeTime: function () {
14395             this.isLoaded();
14396             return this.getData().stateChangeTime;
14397         },
14398 
14399         /**
14400          * Getter for the wrap-up mode of this User.
14401          * @see finesse.restservices.User.WrapUpMode
14402          * @returns {String} The wrap-up mode of this user that is a value of {@link finesse.restservices.User.WrapUpMode}
14403          */
14404         getWrapUpOnIncoming: function () {
14405             this.isLoaded();
14406             return this.getData().settings.wrapUpOnIncoming;
14407         },
14408 
14409         /**
14410          * Is User required to go into wrap-up?
14411          * @see finesse.restservices.User.WrapUpMode
14412          * @return {Boolean}
14413          *      True if this agent is required to go into wrap-up.
14414          */
14415         isWrapUpRequired: function () {
14416             return (this.getWrapUpOnIncoming() === User.WrapUpMode.REQUIRED ||
14417                     this.getWrapUpOnIncoming() === User.WrapUpMode.REQUIRED_WITH_WRAP_UP_DATA);
14418         },
14419 
14420         /**
14421          * Checks to see if the user is considered a mobile agent by checking for
14422          * the existence of the mobileAgent node.
14423          * @returns {Boolean}
14424          *      True if this agent is a mobile agent.
14425          */
14426         isMobileAgent: function () {
14427             this.isLoaded();
14428             var ma = this.getData().mobileAgent;
14429             return ma !== null && typeof ma === "object";
14430         },
14431 
14432         /**
14433          * Getter for the mobile agent work mode.
14434          * @returns {finesse.restservices.User.WorkMode}
14435          *      If available, return the mobile agent work mode, otherwise null.
14436          */
14437         getMobileAgentMode: function () {
14438             this.isLoaded();
14439             if (this.isMobileAgent()) {
14440                 return this.getData().mobileAgent.mode;
14441             }
14442             return null;
14443         },
14444 
14445         /**
14446          * Getter for the mobile agent dial number.
14447          * @returns {String}
14448          *      If available, return the mobile agent dial number, otherwise null.
14449          */
14450         getMobileAgentDialNumber: function () {
14451             this.isLoaded();
14452             if (this.isMobileAgent()) {
14453                 return this.getData().mobileAgent.dialNumber;
14454             }
14455             return null;
14456         },
14457 
14458         /**
14459          * Getter for a Dialogs collection object that is associated with User.
14460          * @param {finesse.interfaces.RestObjectHandlers} [handlers] Object that sets callback handlers (only
14461          * applicable when Object has not been previously created).
14462          * @returns {finesse.restservices.Dialogs}
14463          *     A Dialogs collection object.
14464          */
14465         getDialogs: function (callbacks) {
14466             var options = callbacks || {};
14467             options.parentObj = this;
14468             this.isLoaded();
14469 
14470             if (this._dialogs === null) {
14471                 this._dialogs = new Dialogs(options);
14472             }
14473 
14474             return this._dialogs;
14475         },
14476 
14477         /**
14478          * Getter for Media collection object that is associated with User.
14479          * @param {finesse.interfaces.RestObjectHandlers} [handlers] Object that sets callback handlers (only
14480          * applicable when Object has not been previously created).
14481          * @returns {finesse.restservices.MediaList}
14482          *     A Media Dialogs collection object.
14483          */
14484         getMediaList: function (callbacks) {
14485             var options = callbacks || {};
14486             options.parentObj = this;
14487             this.isLoaded();
14488 
14489             if (this.mediaList === null) {
14490                 this.mediaList = new MediaList(options);
14491             }
14492 
14493             return this.mediaList;
14494         },
14495 
14496         /**
14497          * @private
14498          * Getter for a ClientLog object that is associated with User.
14499          * @param {finesse.interfaces.RestObjectHandlers} [handlers] Object that sets callback handlers (only 
14500          * applicable when Object has not been previously created).
14501          * @returns {finesse.restservices.ClientLog}
14502          *     A ClientLog collection object.
14503          */
14504         getClientLog: function (callbacks) {
14505             var options = callbacks || {};
14506             options.parentObj = this;
14507             this.isLoaded();
14508            
14509             if (this._clientLogObj === null) {
14510                 this._clientLogObj = new ClientLog(options);
14511             }
14512             else {
14513                 if(options.onLoad && typeof options.onLoad === "function") {
14514                 options.onLoad(this._clientLogObj);
14515                 }
14516             }
14517             return this._clientLogObj;
14518         },
14519        
14520         /**
14521          * Getter for a Queues collection object that is associated with User.
14522          * @param {finesse.interfaces.RestObjectHandlers} [handlers] Object that sets callback handlers (only 
14523          * applicable when Object has not been previously created).
14524          * @returns {finesse.restservices.Queues}
14525          *     A Queues collection object.
14526          */
14527         getQueues: function (callbacks) {
14528             var options = callbacks || {};
14529             options.parentObj = this;
14530             this.isLoaded();
14531     
14532             if (this._queues === null) {
14533                 this._queues = new Queues(options);
14534             }
14535     
14536             return this._queues;
14537         },
14538 
14539         /**
14540          * Getter for a WrapUpReasons collection object that is associated with User.
14541          * @param {finesse.interfaces.RestObjectHandlers} [handlers] Object that sets callback handlers (only 
14542          * applicable when Object has not been previously created).
14543          * @returns {finesse.restservices.WrapUpReasons}
14544          *     A WrapUpReasons collection object.
14545          */
14546         getWrapUpReasons: function (callbacks) {
14547             var options = callbacks || {};
14548             options.parentObj = this;
14549             this.isLoaded();
14550     
14551             if (this._wrapUpReasons === null) {
14552                 this._wrapUpReasons = new WrapUpReasons(options);
14553             }
14554     
14555             return this._wrapUpReasons;
14556         },
14557 
14558         /**
14559          * Getter for a PhoneBooks collection object that is associated with User.
14560          * @param {finesse.interfaces.RestObjectHandlers} [handlers] Object that sets callback handlers (only 
14561          * applicable when Object has not been previously created).
14562          * @returns {finesse.restservices.PhoneBooks}
14563          *     A PhoneBooks collection object.
14564          */
14565         getPhoneBooks: function (callbacks) {
14566             var options = callbacks || {};
14567             options.parentObj = this;
14568             this.isLoaded();
14569     
14570             if (this._phoneBooks === null) {
14571                 this._phoneBooks = new PhoneBooks(options);
14572             }
14573     
14574             return this._phoneBooks;
14575         },
14576 
14577         /**
14578          * @private
14579          * Loads the Workflows collection object that is associated with User and
14580          * 'returns' them to the caller via the handlers.
14581          * @param {finesse.interfaces.RestObjectHandlers} [handlers] Object that sets callback handlers (only 
14582          * applicable when Object has not been previously created).
14583          * @see finesse.restservices.Workflow
14584          * @see finesse.restservices.Workflows
14585          * @see finesse.restservices.RestCollectionBase
14586          */
14587         loadWorkflows: function (callbacks) {
14588             var options = callbacks || {};
14589             options.parentObj = this;
14590             this.isLoaded();
14591 
14592             if (this._workflows === null) {
14593                 this._workflows = new Workflows(options);
14594             } else {
14595                 this._workflows.refresh();
14596             }
14597 
14598         },
14599 
14600         /**
14601          * Getter for a UserMediaPropertiesLayout object that is associated with User.
14602          * @param {finesse.interfaces.RestObjectHandlers} [handlers] Object that sets callback handlers (only 
14603          * applicable when Object has not been previously created).
14604          * @returns {finesse.restservices.UserMediaPropertiesLayout}
14605          *     The UserMediaPropertiesLayout object associated with this user
14606          */
14607         getMediaPropertiesLayout: function (callbacks) {
14608             var options = callbacks || {};
14609             options.parentObj = this;
14610             options.id = this._id;
14611     
14612             this.isLoaded();
14613             if (this._mediaPropertiesLayout === null) {
14614                 this._mediaPropertiesLayout = new UserMediaPropertiesLayout(options);
14615             }
14616             return this._mediaPropertiesLayout;
14617         },
14618 
14619         /**
14620          * Getter for a UserMediaPropertiesLayouts object that is associated with User.
14621          * @param {finesse.interfaces.RestObjectHandlers} [handlers] Object that sets callback handlers (only 
14622          * applicable when Object has not been previously created).
14623          * @returns {finesse.restservices.UserMediaPropertiesLayout}
14624          *     The UserMediaPropertiesLayout object associated with this user
14625          */
14626         getMediaPropertiesLayouts: function (callbacks) {
14627             var options = callbacks || {};
14628             options.parentObj = this;
14629     
14630             this.isLoaded();
14631             if (this._mediaPropertiesLayouts === null) {
14632                 this._mediaPropertiesLayouts = new UserMediaPropertiesLayouts(options);
14633             }
14634             return this._mediaPropertiesLayouts;
14635         },
14636     
14637         /**
14638          * Getter for the supervised Teams this User (Supervisor) supervises, if any.
14639          * @see finesse.restservices.Team
14640          * @returns {Array}
14641          *     An array of Teams supervised by this User (Supervisor)
14642          */
14643         getSupervisedTeams: function () {
14644             this.isLoaded();
14645     
14646             try {
14647                 return Utilities.getArray(this.getData().teams.Team);
14648             } catch (e) {
14649                 return [];
14650             }
14651     
14652         },
14653     
14654         /**
14655          * Perform an agent login for this user, associating him with the
14656          * specified extension.
14657          * @param {Object} params
14658          *     An object containing properties for agent login.
14659          * @param {String} params.extension
14660          *     The extension to associate with this user
14661          * @param {Object} [params.mobileAgent]
14662          *     A mobile agent object containing the mode and dial number properties.
14663          * @param {finesse.interfaces.RequestHandlers} params.handlers
14664          * @see finesse.interfaces.RequestHandlers
14665          * @returns {finesse.restservices.User}
14666          *     This User object, to allow cascading
14667          * @private
14668          */
14669         _login: function (params) {
14670             var handlers, contentBody = {},
14671             restType = this.getRestType();
14672             
14673             // Protect against null dereferencing.
14674             params = params || {};
14675     
14676             contentBody[restType] = {
14677                 "state": User.States.LOGIN,
14678                 "extension": params.extension
14679             };
14680     
14681             // Create mobile agent node if available.
14682             if (typeof params.mobileAgent === "object") {
14683                 contentBody[restType].mobileAgent = {
14684                     "mode": params.mobileAgent.mode,
14685                     "dialNumber": params.mobileAgent.dialNumber
14686                 };
14687             }
14688     
14689             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
14690             handlers = params.handlers || {};
14691     
14692             this.restRequest(this.getRestUrl(), {
14693                 method: 'PUT',
14694                 success: handlers.success,
14695                 error: handlers.error,
14696                 content: contentBody
14697             });
14698     
14699             return this; // Allow cascading
14700         },
14701     
14702         /**
14703          * Perform an agent login for this user, associating him with the
14704          * specified extension.
14705          * @param {String} extension
14706          *     The extension to associate with this user
14707          * @param {finesse.interfaces.RequestHandlers} handlers
14708          *     An object containing the handlers for the request
14709          * @returns {finesse.restservices.User}
14710          *     This User object, to allow cascading
14711          */
14712         login: function (extension, handlers) {
14713             this.isLoaded();
14714             var params = {
14715                 "extension": extension,
14716                 "handlers": handlers
14717             };
14718             return this._login(params);
14719         },
14720     
14721         /**
14722          * Perform an agent login for this user, associating him with the
14723          * specified extension.
14724          * @param {String} extension
14725          *     The extension to associate with this user
14726          * @param {String} mode
14727          *     The mobile agent work mode as defined in finesse.restservices.User.WorkMode.
14728          * @param {String} extension
14729          *     The external dial number desired to be used by the mobile agent.
14730          * @param {finesse.interfaces.RequestHandlers} handlers
14731          *     An object containing the handlers for the request
14732          * @returns {finesse.restservices.User}
14733          *     This User object, to allow cascading
14734          */
14735         loginMobileAgent: function (extension, mode, dialNumber, handlers) {
14736             this.isLoaded();
14737             var params = {
14738                 "extension": extension,
14739                 "mobileAgent": {
14740                     "mode": mode,
14741                     "dialNumber": dialNumber
14742                 },
14743                 "handlers": handlers
14744             };
14745             return this._login(params);
14746         },
14747     
14748         /**
14749          * Perform an agent logout for this user.
14750          * @param {String} reasonCode
14751          *     The reason this user is logging out.  Pass null for no reason.
14752          * @param {finesse.interfaces.RequestHandlers} handlers
14753          *     An object containing the handlers for the request
14754          * @returns {finesse.restservices.User}
14755          *     This User object, to allow cascading
14756          */
14757         logout: function (reasonCode, handlers) {
14758             return this.setState("LOGOUT", reasonCode, handlers);
14759         },
14760     
14761         /**
14762          * Set the state of the user.
14763          * @param {String} newState
14764          *     The state you are setting
14765          * @param {ReasonCode} reasonCode
14766          *     The reason this user is logging out.  Pass null for no reason.
14767          * @param {finesse.interfaces.RequestHandlers} handlers
14768          *     An object containing the handlers for the request
14769          * @see finesse.restservices.User.States
14770          * @returns {finesse.restservices.User}
14771          *     This User object, to allow cascading
14772          */
14773         setState: function (newState, reasonCode, handlers) {
14774             this.isLoaded();
14775     
14776             var options, contentBody = {};
14777     
14778             if (!reasonCode) {
14779                 if(newState === "LOGOUT"){
14780                         contentBody[this.getRestType()] = {
14781                             "state": newState,
14782                             "logoutAllMedia": "true"
14783                         };
14784                 }
14785                 else{
14786                         contentBody[this.getRestType()] = {
14787                             "state": newState
14788                         };
14789                 }
14790                 
14791             } else {
14792                   if(newState === "LOGOUT"){
14793                         contentBody[this.getRestType()] = {
14794                              "state": newState,
14795                              "reasonCodeId": reasonCode.id,
14796                              "logoutAllMedia": "true"
14797                          };
14798                 }
14799                 else{
14800                     contentBody[this.getRestType()] = {
14801                              "state": newState,
14802                              "reasonCodeId": reasonCode.id
14803                          };
14804                     }
14805                
14806             }
14807     
14808             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
14809             handlers = handlers || {};
14810     
14811             options = {
14812                 method: 'PUT',
14813                 success: handlers.success,
14814                 error: handlers.error,
14815                 content: contentBody
14816             };
14817     
14818             // After removing the selective 202 handling, we should be able to just use restRequest
14819             this.restRequest(this.getRestUrl(), options);
14820     
14821             return this; // Allow cascading
14822         },
14823     
14824         /**
14825          * Make call to a particular phone number.
14826          *
14827          * @param {String} 
14828          *     The number to call
14829          * @param {finesse.interfaces.RequestHandlers} handlers
14830          *     An object containing the handlers for the request
14831          * @returns {finesse.restservices.User}
14832          *     This User object, to allow cascading
14833          */ 
14834         makeCall: function (number, handlers) {
14835             this.isLoaded();
14836     
14837             this.getDialogs().createNewCallDialog(number, this.getExtension(), handlers);
14838     
14839             return this; // Allow cascading
14840         },
14841     
14842         /**
14843          * Make a silent monitor call to a particular agent's phone number.
14844          *
14845          * @param {String} 
14846          *     The number to call
14847          * @param {finesse.interfaces.RequestHandlers} handlers
14848          *     An object containing the handlers for the request
14849          * @returns {finesse.restservices.User}
14850          *     This User object, to allow cascading
14851          */
14852         makeSMCall: function (number, handlers) {
14853             this.isLoaded();
14854     
14855             var actionType = "SILENT_MONITOR";
14856     
14857             this.getDialogs().createNewSuperviseCallDialog(number, this.getExtension(), actionType, handlers);
14858     
14859             return this; // Allow cascading
14860         },
14861         
14862     
14863         /**
14864          * Make a silent monitor call to a particular agent's phone number.
14865          *
14866          * @param {String}
14867          *     The number to call
14868          * @param {String} dialogUri
14869          *     The associated dialog uri of SUPERVISOR_MONITOR call
14870          * @param {finesse.interfaces.RequestHandlers} handlers
14871          *     An object containing the handlers for the request
14872          * @see finesse.restservices.dialog
14873          * @returns {finesse.restservices.User}
14874          *     This User object, to allow cascading
14875          */
14876         makeBargeCall:function (number, dialogURI, handlers) {
14877             this.isLoaded();
14878             var actionType = "BARGE_CALL";
14879             this.getDialogs().createNewBargeCall( this.getExtension(), number, actionType, dialogURI,handlers);
14880     
14881             return this; // Allow cascading
14882         },
14883         
14884         /**
14885          * Returns true if the user's current state will result in a pending state change. A pending state
14886          * change is a request to change state that does not result in an immediate state change. For
14887          * example if an agent attempts to change to the NOT_READY state while in the TALKING state, the
14888          * agent will not change state until the call ends.
14889          *
14890          * The current set of states that result in pending state changes is as follows:
14891          *     TALKING
14892          *     HOLD
14893          *     RESERVED_OUTBOUND_PREVIEW
14894          *  @returns {Boolean} True if there is a pending state change.
14895          *  @see finesse.restservices.User.States
14896          */
14897         isPendingStateChange: function () {
14898             var state = this.getState();
14899             return state && ((state === User.States.TALKING) || (state === User.States.HOLD) || (state === User.States.RESERVED_OUTBOUND_PREVIEW));
14900         },
14901         
14902         /**
14903          * Returns true if the user's current state is WORK or WORK_READY. This is used so
14904          * that a pending state is not cleared when moving into wrap up (work) mode. 
14905          * Note that we don't add this as a pending state, since changes while in wrap up
14906          * occur immediately (and we don't want any "pending state" to flash on screen.
14907          * 
14908          * @see finesse.restservices.User.States
14909          * @returns {Boolean} True if user is in wrap-up mode.
14910          */
14911         isWrapUp: function () {
14912             var state = this.getState();
14913             return state && ((state === User.States.WORK) || (state === User.States.WORK_READY));
14914         },
14915     
14916         /**
14917          * @private
14918          * Parses a uriString to retrieve the id portion
14919          * @param {String} uriString
14920          * @return {String} id
14921          */
14922         _parseIdFromUriString : function (uriString) {
14923             return Utilities.getId(uriString);
14924         },
14925 
14926         /**
14927          * Gets the user's Reason Code label.
14928          * Works for both Not Ready and Logout reason codes
14929          * @return {String} the reason code label, or empty string if none
14930          */
14931         getReasonCodeLabel : function () {
14932             this.isLoaded();
14933 
14934             if (this.getData().reasonCode) {
14935                 return this.getData().reasonCode.label;
14936             } else {
14937                 return "";
14938             }
14939         },
14940     
14941         /**
14942          * Gets the user's Not Ready reason code.
14943          * @return {String} Reason Code Id, or undefined if not set or indeterminate
14944          */
14945         getNotReadyReasonCodeId : function () {
14946             this.isLoaded();
14947     
14948             var reasoncodeIdResult, finesseServerReasonCodeId;
14949             finesseServerReasonCodeId = this.getData().reasonCodeId;
14950     
14951             //FinesseServer will give "-l" => we will set to undefined (for convenience)
14952             if (finesseServerReasonCodeId !== "-1") {
14953                 reasoncodeIdResult = finesseServerReasonCodeId;
14954             }
14955     
14956             return reasoncodeIdResult;
14957         },
14958     
14959         /**
14960          * Performs a GET against the Finesse server looking up the reasonCodeId specified.
14961          * Note that there is no return value; use the success handler to process a
14962          * valid return.
14963          * @param {finesse.interfaces.RequestHandlers} handlers
14964          *     An object containing the handlers for the request
14965          * @param {String} reasonCodeId The id for the reason code to lookup
14966          * 
14967          */
14968         getReasonCodeById : function (handlers, reasonCodeId)
14969         {
14970             var self = this, contentBody, reasonCode, url;
14971             contentBody = {};
14972     
14973             url = this.getRestUrl() + "/ReasonCode/" + reasonCodeId;
14974             this.restRequest(url, {
14975                 method: 'GET',
14976                 success: function (rsp) {
14977                     reasonCode = {
14978                         uri: rsp.object.ReasonCode.uri,
14979                         label: rsp.object.ReasonCode.label,
14980                         id: self._parseIdFromUriString(rsp.object.ReasonCode.uri)
14981                     };
14982                     handlers.success(reasonCode);
14983                 },
14984                 error: function (rsp) {
14985                     handlers.error(rsp);
14986                 },
14987                 content: contentBody
14988             });
14989         },
14990     
14991         /**
14992          * Performs a GET against Finesse server retrieving all the specified type of reason codes.
14993          * @param {String} type (LOGOUT or NOT_READY)
14994          * @param {finesse.interfaces.RequestHandlers} handlers
14995          *     An object containing the handlers for the request
14996          */
14997         _getReasonCodesByType : function (type, handlers)
14998         {
14999             var self = this, contentBody = {}, url, reasonCodes, i, reasonCodeArray;
15000     
15001             url = this.getRestUrl() + "/ReasonCodes?category=" + type;
15002             this.restRequest(url, {
15003                 method: 'GET',
15004                 success: function (rsp) {
15005                     reasonCodes = [];
15006     
15007                     reasonCodeArray = rsp.object.ReasonCodes.ReasonCode;
15008                     if (reasonCodeArray === undefined) {
15009                         reasonCodes = undefined;
15010                     } else if (reasonCodeArray[0] !== undefined) {
15011                         for (i = 0; i < reasonCodeArray.length; i = i + 1) {
15012                             reasonCodes[i] = {
15013                                 label: rsp.object.ReasonCodes.ReasonCode[i].label,
15014                                 id: self._parseIdFromUriString(rsp.object.ReasonCodes.ReasonCode[i].uri)
15015                             };
15016                         }
15017                     } else {
15018                         reasonCodes[0] = {
15019                             label: rsp.object.ReasonCodes.ReasonCode.label,
15020                             id: self._parseIdFromUriString(rsp.object.ReasonCodes.ReasonCode.uri)
15021                         };
15022                     }
15023                     handlers.success(reasonCodes);
15024                 },
15025                 error: function (rsp) {
15026                     handlers.error(rsp);
15027                 },
15028                 content: contentBody
15029             });
15030         },
15031     
15032         /**
15033          * Performs a GET against Finesse server retrieving all the Signout reason codes.
15034          * Note that there is no return value; use the success handler to process a
15035          * valid return.
15036          * @param {finesse.interfaces.RequestHandlers} handlers
15037          *     An object containing the handlers for the request
15038          */
15039         getSignoutReasonCodes : function (handlers)
15040         {
15041             this._getReasonCodesByType("LOGOUT", handlers);
15042         },
15043     
15044         /**
15045          * Performs a GET against Finesse server retrieving all the Not Ready reason codes.
15046          * Note that there is no return value; use the success handler to process a
15047          * valid return.
15048          * @param {finesse.interfaces.RequestHandlers} handlers
15049          *     An object containing the handlers for the request
15050          */
15051         getNotReadyReasonCodes : function (handlers)
15052         {
15053             this._getReasonCodesByType("NOT_READY", handlers);
15054         }
15055     });
15056     
15057     User.States = /** @lends finesse.restservices.User.States.prototype */ {
15058             /**
15059              * User Login.  Note that while this is an action, is not technically a state, since a 
15060              * logged-in User will always be in a specific state (READY, NOT_READY, TALKING, etc.).
15061              */
15062             LOGIN: "LOGIN",
15063             /**
15064              * User is logged out.
15065              */
15066             LOGOUT: "LOGOUT",
15067             /**
15068              * User is not ready. Note that in UCCX implementations, the user is in this state while on a non-routed call.
15069              */
15070             NOT_READY: "NOT_READY",
15071             /**
15072              * User is ready for calls.
15073              */
15074             READY: "READY",
15075             /**
15076              * User has a call coming in, but has not answered it.
15077              */
15078             RESERVED: "RESERVED",
15079             /**
15080              * User has an outbound call being made, but has not been connected to it.
15081              */
15082             RESERVED_OUTBOUND: "RESERVED_OUTBOUND",
15083             /**
15084              * User has an outbound call's preview information being displayed, but has not acted on it.
15085              */
15086             RESERVED_OUTBOUND_PREVIEW: "RESERVED_OUTBOUND_PREVIEW",
15087             /**
15088              * User is on a call.  Note that in UCCX implementations, this is for routed calls only.
15089              */
15090             TALKING: "TALKING",
15091             /**
15092              * User is on hold.  Note that in UCCX implementations, the user remains in TALKING state while on hold.
15093              */
15094             HOLD: "HOLD",
15095             /**
15096              * User is wrap-up/work mode.  This mode is typically configured to time out, after which the user becomes NOT_READY.
15097              */
15098             WORK: "WORK",
15099             /**
15100              * This is the same as WORK, except that after time out user becomes READY.
15101              */
15102             WORK_READY: "WORK_READY",
15103             /**
15104              * @class Possible User state values.
15105              * @constructs
15106              */
15107             _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
15108           
15109         };
15110     
15111     User.WorkMode = { /** @lends finesse.restservices.User.WorkMode.prototype */
15112         /**
15113          * Mobile agent is connected (dialed) for each incoming call received.
15114          */
15115         CALL_BY_CALL: "CALL_BY_CALL",
15116         /**
15117          * Mobile agent is connected (dialed) at login.
15118          */
15119         NAILED_CONNECTION: "NAILED_CONNECTION",
15120         /**
15121          * @class Possible Mobile Agent Work Mode Types.
15122          * @constructs
15123          */
15124         _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
15125         
15126     };
15127 
15128     User.WrapUpMode = { /** @lends finesse.restservices.User.WrapUpMode.prototype */
15129         /**
15130          * Agent must go into wrap-up when call ends
15131          */
15132         REQUIRED: "REQUIRED",
15133         /**
15134          * Agent must go into wrap-up when call ends and must enter wrap-up data
15135          */
15136         REQUIRED_WITH_WRAP_UP_DATA: "REQUIRED_WITH_WRAP_UP_DATA",
15137         /**
15138          * Agent can choose to go into wrap-up on a call-by-call basis when the call ends
15139          */
15140         OPTIONAL: "OPTIONAL",
15141         /**
15142          * Agent is not allowed to go into wrap-up when call ends.
15143          */
15144         NOT_ALLOWED: "NOT_ALLOWED",
15145         /**
15146          * @class Possible Wrap-up Mode Types.
15147          * @constructs
15148          */
15149         _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
15150         
15151     };
15152 
15153     window.finesse = window.finesse || {};
15154     window.finesse.restservices = window.finesse.restservices || {};
15155     window.finesse.restservices.User = User;
15156         
15157     return User;
15158 });
15159 
15160 /**
15161  * JavaScript representation of the Finesse Users collection
15162  * object which contains a list of Users objects.
15163  *
15164  * @requires finesse.clientservices.ClientServices
15165  * @requires Class
15166  * @requires finesse.FinesseBase
15167  * @requires finesse.restservices.RestBase
15168  * @requires finesse.restservices.RestCollectionBase
15169  * @requires finesse.restservices.User
15170  */
15171 
15172 /** @private */
15173 define('restservices/Users',[
15174     'restservices/RestCollectionBase',
15175     'restservices/RestBase',
15176     'restservices/User'
15177 ],
15178 function (RestCollectionBase, RestBase, User) {
15179 
15180     var Users = RestCollectionBase.extend(/** @lends finesse.restservices.Users.prototype */{
15181 
15182     /**
15183      * @class
15184      * JavaScript representation of a Users collection object. 
15185      * While there is no method provided to retrieve all Users, this collection is
15186      * used to return the Users in a supervised Team.
15187      * @augments finesse.restservices.RestCollectionBase
15188      * @constructs
15189      * @see finesse.restservices.Team
15190      * @see finesse.restservices.User
15191      * @see finesse.restservices.User#getSupervisedTeams
15192      * @example
15193      *  // Note: The following method gets an Array of Teams, not a Collection.
15194      *  _teams = _user.getSupervisedTeams();
15195      *  if (_teams.length > 0) {
15196      *      _team0Users = _teams[0].getUsers();
15197      *  }
15198      */
15199     _fakeConstuctor: function () {
15200         /* This is here to hide the real init constructor from the public docs */
15201     },
15202         
15203     /**
15204      * @private
15205      * JavaScript representation of the Finesse Users collection
15206      * object which contains a list of Users objects.
15207      *
15208 	 * @param {Object} options
15209 	 *     An object with the following properties:<ul>
15210      *         <li><b>id:</b> The id of the object being constructed</li>
15211      *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
15212      *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
15213      *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
15214      *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
15215      *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
15216      *             <li><b>status:</b> {Number} The HTTP status code returned</li>
15217      *             <li><b>content:</b> {String} Raw string of response</li>
15218      *             <li><b>object:</b> {Object} Parsed object of response</li>
15219      *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
15220      *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
15221      *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
15222      *             </ul></li>
15223      *         </ul></li>
15224      *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
15225      **/
15226 	init: function (options) {
15227 		this._super(options);
15228 	},
15229 
15230 	/**
15231      * @private
15232 	 * Gets the REST class for the current object - this is the Users class.
15233 	 */
15234 	getRestClass: function () {
15235 	    return Users;
15236 	},
15237 
15238 	/**
15239      * @private
15240 	 * Gets the REST class for the objects that make up the collection. - this
15241 	 * is the User class.
15242 	 */
15243 	getRestItemClass: function () {
15244 		return User;
15245 	},
15246 
15247 	/**
15248      * @private
15249 	 * Gets the REST type for the current object - this is a "Users".
15250 	 */
15251 	getRestType: function () {
15252 	    return "Users";
15253 	},
15254 
15255 	/**
15256      * @private
15257 	 * Gets the REST type for the objects that make up the collection - this is "User".
15258 	 */
15259 	getRestItemType: function () {
15260 	    return "User";
15261 	},
15262 
15263 	/**
15264      * @private
15265      * Gets the node path for the current object - this is the team Users node
15266      * @returns {String} The node path
15267      */
15268     getXMPPNodePath: function () {
15269 		return this.getRestUrl();
15270     },
15271 
15272     /**
15273      * @private
15274      * Overloading _doGET to reroute the GET to /Team/id from /Team/id/Users
15275      * This needs to be done because the GET /Team/id/Users API is missing
15276      * @returns {Users} This Users (collection) object to allow cascading
15277      */
15278     _doGET: function (handlers) {
15279         var _this = this;
15280         handlers = handlers || {};
15281         // Only do this for /Team/id/Users
15282         if (this._restObj && this._restObj.getRestType() === "Team") {
15283             this._restObj._doGET({
15284                 success: function (rspObj) {
15285                     // Making sure the response was a valid Team
15286                     if (_this._restObj._validate(rspObj.object)) {
15287                         // Shimmying the response to look like a Users collection by extracting it from the Team response
15288                         rspObj.object[_this.getRestType()] = rspObj.object[_this._restObj.getRestType()][_this.getRestType().toLowerCase()];
15289                         handlers.success(rspObj);
15290                     } else {
15291                         handlers.error(rspObj);
15292                     }
15293                 },
15294                 error: handlers.error
15295             });
15296             return this; // Allow cascading
15297         } else {
15298             return this._super(handlers);
15299         }
15300     },
15301 
15302 	/**
15303      * @private
15304      * Override default to indicates that the collection doesn't support making
15305 	 * requests.
15306 	 */
15307 	supportsRequests: false,
15308 
15309     /**
15310      * @private
15311      * Indicates that this collection handles the subscription for its items
15312      */
15313     handlesItemSubscription: true,
15314 	
15315     /**
15316      * @private
15317      * Override default to indicate that we need to subscribe explicitly
15318      */
15319     explicitSubscription: true
15320     
15321 	});
15322 
15323     window.finesse = window.finesse || {};
15324     window.finesse.restservices = window.finesse.restservices || {};
15325     window.finesse.restservices.Users = Users;
15326 
15327     return Users;
15328 });
15329 
15330 /**
15331  * JavaScript representation of the Finesse Team Not Ready Reason Code Assignment object.
15332  *
15333  * @requires finesse.clientservices.ClientServices
15334  * @requires Class
15335  * @requires finesse.FinesseBase
15336  * @requires finesse.restservices.RestBase
15337  */
15338 
15339 /** @private */
15340 define('restservices/TeamNotReadyReasonCode',['restservices/RestBase'], function (RestBase) {
15341     
15342     var TeamNotReadyReasonCode = RestBase.extend( {
15343 
15344         /**
15345          * @class
15346          * JavaScript representation of a Team Not Ready ReasonCode object. Also exposes
15347          * methods to operate on the object against the server.
15348          *
15349          * @param {Object} options
15350          *     An object with the following properties:<ul>
15351          *         <li><b>id:</b> The id of the object being constructed</li>
15352          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
15353          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
15354          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
15355          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
15356          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
15357          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
15358          *             <li><b>content:</b> {String} Raw string of response</li>
15359          *             <li><b>object:</b> {Object} Parsed object of response</li>
15360          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
15361          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
15362          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
15363          *             </ul></li>
15364          *         </ul></li>
15365          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
15366          * @constructs
15367          **/
15368         init: function (options) {
15369             this._super(options);
15370         },
15371     
15372         /**
15373          * @private
15374          * Gets the REST class for the current object - this is the TeamNotReadyReasonCode class.
15375          * @returns {Object} The TeamNotReadyReasonCode class.
15376          */
15377         getRestClass: function () {
15378             return TeamNotReadyReasonCode;
15379         },
15380     
15381         /**
15382          * @private
15383          * Gets the REST type for the current object - this is a "ReasonCode".
15384          * @returns {String} The ReasonCode string.
15385          */
15386         getRestType: function () {
15387             return "ReasonCode";
15388         },
15389     
15390         /**
15391          * @private
15392          * Override default to indicate that this object doesn't support making
15393          * requests.
15394          */
15395         supportsRequests: false,
15396     
15397         /**
15398          * @private
15399          * Override default to indicate that this object doesn't support subscriptions.
15400          */
15401         supportsSubscriptions: false,
15402     
15403         /**
15404          * Getter for the category.
15405          * @returns {String} The category.
15406          */
15407         getCategory: function () {
15408             this.isLoaded();
15409             return this.getData().category;
15410         },
15411     
15412         /**
15413          * Getter for the code.
15414          * @returns {String} The code.
15415          */
15416         getCode: function () {
15417             this.isLoaded();
15418             return this.getData().code;
15419         },
15420     
15421         /**
15422          * Getter for the label.
15423          * @returns {String} The label.
15424          */
15425         getLabel: function () {
15426             this.isLoaded();
15427             return this.getData().label;
15428         },
15429     
15430         /**
15431          * Getter for the forAll value.
15432          * @returns {String} The forAll.
15433          */
15434         getForAll: function () {
15435             this.isLoaded();
15436             return this.getData().forAll;
15437         },
15438     
15439         /**
15440          * Getter for the Uri value.
15441          * @returns {String} The Uri.
15442          */
15443         getUri: function () {
15444             this.isLoaded();
15445             return this.getData().uri;
15446         }
15447 
15448     });
15449     
15450     window.finesse = window.finesse || {};
15451     window.finesse.restservices = window.finesse.restservices || {};
15452     window.finesse.restservices.TeamNotReadyReasonCode = TeamNotReadyReasonCode;
15453         
15454     return TeamNotReadyReasonCode;
15455 });
15456 
15457 /**
15458 * JavaScript representation of the Finesse TeamNotReadyReasonCodes collection
15459 * object which contains a list of TeamNotReadyReasonCode objects.
15460  *
15461  * @requires finesse.clientservices.ClientServices
15462  * @requires Class
15463  * @requires finesse.FinesseBase
15464  * @requires finesse.restservices.RestBase
15465  * @requires finesse.restservices.Dialog
15466  * @requires finesse.restservices.RestCollectionBase
15467  */
15468 
15469 /** @private */
15470 define('restservices/TeamNotReadyReasonCodes',[
15471     'restservices/RestCollectionBase',
15472     'restservices/RestBase',
15473     'restservices/TeamNotReadyReasonCode'
15474 ],
15475 function (RestCollectionBase, RestBase, TeamNotReadyReasonCode) {
15476 
15477     var TeamNotReadyReasonCodes = RestCollectionBase.extend( {
15478 
15479       /**
15480        * @class
15481        * JavaScript representation of a TeamNotReadyReasonCodes collection object. Also exposes
15482        * methods to operate on the object against the server.
15483        *
15484        * @param {Object} options
15485        *     An object with the following properties:<ul>
15486        *         <li><b>id:</b> The id of the object being constructed</li>
15487        *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
15488        *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
15489        *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
15490        *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
15491        *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
15492        *             <li><b>status:</b> {Number} The HTTP status code returned</li>
15493        *             <li><b>content:</b> {String} Raw string of response</li>
15494        *             <li><b>object:</b> {Object} Parsed object of response</li>
15495        *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
15496        *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
15497        *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
15498        *             </ul></li>
15499        *         </ul></li>
15500        *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
15501        * @augments finesse.restservices.RestCollectionBase
15502        * @constructs
15503        **/
15504       init: function (options) {
15505           this._super(options);
15506       },
15507     
15508       /**
15509        * @private
15510        * Gets the REST class for the current object - this is the TeamNotReadyReasonCodes class.
15511        */
15512       getRestClass: function () {
15513           return TeamNotReadyReasonCodes;
15514       },
15515     
15516       /**
15517        * @private
15518        * Gets the REST class for the objects that make up the collection. - this
15519        * is the TeamNotReadyReasonCode class.
15520        */
15521       getRestItemClass: function () {
15522           return TeamNotReadyReasonCode;
15523       },
15524     
15525       /**
15526        * @private
15527        * Gets the REST type for the current object - this is a "ReasonCodes".
15528        */
15529       getRestType: function () {
15530           return "ReasonCodes";
15531       },
15532     
15533       /**
15534        * @private
15535        * Overrides the parent class.  Returns the url for the NotReadyReasonCodes resource
15536        */
15537       getRestUrl: function () {
15538           // return ("/finesse/api/" + this.getRestType() + "?category=NOT_READY");
15539           var restObj = this._restObj,
15540               restUrl = "";
15541           //Prepend the base REST object if one was provided.
15542           //Otherwise prepend with the default webapp name.
15543           if (restObj instanceof RestBase) {
15544               restUrl += restObj.getRestUrl();
15545           }
15546           else {
15547               restUrl += "/finesse/api";
15548           }
15549           //Append the REST type.
15550           restUrl += "/ReasonCodes?category=NOT_READY";
15551           //Append ID if it is not undefined, null, or empty.
15552           if (this._id) {
15553               restUrl += "/" + this._id;
15554           }
15555           return restUrl;
15556       },
15557     
15558       /**
15559        * @private
15560        * Gets the REST type for the objects that make up the collection - this is "ReasonCode".
15561        */
15562       getRestItemType: function () {
15563           return "ReasonCode";
15564       },
15565     
15566       /**
15567        * @private
15568        * Override default to indicates that the collection supports making
15569        * requests.
15570        */
15571       supportsRequests: true,
15572     
15573       /**
15574        * @private
15575        * Override default to indicate that this object doesn't support subscriptions.
15576        */
15577       supportsRestItemSubscriptions: false,
15578     
15579       /**
15580        * @private
15581        * Retrieve the Not Ready Reason Codes.
15582        *
15583        * @returns {TeamNotReadyReasonCodes}
15584        *     This TeamNotReadyReasonCodes object to allow cascading.
15585        */
15586       get: function () {
15587           // set loaded to false so it will rebuild the collection after the get
15588           this._loaded = false;
15589           // reset collection
15590           this._collection = {};
15591           // perform get
15592           this._synchronize();
15593           return this;
15594       },
15595     
15596       /**
15597        * @private
15598        * Set up the PutSuccessHandler for TeamNotReadyReasonCodes
15599        * @param {Object} reasonCodes
15600        * @param {String} contentBody
15601        * @param successHandler    
15602        * @return {function}
15603        */
15604       createPutSuccessHandler: function (reasonCodes, contentBody, successHandler) {
15605           return function (rsp) {
15606               // Update internal structure based on response. Here we
15607               // inject the contentBody from the PUT request into the
15608               // rsp.object element to mimic a GET as a way to take
15609               // advantage of the existing _processResponse method.
15610               rsp.object = contentBody;
15611               reasonCodes._processResponse(rsp);
15612     
15613               //Remove the injected contentBody object before cascading response
15614               rsp.object = {};
15615     
15616               //cascade response back to consumer's response handler
15617               successHandler(rsp);
15618           };
15619       },
15620     
15621       /**
15622        * @private
15623        * Perform the REST API PUT call to update the reason code assignments for the team
15624        * @param {string[]} newValues
15625        * @param handlers     
15626        */
15627       update: function (newValues, handlers) {
15628           this.isLoaded();
15629           var contentBody = {}, contentBodyInner = [], i, innerObject = {};
15630     
15631           contentBody[this.getRestType()] = {
15632           };
15633     
15634           for (i in newValues) {
15635               if (newValues.hasOwnProperty(i)) {
15636                   innerObject = {
15637                       "uri": newValues[i]
15638                   };
15639                   contentBodyInner.push(innerObject);
15640               }
15641           }
15642     
15643           contentBody[this.getRestType()] = {
15644               "ReasonCode" : contentBodyInner
15645           };
15646     
15647           // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
15648           handlers = handlers || {};
15649     
15650           this.restRequest(this.getRestUrl(), {
15651               method: 'PUT',
15652               success: this.createPutSuccessHandler(this, contentBody, handlers.success),
15653               error: handlers.error,
15654               content: contentBody
15655           });
15656     
15657           return this; // Allow cascading
15658       }
15659   });
15660   
15661     window.finesse = window.finesse || {};
15662     window.finesse.restservices = window.finesse.restservices || {};
15663     window.finesse.restservices.TeamNotReadyReasonCodes = TeamNotReadyReasonCodes;
15664     
15665   return TeamNotReadyReasonCodes;
15666 });
15667 
15668 /**
15669  * JavaScript representation of the Finesse Team Wrap Up Reason object.
15670  *
15671  * @requires finesse.clientservices.ClientServices
15672  * @requires Class
15673  * @requires finesse.FinesseBase
15674  * @requires finesse.restservices.RestBase
15675  */
15676 /** @private */
15677 define('restservices/TeamWrapUpReason',['restservices/RestBase'], function (RestBase) {
15678 
15679     var TeamWrapUpReason = RestBase.extend({
15680 
15681     /**
15682      * @class
15683      * JavaScript representation of a TeamWrapUpReason object. Also exposes
15684      * methods to operate on the object against the server.
15685      *
15686      * @param {Object} options
15687      *     An object with the following properties:<ul>
15688      *         <li><b>id:</b> The id of the object being constructed</li>
15689      *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
15690      *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
15691      *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
15692      *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
15693      *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
15694      *             <li><b>status:</b> {Number} The HTTP status code returned</li>
15695      *             <li><b>content:</b> {String} Raw string of response</li>
15696      *             <li><b>object:</b> {Object} Parsed object of response</li>
15697      *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
15698      *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
15699      *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
15700      *             </ul></li>
15701      *         </ul></li>
15702      *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
15703      * @constructs
15704      **/
15705     init: function (options) {
15706         this._super(options);
15707     },
15708 
15709     /**
15710      * @private
15711      * Gets the REST class for the current object - this is the TeamWrapUpReason class.
15712      * @returns {Object} The TeamWrapUpReason class.
15713      */
15714     getRestClass: function () {
15715         return TeamWrapUpReason;
15716     },
15717 
15718     /**
15719      * @private
15720      * Gets the REST type for the current object - this is a "WrapUpReason".
15721      * @returns {String} The WrapUpReason string.
15722      */
15723     getRestType: function () {
15724         return "WrapUpReason";
15725     },
15726 
15727     /**
15728      * @private
15729      * Override default to indicate that this object doesn't support making
15730      * requests.
15731      */
15732     supportsRequests: false,
15733 
15734     /**
15735      * @private
15736      * Override default to indicate that this object doesn't support subscriptions.
15737      */
15738     supportsSubscriptions: false,
15739 
15740     /**
15741      * Getter for the label.
15742      * @returns {String} The label.
15743      */
15744     getLabel: function () {
15745         this.isLoaded();
15746         return this.getData().label;
15747     },
15748 
15749     /**
15750      * @private
15751      * Getter for the forAll value.
15752      * @returns {Boolean} True if global
15753      */
15754     getForAll: function () {
15755         this.isLoaded();
15756         return this.getData().forAll;
15757     },
15758 
15759     /**
15760      * @private
15761      * Getter for the Uri value.
15762      * @returns {String} The Uri.
15763      */
15764     getUri: function () {
15765         this.isLoaded();
15766         return this.getData().uri;
15767     }
15768 	});
15769 
15770     window.finesse = window.finesse || {};
15771     window.finesse.restservices = window.finesse.restservices || {};
15772     window.finesse.restservices.TeamWrapUpReason = TeamWrapUpReason;
15773 
15774     return TeamWrapUpReason;
15775 });
15776 
15777 /**
15778 * JavaScript representation of the Finesse Team Wrap-Up Reasons collection
15779 * object which contains a list of Wrap-Up Reasons objects.
15780  *
15781  * @requires finesse.clientservices.ClientServices
15782  * @requires Class
15783  * @requires finesse.FinesseBase
15784  * @requires finesse.restservices.RestBase
15785  * @requires finesse.restservices.Dialog
15786  * @requires finesse.restservices.RestCollectionBase
15787  */
15788 /** @private */
15789 define('restservices/TeamWrapUpReasons',[
15790     'restservices/RestCollectionBase',
15791     'restservices/RestBase',
15792     'restservices/TeamWrapUpReason'
15793 ],
15794 function (RestCollectionBase, RestBase, TeamWrapUpReason) {
15795 
15796     var TeamWrapUpReasons = RestCollectionBase.extend({
15797 
15798     /**
15799      * @class
15800      * JavaScript representation of a TeamWrapUpReasons collection object. Also exposes
15801      * methods to operate on the object against the server.
15802      *
15803      * @param {Object} options
15804      *     An object with the following properties:<ul>
15805      *         <li><b>id:</b> The id of the object being constructed</li>
15806      *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
15807      *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
15808      *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
15809      *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
15810      *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
15811      *             <li><b>status:</b> {Number} The HTTP status code returned</li>
15812      *             <li><b>content:</b> {String} Raw string of response</li>
15813      *             <li><b>object:</b> {Object} Parsed object of response</li>
15814      *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
15815      *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
15816      *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
15817      *             </ul></li>
15818      *         </ul></li>
15819      *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
15820      * @constructs
15821      **/
15822     init: function (options) {
15823         this._super(options);
15824     },
15825 
15826     /**
15827      * @private
15828      * Gets the REST class for the current object - this is the TeamWrapUpReasons class.
15829      */
15830     getRestClass: function () {
15831         return TeamWrapUpReasons;
15832     },
15833 
15834     /**
15835      * @private
15836      * Gets the REST class for the objects that make up the collection. - this
15837      * is the TeamWrapUpReason class.
15838      */
15839     getRestItemClass: function () {
15840         return TeamWrapUpReason;
15841     },
15842 
15843     /**
15844      * @private
15845      * Gets the REST type for the current object - this is a "WrapUpReasons".
15846      */
15847     getRestType: function () {
15848         return "WrapUpReasons";
15849     },
15850 
15851     /**
15852      * @private
15853      * Gets the REST type for the objects that make up the collection - this is "WrapUpReason".
15854      */
15855     getRestItemType: function () {
15856         return "WrapUpReason";
15857     },
15858 
15859     /**
15860      * @private
15861      * Override default to indicates that the collection supports making
15862      * requests.
15863      */
15864     supportsRequests: true,
15865 
15866     /**
15867      * @private
15868      * Override default to indicate that this object doesn't support subscriptions.
15869      */
15870     supportsRestItemSubscriptions: false,
15871 
15872     /**
15873      * Retrieve the Team Wrap Up Reasons.
15874      *
15875      * @returns {finesse.restservices.TeamWrapUpReasons}
15876      *     This TeamWrapUpReasons object to allow cascading.
15877      */
15878     get: function () {
15879         // set loaded to false so it will rebuild the collection after the get
15880         this._loaded = false;
15881         // reset collection
15882         this._collection = {};
15883         // perform get
15884         this._synchronize();
15885         return this;
15886     },
15887 
15888     /**
15889      * Set up the PutSuccessHandler for TeamWrapUpReasons
15890      * @param {Object} wrapUpReasons
15891      * @param {Object} contentBody
15892      * @param successHandler
15893      * @returns response
15894      */
15895     createPutSuccessHandler: function (wrapUpReasons, contentBody, successHandler) {
15896         return function (rsp) {
15897             // Update internal structure based on response. Here we
15898             // inject the contentBody from the PUT request into the
15899             // rsp.object element to mimic a GET as a way to take
15900             // advantage of the existing _processResponse method.
15901             rsp.object = contentBody;
15902             
15903             wrapUpReasons._processResponse(rsp);
15904 
15905             //Remove the injected contentBody object before cascading response
15906             rsp.object = {};
15907 
15908             //cascade response back to consumer's response handler
15909             successHandler(rsp);
15910         };
15911     },
15912 
15913     /**    
15914      * Perform the REST API PUT call to update the reason code assignments for the team
15915      * @param {String Array} newValues
15916      * @param handlers
15917      * @returns {Object} this
15918      */
15919     update: function (newValues, handlers) {
15920         this.isLoaded();
15921         var contentBody = {}, contentBodyInner = [], i, innerObject = {};
15922 
15923         contentBody[this.getRestType()] = {
15924         };
15925 
15926         for (i in newValues) {
15927             if (newValues.hasOwnProperty(i)) {
15928                 innerObject = {
15929                     "uri": newValues[i]
15930                 };
15931                 contentBodyInner.push(innerObject);
15932             }
15933         }
15934 
15935         contentBody[this.getRestType()] = {
15936             "WrapUpReason" : contentBodyInner
15937         };
15938 
15939         // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
15940         handlers = handlers || {};
15941 
15942         this.restRequest(this.getRestUrl(), {
15943             method: 'PUT',
15944             success: this.createPutSuccessHandler(this, contentBody, handlers.success),
15945             error: handlers.error,
15946             content: contentBody
15947         });
15948 
15949         return this; // Allow cascading
15950     }
15951 	});
15952 
15953     window.finesse = window.finesse || {};
15954     window.finesse.restservices = window.finesse.restservices || {};
15955     window.finesse.restservices.TeamWrapUpReasons = TeamWrapUpReasons;
15956 
15957     return TeamWrapUpReasons;
15958 });
15959 
15960 /**
15961  * JavaScript representation of a TeamSignOutReasonCode.
15962  *
15963  * @requires finesse.clientservices.ClientServices
15964  * @requires Class
15965  * @requires finesse.FinesseBase
15966  * @requires finesse.restservices.RestBase
15967  */
15968 
15969 /** @private */
15970 define('restservices/TeamSignOutReasonCode',['restservices/RestBase'], function (RestBase) {
15971     var TeamSignOutReasonCode = RestBase.extend({
15972 
15973         /**
15974          * @class
15975          * JavaScript representation of a TeamSignOutReasonCode object. Also exposes
15976          * methods to operate on the object against the server.
15977          *
15978          * @param {Object} options
15979          *     An object with the following properties:<ul>
15980          *         <li><b>id:</b> The id of the object being constructed</li>
15981          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
15982          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
15983          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
15984          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
15985          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
15986          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
15987          *             <li><b>content:</b> {String} Raw string of response</li>
15988          *             <li><b>object:</b> {Object} Parsed object of response</li>
15989          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
15990          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
15991          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
15992          *             </ul></li>
15993          *         </ul></li>
15994          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
15995          * @constructs
15996          * @ignore
15997          **/
15998         init: function (options) {
15999             this._super(options);
16000         },
16001 
16002         /**
16003          * @private
16004          * Gets the REST class for the current object - this is the TeamSignOutReasonCode class.
16005          * @returns {Object} The TeamSignOutReasonCode class.
16006          */
16007         getRestClass: function () {
16008             return TeamSignOutReasonCode;
16009         },
16010 
16011         /**
16012          * @private
16013          * Gets the REST type for the current object - this is a "ReasonCode".
16014          * @returns {String} The ReasonCode string.
16015          */
16016         getRestType: function () {
16017             return "ReasonCode";
16018         },
16019 
16020         /**
16021          * @private
16022          * Override default to indicate that this object doesn't support making
16023          * requests.
16024          */
16025         supportsRequests: false,
16026 
16027         /**
16028          * @private
16029          * Override default to indicate that this object doesn't support subscriptions.
16030          */
16031         supportsSubscriptions: false,
16032 
16033         /**
16034          * Getter for the category.
16035          * @returns {String} The category.
16036          */
16037         getCategory: function () {
16038             this.isLoaded();
16039             return this.getData().category;
16040         },
16041 
16042         /**
16043          * Getter for the code.
16044          * @returns {String} The code.
16045          */
16046         getCode: function () {
16047             this.isLoaded();
16048             return this.getData().code;
16049         },
16050 
16051         /**
16052          * Getter for the label.
16053          * @returns {String} The label.
16054          */
16055         getLabel: function () {
16056             this.isLoaded();
16057             return this.getData().label;
16058         },
16059 
16060         /**
16061          * Getter for the forAll value.
16062          * @returns {String} The forAll.
16063          */
16064         getForAll: function () {
16065             this.isLoaded();
16066             return this.getData().forAll;
16067         },
16068 
16069         /**
16070          * Getter for the Uri value.
16071          * @returns {String} The Uri.
16072          */
16073         getUri: function () {
16074             this.isLoaded();
16075             return this.getData().uri;
16076         }
16077 
16078     });
16079 
16080     window.finesse = window.finesse || {};
16081     window.finesse.restservices = window.finesse.restservices || {};
16082     window.finesse.restservices.TeamSignOutReasonCode = TeamSignOutReasonCode;
16083     
16084     return TeamSignOutReasonCode;
16085 });
16086 
16087 /**
16088 * JavaScript representation of the TeamSignOutReasonCodes collection
16089 * object which contains a list of TeamSignOutReasonCode objects.
16090  *
16091  * @requires finesse.clientservices.ClientServices
16092  * @requires Class
16093  * @requires finesse.FinesseBase
16094  * @requires finesse.restservices.RestBase
16095  * @requires finesse.restservices.Dialog
16096  * @requires finesse.restservices.RestCollectionBase
16097  */
16098 
16099 /** @private */
16100 define('restservices/TeamSignOutReasonCodes',[
16101     'restservices/RestCollectionBase',
16102     'restservices/RestBase',
16103     'restservices/TeamSignOutReasonCode'
16104 ],
16105 function (RestCollectionBase, RestBase, TeamSignOutReasonCode) {
16106     
16107     var TeamSignOutReasonCodes = RestCollectionBase.extend({
16108         /**
16109          * @class
16110          * JavaScript representation of a TeamSignOutReasonCodes collection object. Also exposes
16111          * methods to operate on the object against the server.
16112          *
16113          * @param {Object} options
16114          *     An object with the following properties:<ul>
16115          *         <li><b>id:</b> The id of the object being constructed</li>
16116          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
16117          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
16118          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
16119          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
16120          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
16121          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
16122          *             <li><b>content:</b> {String} Raw string of response</li>
16123          *             <li><b>object:</b> {Object} Parsed object of response</li>
16124          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
16125          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
16126          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
16127          *             </ul></li>
16128          *         </ul></li>
16129          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
16130          * @constructs
16131          **/
16132         init: function (options) {
16133             this._super(options);
16134         },
16135 
16136         /**
16137          * @private
16138          * Gets the REST class for the current object - this is the TeamSignOutReasonCodes class.
16139          */
16140         getRestClass: function () {
16141             return TeamSignOutReasonCodes;
16142         },
16143 
16144         /**
16145          * @private
16146          * Gets the REST class for the objects that make up the collection. - this
16147          * is the TeamSignOutReasonCode class.
16148          */
16149         getRestItemClass: function () {
16150             return TeamSignOutReasonCode;
16151         },
16152 
16153         /**
16154          * @private
16155          * Gets the REST type for the current object - this is a "ReasonCodes".
16156          */
16157         getRestType: function () {
16158             return "ReasonCodes";
16159         },
16160 
16161         /**
16162          * Overrides the parent class.  Returns the url for the SignOutReasonCodes resource
16163          */
16164         getRestUrl: function () {
16165             var restObj = this._restObj, restUrl = "";
16166 
16167             //Prepend the base REST object if one was provided.
16168             //Otherwise prepend with the default webapp name.
16169             if (restObj instanceof RestBase) {
16170                 restUrl += restObj.getRestUrl();
16171             } else {
16172                 restUrl += "/finesse/api";
16173             }
16174             //Append the REST type.
16175             restUrl += "/ReasonCodes?category=LOGOUT";
16176             //Append ID if it is not undefined, null, or empty.
16177             if (this._id) {
16178                 restUrl += "/" + this._id;
16179             }
16180             return restUrl;
16181         },
16182 
16183         /**
16184          * @private
16185          * Gets the REST type for the objects that make up the collection - this is "ReasonCode".
16186          */
16187         getRestItemType: function () {
16188             return "ReasonCode";
16189         },
16190 
16191         /**
16192          * @private
16193          * Override default to indicates that the collection supports making requests.
16194          */
16195         supportsRequests: true,
16196 
16197         /**
16198          * @private
16199          * Override default to indicates that the collection does not subscribe to its objects.
16200          */
16201         supportsRestItemSubscriptions: false,
16202 
16203         /**
16204          * Retrieve the Sign Out Reason Codes.
16205          *
16206          * @returns {finesse.restservices.TeamSignOutReasonCodes}
16207          *     This TeamSignOutReasonCodes object to allow cascading.
16208          */
16209         get: function () {
16210             // set loaded to false so it will rebuild the collection after the get
16211             this._loaded = false;
16212             // reset collection
16213             this._collection = {};
16214             // perform get
16215             this._synchronize();
16216             return this;
16217         },
16218 
16219         /* We only use PUT and GET on Reason Code team assignments
16220          * @param {Object} contact
16221          * @param {Object} contentBody
16222          * @param {Function} successHandler
16223          */
16224         createPutSuccessHandler: function (contact, contentBody, successHandler) {
16225             return function (rsp) {
16226                 // Update internal structure based on response. Here we
16227                 // inject the contentBody from the PUT request into the
16228                 // rsp.object element to mimic a GET as a way to take
16229                 // advantage of the existing _processResponse method.
16230                 rsp.object = contentBody;
16231                 contact._processResponse(rsp);
16232 
16233                 //Remove the injected contentBody object before cascading response
16234                 rsp.object = {};
16235 
16236                 //cascade response back to consumer's response handler
16237                 successHandler(rsp);
16238             };
16239         },
16240 
16241         /**
16242          * Update - This should be all that is needed.
16243          * @param {Object} newValues
16244          * @param {Object} handlers
16245          * @returns {finesse.restservices.TeamSignOutReasonCodes}
16246          *     This TeamSignOutReasonCodes object to allow cascading.
16247          */
16248         update: function (newValues, handlers) {
16249             this.isLoaded();
16250             var contentBody = {}, contentBodyInner = [], i, innerObject = {};
16251 
16252             contentBody[this.getRestType()] = {
16253             };
16254 
16255             for (i in newValues) {
16256                 if (newValues.hasOwnProperty(i)) {
16257                     innerObject = {
16258                         "uri": newValues[i]
16259                     };
16260                     contentBodyInner.push(innerObject);
16261                 }
16262             }
16263 
16264             contentBody[this.getRestType()] = {
16265                 "ReasonCode" : contentBodyInner
16266             };
16267 
16268             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
16269             handlers = handlers || {};
16270 
16271             this.restRequest(this.getRestUrl(), {
16272                 method: 'PUT',
16273                 success: this.createPutSuccessHandler(this, contentBody, handlers.success),
16274                 error: handlers.error,
16275                 content: contentBody
16276             });
16277 
16278             return this; // Allow cascading
16279         }
16280 
16281     });
16282     
16283     window.finesse = window.finesse || {};
16284     window.finesse.restservices = window.finesse.restservices || {};
16285     window.finesse.restservices.TeamSignOutReasonCodes = TeamSignOutReasonCodes;
16286     
16287     return TeamSignOutReasonCodes;
16288 });
16289 
16290 /**
16291  * JavaScript representation of the Finesse PhoneBook Assignment object.
16292  *
16293  * @requires finesse.clientservices.ClientServices
16294  * @requires Class
16295  * @requires finesse.FinesseBase
16296  * @requires finesse.restservices.RestBase
16297  */
16298 
16299 /**
16300  * The following comment prevents JSLint errors concerning undefined global variables.
16301  * It tells JSLint that these identifiers are defined elsewhere.
16302  */
16303 /*jslint bitwise:true, browser:true, nomen:true, regexp:true, sloppy:true, white:true */
16304 
16305 /** The following comment is to prevent jslint errors about 
16306  * using variables before they are defined.
16307  */
16308 /*global $, jQuery, Handlebars, dojox, dojo, finesse */
16309 
16310 /** @private */
16311 define('restservices/TeamPhoneBook',['restservices/RestBase'], function (RestBase) {
16312     var TeamPhoneBook = RestBase.extend({
16313 
16314         /**
16315          * @class
16316          * JavaScript representation of a PhoneBook object. Also exposes
16317          * methods to operate on the object against the server.
16318          *
16319          * @param {Object} options
16320          *     An object with the following properties:<ul>
16321          *         <li><b>id:</b> The id of the object being constructed</li>
16322          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
16323          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
16324          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
16325          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
16326          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
16327          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
16328          *             <li><b>content:</b> {String} Raw string of response</li>
16329          *             <li><b>object:</b> {Object} Parsed object of response</li>
16330          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
16331          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
16332          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
16333          *             </ul></li>
16334          *         </ul></li>
16335          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
16336          * @constructs
16337          **/
16338         init: function (options) {
16339             this._super(options);
16340         },
16341 
16342         /**
16343          * @private
16344          * Gets the REST class for the current object - this is the PhoneBooks class.
16345          * @returns {Object} The PhoneBooks class.
16346          */
16347         getRestClass: function () {
16348             return TeamPhoneBook;
16349         },
16350 
16351         /**
16352          * @private
16353          * Gets the REST type for the current object - this is a "PhoneBook".
16354          * @returns {String} The PhoneBook string.
16355          */
16356         getRestType: function () {
16357             return "PhoneBook";
16358         },
16359 
16360         /**
16361          * @private
16362          * Override default to indicate that this object doesn't support making
16363          * requests.
16364          */
16365         supportsRequests: false,
16366 
16367         /**
16368          * @private
16369          * Override default to indicate that this object doesn't support subscriptions.
16370          */
16371         supportsSubscriptions: false,
16372 
16373         /**
16374          * Getter for the name.
16375          * @returns {String} The name.
16376          */
16377         getName: function () {
16378             this.isLoaded();
16379             return this.getData().name;
16380         },
16381 
16382         /**
16383          * Getter for the Uri value.
16384          * @returns {String} The Uri.
16385          */
16386         getUri: function () {
16387             this.isLoaded();
16388             return this.getData().uri;
16389         }
16390 
16391     });
16392 
16393     window.finesse = window.finesse || {};
16394     window.finesse.restservices = window.finesse.restservices || {};
16395     window.finesse.restservices.TeamPhoneBook = TeamPhoneBook;
16396     
16397     return TeamPhoneBook;
16398 });
16399 
16400 /**
16401 * JavaScript representation of the Finesse PhoneBook Assignments collection
16402 * object which contains a list of Not Ready Reason Codes objects.
16403  *
16404  * @requires finesse.clientservices.ClientServices
16405  * @requires Class
16406  * @requires finesse.FinesseBase
16407  * @requires finesse.restservices.RestBase
16408  * @requires finesse.restservices.Dialog
16409  * @requires finesse.restservices.RestCollectionBase
16410  */
16411 
16412 /**
16413  * The following comment prevents JSLint errors concerning undefined global variables.
16414  * It tells JSLint that these identifiers are defined elsewhere.
16415  */
16416 /*jslint bitwise:true, browser:true, nomen:true, regexp:true, sloppy:true, white:true */
16417 
16418 /** The following comment is to prevent jslint errors about 
16419  * using variables before they are defined.
16420  */
16421 /*global $, jQuery, Handlebars, dojox, dojo, finesse */
16422 
16423 /** @private */
16424 define('restservices/TeamPhoneBooks',[
16425     'restservices/RestCollectionBase',
16426     'restservices/RestBase',
16427     'restservices/TeamPhoneBook'
16428 ],
16429 function (RestCollectionBase, RestBase, TeamPhoneBook) {
16430     var TeamPhoneBooks = RestCollectionBase.extend({
16431         
16432         /**
16433          * @class
16434          * JavaScript representation of a TeamPhoneBooks collection object. Also exposes
16435          * methods to operate on the object against the server.
16436          *
16437          * @param {Object} options
16438          *     An object with the following properties:<ul>
16439          *         <li><b>id:</b> The id of the object being constructed</li>
16440          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
16441          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
16442          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
16443          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
16444          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
16445          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
16446          *             <li><b>content:</b> {String} Raw string of response</li>
16447          *             <li><b>object:</b> {Object} Parsed object of response</li>
16448          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
16449          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
16450          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
16451          *             </ul></li>
16452          *         </ul></li>
16453          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
16454          * @constructs
16455          **/
16456         init: function (options) {
16457             this._super(options);           
16458         },
16459 
16460         /**
16461          * @private
16462          * Gets the REST class for the current object - this is the TeamPhoneBooks class.
16463          */
16464         getRestClass: function () {
16465             return TeamPhoneBooks;
16466         },
16467 
16468         /**
16469          * @private
16470          * Gets the REST class for the objects that make up the collection. - this
16471          * is the TeamPhoneBooks class.
16472          */
16473         getRestItemClass: function () {
16474             return TeamPhoneBook;
16475         },
16476 
16477         /**
16478          * @private
16479          * Gets the REST type for the current object - this is a "ReasonCodes".
16480          */
16481         getRestType: function () {
16482             return "PhoneBooks";
16483         },
16484         
16485         /**
16486          * Overrides the parent class.  Returns the url for the PhoneBooks resource
16487          */
16488         getRestUrl: function () {
16489             // return ("/finesse/api/" + this.getRestType() + "?category=NOT_READY");
16490             var restObj = this._restObj,
16491             restUrl = "";
16492             //Prepend the base REST object if one was provided.
16493             if (restObj instanceof RestBase) {
16494                 restUrl += restObj.getRestUrl();
16495             }
16496             //Otherwise prepend with the default webapp name.
16497             else {
16498                 restUrl += "/finesse/api";
16499             }
16500             //Append the REST type.
16501             restUrl += "/PhoneBooks";
16502             //Append ID if it is not undefined, null, or empty.
16503             if (this._id) {
16504                 restUrl += "/" + this._id;
16505             }
16506             return restUrl;        
16507         },
16508         
16509         /**
16510          * @private
16511          * Gets the REST type for the objects that make up the collection - this is "ReasonCode".
16512          */
16513         getRestItemType: function () {
16514             return "PhoneBook";
16515         },
16516 
16517         /**
16518          * @private
16519          * Override default to indicates that the collection supports making
16520          * requests.
16521          */
16522         supportsRequests: true,
16523 
16524         /**
16525          * @private
16526          * Override default to indicates that the collection subscribes to its objects.
16527          */
16528         supportsRestItemSubscriptions: false,
16529         
16530         /**
16531          * Retrieve the Not Ready Reason Codes.
16532          *
16533          * @returns {finesse.restservices.TeamPhoneBooks}
16534          *     This TeamPhoneBooks object to allow cascading.
16535          */
16536         get: function () {
16537             // set loaded to false so it will rebuild the collection after the get
16538             /** @private */
16539             this._loaded = false;
16540             // reset collection
16541             /** @private */
16542             this._collection = {};
16543             // perform get
16544             this._synchronize();
16545             return this;
16546         },
16547 
16548         /* We only use PUT and GET on Reason Code team assignments 
16549          */
16550         createPutSuccessHandler: function(contact, contentBody, successHandler){
16551             return function (rsp) {
16552                 // Update internal structure based on response. Here we
16553                 // inject the contentBody from the PUT request into the
16554                 // rsp.object element to mimic a GET as a way to take
16555                 // advantage of the existing _processResponse method.
16556                 rsp.object = contentBody;
16557                 contact._processResponse(rsp);
16558 
16559                 //Remove the injected Contact object before cascading response
16560                 rsp.object = {};
16561                 
16562                 //cascade response back to consumer's response handler
16563                 successHandler(rsp);
16564             };
16565         },
16566 
16567         /**
16568          * Update - This should be all that is needed.
16569          */
16570         update: function (newValues, handlers) {
16571             this.isLoaded();
16572             var contentBody = {}, contentBodyInner = [], i, innerObject;
16573 
16574             contentBody[this.getRestType()] = {
16575             };
16576         
16577             for (i in newValues) {
16578                 if (newValues.hasOwnProperty(i)) {
16579                     innerObject = {};
16580                     innerObject = {
16581                         "uri": newValues[i]
16582                     };
16583                     contentBodyInner.push(innerObject);
16584                 }
16585             }
16586 
16587             contentBody[this.getRestType()] = {
16588                 "PhoneBook" : contentBodyInner
16589             };
16590 
16591             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
16592             handlers = handlers || {};
16593 
16594             this.restRequest(this.getRestUrl(), {
16595                 method: 'PUT',
16596                 success: this.createPutSuccessHandler(this, contentBody, handlers.success),
16597                 error: handlers.error,
16598                 content: contentBody
16599             });
16600 
16601             return this; // Allow cascading
16602         }       
16603         
16604     });
16605         
16606     window.finesse = window.finesse || {};
16607     window.finesse.restservices = window.finesse.restservices || {};
16608     window.finesse.restservices.TeamPhoneBooks = TeamPhoneBooks;
16609     
16610     return TeamPhoneBooks;
16611 });
16612 
16613 /**
16614  * JavaScript representation of the Finesse LayoutConfig object
16615  * @requires ClientServices
16616  * @requires finesse.FinesseBase
16617  * @requires finesse.restservices.RestBase
16618  */
16619 
16620 /** @private */
16621 define('restservices/LayoutConfig',['restservices/RestBase'], function (RestBase) {
16622     /** @private */
16623 	var LayoutConfig = RestBase.extend({
16624 
16625 		/**
16626 		 * @class
16627 		 * JavaScript representation of a LayoutConfig object. Also exposes methods to operate
16628 		 * on the object against the server.
16629 		 *
16630 		 * @param {String} id
16631 		 *     Not required...
16632 		 * @param {Object} callbacks
16633 		 *     An object containing callbacks for instantiation and runtime
16634 		 * @param {Function} callbacks.onLoad(this)
16635 		 *     Callback to invoke upon successful instantiation
16636 		 * @param {Function} callbacks.onLoadError(rsp)
16637 		 *     Callback to invoke on instantiation REST request error
16638 		 *     as passed by finesse.clientservices.ClientServices.ajax()
16639 		 *     {
16640 		 *         status: {Number} The HTTP status code returned
16641 		 *         content: {String} Raw string of response
16642 		 *         object: {Object} Parsed object of response
16643 		 *         error: {Object} Wrapped exception that was caught
16644 		 *         error.errorType: {String} Type of error that was caught
16645 		 *         error.errorMessage: {String} Message associated with error
16646 		 *     }
16647 		 * @param {Function} callbacks.onChange(this)
16648 		 *     Callback to invoke upon successful update
16649 		 * @param {Function} callbacks.onError(rsp)
16650 		 *     Callback to invoke on update error (refresh or event)
16651 		 *     as passed by finesse.clientservices.ClientServices.ajax()
16652 		 *     {
16653 		 *         status: {Number} The HTTP status code returned
16654 		 *         content: {String} Raw string of response
16655 		 *         object: {Object} Parsed object of response
16656 		 *         error: {Object} Wrapped exception that was caught
16657 		 *         error.errorType: {String} Type of error that was caught
16658 		 *         error.errorMessage: {String} Message associated with error
16659 		 *     }
16660 		 *  
16661 	     * @constructs
16662 		 */
16663 		init: function (callbacks) {
16664 			this._super("", callbacks);
16665 			//when post is performed and id is empty
16666 			/*if (id === "") {
16667 				this._loaded = true;
16668 			}*/
16669 	        this._layoutxml = {};
16670 		},
16671 	
16672 		/**
16673 		 * Returns REST class of LayoutConfig object
16674 		 */
16675 		getRestClass: function () {
16676 			return LayoutConfig;
16677 		},
16678 	
16679 		/**
16680 		 * The type of this REST object is LayoutConfig
16681 		 */
16682 		getRestType: function () {
16683 			return "LayoutConfig";
16684 		},
16685 
16686 		/**
16687 		 * Gets the REST URL of this object.
16688 		 * 
16689 		 * If the parent has an id, the id is appended.
16690 		 * On occasions of POST, it will not have an id.
16691 		 */
16692 		getRestUrl: function () {
16693 			var layoutUri = "/finesse/api/" + this.getRestType() + "/default";
16694 			/*if (this._id) {
16695 				layoutUri = layoutUri + "/" + this._id;
16696 			}*/
16697 			return layoutUri;
16698 		},
16699 	
16700 		/**
16701 		 * This API does not support subscription
16702 		 */
16703 		supportsSubscriptions: false,
16704 		
16705 		keepRestResponse: true,
16706 
16707 
16708 		/**
16709 		 * Gets finesselayout.xml retrieved from the API call
16710 		 */
16711 		getLayoutxml: function () {
16712 			this.isLoaded();
16713 			var layoutxml = this.getData().layoutxml;
16714 
16715             // We need to unescape everything that is unallowed in xml so consumers don't have to deal with it (used in tandem with update())
16716             layoutxml = layoutxml.replace(/&/g,"&");
16717 
16718             return layoutxml;
16719 		},
16720 	
16721 		/**
16722 		 * Gets the type of this LayoutConfig object
16723 		 */
16724 		/*
16725 		getType: function () {
16726 			this.isLoaded();
16727 			return this.getData().type;
16728 		},*/
16729 	
16730 		/**
16731 		 * Retrieve the LayoutConfig settings.
16732 		 * If the id is not provided the API call will fail.
16733 		 * @returns {LayoutConfig}
16734 		 *     This LayoutConfig object to allow cascading.
16735 		 */
16736 		get: function () {      
16737 			this._synchronize();
16738 			return this;
16739 		},
16740 
16741 		/**
16742 		 * Closure handle updating of the internal data for the LayoutConfig object
16743 		 * upon a successful update (PUT) request before calling the intended
16744 		 * success handler provided by the consumer
16745 		 * 
16746 		 * @param {Object}
16747 		 *            layoutconfig Reference to this LayoutConfig object
16748 		 * @param {Object}
16749 		 *            LayoutConfig Object that contains the  settings to be
16750 		 *            submitted in the api request
16751 		 * @param {Function}
16752 		 *            successHandler The success handler specified by the consumer
16753 		 *            of this object
16754 		 * @returns {LayoutConfig} This LayoutConfig object to allow cascading
16755 		 */
16756 	
16757 		createPutSuccessHandler: function (layoutconfig, contentBody, successHandler) {
16758 			return function (rsp) {			
16759 				// Update internal structure based on response. Here we
16760 				// inject the contentBody from the PUT request into the
16761 				// rsp.object element to mimic a GET as a way to take
16762 				// advantage of the existing _processResponse method.
16763 				rsp.content = contentBody;
16764 				rsp.object.LayoutConfig = {};
16765 				rsp.object.LayoutConfig.finesseLayout = contentBody;
16766 				layoutconfig._processResponse(rsp);
16767 	
16768 				//Remove the injected layoutConfig object before cascading response
16769 				rsp.object.LayoutConfig = {};
16770 	
16771 				//cascade response back to consumer's response handler
16772 				successHandler(rsp);
16773 			};
16774 		},
16775 	
16776 		/**
16777 		 *  Update LayoutConfig
16778 		 * @param {Object} finesselayout
16779 		 *     The XML for FinesseLayout being stored
16780 		 * 
16781 		 * @param {Object} handlers
16782 		 *     An object containing callback handlers for the request. Optional.
16783 		 * @param {Function} options.success(rsp)
16784 		 *     A callback function to be invoked for a successful request.
16785 		 *     {
16786 		 *         status: {Number} The HTTP status code returned
16787 		 *         content: {String} Raw string of response
16788 		 *         object: {Object} Parsed object of response
16789 		 *     }
16790 		 * @param {Function} options.error(rsp)
16791 		 *     A callback function to be invoked for an unsuccessful request.
16792 		 *     {
16793 		 *         status: {Number} The HTTP status code returned
16794 		 *         content: {String} Raw string of response
16795 		 *         object: {Object} Parsed object of response (HTTP errors)
16796 		 *         error: {Object} Wrapped exception that was caught
16797 		 *         error.errorType: {String} Type of error that was caught
16798 		 *         error.errorMessage: {String} Message associated with error
16799 		 *     }
16800 		 * @returns {finesse.restservices.LayoutConfig}
16801 		 *     This LayoutConfig object to allow cascading
16802 		 */
16803 	
16804 		update: function (layoutxml, handlers) {
16805 			this.isLoaded();
16806 
16807 			
16808 			var contentBody = {}, 
16809 			//Created a regex (re) to scoop out just the gadget URL (without before and after whitespace characters)
16810 			re = /<gadget>\s*(\S+)\s*<\/gadget>/g;
16811 
16812 			// We need to escape everything that is unallowed in xml so consumers don't have to deal with it (used in tandem with getLayoutxml())
16813 			layoutxml = layoutxml.replace(/&(?!amp;)/g, "&");
16814 
16815 			//used the regex (re) to the update and save the layoutxml with the improved gadget URL (without before/after whitespace)
16816 			layoutxml = layoutxml.replace(re, "<gadget>$1</gadget>");
16817 
16818 			contentBody[this.getRestType()] = {
16819 				"layoutxml": finesse.utilities.Utilities.translateHTMLEntities(layoutxml, true)
16820 			};
16821 
16822 			// Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
16823 			handlers = handlers || {};
16824 
16825 			this.restRequest(this.getRestUrl(), {
16826 				method: 'PUT',
16827 				success: this.createPutSuccessHandler(this, layoutxml, handlers.success),
16828 				error: handlers.error,
16829 				content: contentBody
16830 			});
16831 
16832 			return this; // Allow cascading
16833 		}
16834 	
16835 		/**
16836 		 *TODO createPostSuccessHandler needs to be debugged to make it working
16837 		 * Closure handle creating new  LayoutConfig object
16838 		 * upon a successful create (POST) request before calling the intended
16839 		 * success handler provided by the consumer
16840 		 * 
16841 		 * @param {Object}
16842 		 *            layoutconfig Reference to this LayoutConfig object
16843 		 * @param {Object}
16844 		 *            LayoutConfig Object that contains the  settings to be
16845 		 *            submitted in the api request
16846 		 * @param {Function}
16847 		 *            successHandler The success handler specified by the consumer
16848 		 *            of this object
16849 		 * @returns {finesse.restservices.LayoutConfig} This LayoutConfig object to allow cascading
16850 		 */
16851 	/*
16852 		createPostSuccessHandler: function (layoutconfig, contentBody, successHandler) {
16853 			return function (rsp) {
16854 	
16855 				rsp.object = contentBody;
16856 				layoutconfig._processResponse(rsp);
16857 	
16858 				//Remove the injected layoutConfig object before cascading response
16859 				rsp.object = {};
16860 	
16861 				//cascade response back to consumer's response handler
16862 				successHandler(rsp);
16863 			};
16864 		}, */
16865 	
16866 		/**
16867 		 * TODO Method needs to be debugged to make POST working
16868 		 *  Add LayoutConfig
16869 		 * @param {Object} finesselayout
16870 		 *     The XML for FinesseLayout being stored
16871 		 * 
16872 		 * @param {Object} handlers
16873 		 *     An object containing callback handlers for the request. Optional.
16874 		 * @param {Function} options.success(rsp)
16875 		 *     A callback function to be invoked for a successful request.
16876 		 *     {
16877 		 *         status: {Number} The HTTP status code returned
16878 		 *         content: {String} Raw string of response
16879 		 *         object: {Object} Parsed object of response
16880 		 *     }
16881 		 * @param {Function} options.error(rsp)
16882 		 *     A callback function to be invoked for an unsuccessful request.
16883 		 *     {
16884 		 *         status: {Number} The HTTP status code returned
16885 		 *         content: {String} Raw string of response
16886 		 *         object: {Object} Parsed object of response (HTTP errors)
16887 		 *         error: {Object} Wrapped exception that was caught
16888 		 *         error.errorType: {String} Type of error that was caught
16889 		 *         error.errorMessage: {String} Message associated with error
16890 		 *     }
16891 		 * @returns {finesse.restservices.LayoutConfig}
16892 		 *     This LayoutConfig object to allow cascading
16893 		 */
16894 	/*
16895 		add: function (layoutxml, handlers) {
16896 			this.isLoaded();
16897 			var contentBody = {};
16898 	
16899 	
16900 			contentBody[this.getRestType()] = {
16901 					"layoutxml": layoutxml,
16902 					"type": "current"
16903 			    };
16904 	
16905 			// Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
16906 			handlers = handlers || {};
16907 	
16908 			this.restRequest(this.getRestUrl(), {
16909 				method: 'POST',
16910 				success: this.createPostSuccessHandler(this, contentBody, handlers.success),
16911 				error: handlers.error,
16912 				content: contentBody
16913 			});
16914 	
16915 			return this; // Allow cascading
16916 		} */
16917 	});
16918 	
16919 	window.finesse = window.finesse || {};
16920     window.finesse.restservices = window.finesse.restservices || {};
16921     window.finesse.restservices.LayoutConfig = LayoutConfig;
16922     
16923 	return LayoutConfig;
16924 	
16925 });
16926 
16927 /**
16928  * JavaScript representation of the Finesse LayoutConfig object for a Team.
16929  *
16930  * @requires finesse.clientservices.ClientServices
16931  * @requires Class
16932  * @requires finesse.FinesseBase
16933  * @requires finesse.restservices.RestBase
16934  * @requires finesse.utilities.Utilities
16935  * @requires finesse.restservices.LayoutConfig
16936  */
16937 
16938 /** The following comment is to prevent jslint errors about 
16939  * using variables before they are defined.
16940  */
16941 /*global Exception */
16942 
16943 /** @private */
16944 define('restservices/TeamLayoutConfig',[
16945     'restservices/RestBase',
16946     'utilities/Utilities',
16947     'restservices/LayoutConfig'
16948 ],
16949 function (RestBase, Utilities, LayoutConfig) {
16950     
16951     var TeamLayoutConfig = RestBase.extend({
16952       // Keep the restresponse so we can parse the layoutxml out of it in getLayoutXML()
16953       keepRestResponse: true,
16954     
16955       /**
16956        * @class
16957        * JavaScript representation of a LayoutConfig object for a Team. Also exposes
16958        * methods to operate on the object against the server.
16959        *
16960        * @param {Object} options
16961        *     An object with the following properties:<ul>
16962        *         <li><b>id:</b> The id of the object being constructed</li>
16963        *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
16964        *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
16965        *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
16966        *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
16967        *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
16968        *             <li><b>status:</b> {Number} The HTTP status code returned</li>
16969        *             <li><b>content:</b> {String} Raw string of response</li>
16970        *             <li><b>object:</b> {Object} Parsed object of response</li>
16971        *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
16972        *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
16973        *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
16974        *             </ul></li>
16975        *         </ul></li>
16976        *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
16977        * @constructs
16978        **/
16979       init: function (options) {
16980           this._super(options);
16981       },
16982     
16983       /**
16984        * @private
16985        * Gets the REST class for the current object - this is the LayoutConfigs class.
16986        * @returns {Object} The LayoutConfigs class.
16987        */
16988       getRestClass: function () {
16989           return TeamLayoutConfig;
16990       },
16991     
16992       /**
16993        * @private
16994        * Gets the REST type for the current object - this is a "LayoutConfig".
16995        * @returns {String} The LayoutConfig string.
16996        */
16997       getRestType: function () {
16998           return "TeamLayoutConfig";
16999       },
17000     
17001       /**
17002        * @private
17003        * Override default to indicate that this object doesn't support making
17004        * requests.
17005        */
17006       supportsRequests: false,
17007     
17008       /**
17009        * @private
17010        * Override default to indicate that this object doesn't support subscriptions.
17011        */
17012       supportsSubscriptions: false,
17013     
17014       /**
17015        * Getter for the category.
17016        * @returns {String} The category.
17017        */
17018       getLayoutXML: function () {
17019           this.isLoaded();
17020           var layoutxml = this.getData().layoutxml;
17021 
17022           // We need to unescape everything that is unallowed in xml so consumers don't have to deal with it (used in tandem with put())
17023           layoutxml = layoutxml.replace(/&/g,"&");
17024 
17025           return layoutxml;
17026       },
17027     
17028       /**
17029        * Getter for the code.
17030        * @returns {String} The code.
17031        */
17032       getUseDefault: function () {
17033           this.isLoaded();
17034           return this.getData().useDefault;
17035       },
17036       
17037       /**
17038        * Retrieve the TeamLayoutConfig.
17039        *
17040        * @returns {finesse.restservices.TeamLayoutConfig}
17041        */
17042       get: function () {
17043           // this._id is needed, but is not used in this object.. we're overriding getRestUrl anyway
17044           this._id = "0";
17045           // set loaded to false so it will rebuild the collection after the get
17046           this._loaded = false;
17047           // reset collection
17048           this._collection = {};
17049           // perform get
17050           this._synchronize();
17051           return this;
17052       },
17053     
17054       createPutSuccessHandler: function(contact, contentBody, successHandler){
17055           return function (rsp) {
17056               // Update internal structure based on response. Here we
17057               // inject the contentBody from the PUT request into the
17058               // rsp.object element to mimic a GET as a way to take
17059               // advantage of the existing _processResponse method.
17060               rsp.object = contentBody;
17061               contact._processResponse(rsp);
17062     
17063               //Remove the injected Contact object before cascading response
17064               rsp.object = {};
17065               
17066               //cascade response back to consumer's response handler
17067               successHandler(rsp);
17068           };
17069       },
17070       
17071       put: function (newValues, handlers) {
17072           // this._id is needed, but is not used in this object.. we're overriding getRestUrl anyway
17073           this._id = "0";
17074           this.isLoaded();
17075 
17076           // We need to escape everything that is unallowed in xml so consumers don't have to deal with it (used in tandem with getLayoutxml())
17077           var layoutxml = newValues.layoutXML.replace(/&(?!amp;)/g, "&"),
17078               contentBody = {}, 
17079               //Created a regex (re) to scoop out just the gadget URL (without before and after whitespace characters)
17080               re = /<gadget>\s*(\S+)\s*<\/gadget>/g;
17081 
17082           //used the regex (re) to the update and save the layoutxml with the improved gadget URL (without before/after whitespace)
17083           layoutxml = layoutxml.replace(re, "<gadget>$1</gadget>");
17084           
17085           contentBody[this.getRestType()] = {
17086               "useDefault": newValues.useDefault,
17087               // The LayoutConfig restservice javascript class only translates ampersands, so we'll do that also
17088               "layoutxml": finesse.utilities.Utilities.translateHTMLEntities(layoutxml, true)
17089           };
17090     
17091           // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
17092           handlers = handlers || {};
17093     
17094           this.restRequest(this.getRestUrl(), {
17095               method: 'PUT',
17096               success: this.createPutSuccessHandler(this, contentBody, handlers.success),
17097               error: handlers.error,
17098               content: contentBody
17099           });
17100     
17101           return this; // Allow cascading
17102       },
17103     
17104       getRestUrl: function(){
17105           // return team's url + /LayoutConfig
17106           // eg: /api/Team/1/LayoutConfig
17107           if(this._restObj === undefined){
17108               throw new Exception("TeamLayoutConfig instances must have a parent team object.");
17109           }
17110           return this._restObj.getRestUrl() + '/LayoutConfig';
17111       }
17112     
17113       });
17114         
17115     window.finesse = window.finesse || {};
17116     window.finesse.restservices = window.finesse.restservices || {};
17117     window.finesse.restservices.TeamLayoutConfig = TeamLayoutConfig;
17118       
17119     return TeamLayoutConfig;
17120 });
17121 
17122 /**
17123  * JavaScript representation of a TeamWorkflow.
17124  *
17125  * @requires finesse.clientservices.ClientServices
17126  * @requires Class
17127  * @requires finesse.FinesseBase
17128  * @requires finesse.restservices.RestBase
17129  */
17130 /** @private */
17131 define('restservices/TeamWorkflow',['restservices/RestBase'], function (RestBase) {
17132 
17133     var TeamWorkflow = RestBase.extend({
17134 
17135         /**
17136          * @class
17137          * JavaScript representation of a TeamWorkflow object. Also exposes
17138          * methods to operate on the object against the server.
17139          *
17140          * @param {Object} options
17141          *     An object with the following properties:<ul>
17142          *         <li><b>id:</b> The id of the object being constructed</li>
17143          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
17144          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
17145          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
17146          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
17147          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
17148          *             <li><b>status:</b> {Number} The HTTP status description returned</li>
17149          *             <li><b>content:</b> {String} Raw string of response</li>
17150          *             <li><b>object:</b> {Object} Parsed object of response</li>
17151          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
17152          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
17153          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
17154          *             </ul></li>
17155          *         </ul></li>
17156          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
17157          * @constructs
17158          **/
17159         init: function (options) {
17160             this._super(options);
17161         },
17162 
17163         /**
17164          * @private
17165          * Gets the REST class for the current object - this is the TeamWorkflow class.
17166          * @returns {Object} The TeamWorkflow class.
17167          */
17168         getRestClass: function () {
17169             return TeamWorkflow;
17170         },
17171 
17172         /**
17173          * @private
17174          * Gets the REST type for the current object - this is a "Workflow".
17175          * @returns {String} The Workflow string.
17176          */
17177         getRestType: function () {
17178             return "Workflow";
17179         },
17180 
17181         /**
17182          * @private
17183          * Override default to indicate that this object doesn't support making
17184          * requests.
17185          */
17186         supportsRequests: false,
17187 
17188         /**
17189          * @private
17190          * Override default to indicate that this object doesn't support subscriptions.
17191          */
17192         supportsSubscriptions: false,
17193 
17194         /**
17195          * Getter for the name.
17196          * @returns {String} The name.
17197          */
17198         getName: function () {
17199             this.isLoaded();
17200             return this.getData().name;
17201         },
17202 
17203         /**
17204          * Getter for the description.
17205          * @returns {String} The description.
17206          */
17207         getDescription: function () {
17208             this.isLoaded();
17209             return this.getData().description;
17210         },
17211 
17212         /**
17213          * Getter for the Uri value.
17214          * @returns {String} The Uri.
17215          */
17216         getUri: function () {
17217             this.isLoaded();
17218             return this.getData().uri;
17219         }
17220 
17221     });
17222     
17223 	window.finesse = window.finesse || {};
17224     window.finesse.restservices = window.finesse.restservices || {};
17225     window.finesse.restservices.TeamWorkflow = TeamWorkflow;
17226 
17227     return TeamWorkflow;
17228 });
17229 
17230 /**
17231 * JavaScript representation of the TeamWorkflows collection
17232 * object which contains a list of TeamWorkflow objects.
17233  *
17234  * @requires finesse.clientservices.ClientServices
17235  * @requires Class
17236  * @requires finesse.FinesseBase
17237  * @requires finesse.restservices.RestBase
17238  * @requires finesse.restservices.Dialog
17239  * @requires finesse.restservices.RestCollectionBase
17240  */
17241 /** @private */
17242 define('restservices/TeamWorkflows',[
17243     'restservices/RestCollectionBase',
17244     'restservices/TeamWorkflow',
17245     'restservices/RestBase'
17246 ],
17247 function (RestCollectionBase, TeamWorkflow, RestBase) {
17248 
17249     var TeamWorkflows = RestCollectionBase.extend({
17250     
17251         /**
17252          * @class
17253          * JavaScript representation of a TeamWorkflows collection object. Also exposes
17254          * methods to operate on the object against the server.
17255          *
17256          * @param {Object} options
17257          *     An object with the following properties:<ul>
17258          *         <li><b>id:</b> The id of the object being constructed</li>
17259          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
17260          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
17261          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
17262          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
17263          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
17264          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
17265          *             <li><b>content:</b> {String} Raw string of response</li>
17266          *             <li><b>object:</b> {Object} Parsed object of response</li>
17267          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
17268          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
17269          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
17270          *             </ul></li>
17271          *         </ul></li>
17272          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
17273          * @constructs
17274          **/
17275         init: function (options) {
17276             this._super(options);
17277         },
17278 
17279         /**
17280          * @private
17281          * Gets the REST class for the current object - this is the TeamWorkflows class.
17282          */
17283         getRestClass: function () {
17284             return TeamWorkflows;
17285         },
17286 
17287         /**
17288          * @private
17289          * Gets the REST class for the objects that make up the collection. - this
17290          * is the TeamWorkflow class.
17291          */
17292         getRestItemClass: function () {
17293             return TeamWorkflow;
17294         },
17295 
17296         /**
17297          * @private
17298          * Gets the REST type for the current object - this is a "Workflows".
17299          */
17300         getRestType: function () {
17301             return "Workflows";
17302         },
17303 
17304         /**
17305          * Overrides the parent class.  Returns the url for the Workflows resource
17306          */
17307         getRestUrl: function () {
17308             var restObj = this._restObj, restUrl = "";
17309 
17310             //Prepend the base REST object if one was provided.
17311             //Otherwise prepend with the default webapp name.
17312             if (restObj instanceof RestBase) {
17313                 restUrl += restObj.getRestUrl();
17314             } else {
17315                 restUrl += "/finesse/api/Team";
17316             }
17317             //Append ID if it is not undefined, null, or empty.
17318             if (this._id) {
17319                 restUrl += "/" + this._id;
17320             }
17321             //Append the REST type.
17322             restUrl += "/Workflows";
17323             
17324             return restUrl;
17325         },
17326 
17327         /**
17328          * @private
17329          * Gets the REST type for the objects that make up the collection - this is "Workflow".
17330          */
17331         getRestItemType: function () {
17332             return "Workflow";
17333         },
17334 
17335         /**
17336          * @private
17337          * Override default to indicates that the collection supports making requests.
17338          */
17339         supportsRequests: true,
17340 
17341         /**
17342          * @private
17343          * Override default to indicates that the collection does not subscribe to its objects.
17344          */
17345         supportsRestItemSubscriptions: false,
17346 
17347         /**
17348          * Retrieve the Sign Out Reason Codes.
17349          *
17350          * @returns {finesse.restservices.TeamWorkflows}
17351          *     This TeamWorkflows object to allow cascading.
17352          */
17353         get: function () {
17354             // set loaded to false so it will rebuild the collection after the get
17355             this._loaded = false;
17356             // reset collection
17357             this._collection = {};
17358             // perform get
17359             this._synchronize();
17360             return this;
17361         },
17362 
17363         /* We only use PUT and GET on Reason Code team assignments
17364          * @param {Object} contact
17365          * @param {Object} contentBody
17366          * @param {Function} successHandler
17367          */
17368         createPutSuccessHandler: function (contact, contentBody, successHandler) {
17369             return function (rsp) {
17370                 // Update internal structure based on response. Here we
17371                 // inject the contentBody from the PUT request into the
17372                 // rsp.object element to mimic a GET as a way to take
17373                 // advantage of the existing _processResponse method.
17374                 rsp.object = contentBody;
17375                 contact._processResponse(rsp);
17376 
17377                 //Remove the injected contentBody object before cascading response
17378                 rsp.object = {};
17379 
17380                 //cascade response back to consumer's response handler
17381                 successHandler(rsp);
17382             };
17383         },
17384 
17385         /**
17386          * Update - This should be all that is needed.
17387          * @param {Object} newValues
17388          * @param {Object} handlers
17389          * @returns {finesse.restservices.TeamWorkflows}
17390          *     This TeamWorkflows object to allow cascading.
17391          */
17392         update: function (newValues, handlers) {
17393             this.isLoaded();
17394             var contentBody = {}, contentBodyInner = [], i, innerObject = {};
17395 
17396             contentBody[this.getRestType()] = {
17397             };
17398 
17399             for (i in newValues) {
17400                 if (newValues.hasOwnProperty(i)) {
17401                     innerObject = {
17402                         "uri": newValues[i]
17403                     };
17404                     contentBodyInner.push(innerObject);
17405                 }
17406             }
17407 
17408             contentBody[this.getRestType()] = {
17409                 "Workflow" : contentBodyInner
17410             };
17411 
17412             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
17413             handlers = handlers || {};
17414 
17415             this.restRequest(this.getRestUrl(), {
17416                 method: 'PUT',
17417                 success: this.createPutSuccessHandler(this, contentBody, handlers.success),
17418                 error: handlers.error,
17419                 content: contentBody
17420             });
17421 
17422             return this; // Allow cascading
17423         }
17424 
17425     });
17426     
17427 	window.finesse = window.finesse || {};
17428     window.finesse.restservices = window.finesse.restservices || {};
17429     window.finesse.restservices.TeamWorkflows = TeamWorkflows;
17430     
17431     return TeamWorkflows;
17432 });
17433 
17434 /**
17435  * JavaScript representation of the Finesse Team REST object.
17436  *
17437  * @requires finesse.clientservices.ClientServices
17438  * @requires Class
17439  * @requires finesse.FinesseBase
17440  * @requires finesse.restservices.RestBase
17441  * @requires finesse.restservices.RestCollectionBase
17442  * @requires finesse.restservices.User
17443  * @requires finesse.restservices.Users
17444  */
17445 
17446 /**
17447  * The following comment prevents JSLint errors concerning undefined global variables.
17448  * It tells JSLint that these identifiers are defined elsewhere.
17449  */
17450 /*jslint bitwise:true, browser:true, nomen:true, regexp:true, sloppy:true, white:true */
17451 
17452 /** The following comment is to prevent jslint errors about 
17453  * using variables before they are defined.
17454  */
17455 /*global $, jQuery, Handlebars, dojox, dojo, finesse */
17456 
17457 /** @private */
17458 define('restservices/Team',[
17459     'restservices/RestBase',
17460     'utilities/Utilities',
17461     'restservices/Users',
17462     'restservices/TeamNotReadyReasonCodes',
17463     'restservices/TeamWrapUpReasons',
17464     'restservices/TeamSignOutReasonCodes',
17465     'restservices/TeamPhoneBooks',
17466     'restservices/TeamLayoutConfig',
17467     'restservices/TeamWorkflows'
17468 ],
17469 function (RestBase, Utilities, Users, TeamNotReadyReasonCodes, TeamWrapUpReasons, TeamSignOutReasonCodes, TeamPhoneBooks, TeamLayoutConfig, TeamWorkflows) {
17470     var Team = RestBase.extend(/** @lends finesse.restservices.Team.prototype */{
17471         
17472         _teamLayoutConfig: null,
17473 
17474         /**
17475          * @class
17476          * A Team is a set of Agent Users, typically supervised by one or more Supervisor Users.
17477          *
17478          * @augments finesse.restservices.RestBase
17479          * @see finesse.restservices.User#getSupervisedTeams
17480          * @see finesse.restservices.Users
17481          * @constructs
17482          */
17483         _fakeConstuctor: function () {
17484             /* This is here to hide the real init constructor from the public docs */
17485         },
17486         
17487         /**
17488          * @private
17489          * @class
17490          * JavaScript representation of a Team object. Also exposes methods to operate
17491          * on the object against the server.
17492          *
17493          * @param {Object} options
17494          *     An object with the following properties:<ul>
17495          *         <li><b>id:</b> The id of the object being constructed</li>
17496          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
17497          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
17498          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
17499          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
17500          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
17501          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
17502          *             <li><b>content:</b> {String} Raw string of response</li>
17503          *             <li><b>object:</b> {Object} Parsed object of response</li>
17504          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
17505          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
17506          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
17507          *             </ul></li>
17508          *         </ul></li>
17509          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
17510          **/
17511         init: function (options) {
17512             this._super(options);
17513         },
17514     
17515         /**
17516          * @private
17517          * Gets the REST class for the current object - this is the Team class.
17518          * @returns {Object} The Team constructor.
17519          */
17520         getRestClass: function () {
17521             return finesse.restesrvices.Team;
17522         },
17523     
17524         /**
17525          * @private
17526          * Gets the REST type for the current object - this is a "Team".
17527          * @returns {String} The Team string.
17528          */
17529         getRestType: function () {
17530             return "Team";
17531         },
17532     
17533         /**
17534          * @private
17535          * Override default to indicate that this object doesn't support making
17536          * requests.
17537          */
17538         supportsSubscriptions: false,
17539     
17540         /**
17541          * Getter for the team id.
17542          * @returns {String} The team id.
17543          */
17544         getId: function () {
17545             this.isLoaded();
17546             return this.getData().id;
17547         },
17548     
17549         /**
17550          * Getter for the team name.
17551          * @returns {String} The team name
17552          */
17553         getName: function () {
17554             this.isLoaded();
17555             return this.getData().name;
17556         },
17557     
17558         /**
17559          * @private
17560          * Getter for the team uri.
17561          * @returns {String} The team uri
17562          */
17563         getUri: function () {
17564             this.isLoaded();
17565             return this.getData().uri;        
17566         },
17567     
17568         /**
17569          * Constructs and returns a collection of Users.
17570          * @param {finesse.interfaces.RestObjectHandlers} [handlers] Object that sets callback handlers.
17571          * @returns {finesse.restservices.Users} Users collection of User objects.
17572          */
17573         getUsers: function (options) {
17574             this.isLoaded();
17575             options = options || {};
17576     
17577             options.parentObj = this;
17578             // We are using getData() instead of getData.Users because the superclass (RestCollectionBase)
17579             // for Users needs the "Users" key to validate the provided payload matches the class type.
17580             options.data = this.getData();
17581     
17582             return new Users(options);
17583         },
17584     
17585         /**
17586          * @private
17587          * Getter for a teamNotReadyReasonCodes collection object that is associated with Team.
17588          * @param callbacks
17589          * @returns {teamNotReadyReasonCodes}
17590          *     A teamNotReadyReasonCodes collection object.
17591          */
17592         getTeamNotReadyReasonCodes: function (callbacks) {
17593             var options = callbacks || {};
17594             options.parentObj = this;
17595             this.isLoaded();
17596     
17597             if (!this._teamNotReadyReasonCodes) {
17598                 this._teamNotReadyReasonCodes = new TeamNotReadyReasonCodes(options);
17599             }
17600     
17601             return this._teamNotReadyReasonCodes;
17602         },
17603     
17604         /**
17605          * @private
17606          * Getter for a teamWrapUpReasons collection object that is associated with Team.
17607          * @param callbacks
17608          * @returns {teamWrapUpReasons}
17609          *     A teamWrapUpReasons collection object.
17610          */
17611         getTeamWrapUpReasons: function (callbacks) {
17612             var options = callbacks || {};
17613             options.parentObj = this;
17614             this.isLoaded();
17615     
17616             if (!this._teamWrapUpReasons) {
17617                 this._teamWrapUpReasons = new TeamWrapUpReasons(options);
17618             }
17619     
17620             return this._teamWrapUpReasons;
17621         },
17622     
17623         /**
17624          * @private
17625          * Getter for a teamSignOutReasonCodes collection object that is associated with Team.
17626          * @param callbacks
17627          * @returns {teamSignOutReasonCodes}
17628          *     A teamSignOutReasonCodes collection object.
17629          */
17630     
17631         getTeamSignOutReasonCodes: function (callbacks) {
17632             var options = callbacks || {};
17633             options.parentObj = this;
17634             this.isLoaded();
17635     
17636             if (!this._teamSignOutReasonCodes) {
17637                 this._teamSignOutReasonCodes = new TeamSignOutReasonCodes(options);
17638             }
17639     
17640             return this._teamSignOutReasonCodes;
17641         },
17642     
17643         /**
17644          * @private
17645          * Getter for a teamPhoneBooks collection object that is associated with Team.
17646          * @param callbacks
17647          * @returns {teamPhoneBooks}
17648          *     A teamPhoneBooks collection object.
17649          */
17650         getTeamPhoneBooks: function (callbacks) {
17651             var options = callbacks || {};
17652             options.parentObj = this;
17653             this.isLoaded();
17654     
17655             if (!this._phonebooks) {
17656                 this._phonebooks = new TeamPhoneBooks(options);
17657             }
17658     
17659             return this._phonebooks;
17660         },
17661     
17662         /**
17663          * @private
17664          * Getter for a teamWorkflows collection object that is associated with Team.
17665          * @param callbacks
17666          * @returns {teamWorkflows}
17667          *     A teamWorkflows collection object.
17668          */
17669         getTeamWorkflows: function (callbacks) {
17670             var options = callbacks || {};
17671             options.parentObj = this;
17672             this.isLoaded();
17673     
17674             if (!this._workflows) {
17675                 this._workflows = new TeamWorkflows(options);
17676             }
17677     
17678             return this._workflows;
17679         },
17680     
17681         /**
17682          * @private
17683          * Getter for a teamLayoutConfig object that is associated with Team.
17684          * @param callbacks
17685          * @returns {teamLayoutConfig}
17686          */
17687         getTeamLayoutConfig: function (callbacks) {
17688             var options = callbacks || {};
17689             options.parentObj = this;
17690             this.isLoaded();
17691     
17692             if (this._teamLayoutConfig === null) {
17693                 this._teamLayoutConfig = new TeamLayoutConfig(options);
17694             }
17695     
17696             return this._teamLayoutConfig;
17697         }
17698     
17699     });
17700     
17701     window.finesse = window.finesse || {};
17702     window.finesse.restservices = window.finesse.restservices || {};
17703     window.finesse.restservices.Team = Team;
17704     
17705     return Team;    
17706 });
17707 
17708 /**
17709  * JavaScript representation of the Finesse Teams collection.
17710  * object which contains a list of Team objects
17711  * @requires finesse.clientservices.ClientServices
17712  * @requires Class
17713  * @requires finesse.FinesseBase
17714  * @requires finesse.restservices.RestBase
17715  * @requires finesse.restservices.RestCollectionBase
17716  */
17717 
17718 /** @private */
17719 define('restservices/Teams',[
17720     'restservices/RestCollectionBase',
17721     'restservices/Team'
17722 ],
17723 function (RestCollectionBase, Team) {
17724     /** @private */
17725     var Teams = RestCollectionBase.extend({
17726 
17727         /**
17728          * @class
17729          * JavaScript representation of a Teams collection object. Also exposes methods to operate
17730          * on the object against the server.
17731          *
17732          * @param {Object} options
17733          *     An object with the following properties:<ul>
17734          *         <li><b>id:</b> The id of the object being constructed</li>
17735          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
17736          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
17737          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
17738          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
17739          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
17740          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
17741          *             <li><b>content:</b> {String} Raw string of response</li>
17742          *             <li><b>object:</b> {Object} Parsed object of response</li>
17743          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
17744          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
17745          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
17746          *             </ul></li>
17747          *         </ul></li>
17748          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
17749          * @constructs
17750          **/
17751         init: function (options) {
17752             this._super(options);
17753         },
17754 
17755         /**
17756          * @private
17757          * Gets the REST class for the current object - this is the Teams class.
17758          * @returns {Object} The Teams constructor.
17759          */
17760         getRestClass: function () {
17761             return Teams;
17762         },
17763 
17764         /**
17765          * @private
17766          * Gets the REST class for the objects that make up the collection. - this
17767          * is the Team class.
17768          */
17769         getRestItemClass: function () {
17770             return Team;
17771         },
17772 
17773         /**
17774          * @private
17775          * Gets the REST type for the current object - this is a "Teams".
17776          * @returns {String} The Teams string.
17777          */
17778         getRestType: function () {
17779             return "Teams";
17780         },
17781         
17782         /**
17783          * @private
17784          * Gets the REST type for the objects that make up the collection - this is "Team".
17785          */
17786         getRestItemType: function () {
17787             return "Team";
17788         },
17789 
17790         /**
17791          * @private
17792          * Override default to indicates that the collection supports making
17793          * requests.
17794          */
17795         supportsRequests: true,
17796 
17797         /**
17798          * @private
17799          * Override default to indicate that this object doesn't support subscriptions.
17800          */
17801         supportsRestItemSubscriptions: false,
17802         
17803         /**
17804          * @private
17805          * Retrieve the Teams.  This call will re-query the server and refresh the collection.
17806          *
17807          * @returns {finesse.restservices.Teams}
17808          *     This Teams object to allow cascading.
17809          */
17810         get: function () {
17811             // set loaded to false so it will rebuild the collection after the get
17812             this._loaded = false;
17813             // reset collection
17814             this._collection = {};
17815             // perform get
17816             this._synchronize();
17817             return this;
17818         }
17819 
17820     });
17821 
17822     window.finesse = window.finesse || {};
17823     window.finesse.restservices = window.finesse.restservices || {};
17824     window.finesse.restservices.Teams = Teams;
17825     
17826     return Teams;
17827 });
17828 
17829 /**
17830  * JavaScript representation of the Finesse SystemInfo object
17831  *
17832  * @requires finesse.clientservices.ClientServices
17833  * @requires Class
17834  * @requires finesse.FinesseBase
17835  * @requires finesse.restservices.RestBase
17836  */
17837 
17838 /** @private */
17839 define('restservices/SystemInfo',['restservices/RestBase'], function (RestBase) {
17840     
17841     var SystemInfo = RestBase.extend(/** @lends finesse.restservices.SystemInfo.prototype */{
17842         /**
17843          * @private
17844          * Returns whether this object supports subscriptions
17845          */
17846         supportsSubscriptions: false,
17847 
17848         doNotRefresh: true,
17849       
17850         /**
17851          * @class
17852          * JavaScript representation of a SystemInfo object.
17853          * 
17854          * @augments finesse.restservices.RestBase
17855          * @see finesse.restservices.SystemInfo.Statuses
17856          * @constructs
17857          */
17858         _fakeConstuctor: function () {
17859             /* This is here to hide the real init constructor from the public docs */
17860         },
17861         
17862          /**
17863          * @private
17864          * JavaScript representation of a SystemInfo object. Also exposes methods to operate
17865          * on the object against the server.
17866          *
17867          * @param {Object} options
17868          *     An object with the following properties:<ul>
17869          *         <li><b>id:</b> The id of the object being constructed</li>
17870          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
17871          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
17872          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
17873          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
17874          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
17875          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
17876          *             <li><b>content:</b> {String} Raw string of response</li>
17877          *             <li><b>object:</b> {Object} Parsed object of response</li>
17878          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
17879          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
17880          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
17881          *             </ul></li>
17882          *         </ul></li>
17883          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
17884          **/
17885         init: function (id, callbacks, restObj)
17886         {
17887             this._super(id, callbacks, restObj);
17888         },
17889 
17890         /**
17891          * @private
17892          * Gets the REST class for the current object - this is the SystemInfo object.
17893          */
17894         getRestClass: function () {
17895             return SystemInfo;
17896         },
17897 
17898         /**
17899          * @private
17900          * Gets the REST type for the current object - this is a "SystemInfo".
17901          */
17902         getRestType: function ()
17903         {
17904             return "SystemInfo";
17905         },
17906         
17907         _validate: function (obj)
17908         {
17909             return true;
17910         },
17911         
17912         /**
17913          * Returns the status of the Finesse system.
17914          *   IN_SERVICE if the Finesse API reports that it is in service,
17915          *   OUT_OF_SERVICE otherwise.
17916          * @returns {finesse.restservices.SystemInfo.Statuses} System Status
17917          */
17918         getStatus: function () {
17919             this.isLoaded();
17920             return this.getData().status;
17921         },
17922         
17923         /**
17924          * Returns the current timestamp from this SystemInfo object.
17925          *   This is used to calculate time drift delta between server and client.
17926          *  @returns {String} Time (GMT): yyyy-MM-dd'T'HH:mm:ss'Z'
17927          */
17928         getCurrentTimestamp: function () {
17929             this.isLoaded();
17930             return this.getData().currentTimestamp;
17931         },
17932         
17933         /**
17934          * Getter for the xmpp domain of the system.
17935          * @returns {String} The xmpp domain corresponding to this SystemInfo object.
17936          */
17937         getXmppDomain: function () {
17938             this.isLoaded();
17939             return this.getData().xmppDomain;
17940         },
17941         
17942         /**
17943          * Getter for the xmpp pubsub domain of the system.
17944          * @returns {String} The xmpp pubsub domain corresponding to this SystemInfo object.
17945          */
17946         getXmppPubSubDomain: function () {
17947             this.isLoaded();
17948             return this.getData().xmppPubSubDomain;
17949         },
17950 
17951         /**
17952          * Getter for the deployment type (UCCE or UCCX).
17953          * @returns {String} "UCCE" or "UCCX"
17954          */ 
17955         getDeploymentType: function () {
17956             this.isLoaded();
17957             return this.getData().deploymentType;
17958         },
17959 
17960         /**
17961          * Returns whether this is a single node deployment or not by checking for the existence of the secondary node in SystemInfo.
17962          * @returns {Boolean} True for single node deployments, false otherwise.
17963          */ 
17964         isSingleNode: function () {
17965             var secondary = this.getData().secondaryNode;
17966             if (secondary && secondary.host) {
17967                 return false;
17968             }
17969             return true;
17970         },
17971 
17972         /**
17973          * Checks all arguments against the primary and secondary hosts (FQDN) and returns the match.
17974          * This is useful for getting the FQDN of the current Finesse server.
17975          * @param {String} ...arguments[]... - any number of arguments to match against
17976          * @returns {String} FQDN (if properly configured) of the matched host of the primary or secondary node, or undefined if no match is found.
17977          */ 
17978         getThisHost: function () {
17979             var i,
17980             primary = this.getData().primaryNode,
17981             secondary = this.getData().secondaryNode;
17982 
17983             for (i = 0; (i < arguments.length); i = i + 1) {
17984                 if (primary && arguments[i] === primary.host) {
17985                     return primary.host;
17986                 } else if (secondary && arguments[i] === secondary.host) {
17987                     return secondary.host;
17988                 }
17989             }
17990         },
17991 
17992         /**
17993          * Checks all arguments against the primary and secondary hosts (FQDN) and returns the other node.
17994          * This is useful for getting the FQDN of the other Finesse server, i.e. for failover purposes.
17995          * @param {String} arguments - any number of arguments to match against
17996          * @returns {String} FQDN (if properly configured) of the alternate node, defaults to primary if no match is found, undefined for single node deployments.
17997          */ 
17998         getAlternateHost: function () {
17999             var i,
18000             isPrimary = false,
18001             primary = this.getData().primaryNode,
18002             secondary = this.getData().secondaryNode,
18003             xmppDomain = this.getData().xmppDomain,
18004             alternateHost;
18005 
18006             if (primary && primary.host) {
18007                     if (xmppDomain === primary.host) {
18008                         isPrimary = true;
18009                     }
18010                 if (secondary && secondary.host) {
18011                     if (isPrimary) {
18012                         return secondary.host;
18013                     }
18014                     return primary.host;
18015                 }
18016             }
18017         },
18018         
18019         /**
18020          * Gets the peripheral ID that Finesse is connected to. The peripheral
18021          * ID is the ID of the PG Routing Client (PIM).
18022          * 
18023          * @returns {String} The peripheral Id if UCCE, or empty string otherwise.
18024          */
18025         getPeripheralId : function () {
18026              this.isLoaded();
18027              
18028              var peripheralId = this.getData().peripheralId;
18029              if (peripheralId === null) {
18030                   return "";
18031              } else {
18032                   return this.getData().peripheralId;
18033              }
18034         },
18035 
18036          /**
18037          * Gets the license. Only apply to UCCX.
18038          * 
18039          * @returns {String} The license if UCCX, or empty string otherwise.
18040          */
18041         getlicense : function () {
18042              this.isLoaded();
18043              return this.getData().license || "";
18044         },
18045         
18046         /**
18047          * Gets the systemAuthMode for the current deployment
18048          * 
18049          * @returns {String} The System auth mode for current deployment
18050          */
18051         getSystemAuthMode : function() {
18052             this.isLoaded();
18053             return this.getData().systemAuthMode;
18054         }
18055     });
18056     
18057     SystemInfo.Statuses = /** @lends finesse.restservices.SystemInfo.Statuses.prototype */ { 
18058         /** 
18059          * Finesse is in service. 
18060          */
18061         IN_SERVICE: "IN_SERVICE",
18062         /** 
18063          * Finesse is not in service. 
18064          */
18065         OUT_OF_SERVICE: "OUT_OF_SERVICE",
18066         /**
18067          * @class SystemInfo status values.
18068          * @constructs
18069          */
18070         _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
18071 
18072     };
18073     
18074     window.finesse = window.finesse || {};
18075     window.finesse.restservices = window.finesse.restservices || {};
18076     window.finesse.restservices.SystemInfo = SystemInfo;
18077     
18078     return SystemInfo;
18079 });
18080 
18081 define('restservices/DialogLogoutActions',[], function ()
18082 {
18083     var DialogLogoutActions = /** @lends finesse.restservices.DialogLogoutActions.prototype */ {
18084 
18085         /**
18086          * Set this action to close active dialogs when the agent logs out.
18087          */
18088         CLOSE: "CLOSE",
18089 
18090         /**
18091          * Set this action to transfer active dialogs when the agent logs out.
18092          */
18093         TRANSFER: "TRANSFER",
18094 
18095         /**
18096          * @class Actions used to handle tasks that are associated with a given media at logout time.
18097          *
18098          * @constructs
18099          */
18100         _fakeConstructor: function ()
18101         {
18102         }, // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
18103 
18104         /**
18105          * Is the given action a valid dialog logout action.
18106          *
18107          * @param {String} action the action to evaluate
18108          * @returns {Boolean} true if the action is valid; false otherwise
18109          */
18110         isValidAction: function(action)
18111         {
18112             if ( !action )
18113             {
18114                 return false;
18115             }
18116 
18117             return DialogLogoutActions.hasOwnProperty(action.toUpperCase());
18118         }
18119     };
18120 
18121     window.finesse = window.finesse || {};
18122     window.finesse.restservices = window.finesse.restservices || {};
18123     window.finesse.restservices.DialogLogoutActions = DialogLogoutActions;
18124 
18125     return DialogLogoutActions;
18126 });
18127 define('restservices/InterruptActions',[], function ()
18128 {
18129     var InterruptActions = /** @lends finesse.restservices.InterruptActions.prototype */
18130     {
18131         /**
18132          * The interrupt will be accepted and the agent will not work on dialogs in this media until the media is no longer interrupted.
18133          */
18134         ACCEPT: "ACCEPT",
18135 
18136         /**
18137          * the interrupt will be ignored and the agent is allowed to work on dialogs while the media is interrupted.
18138          */
18139         IGNORE: "IGNORE",
18140 
18141         /**
18142          * @class
18143          *
18144          * The action to be taken in the event this media is interrupted. The action will be one of the following:<ul>
18145          *     <li><b>ACCEPT:</b> the interrupt will be accepted and the agent will not work on dialogs in this media
18146          *     until the media is no longer interrupted.</li>
18147          *     <li><b>IGNORE:</b> the interrupt will be ignored and the agent is allowed to work on dialogs while the
18148          *     media is interrupted.</li></ul>
18149          *
18150          * @constructs
18151          */
18152         _fakeConstructor: function ()
18153         {
18154         }, // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
18155 
18156         /**
18157          * Is the given action a valid dialog logout action.
18158          *
18159          * @param {String} action the action to evaluate
18160          * @returns {Boolean} true if the action is valid; false otherwise
18161          */
18162         isValidAction: function(action)
18163         {
18164             if ( !action )
18165             {
18166                 return false;
18167             }
18168 
18169             return InterruptActions.hasOwnProperty(action.toUpperCase());
18170         }
18171     };
18172 
18173     window.finesse = window.finesse || {};
18174     window.finesse.restservices = window.finesse.restservices || {};
18175     window.finesse.restservices.InterruptActions = InterruptActions;
18176 
18177     return InterruptActions;
18178 });
18179 /**
18180  * Provides standard way resolve message keys with substitution
18181  *
18182  * @requires finesse.container.I18n or gadgets.Prefs
18183  */
18184 
18185 // Add Utilities to the finesse.utilities namespace
18186 define('utilities/I18n',[], function () {
18187     var I18n = (function () {
18188 
18189         /**
18190          * Shortcut to finesse.container.I18n.getMsg or gadgets.Prefs.getMsg
18191          * @private
18192          */
18193         var _getMsg;
18194 
18195         return {
18196             /**
18197              * Provides a message resolver for this utility singleton.
18198              * @param {Function} getMsg
18199              *     A function that returns a string given a message key.
18200              *     If the key is not found, this function must return 
18201              *     something that tests false (i.e. undefined or "").
18202              */
18203             setGetter : function (getMsg) {
18204                 _getMsg = getMsg;
18205             },
18206 
18207             /**
18208              * Resolves the given message key, also performing substitution.
18209              * This generic utility will use a custom function to resolve the key
18210              * provided by finesse.utilities.I18n.setGetter. Otherwise, it will 
18211              * discover either finesse.container.I18n.getMsg or gadgets.Prefs.getMsg
18212              * upon the first invocation and store that reference for efficiency.
18213              * 
18214              * Since this will construct a new gadgets.Prefs object, it is recommended
18215              * for gadgets to explicitly provide the setter to prevent duplicate
18216              * gadgets.Prefs objects. This does not apply if your gadget does not need
18217              * access to gadgets.Prefs other than getMsg. 
18218              * 
18219              * @param {String} key
18220              *     The key to lookup
18221              * @param {String} arguments
18222              *     Arguments for substitution
18223              * @returns {String/Function}
18224              *     The resolved string if successful, otherwise a function that returns
18225              *     a '???' string that can also be casted into a string.
18226              */
18227             getString : function (key) {
18228                 var prefs, i, retStr, noMsg, getFailed = "";
18229                 if (!_getMsg) {
18230                     if (finesse.container && finesse.container.I18n) {
18231                         _getMsg = finesse.container.I18n.getMsg;
18232                     } else if (gadgets) {
18233                         prefs = new gadgets.Prefs();
18234                         _getMsg = prefs.getMsg;
18235                     }
18236                 }
18237                 
18238                 try {
18239                     retStr = _getMsg(key);
18240                 } catch (e) {
18241                     getFailed = "finesse.utilities.I18n.getString(): invalid _getMsg";
18242                 }
18243                 
18244                 if (retStr) { // Lookup was successful, perform substitution (if any)
18245                     for (i = 1; i < arguments.length; i += 1) {
18246                         retStr = retStr.replace(new RegExp("\\{" + (i - 1) + "\\}", "g"), arguments[i]);
18247                     }
18248                     //in order to fix French text with single quotes in it, we need to replace \' with '
18249                     return retStr.replace(/\\'/g, "'");
18250                 }
18251                 // We want a function because jQuery.html() and jQuery.text() is smart enough to invoke it.
18252                 /** @private */
18253                 noMsg = function () {
18254                     return "???" + key + "???" + getFailed;
18255                 };
18256                 // We overload the toString() of this "function" to allow JavaScript to cast it into a string
18257                 // For example, var myMsg = "something " + finesse.utilities.I18n.getMsg("unresolvable.key");
18258                 /** @private */
18259                 noMsg.toString = function () {
18260                     return "???" + key + "???" + getFailed;
18261                 };
18262                 return noMsg;
18263 
18264             }
18265         };
18266     }());
18267     
18268     window.finesse = window.finesse || {};
18269     window.finesse.utilities = window.finesse.utilities || {};
18270     window.finesse.utilities.I18n = I18n;
18271 
18272     return I18n;
18273 });
18274 
18275 /**
18276  * Logging.js: provides simple logging for clients to use and overrides synchronous native methods: alert(), confirm(), and prompt().
18277  * 
18278  * On Firefox, it will hook into console for logging.  On IE, it will log to the status bar. 
18279  */
18280 // Add Utilities to the finesse.utilities namespace
18281 define('utilities/Logger',[], function () {
18282     var Logger = (function () {
18283         
18284         var
18285         
18286         /** @private **/
18287         debugOn,
18288         
18289         /**
18290          * Pads a single digit number for display purposes (e.g. '4' shows as '04')
18291          * @param num is the number to pad to 2 digits
18292          * @returns a two digit padded string
18293          * @private
18294          */
18295         padTwoDigits = function (num) {        
18296             return (num < 10) ? '0' + num : num;  
18297         },
18298         
18299         /**
18300          * Checks to see if we have a console - this allows us to support Firefox or IE.
18301          * @returns {Boolean} True for Firefox, False for IE
18302          * @private
18303          */
18304         hasConsole = function () {
18305             var retval = false;
18306             try
18307             {
18308                 if (window.console !== undefined) 
18309                 {
18310                     retval = true;
18311                 }
18312             } 
18313             catch (err)
18314             {
18315                 retval = false;
18316             }
18317               
18318             return retval;
18319         },
18320         
18321         /**
18322          * Gets a timestamp.
18323          * @returns {String} is a timestamp in the following format: HH:MM:SS
18324          * @private
18325          */
18326         getTimeStamp = function () {
18327             var date = new Date(), timeStr;
18328             timeStr = padTwoDigits(date.getHours()) + ":" + padTwoDigits(date.getMinutes()) + ":" + padTwoDigits(date.getSeconds());
18329 
18330             return timeStr;
18331         };
18332         
18333         return {
18334             /**
18335              * Enable debug mode. Debug mode may impact performance on the UI.
18336              *
18337              * @param {Boolean} enable
18338              *      True to enable debug logging.
18339              * @private
18340              */
18341             setDebug : function (enable) {
18342                 debugOn = enable;
18343             },
18344             
18345             /**
18346              * Logs a string as DEBUG.
18347              * 
18348              * @param str is the string to log. 
18349              * @private
18350              */
18351             log : function (str) {
18352                 var timeStr = getTimeStamp();
18353                 
18354                 if (debugOn) {
18355                     if (hasConsole())
18356                     {
18357                         window.console.log(timeStr + ": " + "DEBUG" + " - " + str);
18358                     }
18359                 }
18360             },
18361             
18362             /**
18363              * Logs a string as INFO.
18364              * 
18365              * @param str is the string to log. 
18366              * @private
18367              */
18368             info : function (str) {
18369                 var timeStr = getTimeStamp();
18370                 
18371                 if (hasConsole())
18372                 {
18373                     window.console.info(timeStr + ": " + "INFO" + " - " + str);
18374                 }
18375             },
18376             
18377             /**
18378              * Logs a string as WARN.
18379              * 
18380              * @param str is the string to log. 
18381              * @private
18382              */
18383             warn : function (str) {
18384                 var timeStr = getTimeStamp();
18385                 
18386                 if (hasConsole())
18387                 {
18388                     window.console.warn(timeStr + ": " + "WARN" + " - " + str);
18389                 }
18390             },
18391             /**
18392              * Logs a string as ERROR.
18393              * 
18394              * @param str is the string to log. 
18395              * @private
18396              */
18397             error : function (str) {
18398                 var timeStr = getTimeStamp();
18399                 
18400                 if (hasConsole())
18401                 {
18402                     window.console.error(timeStr + ": " + "ERROR" + " - " + str);
18403                 }
18404             }
18405         };
18406     }());
18407     
18408     return Logger;
18409 });
18410 
18411 /**
18412  * BackSpaceHandler.js: provides functionality to prevent the page from navigating back and hence losing the unsaved data when backspace is pressed.
18413  * 
18414  */
18415 define('utilities/BackSpaceHandler',[], function () {
18416 			var eventCallback = function(event) {
18417 				var doPrevent = false, d = event.srcElement || event.target;
18418 				if (event.keyCode === 8) {
18419 					if ((d.tagName.toUpperCase() === 'INPUT' && (d.type
18420 							.toUpperCase() === 'TEXT'
18421 							|| d.type.toUpperCase() === 'PASSWORD'
18422 							|| d.type.toUpperCase() === 'FILE'
18423 							|| d.type.toUpperCase() === 'SEARCH'
18424 							|| d.type.toUpperCase() === 'EMAIL'
18425 							|| d.type.toUpperCase() === 'NUMBER' || d.type
18426 							.toUpperCase() === 'DATE'))
18427 							|| d.tagName.toUpperCase() === 'TEXTAREA') {
18428 						doPrevent = d.readOnly || d.disabled;
18429 					} else {
18430 						doPrevent = true;
18431 					}
18432 				}
18433 
18434 				if (doPrevent) {
18435 					event.preventDefault();
18436 				}
18437 			};
18438 
18439 			if (window.addEventListener) {
18440 				window.addEventListener('keydown', eventCallback);
18441 			} else if (window.attachEvent) {
18442 				window.attachEvent('onkeydown', eventCallback);
18443 			} else {
18444 				window.console.error("Unable to attach backspace handler event ");
18445 			}
18446 });
18447 /* using variables before they are defined.
18448  */
18449 /*global navigator,unescape,sessionStorage,localStorage,_initSessionList,_initSessionListComplete */
18450 
18451 /**
18452  * Allows each gadget to communicate with the server to send logs.
18453  */
18454 
18455 /**
18456  * @class
18457  * @private
18458  * Allows each product to initialize its method of storage
18459  */
18460 define('cslogger/FinesseLogger',["clientservices/ClientServices", "utilities/Utilities"], function (ClientServices, Utilities) {
18461     
18462     var FinesseLogger = (function () { 
18463 
18464         var
18465 
18466         /**
18467          * Array use to collect ongoing logs in memory
18468          * @private
18469          */
18470         _logArray = [],
18471 
18472         /**
18473          * The final data string sent to the server, =_logArray.join
18474          * @private
18475          */
18476         _logStr = "",
18477 
18478         /**
18479          * Keep track of size of log
18480          * @private
18481          */
18482         _logSize = 0,
18483 
18484         /**
18485          * Flag to keep track show/hide of send log link
18486          * @private
18487          */
18488         _sendLogShown = false,
18489 
18490         /**
18491          * Flag to keep track if local log initialized
18492          * @private
18493          */
18494         _loggingInitialized = false,
18495         
18496 
18497         /**
18498          * local log size limit
18499          * @private
18500          */
18501         _maxLocalStorageSize = 5000000,
18502 
18503         /**
18504          * half local log size limit
18505          * @private
18506          */
18507         _halfMaxLocalStorageSize = 0.5*_maxLocalStorageSize,
18508 
18509         
18510         /**
18511          * threshold for purge 
18512          * @private
18513          */
18514         _purgeStartPercent = 0.75,
18515         
18516         /**
18517          * log item prefix 
18518          * @private
18519          */
18520         _linePrefix = null,
18521         
18522         /**
18523          * locallog session 
18524          * @private
18525          */
18526         _session = null,
18527         
18528         /**
18529          * Flag to keep track show/hide of send log link
18530          * @private
18531          */
18532         _sessionKey = null,
18533         /**
18534          * Log session metadata 
18535          * @private
18536          */
18537         _logInfo = {},
18538         
18539         /**
18540          * Flag to find sessions 
18541          * @private
18542          */
18543         _findSessionsObj = null,
18544 
18545         /**
18546          * Wrap up console.log esp. for IE9 
18547          * @private
18548          */
18549         _myConsoleLog = function (str) {
18550             if (window.console !== undefined) {
18551               window.console.log(str);
18552             }
18553         },
18554         /**
18555          * Initialize the Local Logging
18556          * @private
18557          */
18558         _initLogging = function () {
18559             if (_loggingInitialized) {
18560                 return;
18561             }
18562             //Build a new store
18563             _session = sessionStorage.getItem("finSessKey");
18564             //if the _session is null or empty, skip the init
18565             if (!_session) {
18566               return;
18567             }
18568             _sessionKey = "Fi"+_session;
18569             _linePrefix = _sessionKey + "_";
18570             _logInfo = {};
18571             _logInfo.name = _session;
18572             _logInfo.size = 0;
18573             _logInfo.head = 0;
18574             _logInfo.tail = 0;
18575             _logInfo.startTime = new Date().getTime();
18576             _loggingInitialized = true;
18577             _initSessionList();
18578         },
18579         
18580         /**
18581          * get total data size 
18582          *
18583          * @return {Integer} which is the amount of data stored in local storage.
18584          * @private
18585          */
18586         _getTotalData = function ()
18587         {
18588             var sessName, sessLogInfoStr,sessLogInfoObj, sessionsInfoObj, totalData = 0,
18589             sessionsInfoStr = localStorage.getItem("FinesseSessionsInfo");
18590             if (!sessionsInfoStr) {
18591                  return 0;
18592             }
18593             sessionsInfoObj = JSON.parse(sessionsInfoStr);
18594 
18595             for (sessName in sessionsInfoObj.sessions)
18596             {
18597                 if (sessionsInfoObj.sessions.hasOwnProperty(sessName)) {
18598                     sessLogInfoStr = localStorage.getItem("Fi" + sessName);
18599                     if (!sessLogInfoStr) {
18600                         _myConsoleLog("_getTotalData failed to get log info for "+sessName);
18601                     }
18602                     else {
18603                        sessLogInfoObj = JSON.parse(sessLogInfoStr);
18604                        totalData = totalData + sessLogInfoObj.size;
18605                     }
18606                 }
18607             }
18608 
18609               return totalData;
18610         },
18611         
18612         /**
18613          * Remove lines from tail up until store size decreases to half of max size limit.
18614          *
18615          * @private
18616          */
18617         _purgeCurrentSession = function() {
18618             var curStoreSize, purgedSize=0, line, tailKey, secLogInfoStr, logInfoStr, theLogInfo;
18619             curStoreSize = _getTotalData();
18620             if (curStoreSize < _halfMaxLocalStorageSize) {
18621                return;
18622             }
18623             logInfoStr = localStorage.getItem(_sessionKey);
18624             if (!logInfoStr) {
18625                return;
18626             }
18627             theLogInfo = JSON.parse(logInfoStr);
18628             //_myConsoleLog("Starting _purgeCurrentSession() - currentStoreSize=" + curStoreSize);
18629             while(curStoreSize > _halfMaxLocalStorageSize) {
18630                try {
18631                    tailKey = _sessionKey+"_"+theLogInfo.tail;
18632                    line = localStorage.getItem(tailKey);
18633                    if (line) {
18634                        purgedSize = purgedSize +line.length;
18635                        localStorage.removeItem(tailKey);
18636                        curStoreSize = curStoreSize - line.length;
18637                        theLogInfo.size = theLogInfo.size - line.length;
18638                    }
18639                }
18640                catch (err) {
18641                    _myConsoleLog("purgeCurrentSession encountered err="+err);
18642                }
18643                if (theLogInfo.tail < theLogInfo.head) {
18644                    theLogInfo.tail = theLogInfo.tail  + 1;
18645                }
18646                else {
18647                    break;
18648                }
18649             }
18650             //purge stops here, we need to update session's meta data in storage
18651             secLogInfoStr = localStorage.getItem(_sessionKey);
18652             if (!secLogInfoStr) {
18653                 //somebody cleared the localStorage
18654                 return;
18655             }
18656             
18657             //_myConsoleLog("In _purgeCurrentSession() - after purging current session, currentStoreSize=" + curStoreSize);
18658             //_myConsoleLog("In _purgeCurrentSession() - after purging purgedSize=" + purgedSize);
18659             //_myConsoleLog("In _purgeCurrentSession() - after purging logInfo.size=" + theLogInfo.size);
18660             //_myConsoleLog("In _purgeCurrentSession() - after purging logInfo.tail=" + theLogInfo.tail);
18661             localStorage.setItem(_sessionKey, JSON.stringify(theLogInfo));
18662             _myConsoleLog("Done _purgeCurrentSession() - currentStoreSize=" + curStoreSize);
18663         },
18664        
18665         /**
18666          * Purge a session 
18667          *
18668          * @param sessionName is the name of the session
18669          * @return {Integer} which is the current amount of data purged
18670          * @private
18671          */
18672         _purgeSession = function (sessionName) {
18673               var theLogInfo, logInfoStr, sessionsInfoStr, sessionsInfoObj;
18674               //Get the session logInfo
18675               logInfoStr = localStorage.getItem("Fi" + sessionName);
18676               if (!logInfoStr) {
18677                  _myConsoleLog("_purgeSession failed to get logInfo for "+sessionName);
18678                  return 0;
18679               }
18680               theLogInfo = JSON.parse(logInfoStr);
18681               
18682               //Note: This assumes that we don't crash in the middle of purging
18683               //=> if we do then it should get deleted next time
18684               //Purge tail->head
18685               while (theLogInfo.tail <= theLogInfo.head)
18686               {
18687                   try {
18688                       localStorage.removeItem("Fi" + sessionName + "_" + theLogInfo.tail);
18689                       theLogInfo.tail = theLogInfo.tail + 1;
18690                   }
18691                   catch (err) {
18692                       _myConsoleLog("In _purgeSession err="+err);
18693                       break;
18694                   }
18695               }
18696 
18697               //Remove the entire session
18698               localStorage.removeItem("Fi" + sessionName);
18699 
18700               //Update FinesseSessionsInfo
18701               sessionsInfoStr = localStorage.getItem("FinesseSessionsInfo");
18702               if (!sessionsInfoStr) {
18703                  _myConsoleLog("_purgeSession could not get sessions Info, it was cleared?");
18704                  return 0;
18705               }
18706               sessionsInfoObj = JSON.parse(sessionsInfoStr);
18707               if (sessionsInfoObj.sessions !== null)
18708               {
18709                  delete sessionsInfoObj.sessions[sessionName];
18710               
18711                  sessionsInfoObj.total = sessionsInfoObj.total - 1;
18712                  sessionsInfoObj.lastWrittenBy = _session;
18713                  localStorage.setItem("FinesseSessionsInfo", JSON.stringify(sessionsInfoObj));
18714               }
18715               
18716               return theLogInfo.size;
18717         },
18718         
18719          /**
18720           * purge old sessions
18721           * 
18722           * @param storeSize
18723 	  * @return {Boolean} whether purging reaches its target
18724           * @private
18725          */
18726          _purgeOldSessions = function (storeSize) {
18727              var sessionsInfoStr, purgedSize = 0, sessName, sessions, curStoreSize, activeSession, sessionsInfoObj;
18728              sessionsInfoStr = localStorage.getItem("FinesseSessionsInfo");
18729              if (!sessionsInfoStr) {
18730                 _myConsoleLog("Could not get FinesseSessionsInfo");
18731                 return true;
18732              }
18733              sessionsInfoObj = JSON.parse(sessionsInfoStr);
18734              curStoreSize = _getTotalData();
18735              
18736              activeSession = _session;
18737              sessions = sessionsInfoObj.sessions;
18738              for (sessName in sessions) {
18739                 if (sessions.hasOwnProperty(sessName)) {
18740                     if (sessName !== activeSession) {
18741                         purgedSize = purgedSize + _purgeSession(sessName);
18742                         if ((curStoreSize-purgedSize) < _halfMaxLocalStorageSize) {
18743                             return true;
18744                         }
18745                     }
18746                 }
18747              }
18748             //purge is not done, so return false
18749             return false;
18750          },
18751          
18752        /**
18753         * handle insert error
18754         *
18755         * @param error
18756         * @private
18757         */
18758         _insertLineHandleError = function (error) {
18759             _myConsoleLog(error);
18760         },
18761 
18762         /**
18763          * check storage data size and if need purge
18764          * @private
18765          */
18766         _checkSizeAndPurge = function () {
18767             var purgeIsDone=false, totalSize = _getTotalData();
18768             if (totalSize > 0.75*_maxLocalStorageSize) {
18769                _myConsoleLog("in _checkSizeAndPurge, totalSize ("+totalSize+") exceeds limit");
18770                purgeIsDone = _purgeOldSessions(totalSize);
18771                if (purgeIsDone) {
18772                   _myConsoleLog("in _checkSizeAndPurge after purging old session, purge is done");
18773                }
18774                else {
18775                   //after all old sessions purged, still need purge
18776                   totalSize = _getTotalData();
18777                   if (totalSize > 0.75*_maxLocalStorageSize) {
18778                       _myConsoleLog("in _checkSizeAndPurge after purging old session,still needs purging, now storeSize ("+totalSize+")");
18779                      _purgeCurrentSession();
18780                      _myConsoleLog("in _checkSizeAndPurge done purging current session.");
18781                   }
18782                }
18783             }
18784         },
18785         
18786         /**
18787          * check if the session is already in meta data  
18788          * 
18789          * @param metaData
18790          * @param sessionName
18791          * @return {Boolean} true if session has metaData (false otherwise)
18792          * @private
18793          */
18794         _sessionsInfoContains = function (metaData, sessionName) {
18795            if (metaData && metaData.sessions && metaData.sessions.hasOwnProperty(sessionName)) {
18796               return true;
18797            }
18798            return false;
18799         },
18800         
18801         
18802         /**
18803          * setup sessions in local storage 
18804          * 
18805          * @param logInfo
18806          * @private
18807          */
18808         _getAndSetNumberOfSessions = function (logInfo) {
18809             var numOfSessionsPass1, numOfSessionsPass2, l;
18810             numOfSessionsPass1 = localStorage.getItem("FinesseSessionsInfo");
18811             if (numOfSessionsPass1 === null) {
18812                 //Init first time
18813                 numOfSessionsPass1 = {};
18814                 numOfSessionsPass1.total = 1;
18815                 numOfSessionsPass1.sessions = {};
18816                 numOfSessionsPass1.sessions[logInfo.name] = logInfo.startTime;
18817                 numOfSessionsPass1.lastWrittenBy = logInfo.name;
18818                 localStorage.setItem("FinesseSessionsInfo", JSON.stringify(numOfSessionsPass1));
18819             }
18820             else {
18821                 numOfSessionsPass1 = JSON.parse(numOfSessionsPass1);
18822                 //check if the session is already in the FinesseSessionSInfo
18823                 if (_sessionsInfoContains(numOfSessionsPass1, logInfo.name)) {
18824                     return;
18825                 }             
18826                 //Save numOfSessionsPass1
18827                 numOfSessionsPass1.total = parseInt(numOfSessionsPass1.total, 10) + 1;
18828                 numOfSessionsPass1.sessions[logInfo.name] = logInfo.startTime;
18829                 numOfSessionsPass1.lastWrittenBy = logInfo.name;
18830                 localStorage.setItem("FinesseSessionsInfo", JSON.stringify(numOfSessionsPass1));
18831                 numOfSessionsPass2 = localStorage.getItem("FinesseSessionsInfo");
18832                 if (!numOfSessionsPass2) {
18833                    _myConsoleLog("Could not get FinesseSessionsInfo");
18834                    return;
18835                 }
18836                 numOfSessionsPass2 = JSON.parse(numOfSessionsPass2);
18837                 //in future we need to confirm the numOfSessionsPass2 is the same as numOfSessionsPass1
18838                 ////if (numOfSessionsPass1.lastWrittenBy !== numOfSessionsPass2.lastWrittenBy) {
18839                 ////    _myConsoleLog("Rebuild sessions");
18840                 ////    _sessionTimerId = setTimeout(_initSessionList, 10000);
18841                 ////}
18842                 ////else {
18843                 ////    _sessionTimerId = null;
18844                 ////callback(numOfSessionsPass2.sessions);
18845                 ////}
18846             }
18847             if (!localStorage.getItem(_sessionKey)) {
18848                 localStorage.setItem(_sessionKey, JSON.stringify(_logInfo));
18849             }
18850         },
18851         
18852         
18853         /**
18854          * init session list 
18855          * @private
18856          */
18857         _initSessionList = function () {
18858             _getAndSetNumberOfSessions(_logInfo);
18859         },
18860         
18861        /**
18862         * do the real store of log line
18863         * 
18864         * @param line
18865         * @private
18866         */
18867         _persistLine = function (line) {
18868             var key, logInfoStr;
18869             logInfoStr = localStorage.getItem(_sessionKey);
18870             if (logInfoStr === null) {
18871                return;
18872             }
18873             _logInfo = JSON.parse(logInfoStr);
18874             _logInfo.head = _logInfo.head + 1;
18875             key = _linePrefix + _logInfo.head;
18876             localStorage.setItem(key, line);
18877             //Save the size
18878             _logInfo.size = _logInfo.size + line.length;
18879             if (_logInfo.tail === 0) {
18880                 _logInfo.tail = _logInfo.head;
18881             }
18882         
18883             localStorage.setItem(_sessionKey, JSON.stringify(_logInfo));
18884             _checkSizeAndPurge();
18885         },
18886         
18887         /**
18888          * Insert a line into the localStorage.
18889          *
18890          * @param line line to be inserted 
18891          * @private
18892         */
18893         _insertLine = function (line) {
18894             //_myConsoleLog("_insertLine: [" + line + "]");
18895             //Write the next line to localStorage
18896             try {
18897                //Persist the line 
18898                _persistLine(line);
18899             }
18900             catch (err) {
18901                _myConsoleLog("error in _insertLine(), err="+err);
18902                //_insertLineHandleError(err);
18903             }
18904         },
18905          
18906         
18907         /**
18908          * Clear the local storage
18909          * @private
18910          */
18911         _clearLocalStorage = function() {
18912             localStorage.clear();
18913 
18914         },
18915 
18916         /**
18917          * Collect logs when onCollect called
18918          *
18919          * @param data
18920          * @private
18921          */
18922         _collectMethod = function(data) {
18923           //Size of log should not exceed 1.5MB
18924           var info, maxLength = 1572864;
18925           
18926           //add size buffer equal to the size of info to be added when publish
18927           info = Utilities.getSanitizedUserAgentString() + "
";
18928           info = escape(info);
18929 
18930             //If log was empty previously, fade in buttons
18931             if (!_sendLogShown) {
18932                 //call the fadeInSendLog() in Footer
18933                 finesse.modules.Footer.sendLogAppear();
18934                 _sendLogShown = true;
18935                 _logSize = info.length;
18936             }
18937             
18938             //if local storage logging is enabled, then insert the log into local storage
18939             if (window.sessionStorage.getItem('enableLocalLog')==='true') {
18940                 if (data) {
18941                    if (data.length>0 && data.substring(0,1) === '\n') {
18942                       _insertLine(data.substring(1));
18943                    }
18944                    else {
18945                       _insertLine(data);
18946                    }
18947                 }
18948             }
18949               
18950             //escape all data to get accurate size (shindig will escape when it builds request)
18951             //escape 6 special chars for XML: &<>"'\n
18952             data = data.replace(/&/g, "&").replace(/"/g, """).replace(/'/g, "'").replace(/>/g, ">").replace(/</g, "<").replace(/\n/g, "
");
18953             data = escape(data+"\n");
18954 
18955             if (data.length < maxLength){
18956                 //make room for new data if log is exceeding max length
18957                 while (_logSize + data.length > maxLength) {
18958                     _logSize -= (_logArray.shift()).length;
18959                 }
18960             }
18961 
18962             //Else push the log into memory, increment the log size
18963             _logArray.push(data);
18964 
18965             //inc the size accordingly
18966             _logSize+=data.length;
18967 
18968         };
18969 
18970         return {
18971 
18972             /**
18973              * @private
18974              * Initiate FinesseLogger.
18975              */
18976             init: function () {
18977                 ClientServices.subscribe("finesse.clientLogging.*", _collectMethod);
18978                 _initLogging();
18979             },
18980 
18981             /**
18982              * @private
18983              * Clear all items stored in localStorage.
18984             */
18985             clear : function () {
18986                _clearLocalStorage();
18987             },
18988 
18989             /**
18990              * @private
18991              * Initialize the local storage logging.
18992             */
18993             initLocalLog: function () {
18994                _initLogging();
18995             },
18996 
18997             /**
18998              * @private
18999              * Inserts a line into the localStorage.
19000              * @param line to insert
19001             */
19002             localLog : function (line) {
19003                _insertLine(line);
19004             },
19005 
19006            /**
19007             * @ignore
19008             * Publish logs to server and clear the memory
19009             *
19010             * @param userObj
19011             * @param options
19012             * @param callBack
19013             */
19014             publish: function(userObj, options, callBack) {
19015                 // Avoid null references.
19016                 options = options || {};
19017                 callBack = callBack || {};
19018 
19019                 if (callBack.sending === "function") {
19020                     callBack.sending();
19021                 }
19022 
19023                 //logs the basic version and machine info and escaped new line
19024                 _logStr = Utilities.getSanitizedUserAgentString() + "
";
19025                 
19026                 //join the logs to correct string format
19027                 _logStr += unescape(_logArray.join(""));
19028 
19029                 //turning log string to JSON obj
19030                 var logObj = {
19031                         ClientLog: {
19032                         logData : _logStr //_logStr
19033                     }
19034                 },
19035                 tmpOnAdd = (options.onAdd && typeof options.onAdd === "function")? options.onAdd : function(){};
19036                 /** @private */
19037                 options.onAdd = function(){
19038                     tmpOnAdd();
19039                     _logArray.length = 0; _logSize =0;
19040                     _sendLogShown = false;
19041                     };
19042                 //adding onLoad to the callbacks, this is the subscribe success case for the first time user subscribe to the client log node
19043                 /** @private */
19044                 options.onLoad = function (clientLogObj) {
19045                     clientLogObj.sendLogs(logObj,{
19046                             error: callBack.error
19047                         });
19048                     };
19049 
19050                 userObj.getClientLog(options);
19051             }
19052         };
19053     }());
19054 
19055     window.finesse = window.finesse || {};
19056     window.finesse.cslogger = window.finesse.cslogger || {};
19057     /** @private */
19058     window.finesse.cslogger.FinesseLogger = FinesseLogger;
19059 
19060     return FinesseLogger;
19061 });
19062 
19063 /**
19064  *  Contains a list of topics used for containerservices pubsub.
19065  *
19066  */
19067 
19068 /**
19069  * @class
19070  * Contains a list of topics with some utility functions.
19071  */
19072 /** @private */
19073 define('containerservices/Topics',[], function () {
19074 
19075     var Topics = (function () {
19076 
19077     /**
19078      * The namespace prepended to all Finesse topics.
19079      */
19080     this.namespace = "finesse.containerservices";
19081 
19082     /**
19083      * @private
19084      * Gets the full topic name with the ContainerServices namespace prepended.
19085      * @param {String} topic
19086      *     The topic category.
19087      * @returns {String}
19088      *     The full topic name with prepended namespace.
19089      */
19090     var _getNSTopic = function (topic) {
19091         return this.namespace + "." + topic;
19092     };
19093 
19094 
19095 
19096     /** @scope finesse.containerservices.Topics */
19097     return {
19098         /** 
19099          * @private
19100          * request channel. */
19101         REQUESTS: _getNSTopic("requests"),
19102 
19103         /** 
19104          * @private
19105          * reload gadget channel. */
19106         RELOAD_GADGET: _getNSTopic("reloadGadget"),
19107 
19108         /**
19109          * @private
19110          * Convert a Finesse REST URI to a OpenAjax compatible topic name.
19111          */
19112         getTopic: function (restUri) {
19113             //The topic should not start with '/' else it will get replaced with
19114             //'.' which is invalid.
19115             //Thus, remove '/' if it is at the beginning of the string
19116             if (restUri.indexOf('/') === 0) {
19117                 restUri = restUri.substr(1);
19118             }
19119 
19120             //Replace every instance of "/" with ".". This is done to follow the
19121             //OpenAjaxHub topic name convention.
19122             return restUri.replace(/\//g, ".");
19123         }
19124     };
19125 	}());
19126 	
19127 	window.finesse = window.finesse || {};
19128     window.finesse.containerservices = window.finesse.containerservices || {};
19129     window.finesse.containerservices.Topics = Topics;
19130     
19131     /** @namespace JavaScript class objects and methods to handle gadget container services.*/
19132     finesse.containerservices = finesse.containerservices || {};
19133 
19134     return Topics;
19135  });
19136 
19137 /** The following comment is to prevent jslint errors about 
19138  * using variables before they are defined.
19139  */
19140 /*global finesse*/
19141 
19142 /**
19143  * Per containerservices request, publish to the OpenAjax gadget pubsub infrastructure.
19144  *
19145  * @requires OpenAjax, finesse.containerservices.Topics
19146  */
19147 
19148 /** @private */
19149 define('containerservices/MasterPublisher',[
19150     "utilities/Utilities",
19151     "containerservices/Topics"
19152 ],
19153 function (Utilities, Topics) {
19154 
19155     var MasterPublisher = function () {
19156 
19157     var
19158     
19159     /**
19160      * Reference to the gadget pubsub Hub instance.
19161      * @private
19162      */
19163     _hub = gadgets.Hub,
19164 
19165     /**
19166      * Reference to the Topics class.
19167      * @private
19168      */
19169     _topics = Topics,
19170     
19171     /**
19172      * Reference to conversion utilities class.
19173      * @private
19174      */
19175     _utils = Utilities,
19176     
19177     /**
19178      * References to ClientServices logger methods
19179      * @private
19180      */
19181     _logger = {
19182         log: finesse.clientservices.ClientServices.log
19183     },
19184     
19185    /**
19186      * The types of possible request types supported when listening to the
19187      * requests channel. Each request type could result in different operations.
19188      * @private
19189      */
19190     _REQTYPES = {
19191 		ACTIVETAB: "ActiveTabReq",
19192 		SET_ACTIVETAB: "SetActiveTabReq",
19193         RELOAD_GADGET: "ReloadGadgetReq"
19194     },
19195 
19196     /**
19197      * Handles client requests made to the request topic. The type of the
19198      * request is described in the "type" property within the data payload. Each
19199      * type can result in a different operation.
19200      * @param {String} topic
19201      *     The topic which data was published to.
19202      * @param {Object} data
19203      *     The data containing requests information published by clients.
19204      * @param {String} data.type
19205      *     The type of the request. Supported: "ActiveTabReq", "SetActiveTabReq", "ReloadGadgetReq"
19206      * @param {Object} data.data
19207      *     May contain data relevant for the particular requests.
19208      * @param {String} [data.invokeID]
19209      *     The ID used to identify the request with the response. The invoke ID
19210      *     will be included in the data in the publish to the topic. It is the
19211      *     responsibility of the client to correlate the published data to the
19212      *     request made by using the invoke ID.
19213      * @private
19214      */
19215     _clientRequestHandler = function (topic, data) {
19216     
19217         //Ensure a valid data object with "type" and "data" properties.
19218         if (typeof data === "object" &&
19219                 typeof data.type === "string" &&
19220                 typeof data.data === "object") {
19221 			switch (data.type) {
19222 			case _REQTYPES.ACTIVETAB:
19223                 _hub.publish("finesse.containerservices.activeTab", finesse.container.Tabs.getActiveTab());
19224                 break;
19225             case _REQTYPES.SET_ACTIVETAB:
19226                 if (typeof data.data.id === "string") {
19227                     _logger.log("Handling request to activate tab: " + data.data.id);
19228                     if (!finesse.container.Tabs.activateTab(data.data.id)) {
19229                         _logger.log("No tab found with id: " + data.data.id);
19230                     }
19231                 }
19232                 break;
19233             case _REQTYPES.RELOAD_GADGET:
19234                 _hub.publish("finesse.containerservices.reloadGadget", data.data);
19235                 break;
19236 			default:
19237 				break;
19238 			}
19239         }
19240     };
19241 
19242     (function () {
19243 
19244         //Listen to a request channel to respond to any requests made by other
19245         //clients because the Master may have access to useful information.
19246         _hub.subscribe(_topics.REQUESTS, _clientRequestHandler);
19247     }());
19248 
19249     //BEGIN TEST CODE//
19250     /**
19251      * Test code added to expose private functions that are used by unit test
19252      * framework. This section of code is removed during the build process
19253      * before packaging production code. The [begin|end]TestSection are used
19254      * by the build to identify the section to strip.
19255      * @ignore
19256      */
19257     this.beginTestSection = 0;
19258 
19259     /**
19260      * @ignore
19261      */
19262     this.getTestObject = function () {
19263         //Load mock dependencies.
19264         var _mock = new MockControl();
19265         _hub = _mock.createMock(gadgets.Hub);
19266 
19267         return {
19268             //Expose mock dependencies
19269             mock: _mock,
19270             hub: _hub,
19271 			
19272             //Expose internal private functions
19273             reqtypes: _REQTYPES,
19274             
19275             clientRequestHandler: _clientRequestHandler
19276 
19277         };
19278     };
19279 
19280 
19281     /**
19282      * @ignore
19283      */
19284     this.endTestSection = 0;
19285     //END TEST CODE//
19286 	};
19287 	
19288 	window.finesse = window.finesse || {};
19289     window.finesse.containerservices = window.finesse.containerservices || {};
19290     window.finesse.containerservices.MasterPublisher = MasterPublisher;
19291 	
19292     return MasterPublisher;
19293 });
19294 
19295 /**
19296  * JavaScript representation of the Finesse WorkflowActionEvent object.
19297  *
19298  * @requires finesse.FinesseBase
19299  */
19300 
19301 /** The following comment is to prevent jslint errors about 
19302  * using variables before they are defined.
19303  */
19304 /*global FinesseBase: true, publisher:true, define:true, finesse:true, window:true */
19305 /** @private */
19306 define('containerservices/WorkflowActionEvent', ["FinesseBase"], function (FinesseBase) {
19307     var WorkflowActionEvent = FinesseBase.extend(/** @lends finesse.containerservices.WorkflowActionEvent.prototype */{
19308         /**
19309          * Reference to the WorkflowActionEvent name
19310          * This will be set by setWorkflowActionEvent
19311          * @private
19312          */
19313         _name: null,
19314 
19315         /**
19316          * Reference to the WorkflowActionEvent type
19317          * This will be set by setWorkflowActionEvent
19318          * @private
19319          */
19320         _type: null,
19321 
19322         /**
19323          * Reference to the WorkflowActionEvent handledBy value
19324          * This will be set by setWorkflowActionEvent
19325          * @private
19326          */
19327         _handledBy: null,
19328 
19329         /**
19330          * Reference to the WorkflowActionEvent params array
19331          * This will be set by setWorkflowActionEvent
19332          * @private
19333          */
19334         _params: [],
19335 
19336         /**
19337          * Reference to the WorkflowActionEvent actionVariables array
19338          * This will be set by setWorkflowActionEvent
19339          * @private
19340          */            
19341         _actionVariables: [], 
19342         
19343         /**
19344          * @class
19345          * JavaScript representation of a WorkflowActionEvent object.
19346          * The WorkflowActionEvent object is delivered as the payload of
19347          * a WorkflowAction callback.  This can be subscribed to by using
19348          * {@link finesse.containerservices.ContainerServices#addHandler} with a 
19349          * topic of {@link finesse.containerservices.ContainerServices.Topics#WORKFLOW_ACTION_EVENT}. 
19350          * Gadgets should key on events with a handleBy value of "OTHER".
19351          * 
19352          * @constructs
19353          **/
19354         init: function () {
19355             this._super();
19356         },        
19357 
19358         /**
19359 	     * Validate that the passed in object is a WorkflowActionEvent object
19360 	     * and sets the variables if it is
19361 	     * @param maybeWorkflowActionEvent A possible WorkflowActionEvent object to be evaluated and set if 
19362 	     *                                 it validates successfully.
19363 	     * @returns {Boolean} Whether it is valid or not.
19364          * @private
19365 	     */
19366 	    setWorkflowActionEvent: function(maybeWorkflowActionEvent) {
19367 	        var returnValue;
19368 	
19369 	        if (maybeWorkflowActionEvent.hasOwnProperty("name") === true &&
19370 	                maybeWorkflowActionEvent.hasOwnProperty("type") === true &&
19371                     maybeWorkflowActionEvent.hasOwnProperty("handledBy") === true &&
19372 	                maybeWorkflowActionEvent.hasOwnProperty("params") === true &&
19373 	                maybeWorkflowActionEvent.hasOwnProperty("actionVariables") === true) {
19374 	            this._name = maybeWorkflowActionEvent.name;
19375 	            this._type = maybeWorkflowActionEvent.type;
19376                 this._handledBy = maybeWorkflowActionEvent.handledBy;
19377 	            this._params = maybeWorkflowActionEvent.params;
19378 	            this._actionVariables = maybeWorkflowActionEvent.actionVariables;
19379 	            returnValue = true;
19380 	        } else {
19381 	            returnValue = false;
19382 	        }
19383 	
19384 	        return returnValue;
19385 	    },
19386 	
19387 	    /**
19388 	     * Getter for the WorkflowActionEvent name.
19389 	     * @returns {String} The name of the WorkflowAction.
19390 	     */
19391 	    getName: function () {
19392 	        // escape nulls to empty string
19393 	        return this._name || "";
19394 	    },
19395 	
19396 	    /**
19397 	     * Getter for the WorkflowActionEvent type.
19398 	     * @returns {String} The type of the WorkflowAction (BROWSER_POP, HTTP_REQUEST).
19399 	     */
19400 	    getType: function () {
19401 	        // escape nulls to empty string
19402 	        return this._type || "";
19403 	    },
19404 	
19405         /**
19406          * Getter for the WorkflowActionEvent handledBy value. Gadgets should look for
19407          * events with a handleBy of "OTHER".
19408          * @see finesse.containerservices.WorkflowActionEvent.HandledBy
19409          * @returns {String} The handledBy value of the WorkflowAction that is a value of {@link finesse.containerservices.WorkflowActionEvent.HandledBy}.
19410          */
19411         getHandledBy: function () {
19412             // escape nulls to empty string
19413             return this._handledBy || "";
19414         },
19415 
19416 
19417 	    /**
19418 	     * Getter for the WorkflowActionEvent Params map.
19419 	     * @returns {Object} key = param name, value = Object{name, value, expandedValue}
19420 	     * BROWSER_POP<ul>
19421 	     * <li>windowName : Name of window to pop into, or blank to always open new window.
19422 	     * <li>path : URL to open.</ul>
19423 	     * HTTP_REQUEST<ul>
19424 	     * <li>method : "PUT" or "POST".
19425 	     * <li>location : "FINESSE" or "OTHER".
19426 	     * <li>contentType : MIME type of request body, if applicable, e.g. "text/plain".
19427 	     * <li>path : Request URL.
19428 	     * <li>body : Request content for POST requests.</ul>
19429 	     */
19430 	    getParams: function () {
19431 	        var map = {},
19432 	            params = this._params,
19433 	            i,
19434 	            param;
19435 	
19436 	        if (params === null || params.length === 0) {
19437 	            return map;
19438 	        }
19439 	
19440 	        for (i = 0; i < params.length; i += 1) {
19441 	            param = params[i];
19442 	            // escape nulls to empty string
19443 	            param.name = param.name || "";
19444 	            param.value = param.value || "";
19445 	            param.expandedValue = param.expandedValue || "";
19446 	            map[param.name] = param;
19447 	        }
19448 	
19449 	        return map;
19450 	    },
19451 	    
19452 	    /**
19453 	     * Getter for the WorkflowActionEvent ActionVariables map
19454 	     * @returns {Object} key = action variable name, value = Object{name, type, node, testValue, actualValue}
19455 	     */
19456 	    getActionVariables: function() {
19457 	        var map = {},
19458 	            actionVariables = this._actionVariables,
19459 	            i,
19460 	            actionVariable;
19461 	
19462 	        if (actionVariables === null || actionVariables.length === 0) {
19463 	            return map;
19464 	        }
19465 	
19466 	        for (i = 0; i < actionVariables.length; i += 1) {
19467 	            actionVariable = actionVariables[i];
19468 	            // escape nulls to empty string
19469 	            actionVariable.name = actionVariable.name || "";
19470 	            actionVariable.type = actionVariable.type || "";
19471 	            actionVariable.node = actionVariable.node || "";
19472 	            actionVariable.testValue = actionVariable.testValue || "";
19473 	            actionVariable.actualValue = actionVariable.actualValue || "";
19474 	            map[actionVariable.name] = actionVariable;
19475 	        }
19476 	
19477 	        return map;
19478 	    }
19479     }); 
19480     
19481     
19482     WorkflowActionEvent.HandledBy = /** @lends finesse.containerservices.WorkflowActionEvent.HandledBy.prototype */ {
19483         /**
19484          * This specifies that Finesse will handle this WorkflowActionEvent.  A 3rd Party can do additional processing
19485          * with the action, but first and foremost Finesse will handle this WorkflowAction.
19486          */
19487         FINESSE: "FINESSE",
19488 
19489         /**
19490          * This specifies that a 3rd Party will handle this WorkflowActionEvent.  Finesse's Workflow Engine Executor will 
19491          * ignore this action and expects Gadget Developers to take action.
19492          */
19493         OTHER: "OTHER",
19494         
19495         /**
19496          * @class This is the set of possible HandledBy values used for WorkflowActionEvent from ContainerServices.  This
19497          * is provided from the {@link finesse.containerservices.WorkflowActionEvent#getHandledBy} method.
19498          * @constructs
19499          */
19500         _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
19501     };    
19502     
19503     window.finesse = window.finesse || {};
19504     window.finesse.containerservices = window.finesse.containerservices || {};
19505     window.finesse.containerservices.WorkflowActionEvent = WorkflowActionEvent;
19506     
19507     return WorkflowActionEvent;
19508 });
19509 
19510 /**
19511  * JavaScript representation of the Finesse TimerTickEvent
19512  *
19513  * @requires finesse.FinesseBase
19514  */
19515 
19516 /** The following comment is to prevent jslint errors about 
19517  * using variables before they are defined.
19518  */
19519 /*global FinesseBase: true, publisher:true, define:true, finesse:true, window:true */
19520 /** @private */
19521 define('containerservices/TimerTickEvent',[
19522     "FinesseBase"
19523 ],
19524 function (FinesseBase) {
19525     var TimerTickEvent = FinesseBase.extend(/** @lends finesse.containerservices.TimerTickEvent.prototype */{
19526         /**
19527          * date the TimerTickEvent was queued 
19528          * @private
19529          */
19530         _dateQueued: null,
19531 
19532         /**
19533          * the frequency of the timer tick (in miiliseconds)
19534          * @private
19535          */
19536         _tickFrequency: 1000,
19537 
19538         /**
19539          * @class
19540          * JavaScript representation of a TimerTickEvent object.
19541          * The TimerTickEvent object is delivered as the payload of
19542          * a TimerTickEvent callback.  This can be subscribed to by using
19543          * {@link finesse.containerservices.ContainerServices#addHandler} with a 
19544          * topic of {@link finesse.containerservices.ContainerServices.Topics#TIMER_TICK_EVENT}. 
19545          * 
19546          * @constructs
19547          **/
19548         init: function (tickFrequency, dateQueued) {
19549             this._super();
19550             
19551             this._tickFrequency = tickFrequency;
19552             this._dateQueued = dateQueued;
19553         },
19554 
19555        /**
19556          * Get the "tickFrequency" field
19557          * @param {int} which is the "TickFrequency" field
19558          * @private
19559          */
19560         getTickFrequency: function () {
19561             return this._tickFrequency;
19562         },
19563 
19564         /**
19565          * Getter for the TimerTickEvent "DateQueued" field. 
19566          * @returns {Date} which is a Date object when the TimerTickEvent was queued
19567          */
19568         getDateQueued: function () {
19569             return this._dateQueued;
19570         }
19571 
19572     });
19573     
19574     window.finesse = window.finesse || {};
19575     window.finesse.containerservices = window.finesse.containerservices || {};
19576     window.finesse.containerservices.TimerTickEvent = TimerTickEvent;
19577     
19578     return TimerTickEvent;
19579 });
19580 
19581 /**
19582  * JavaScript representation of the Finesse GadgetViewChangedEvent object.
19583  *
19584  * @requires finesse.FinesseBase
19585  */
19586 
19587 /** The following comment is to prevent jslint errors about 
19588  * using variables before they are defined.
19589  */
19590 /*global FinesseBase: true, publisher:true, define:true, finesse:true, window:true */
19591 /** @private */
19592 define('containerservices/GadgetViewChangedEvent',[
19593     "FinesseBase"
19594 ],
19595 function (FinesseBase) {
19596     var GadgetViewChangedEvent = FinesseBase.extend(/** @lends finesse.containerservices.GadgetViewChangedEvent.prototype */{
19597         /**
19598          * Reference to the gadget id
19599          * @private
19600          */
19601         _gadgetId: null,
19602 
19603         /**
19604          * Reference to the tab id
19605          * @private
19606          */
19607         _tabId: null,
19608 
19609         /**
19610          * Reference to the maxAvailableHeight
19611          * @private
19612          */
19613         _maxAvailableHeight: null,
19614 
19615         /**
19616          * Reference to the view
19617          * E.g. 'default' or 'canvas'
19618          * @private
19619          */
19620         _view: null,
19621         
19622         /**
19623          * @class
19624          * JavaScript representation of a GadgetViewChangedEvent object.
19625          * The GadgetViewChangedEvent object is delivered as the payload of
19626          * a GadgetViewChangedEvent callback.  This can be subscribed to by using
19627          * {@link finesse.containerservices.ContainerServices#addHandler} with a 
19628          * topic of {@link finesse.containerservices.ContainerServices.Topics#GADGET_VIEW_CHANGED_EVENT}. 
19629          * 
19630          * @constructs
19631          **/
19632         init: function (gadgetId, tabId, maxAvailableHeight, view) {
19633             this._super();
19634 
19635             this._gadgetId = gadgetId;
19636             this._tabId = tabId;
19637             this._maxAvailableHeight = maxAvailableHeight;
19638             this._view = view;
19639         },
19640     
19641         /**
19642          * Getter for the gadget id.
19643          * @returns {String} The identifier for the gadget changing view.
19644          */
19645         getGadgetId: function () {
19646             // escape nulls to empty string
19647             return this._gadgetId || "";
19648         },
19649     
19650         /**
19651          * Getter for the maximum available height.
19652          * @returns {String} The maximum available height for the gadget's view.
19653          */
19654         getMaxAvailableHeight: function () {
19655             // escape nulls to empty string
19656             return this._maxAvailableHeight || "";
19657         },
19658 
19659         /**
19660          * Getter for the tab id.
19661          * @returns {String} The identifier for the tab where the gadget changing view resides.
19662          */
19663         getTabId: function () {
19664             // escape nulls to empty string
19665             return this._tabId || "";
19666         },
19667 
19668         /**
19669          * Getter for the view.
19670          * @returns {String} The view type the gadget is changing to.
19671          */
19672         getView: function () {
19673             // escape nulls to empty string
19674             return this._view || "";
19675         }
19676     });
19677     
19678     window.finesse = window.finesse || {};
19679     window.finesse.containerservices = window.finesse.containerservices || {};
19680     window.finesse.containerservices.GadgetViewChangedEvent = GadgetViewChangedEvent;
19681     
19682     return GadgetViewChangedEvent;
19683 });
19684 
19685 /**
19686  * JavaScript representation of the Finesse MaxAvailableHeightChangedEvent object.
19687  *
19688  * @requires finesse.FinesseBase
19689  */
19690 
19691 /** The following comment is to prevent jslint errors about 
19692  * using variables before they are defined.
19693  */
19694 /*global FinesseBase: true, publisher:true, define:true, finesse:true, window:true */
19695 /** @private */
19696 define('containerservices/MaxAvailableHeightChangedEvent',[
19697     "FinesseBase"
19698 ],
19699 function (FinesseBase) {
19700     var MaxAvailableHeightChangedEvent = FinesseBase.extend(/** @lends finesse.containerservices.MaxAvailableHeightChangedEvent.prototype */{
19701 
19702         /**
19703          * Reference to the maxAvailableHeight
19704          * @private
19705          */
19706         _maxAvailableHeight: null,
19707         
19708         /**
19709          * @class
19710          * JavaScript representation of a MaxAvailableHeightChangedEvent object.
19711          * The MaxAvailableHeightChangedEvent object is delivered as the payload of
19712          * a MaxAvailableHeightChangedEvent callback.  This can be subscribed to by using
19713          * {@link finesse.containerservices.ContainerServices#addHandler} with a 
19714          * topic of {@link finesse.containerservices.ContainerServices.Topics#MAX_AVAILABLE_HEIGHT_CHANGED_EVENT}. 
19715          * 
19716          * @constructs
19717          **/
19718         init: function (maxAvailableHeight) {
19719             this._super();
19720 
19721             this._maxAvailableHeight = maxAvailableHeight;
19722         },
19723     
19724         /**
19725          * Getter for the maximum available height.
19726          * @returns {String} The maximum available height for a gadget in canvas view
19727          */
19728         getMaxAvailableHeight: function () {
19729             // escape nulls to empty string
19730             return this._maxAvailableHeight || "";
19731         }
19732     });
19733     
19734     window.finesse = window.finesse || {};
19735     window.finesse.containerservices = window.finesse.containerservices || {};
19736     window.finesse.containerservices.MaxAvailableHeightChangedEvent = MaxAvailableHeightChangedEvent;
19737     
19738     return MaxAvailableHeightChangedEvent;
19739 });
19740 
19741 /**
19742  * Exposes a set of API wrappers that will hide the dirty work of
19743  *     constructing Finesse API requests and consuming Finesse events.
19744  *
19745  * @requires OpenAjax, jQuery 1.5, finesse.utilities.Utilities
19746  */
19747 
19748 /** The following comment is to prevent jslint errors about using variables before they are defined. */
19749 /*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 */
19750 /*jslint nomen: true, unparam: true, sloppy: true, white: true */
19751 /** @private */
19752 define('containerservices/ContainerServices',[
19753     "utilities/Utilities",
19754     "restservices/Notifier",
19755     "containerservices/Topics",
19756     "containerservices/MasterPublisher",
19757     "containerservices/WorkflowActionEvent",
19758     "containerservices/TimerTickEvent",
19759     "containerservices/GadgetViewChangedEvent",
19760     "containerservices/MaxAvailableHeightChangedEvent"
19761 ],
19762 function (Utilities, Notifier, Topics, MasterPublisher, WorkflowActionEvent) {
19763 
19764     var ContainerServices = ( function () { /** @lends finesse.containerservices.ContainerServices.prototype */
19765 
19766     var
19767 
19768     /**
19769      * Shortcut reference to the Utilities singleton
19770      * This will be set by init()
19771      * @private
19772      */
19773     _util,
19774 
19775     /**
19776      * Shortcut reference to the gadget pubsub Hub instance.
19777      * This will be set by init()
19778      * @private
19779      */
19780     _hub,
19781 
19782     /**
19783      * Boolean whether this instance is master or not
19784      * @private
19785      */
19786     _master = false,
19787 
19788     /**
19789      * Whether the Client Services have been initiated yet.
19790      * @private
19791      */
19792     _inited = false,
19793     
19794     /**
19795      * References to ClientServices logger methods
19796      * @private
19797      */
19798     _logger = {
19799         log: finesse.clientservices.ClientServices.log
19800     },
19801     
19802      /**
19803      * Stores the list of subscription IDs for all subscriptions so that it
19804      * could be retrieve for unsubscriptions.
19805      * @private
19806      */
19807     _subscriptionID = {},
19808     
19809     /**
19810      * Reference to the gadget's parent container
19811      * @private
19812      */
19813     _container,
19814 
19815     /**
19816      * Reference to the MasterPublisher
19817      * @private
19818      */
19819     _publisher,
19820     
19821     /**
19822      * Object that will contain the Notifiers
19823      * @private
19824      */
19825     _notifiers = {},
19826 
19827     /**
19828      * Reference to the tabId that is associated with the gadget
19829      * @private
19830      */
19831     _myTab = null,
19832     
19833     /**
19834      * Reference to the visibility of current gadget
19835      * @private
19836      */
19837     _visible = false,
19838     
19839     /**
19840      * Reference for auth modes constants.
19841      * @private
19842      */
19843     _authModes,
19844     
19845     /**
19846      * Shortcut reference to the Topics class.
19847      * This will be set by init()
19848      * @private
19849      */
19850     _topics,
19851 
19852     /**
19853      * Associates a topic name with the private handler function.
19854      * Adding a new topic requires that you add this association here 
19855      *  in to keep addHandler generic.
19856      * @param {String} topic : Specifies the callback to retrieve
19857      * @return {Function} The callback function associated with the topic param.
19858      * @private
19859      */
19860     _topicCallback = function (topic) {
19861         var callback, notifier;
19862         switch (topic)
19863         {
19864             case finesse.containerservices.ContainerServices.Topics.ACTIVE_TAB:
19865                 callback = _tabTracker;
19866                 break;
19867             case finesse.containerservices.ContainerServices.Topics.WORKFLOW_ACTION_EVENT:
19868                 callback = _workflowActionEventTracker;
19869                 break;
19870             case finesse.containerservices.ContainerServices.Topics.RELOAD_GADGET_EVENT:
19871                 callback = _masterReloader;
19872                 break;
19873             case finesse.containerservices.ContainerServices.Topics.GADGET_VIEW_CHANGED_EVENT:
19874                 callback = _gadgetViewChanged;
19875                 break;
19876             case finesse.containerservices.ContainerServices.Topics.MAX_AVAILABLE_HEIGHT_CHANGED_EVENT:
19877                 callback = _maxAvailableHeightChanged;
19878                 break;
19879             case finesse.containerservices.ContainerServices.Topics.ACCESS_TOKEN_REFRESHED_EVENT:
19880                 callback = _accessTokenRefreshed;
19881                 break;
19882             default:
19883                 callback = function (param) {
19884                      var data = null;
19885                      
19886                      notifier = _getNotifierReference(topic);
19887                      
19888                      if (arguments.length === 1) {
19889                         data = param;
19890                      } else {
19891                         data = arguments;
19892                      }
19893                      notifier.notifyListeners(data);
19894                 };
19895         }
19896         return callback;
19897     },
19898 
19899     /**
19900      * Ensure that ClientServices have been inited.
19901      * @private
19902      */
19903     _isInited = function () {
19904         if (!_inited) {
19905             throw new Error("ContainerServices needs to be inited.");
19906         }
19907     },
19908 
19909     /**
19910      * Retrieves a Notifier reference to a particular topic, and creates one if it doesn't exist.
19911      * @param {String} topic : Specifies the notifier to retrieve
19912      * @return {Notifier} The notifier object.
19913      * @private
19914      */
19915     _getNotifierReference = function (topic) {
19916         if (!_notifiers.hasOwnProperty(topic))
19917         {
19918             _notifiers[topic] = new Notifier();
19919         }
19920 
19921         return _notifiers[topic];
19922     },
19923 
19924     /**
19925      * Utility function to make a subscription to a particular topic. Only one
19926      * callback function is registered to a particular topic at any time.
19927      * @param {String} topic
19928      *     The full topic name. The topic name should follow the OpenAjax
19929      *     convention using dot notation (ex: finesse.api.User.1000).
19930      * @param {Function} callback
19931      *     The function that should be invoked with the data when an event
19932      *     is delivered to the specific topic.
19933      * @returns {Boolean}
19934      *     True if the subscription was made successfully and the callback was
19935      *     been registered. False if the subscription already exist, the
19936      *     callback was not overwritten.
19937      * @private
19938      */
19939     _subscribe = function (topic, callback) {
19940         _isInited();
19941 
19942         //Ensure that the same subscription isn't made twice.
19943         if (!_subscriptionID[topic]) {
19944             //Store the subscription ID using the topic name as the key.
19945             _subscriptionID[topic] = _hub.subscribe(topic,
19946                 //Invoke the callback just with the data object.
19947                 function (topic, data) {
19948                     callback(data);
19949                 });
19950             return true;
19951         }
19952         return false;
19953     },
19954 
19955     /**
19956      * Unsubscribe from a particular topic.
19957      * @param {String} topic : The full topic name.
19958      * @private
19959      */
19960     _unsubscribe = function (topic) {
19961         _isInited();
19962 
19963         //Unsubscribe from the topic using the subscription ID recorded when
19964         //the subscription was made, then delete the ID from data structure.
19965         _hub.unsubscribe(_subscriptionID[topic]);
19966         delete _subscriptionID[topic];
19967     },
19968 
19969     /**
19970      * Get my tab id.
19971      * @returns {String} tabid : The tabid of this container/gadget.
19972      * @private
19973      */
19974     _getMyTab = function () {
19975         if (_myTab === null)
19976         {
19977             try {
19978             _myTab = $(frameElement).closest("div.tab-panel").attr("id").replace("panel_", "");
19979             } catch (err) {
19980                 _logger.log("Error accessing current tab: " + err.message);
19981                _myTab = null;
19982             }
19983         }
19984         return _myTab;
19985     },
19986     
19987     /**
19988      * Callback function that is called when an activeTab message is posted to the Hub.
19989      * Notifies listener functions if this tab is the one that was just made active.
19990      * @param {String} tabId : The tabId which was just made visible.
19991      * @private
19992      */
19993     _tabTracker = function(tabId) {
19994         if (tabId === _getMyTab()) {
19995             if(!_visible) {
19996                 _visible = true;
19997                 _notifiers[finesse.containerservices.ContainerServices.Topics.ACTIVE_TAB].notifyListeners(this);
19998             }
19999         } else {
20000             _visible = false;
20001         }
20002     },
20003     
20004     /**
20005      * Make a request to set a particular tab active. This
20006      * method should be called after {@link finesse.containerservices.ContainerServices#addHandler}
20007      * to ensure the gadget gets properly initialized.
20008      * @param {String} tabId
20009      *    The tabId (not the label text) of the tab to make active.  If the id is invalid, no action will occur.
20010      * @private
20011      */
20012     _activateTab = function ( tabId ) {
20013         _logger.log("Sending request to activate tab: " + tabId);
20014         if(_hub){
20015             var data = {
20016                 type: "SetActiveTabReq",
20017                 data: { id: tabId },
20018                 invokeID: (new Date()).getTime()          
20019             };
20020             _hub.publish(_topics.REQUESTS, data);
20021         } else {
20022             throw new Error("Hub is not defined.");
20023         }
20024         
20025     },
20026 
20027     /**
20028      * Callback function that is called when a gadget view changed message is posted to the Hub.
20029      * @private
20030      */
20031     _gadgetViewChanged = function (data) {
20032         if (data) {
20033             var gadgetViewChangedEvent = new finesse.containerservices.GadgetViewChangedEvent(
20034                 data.gadgetId,
20035                 data.tabId,
20036                 data.maxAvailableHeight,
20037                 data.view);
20038 
20039             _notifiers[finesse.containerservices.ContainerServices.Topics.GADGET_VIEW_CHANGED_EVENT].notifyListeners(gadgetViewChangedEvent);
20040         }
20041     },
20042 
20043     /**
20044      * Callback function that is called when a max available height changed message is posted to the Hub.
20045      * @private
20046      */
20047     _maxAvailableHeightChanged = function (data) {
20048         if (data) {
20049             var maxAvailableHeightChangedEvent = new finesse.containerservices.MaxAvailableHeightChangedEvent(
20050                 data.maxAvailableHeight);
20051 
20052             _notifiers[finesse.containerservices.ContainerServices.Topics.MAX_AVAILABLE_HEIGHT_CHANGED_EVENT].notifyListeners(maxAvailableHeightChangedEvent);
20053         }
20054     },
20055 
20056     /**
20057      * Callback function that is called when a workflowActionEvent message is posted to the Hub.
20058      * Notifies listener functions if the posted object can be converted to a proper WorkflowActionEvent object.
20059      * @param {String} workflowActionEvent : The workflowActionEvent that was posted to the Hub
20060      * @private
20061      */
20062     _workflowActionEventTracker = function(workflowActionEvent) {
20063         var vWorkflowActionEvent = new finesse.containerservices.WorkflowActionEvent();
20064                 
20065         if (vWorkflowActionEvent.setWorkflowActionEvent(workflowActionEvent)) {
20066             _notifiers[finesse.containerservices.ContainerServices.Topics.WORKFLOW_ACTION_EVENT].notifyListeners(vWorkflowActionEvent);
20067         }
20068         // else
20069         // {
20070             //?console.log("Error in ContainerServices : _workflowActionEventTracker - could not map published HUB object to WorkflowActionEvent");
20071         // }
20072 
20073     },
20074 
20075     /**
20076      * Callback function that is called when a reloadGadget event message is posted to the Hub.
20077      *
20078      * Grabs the id of the gadget we want to reload from the data and reload it!
20079      *
20080      * @param {String} topic
20081      *      which topic the event came on (unused)
20082      * @param {Object} data
20083      *      the data published with the event
20084      * @private
20085      */
20086     _masterReloader = function (topic, data) {
20087         var gadgetId = data.gadgetId;
20088         if (gadgetId) {
20089             _container.reloadGadget(gadgetId);
20090         }
20091     },
20092     
20093     /**
20094      * Callback function that is called when a refresh access token event message is posted to the Hub.
20095      *
20096      * Get access token from the data and update the finesse.gadget.Config object!
20097      *
20098      * @param {String} topic
20099      *      which topic the event came on (unused)
20100      * @param {Object} data
20101      *      the data published with the event
20102      * @private
20103      */
20104     _accessTokenRefreshed = function(topic , data){
20105         _logger.log("Access token refreshed: updating new token to finesse.gadget.Config");
20106 
20107         if(data.authToken){
20108             finesse.gadget.Config.authToken = data.authToken;
20109         }
20110 
20111      },
20112     
20113     /**
20114      * Pulls the gadget id from the url parameters
20115      * @return {String} id of the gadget
20116      * @private
20117      */
20118     _findMyGadgetId = function () {
20119         if (gadgets && gadgets.util && gadgets.util.getUrlParameters()) {
20120             return gadgets.util.getUrlParameters().mid;
20121         }
20122     };
20123 
20124     return {
20125         /**
20126          * @class
20127          * This class provides container-level services for gadget developers, exposing container events by
20128          * calling a set of exposed functions. Gadgets can utilize the container dialogs and 
20129          * event handling (add/remove).
20130          * @example
20131          *    containerServices = finesse.containerservices.ContainerServices.init();
20132          *    containerServices.addHandler(
20133          *      finesse.containerservices.ContainerServices.Topics.ACTIVE_TAB, 
20134          *      function() {
20135          *          clientLogs.log("Gadget is now visible");  // log to Finesse logger
20136          *          // automatically adjust the height of the gadget to show the html
20137          *          gadgets.window.adjustHeight();
20138          *      });
20139          *    containerServices.makeActiveTabReq();
20140          *    
20141          * @constructs
20142          */
20143         _fakeConstuctor: function () {
20144             /* This is here so we can document init() as a method rather than as a constructor. */
20145         },
20146         
20147         /**
20148          * Initialize ContainerServices for use in gadget.
20149          * @param {Boolean} [master=false] Do not use this parameter from your gadget.
20150          * @returns ContainerServices instance.
20151          */
20152         init: function (master) {
20153             if (!_inited) {
20154                 _inited = true;
20155                 // Set shortcuts
20156                 _util = Utilities;
20157                 _authModes = _util.getAuthModes();
20158 
20159                 //init the hub only when it's available
20160                 if(gadgets.Hub) {
20161                     _hub = gadgets.Hub;
20162                 }
20163 
20164                 if(Topics) {
20165                     _topics = Topics;
20166                 }
20167 
20168                 if (master) {
20169                     _master = true;
20170                     _container = finesse.container.Container;
20171                     _publisher = new MasterPublisher();
20172 
20173                     // subscribe for reloading gadget events
20174                     // we only want the master ContainerServices handling these events
20175                     _hub.subscribe(_topics.RELOAD_GADGET, _topicCallback(_topics.RELOAD_GADGET));
20176                 } else {
20177                     _container = parent.finesse.container.Container;
20178                 }
20179             }
20180             
20181             this.makeActiveTabReq();
20182             
20183             //Every gadget should subscribe for access token refresh events in case of SSO deployment
20184             if( finesse.container.Config.systemAuthMode === _authModes.SSO){
20185                 _hub.subscribe(finesse.containerservices.ContainerServices.Topics.ACCESS_TOKEN_REFRESHED_EVENT, _topicCallback(finesse.containerservices.ContainerServices.Topics.ACCESS_TOKEN_REFRESHED_EVENT));
20186             }
20187 
20188             //Return the CS object for object chaining.
20189             return this;
20190         },
20191 
20192         /**
20193          * Shows the jQuery UI Dialog with the specified parameters. The following are the
20194          * default parameters: <ul>
20195          *     <li> Title of "Cisco Finesse".</li>
20196          *     <li>Message of "A generic error has occured".</li>
20197          *     <li>The only button, "Ok", closes the dialog.</li>
20198          *     <li>Modal (blocks other dialogs).</li>
20199          *     <li>Not draggable.</li>
20200          *     <li>Fixed size.</li></ul>
20201          * @param {Object} options
20202          *  An object containing additional options for the dialog.
20203          * @param {String/Boolean} options.title
20204          *  Title to use. undefined defaults to "Cisco Finesse". false to hide
20205          * @param {Function} options.close
20206          *  A function to invoke when the dialog is closed.
20207          * @param {String} options.message
20208          *  The message to display in the dialog.
20209          *  Defaults to "A generic error has occurred."
20210          * @param {Boolean} options.isBlocking
20211          *  Flag indicating whether this dialog will block other dialogs from being shown (Modal).
20212          * @returns {jQuery} JQuery wrapped object of the dialog DOM element.
20213          * @see finesse.containerservices.ContainerServices#hideDialog
20214          */
20215         showDialog: function(options) {
20216             if ((_container.showDialog !== undefined) && (_container.showDialog !== this.showDialog)) {
20217                 return _container.showDialog(options);
20218             }
20219         },
20220         
20221         /**
20222          * Hides the jQuery UI Dialog.
20223          * @returns {jQuery} jQuery wrapped object of the dialog DOM element
20224          * @see finesse.containerservices.ContainerServices#showDialog
20225          */
20226         hideDialog: function() {
20227             if ((_container.hideDialog !== undefined) && (_container.hideDialog !== this.hideDialog)) {
20228                 return _container.hideDialog();
20229             }
20230         },
20231 
20232         /**
20233          *  Reloads the current gadget. 
20234          *  For use from within a gadget only.
20235          */
20236         reloadMyGadget: function () {
20237             var topic, gadgetId, data;
20238 
20239             if (!_master) {
20240                 // first unsubscribe this gadget from all topics on the hub
20241                 for (topic in _notifiers) {
20242                     if (_notifiers.hasOwnProperty(topic)) {
20243                         _unsubscribe(topic);
20244                         delete _notifiers[topic];
20245                     }
20246                 }
20247 
20248                 // send an asynch request to the hub to tell the master container
20249                 // services that we want to refresh this gadget
20250                 gadgetId = _findMyGadgetId();
20251                 data = {
20252                     type: "ReloadGadgetReq",
20253                     data: {gadgetId: gadgetId},
20254                     invokeID: (new Date()).getTime()          
20255                 };
20256                 _hub.publish(_topics.REQUESTS, data);
20257             }            
20258         },
20259 
20260         /**
20261          * Updates the url for this gadget and then reload it.
20262          * 
20263          * This allows the gadget to be reloaded from a different location
20264          * than what is uploaded to the current server. For example, this
20265          * would be useful for 3rd party gadgets to implement their own failover
20266          * mechanisms.
20267          *
20268          * For use from within a gadget only.
20269          *
20270          * @param {String} url
20271          *      url from which to reload gadget
20272          */
20273         reloadMyGadgetFromUrl: function (url) {
20274             if (!_master) {
20275                 var gadgetId = _findMyGadgetId();
20276 
20277                 // update the url in the container
20278                 _container.modifyGadgetUrl(gadgetId, url);
20279 
20280                 // reload it
20281                 this.reloadMyGadget();
20282             }
20283         },
20284         
20285         /**
20286          * Adds a handler for one of the supported topics provided by ContainerServices.  The callbacks provided
20287          * will be invoked when that topic is notified.  
20288          * @param {String} topic
20289          *  The Hub topic to which we are listening.
20290          * @param {Function} callback
20291          *  The callback function to invoke.
20292          * @see finesse.containerservices.ContainerServices.Topics
20293          * @see finesse.containerservices.ContainerServices#removeHandler
20294          */
20295         addHandler: function (topic, callback) {
20296             _isInited();
20297             var notifier = null;
20298             
20299             try {    
20300                 // For backwards compatibility...
20301                 if (topic === "tabVisible") {
20302                     if (window.console && typeof window.console.log === "function") {
20303                         window.console.log("WARNING - Using tabVisible as topic.  This is deprecated.  Use finesse.containerservices.ContainerServices.Topics.ACTIVE_TAB now!");
20304                     }
20305                     
20306                     topic = finesse.containerservices.ContainerServices.Topics.ACTIVE_TAB;
20307                 }
20308                 
20309                 // Add the callback to the notifier.
20310                 _util.validateHandler(callback);
20311             
20312                 notifier = _getNotifierReference(topic);
20313             
20314                 notifier.addListener(callback);
20315             
20316                 // Subscribe to the topic. _subscribe ensures that a topic is only subscribed to once,
20317                 // so attempt to subscribe each time a handler is added. This ensures that a topic is subscribed
20318                 // to only when necessary.
20319                 _subscribe(topic, _topicCallback(topic));
20320             
20321             } catch (err) {
20322                 throw new Error("addHandler(): " + err);
20323             }
20324         }, 
20325         
20326         /**
20327          * Removes a previously-added handler for one of the supported topics.
20328          * @param {String} topic
20329          *  The Hub topic from which we are removing the callback.
20330          * @param {Function} callback
20331          *  The name of the callback function to remove.
20332          * @see finesse.containerservices.ContainerServices.Topics
20333          * @see finesse.containerservices.ContainerServices#addHandler
20334          */
20335         removeHandler: function(topic, callback) {
20336             var notifier = null;
20337             
20338             try {
20339                 _util.validateHandler(callback);
20340     
20341                 notifier = _getNotifierReference(topic);
20342     
20343                 notifier.removeListener(callback);
20344             } catch (err) {
20345                 throw new Error("removeHandler(): " + err);
20346             }
20347         },
20348 
20349         /**
20350          * Returns the visibility of current gadget.  Note that this 
20351          * will not be set until after the initialization of the gadget.
20352          * @return {Boolean} The visibility of current gadget.
20353          */
20354         tabVisible: function(){
20355             return _visible;
20356         },
20357         
20358         /**
20359          * Make a request to check the current tab.  The 
20360          * activeTab event will be invoked if on the active tab.  This
20361          * method should be called after {@link finesse.containerservices.ContainerServices#addHandler}
20362          * to ensure the gadget gets properly initialized.
20363          */
20364         makeActiveTabReq : function () {
20365             if(_hub){
20366                 var data = {
20367                     type: "ActiveTabReq",
20368                     data: {},
20369                     invokeID: (new Date()).getTime()          
20370                 };
20371                 _hub.publish(_topics.REQUESTS, data);
20372             } else {
20373                 throw new Error("Hub is not defined.");
20374             }
20375             
20376         },
20377 
20378         /**
20379          * Make a request to set a particular tab active. This
20380          * method should be called after {@link finesse.containerservices.ContainerServices#addHandler}
20381          * to ensure the gadget gets properly initialized.
20382          * @param {String} tabId
20383          *    The tabId (not the label text) of the tab to make active.  If the id is invalid, no action will occur.
20384          */
20385         activateTab : function (tabId) {
20386             _activateTab(tabId);
20387         },
20388         
20389         /**
20390          * Make a request to set this container's tab active. This
20391          * method should be called after {@link finesse.containerservices.ContainerServices#addHandler}
20392          * to ensure the gadget gets properly initialized.
20393          */
20394         activateMyTab : function () {
20395             _activateTab( _getMyTab() );
20396         },
20397         
20398         /**
20399          * Get the tabId of my container/gadget.
20400          * @returns {String} tabid : The tabid of this container/gadget.
20401          */
20402         getMyTabId : function () {
20403             return _getMyTab();
20404         },
20405 
20406         /**
20407          * Gets the id of the gadget.
20408          * @returns {number} the id of the gadget
20409          */
20410         getMyGadgetId : function () {
20411             return _findMyGadgetId();
20412         },
20413 
20414         //BEGIN TEST CODE//
20415         /**
20416          * Test code added to expose private functions that are used by unit test
20417          * framework. This section of code is removed during the build process
20418          * before packaging production code. The [begin|end]TestSection are used
20419          * by the build to identify the section to strip.
20420          * @ignore
20421          */
20422         beginTestSection : 0,
20423 
20424         /**
20425          * @ignore
20426          */
20427         getTestObject: function () {
20428             //Load mock dependencies.
20429             var _mock = new MockControl();
20430             _util = _mock.createMock(Utilities);
20431             _hub = _mock.createMock(gadgets.Hub);
20432             _inited = true;
20433             return {
20434                 //Expose mock dependencies
20435                 mock: _mock,
20436                 hub: _hub,
20437                 util: _util,
20438                 addHandler: this.addHandler,
20439                 removeHandler: this.removeHandler
20440             };
20441         },
20442 
20443         /**
20444          * @ignore
20445          */
20446        endTestSection: 0
20447         //END TEST CODE//
20448     };
20449     }());
20450     
20451     ContainerServices.Topics = /** @lends finesse.containerservices.ContainerServices.Topics.prototype */ {
20452         /**
20453          * Topic for subscribing to be notified when the active tab changes.
20454          * The provided callback will be invoked when the tab that the gadget 
20455          * that subscribes with this becomes active.  To ensure code is called
20456          * when the gadget is already on the active tab use the 
20457          * {@link finesse.containerservices.ContainerServices#makeActiveTabReq}
20458          * method.
20459          */
20460         ACTIVE_TAB: "finesse.containerservices.activeTab",
20461 
20462         /**
20463          * Topic for WorkflowAction events traffic.
20464          * The provided callback will be invoked when a WorkflowAction needs
20465          * to be handled.  The callback will be passed a {@link finesse.containerservices.WorkflowActionEvent}
20466          * that can be used to interrogate the WorkflowAction and determine to use or not.
20467          */
20468         WORKFLOW_ACTION_EVENT: "finesse.containerservices.workflowActionEvent",
20469         
20470         /**
20471          * Topic for Timer Tick event.
20472          * The provided callback will be invoked when this event is fired.
20473          * The callback will be passed a {@link finesse.containerservices.TimerTickEvent}.
20474          */
20475         TIMER_TICK_EVENT : "finesse.containerservices.timerTickEvent",
20476 
20477         /**
20478          * Topic for Reload Gadget events traffic.
20479          * Only the master ContainerServices instance will handle this event.
20480          */
20481         RELOAD_GADGET_EVENT: "finesse.containerservices.reloadGadget",
20482         
20483         /**
20484          * Topic for listening to gadget view changed events.
20485          * The provided callback will be invoked when a gadget changes view.
20486          * The callback will be passed a {@link finesse.containerservices.GadgetViewChangedEvent}.
20487          */
20488         GADGET_VIEW_CHANGED_EVENT: "finesse.containerservices.gadgetViewChangedEvent",
20489 
20490         /**
20491          * Topic for listening to max available height changed events.
20492          * The provided callback will be invoked when the maximum height available to a maximized gadget changes.
20493          * This event is only meant for maximized gadgets and will not be published unless a maximized gadget exists.
20494          * The callback will be passed a {@link finesse.containerservices.MaxAvailableHeightChangedEvent}.
20495          */
20496         MAX_AVAILABLE_HEIGHT_CHANGED_EVENT: "finesse.containerservices.maxAvailableHeightChangedEvent",
20497         
20498         /**
20499          * Topic for listening to token refresh events.
20500          * The provided callback will be invoked when the access token is refreshed.
20501          * This event is only meant for updating the access token in finesse.gadget.Config object
20502          * The callback will be passed a {@link finesse.containerservices.accessTokenRefreshedEvent}.
20503          */
20504         ACCESS_TOKEN_REFRESHED_EVENT: "finesse.containerservices.accessTokenRefreshedEvent",
20505 
20506         /**
20507          * @class This is the set of Topics used for subscribing for events from ContainerServices.
20508          * Use {@link finesse.containerservices.ContainerServices#addHandler} to subscribe to the topic.
20509          * 
20510          * @constructs
20511          */
20512         _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
20513     };
20514     
20515     window.finesse = window.finesse || {};
20516     window.finesse.containerservices = window.finesse.containerservices || {};
20517     window.finesse.containerservices.ContainerServices = ContainerServices;
20518 
20519     return ContainerServices;
20520  });
20521 
20522 /**
20523  * FinesseToaster is a utility class to show toaster notification in Finesse.
20524  * FinesseToaster leverages HTML5 Notification API to display Toaster
20525  * Notification.
20526  * 
20527  */
20528 
20529 define('containerservices/FinesseToaster',[],function() {
20530 
20531 	var FinesseToaster = (function() { /** @lends finesse.containerservices.FinesseToaster.prototype */
20532 
20533 		var
20534 
20535 		/** How long the toaster will be displayed by default */
20536 		AUTO_CLOSE_TIME = -1,
20537 
20538 		/** PERMISSION_GRANTED constant for granted string */
20539 		PERMISSION_GRANTED = 'granted',
20540 
20541 		/** PERMISSION_DEFAULT constant for default string */
20542 		PERMISSION_DEFAULT = 'default',
20543 
20544 		/** PERMISSION_DENIED constant for denied string */
20545 		PERMISSION_DENIED = 'denied',
20546 
20547 		/** ICON_PATH constant for holding path icon images */
20548 		ICON_PATH = '/desktop/theme/finesse/images/modules/',
20549 
20550 		/**
20551 		 * Shortcut reference to finesse.cslogger.ClientLogger singleton This
20552 		 * will be set by init(), it should already be initialized by
20553 		 * PageServices
20554 		 *
20555 		 * @private
20556 		 */
20557 		_logger,
20558 
20559 		/**
20560 		 * _createNotification creates Notification instance
20561 		 *
20562 		 * @param {String}
20563 		 *            title title string should be displayed in the Toaster
20564 		 * @param {Object}
20565 		 *            options JSON object for notification options.
20566 		 */
20567 		_createNotification = function(title, options) {
20568 			var notification = new window.Notification(title, options);
20569 			return notification;
20570 		},
20571 
20572 		/**
20573 		 * _setAutoClose set the auto close time for toaster, it checks if
20574 		 * client code passed custom time out for the toaster, otherwise it set
20575 		 * the AUTO_CLOSE_TIME
20576 		 *
20577 		 * @param {Object}
20578 		 *            notification window.Notification that creates Html5
20579 		 *            Notification.
20580 		 * @param {String}
20581 		 *            autoClose autoClose time of the Toaster
20582 		 * @return toasterTimeout Toaster Timeout
20583 		 */
20584 		_setAutoClose = function(notification, autoClose) {
20585 
20586 			// check if custom close time passed other wise set
20587 			// DEFAULT_AUTO_CLOSE
20588 			var autoCloseTime = (autoClose && !isNaN(autoClose)) ? autoClose
20589 					: AUTO_CLOSE_TIME,
20590 			// set the time out for notification toaster
20591 			toasterTimer = setTimeout(function() {
20592 				notification.close();
20593 			}, autoCloseTime);
20594 
20595 			return toasterTimer;
20596 
20597 		},
20598 
20599 		/** This method will request permission to display Toaster. */
20600 		_requestPermission = function() {
20601 			// If they are not denied (i.e. default)
20602 			if (window.Notification
20603 					&& window.Notification.permission !== PERMISSION_DENIED) {
20604 				// Request permission
20605 				window.Notification
20606 						.requestPermission(function(status) {
20607 
20608 							// Change based on user's decision
20609 							if (window.Notification.permission !== status) {
20610 								window.Notification.permission = status;
20611 							}
20612 							_logger
20613 									.log("FinesseToaster.requestPermission(): request permission status "
20614 											+ status);
20615 
20616 						});
20617 
20618 			} else {
20619 				_logger
20620 						.log("FinesseToaster.requestPermission(): Notification not supported or permission denied.");
20621 			}
20622 
20623 		},
20624 
20625 		/**
20626 		 * This method will add onclick and onerror listener to Notification.
20627 		 *
20628 		 * @param {Object}
20629 		 *            notification window.Notification that creates Html5
20630 		 *            Notification.
20631 		 * @param {Object}
20632 		 *            options JSON object for notification options.
20633 		 */
20634 		_addToasterListeners = function(notification, options, toasterTimer) {
20635 			// this is onlcik handler of toaster. this handler will be invoked
20636 			// on click of toaster
20637 			notification.onclick = function() {
20638 				// in case of manually closed toaster, stop the notification
20639 				// auto close method to be invoked
20640 				clearTimeout(toasterTimer);
20641 				// This will maximize/activate chrome browser on click of
20642 				// toaster. this handling required only in case of Chrome
20643 				if (window.chrome) {
20644 					window.focus();
20645 				}
20646 
20647 				if (options && options.onclick) {
20648 					options.onclick();
20649 				}
20650 				
20651 				//close toaster upon click
20652 				this.close();
20653 
20654 			};
20655 
20656 			// this is onerror handler of toaster, if there is any error while
20657 			// loading toaster this hadnler will be invoked
20658 			notification.onerror = function() {
20659 				if (options !== undefined && options.onerror) {
20660 					options.onerror();
20661 				}
20662 			};
20663 		};
20664 
20665 		return {
20666 
20667 			/**
20668              * @class
20669              *  FinesseToaster is a utility class to show toaster
20670 			 *        notification in Finesse. FinesseToaster leverages <a
20671 			 *        href="https://www.w3.org/TR/notifications/">HTML5
20672 			 *        Notification</a> API to display Toaster Notification. 
20673 			 *       <p> <a
20674 			 *        href="https://developer.mozilla.org/en/docs/Web/API/notification#Browser_compatibility">For
20675 			 *        HTML5 Notification API and browser compatibility, please click
20676 			 *        here.</a></p>
20677              * 
20678              * @constructs
20679              */
20680             _fakeConstuctor: function () {
20681                 
20682             },
20683 			/**
20684 			 * TOASTER_DEFAULT_ICONS constants has list of predefined icons (e.g INCOMING_CALL_ICON).
20685 			 * <p><b>Constant list</b></p>
20686 			 * <ul>
20687 			 * <li>TOASTER_DEFAULT_ICONS.INCOMING_CALL_ICON</li>
20688 			 * </ul>
20689 			 */
20690 			TOASTER_DEFAULT_ICONS : {
20691 				INCOMING_CALL_ICON : ICON_PATH + "incoming_call.png"
20692 			},
20693 
20694 			
20695 			/**
20696 			 * <b>showToaster </b>: shows Toaster Notification.
20697 			 *
20698 			 * @param {String}
20699 			 *            <b>title</b> : title string should be displayed in the Toaster
20700 			 * @param {Object}
20701 			 *            options is JSON object for notification options. 
20702 			 *            <ul>
20703 			 *            <li><b>options</b> = { </li>
20704 			 *            <li><b>body</b> : The body string of the notification as
20705 			 *            specified in the options parameter of the constructor.</li>
20706 			 *            <li><b>icon</b>: The URL of the image used as an icon of the
20707 			 *            notification as specified in the options parameter of
20708 			 *            the constructor.</li>
20709 			 *            <li><b>autoClose</b> : custom auto close time of the toaster</li>
20710 			 *            <li><b>showWhenVisible</b> : 'true' toaster shows up even when page is 
20711 			 *            visible,'false' toaster  shows up only when page is invisible </li>
20712 			 *           <li> }</li>
20713 			 *            </ul>
20714 			 *
20715 			 */
20716 			showToaster : function(title, options) {
20717 
20718 				var notification, toasterTimer;
20719 
20720 				// If notifications are granted show the notification
20721 				if (window.Notification
20722 						&& window.Notification.permission === PERMISSION_GRANTED) {
20723 					
20724 					// document.hasFocus() used over document.hidden to keep the consistent behavior across mozilla/chrome
20725 					if(document.hasFocus()===false || options.showWhenVisible) {
20726 						if (_logger && AUTO_CLOSE_TIME > -1) {
20727 							notification = _createNotification(title, options);
20728 
20729 							// set the auto close time out of the toaster
20730 							toasterTimer = _setAutoClose(notification,
20731 									options.autoClose);
20732 
20733 							// and Toaster Event listeners. eg. onclick , onerror.
20734 							_addToasterListeners(notification, options,
20735 									toasterTimer);
20736 						} else {
20737 							_logger
20738 							.log("FinesseToaster is not initialised, call FineeseToaster.init() before showToaster");
20739 						}
20740 					} else {
20741 						_logger
20742 						.log("FinesseToaster supressed : Page is visible and  FineeseToaster.options.showWhenVisible is false");
20743 					}
20744 
20745 				}
20746 
20747 				return notification;
20748 			},
20749 
20750 			/**
20751 			 * initialize FininseToaster and inject dependencies. this method
20752 			 * will also request permission in browser from user to display
20753 			 * Toaster Notification.
20754 			 *
20755 			 * @param {Object}
20756 			 *            finesse.cslogger.ClientLogger
20757 			 * @return finesse.containerservices.FinesseToaster
20758 			 */
20759 			init : function(logger) {
20760 				// This is for injecting mocked logger.
20761 				if (logger) {
20762 					_logger = logger;
20763 				} else {
20764 					_logger = finesse.cslogger.ClientLogger;
20765 				}
20766 
20767 				//set default toaster notification timeout
20768 				if (finesse.container.Config.toasterNotificationDefaultTimeout) {
20769 					AUTO_CLOSE_TIME = finesse.container.Config.toasterNotificationDefaultTimeout;
20770 				} else {
20771 					AUTO_CLOSE_TIME = 0;
20772 					_logger
20773 							.log("Finesse Toaster Notification Default Timeout not configured in desktop.properties");
20774 				}
20775 
20776 				// Request permission
20777 				_requestPermission();
20778 				return finesse.containerservices.FinesseToaster;
20779 			}
20780 		};
20781 
20782 	}());
20783 
20784 	window.finesse = window.finesse || {};
20785 	window.finesse.containerservices = window.finesse.containerservices || {};
20786 	window.finesse.containerservices.FinesseToaster = FinesseToaster;
20787 
20788 	return FinesseToaster;
20789 });
20790 
20791 /**
20792  * This "interface" is just a way to easily jsdoc the Object callback handlers.
20793  *
20794  * @requires finesse.clientservices.ClientServices
20795  * @requires Class
20796  */
20797 /** @private */
20798 define('interfaces/RestObjectHandlers',[
20799     "FinesseBase",
20800      "utilities/Utilities",
20801      "restservices/Notifier",
20802      "clientservices/ClientServices",
20803      "clientservices/Topics"
20804 ],
20805 function () {
20806 
20807     var RestObjectHandlers = ( function () { /** @lends finesse.interfaces.RestObjectHandlers.prototype */
20808         
20809         return {
20810 
20811             /**
20812              * @class
20813              * This "interface" defines REST Object callback handlers, passed as an argument to
20814              * Object getter methods in cases where the Object is going to be created.
20815              * 
20816              * @param {Object} [handlers]
20817              *     An object containing callback handlers for instantiation and runtime
20818              *     Callback to invoke upon successful instantiation, passes in REST object.
20819              * @param {Function} [handlers.onLoad(this)]
20820              *     Callback to invoke upon loading the data for the first time.
20821              * @param {Function} [handlers.onChange(this)]
20822              *     Callback to invoke upon successful update object (PUT)
20823              * @param {Function} [handlers.onAdd(this)]
20824              *     Callback to invoke upon successful update to add object (POST)
20825              * @param {Function} [handlers.onDelete(this)]
20826              *     Callback to invoke upon successful update to delete object (DELETE)
20827              * @param {Function} [handlers.onError(rsp)]
20828              *     Callback to invoke on update error (refresh or event)
20829              *     as passed by finesse.restservices.RestBase.restRequest()<br>
20830              *     {<br>
20831              *         status: {Number} The HTTP status code returned<br>
20832              *         content: {String} Raw string of response<br>
20833              *         object: {Object} Parsed object of response<br>
20834              *         error: {Object} Wrapped exception that was caught<br>
20835              *         error.errorType: {String} Type of error that was caught<br>
20836              *         error.errorMessage: {String} Message associated with error<br>
20837              *     }<br>
20838              *     <br>
20839              * Note that RestCollections have two additional callback handlers:<br>
20840              * <br>
20841              * @param {Function} [handlers.onCollectionAdd(this)]: when an object is added to this collection
20842              * @param {Function} [handlers.onCollectionDelete(this)]: when an object is removed from this collection
20843 
20844              * @constructs
20845              */
20846             _fakeConstuctor: function () {
20847                 /* This is here to enable jsdoc to document this as a class. */
20848             }
20849         };
20850     }());
20851 
20852 window.finesse = window.finesse || {};
20853 window.finesse.interfaces = window.finesse.interfaces || {};
20854 window.finesse.interfaces.RestObjectHandlers = RestObjectHandlers;
20855 
20856 return RestObjectHandlers;
20857 
20858 });
20859 
20860 
20861 /**
20862  * This "interface" is just a way to easily jsdoc the REST request handlers.
20863  *
20864  * @requires finesse.clientservices.ClientServices
20865  * @requires Class
20866  */
20867 /** @private */
20868 define('interfaces/RequestHandlers',[
20869     "FinesseBase",
20870      "utilities/Utilities",
20871      "restservices/Notifier",
20872      "clientservices/ClientServices",
20873      "clientservices/Topics"
20874 ],
20875 function () {
20876 
20877     var RequestHandlers = ( function () { /** @lends finesse.interfaces.RequestHandlers.prototype */
20878         
20879         return {
20880 
20881             /**
20882              * @class
20883              * This "interface" defines REST Object callback handlers, passed as an argument to
20884              * Object getter methods in cases where the Object is going to be created.
20885              * 
20886              * @param {Object} handlers
20887              *     An object containing the following (optional) handlers for the request:<ul>
20888              *         <li><b>success(rsp):</b> A callback function for a successful request to be invoked with the following
20889              *         response object as its only parameter:<ul>
20890              *             <li><b>status:</b> {Number} The HTTP status code returned</li>
20891              *             <li><b>content:</b> {String} Raw string of response</li>
20892              *             <li><b>object:</b> {Object} Parsed object of response</li></ul>
20893              *         <li><b>error(rsp):</b> An error callback function for an unsuccessful request to be invoked with the
20894              *         error response object as its only parameter:<ul>
20895              *             <li><b>status:</b> {Number} The HTTP status code returned</li>
20896              *             <li><b>content:</b> {String} Raw string of response</li>
20897              *             <li><b>object:</b> {Object} Parsed object of response (HTTP errors)</li>
20898              *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
20899              *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
20900              *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
20901              *             </ul></li>
20902              *         </ul>
20903 
20904              * @constructs 
20905              */
20906             _fakeConstuctor: function () {
20907                 /* This is here to enable jsdoc to document this as a class. */
20908             }
20909         };
20910     }());
20911 
20912 window.finesse = window.finesse || {};
20913 window.finesse.interfaces = window.finesse.interfaces || {};
20914 window.finesse.interfaces.RequestHandlers = RequestHandlers;
20915 
20916 finesse = finesse || {};
20917 /** @namespace These interfaces are just a convenience for documenting common parameter structures. */
20918 finesse.interfaces = finesse.interfaces || {};
20919 
20920 return RequestHandlers;
20921 
20922 });
20923 
20924 
20925 
20926 define('gadget/Config',[
20927 	"utilities/Utilities"
20928 ], function (Utilities) {  
20929 	var Config = (function () { /** @lends finesse.gadget.Config.prototype */
20930 		
20931 		if (gadgets && gadgets.Prefs) {
20932 		
20933 			var _prefs = new gadgets.Prefs();
20934 		
20935 			return {
20936 				/**
20937 				 * The base64 encoded "id:password" string used for authentication.
20938 				 */
20939 				authorization: Utilities.getUserAuthString(),
20940 				
20941 				/**
20942 				 * The  auth token string used for authentication in SSO deployments.
20943 				 */
20944 				authToken: Utilities.getToken(),
20945 				
20946 				/**
20947 				 * The country code of the client (derived from locale).
20948 				 */
20949 				country: _prefs.getString("country"),
20950 				
20951 				/**
20952 				 * The language code of the client (derived from locale).
20953 				 */
20954 				language: _prefs.getString("language"),
20955 				
20956 				/**
20957 				 * The locale of the client.
20958 				 */
20959 				locale: _prefs.getString("locale"),
20960 				
20961 				/**
20962 				 * The Finesse server IP/host as reachable from the browser.
20963 				 */
20964 				host: _prefs.getString("host"),
20965 				
20966 				/**
20967 				 * The Finesse server host's port reachable from the browser.
20968 				 */
20969 				hostPort: _prefs.getString("hostPort"),
20970 				
20971 				/**
20972 				 * The extension of the user.
20973 				 */
20974 				extension: _prefs.getString("extension"),
20975 				
20976 				/**
20977 				 * One of the work modes found in {@link finesse.restservices.User.WorkMode}, or something false (undefined) for a normal login.
20978 				 */
20979 				mobileAgentMode: _prefs.getString("mobileAgentMode"),
20980 				
20981 				/**
20982 				 * The dial number to use for mobile agent, or something false (undefined) for a normal login.
20983 				 */
20984 				mobileAgentDialNumber: _prefs.getString("mobileAgentDialNumber"),
20985 				
20986 				/**
20987 				 * The domain of the XMPP server.
20988 				 */
20989 				xmppDomain: _prefs.getString("xmppDomain"),
20990 				
20991 				/**
20992 				 * The pub sub domain where the pub sub service is running.
20993 				 */
20994 				pubsubDomain: _prefs.getString("pubsubDomain"),
20995 				
20996 				/**
20997 				 * The Finesse API IP/host as reachable from the gadget container.
20998 				 */
20999 				restHost: _prefs.getString("restHost"),
21000 				
21001 				/**
21002 				 * The type of HTTP protocol (http or https).
21003 				 */
21004 				scheme: _prefs.getString("scheme"),
21005 				
21006 				/**
21007 				 * The localhost fully qualified domain name.
21008 				 */
21009 				localhostFQDN: _prefs.getString("localhostFQDN"),
21010 				
21011 				/**
21012 				 * The localhost port.
21013 				 */
21014 				localhostPort: _prefs.getString("localhostPort"),
21015 				
21016 				/**
21017 				 * The id of the team the user belongs to.
21018 				 */
21019 				teamId: _prefs.getString("teamId"),
21020 				
21021 				/**
21022 				 * The name of the team the user belongs to.
21023 				 */
21024 				teamName: _prefs.getString("teamName"),
21025 				
21026 				/**
21027 				 * The drift time between the client and the server in milliseconds.
21028 				 */
21029 				clientDriftInMillis: _prefs.getInt("clientDriftInMillis"),
21030 				
21031 				/**
21032 				 * The client compatibility mode configuration (true if it is or false otherwise).
21033 				 */
21034 				compatibilityMode: _prefs.getString("compatibilityMode"),
21035 				
21036 				/**
21037 				 * The peripheral Id that Finesse is connected to.
21038 				 */
21039 				peripheralId: _prefs.getString("peripheralId"),
21040 				
21041 				/**
21042 				 * The auth mode of the finesse deployment.
21043 				 */
21044 				systemAuthMode: _prefs.getString("systemAuthMode"),
21045 				
21046 				/**
21047 				* @class
21048 				* The Config object for gadgets within the Finesse desktop container which
21049 				* contains configuration data provided by the container page.
21050 				* @constructs
21051 				*/
21052 				_fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
21053 				
21054 			};
21055 		} else {
21056 			return {};
21057 		}
21058 	}());
21059 	
21060 	/** Assign to container and gadget namespace to have config available in both  */
21061 	window.finesse = window.finesse || {};
21062 	window.finesse.container = window.finesse.container || {};
21063 	window.finesse.container.Config = window.finesse.container.Config || Config;
21064 
21065 	window.finesse.gadget = window.finesse.gadget || {};
21066 	window.finesse.gadget.Config = Config;
21067 
21068 	return Config;
21069 });
21070 define('finesse',[
21071     'restservices/Users',
21072     'restservices/Teams',
21073     'restservices/SystemInfo',
21074     'restservices/Media',
21075     'restservices/MediaDialogs',
21076     'restservices/DialogLogoutActions',
21077     'restservices/InterruptActions',
21078     'utilities/I18n',
21079     'utilities/Logger',
21080     'utilities/SaxParser',
21081     'utilities/BackSpaceHandler',
21082     'cslogger/ClientLogger',
21083     'cslogger/FinesseLogger',
21084     'containerservices/ContainerServices',
21085     'containerservices/FinesseToaster',
21086     'interfaces/RestObjectHandlers',
21087     'interfaces/RequestHandlers',
21088     'gadget/Config'
21089 ],
21090 function () {
21091     return window.finesse;
21092 });
21093 
21094 require(["finesse"]);
21095 return require('finesse'); }));
21096 
21097 // Prevent other JS files from wiping out window.finesse from the namespace
21098 var finesse = window.finesse;