1 /**
  2  * Cisco Finesse - JavaScript Library
  3  * Version 10.6(1)
  4  * Cisco Systems, Inc.
  5  * http://www.cisco.com/
  6  *
  7  * Portions created or assigned to Cisco Systems, Inc. are
  8  * Copyright (c) 2017 Cisco Systems, Inc. or its affiliated entities.  All Rights Reserved.
  9  */
 10 /**
 11  * This JavaScript library is made available to Cisco partners and customers as
 12  * a convenience to help minimize the cost of Cisco Finesse customizations.
 13  * This library can be used in Cisco Finesse deployments.  Cisco does not
 14  * permit the use of this library in customer deployments that do not include
 15  * Cisco Finesse.  Support for the JavaScript library is provided on a
 16  * "best effort" basis via CDN.  Like any custom deployment, it is the
 17  * responsibility of the partner and/or customer to ensure that the
 18  * customization works correctly and this includes ensuring that the Cisco
 19  * Finesse JavaScript is properly integrated into 3rd party applications.
 20  * Cisco reserves the right to make changes to the JavaScript code and
 21  * corresponding API as part of the normal Cisco Finesse release cycle.  The
 22  * implication of this is that new versions of the JavaScript might be
 23  * incompatible with applications built on older Finesse integrations.  That
 24  * said, it is Cisco's intention to ensure JavaScript compatibility across
 25  * versions as much as possible and Cisco will make every effort to clearly
 26  * document any differences in the JavaScript across versions in the event
 27  * that a backwards compatibility impacting change is made.
 28  */
 29 (function (root, factory) {
 30 	if (typeof define === 'function' && define.amd) {
 31 		define('finesse', [], factory);
 32 	} else {
 33 		root.finesse = factory();
 34 	}
 35 }(this, function () {
 36 /**
 37  * @license almond 0.2.9 Copyright (c) 2011-2014, The Dojo Foundation All Rights Reserved.
 38  * Available via the MIT or new BSD license.
 39  * see: http://github.com/jrburke/almond for details
 40  */
 41 //Going sloppy to avoid 'use strict' string cost, but strict practices should
 42 //be followed.
 43 /*jslint sloppy: true */
 44 /*global setTimeout: false */
 45 
 46 var requirejs, require, define;
 47 (function (undef) {
 48     var main, req, makeMap, handlers,
 49         defined = {},
 50         waiting = {},
 51         config = {},
 52         defining = {},
 53         hasOwn = Object.prototype.hasOwnProperty,
 54         aps = [].slice,
 55         jsSuffixRegExp = /\.js$/;
 56 
 57     function hasProp(obj, prop) {
 58         return hasOwn.call(obj, prop);
 59     }
 60 
 61     /**
 62      * Given a relative module name, like ./something, normalize it to
 63      * a real name that can be mapped to a path.
 64      * @param {String} name the relative name
 65      * @param {String} baseName a real name that the name arg is relative
 66      * to.
 67      * @returns {String} normalized name
 68      */
 69     function normalize(name, baseName) {
 70         var nameParts, nameSegment, mapValue, foundMap, lastIndex,
 71             foundI, foundStarMap, starI, i, j, part,
 72             baseParts = baseName && baseName.split("/"),
 73             map = config.map,
 74             starMap = (map && map['*']) || {};
 75 
 76         //Adjust any relative paths.
 77         if (name && name.charAt(0) === ".") {
 78             //If have a base name, try to normalize against it,
 79             //otherwise, assume it is a top-level require that will
 80             //be relative to baseUrl in the end.
 81             if (baseName) {
 82                 //Convert baseName to array, and lop off the last part,
 83                 //so that . matches that "directory" and not name of the baseName's
 84                 //module. For instance, baseName of "one/two/three", maps to
 85                 //"one/two/three.js", but we want the directory, "one/two" for
 86                 //this normalization.
 87                 baseParts = baseParts.slice(0, baseParts.length - 1);
 88                 name = name.split('/');
 89                 lastIndex = name.length - 1;
 90 
 91                 // Node .js allowance:
 92                 if (config.nodeIdCompat && jsSuffixRegExp.test(name[lastIndex])) {
 93                     name[lastIndex] = name[lastIndex].replace(jsSuffixRegExp, '');
 94                 }
 95 
 96                 name = baseParts.concat(name);
 97 
 98                 //start trimDots
 99                 for (i = 0; i < name.length; i += 1) {
100                     part = name[i];
101                     if (part === ".") {
102                         name.splice(i, 1);
103                         i -= 1;
104                     } else if (part === "..") {
105                         if (i === 1 && (name[2] === '..' || name[0] === '..')) {
106                             //End of the line. Keep at least one non-dot
107                             //path segment at the front so it can be mapped
108                             //correctly to disk. Otherwise, there is likely
109                             //no path mapping for a path starting with '..'.
110                             //This can still fail, but catches the most reasonable
111                             //uses of ..
112                             break;
113                         } else if (i > 0) {
114                             name.splice(i - 1, 2);
115                             i -= 2;
116                         }
117                     }
118                 }
119                 //end trimDots
120 
121                 name = name.join("/");
122             } else if (name.indexOf('./') === 0) {
123                 // No baseName, so this is ID is resolved relative
124                 // to baseUrl, pull off the leading dot.
125                 name = name.substring(2);
126             }
127         }
128 
129         //Apply map config if available.
130         if ((baseParts || starMap) && map) {
131             nameParts = name.split('/');
132 
133             for (i = nameParts.length; i > 0; i -= 1) {
134                 nameSegment = nameParts.slice(0, i).join("/");
135 
136                 if (baseParts) {
137                     //Find the longest baseName segment match in the config.
138                     //So, do joins on the biggest to smallest lengths of baseParts.
139                     for (j = baseParts.length; j > 0; j -= 1) {
140                         mapValue = map[baseParts.slice(0, j).join('/')];
141 
142                         //baseName segment has  config, find if it has one for
143                         //this name.
144                         if (mapValue) {
145                             mapValue = mapValue[nameSegment];
146                             if (mapValue) {
147                                 //Match, update name to the new value.
148                                 foundMap = mapValue;
149                                 foundI = i;
150                                 break;
151                             }
152                         }
153                     }
154                 }
155 
156                 if (foundMap) {
157                     break;
158                 }
159 
160                 //Check for a star map match, but just hold on to it,
161                 //if there is a shorter segment match later in a matching
162                 //config, then favor over this star map.
163                 if (!foundStarMap && starMap && starMap[nameSegment]) {
164                     foundStarMap = starMap[nameSegment];
165                     starI = i;
166                 }
167             }
168 
169             if (!foundMap && foundStarMap) {
170                 foundMap = foundStarMap;
171                 foundI = starI;
172             }
173 
174             if (foundMap) {
175                 nameParts.splice(0, foundI, foundMap);
176                 name = nameParts.join('/');
177             }
178         }
179 
180         return name;
181     }
182 
183     function makeRequire(relName, forceSync) {
184         return function () {
185             //A version of a require function that passes a moduleName
186             //value for items that may need to
187             //look up paths relative to the moduleName
188             return req.apply(undef, aps.call(arguments, 0).concat([relName, forceSync]));
189         };
190     }
191 
192     function makeNormalize(relName) {
193         return function (name) {
194             return normalize(name, relName);
195         };
196     }
197 
198     function makeLoad(depName) {
199         return function (value) {
200             defined[depName] = value;
201         };
202     }
203 
204     function callDep(name) {
205         if (hasProp(waiting, name)) {
206             var args = waiting[name];
207             delete waiting[name];
208             defining[name] = true;
209             main.apply(undef, args);
210         }
211 
212         if (!hasProp(defined, name) && !hasProp(defining, name)) {
213             throw new Error('No ' + name);
214         }
215         return defined[name];
216     }
217 
218     //Turns a plugin!resource to [plugin, resource]
219     //with the plugin being undefined if the name
220     //did not have a plugin prefix.
221     function splitPrefix(name) {
222         var prefix,
223             index = name ? name.indexOf('!') : -1;
224         if (index > -1) {
225             prefix = name.substring(0, index);
226             name = name.substring(index + 1, name.length);
227         }
228         return [prefix, name];
229     }
230 
231     /**
232      * Makes a name map, normalizing the name, and using a plugin
233      * for normalization if necessary. Grabs a ref to plugin
234      * too, as an optimization.
235      */
236     makeMap = function (name, relName) {
237         var plugin,
238             parts = splitPrefix(name),
239             prefix = parts[0];
240 
241         name = parts[1];
242 
243         if (prefix) {
244             prefix = normalize(prefix, relName);
245             plugin = callDep(prefix);
246         }
247 
248         //Normalize according
249         if (prefix) {
250             if (plugin && plugin.normalize) {
251                 name = plugin.normalize(name, makeNormalize(relName));
252             } else {
253                 name = normalize(name, relName);
254             }
255         } else {
256             name = normalize(name, relName);
257             parts = splitPrefix(name);
258             prefix = parts[0];
259             name = parts[1];
260             if (prefix) {
261                 plugin = callDep(prefix);
262             }
263         }
264 
265         //Using ridiculous property names for space reasons
266         return {
267             f: prefix ? prefix + '!' + name : name, //fullName
268             n: name,
269             pr: prefix,
270             p: plugin
271         };
272     };
273 
274     function makeConfig(name) {
275         return function () {
276             return (config && config.config && config.config[name]) || {};
277         };
278     }
279 
280     handlers = {
281         require: function (name) {
282             return makeRequire(name);
283         },
284         exports: function (name) {
285             var e = defined[name];
286             if (typeof e !== 'undefined') {
287                 return e;
288             } else {
289                 return (defined[name] = {});
290             }
291         },
292         module: function (name) {
293             return {
294                 id: name,
295                 uri: '',
296                 exports: defined[name],
297                 config: makeConfig(name)
298             };
299         }
300     };
301 
302     main = function (name, deps, callback, relName) {
303         var cjsModule, depName, ret, map, i,
304             args = [],
305             callbackType = typeof callback,
306             usingExports;
307 
308         //Use name if no relName
309         relName = relName || name;
310 
311         //Call the callback to define the module, if necessary.
312         if (callbackType === 'undefined' || callbackType === 'function') {
313             //Pull out the defined dependencies and pass the ordered
314             //values to the callback.
315             //Default to [require, exports, module] if no deps
316             deps = !deps.length && callback.length ? ['require', 'exports', 'module'] : deps;
317             for (i = 0; i < deps.length; i += 1) {
318                 map = makeMap(deps[i], relName);
319                 depName = map.f;
320 
321                 //Fast path CommonJS standard dependencies.
322                 if (depName === "require") {
323                     args[i] = handlers.require(name);
324                 } else if (depName === "exports") {
325                     //CommonJS module spec 1.1
326                     args[i] = handlers.exports(name);
327                     usingExports = true;
328                 } else if (depName === "module") {
329                     //CommonJS module spec 1.1
330                     cjsModule = args[i] = handlers.module(name);
331                 } else if (hasProp(defined, depName) ||
332                            hasProp(waiting, depName) ||
333                            hasProp(defining, depName)) {
334                     args[i] = callDep(depName);
335                 } else if (map.p) {
336                     map.p.load(map.n, makeRequire(relName, true), makeLoad(depName), {});
337                     args[i] = defined[depName];
338                 } else {
339                     throw new Error(name + ' missing ' + depName);
340                 }
341             }
342 
343             ret = callback ? callback.apply(defined[name], args) : undefined;
344 
345             if (name) {
346                 //If setting exports via "module" is in play,
347                 //favor that over return value and exports. After that,
348                 //favor a non-undefined return value over exports use.
349                 if (cjsModule && cjsModule.exports !== undef &&
350                         cjsModule.exports !== defined[name]) {
351                     defined[name] = cjsModule.exports;
352                 } else if (ret !== undef || !usingExports) {
353                     //Use the return value from the function.
354                     defined[name] = ret;
355                 }
356             }
357         } else if (name) {
358             //May just be an object definition for the module. Only
359             //worry about defining if have a module name.
360             defined[name] = callback;
361         }
362     };
363 
364     requirejs = require = req = function (deps, callback, relName, forceSync, alt) {
365         if (typeof deps === "string") {
366             if (handlers[deps]) {
367                 //callback in this case is really relName
368                 return handlers[deps](callback);
369             }
370             //Just return the module wanted. In this scenario, the
371             //deps arg is the module name, and second arg (if passed)
372             //is just the relName.
373             //Normalize module name, if it contains . or ..
374             return callDep(makeMap(deps, callback).f);
375         } else if (!deps.splice) {
376             //deps is a config object, not an array.
377             config = deps;
378             if (config.deps) {
379                 req(config.deps, config.callback);
380             }
381             if (!callback) {
382                 return;
383             }
384 
385             if (callback.splice) {
386                 //callback is an array, which means it is a dependency list.
387                 //Adjust args if there are dependencies
388                 deps = callback;
389                 callback = relName;
390                 relName = null;
391             } else {
392                 deps = undef;
393             }
394         }
395 
396         //Support require(['a'])
397         callback = callback || function () {};
398 
399         //If relName is a function, it is an errback handler,
400         //so remove it.
401         if (typeof relName === 'function') {
402             relName = forceSync;
403             forceSync = alt;
404         }
405 
406         //Simulate async callback;
407         if (forceSync) {
408             main(undef, deps, callback, relName);
409         } else {
410             //Using a non-zero value because of concern for what old browsers
411             //do, and latest browsers "upgrade" to 4 if lower value is used:
412             //http://www.whatwg.org/specs/web-apps/current-work/multipage/timers.html#dom-windowtimers-settimeout:
413             //If want a value immediately, use require('id') instead -- something
414             //that works in almond on the global level, but not guaranteed and
415             //unlikely to work in other AMD implementations.
416             setTimeout(function () {
417                 main(undef, deps, callback, relName);
418             }, 4);
419         }
420 
421         return req;
422     };
423 
424     /**
425      * Just drops the config on the floor, but returns req in case
426      * the config return value is used.
427      */
428     req.config = function (cfg) {
429         return req(cfg);
430     };
431 
432     /**
433      * Expose module registry for debugging and tooling
434      */
435     requirejs._defined = defined;
436 
437     define = function (name, deps, callback) {
438 
439         //This module may not have dependencies
440         if (!deps.splice) {
441             //deps is not an array, so probably means
442             //an object literal or factory function for
443             //the value. Adjust args.
444             callback = deps;
445             deps = [];
446         }
447 
448         if (!hasProp(defined, name) && !hasProp(waiting, name)) {
449             waiting[name] = [name, deps, callback];
450         }
451     };
452 
453     define.amd = {
454         jQuery: true
455     };
456 }());
457 define("../thirdparty/almond", function(){});
458 
459 /* Simple JavaScript Inheritance
460  * By John Resig http://ejohn.org/
461  * MIT Licensed.
462  */
463 // Inspired by base2 and Prototype
464 define('../thirdparty/Class',[], function () {
465         var initializing = false, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;
466         // The base Class implementation (does nothing)
467         /** @private */
468         Class = function(){};
469         
470         // Create a new Class that inherits from this class
471         /** @private */
472         Class.extend = function(prop) {
473           var _super = this.prototype;
474           
475           // Instantiate a base class (but only create the instance,
476           // don't run the init constructor)
477           initializing = true;
478           var prototype = new this();
479           initializing = false;
480           
481           // Copy the properties over onto the new prototype
482           for (var name in prop) {
483             // Check if we're overwriting an existing function
484             prototype[name] = typeof prop[name] == "function" && 
485               typeof _super[name] == "function" && fnTest.test(prop[name]) ?
486               (function(name, fn){
487                 return function() {
488                   var tmp = this._super;
489                   
490                   // Add a new ._super() method that is the same method
491                   // but on the super-class
492                   this._super = _super[name];
493                   
494                   // The method only need to be bound temporarily, so we
495                   // remove it when we're done executing
496                   var ret = fn.apply(this, arguments);        
497                   this._super = tmp;
498                   
499                   return ret;
500                 };
501               })(name, prop[name]) :
502               prop[name];
503           }
504           
505           // The dummy class constructor
506           /** @private */
507           function Class() {
508             // All construction is actually done in the init method
509             if ( !initializing && this.init )
510               this.init.apply(this, arguments);
511           }
512           
513           // Populate our constructed prototype object
514           Class.prototype = prototype;
515           
516           // Enforce the constructor to be what we expect
517           Class.prototype.constructor = Class;
518 
519           // And make this class extendable
520           Class.extend = arguments.callee;
521           
522           return Class;
523         };
524     return Class;
525 });
526 
527 /**
528  * JavaScript base object that all finesse objects should inherit
529  * from because it encapsulates and provides the common functionality.
530  *
531  * Note: This javascript class requires the "inhert.js" to be included
532  * (which simplifies the class inheritance).
533  *
534  *
535  * @requires finesse.utilities.Logger
536  */
537 
538 /** The following comment is to prevent jslint errors about 
539  * using variables before they are defined.
540  */
541 /*global Class */
542 define('FinesseBase', ["../thirdparty/Class"], function (Class) {
543     var FinesseBase = Class.extend({
544         init: function () {
545         }
546     }); 
547     
548     window.finesse = window.finesse || {};
549     window.finesse.FinesseBase = FinesseBase;
550     
551     return FinesseBase;
552 });
553 
554 /**
555  * A collection of conversion utilities.
556  * Last modified 07-06-2011, Cisco Systems
557  *
558  */
559 /** @private */
560 define('utilities/../../thirdparty/util/converter',[], function () {
561     /**
562      * @class
563      * Contains a collection of utility functions.
564      * @private
565      */
566     Converter = (function () {
567         return {
568             /*  This work is licensed under Creative Commons GNU LGPL License.
569 
570                 License: http://creativecommons.org/licenses/LGPL/2.1/
571                Version: 0.9
572                 Author:  Stefan Goessner/2006
573                 Web:     http://goessner.net/ 
574 
575                 2013-09-16 Modified to remove use of XmlNode.innerHTML in the innerXml function by Cisco Systems, Inc.
576             */
577             xml2json: function (xml, tab) {
578                 var X = {
579                     toObj: function (xml) {
580                         var o = {};
581                         if (xml.nodeType === 1) {
582                             // element node ..
583                             if (xml.attributes.length)
584                             // element with attributes  ..
585                             for (var i = 0; i < xml.attributes.length; i++)
586                             o["@" + xml.attributes[i].nodeName] = (xml.attributes[i].nodeValue || "").toString();
587                             if (xml.firstChild) {
588                                 // element has child nodes ..
589                                 var textChild = 0,
590                                 cdataChild = 0,
591                                 hasElementChild = false;
592                                 for (var n = xml.firstChild; n; n = n.nextSibling) {
593                                     if (n.nodeType == 1) hasElementChild = true;
594                                     else if (n.nodeType == 3 && n.nodeValue.match(/[^ \f\n\r\t\v]/)) textChild++;
595                                     // non-whitespace text
596                                     else if (n.nodeType == 4) cdataChild++;
597                                     // cdata section node
598                                 }
599                                 if (hasElementChild) {
600                                     if (textChild < 2 && cdataChild < 2) {
601                                         // structured element with evtl. a single text or/and cdata node ..
602                                         X.removeWhite(xml);
603                                         for (var n = xml.firstChild; n; n = n.nextSibling) {
604                                             if (n.nodeType == 3)
605                                             // text node
606                                             o["#text"] = X.escape(n.nodeValue);
607                                             else if (n.nodeType == 4)
608                                             // cdata node
609                                             o["#cdata"] = X.escape(n.nodeValue);
610                                             else if (o[n.nodeName]) {
611                                                 // multiple occurence of element ..
612                                                 if (o[n.nodeName] instanceof Array)
613                                                 o[n.nodeName][o[n.nodeName].length] = X.toObj(n);
614                                                 else
615                                                 o[n.nodeName] = [o[n.nodeName], X.toObj(n)];
616                                             }
617                                             else
618                                             // first occurence of element..
619                                             o[n.nodeName] = X.toObj(n);
620                                         }
621                                     }
622                                     else {
623                                         // mixed content
624                                         if (!xml.attributes.length)
625                                         o = X.escape(X.innerXml(xml));
626                                         else
627                                         o["#text"] = X.escape(X.innerXml(xml));
628                                     }
629                                 }
630                                 else if (textChild) {
631                                     // pure text
632                                     if (!xml.attributes.length)
633                                     o = X.escape(X.innerXml(xml));
634                                     else
635                                     o["#text"] = X.escape(X.innerXml(xml));
636                                 }
637                                 else if (cdataChild) {
638                                     // cdata
639                                     if (cdataChild > 1)
640                                     o = X.escape(X.innerXml(xml));
641                                     else
642                                     for (var n = xml.firstChild; n; n = n.nextSibling)
643                                     o["#cdata"] = X.escape(n.nodeValue);
644                                 }
645                             }
646                             if (!xml.attributes.length && !xml.firstChild) o = null;
647                         }
648                         else if (xml.nodeType == 9) {
649                             // document.node
650                             o = X.toObj(xml.documentElement);
651                         }
652                         else
653                             throw ("unhandled node type: " + xml.nodeType);
654                         return o;
655                     },
656                     toJson: function(o, name, ind) {
657                         var json = name ? ("\"" + name + "\"") : "";
658                         if (o instanceof Array) {
659                             for (var i = 0, n = o.length; i < n; i++)
660                             o[i] = X.toJson(o[i], "", ind + "\t");
661                             json += (name ? ":[": "[") + (o.length > 1 ? ("\n" + ind + "\t" + o.join(",\n" + ind + "\t") + "\n" + ind) : o.join("")) + "]";
662                         }
663                         else if (o == null)
664                         json += (name && ":") + "null";
665                         else if (typeof(o) == "object") {
666                             var arr = [];
667                             for (var m in o)
668                             arr[arr.length] = X.toJson(o[m], m, ind + "\t");
669                             json += (name ? ":{": "{") + (arr.length > 1 ? ("\n" + ind + "\t" + arr.join(",\n" + ind + "\t") + "\n" + ind) : arr.join("")) + "}";
670                         }
671                         else if (typeof(o) == "string")
672                         json += (name && ":") + "\"" + o.toString() + "\"";
673                         else
674                         json += (name && ":") + o.toString();
675                         return json;
676                     },
677                     innerXml: function(node) {
678                         var s = "";
679                         var asXml = function(n) {
680                             var s = "";
681                             if (n.nodeType == 1) {
682                                 s += "<" + n.nodeName;
683                                 for (var i = 0; i < n.attributes.length; i++)
684                                 s += " " + n.attributes[i].nodeName + "=\"" + (n.attributes[i].nodeValue || "").toString() + "\"";
685                                 if (n.firstChild) {
686                                     s += ">";
687                                     for (var c = n.firstChild; c; c = c.nextSibling)
688                                     s += asXml(c);
689                                     s += "</" + n.nodeName + ">";
690                                 }
691                                 else
692                                 s += "/>";
693                             }
694                             else if (n.nodeType == 3)
695                             s += n.nodeValue;
696                             else if (n.nodeType == 4)
697                             s += "<![CDATA[" + n.nodeValue + "]]>";
698                             return s;
699                         };
700                         for (var c = node.firstChild; c; c = c.nextSibling)
701                         s += asXml(c);
702                         return s;
703                     },
704                     escape: function(txt) {
705                         return txt.replace(/[\\]/g, "\\\\")
706                         .replace(/[\"]/g, '\\"')
707                         .replace(/[\n]/g, '\\n')
708                         .replace(/[\r]/g, '\\r');
709                     },
710                     removeWhite: function(e) {
711                         e.normalize();
712                         for (var n = e.firstChild; n;) {
713                             if (n.nodeType == 3) {
714                                 // text node
715                                 if (!n.nodeValue.match(/[^ \f\n\r\t\v]/)) {
716                                     // pure whitespace text node
717                                     var nxt = n.nextSibling;
718                                     e.removeChild(n);
719                                     n = nxt;
720                                 }
721                                 else
722                                 n = n.nextSibling;
723                             }
724                             else if (n.nodeType == 1) {
725                                 // element node
726                                 X.removeWhite(n);
727                                 n = n.nextSibling;
728                             }
729                             else
730                             // any other node
731                             n = n.nextSibling;
732                         }
733                         return e;
734                     }
735                 };
736                 if (xml.nodeType == 9)
737                 // document node
738                 xml = xml.documentElement;
739                 var json = X.toJson(X.toObj(X.removeWhite(xml)), xml.nodeName, "\t");
740                 return "{\n" + tab + (tab ? json.replace(/\t/g, tab) : json.replace(/\t|\n/g, "")) + "\n}";
741             },
742             
743             /*  This work is licensed under Creative Commons GNU LGPL License.
744 
745                 License: http://creativecommons.org/licenses/LGPL/2.1/
746                Version: 0.9
747                 Author:  Stefan Goessner/2006
748                 Web:     http://goessner.net/ 
749             */
750             json2xml: function(o, tab) {
751                 var toXml = function(v, name, ind) {
752                     var xml = "";
753                     if (v instanceof Array) {
754                         for (var i = 0, n = v.length; i < n; i++)
755                         xml += ind + toXml(v[i], name, ind + "\t") + "\n";
756                     }
757                     else if (typeof(v) == "object") {
758                         var hasChild = false;
759                         xml += ind + "<" + name;
760                         for (var m in v) {
761                             if (m.charAt(0) == "@")
762                             xml += " " + m.substr(1) + "=\"" + v[m].toString() + "\"";
763                             else
764                             hasChild = true;
765                         }
766                         xml += hasChild ? ">": "/>";
767                         if (hasChild) {
768                             for (var m in v) {
769                                 if (m == "#text")
770                                 xml += v[m];
771                                 else if (m == "#cdata")
772                                 xml += "<![CDATA[" + v[m] + "]]>";
773                                 else if (m.charAt(0) != "@")
774                                 xml += toXml(v[m], m, ind + "\t");
775                             }
776                             xml += (xml.charAt(xml.length - 1) == "\n" ? ind: "") + "</" + name + ">";
777                         }
778                     }
779                     else {
780                         xml += ind + "<" + name + ">" + v.toString() + "</" + name + ">";
781                     }
782                     return xml;
783                 },
784                 xml = "";
785                 for (var m in o)
786                 xml += toXml(o[m], m, "");
787                 return tab ? xml.replace(/\t/g, tab) : xml.replace(/\t|\n/g, "");
788             }
789         };
790     })();
791 
792     window.finesse = window.finesse || {};
793     window.finesse.Converter = Converter;
794 
795     return Converter;
796 });
797 
798 /**
799  * SaxParser.js: provides a simple SAX parser
800  *
801  * NONVALIDATING - this will not validate whether you have valid XML or not. It will simply report what it finds.
802  * Only supports elements, attributes, and text. No comments, cdata, processing instructions, etc.
803  */
804 
805 /**
806  * @requires
807  * @ignore
808  */
809 // Add SaxParser to the finesse.utilities namespace
810 define('utilities/SaxParser',[], function () {
811 	var SaxParser = {
812 		parse: function(xml, callback) {
813 			// Event callbacks
814             /** @private */
815 			var triggerEvent = function (type, data) {
816 					callback.call(null, type, data);
817 				},
818                 /** @private */
819 				triggerStartElement = function (name) {
820 					triggerEvent("StartElement", name);
821 				},
822                 /** @private */
823 				triggerEndElement = function (name) {
824 					triggerEvent("EndElement", name);
825 				},
826                 /** @private */
827 				triggerAttribute = function (name, value) {
828 					triggerEvent("Attribute", { "name": name, "value": value });
829 				},
830                 /** @private */
831 				triggerText = function (text) {
832 					triggerEvent("Text", text);
833 				},
834 
835 				// Parsing
836 				cursor = 0,
837 				xmlLength = xml.length,
838 				whitespaceRegex = /^[ \t\r\n]*$/,
839 				/** @private */
840 				isWhitespace = function (text) {
841 					return whitespaceRegex.test(text);
842 				},
843                 /** @private */
844 				moveToNonWhitespace = function () {
845 					while (isWhitespace(xml.charAt(cursor))) {
846 						cursor += 1;
847 					}
848 				},
849                 /** @private */
850 				parseAttribute = function () {
851 					var nameBuffer = [],
852 						valueBuffer = [],
853 						valueIsQuoted = false,
854 						cursorChar = "";
855 
856 					nameBuffer.push(xml.charAt(cursor));
857 
858 					// Get the name
859 					cursor += 1;
860 					while (cursor < xmlLength) {
861 						cursorChar = xml.charAt(cursor);
862 						if (isWhitespace(cursorChar) || cursorChar === "=") {
863 							// Move on to gathering value
864 							break;
865 						}
866 						else {
867 							nameBuffer.push(cursorChar);
868 						}
869 						cursor += 1;
870 					}
871 
872 					// Skip the equals sign and any whitespace
873 					moveToNonWhitespace();
874 					if (cursorChar === "=") {
875 						cursor += 1;
876 					} else {
877 						throw new Error("Did not find = following attribute name at " + cursor);
878 					}
879 					moveToNonWhitespace();
880 
881 					// Get the value
882 					valueIsQuoted = cursor !== xmlLength - 1 ? xml.charAt(cursor) === "\"": false;
883 					if (valueIsQuoted) {
884 						cursor += 1;
885 						while (cursor < xmlLength) {
886 							cursorChar = xml.charAt(cursor);
887 							if (cursorChar === "\"") {
888 								// Found the closing quote, so end value
889 								triggerAttribute(nameBuffer.join(""), valueBuffer.join(""));
890 								break;
891 							}
892 							else {
893 								valueBuffer.push(cursorChar);
894 							}
895 							cursor += 1;
896 						}
897 					}
898 					else {
899 						throw new Error("Found unquoted attribute value at " + cursor);
900 					}
901 				},
902                 /** @private */
903 				parseEndElement = function () {
904 					var elementNameBuffer = [],
905 						cursorChar = "";
906 					cursor += 2;
907 					while (cursor < xmlLength) {
908 						cursorChar = xml.charAt(cursor);
909 						if (cursorChar === ">") {
910 							triggerEndElement(elementNameBuffer.join(""));
911 							break;
912 						}
913 						else {
914 							elementNameBuffer.push(cursorChar);
915 						}
916 						cursor += 1;
917 					}
918 				},
919                 /** @private */
920 				parseReference = function() {
921 					var type,
922 						TYPE_DEC_CHAR_REF = 1,
923 						TYPE_HEX_CHAR_REF = 2,
924 						TYPE_ENTITY_REF = 3,
925 						buffer = "";
926 					cursor += 1;
927 					// Determine the type of reference.
928 					if (xml.charAt(cursor) === "#") {
929 						cursor += 1;
930 						if (xml.charAt(cursor) === "x") {
931 							type = TYPE_HEX_CHAR_REF;
932 							cursor += 1;
933 						} else {
934 							type = TYPE_DEC_CHAR_REF;
935 						}
936 					} else {
937 						type = TYPE_ENTITY_REF;
938 					}
939 					// Read the reference into a buffer.
940 					while (xml.charAt(cursor) !== ";") {
941 						buffer += xml.charAt(cursor);
942 						cursor += 1;
943 						if (cursor >= xmlLength) {
944 							throw new Error("Unterminated XML reference: " + buffer);
945 						}
946 					}
947 					// Convert the reference to the appropriate character.
948 					switch (type) {
949 						case TYPE_DEC_CHAR_REF:
950 							return String.fromCharCode(parseInt(buffer, 10));
951 						case TYPE_HEX_CHAR_REF:
952 							return String.fromCharCode(parseInt(buffer, 16));
953 						case TYPE_ENTITY_REF:
954 							switch (buffer) {
955 								case "amp":
956 									return "&";
957 								case "lt":
958 									return "<";
959 								case "gt":
960 									return ">";
961 								case "apos":
962 									return "'";
963 								case "quot":
964 									return "\"";
965 								default:
966 									throw new Error("Invalid XML entity reference: " + buffer);
967 							}
968 							// break; (currently unreachable)
969 					}
970 				},
971                 /** @private */
972 				parseElement = function () {
973 					var elementNameBuffer = [],
974 						textBuffer = [],
975 						cursorChar = "",
976 						whitespace = false;
977 
978 					// Get element name
979 					cursor += 1;
980 					while (cursor < xmlLength) {
981 						cursorChar = xml.charAt(cursor);
982 						whitespace = isWhitespace(cursorChar);
983 						if (!whitespace && cursorChar !== "/" && cursorChar !== ">") {
984 							elementNameBuffer.push(cursorChar);
985 						}
986 						else {
987 							elementNameBuffer = elementNameBuffer.join("");
988 							triggerStartElement(elementNameBuffer);
989 							break;
990 						}
991 						cursor += 1;
992 					}
993 
994 					// Get attributes
995 					if (whitespace) {
996 						while (cursor < xmlLength) {
997 							moveToNonWhitespace();
998 							cursorChar = xml.charAt(cursor);
999 							if (cursorChar !== "/" && cursorChar !== ">") {
1000 								// Start of attribute
1001 								parseAttribute();
1002 							}
1003 							cursorChar = xml.charAt(cursor);
1004 							if (cursorChar === "/" || cursorChar === ">") {
1005 								break;
1006 							}
1007 							else {
1008 								cursor += 1;
1009 							}
1010 						}
1011 					}
1012 
1013 					// End tag if "/>" was found,
1014 					// otherwise we're at the end of the start tag and have to parse into it
1015 					if (cursorChar === "/") {
1016 						if (cursor !== xmlLength - 1 && xml.charAt(cursor + 1) === ">") {
1017 							cursor += 1;
1018 							triggerEndElement(elementNameBuffer);
1019 						}
1020 					}
1021 					else {
1022 						// cursor is on ">", so parse into element content. Assume text until we find a "<",
1023 						// which could be a child element or the current element's end tag. We do not support
1024 						// mixed content of text and elements as siblings unless the text is only whitespace.
1025 						// Text cannot contain <, >, ", or &. They should be <, >, ", & respectively.
1026 						cursor += 1;
1027 						while (cursor < xmlLength) {
1028 							cursorChar = xml.charAt(cursor);
1029 							if (cursorChar === "<") {
1030 								// Determine if end tag or element
1031 								if (cursor !== xmlLength - 1 && xml.charAt(cursor + 1) === "/") {
1032 									// At end tag
1033 									textBuffer = textBuffer.join("");
1034 									if (!isWhitespace(textBuffer)) {
1035 										triggerText(textBuffer);
1036 									}
1037 									parseEndElement();
1038 									break;
1039 								}
1040 								else {
1041 									// At start tag
1042 									textBuffer = textBuffer.join("");
1043 									if (!isWhitespace(textBuffer)) {
1044 										triggerText(textBuffer);
1045 									}
1046 									parseElement();
1047 									textBuffer = [];
1048 								}
1049 							} else if (cursorChar === "&") {
1050 								textBuffer.push(parseReference());
1051 							}
1052 							else {
1053 								textBuffer.push(cursorChar);
1054 							}
1055 							cursor += 1;
1056 						}
1057 					}
1058 				},
1059                 /** @private */
1060 				skipXmlDeclaration = function() {
1061 					if (xml.substr(0, 5) === "<?xml" && isWhitespace(xml.charAt(5))) {
1062 						cursor = xml.indexOf(">") + 1;
1063 					}
1064 					moveToNonWhitespace();
1065 				};
1066 
1067 			// Launch.
1068 			skipXmlDeclaration();
1069 			parseElement();
1070 		}
1071 	};
1072 
1073     window.finesse = window.finesse || {};
1074     window.finesse.utilities = window.finesse.utilities || {};
1075     window.finesse.utilities.SaxParser = SaxParser;
1076 
1077 	return SaxParser;
1078 });
1079 
1080 /**
1081 * Date.parse with progressive enhancement for ISO 8601 <https://github.com/csnover/js-iso8601>
1082 * ?? 2011 Colin Snover <http://zetafleet.com>
1083 * Released under MIT license.
1084 */
1085 define('iso8601',[], function () {
1086     (function (Date, undefined) {
1087         var origParse = Date.parse, numericKeys = [ 1, 4, 5, 6, 7, 10, 11 ];
1088         /** @private **/
1089         Date.parse = function (date) {
1090             var timestamp, struct, minutesOffset = 0;
1091 
1092             // ES5 ??15.9.4.2 states that the string should attempt to be parsed as a Date Time String Format string
1093             // before falling back to any implementation-specific date parsing, so that???s what we do, even if native
1094             // implementations could be faster
1095             // 1 YYYY 2 MM 3 DD 4 HH 5 mm 6 ss 7 msec 8 Z 9 ?? 10 tzHH 11 tzmm
1096             if ((struct = /^(\d{4}|[+\-]\d{6})(?:-(\d{2})(?:-(\d{2}))?)?(?:T(\d{2}):(\d{2})(?::(\d{2})(?:\.(\d{3}))?)?(?:(Z)|([+\-])(\d{2})(?::(\d{2}))?)?)?$/.exec(date))) {
1097                 // avoid NaN timestamps caused by ???undefined??? values being passed to Date.UTC
1098                 for (var i = 0, k; (k = numericKeys[i]); ++i) {
1099                     struct[k] = +struct[k] || 0;
1100                 }
1101 
1102                 // allow undefined days and months
1103                 struct[2] = (+struct[2] || 1) - 1;
1104                 struct[3] = +struct[3] || 1;
1105 
1106                 if (struct[8] !== 'Z' && struct[9] !== undefined) {
1107                     minutesOffset = struct[10] * 60 + struct[11];
1108 
1109                     if (struct[9] === '+') {
1110                         minutesOffset = 0 - minutesOffset;
1111                     }
1112                 }
1113 
1114                 timestamp = Date.UTC(struct[1], struct[2], struct[3], struct[4], struct[5] + minutesOffset, struct[6], struct[7]);
1115             }
1116             else {
1117                 timestamp = origParse ? origParse(date) : NaN;
1118             }
1119 
1120             return timestamp;
1121         };
1122     }(Date));
1123 });
1124 
1125 /*!
1126 Math.uuid.js (v1.4)
1127 http://www.broofa.com
1128 mailto:robert@broofa.com
1129 
1130 Copyright (c) 2010 Robert Kieffer
1131 Dual licensed under the MIT and GPL licenses.
1132 */
1133 
1134 /*
1135  * Generate a random uuid.
1136  *
1137  * USAGE: Math.uuid(length, radix)
1138  *   length - the desired number of characters
1139  *   radix  - the number of allowable values for each character.
1140  *
1141  * EXAMPLES:
1142  *   // No arguments  - returns RFC4122, version 4 ID
1143  *   >>> Math.uuid()
1144  *   "92329D39-6F5C-4520-ABFC-AAB64544E172"
1145  *
1146  *   // One argument - returns ID of the specified length
1147  *   >>> Math.uuid(15)     // 15 character ID (default base=62)
1148  *   "VcydxgltxrVZSTV"
1149  *
1150  *   // Two arguments - returns ID of the specified length, and radix. (Radix must be <= 62)
1151  *   >>> Math.uuid(8, 2)  // 8 character ID (base=2)
1152  *   "01001010"
1153  *   >>> Math.uuid(8, 10) // 8 character ID (base=10)
1154  *   "47473046"
1155  *   >>> Math.uuid(8, 16) // 8 character ID (base=16)
1156  *   "098F4D35"
1157  */
1158 define('Math.uuid',[], function () {
1159     (function() {
1160         // Private array of chars to use
1161         var CHARS = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('');
1162 
1163         /** @private **/
1164         Math.uuid = function (len, radix) {
1165           var chars = CHARS, uuid = [], i;
1166           radix = radix || chars.length;
1167 
1168           if (len) {
1169             // Compact form
1170             for (i = 0; i < len; i++) uuid[i] = chars[0 | Math.random()*radix];
1171           } else {
1172             // rfc4122, version 4 form
1173             var r;
1174 
1175             // rfc4122 requires these characters
1176             uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-';
1177             uuid[14] = '4';
1178 
1179             // Fill in random data.  At i==19 set the high bits of clock sequence as
1180             // per rfc4122, sec. 4.1.5
1181             for (i = 0; i < 36; i++) {
1182               if (!uuid[i]) {
1183                 r = 0 | Math.random()*16;
1184                 uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r];
1185               }
1186             }
1187           }
1188 
1189           return uuid.join('');
1190         };
1191 
1192         // A more performant, but slightly bulkier, RFC4122v4 solution.  We boost performance
1193         // by minimizing calls to random()
1194         /** @private **/
1195         Math.uuidFast = function() {
1196           var chars = CHARS, uuid = new Array(36), rnd=0, r;
1197           for (var i = 0; i < 36; i++) {
1198             if (i==8 || i==13 ||  i==18 || i==23) {
1199               uuid[i] = '-';
1200             } else if (i==14) {
1201               uuid[i] = '4';
1202             } else {
1203               if (rnd <= 0x02) rnd = 0x2000000 + (Math.random()*0x1000000)|0;
1204               r = rnd & 0xf;
1205               rnd = rnd >> 4;
1206               uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r];
1207             }
1208           }
1209           return uuid.join('');
1210         };
1211 
1212         // A more compact, but less performant, RFC4122v4 solution:
1213         /** @private **/
1214         Math.uuidCompact = function() {
1215           return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
1216             var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
1217             return v.toString(16);
1218           });
1219         };
1220       })();
1221 });
1222 
1223 /**
1224  * The following comment prevents JSLint errors concerning undefined global variables.
1225  * It tells JSLint that these identifiers are defined elsewhere.
1226  */
1227 /*jslint bitwise:true, browser:true, nomen:true, regexp:true, sloppy:true, white:true, plusplus: true, unparam: true, forin: true */
1228 
1229 /** The following comment is to prevent jslint errors about 
1230  * using variables before they are defined.
1231  */
1232 /*global $, _prefs,_uiMsg,ciscowidgets,dojo,finesse,gadgets,hostUrl, Handlebars */
1233 
1234 /**
1235  *  A collection of utility functions.
1236  *
1237  * @requires finesse.Converter
1238  */
1239 define('utilities/Utilities',[
1240     "../../thirdparty/util/converter",
1241     "utilities/SaxParser",
1242     "iso8601",
1243     "Math.uuid"
1244 ],
1245 function (Converter, SaxParser) {
1246     var Utilities = /** @lends finesse.utilities.Utilities */ {
1247 
1248         /**
1249          * @class
1250          * A PhoneBook is a list of Contacts available to a User for quick dial.
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          * Takes a string (typically from window.location) and finds the value which corresponds to a name. For
2490          * example: http://www.company.com/?param1=value1¶m2=value2
2491          *
2492          * @param str is the string to search
2493          * @param name is the name to search for
2494          */
2495         getParameterByName : function(str, name) {
2496             var regex, results;
2497             name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
2498             regex = new RegExp("[\\?&]" + name + "=([^&#]*)");
2499             results = regex.exec(str);
2500             return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
2501         }, 
2502         
2503         /**
2504          * @private
2505          * Gets the user Authorization String from the window session.
2506          * @returns the Authorization String
2507          * 
2508          */
2509         getUserAuthString: function () {
2510             var authString = window.sessionStorage.getItem('userFinesseAuth');
2511             return authString;
2512         },
2513         
2514         /**
2515          * @private
2516          * Adds a new cookie to the page with a default domain.
2517          * @param {String} key
2518          *      the key to assign a value to
2519          * @param {String} value
2520          *      the value to assign to the key
2521          * @param {Number} days
2522          *      number of days (from current) until the cookie should expire
2523          */
2524         addCookie : function (key, value, days) {
2525             var date, expires = "",
2526                 cookie = key + "=" + escape(value);
2527             if (typeof days === "number") {
2528                 date = new Date();
2529                 date.setTime(date.getTime() + (days * 24 * 3600 * 1000));
2530                 cookie += "; expires=" + date.toGMTString();
2531             }
2532             document.cookie = cookie + "; path=/";
2533         },
2534 
2535         /**
2536          * @private
2537          * Get the value of a cookie given a key.
2538          * @param {String} key
2539          *      a key to lookup
2540          * @returns {String}
2541          *      the value mapped to a key, null if key doesn't exist
2542          */
2543         getCookie : function (key) {
2544             var i, pairs, pair;
2545             if (document.cookie) {
2546                 pairs = document.cookie.split(";");
2547                 for (i = 0; i < pairs.length; i += 1) {
2548                     pair = this.trim(pairs[i]).split("=");
2549                     if (pair[0] === key) {
2550                         return unescape(pair[1]);
2551                     }
2552                 }
2553             }
2554             return null;
2555         },
2556 
2557         /**
2558          * @private
2559          * Deletes the cookie mapped to specified key.
2560          * @param {String} key
2561          *      the key to delete
2562          */
2563         deleteCookie : function (key) {
2564             this.addCookie(key, "", -1);
2565         },
2566 
2567         /**
2568          * @private
2569          * Case insensitive sort for use with arrays or Dojox stores
2570          * @param {String} a
2571          *      first value
2572          * @param {String} b
2573          *      second value
2574          */
2575         caseInsensitiveSort: function (a, b) {
2576             var ret = 0, emptyString = "";
2577             a = a + emptyString;
2578             b = b + emptyString;
2579             a = a.toLowerCase();
2580             b = b.toLowerCase();
2581             if (a > b) {
2582                 ret = 1;
2583             }
2584             if (a < b) { 
2585                 ret = -1;
2586             }
2587             return ret;
2588         },
2589 
2590         /**
2591          * @private
2592         * Calls the specified function to render the dojo wijit for a gadget  when the gadget first becomes visible.
2593         *
2594         * The displayWjitFunc function will be called once and only once when the div for our wijit 
2595         * becomes visible for the first time.  This is necessary because some dojo wijits such as the grid
2596         * throw exceptions and do not render properly if they are created in a display:none div.
2597         * If our gadget is visisble the function will be called immediately.
2598         * If our gadget is not yet visisble, then it sets a timer and waits for it to become visible.
2599         * NOTE:  The timer may seem inefficent, originally I tried connecting to the tab onclick handler, but
2600         * there is a problem with dojo.connnect to an iframe's parent node in Internet Explorer. 
2601         * In Firefox the click handler works OK, but it happens before the node is actually visisble, so you
2602         * end up waiting for the node to become visisble anyway.
2603         * @displayWjitFunc:  A function to be called once our gadget has become visisble for th first time.
2604         */  
2605         onGadgetFirstVisible: function (displayWjitFunc) {
2606             var i, q, frameId, gadgetNbr, gadgetTitleId, panelId, panelNode, link, iterval, once = false, active = false, tabId = "#finesse-tab-selector";
2607             try {
2608                 frameId = dojo.attr(window.frameElement, "id"); // Figure out what gadget number we are by looking at our frameset
2609                 gadgetNbr = frameId.match(/\d+$/)[0];  // Strip the number off the end of the frame Id, that's our gadget number
2610                 gadgetTitleId = "#finesse_gadget_" + gadgetNbr + "_title";  // Create a a gadget title id from the number
2611                 
2612                 // Loop through all of the tab panels to find one that has our gadget id
2613                 dojo.query('.tab-panel', window.parent.document).some(function (node, index, arr) {
2614                     q = dojo.query(gadgetTitleId, node);  // Look in this panel for our gadget id
2615                     if (q.length > 0) {  // You found it
2616                         panelNode = node;
2617                         panelId = dojo.attr(panelNode, "id");  // Get panel id  e.g. panel_Workgroups
2618                         active = dojo.hasClass(panelNode, "active");
2619                         tabId = "#tab_" + panelId.slice(6);  // Turn it into a tab id e.g.tab_Workgroups
2620                         return;
2621                     }
2622                 });
2623                 // If panel is already active - execute the function - we're done
2624                 if (active) {
2625                     //?console.log(frameId + " is visible display it");
2626                     setTimeout(displayWjitFunc);
2627                 } 
2628                 // If its not visible - wait for the active class to show up.
2629                 else {
2630                     //?console.log(frameId  + " (" + tabId + ") is NOT active wait for it");
2631                     iterval = setInterval(dojo.hitch(this, function () {
2632                         if (dojo.hasClass(panelNode, "active")) {
2633                             //?console.log(frameId  + " (" + tabId + ") is visible display it");
2634                             clearInterval(iterval);
2635                             setTimeout(displayWjitFunc);
2636                         } 
2637                     }), 250);
2638                 }
2639             } catch (err) {
2640                 //?console.log("Could not figure out what tab " + frameId + " is in: " + err);
2641             }
2642         },
2643 
2644         /**
2645          * @private
2646          * Downloads the specified url using a hidden iframe. In order to cause the browser to download rather than render
2647          * in the hidden iframe, the server code must append the header "Content-Disposition" with a value of 
2648          * "attachment; filename=\"<WhateverFileNameYouWant>\"".
2649          */
2650         downloadFile : function (url) {
2651             var iframe = document.getElementById("download_iframe");
2652 
2653             if (!iframe)
2654             {
2655                 iframe = document.createElement("iframe");
2656                 $(document.body).append(iframe);
2657                 $(iframe).css("display", "none");
2658             }
2659 
2660             iframe.src = url;
2661         },
2662 
2663         /**
2664          * @private
2665          * bitMask has functions for testing whether bit flags specified by integers are set in the supplied value
2666          */
2667         bitMask: {
2668             /** @private */
2669             isSet: function (value, mask) {
2670                 return (value & mask) === mask;
2671             },
2672             /**
2673              * Returns true if all flags in the intArray are set on the specified value
2674              * @private 
2675              */
2676             all: function (value, intArray) {
2677                 var i = intArray.length;
2678                 if (typeof(i) === "undefined")
2679                 {
2680                     intArray = [intArray];
2681                     i = 1;
2682                 }
2683                 while ((i = i - 1) !== -1)
2684                 {
2685                     if (!this.isSet(value, intArray[i]))
2686                     {
2687                         return false;
2688                     }
2689                 }
2690                 return true;
2691             },
2692             /**
2693              * @private
2694              * Returns true if any flags in the intArray are set on the specified value
2695              */
2696             any: function (value, intArray) {
2697                 var i = intArray.length;
2698                 if (typeof(i) === "undefined")
2699                 {
2700                     intArray = [intArray];
2701                     i = 1;
2702                 }
2703                 while ((i = i - 1) !== -1)
2704                 {
2705                     if (this.isSet(value, intArray[i]))
2706                     {
2707                         return true;
2708                     }
2709                 }
2710                 return false;
2711             }
2712         },
2713 
2714         /** @private */
2715         renderDojoGridOffScreen: function (grid) {
2716             var offscreenDiv = $("<div style='position: absolute; left: -5001px; width: 5000px;'></div>")[0];
2717             $(document.body).append(offscreenDiv);
2718             grid.placeAt(offscreenDiv);
2719             grid.startup();
2720             document.body.removeChild(offscreenDiv);
2721             return grid;
2722         },
2723 
2724         /** @private */
2725         initializeSearchInput: function(searchInput, changeCallback, callbackDelay, callbackScope, placeholderText) {
2726             var timerId = null,
2727                 theControl = typeof(searchInput) === "string" ? $("#" + searchInput) : $(searchInput),
2728                 theInputControl = theControl.find("input"),
2729                 theClearButton = theControl.find("a"),
2730                 inputControlWidthWithClear = 204,
2731                 inputControlWidthNoClear = 230,
2732                 sPreviousInput = theInputControl.val(),
2733                 /** @private **/
2734                 toggleClearButton = function(){
2735                     if (theInputControl.val() === "") {
2736                         theClearButton.hide();
2737                         theControl.removeClass("input-append");
2738                         theInputControl.width(inputControlWidthNoClear);
2739                     } else {
2740                         theInputControl.width(inputControlWidthWithClear);
2741                         theClearButton.show();
2742                         theControl.addClass("input-append");
2743                     }
2744                 };
2745 
2746             // set placeholder text
2747             theInputControl.attr('placeholder', placeholderText);
2748 
2749             theInputControl.unbind('keyup').bind('keyup', function() {
2750                 if (sPreviousInput !== theInputControl.val()) {
2751                     window.clearTimeout(timerId);
2752                     sPreviousInput = theInputControl.val();
2753                     timerId = window.setTimeout(function() {
2754                         changeCallback.call((callbackScope || window), theInputControl.val());
2755                         theInputControl[0].focus();
2756                     }, callbackDelay);
2757                 }
2758 
2759                 toggleClearButton();
2760             });
2761 
2762             theClearButton.bind('click', function() {
2763                 theInputControl.val('');
2764                 changeCallback.call((callbackScope || window), '');
2765 
2766                 toggleClearButton();
2767                 theInputControl[0].focus(); // jquery and dojo on the same page break jquery's focus() method
2768             });
2769 
2770             theInputControl.val("");
2771             toggleClearButton();
2772         },
2773 
2774         DataTables: {
2775             /** @private */
2776             createDataTable: function (options, dataTableOptions) {
2777                 var grid,
2778                     table = $('<table cellpadding="0" cellspacing="0" border="0" class="finesse"><thead><tr></tr></thead></table>'),
2779                     headerRow = table.find("tr"),
2780                     defaultOptions = {
2781                         "aaData": [],
2782                         "bPaginate": false,
2783                         "bLengthChange": false,
2784                         "bFilter": false,
2785                         "bInfo": false,
2786                         "sScrollY": "176",
2787                         "oLanguage": {
2788                             "sEmptyTable": "",
2789                             "sZeroRecords": ""
2790                         }
2791                     },
2792                     gridOptions = $.extend({}, defaultOptions, dataTableOptions),
2793                     columnDefs = [],
2794                     columnFormatter;
2795 
2796                 // Create a header cell for each column, and set up the datatable definition for the column
2797                 $(options.columns).each(function (index, column) {
2798                     headerRow.append($("<th></th>"));
2799                     columnDefs[index] = {
2800                         "mData": column.propertyName,
2801                         "sTitle": column.columnHeader,
2802                         "sWidth": column.width,
2803                         "aTargets": [index],
2804                         "bSortable": column.sortable,
2805                         "bVisible": column.visible,
2806                         "mRender": column.render
2807                     };
2808                     if (typeof(column.renderFunction) === "function")
2809                     {
2810                         /** @ignore **/
2811                         columnDefs[index].mRender = /** @ignore **/ function (value, type, dataObject) { 
2812                             var returnValue;
2813 
2814                             //Apply column render logic to value before applying extra render function
2815                             if (typeof(column.render) === "function")
2816                             {
2817                                 value = column.render.call(value, value, value);
2818                             }
2819 
2820                             if (typeof(type) === "string")
2821                             {
2822                                 switch (type)
2823                                 {
2824                                 case "undefined":
2825                                 case "sort":
2826                                     returnValue = value;
2827                                     break;
2828                                 case "set":
2829                                     throw new Error("Unsupported set data in Finesse Grid");
2830                                 case "filter":
2831                                 case "display":
2832                                 case "type":
2833                                     returnValue = column.renderFunction.call(dataObject, value, dataObject);
2834                                     break;
2835                                 default:
2836                                     break;
2837                                 }
2838                             }
2839                             else
2840                             {
2841                                 throw new Error("type param not specified in Finesse DataTable mData");
2842                             }
2843 
2844                             return  returnValue;
2845                         };
2846                     }
2847                 });
2848                 gridOptions.aoColumnDefs = columnDefs;
2849 
2850                 // Set the height
2851                 if (typeof(options.bodyHeightPixels) !== "undefined" && options.bodyHeightPixels !== null)
2852                 {
2853                     gridOptions.sScrollY = options.bodyHeightPixels + "px";
2854                 }
2855 
2856                 // Place it into the DOM
2857                 if (typeof(options.container) !== "undefined" && options.container !== null)
2858                 {
2859                     $(options.container).append(table);
2860                 }
2861 
2862                 // Create the DataTable
2863                 table.dataTable(gridOptions);
2864 
2865                 return table;
2866             }
2867         },
2868         
2869         /**
2870          * @private
2871          * Sets a dojo button to the specified disable state, removing it from
2872          * the tab order if disabling, and restoring it to the tab order if enabling.
2873          * @param {Object} dojoButton Reference to the dijit.form.Button object. This is not the DOM element.
2874          * @param {bool} disabled
2875          */
2876         setDojoButtonDisabledAttribute: function (dojoButton, disabled) {
2877             var labelNode,
2878                 tabIndex;
2879 
2880             dojoButton.set("disabled", disabled);
2881 
2882             // Remove the tabindex attribute on disabled buttons, store it, 
2883             // and replace it when it becomes enabled again
2884             labelNode = $("#" + dojoButton.id + "_label");
2885             if (disabled)
2886             {
2887                 labelNode.data("finesse:dojoButton:tabIndex", labelNode.attr("tabindex"));
2888                 labelNode.removeAttr("tabindex");
2889             }
2890             else
2891             {
2892                 tabIndex = labelNode.data("finesse:dojoButton:tabIndex");
2893                 if (typeof(tabIndex) === "string")
2894                 {
2895                     labelNode.attr("tabindex", Number(tabIndex));
2896                 }
2897             }
2898         },
2899 
2900         /**
2901          * @private
2902          * Measures the given text using the supplied fontFamily and fontSize
2903          * @param  {string} text       text to measure
2904          * @param  {string} fontFamily
2905          * @param  {string} fontSize
2906          * @return {number} pixel width
2907          */
2908         measureText: function (text, fontFamily, fontSize) {
2909             var width,
2910                 element = $("<div></div>").text(text).css({
2911                     "fontSize": fontSize,
2912                     "fontFamily": fontFamily
2913                 }).addClass("offscreen").appendTo(document.body);
2914 
2915             width = element.width();
2916             element.remove();
2917 
2918             return width;
2919         },
2920 
2921         /**
2922          * Adjusts the gadget height. Shindig's gadgets.window.adjustHeight fails when
2923          * needing to resize down in IE. This gets around that by calculating the height
2924          * manually and passing it in.
2925          * @return {undefined}
2926          */
2927         "adjustGadgetHeight": function () {
2928             var bScrollHeight = $("body").height() + 20;
2929             gadgets.window.adjustHeight(bScrollHeight);
2930         },
2931 
2932         /**
2933         * Private helper method for converting a javascript object to xml, where the values of the elements are
2934         * appropriately escaped for XML.
2935         * This is a simple implementation that does not implement cdata or attributes. It is also 'unformatted' in that
2936         * there is no whitespace between elements.
2937         * @param object The javascript object to convert to XML.
2938         * @returns The XML string.
2939         * @private
2940         */
2941         _json2xmlWithEscape: function(object) {
2942             var that = this,
2943                 xml = "",
2944                 m,
2945                 /** @private **/
2946                 toXmlHelper = function(value, name) {
2947                 var xml = "",
2948                     i,
2949                     m;
2950                 if (value instanceof Array) {
2951                     for (i = 0; i < value.length; ++i) {
2952                         xml += toXmlHelper(value[i], name);
2953                     }
2954                 }
2955                 else if (typeof value === "object") {
2956                     xml += "<" + name + ">";
2957                     for (m in value) {
2958                         if (value.hasOwnProperty(m)) {
2959                            xml += toXmlHelper(value[m], m);
2960                         }
2961                     }
2962                     xml += "</" + name + ">";
2963                 }
2964                 else {
2965                     // is a leaf node
2966                     xml += "<" + name + ">" + that.translateHTMLEntities(value.toString(), true) +
2967                         "</" + name + ">";
2968                 }
2969                 return xml;
2970             };
2971             for (m in object) {
2972                 if (object.hasOwnProperty(m)) {
2973                     xml += toXmlHelper(object[m], m);
2974                 }
2975             }
2976             return xml;
2977         },
2978 
2979         /**
2980          * Private method for returning a sanitized version of the user agent string.
2981          * @returns the user agent string, but sanitized!
2982          * @private
2983          */
2984         getSanitizedUserAgentString: function () {
2985             return this.translateXMLEntities(navigator.userAgent, true);
2986         },
2987 
2988         /**
2989          * Use JQuery's implementation of Promises (Deferred) to execute code when 
2990          * multiple async processes have finished. An example use:
2991          *
2992          * var asyncProcess1 = $.Deferred(),
2993          *     asyncProcess2 = $.Deferred();
2994          *     
2995          * finesse.utilities.Utilities.whenAllDone(asyncProcess1, asyncProcess2) // WHEN both asyncProcess1 and asyncProcess2 are resolved or rejected ...
2996          *     .then(
2997          *         // First function passed to then() is called when all async processes are complete, regardless of errors
2998          *         function () {
2999          *             console.log("all processes completed");
3000          *         },
3001          *         // Second function passed to then() is called if any async processed threw an exception
3002          *         function (failures) { // Array of failure messages
3003          *             console.log("Number of failed async processes: " + failures.length);
3004          *         });
3005          *
3006          * Found at:
3007          * http://stackoverflow.com/a/15094263/1244030
3008          *
3009          * Pass in any number of $.Deferred instances.
3010          * @returns {Object}
3011          */
3012         whenAllDone: function () {
3013             var deferreds = [],
3014                 result = $.Deferred();
3015 
3016             $.each(arguments, function(i, current) {
3017                 var currentDeferred = $.Deferred();
3018                 current.then(function() {
3019                     currentDeferred.resolve(false, arguments);
3020                 }, function() {
3021                     currentDeferred.resolve(true, arguments);
3022                 });
3023                 deferreds.push(currentDeferred);
3024             });
3025 
3026             $.when.apply($, deferreds).then(function() {
3027                 var failures = [],
3028                     successes = [];
3029 
3030                 $.each(arguments, function(i, args) {
3031                     // If we resolved with `true` as the first parameter
3032                     // we have a failure, a success otherwise
3033                     var target = args[0] ? failures : successes,
3034                         data = args[1];
3035                     // Push either all arguments or the only one
3036                     target.push(data.length === 1 ? data[0] : args);
3037                 });
3038 
3039                 if(failures.length) {
3040                     return result.reject.apply(result, failures);
3041                 }
3042 
3043                 return result.resolve.apply(result, successes);
3044             });
3045 
3046             return result;
3047         }
3048     };    
3049 
3050     window.finesse = window.finesse || {};
3051     window.finesse.utilities = window.finesse.utilities || {};
3052     window.finesse.utilities.Utilities = Utilities;
3053     
3054     return Utilities;
3055 });
3056 
3057 /** The following comment is to prevent jslint errors about 
3058  * using variables before they are defined.
3059  */
3060 /*global finesse*/
3061 
3062 /**
3063  * Initiated by the Master to create a shared BOSH connection.
3064  *
3065  * @requires Utilities
3066  */
3067 
3068 /**
3069  * @class
3070  * Establishes a shared event connection by creating a communication tunnel
3071  * with the notification server and consume events which could be published.
3072  * Public functions are exposed to register to the connection status information
3073  * and events.
3074  * @constructor
3075  * @param {String} host
3076  *     The host name/ip of the Finesse server.
3077  * @throws {Error} If required constructor parameter is missing.
3078  */
3079 /** @private */
3080 define('clientservices/MasterTunnel',["utilities/Utilities"], function (Utilities) {
3081      var MasterTunnel = function (host, scheme) { 
3082         if (typeof host !== "string" || host.length === 0) {
3083             throw new Error("Required host parameter missing.");
3084         }
3085 
3086         var
3087 
3088         /**
3089          * Flag to indicate whether the tunnel frame is loaded.
3090          * @private
3091          */
3092         _isTunnelLoaded = false,
3093 
3094         /**
3095          * Short reference to the Finesse utility.
3096          * @private
3097          */
3098         _util = Utilities,
3099 
3100         /**
3101          * The URL with host and port to the Finesse server.
3102          * @private
3103          */
3104         _tunnelOrigin,
3105 
3106         /**
3107          * Location of the tunnel HTML URL.
3108          * @private
3109          */
3110         _tunnelURL,
3111         
3112         /**
3113          * The port on which to connect to the Finesse server to load the eventing resources.
3114          * @private
3115          */
3116         _tunnelOriginPort,
3117         
3118         /**
3119          * Flag to indicate whether we have processed the tunnel config yet.
3120          * @private
3121          */
3122         _isTunnelConfigInit = false,
3123 
3124         /**
3125          * The tunnel frame window object.
3126          * @private
3127          */
3128         _tunnelFrame,
3129 
3130         /**
3131          * The handler registered with the object to be invoked when an event is
3132          * delivered by the notification server.
3133          * @private
3134          */
3135         _eventHandler,
3136         
3137         /**
3138          * The handler registered with the object to be invoked when presence is
3139          * delivered by the notification server.
3140          * @private
3141          */
3142         _presenceHandler,
3143 
3144         /**
3145          * The handler registered with the object to be invoked when the BOSH
3146          * connection has changed states. The object will contain the "status"
3147          * property and a "resourceID" property only if "status" is "connected".
3148          * @private
3149          */
3150         _connInfoHandler,
3151 
3152         /**
3153          * The last connection status published by the JabberWerx library.
3154          * @private
3155          */
3156         _statusCache,
3157 
3158         /**
3159          * The last event sent by notification server.
3160          * @private
3161          */
3162         _eventCache,
3163 
3164         /**
3165          * The ID of the user logged into notification server.
3166          * @private
3167          */
3168         _id,
3169 
3170         /**
3171          * The domain of the XMPP server, representing the portion of the JID
3172          * following '@': userid@domain.com
3173          * @private
3174          */
3175         _xmppDomain,
3176 
3177         /**
3178          * The password of the user logged into notification server.
3179          * @private
3180          */
3181         _password,
3182 
3183         /**
3184          * The jid of the pubsub service on the XMPP server
3185          * @private
3186          */
3187         _pubsubDomain,
3188 
3189         /**
3190          * The resource to use for the BOSH connection.
3191          * @private
3192          */
3193         _resource,
3194 
3195         /**
3196          * The resource ID identifying the client device (that we receive from the server).
3197          * @private
3198          */
3199         _resourceID,
3200 
3201         /**
3202          * The different types of messages that could be sent to the parent frame.
3203          * The types here should be understood by the parent frame and used to
3204          * identify how the message is formatted.
3205          * @private
3206          */
3207         _TYPES = {
3208             EVENT: 0,
3209             ID: 1,
3210             PASSWORD: 2,
3211             RESOURCEID: 3,
3212             STATUS: 4,
3213             XMPPDOMAIN: 5,
3214             PUBSUBDOMAIN: 6,
3215             SUBSCRIBE: 7,
3216             UNSUBSCRIBE: 8,
3217             PRESENCE: 9,
3218             CONNECT_REQ: 10
3219         },
3220 
3221         _handlers = {
3222             subscribe: {},
3223             unsubscribe: {}
3224         },
3225         
3226 
3227         /**
3228          * Create a connection info object.
3229          * @returns {Object}
3230          *     A connection info object containing a "status" and "resourceID".
3231          * @private
3232          */
3233         _createConnInfoObj = function () {
3234             return {
3235                 status: _statusCache,
3236                 resourceID: _resourceID
3237             };
3238         },
3239 
3240         /**
3241          * Utility function which sends a message to the dynamic tunnel frame
3242          * event frame formatted as follows: "type|message".
3243          * @param {Number} type
3244          *     The category type of the message.
3245          * @param {String} message
3246          *     The message to be sent to the tunnel frame.
3247          * @private
3248          */
3249         _sendMessage = function (type, message) {
3250             message = type + "|" + message;
3251             _util.sendMessage(message, _tunnelFrame, _tunnelOrigin);
3252         },
3253 
3254         /**
3255          * Utility to process the response of a subscribe request from
3256          * the tunnel frame, then invoking the stored callback handler
3257          * with the respective data (error, when applicable)
3258          * @param {String} data
3259          *     The response in the format of "node[|error]"
3260          * @private
3261          */
3262         _processSubscribeResponse = function (data) {
3263             var dataArray = data.split("|"),
3264             node = dataArray[0],
3265             err;
3266             
3267             //Error is optionally the second item in the array
3268             if (dataArray.length) {
3269                 err = dataArray[1];
3270             }
3271             
3272             // These response handlers are short lived and should be removed and cleaned up immediately after invocation.
3273             if (_handlers.subscribe[node]) {
3274                 _handlers.subscribe[node](err);
3275                 delete _handlers.subscribe[node];
3276             }
3277         },
3278 
3279         /**
3280          * Utility to process the response of an unsubscribe request from
3281          * the tunnel frame, then invoking the stored callback handler
3282          * with the respective data (error, when applicable)
3283          * @param {String} data
3284          *     The response in the format of "node[|error]"
3285          * @private
3286          */
3287         _processUnsubscribeResponse = function (data) {
3288             var dataArray = data.split("|"),
3289             node = dataArray[0],
3290             err;
3291             
3292             //Error is optionally the second item in the array
3293             if (dataArray.length) {
3294                 err = dataArray[1];
3295             }
3296             
3297             // These response handlers are short lived and should be removed and cleaned up immediately after invocation.
3298             if (_handlers.unsubscribe[node]) {
3299                 _handlers.unsubscribe[node](err);
3300                 delete _handlers.unsubscribe[node];
3301             }
3302         },
3303 
3304         /**
3305          * Handler for messages delivered by window.postMessage. Listens for events
3306          * published by the notification server, connection status published by
3307          * the JabberWerx library, and the resource ID created when the BOSH
3308          * connection has been established.
3309          * @param {Object} e
3310          *     The message object as provided by the window.postMessage feature.
3311          * @private
3312          */
3313         _messageHandler = function (e) {
3314             var
3315 
3316             //Extract the message type and message data. The expected format is
3317             //"type|data" where type is a number represented by the TYPES object.
3318             delimPos = e.data.indexOf("|"),
3319             type = Number(e.data.substr(0, delimPos)),
3320             data =  e.data.substr(delimPos + 1);
3321             
3322             //Accepts messages and invoke the correct registered handlers.
3323             switch (type) {
3324             case _TYPES.EVENT:
3325                 _eventCache = data;
3326                 if (typeof _eventHandler === "function") {
3327                     _eventHandler(data);
3328                 }
3329                 break;
3330             case _TYPES.STATUS:
3331                 _statusCache = data;
3332 
3333                 //A "loaded" status means that the frame is ready to accept
3334                 //credentials for establishing a BOSH connection.
3335                 if (data === "loaded") {
3336                     _isTunnelLoaded = true;
3337                     if(_resource) {
3338                         _sendMessage(_TYPES.RESOURCEID, _resource);
3339                     }
3340                     _sendMessage(_TYPES.ID, _id);
3341                     _sendMessage(_TYPES.XMPPDOMAIN, _xmppDomain);
3342                     _sendMessage(_TYPES.PASSWORD, _password);
3343                     _sendMessage(_TYPES.PUBSUBDOMAIN, _pubsubDomain);
3344                 } else if (typeof _connInfoHandler === "function") {
3345                     _connInfoHandler(_createConnInfoObj());
3346                 }
3347                 break;
3348             case _TYPES.RESOURCEID:
3349                 _resourceID = data;
3350                 break;
3351             case _TYPES.SUBSCRIBE:
3352                 _processSubscribeResponse(data);
3353                 break;
3354             case _TYPES.UNSUBSCRIBE:
3355                 _processUnsubscribeResponse(data);
3356                 break;
3357             case _TYPES.PRESENCE:
3358                 if (typeof _presenceHandler === "function") {
3359                     _presenceHandler(data);
3360                 }
3361                 break;
3362             default:
3363                 break;
3364             }
3365         },
3366 
3367         /**
3368          * Initialize the tunnel config so that the url can be http or https with the appropriate port
3369          * @private
3370          */
3371         _initTunnelConfig = function () {
3372             if (_isTunnelConfigInit === true) {
3373                 return;
3374             }
3375             
3376             //Initialize tunnel origin
3377             //Determine tunnel origin based on host and scheme
3378             _tunnelOriginPort = (scheme && scheme.indexOf("https") !== -1) ? "7443" : "7071";
3379             if (scheme) {
3380                 _tunnelOrigin = scheme + "://" + host + ":" + _tunnelOriginPort;
3381             } else {
3382                 _tunnelOrigin = "http://" + host + ":" + _tunnelOriginPort;
3383             }
3384             _tunnelURL = _tunnelOrigin + "/tunnel/";
3385             
3386             _isTunnelConfigInit = true;
3387         },
3388 
3389         /**
3390          * Create the tunnel iframe which establishes the shared BOSH connection.
3391          * Messages are sent across frames using window.postMessage.
3392          * @private
3393          */
3394         _createTunnel = function () {
3395             var tunnelID = ((self === parent) ? "tunnel-frame" : "autopilot-tunnel-frame"),
3396             iframe = document.createElement("iframe");         
3397             iframe.style.display = "none";
3398             iframe.setAttribute("id", tunnelID);
3399             iframe.setAttribute("name", tunnelID);
3400             iframe.setAttribute("src", _tunnelURL);
3401             document.body.appendChild(iframe);
3402             _tunnelFrame = window.frames[tunnelID];
3403         };
3404 
3405         /**
3406          * Sends a message via postmessage to the EventTunnel to attempt to connect to the XMPP server
3407          * @private
3408          */
3409         this.makeConnectReq = function () {
3410             _sendMessage(_TYPES.PASSWORD, _password);
3411         };
3412         
3413         /**
3414          * @private
3415          * Returns the host of the Finesse server.
3416          * @returns {String}
3417          *     The host specified during the creation of the object.
3418          */
3419         this.getHost = function () {
3420             return host;
3421         };
3422 
3423         /**
3424          * @private
3425          * The resource ID of the user who is logged into the notification server.
3426          * @returns {String}
3427          *     The resource ID generated by the notification server.
3428          */
3429         this.getResourceID = function () {
3430             return _resourceID;
3431         };
3432 
3433         /**
3434          * @private
3435          * Indicates whether the tunnel frame is loaded.
3436          * @returns {Boolean}
3437          *     True if the tunnel frame is loaded, false otherwise.
3438          */
3439         this.isTunnelLoaded = function () {
3440             return _isTunnelLoaded;
3441         };
3442 
3443         /**
3444          * @private
3445          * The location of the tunnel HTML URL.
3446          * @returns {String}
3447          *     The location of the tunnel HTML URL.
3448          */
3449         this.getTunnelURL = function () {
3450             return _tunnelURL;
3451         };
3452 
3453         /**
3454          * @private
3455          * Tunnels a subscribe request to the eventing iframe.
3456          * @param {String} node
3457          *     The node to subscribe to
3458          * @param {Function} handler
3459          *     Handler to invoke upon success or failure
3460          */
3461         this.subscribe = function (node, handler) {
3462             if (handler && typeof handler !== "function") {
3463                 throw new Error("Parameter is not a function.");
3464             }
3465             _handlers.subscribe[node] = handler;
3466             _sendMessage(_TYPES.SUBSCRIBE, node);
3467         };
3468 
3469         /**
3470          * @private
3471          * Tunnels an unsubscribe request to the eventing iframe.
3472          * @param {String} node
3473          *     The node to unsubscribe from
3474          * @param {Function} handler
3475          *     Handler to invoke upon success or failure
3476          */
3477         this.unsubscribe = function (node, handler) {
3478             if (handler && typeof handler !== "function") {
3479                 throw new Error("Parameter is not a function.");
3480             }
3481             _handlers.unsubscribe[node] = handler;
3482             _sendMessage(_TYPES.UNSUBSCRIBE, node);
3483         };
3484 
3485         /**
3486          * @private
3487          * Registers a handler to be invoked when an event is delivered. Only one
3488          * is registered at a time. If there has already been an event that was
3489          * delivered, the handler will be invoked immediately.
3490          * @param {Function} handler
3491          *     Invoked when an event is delivered through the event connection.
3492          */
3493         this.registerEventHandler = function (handler) {
3494             if (typeof handler !== "function") {
3495                 throw new Error("Parameter is not a function.");
3496             }
3497             _eventHandler = handler;
3498             if (_eventCache) {
3499                 handler(_eventCache);
3500             }
3501         };
3502 
3503         /**
3504          * @private
3505          * Unregisters the event handler completely.
3506          */
3507         this.unregisterEventHandler = function () {
3508             _eventHandler = undefined;
3509         };
3510         
3511         /**
3512          * @private
3513          * Registers a handler to be invoked when a presence event is delivered. Only one
3514          * is registered at a time. 
3515          * @param {Function} handler
3516          *     Invoked when a presence event is delivered through the event connection.
3517          */
3518         this.registerPresenceHandler = function (handler) {
3519             if (typeof handler !== "function") {
3520                 throw new Error("Parameter is not a function.");
3521             }
3522             _presenceHandler = handler;
3523         };
3524         
3525         /**
3526          * @private
3527          * Unregisters the presence event handler completely.
3528          */
3529         this.unregisterPresenceHandler = function () {
3530             _presenceHandler = undefined;
3531         };
3532 
3533         /**
3534          * @private
3535          * Registers a handler to be invoked when a connection status changes. The
3536          * object passed will contain a "status" property, and a "resourceID"
3537          * property, which will contain the most current resource ID assigned to
3538          * the client. If there has already been an event that was delivered, the
3539          * handler will be invoked immediately.
3540          * @param {Function} handler
3541          *     Invoked when a connection status changes.
3542          */
3543         this.registerConnectionInfoHandler = function (handler) {
3544             if (typeof handler !== "function") {
3545                 throw new Error("Parameter is not a function.");
3546             }
3547             _connInfoHandler = handler;
3548             if (_statusCache) {
3549                 handler(_createConnInfoObj());
3550             }
3551         };
3552 
3553         /**
3554          * @private
3555          * Unregisters the connection information handler.
3556          */
3557         this.unregisterConnectionInfoHandler = function () {
3558             _connInfoHandler = undefined;
3559         };
3560 
3561         /**
3562          * @private
3563          * Start listening for events and create a event tunnel for the shared BOSH
3564          * connection.
3565          * @param {String} id
3566          *     The ID of the user for the notification server.
3567          * @param {String} password
3568          *     The password of the user for the notification server.
3569          * @param {String} xmppDomain
3570          *     The XMPP domain of the notification server
3571          * @param {String} pubsubDomain
3572          *     The location (JID) of the XEP-0060 PubSub service
3573          * @param {String} resource
3574          *     The resource to connect to the notification servier with.
3575          */
3576         this.init = function (id, password, xmppDomain, pubsubDomain, resource) {
3577             
3578             if (typeof id !== "string" || typeof password !== "string" || typeof xmppDomain !== "string" || typeof pubsubDomain !== "string") {
3579                 throw new Error("Invalid or missing required parameters.");
3580             }
3581 
3582             _initTunnelConfig();
3583             
3584             _id = id;
3585             _password = password;
3586             _xmppDomain = xmppDomain;
3587             _pubsubDomain = pubsubDomain;
3588             _resource = resource;
3589 
3590             //Attach a listener for messages sent from tunnel frame.
3591             _util.receiveMessage(_messageHandler, _tunnelOrigin);
3592 
3593             //Create the tunnel iframe which will establish the shared connection.
3594             _createTunnel();
3595         };
3596 
3597         //BEGIN TEST CODE//
3598 //        /**
3599 //         * Test code added to expose private functions that are used by unit test
3600 //         * framework. This section of code is removed during the build process
3601 //         * before packaging production code. The [begin|end]TestSection are used
3602 //         * by the build to identify the section to strip.
3603 //         * @ignore
3604 //         */
3605 //        this.beginTestSection = 0;
3606 //
3607 //        /**
3608 //         * @ignore
3609 //         */
3610 //        this.getTestObject = function () {
3611 //            //Load mock dependencies.
3612 //            var _mock = new MockControl();
3613 //            _util = _mock.createMock(finesse.utilities.Utilities);
3614 //
3615 //            return {
3616 //                //Expose mock dependencies
3617 //                mock: _mock,
3618 //                util: _util,
3619 //
3620 //                //Expose internal private functions
3621 //                types: _TYPES,
3622 //                createConnInfoObj: _createConnInfoObj,
3623 //                sendMessage: _sendMessage,
3624 //                messageHandler: _messageHandler,
3625 //                createTunnel: _createTunnel,
3626 //                handlers: _handlers,
3627 //                initTunnelConfig : _initTunnelConfig
3628 //            };
3629 //        };
3630 //
3631 //        /**
3632 //         * @ignore
3633 //         */
3634 //        this.endTestSection = 0;
3635 //        //END TEST CODE//
3636     };
3637     
3638     /** @namespace JavaScript class objects and methods to handle the subscription to Finesse events.*/
3639     finesse.clientservices = finesse.clientservices || {};
3640 
3641     window.finesse = window.finesse || {};
3642     window.finesse.clientservices = window.finesse.clientservices || {};
3643     window.finesse.clientservices.MasterTunnel = MasterTunnel;
3644 
3645     return MasterTunnel;
3646 
3647 });
3648 
3649 /**
3650  * Contains a list of topics used for client side pubsub.
3651  *
3652  */
3653 
3654 /** @private */
3655 define('clientservices/Topics',[], function () {
3656     
3657    var Topics = (function () {
3658 
3659         /**
3660          * @private
3661          * The namespace prepended to all Finesse topics.
3662          */
3663         this.namespace = "finesse.info";
3664     
3665         /**
3666          * @private
3667          * Gets the full topic name with the Finesse namespace prepended.
3668          * @param {String} topic
3669          *     The topic category.
3670          * @returns {String}
3671          *     The full topic name with prepended namespace.
3672          */
3673         var _getNSTopic = function (topic) {
3674             return this.namespace + "." + topic;
3675         };
3676         
3677         /** @scope finesse.clientservices.Topics */
3678         return {
3679             /** 
3680              * @private
3681              * Client side request channel. 
3682              */
3683             REQUESTS: _getNSTopic("requests"),
3684     
3685             /** 
3686              * @private
3687              * Client side response channel. 
3688              */
3689             RESPONSES: _getNSTopic("responses"),
3690 
3691             /** 
3692              * @private
3693              * Connection status. 
3694              */
3695             EVENTS_CONNECTION_INFO: _getNSTopic("connection"),
3696             
3697             /** 
3698              * @private
3699              * Presence channel 
3700              */
3701             PRESENCE: _getNSTopic("presence"),
3702     
3703             /**
3704              * @private
3705              * Convert a Finesse REST URI to a OpenAjax compatible topic name.
3706              */
3707             getTopic: function (restUri) {
3708                 //The topic should not start with '/' else it will get replaced with
3709                 //'.' which is invalid.
3710                 //Thus, remove '/' if it is at the beginning of the string
3711                 if (restUri.indexOf('/') === 0) {
3712                     restUri = restUri.substr(1);
3713                 }
3714     
3715                 //Replace every instance of "/" with ".". This is done to follow the
3716                 //OpenAjaxHub topic name convention.
3717                 return restUri.replace(/\//g, ".");
3718             }
3719         };
3720     }());
3721     window.finesse = window.finesse || {};
3722     window.finesse.clientservices = window.finesse.clientservices || {};
3723     /** @private */
3724     window.finesse.clientservices.Topics = Topics;
3725     
3726     return Topics;
3727 });
3728 
3729 /** The following comment is to prevent jslint errors about 
3730  * using variables before they are defined.
3731  */
3732 /*global finesse*/
3733 
3734 /**
3735  * Registers with the MasterTunnel to receive events, which it
3736  *     could publish to the OpenAjax gadget pubsub infrastructure.
3737  *
3738  * @requires OpenAjax, finesse.clientservices.MasterTunnel, finesse.clientservices.Topics
3739  */
3740 
3741 /** @private */
3742 define('clientservices/MasterPublisher',[
3743     "clientservices/MasterTunnel",
3744     "clientservices/Topics",
3745     "utilities/Utilities"
3746 ],
3747 function (MasterTunnel, Topics, Utilities) {
3748     
3749      var MasterPublisher = function (tunnel, hub) {
3750         if (!(tunnel instanceof MasterTunnel)) {
3751             throw new Error("Required tunnel object missing or invalid.");
3752         }
3753 
3754         var
3755         
3756         ClientServices = finesse.clientservices.ClientServices,
3757 
3758         /**
3759          * Reference to the gadget pubsub Hub instance.
3760          * @private
3761          */
3762         _hub = hub,
3763 
3764         /**
3765          * Reference to the Topics class.
3766          * @private
3767          */
3768         _topics = Topics,
3769         
3770         /**
3771          * Reference to conversion utilities class.
3772          * @private
3773          */
3774         _utils = Utilities,
3775         
3776         /**
3777          * References to ClientServices logger methods
3778          * @private
3779          */
3780         _logger = {
3781             log: ClientServices.log
3782         },
3783         
3784         /**
3785          * Store the passed in tunnel.
3786          * @private
3787          */
3788         _tunnel = tunnel,
3789 
3790         /**
3791          * Caches the connection info event so that it could be published if there
3792          * is a request for it.
3793          * @private
3794          */
3795         _connInfoCache = {},
3796 
3797         /**
3798          * The types of possible request types supported when listening to the
3799          * requests channel. Each request type could result in different operations.
3800          * @private
3801          */
3802         _REQTYPES = {
3803             CONNECTIONINFO: "ConnectionInfoReq",
3804             SUBSCRIBE: "SubscribeNodeReq",
3805             UNSUBSCRIBE: "UnsubscribeNodeReq",
3806             CONNECT: "ConnectionReq"
3807         },
3808 
3809         /**
3810          * Will store list of nodes that have OF subscriptions created
3811          *     _nodesList[node][subscribing].reqIds[subid]
3812          *     _nodesList[node][active].reqIds[subid]
3813          *     _nodesList[node][unsubscribing].reqIds[subid]
3814          *     _nodesList[node][holding].reqIds[subid]
3815          * @private
3816          */
3817         _nodesList = {},
3818         
3819         /**
3820          * The states that a subscription can be in
3821          * @private
3822          */
3823         _CHANNELSTATES = {
3824             UNINITIALIZED: "Uninitialized",
3825             PENDING: "Pending",
3826             OPERATIONAL: "Operational"
3827         },
3828 
3829         /**
3830           * Checks if the payload is JSON 
3831           * @returns {Boolean}
3832           * @private
3833           */
3834         _isJsonPayload = function(event) {
3835             var delimStart, delimEnd, retval = false;
3836             
3837             try { 
3838               delimStart = event.indexOf('{');
3839               delimEnd = event.lastIndexOf('}');
3840 
3841               if ((delimStart !== -1 ) && (delimEnd === (event.length - 1))) {
3842                 retval = true;  //event contains JSON payload
3843               }
3844             } catch (err) {
3845               _logger.log("MasterPublisher._isJsonPayload() - Caught error: " + err);
3846             }
3847             return retval;
3848         },
3849         
3850                 /**
3851           * Parses a JSON event and then publishes.
3852           *
3853           * @param {String} event
3854           *     The full event payload.
3855           * @throws {Error} If the payload object is malformed.
3856           * @private
3857           */
3858         _parseAndPublishJSONEvent = function(event) {
3859             var topic, eventObj, publishEvent,
3860             delimPos = event.indexOf("{"),
3861             node, parser,
3862             eventJson = event,
3863             returnObj = {node: null, data: null};
3864 
3865             try {
3866                //Extract and strip the node path from the message
3867                if (delimPos > 0) 
3868                {
3869                   //We need to decode the URI encoded node path
3870                   //TODO: make sure this is kosher with OpenAjax topic naming
3871                   node = decodeURI(event.substr(0, delimPos));
3872                   eventJson = event.substr(delimPos);
3873                   
3874                   //Converting the node path to openAjaxhub topic
3875                   topic = _topics.getTopic(node);
3876                   
3877                   returnObj.node = node;
3878                   returnObj.payload = eventJson;
3879                } 
3880                else 
3881                {
3882                   _logger.log("MasterPublisher._parseAndPublishJSONEvent() - [ERROR] node is not given in postMessage: " + eventJson);
3883                   throw new Error("node is not given in postMessage: " + eventJson);
3884                }
3885 
3886                parser = _utils.getJSONParser();
3887 
3888                eventObj = parser.parse(eventJson);
3889                returnObj.data = eventObj;
3890 
3891             } catch (err) {
3892                _logger.log("MasterPublisher._parseAndPublishJSONEvent() - [ERROR] Malformed event payload: " + err);
3893                throw new Error("Malformed event payload : " + err);
3894             }
3895             
3896             _logger.log("MasterPublisher._parseAndPublishJSONEvent() - Received JSON event on node '" + node + "': " + eventJson); 
3897             
3898             publishEvent = {content : event, object : eventObj };
3899 
3900             //Publish event to proper event topic.
3901             if (topic && eventObj) {
3902                _hub.publish(topic, publishEvent);
3903             }
3904         },
3905         
3906         /**
3907           * Parses an XML event and then publishes.
3908           *
3909           * @param {String} event
3910           *     The full event payload.
3911           * @throws {Error} If the payload object is malformed.
3912           * @private
3913           */
3914         _parseAndPublishXMLEvent = function(event) {
3915             var topic, eventObj, publishEvent, restTopic,
3916             delimPos = event.indexOf("<"),
3917             node,
3918             eventXml = event;
3919             
3920             try {
3921                //Extract and strip the node path from the message
3922                if (delimPos > 0) {
3923                   //We need to decode the URI encoded node path
3924                   //TODO: make sure this is kosher with OpenAjax topic naming
3925                   node = decodeURI(event.substr(0, delimPos));
3926                   eventXml = event.substr(delimPos);
3927                   //Converting the node path to openAjaxhub topic
3928                   topic = _topics.getTopic(node);
3929                } else {
3930                   _logger.log("MasterPublisher._parseAndPublishXMLEvent() - [ERROR] node is not given in postMessage: " + eventXml);
3931                   throw new Error("node is not given in postMessage: " + eventXml);
3932                }
3933 
3934                eventObj = _utils.xml2JsObj(eventXml);
3935                   
3936            } catch (err) {
3937                _logger.log("MasterPublisher._parseAndPublishXMLEvent() - [ERROR] Malformed event payload: " + err);
3938                throw new Error("Malformed event payload : " + err);
3939            }
3940            
3941            _logger.log("MasterPublisher._parseAndPublishXMLEvent() - Received XML event on node '" + node + "': " + eventXml);
3942            
3943            publishEvent = {content : event, object : eventObj };
3944 
3945            //Publish event to proper event topic.
3946            if (topic && eventObj) {
3947                _hub.publish(topic, publishEvent);
3948            }
3949         },
3950         
3951         /**
3952          * Publishes events to the appropriate topic. The topic name is determined
3953          * by fetching the source value from the event.
3954          * @param {String} event
3955          *     The full event payload.
3956          * @throws {Error} If the payload object is malformed.
3957          * @private
3958          */
3959         _eventHandler = function (event) {
3960             
3961             //Handle JSON or XML events
3962             if (!_isJsonPayload(event))
3963             {
3964                //XML
3965                _parseAndPublishXMLEvent(event);
3966             }
3967             else
3968             {
3969                //JSON
3970                _parseAndPublishJSONEvent(event);
3971             }
3972         },
3973         
3974         
3975         /**
3976          * Handler for when presence events are sent through the MasterTunnel.
3977          * @returns {Object}
3978          *     A presence xml event.
3979          * @private
3980          */
3981         _presenceHandler = function (event) {
3982             var eventObj = _utils.xml2JsObj(event), publishEvent;
3983             
3984             publishEvent = {content : event, object : eventObj};
3985             
3986             if (eventObj) {
3987                 _hub.publish(_topics.PRESENCE, publishEvent);
3988             }
3989         },
3990 
3991         /**
3992          * Clone the connection info object from cache.
3993          * @returns {Object}
3994          *     A connection info object containing a "status" and "resourceID".
3995          * @private
3996          */
3997         _cloneConnInfoObj = function () {
3998             if (_connInfoCache) {
3999                 return {
4000                     status: _connInfoCache.status,
4001                     resourceID: _connInfoCache.resourceID
4002                 };
4003             } else {
4004                 return null;
4005             }
4006         },
4007 
4008         /**
4009          * Cleans up any outstanding subscribe/unsubscribe requests and notifies them of errors.
4010          * This is done if we get disconnected because we cleanup explicit subscriptions on disconnect.
4011          * @private
4012          */
4013         _cleanupPendingRequests = function () {
4014             var node, curSubid, errObj = {
4015                 error: {
4016                     errorType: "Disconnected",
4017                     errorMessage: "Outstanding request will never complete."
4018                 }
4019             };
4020 
4021             // Iterate through all outstanding subscribe requests to notify them that it will never return
4022             for (node in _nodesList) {
4023                 if (_nodesList.hasOwnProperty(node)) {
4024                     for (curSubid in _nodesList[node].subscribing.reqIds) {
4025                         if (_nodesList[node].subscribing.reqIds.hasOwnProperty(curSubid)) {
4026                             // Notify this outstanding subscribe request to give up and error out
4027                             _hub.publish(_topics.RESPONSES + "." + curSubid, errObj); 
4028                         }
4029                     }
4030                     for (curSubid in _nodesList[node].unsubscribing.reqIds) {
4031                         if (_nodesList[node].unsubscribing.reqIds.hasOwnProperty(curSubid)) {
4032                             // Notify this outstanding unsubscribe request to give up and error out
4033                             _hub.publish(_topics.RESPONSES + "." + curSubid, errObj); 
4034                         }
4035                     }
4036                 }
4037             }
4038         },
4039 
4040         /**
4041          * Publishes the connection info to the connection info topic.
4042          * @param {Object} connInfo
4043          *     The connection info object containing the status and resource ID.
4044          * @private
4045          */
4046         _connInfoHandler = function (connInfo) {
4047             var before = _connInfoCache.status;
4048             _connInfoCache = connInfo;
4049             _logger.log("MasterPublisher._connInfoHandler() - Connection status: " + connInfo.status);
4050             _hub.publish(_topics.EVENTS_CONNECTION_INFO, _cloneConnInfoObj());
4051             if (before === "connected" && connInfo.status !== "connected") {
4052                 // Fail all pending requests when we transition to disconnected
4053                 _cleanupPendingRequests();
4054             }
4055         },
4056 
4057         
4058         /**
4059          * Utility method to bookkeep node subscription requests and determine
4060          * whehter it is necessary to tunnel the request to JabberWerx.
4061          * @param {String} node
4062          *     The node of interest
4063          * @param {String} reqId
4064          *     A unique string identifying the request/subscription
4065          * @private
4066          */
4067         _subscribeNode = function (node, subid) {
4068             if (_connInfoCache.status !== "connected") {
4069                 _hub.publish(_topics.RESPONSES + "." + subid, {
4070                     error: {
4071                         errorType: "Not connected",
4072                         errorMessage: "Cannot subscribe without connection."
4073                     }
4074                 });
4075                 return;
4076             }
4077             // NODE DOES NOT YET EXIST
4078             if (!_nodesList[node]) {
4079                 _nodesList[node] = {
4080                     "subscribing": {
4081                         "reqIds": {},
4082                         "length": 0
4083                     },
4084                     "active": {
4085                         "reqIds": {},
4086                         "length": 0
4087                     },
4088                     "unsubscribing": {
4089                         "reqIds": {},
4090                         "length": 0
4091                     },
4092                     "holding": {
4093                         "reqIds": {},
4094                         "length": 0
4095                     }
4096                 };
4097             }
4098             if (_nodesList[node].active.length === 0) {
4099                 if (_nodesList[node].unsubscribing.length === 0) {
4100                     if (_nodesList[node].subscribing.length === 0) {
4101                         _nodesList[node].subscribing.reqIds[subid] = true;
4102                         _nodesList[node].subscribing.length += 1;
4103 
4104                         _logger.log("MasterPublisher._subscribeNode() - Attempting to subscribe to node '" + node + "'");
4105                         _tunnel.subscribe(node, function (err) {
4106                             var errObj, curSubid;
4107                             if (err) {
4108                                 errObj = {
4109                                     subscribe: {
4110                                         content: err
4111                                     }
4112                                 };
4113 
4114                                 try {
4115                                     errObj.subscribe.object = gadgets.json.parse((_utils.xml2json(jQuery.parseXML(err), "")));
4116                                 } catch (e) {
4117                                     errObj.error = {
4118                                         errorType: "parseError",
4119                                         errorMessage: "Could not serialize XML: " + e
4120                                     };
4121                                 }
4122                                 _logger.log("MasterPublisher._subscribeNode() - Error subscribing to node '" + node + "': " + err);
4123                             } else {
4124                                 _logger.log("MasterPublisher._subscribeNode() - Subscribed to node '" + node + "'");
4125                             }
4126 
4127                             for (curSubid in _nodesList[node].subscribing.reqIds) {
4128                                 if (_nodesList[node].subscribing.reqIds.hasOwnProperty(curSubid)) {
4129                                     _hub.publish(_topics.RESPONSES + "." + curSubid, errObj);
4130                                     if (!err) {
4131                                         _nodesList[node].active.reqIds[curSubid] = true;
4132                                         _nodesList[node].active.length += 1;
4133                                     }
4134                                     delete _nodesList[node].subscribing.reqIds[curSubid];
4135                                     _nodesList[node].subscribing.length -= 1;
4136                                 }
4137                             }
4138                         });
4139                         
4140                     } else { //other ids are subscribing
4141                         _nodesList[node].subscribing.reqIds[subid] = true;
4142                         _nodesList[node].subscribing.length += 1;
4143                     }                       
4144                 } else { //An unsubscribe request is pending, hold onto these subscribes until it is done
4145                     _nodesList[node].holding.reqIds[subid] = true;
4146                     _nodesList[node].holding.length += 1;
4147                 }
4148             } else { // The node has active subscriptions; add this subid and return successful response
4149                 _nodesList[node].active.reqIds[subid] = true;
4150                 _nodesList[node].active.length += 1;
4151                 _hub.publish(_topics.RESPONSES + "." + subid, undefined); 
4152             }
4153         },
4154 
4155         /**
4156          * Utility method to bookkeep node unsubscribe requests and determine
4157          * whehter it is necessary to tunnel the request to JabberWerx.
4158          * @param {String} node
4159          *     The node to unsubscribe from
4160          * @param {String} reqId
4161          *     A unique string identifying the subscription to remove
4162          * @private
4163          */
4164         _unsubscribeNode = function (node, subid) {
4165             if (!_nodesList[node]) { //node DNE, publish success response
4166                 _hub.publish(_topics.RESPONSES + "." + subid, undefined); 
4167             } else {
4168                 if (_connInfoCache.status !== "connected") {
4169                     _hub.publish(_topics.RESPONSES + "." + subid, {
4170                         error: {
4171                             errorType: "Not connected",
4172                             errorMessage: "Cannot unsubscribe without connection."
4173                         }
4174                     });
4175                     return;
4176                 }
4177                 if (_nodesList[node].active.length > 1) {
4178                     delete _nodesList[node].active.reqIds[subid];
4179                     _hub.publish(_topics.RESPONSES + "." + subid, undefined); 
4180                     _nodesList[node].active.length -= 1;
4181                 } else if (_nodesList[node].active.length === 1) { // transition subid from active category to unsubscribing category
4182                     _nodesList[node].unsubscribing.reqIds[subid] = true;
4183                     _nodesList[node].unsubscribing.length += 1;
4184                     delete _nodesList[node].active.reqIds[subid];
4185                     _nodesList[node].active.length -= 1;
4186 
4187                     _logger.log("MasterPublisher._unsubscribeNode() - Attempting to unsubscribe from node '" + node + "'");
4188                     _tunnel.unsubscribe(node, function (err) {
4189                         var errObj, curSubid;
4190                         if (err) {
4191                             errObj = {
4192                                 subscribe: {
4193                                     content: err
4194                                 }
4195                             };
4196 
4197                             try {
4198                                 errObj.subscribe.object = gadgets.json.parse((_utils.xml2json(jQuery.parseXML(err), "")));
4199                             } catch (e) {
4200                                 errObj.error = {
4201                                     errorType: "parseError",
4202                                     errorMessage: "Could not serialize XML: " + e
4203                                 };
4204                             }
4205                             _logger.log("MasterPublisher._unsubscribeNode() - Error unsubscribing from node '" + node + "': " + err);
4206                         } else {
4207                             _logger.log("MasterPublisher._unsubscribeNode() - Unsubscribed from node '" + node + "'");
4208                         }
4209 
4210                         for (curSubid in _nodesList[node].unsubscribing.reqIds) {
4211                             if (_nodesList[node].unsubscribing.reqIds.hasOwnProperty(curSubid)) {
4212                                 // publish to all subids whether unsubscribe failed or succeeded
4213                                 _hub.publish(_topics.RESPONSES + "." + curSubid, errObj); 
4214                                 if (!err) {
4215                                     delete _nodesList[node].unsubscribing.reqIds[curSubid];
4216                                     _nodesList[node].unsubscribing.length -= 1;
4217                                 } else { // Just remove the subid from unsubscribing; the next subscribe request will operate with node already created
4218                                     delete _nodesList[node].unsubscribing.reqIds[curSubid];
4219                                     _nodesList[node].unsubscribing.length -= 1;
4220                                 }   
4221                             }
4222                         }
4223                         
4224                         if (!err && _nodesList[node].holding.length > 0) { // if any subscribe requests came in while unsubscribing from OF, now transition from holding to subscribing
4225                             for (curSubid in _nodesList[node].holding.reqIds) {
4226                                 if (_nodesList[node].holding.reqIds.hasOwnProperty(curSubid)) {
4227                                     delete _nodesList[node].holding.reqIds[curSubid];
4228                                     _nodesList[node].holding.length -= 1;
4229                                     _subscribeNode(node, curSubid);                             
4230                                 }
4231                             }
4232                         }
4233                     });
4234                 } else { // length <= 0?
4235                     _hub.publish(_topics.RESPONSES + "." + subid, undefined);
4236                 }
4237             }
4238         },
4239         
4240         /**
4241          * Handles client requests to establish a BOSH connection.
4242          * @param {String} id
4243          *     id of the xmpp user
4244          * @param {String} password
4245          *     password of the xmpp user
4246          * @param {String} xmppDomain
4247          *     xmppDomain of the xmpp user account
4248          * @private
4249          */
4250         _connect = function (id, password, xmppDomain) {
4251             _tunnel.makeConnectReq(id, password, xmppDomain);
4252         },
4253 
4254         /**
4255          * Handles client requests made to the request topic. The type of the
4256          * request is described in the "type" property within the data payload. Each
4257          * type can result in a different operation.
4258          * @param {String} topic
4259          *     The topic which data was published to.
4260          * @param {Object} data
4261          *     The data containing requests information published by clients.
4262          * @param {String} data.type
4263          *     The type of the request. Supported: "ConnectionInfoReq"
4264          * @param {Object} data.data
4265          *     May contain data relevant for the particular requests.
4266          * @param {String} [data.invokeID]
4267          *     The ID used to identify the request with the response. The invoke ID
4268          *     will be included in the data in the publish to the topic. It is the
4269          *     responsibility of the client to correlate the published data to the
4270          *     request made by using the invoke ID.
4271          * @private
4272          */
4273         _clientRequestHandler = function (topic, data) {
4274             var dataCopy;
4275 
4276             //Ensure a valid data object with "type" and "data" properties.
4277             if (typeof data === "object" &&
4278                     typeof data.type === "string" &&
4279                     typeof data.data === "object") {
4280                 switch (data.type) {
4281                 case _REQTYPES.CONNECTIONINFO:
4282                     //It is possible that Slave clients come up before the Master
4283                     //client. If that is the case, the Slaves will need to make a
4284                     //request for the Master to send the latest connection info to the
4285                     //connectionInfo topic.
4286                     dataCopy = _cloneConnInfoObj();
4287                     if (dataCopy) {
4288                         if (data.invokeID !== undefined) {
4289                             dataCopy.invokeID = data.invokeID;
4290                         }
4291                         _hub.publish(_topics.EVENTS_CONNECTION_INFO, dataCopy);
4292                     }
4293                     break;
4294                 case _REQTYPES.SUBSCRIBE:
4295                     if (typeof data.data.node === "string") {
4296                         _subscribeNode(data.data.node, data.invokeID);
4297                     }
4298                     break;
4299                 case _REQTYPES.UNSUBSCRIBE:
4300                     if (typeof data.data.node === "string") {
4301                         _unsubscribeNode(data.data.node, data.invokeID);
4302                     }
4303                     break;
4304                 case _REQTYPES.CONNECT:
4305                     // Deprecated: Disallow others (non-masters) from administering BOSH connections that are not theirs
4306                     _logger.log("MasterPublisher._clientRequestHandler(): F403: Access denied. Non-master ClientService instances do not have the clearance level to make this request: makeConnectionReq");
4307                     break;
4308                 default:
4309                     break;
4310                 }
4311             }
4312         };
4313 
4314         (function () {
4315             //Register to receive events and connection status from tunnel.
4316             _tunnel.registerEventHandler(_eventHandler);
4317             _tunnel.registerPresenceHandler(_presenceHandler);
4318             _tunnel.registerConnectionInfoHandler(_connInfoHandler);
4319 
4320             //Listen to a request channel to respond to any requests made by other
4321             //clients because the Master may have access to useful information.
4322             _hub.subscribe(_topics.REQUESTS, _clientRequestHandler);
4323         }());
4324 
4325         /**
4326          * @private
4327          * Handles client requests to establish a BOSH connection.
4328          * @param {String} id
4329          *     id of the xmpp user
4330          * @param {String} password
4331          *     password of the xmpp user
4332          * @param {String} xmppDomain
4333          *     xmppDomain of the xmpp user account
4334          */
4335         this.connect = function (id, password, xmppDomain) {
4336           _connect(id, password, xmppDomain);
4337         };
4338 
4339         /**
4340          * @private
4341          * Resets the list of explicit subscriptions
4342          */
4343         this.wipeout = function () {
4344             _cleanupPendingRequests();
4345             _nodesList = {};
4346        };
4347 
4348         //BEGIN TEST CODE//
4349        /**
4350         * Test code added to expose private functions that are used by unit test
4351         * framework. This section of code is removed during the build process
4352         * before packaging production code. The [begin|end]TestSection are used
4353         * by the build to identify the section to strip.
4354         * @ignore
4355         */
4356        this.beginTestSection = 0;
4357 
4358        /**
4359         * @ignore
4360         */
4361        this.getTestObject = function () {
4362            //Load mock dependencies.
4363            var _mock = new MockControl();
4364            _hub = _mock.createMock(gadgets.Hub);
4365            _tunnel = _mock.createMock();
4366 
4367            return {
4368                //Expose mock dependencies
4369                mock: _mock,
4370                hub: _hub,
4371                tunnel: _tunnel,
4372                setTunnel: function (tunnel) {
4373                    _tunnel = tunnel;
4374                },
4375                getTunnel: function () {
4376                    return _tunnel;
4377                },
4378 
4379                //Expose internal private functions
4380                reqtypes: _REQTYPES,
4381                eventHandler: _eventHandler,
4382                presenceHandler: _presenceHandler,
4383                
4384                subscribeNode: _subscribeNode,
4385                unsubscribeNode: _unsubscribeNode,
4386                
4387                getNodeList: function () {
4388                    return _nodesList;
4389                },
4390                setNodeList: function (nodelist) {
4391                    _nodesList = nodelist;
4392                },
4393                
4394                cloneConnInfoObj: _cloneConnInfoObj,
4395                connInfoHandler: _connInfoHandler,
4396                clientRequestHandler: _clientRequestHandler
4397 
4398            };
4399        };
4400 
4401 
4402        /**
4403         * @ignore
4404         */
4405        this.endTestSection = 0;
4406        //END TEST CODE//
4407 
4408     };
4409     
4410     window.finesse = window.finesse || {};
4411     window.finesse.clientservices = window.finesse.clientservices || {};
4412     window.finesse.clientservices.MasterPublisher = MasterPublisher;
4413 
4414     return MasterPublisher;
4415 });
4416 
4417 /** The following comment is to prevent jslint errors about 
4418  * using variables before they are defined.
4419  */
4420 /*global publisher:true */
4421 
4422 /**
4423  * Exposes a set of API wrappers that will hide the dirty work of
4424  *     constructing Finesse API requests and consuming Finesse events.
4425  *
4426  * @requires OpenAjax, jQuery 1.5, finesse.utilities.Utilities
4427  */
4428 
4429 
4430 /**
4431  * Allow clients to make Finesse API requests and consume Finesse events by
4432  * calling a set of exposed functions. The Services layer will do the dirty
4433  * work of establishing a shared BOSH connection (for designated Master
4434  * modules), consuming events for client subscriptions, and constructing API
4435  * requests.
4436  */
4437 /** @private */
4438 define('clientservices/ClientServices',[
4439     "clientservices/MasterTunnel",
4440     "clientservices/MasterPublisher",
4441     "clientservices/Topics",
4442     "utilities/Utilities"
4443 ],
4444 function (MasterTunnel, MasterPublisher, Topics, Utilities) {
4445     
4446      var ClientServices = (function () {/** @lends finesse.clientservices.ClientServices.prototype */
4447         var
4448 
4449         /**
4450          * Shortcut reference to the master tunnel
4451          * @private
4452          */
4453         _tunnel,
4454 
4455         _publisher,
4456 
4457         /**
4458          * Shortcut reference to the finesse.utilities.Utilities singleton
4459          * This will be set by init()
4460          * @private
4461          */
4462         _util,
4463 
4464         /**
4465          * Shortcut reference to the gadgets.io object.
4466          * This will be set by init()
4467          * @private
4468          */
4469         _io,
4470 
4471         /**
4472          * Shortcut reference to the gadget pubsub Hub instance.
4473          * This will be set by init()
4474          * @private
4475          */
4476         _hub,
4477 
4478         /**
4479          * Logger object set externally by setLogger, defaults to nothing.
4480          * @private
4481          */
4482         _logger = {},
4483 
4484         /**
4485          * Shortcut reference to the Topics class.
4486          * This will be set by init()
4487          * @private
4488          */
4489         _topics,
4490 
4491         /**
4492          * Config object needed to initialize this library
4493          * This must be set by init()
4494          * @private
4495          */
4496         _config,
4497 
4498         /**
4499          * @private
4500          * Whether or not this ClientService instance is a Master.
4501          */
4502         _isMaster = false,
4503 
4504         /**
4505          * @private
4506          * Whether the Client Services have been initiated yet.
4507          */
4508         _inited = false,
4509 
4510         /**
4511          * Stores the list of subscription IDs for all subscriptions so that it
4512          * could be retrieve for unsubscriptions.
4513          * @private
4514          */
4515         _subscriptionID = {},
4516 
4517         /**
4518          * The possible states of the JabberWerx BOSH connection.
4519          * @private
4520          */
4521         _STATUS = {
4522             CONNECTING: "connecting",
4523             CONNECTED: "connected",
4524             DISCONNECTED: "disconnected",
4525             DISCONNECTED_CONFLICT: "conflict",
4526             DISCONNECTED_UNAUTHORIZED: "unauthorized",
4527             DISCONNECTING: "disconnecting",
4528             RECONNECTING: "reconnecting",
4529             UNLOADING: "unloading",
4530             FAILING: "failing",
4531             RECOVERED: "recovered"
4532         },
4533 
4534         _failoverMode = false,
4535 
4536         /**
4537          * Handler function to be invoked when BOSH connection is connecting.
4538          * @private
4539          */
4540         _onConnectingHandler,
4541 
4542         /**
4543          * Handler function to be invoked when BOSH connection is connected
4544          * @private
4545          */
4546         _onConnectHandler,
4547 
4548         /**
4549          * Handler function to be invoked when BOSH connection is disconnecting.
4550          * @private
4551          */
4552         _onDisconnectingHandler,
4553 
4554         /**
4555          * Handler function to be invoked when the BOSH is disconnected.
4556          * @private
4557          */
4558         _onDisconnectHandler,
4559 
4560         /**
4561          * Handler function to be invoked when the BOSH is reconnecting.
4562          * @private
4563          */
4564         _onReconnectingHandler,
4565         
4566         /**
4567          * Handler function to be invoked when the BOSH is unloading.
4568          * @private
4569          */
4570         _onUnloadingHandler,
4571 
4572         /**
4573          * Contains a cache of the latest connection info containing the current
4574          * state of the BOSH connection and the resource ID.
4575          * @private
4576          */
4577         _connInfo,
4578 
4579         /**
4580          * Keeps track of all the objects that need to be refreshed when we recover
4581          * due to our resilient connection. Only objects that we subscribe to will
4582          * be added to this list.
4583          * @private
4584          */
4585         _refreshList = [],
4586 
4587         /**
4588          * @private
4589          * Centralized logger.log method for external logger
4590          * @param {String} msg
4591          *     Message to log
4592          */
4593         _log = function (msg) {
4594             // If the external logger throws up, it stops here.
4595             try {
4596                 if (_logger.log) {
4597                     _logger.log("[ClientServices] " + msg);
4598                 }
4599             } catch (e) { }
4600         },
4601 
4602         /**
4603          * Go through each object in the _refreshList and call its refresh() function
4604          * @private
4605          */
4606         _refreshObjects = function () {
4607             var i;
4608 
4609             // wipe out the explicit subscription list before we refresh objects
4610             if (_publisher) {
4611                 _publisher.wipeout();
4612             }
4613 
4614             // refresh each item in the refresh list
4615             for (i = _refreshList.length - 1; i >= 0; i -= 1) {
4616                 _log("Refreshing " + _refreshList[i].getRestUrl());
4617                 _refreshList[i].refresh(10);
4618             }
4619         },
4620 
4621         /**
4622          * Handler to process connection info publishes.
4623          * @param {Object} data
4624          *     The connection info data object.
4625          * @param {String} data.status
4626          *     The BOSH connection status.
4627          * @param {String} data.resourceID
4628          *     The resource ID for the connection.
4629          * @private
4630          */
4631         _connInfoHandler =  function (data) {
4632 
4633             //Invoke registered handler depending on status received. Due to the
4634             //request topic where clients can make request for the Master to publish
4635             //the connection info, there is a chance that duplicate connection info
4636             //events may be sent, so ensure that there has been a state change
4637             //before invoking the handlers.
4638             if (_connInfo === undefined || _connInfo.status !== data.status) {
4639                 _connInfo = data;
4640                 switch (data.status) {
4641                 case _STATUS.CONNECTING:
4642                     if (_isMaster && _onConnectingHandler) {
4643                         _onConnectingHandler();
4644                     }
4645                     break;
4646                 case _STATUS.CONNECTED:
4647                     if ((_isMaster || !_failoverMode) && _onConnectHandler) {
4648                         _onConnectHandler();
4649                     }
4650                     break;
4651                 case _STATUS.DISCONNECTED:
4652                     if (_isMaster && _onDisconnectHandler) {
4653                         _onDisconnectHandler();
4654                     }
4655                     break;
4656                 case _STATUS.DISCONNECTED_CONFLICT:
4657                     if (_isMaster && _onDisconnectHandler) {
4658                         _onDisconnectHandler("conflict");
4659                     }
4660                     break;
4661                 case _STATUS.DISCONNECTED_UNAUTHORIZED:
4662                     if (_isMaster && _onDisconnectHandler) {
4663                         _onDisconnectHandler("unauthorized");
4664                     }
4665                     break;
4666                 case _STATUS.DISCONNECTING:
4667                     if (_isMaster && _onDisconnectingHandler) {
4668                         _onDisconnectingHandler();
4669                     }
4670                     break;
4671                 case _STATUS.RECONNECTING:
4672                     if (_isMaster && _onReconnectingHandler) {
4673                         _onReconnectingHandler();
4674                     }
4675                     break;
4676                 case _STATUS.UNLOADING:
4677                     if (_isMaster && _onUnloadingHandler) {
4678                         _onUnloadingHandler();
4679                     }
4680                     break;
4681                 case _STATUS.FAILING:
4682                     if (!_isMaster) {
4683                         // Stop
4684                         _failoverMode = true;
4685                         if (_onDisconnectHandler) {
4686                             _onDisconnectHandler();
4687                         }
4688                     }
4689                     break;
4690                 case _STATUS.RECOVERED:
4691                     if (!_isMaster) {
4692                         _failoverMode = false;
4693                         if (_onConnectHandler) {
4694                             _onConnectHandler();
4695                         }
4696                     }
4697                     // Whenever we are recovered, we need to refresh any objects
4698                     // that are stored.
4699                     _refreshObjects();
4700                     break;
4701                 }
4702             }
4703         },
4704 
4705         /**
4706          * Ensure that ClientServices have been inited.
4707          * @private
4708          */
4709         _isInited = function () {
4710             if (!_inited) {
4711                 throw new Error("ClientServices needs to be inited.");
4712             }
4713         },
4714 
4715         /**
4716          * Have the client become the Master by initiating a tunnel to a shared
4717          * event BOSH connection. The Master is responsible for publishing all
4718          * events to the pubsub infrastructure.
4719          * @private
4720          */
4721         _becomeMaster = function () {
4722             _tunnel = new MasterTunnel(_config.host, _config.scheme);
4723             _publisher = new MasterPublisher(_tunnel, _hub);
4724             _tunnel.init(_config.id, _config.password, _config.xmppDomain, _config.pubsubDomain, _config.resource);
4725             _isMaster = true;
4726         },
4727 
4728         /**
4729          * Make a request to the request channel to have the Master publish the
4730          * connection info object.
4731          * @private
4732          */
4733         _makeConnectionInfoReq = function () {
4734             var data = {
4735                 type: "ConnectionInfoReq",
4736                 data: {},
4737                 invokeID: (new Date()).getTime()
4738             };
4739             _hub.publish(_topics.REQUESTS, data);
4740         },
4741 
4742         /**
4743          * Utility method to register a handler which is associated with a
4744          * particular connection status.
4745          * @param {String} status
4746          *     The connection status string.
4747          * @param {Function} handler
4748          *     The handler to associate with a particular connection status.
4749          * @throws {Error}
4750          *     If the handler provided is not a function.
4751          * @private
4752          */
4753         _registerHandler = function (status, handler) {
4754             if (typeof handler === "function") {
4755                 if (_connInfo && _connInfo.status === status) {
4756                     handler();
4757                 }
4758                 switch (status) {
4759                 case _STATUS.CONNECTING:
4760                     _onConnectingHandler = handler;
4761                     break;
4762                 case _STATUS.CONNECTED:
4763                     _onConnectHandler = handler;
4764                     break;
4765                 case _STATUS.DISCONNECTED:
4766                     _onDisconnectHandler = handler;
4767                     break;
4768                 case _STATUS.DISCONNECTING:
4769                     _onDisconnectingHandler = handler;
4770                     break;
4771                 case _STATUS.RECONNECTING:
4772                     _onReconnectingHandler = handler;
4773                     break;
4774                 case _STATUS.UNLOADING:
4775                     _onUnloadingHandler = handler;
4776                     break;
4777                 }
4778 
4779             } else {
4780                 throw new Error("Callback is not a function");
4781             }
4782         };
4783 
4784         return {
4785 
4786             /**
4787              * @private
4788              * Adds an item to the list to be refreshed upon reconnect
4789              * @param {RestBase} object - rest object to be refreshed
4790              */
4791             addToRefreshList: function (object) {
4792                 _refreshList.push(object);
4793             },
4794 
4795             /**
4796              * @private
4797              * Removes the given item from the refresh list
4798              * @param  {RestBase} object - rest object to be removed
4799              */
4800             removeFromRefreshList: function (object) {
4801                 var i;
4802                 for (i = _refreshList.length - 1; i >= 0; i -= 1) {
4803                     if (_refreshList[i] === object) {
4804                         _refreshList.splice(i, 1);
4805                         break;
4806                     }
4807                 }
4808             },
4809 
4810             /**
4811              * @private
4812              * The location of the tunnel HTML URL.
4813              * @returns {String}
4814              *     The location of the tunnel HTML URL.
4815              */
4816             getTunnelURL: function () {
4817                 return _tunnel.getTunnelURL();            
4818             },
4819             
4820             /**
4821              * @private
4822              * Indicates whether the tunnel frame is loaded.
4823              * @returns {Boolean}
4824              *     True if the tunnel frame is loaded, false otherwise.
4825              */
4826             isTunnelLoaded: function () {
4827                 return _tunnel.isTunnelLoaded();            
4828             },
4829             
4830             /**
4831              * @private
4832              * Indicates whether the ClientServices instance is a Master.
4833              * @returns {Boolean}
4834              *     True if this instance of ClientServices is a Master, false otherwise.
4835              */
4836             isMaster: function () {
4837                 return _isMaster;
4838             },
4839 
4840             /**
4841              * @private
4842              * Get the resource ID. An ID is only available if the BOSH connection has
4843              * been able to connect successfully.
4844              * @returns {String}
4845              *     The resource ID string. Null if the BOSH connection was never
4846              *     successfully created and/or the resource ID has not been associated.
4847              */
4848             getResourceID: function () {
4849                 if (_connInfo !== undefined) {
4850                     return _connInfo.resourceID;
4851                 }
4852                 return null;
4853             },
4854             
4855             /*
4856             getHub: function () {
4857                 return _hub;
4858             },
4859         */
4860             /**
4861              * @private
4862              * Add a callback to be invoked when the BOSH connection is attempting
4863              * to connect. If the connection is already trying to connect, the
4864              * callback will be invoked immediately.
4865              * @param {Function} handler
4866              *      An empty param function to be invoked on connecting. Only one
4867              *      handler can be registered at a time. Handlers already registered
4868              *      will be overwritten.
4869              */
4870             registerOnConnectingHandler: function (handler) {
4871                 _registerHandler(_STATUS.CONNECTING, handler);
4872             },
4873 
4874             /**
4875              * @private
4876              * Removes the on connecting callback that was registered.
4877              */
4878             unregisterOnConnectingHandler: function () {
4879                 _onConnectingHandler = undefined;
4880             },
4881 
4882             /**
4883              * Add a callback to be invoked when all of the following conditions are met:
4884              * <ul>
4885              *   <li>When Finesse goes IN_SERVICE</li>
4886              *   <li>The BOSH connection is established</li>
4887              *   <li>The Finesse user presence becomes available</li>
4888              * </ul>
4889              * If all these conditions are met at the time this function is called, then
4890              * the handler will be invoked immediately.
4891              * @param {Function} handler
4892              *      An empty param function to be invoked on connect. Only one handler
4893              *      can be registered at a time. Handlers already registered will be
4894              *      overwritten.
4895              * @example
4896              *      finesse.clientservices.ClientServices.registerOnConnectHandler(gadget.myCallback);
4897              */
4898             registerOnConnectHandler: function (handler) {
4899                 _registerHandler(_STATUS.CONNECTED, handler);
4900             },
4901 
4902             /**
4903              * @private
4904              * Removes the on connect callback that was registered.
4905              */
4906             unregisterOnConnectHandler: function () {
4907                 _onConnectHandler = undefined;
4908             },
4909 
4910             /**
4911              * Add a callback to be invoked when any of the following occurs:
4912              * <ul>
4913              *   <li>Finesse is no longer IN_SERVICE</li>
4914              *   <li>The BOSH connection is lost</li>
4915              *   <li>The presence of the Finesse user is no longer available</li>
4916              * </ul>
4917              * If any of these conditions are met at the time this function is
4918              * called, the callback will be invoked immediately.
4919              * @param {Function} handler
4920              *      An empty param function to be invoked on disconnected. Only one
4921              *      handler can be registered at a time. Handlers already registered
4922              *      will be overwritten.
4923              * @example
4924              *      finesse.clientservices.ClientServices.registerOnDisconnectHandler(gadget.myCallback);
4925              */
4926             registerOnDisconnectHandler: function (handler) {
4927                 _registerHandler(_STATUS.DISCONNECTED, handler);
4928             },
4929 
4930             /**
4931              * @private
4932              * Removes the on disconnect callback that was registered.
4933              */
4934             unregisterOnDisconnectHandler: function () {
4935                 _onDisconnectHandler = undefined;
4936             },
4937 
4938             /**
4939              * @private
4940              * Add a callback to be invoked when the BOSH is currently disconnecting. If
4941              * the connection is already disconnecting, invoke the callback immediately.
4942              * @param {Function} handler
4943              *      An empty param function to be invoked on disconnected. Only one
4944              *      handler can be registered at a time. Handlers already registered
4945              *      will be overwritten.
4946              */
4947             registerOnDisconnectingHandler: function (handler) {
4948                 _registerHandler(_STATUS.DISCONNECTING, handler);
4949             },
4950 
4951             /**
4952              * @private
4953              * Removes the on disconnecting callback that was registered.
4954              */
4955             unregisterOnDisconnectingHandler: function () {
4956                 _onDisconnectingHandler = undefined;
4957             },
4958 
4959             /**
4960              * @private
4961              * Add a callback to be invoked when the BOSH connection is attempting
4962              * to connect. If the connection is already trying to connect, the
4963              * callback will be invoked immediately.
4964              * @param {Function} handler
4965              *      An empty param function to be invoked on connecting. Only one
4966              *      handler can be registered at a time. Handlers already registered
4967              *      will be overwritten.
4968              */
4969             registerOnReconnectingHandler: function (handler) {
4970                 _registerHandler(_STATUS.RECONNECTING, handler);
4971             },
4972 
4973             /**
4974              * @private
4975              * Removes the on reconnecting callback that was registered.
4976              */
4977             unregisterOnReconnectingHandler: function () {
4978                 _onReconnectingHandler = undefined;
4979             },
4980             
4981             /**
4982              * @private
4983              * Add a callback to be invoked when the BOSH connection is unloading
4984              * 
4985              * @param {Function} handler
4986              *      An empty param function to be invoked on connecting. Only one
4987              *      handler can be registered at a time. Handlers already registered
4988              *      will be overwritten.
4989              */
4990             registerOnUnloadingHandler: function (handler) {
4991                 _registerHandler(_STATUS.UNLOADING, handler);
4992             },
4993             
4994             /**
4995              * @private
4996              * Removes the on unloading callback that was registered.
4997              */
4998             unregisterOnUnloadingHandler: function () {
4999                 _onUnloadingHandler = undefined;
5000             },
5001 
5002             /**
5003              * @private
5004              * Proxy method for gadgets.io.makeRequest. The will be identical to gadgets.io.makeRequest
5005              * ClientServices will mixin the BASIC Auth string, locale, and host, since the
5006              * configuration is encapsulated in here anyways.
5007              * This removes the dependency
5008              * @param {String} url
5009              *     The relative url to make the request to (the host from the passed in config will be
5010              *     appended). It is expected that any encoding to the URL is already done.
5011              * @param {Function} handler
5012              *     Callback handler for makeRequest to invoke when the response returns.
5013              *     Completely passed through to gadgets.io.makeRequest
5014              * @param {Object} params
5015              *     The params object that gadgets.io.makeRequest expects. Authorization and locale
5016              *     headers are mixed in.
5017              */
5018             makeRequest: function (url, handler, params) {
5019                 var requestedScheme, scheme = "http";
5020                 
5021                 // ClientServices needs to be initialized with a config for restHost, auth, and locale
5022                 _isInited();
5023                 
5024                 // Allow mixin of auth and locale headers
5025                 params = params || {};
5026 
5027                 // Override refresh interval to 0 instead of default 3600 as a way to workaround makeRequest 
5028                 // using GET http method because then the params are added to the url as query params, which 
5029                 // exposes the authorization string in the url. This is a placeholder until oauth comes in
5030                 params[gadgets.io.RequestParameters.REFRESH_INTERVAL] = params[gadgets.io.RequestParameters.REFRESH_INTERVAL] || 0;
5031                 
5032                 params[gadgets.io.RequestParameters.HEADERS] = params[gadgets.io.RequestParameters.HEADERS] || {};
5033                 
5034                 // Add Basic auth to request header
5035                 params[gadgets.io.RequestParameters.HEADERS].Authorization = "Basic " + _config.authorization;
5036                 //Locale
5037                 params[gadgets.io.RequestParameters.HEADERS].locale = _config.locale;
5038 
5039                 //Allow clients to override the scheme:
5040                 //  - If not specified  => we use HTTP
5041                 //  - If null specified => we use _config.scheme
5042                 //  - Otherwise         => we use whatever they provide
5043                 requestedScheme = params.SCHEME; 
5044                 if (!(requestedScheme === undefined || requestedScheme === "undefined")) {
5045                     if (requestedScheme === null) {
5046                        scheme = _config.scheme;
5047                     } else {
5048                        scheme = requestedScheme;
5049                     }
5050                 }
5051                 
5052                 _log("RequestedScheme: " + requestedScheme + "; Scheme: " + scheme);
5053                 gadgets.io.makeRequest(encodeURI(scheme + "://" + _config.restHost + ":" + _config.localhostPort) + url, handler, params);
5054             },
5055 
5056             /**
5057              * @private
5058              * Utility function to make a subscription to a particular topic. Only one
5059              * callback function is registered to a particular topic at any time.
5060              * @param {String} topic
5061              *     The full topic name. The topic name should follow the OpenAjax
5062              *     convention using dot notation (ex: finesse.api.User.1000).
5063              * @param {Function} callback
5064              *     The function that should be invoked with the data when an event
5065              *     is delivered to the specific topic.
5066              * @returns {Boolean}
5067              *     True if the subscription was made successfully and the callback was
5068              *     been registered. False if the subscription already exist, the
5069              *     callback was not overwritten.
5070              */
5071             subscribe: function (topic, callback, disableDuringFailover) {
5072                 _isInited();
5073 
5074                 //Ensure that the same subscription isn't made twice.
5075                 if (!_subscriptionID[topic]) {
5076                     //Store the subscription ID using the topic name as the key.
5077                     _subscriptionID[topic] = _hub.subscribe(topic,
5078                         //Invoke the callback just with the data object.
5079                         function (topic, data) {
5080                             if (!disableDuringFailover || _isMaster || !_failoverMode) {
5081                                 // Halt all intermediate event processing while the master instance attempts to rebuild the connection. This only occurs:
5082                                 // - For RestBase objects (which pass the disableDuringFailover flag), so that intermediary events while recovering are hidden away until everything is all good
5083                                 //    - We shouldn't be halting anything else because we have infrastructure requests that use the hub pub sub
5084                                 // - Master instance will get all events regardless, because it is responsible for managing failover
5085                                 // - If we are not in a failover mode, everything goes
5086                                 // _refreshObjects will reconcile anything that was missed once we are back in action
5087                                 callback(data);
5088                             } 
5089                         });
5090                     return true;
5091                 }
5092                 return false;
5093             },
5094 
5095             /**
5096              * @private
5097              * Unsubscribe from a particular topic.
5098              * @param {String} topic
5099              *     The full topic name.
5100              */
5101             unsubscribe: function (topic) {
5102                 _isInited();
5103 
5104                 //Unsubscribe from the topic using the subscription ID recorded when
5105                 //the subscription was made, then delete the ID from data structure.
5106                 if (_subscriptionID[topic]) {
5107                     _hub.unsubscribe(_subscriptionID[topic]);
5108                     delete _subscriptionID[topic];
5109                 }
5110             },
5111 
5112             /**
5113              * @private
5114              * Make a request to the request channel to have the Master subscribe
5115              * to a node.
5116              * @param {String} node
5117              *     The node to subscribe to.
5118              */
5119             subscribeNode: function (node, handler) {
5120                 if (handler && typeof handler !== "function") {
5121                     throw new Error("ClientServices.subscribeNode: handler is not a function");
5122                 }
5123                 
5124                 // Construct the request to send to MasterPublisher through the OpenAjax Hub
5125                 var data = {
5126                     type: "SubscribeNodeReq",
5127                     data: {node: node},
5128                     invokeID: _util.generateUUID()
5129                 },
5130                 responseTopic = _topics.RESPONSES + "." + data.invokeID,
5131                 _this = this;
5132 
5133                 // We need to first subscribe to the response channel
5134                 this.subscribe(responseTopic, function (rsp) {
5135                     // Since this channel is only used for this singular request,
5136                     // we are not interested anymore.
5137                     // This is also critical to not leaking memory by having OpenAjax
5138                     // store a bunch of orphaned callback handlers that enclose on
5139                     // our entire ClientServices singleton
5140                     _this.unsubscribe(responseTopic);
5141                     if (handler) {
5142                         handler(data.invokeID, rsp);
5143                     }
5144                 });
5145                 // Then publish the request on the request channel
5146                 _hub.publish(_topics.REQUESTS, data);
5147             },
5148 
5149             /**
5150              * @private
5151              * Make a request to the request channel to have the Master unsubscribe
5152              * from a node.
5153              * @param {String} node
5154              *     The node to unsubscribe from.
5155              */
5156             unsubscribeNode: function (node, subid, handler) {
5157                 if (handler && typeof handler !== "function") {
5158                     throw new Error("ClientServices.unsubscribeNode: handler is not a function");
5159                 }
5160                 
5161                 // Construct the request to send to MasterPublisher through the OpenAjax Hub
5162                 var data = {
5163                     type: "UnsubscribeNodeReq",
5164                     data: {
5165                         node: node,
5166                         subid: subid
5167                     },
5168                     invokeID: _util.generateUUID()
5169                 },
5170                 responseTopic = _topics.RESPONSES + "." + data.invokeID,
5171                 _this = this;
5172 
5173                 // We need to first subscribe to the response channel
5174                 this.subscribe(responseTopic, function (rsp) {
5175                     // Since this channel is only used for this singular request,
5176                     // we are not interested anymore.
5177                     // This is also critical to not leaking memory by having OpenAjax
5178                     // store a bunch of orphaned callback handlers that enclose on
5179                     // our entire ClientServices singleton
5180                     _this.unsubscribe(responseTopic);
5181                     if (handler) {
5182                         handler(rsp);
5183                     }
5184                 });
5185                 // Then publish the request on the request channel
5186                 _hub.publish(_topics.REQUESTS, data);
5187             },
5188             
5189             /**
5190              * @private
5191              * Make a request to the request channel to have the Master connect to the XMPP server via BOSH
5192              */
5193             makeConnectionReq : function () {
5194                 // Disallow others (non-masters) from administering BOSH connections that are not theirs
5195                 if (_isMaster && _publisher) {
5196                     _publisher.connect(_config.id, _config.password, _config.xmppDomain);
5197                 } else {
5198                     _log("F403: Access denied. Non-master ClientService instances do not have the clearance level to make this request: makeConnectionReq");
5199                 }
5200             },
5201         
5202             /**
5203              * @private
5204              * Set's the global logger for this Client Services instance.
5205              * @param {Object} logger
5206              *     Logger object with the following attributes defined:<ul>
5207              *         <li><b>log:</b> function (msg) to simply log a message
5208              *     </ul>
5209              */
5210             setLogger: function (logger) {
5211                 // We want to check the logger coming in so we don't have to check every time it is called.
5212                 if (logger && typeof logger === "object" && typeof logger.log === "function") {
5213                     _logger = logger;
5214                 } else {
5215                     // We are resetting it to an empty object so that _logger.log in .log is falsy.
5216                     _logger = {};
5217                 }
5218             },
5219             
5220             /**
5221              * @private
5222              * Centralized logger.log method for external logger
5223              * @param {String} msg
5224              *     Message to log
5225              */
5226             log: _log,
5227 
5228             /**
5229              * @class
5230              * Allow clients to make Finesse API requests and consume Finesse events by
5231              * calling a set of exposed functions. The Services layer will do the dirty
5232              * work of establishing a shared BOSH connection (for designated Master
5233              * modules), consuming events for client subscriptions, and constructing API
5234              * requests.
5235              * 
5236              * @constructs
5237              */
5238             _fakeConstuctor: function () {
5239                 /* This is here so we can document init() as a method rather than as a constructor. */
5240             },
5241             
5242             /**
5243              * Initiates the Client Services with the specified config parameters.
5244              * Enabling the Client Services as Master will trigger the establishment
5245              * of a BOSH event connection.
5246              * @param {Object} config
5247              *     Configuration object containing properties used for making REST requests:<ul>
5248              *         <li><b>host:</b> The Finesse server IP/host as reachable from the browser
5249              *         <li><b>restHost:</b> The Finesse API IP/host as reachable from the gadget container
5250              *         <li><b>id:</b> The ID of the user. This is an optional param as long as the
5251              *         appropriate authorization string is provided, otherwise it is
5252              *         required.</li>
5253              *         <li><b>password:</b> The password belonging to the user. This is an optional param as
5254              *         long as the appropriate authorization string is provided,
5255              *         otherwise it is required.</li>
5256              *         <li><b>authorization:</b> The base64 encoded "id:password" authentication string. This
5257              *         param is provided to allow the ability to hide the password
5258              *         param. If provided, the id and the password extracted from this
5259              *         string will be used over the config.id and config.password.</li>
5260              *     </ul>
5261              * @throws {Error} If required constructor parameter is missing.
5262              * @example
5263              *      finesse.clientservices.ClientServices.init(finesse.gadget.Config);
5264              */
5265             init: function (config) {
5266                 if (!_inited) {
5267                     //Validate the properties within the config object if one is provided.
5268                     if (!(typeof config === "object" &&
5269                          typeof config.host === "string" && config.host.length > 0 && config.restHost && 
5270                          (typeof config.authorization === "string" ||
5271                                  (typeof config.id === "string" &&
5272                                          typeof config.password === "string")))) {
5273                         throw new Error("Config object contains invalid properties.");
5274                     }
5275 
5276                     // Initialize configuration
5277                     _config = config;
5278 
5279                     // Set shortcuts
5280                     _util = Utilities;
5281                     _topics = Topics;
5282                     
5283                     //TODO: document when this is properly supported
5284                     // Allows hub and io dependencies to be passed in. Currently only used for unit tests.
5285                     _hub = config.hub || gadgets.Hub;
5286                     _io = config.io || gadgets.io;
5287 
5288                     //If the authorization string is provided, then use that to
5289                     //extract the ID and the password. Otherwise use the ID and
5290                     //password from the respective ID and password params.
5291                     if (_config.authorization) {
5292                         var creds = _util.getCredentials(_config.authorization);
5293                         _config.id = creds.id;
5294                         _config.password = creds.password;
5295                     }
5296                     else {
5297                         _config.authorization = _util.b64Encode(
5298                                 _config.id + ":" + _config.password);
5299                     }
5300 
5301                     _inited = true;
5302 
5303                     if (_hub) {
5304                         //Subscribe to receive connection information. Since it is possible that
5305                         //the client comes up after the Master comes up, the client will need
5306                         //to make a request to have the Master send the latest connection info.
5307                         //It would be possible that all clients get connection info again.
5308                         this.subscribe(_topics.EVENTS_CONNECTION_INFO, _connInfoHandler);
5309                         _makeConnectionInfoReq();
5310                     }
5311                 }
5312 
5313                 //Return the CS object for object chaining.
5314                 return this;
5315             },
5316 
5317             /**
5318              * @private
5319              * Initializes the BOSH component of this ClientServices instance. This establishes
5320              * the BOSH connection and will trigger the registered handlers as the connection
5321              * status changes respectively:<ul>
5322              *     <li>registerOnConnectingHandler</li>
5323              *     <li>registerOnConnectHandler</li>
5324              *     <li>registerOnDisconnectHandler</li>
5325              *     <li>registerOnDisconnectingHandler</li>
5326              *     <li>registerOnReconnectingHandler</li>
5327              *     <li>registerOnUnloadingHandler</li>
5328              * <ul>
5329              *
5330              * @param {Object} config
5331              *     An object containing the following (optional) handlers for the request:<ul>
5332              *         <li><b>xmppDomain:</b> {String} The domain of the XMPP server. Available from the SystemInfo object.
5333              *         This is used to construct the JID: user@domain.com</li>
5334              *         <li><b>pubsubDomain:</b> {String} The pub sub domain where the pub sub service is running.
5335              *         Available from the SystemInfo object.
5336              *         This is used for creating or removing subscriptions.</li>
5337              *         <li><b>resource:</b> {String} The resource to connect to the notification server with.</li>
5338              *     </ul>
5339              */
5340             initBosh: function (config) {
5341                 //Validate the properties within the config object if one is provided.
5342                 if (!(typeof config === "object" && typeof config.xmppDomain === "string" && typeof config.pubsubDomain === "string")) {
5343                     throw new Error("Config object contains invalid properties.");
5344                 }
5345                 
5346                 // Mixin the required information for establishing the BOSH connection
5347                 _config.xmppDomain = config.xmppDomain;
5348                 _config.pubsubDomain = config.pubsubDomain;
5349                 _config.resource = config.resource;
5350                 
5351                 //Initiate Master launch sequence
5352                 _becomeMaster(); 
5353             },
5354 
5355             /**
5356              * @private
5357              * Sets the failover mode to either FAILING or RECOVERED. This will only occur in the master instance and is meant to be
5358              * used by a stereotypical failover monitor type module to notify non-master instances (i.e. in gadgets)
5359              * @param {Object} failoverMode
5360              *     true if failing, false or something falsy when recovered
5361              */
5362             setFailoverMode: function (failoverMode) {
5363                 if (_isMaster) {
5364                     _hub.publish(_topics.EVENTS_CONNECTION_INFO, {
5365                         status: (failoverMode ? _STATUS.FAILING : _STATUS.RECOVERED)
5366                     });
5367                 }
5368             },
5369 
5370             /**
5371              * @private
5372              * Private accessor used to inject mocked private dependencies for unit testing
5373              */
5374             _getTestObj: function () {
5375                 return {
5376                     setPublisher: function (publisher) {
5377                         _publisher = publisher;
5378                     }
5379                 };
5380             }
5381         };
5382     }());
5383      
5384     window.finesse = window.finesse || {};
5385     window.finesse.clientservices = window.finesse.clientservices || {};
5386     window.finesse.clientservices.ClientServices = ClientServices;
5387     
5388     return ClientServices;
5389 
5390 });
5391 
5392 /**
5393  * The following comment prevents JSLint errors concerning undefined global variables.
5394  * It tells JSLint that these identifiers are defined elsewhere.
5395  */
5396 /*jslint bitwise:true, browser:true, nomen:true, regexp:true, sloppy:true, white:true */
5397 
5398 /** The following comment is to prevent jslint errors about 
5399  * using variables before they are defined.
5400  */
5401 /*global Handlebars */
5402 
5403 /**
5404  * JavaScript class to implement common notification
5405  *               functionality.
5406  * 
5407  * @requires Class
5408  * @requires finesse.FinesseBase
5409  */
5410 /** @private */
5411 define('restservices/Notifier',[
5412     'FinesseBase',
5413     'clientservices/ClientServices'
5414 ],
5415 function (FinesseBase, ClientServices) {
5416     var Notifier = FinesseBase.extend({
5417 		/**
5418          * Initializes the notifier object.
5419          */
5420         init : function () {
5421             this._super();
5422             this._listenerCallback = [];
5423         },
5424 
5425         /**
5426          * Add a listener.
5427          * 
5428          * @param callback_function
5429          * @param scope
5430          *            is the callback function to add
5431          */
5432         addListener : function (callback_function, scope) {
5433             var len = this._listenerCallback.length, i, cb, add = true;
5434             for (i = 0; i < len; i += 1) {
5435                 cb = this._listenerCallback[i].callback;
5436                 if (cb === callback_function) {
5437                     // this callback already exists
5438                     add = false;
5439                     break;
5440                 }
5441             }
5442             if (add) {
5443                 this._listenerCallback.push({ "callback": this._isAFunction(callback_function), "scope": (scope || window) });
5444             }            
5445         },
5446 
5447         /**
5448          * Remove a listener.
5449          * 
5450          * @param callback_function
5451          *            is the callback function to remove
5452          * @return {Boolean} true if removed
5453          */
5454         removeListener : function (callback_function) {
5455 
5456             var result = false, len = this._listenerCallback.length, i, cb;
5457             for (i = len - 1; i >= 0; i -=1) {
5458                 cb = this._listenerCallback[i].callback;
5459                 if (cb === callback_function) {
5460                     this._listenerCallback[i] = undefined;
5461                     this._listenerCallback.splice(i, 1);
5462                     result = true;
5463                     break;
5464                 }
5465             }
5466             
5467             return result;
5468         },
5469 
5470         /**
5471 	 * Removes all listeners
5472 	 * @return {undefined}
5473 	 */
5474 	reset: function () {
5475 		this._listenerCallback = [];
5476 	},
5477 
5478 	/**
5479          * Notify all listeners.
5480          * 
5481          * @param obj
5482          *            is the object that has changed
5483          */
5484         notifyListeners : function (obj) {
5485             var len = this._listenerCallback.length, i, callbackFunction, scope;
5486 
5487             for (i = 0; i < len; i += 1) {
5488                 // Be sure that one bad callback does not prevent other listeners
5489                 // from receiving.
5490                 try {
5491                     callbackFunction = this._listenerCallback[i].callback;
5492                     scope = this._listenerCallback[i].scope;
5493                     if (typeof callbackFunction === 'function') {
5494                         callbackFunction.call(scope, obj);
5495                     }
5496                 } catch (err) {
5497                     ClientServices.log("Exception caught: " + err);
5498                 }
5499             }
5500         },
5501 
5502         /**
5503          * Gets a copy of the listeners.
5504          * @return changeListenerCopy (array of callbacks)
5505          */
5506         getListeners : function () {
5507             var changeListenerCopy = [], len = this._listenerCallback.length, i;
5508 
5509             for (i = 0; i < len; i += 1) {
5510                 changeListenerCopy.push(this._listenerCallback[i].callback);
5511             }
5512 
5513             return changeListenerCopy;
5514         },
5515         
5516         /**
5517          * Verifies that the handler is function.
5518          * @param handler to verify
5519          * @return the handler 
5520          * @throws Error if not a function
5521          */
5522         _isAFunction : function (handler) {
5523             if (handler === undefined || typeof handler === "function") {
5524                 return handler;
5525             } else {
5526                 throw new Error("handler must be a function");
5527             }
5528         }
5529 	});
5530 	
5531 	window.finesse = window.finesse || {};
5532 	window.finesse.restservices = window.finesse.restservices || {};
5533 	window.finesse.restservices.Notifier = Notifier;
5534 
5535 	/** @namespace JavaScript classes and methods that represent REST objects and collections. */
5536     finesse.restservices = finesse.restservices || {};
5537 	
5538     return Notifier;
5539 });
5540 
5541 /**
5542  * JavaScript base object that all REST objects should inherit
5543  * from because it encapsulates and provides the common functionality that
5544  * all REST objects need.
5545  *
5546  * @requires finesse.clientservices.ClientServices
5547  * @requires Class
5548  */
5549 
5550 /** @private */
5551 define('restservices/RestBase',[
5552     "FinesseBase",
5553     "utilities/Utilities",
5554     "restservices/Notifier",
5555     "clientservices/ClientServices",
5556     "clientservices/Topics"
5557 ],
5558 function (FinesseBase, Utilities, Notifier, ClientServices, Topics) {
5559     
5560     var RestBase = FinesseBase.extend(/** @lends finesse.restservices.RestBase.prototype */{
5561 
5562         doNotLog: false,        
5563 
5564         /**
5565          * Used by _processUpdate() and restRequest().
5566          * Maps requestIds to object-wrapped callbacks passed to restRequest(),
5567          * so that one of the callbacks can be fired when a corresponding event is
5568          * received inside _processUpdate().
5569          * @private
5570          */
5571         _pendingCallbacks: {},
5572         
5573         /**
5574          * Gets the REST class for the current object.  This object throws an
5575          * exception because subtype must implement.
5576          * @throws {Error} because subtype must implement
5577          * @private
5578          */
5579         getRestClass: function () {
5580             throw new Error("getRestClass(): Not implemented in subtype.");
5581         },
5582 
5583         /**
5584          * Gets the REST type for the current object.  This object throws an
5585          * exception because subtype must implement.
5586          * @throws {Error} because subtype must implement.
5587          * @private
5588          */
5589         getRestType: function () {
5590             throw new Error("getRestType(): Not implemented in subtype.");
5591         },
5592 
5593         /**
5594          * Gets the node path for the current object.  This object throws an
5595          * exception because subtype must implement.
5596          * @throws {Error} because subtype must implement.
5597          * @private
5598          */
5599         getXMPPNodePath: function () {
5600             throw new Error("getXMPPNodePath(): Not implemented in subtype.");
5601         },
5602         
5603         /**
5604          * Boolean function that specifies whether the REST object supports
5605          * requests. True by default. Subclasses should override if false.
5606          * @private
5607          */
5608         supportsRequests: true,
5609 
5610         /**
5611          * Boolean function that specifies whether the REST object supports
5612          * subscriptions. True by default. Subclasses should override if false.
5613          * @private
5614          */
5615         supportsSubscriptions: true,
5616         
5617         /**
5618          * Boolean function that specifies whether the REST object should retain
5619          * a copy of the REST response. False by default. Subclasses should override if true.
5620          * @private
5621          */
5622         keepRestResponse: false,
5623 
5624         /**
5625          * Boolean function that specifies whether the REST object explicitly
5626          * subscribes. False by default. Subclasses should override if true.
5627          * @private
5628          */
5629         explicitSubscription: false,
5630 
5631         /**
5632          * Boolean function that specifies whether subscribing should be
5633          * automatically done at construction. Defaults to true.
5634          * This be overridden at object construction, not by implementing subclasses
5635          * @private
5636          */
5637         autoSubscribe: true,
5638         
5639         /**
5640          * Private reference to default logger
5641          * @private
5642          */
5643         _logger: {
5644             log: ClientServices.log,
5645             error: ClientServices.log
5646         },
5647 
5648         /**
5649          * @class
5650          * JavaScript representation of a REST object. Also exposes methods to operate
5651          * on the object against the server.  This object is typically extended into individual
5652          * REST Objects (like Dialog, User, etc...), and shouldn't be used directly.
5653          *
5654          * @constructor
5655          * @param {String} id
5656          *     The ID that uniquely identifies the REST object.
5657          * @param {Object} callbacks
5658          *     An object containing callbacks for instantiation and runtime
5659          *     Callback to invoke upon successful instantiation, passes in REST object.
5660          * @param {Function} callbacks.onLoad(this)
5661          *     Callback to invoke upon loading the data for the first time.
5662          * @param {Function} callbacks.onChange(this)
5663          *     Callback to invoke upon successful update object (PUT)
5664          * @param {Function} callbacks.onAdd(this)
5665          *     Callback to invoke upon successful update to add object (POST)
5666          * @param {Function} callbacks.onDelete(this)
5667          *     Callback to invoke upon successful update to delete object (DELETE)
5668          * @param {Function} callbacks.onError(rsp)
5669          *     Callback to invoke on update error (refresh or event)
5670          *     as passed by finesse.restservices.RestBase.restRequest()
5671          *     {
5672          *         status: {Number} The HTTP status code returned
5673          *         content: {String} Raw string of response
5674          *         object: {Object} Parsed object of response
5675          *         error: {Object} Wrapped exception that was caught
5676          *         error.errorType: {String} Type of error that was caught
5677          *         error.errorMessage: {String} Message associated with error
5678          *     }
5679          * @param {RestBase} [restObj]
5680          *     A RestBase parent object which this object has an association with.
5681          * @constructs
5682          */
5683         init: function (options, callbacks, restObj) {
5684             /**
5685               * Initialize the base class
5686               */
5687             var _this = this;
5688 
5689             this._super();
5690 
5691             if (typeof options === "object") {
5692                 this._id = options.id;
5693                 this._restObj = options.parentObj;
5694                 this.autoSubscribe = (options.autoSubscribe === false) ? false : true;
5695                 this.doNotSubscribe = options.doNotSubscribe;
5696                 this.doNotRefresh = this.doNotRefresh || options.doNotRefresh;
5697                 callbacks = {
5698                     onLoad: options.onLoad,
5699                     onChange: options.onChange,
5700                     onAdd: options.onAdd,
5701                     onDelete: options.onDelete,
5702                     onError: options.onError
5703                 };
5704             } else {
5705                 this._id = options;
5706                 this._restObj = restObj;
5707             }
5708             
5709             // Common stuff
5710             
5711             this._data = {};
5712             
5713             //Contains the full rest response to be processed by upper layers if needed
5714             this._restResponse = undefined;
5715 
5716             this._lastUpdate = {};
5717 
5718             this._util = Utilities;
5719 
5720             //Should be correctly initialized in either a window OR gadget context
5721             this._config = finesse.container.Config;
5722 
5723             // Setup all the notifiers - change, load and error.
5724             this._changeNotifier = new Notifier();
5725             this._loadNotifier = new Notifier();
5726             this._addNotifier = new Notifier();
5727             this._deleteNotifier = new Notifier();
5728             this._errorNotifier = new Notifier();
5729 
5730             this._loaded = false;
5731 
5732             // Protect against null dereferencing of options allowing its
5733             // (nonexistent) keys to be read as undefined
5734             callbacks = callbacks || {};
5735 
5736             this.addHandler('load', callbacks.onLoad);
5737             this.addHandler('change', callbacks.onChange);
5738             this.addHandler('add', callbacks.onAdd);
5739             this.addHandler('delete', callbacks.onDelete);
5740             this.addHandler('error', callbacks.onError);
5741 
5742             // Attempt to get the RestType then synchronize
5743             try {
5744                 this.getRestType();
5745 
5746                 // Only subscribe if this REST object supports subscriptions
5747                 // and autoSubscribe was not requested to be disabled as a construction option
5748                 if (this.supportsSubscriptions && this.autoSubscribe && !this.doNotSubscribe) {
5749                     this.subscribe({
5750                         success: function () {
5751                             //TODO: figure out how to use Function.call() or Function.apply() here...
5752                             //this is exactly the same as the below else case other than the scope of "this"
5753                             if (typeof options === "object" && options.data) {
5754                                 if (!_this._processObject(_this._normalize(options.data))) {
5755                                     // notify of error if we fail to construct
5756                                     _this._errorNotifier.notifyListeners(_this);
5757                                 }
5758                             } else {
5759                                 // Only subscribe if this REST object supports requests
5760                                 if (_this.supportsRequests) {
5761                                     _this._synchronize();
5762                                 }
5763                             }
5764                         },
5765                         error: function (err) {
5766                             _this._errorNotifier.notifyListeners(err);
5767                         }
5768                     });
5769                 } else {
5770                     if (typeof options === "object" && options.data) {
5771                         if (!this._processObject(this._normalize(options.data))) {
5772                             // notify of error if we fail to construct
5773                             this._errorNotifier.notifyListeners(this);
5774                         }
5775                     } else {
5776                         // Only subscribe if this REST object supports requests
5777                         if (this.supportsRequests) {
5778                             this._synchronize();
5779                         }
5780                     }
5781                 }
5782 
5783             } catch (err) {
5784                 this._logger.error('id=' + this._id + ': ' + err);
5785             }
5786         },
5787 
5788         /**
5789          * Determines if the object has a particular property.
5790          * @param obj is the object to examine
5791          * @param property is the property to check for
5792          * @returns {Boolean}
5793          */
5794         hasProperty: function (obj, prop) {
5795             return (obj !== null) && (obj.hasOwnProperty(prop));
5796         },
5797 
5798         /**
5799          * Gets a property from the object.
5800          * @param obj is the object to examine
5801          * @param property is the property to get
5802          * @returns {Property Value} or {Null} if not found
5803          */
5804         getProperty: function (obj, property) {
5805             var result = null;
5806 
5807             if (this.hasProperty(obj, property) === false) {
5808                 result = null;
5809             } else {
5810                 result = obj[property];
5811             }
5812             return result;
5813         },
5814 
5815         /**
5816          * Utility to extracts the ID from the specified REST URI. This is with the
5817          * assumption that the ID is always the last element in the URI after the
5818          * "/" delimiter.
5819          * @param {String} restUri
5820          *     The REST uri (i.e. /finesse/api/User/1000).
5821          * @private
5822          */
5823         _extractId: function (restObj) {
5824             var obj, restUri = "", strLoc;
5825             for (obj in restObj) {
5826                 if (restObj.hasOwnProperty(obj)) {
5827                     restUri = restObj[obj].uri;
5828                     break;
5829                 }
5830             }
5831             return Utilities.getId(restUri);
5832         },
5833 
5834         /**
5835          * Gets the data for this object.
5836          * @returns {Object} which is contained in data
5837          */
5838         getData: function () {
5839             return this._data;
5840         },
5841         
5842         /**
5843          * Gets the complete REST response to the request made
5844          * @returns {Object} which is contained in data
5845          * @private
5846          */
5847         getRestResponse: function () {
5848             return this._restResponse;
5849         },
5850 
5851         /**
5852          * The REST URL in which this object can be referenced.
5853          * @return {String}
5854          *     The REST URI for this object.
5855          * @private
5856          */
5857         getRestUrl: function () {
5858             var
5859             restObj = this._restObj,
5860             restUrl = "";
5861 
5862             //Prepend the base REST object if one was provided.
5863             if (restObj instanceof RestBase) {
5864                 restUrl += restObj.getRestUrl();
5865             }
5866             //Otherwise prepend with the default webapp name.
5867             else {
5868                 restUrl += "/finesse/api";
5869             }
5870 
5871             //Append the REST type.
5872             restUrl += "/" + this.getRestType();
5873 
5874             //Append ID if it is not undefined, null, or empty.
5875             if (this._id) {
5876                 restUrl += "/" + this._id;
5877             }
5878             return restUrl;
5879         },
5880 
5881         /**
5882          * Getter for the id of this RestBase
5883          * @returns {String}
5884          *     The id of this RestBase
5885          */
5886         getId: function () {
5887             return this._id;
5888         },
5889 
5890         /**
5891          * Synchronize this object with the server using REST GET request.
5892          * @returns {Object}
5893          *     {
5894          *         abort: {function} Function that signifies the callback handler to NOT process the response of the rest request
5895          *     }
5896          * @private
5897          */
5898         _synchronize: function (retries) {
5899             // Fetch this REST object
5900             if (typeof this._id === "string") {
5901                 var _this = this, isLoaded = this._loaded, _RETRY_INTERVAL = 10 * 1000;
5902 
5903                 return this._doGET(
5904                     {
5905                         success: function (rsp) {
5906                             if (!_this._processResponse(rsp)) {
5907                                 if (retries > 0) {
5908                                     setTimeout(function () {
5909                                         _this._synchronize(retries - 1);
5910                                     }, _RETRY_INTERVAL);
5911                                 } else {
5912                                     _this._errorNotifier.notifyListeners(_this);
5913                                 }
5914                             } else {
5915                                 // If this object was already "loaded" prior to
5916                                 // the _doGET request, then call the
5917                                 // changeNotifier
5918                                 if (isLoaded) {
5919                                     _this._changeNotifier.notifyListeners(_this);
5920                                 }
5921                             }
5922                         },
5923                         error: function (rsp) {
5924                             if (retries > 0) {
5925                                 setTimeout(function () {
5926                                     _this._synchronize(retries - 1);
5927                                 }, _RETRY_INTERVAL);
5928                                 
5929                             } else {
5930                                 _this._errorNotifier.notifyListeners(rsp);
5931                             }
5932                         }
5933                     }
5934                 );
5935 
5936             } else {
5937                 throw new Error("Can't construct a <" + this.getRestType() + "> due to invalid id type.");
5938             }
5939         },
5940 
5941         /**
5942          * Adds an handler to this object.
5943          * If notifierType is 'load' and the object has already loaded, the callback is invoked immediately
5944          * @param {String} notifierType
5945          *     The type of notifier to add to ('load', 'change', 'add', 'delete', 'error')
5946          * @param {Function} callback
5947          *     The function callback to invoke.
5948          * @example
5949          *   // Handler for additions to the Dialogs collection object.  
5950          *   // When Dialog (a RestBase object) is created, add a change handler.
5951          *   _handleDialogAdd = function(dialog) {
5952          *      dialog.addHandler('change', _handleDialogChange);
5953          *   }
5954          */
5955         addHandler: function (notifierType, callback, scope) {
5956             var notifier = null;
5957             try {
5958                 Utilities.validateHandler(callback);
5959 
5960                 notifier = this._getNotifierReference(notifierType);
5961 
5962                 notifier.addListener(callback, scope);
5963                 
5964                 // If load handler is added and object has
5965                 // already been loaded, invoke callback
5966                 // immediately
5967                 if (notifierType === 'load' && this._loaded) {
5968                     callback.call((scope || window), this);
5969                 }
5970             } catch (err) {
5971                 this._logger.error('id=' + this._id + ': ' + err);
5972             }
5973         },
5974 
5975         /**
5976          * Removes a handler from this object.
5977          * @param {String} notifierType
5978          *     The type of notifier to remove ('load', 'change', 'add', 'delete', 'error')
5979          * @param {Function} callback
5980          *     The function to remove.
5981          */
5982         removeHandler: function (notifierType, callback) {
5983             var notifier = null;
5984             try {
5985                 Utilities.validateHandler(callback);
5986 
5987                 notifier = this._getNotifierReference(notifierType);
5988 
5989                 if (typeof(callback) === "undefined")
5990                 {
5991                     // Remove all listeners for the type
5992                     notifier.reset();
5993                 }
5994                 else
5995                 {
5996                     // Remove the specified listener
5997                     finesse.utilities.Utilities.validateHandler(callback);
5998                     notifier.removeListener(callback);
5999                 }
6000             } catch (err) {
6001                 this._logger.error('id=' + this._id + ': ' + err);
6002             }
6003         },
6004 
6005         /**
6006          * Utility method gating any operations that require complete instantiation
6007          * @throws Error
6008          *     If this object was not fully instantiated yet
6009          * @returns {finesse.restservices.RestBase}
6010          *     This RestBase object to allow cascading
6011          */
6012         isLoaded: function () {
6013             if (!this._loaded) {
6014                 throw new Error("Cannot operate on object that is not fully instantiated, use onLoad/onLoadError handlers");
6015             }
6016             return this; // Allow cascading
6017         },
6018 
6019 
6020 
6021         /**
6022          * Force an update on this object. Since an asynchronous GET is performed,
6023          * it is necessary to have an onChange handler registered in order to be
6024          * notified when the response of this returns.
6025          * @param {Integer} retries
6026          *    The number or retry attempts to make.
6027          * @returns {Object}
6028          *     {
6029          *         abort: {function} Function that signifies the callback handler to NOT process the response of the asynchronous request
6030          *     }
6031          */
6032         refresh: function (retries) {
6033             var _this = this;
6034 
6035             if (this.explicitSubscription) {
6036                 this._subscribeNode({
6037                     success: function () {
6038                         //Disallow GETs if object doesn't support it.
6039                         if (!_this.supportsRequests) {
6040                             throw new Error("Object doesn't support request operations.");
6041                         }
6042 
6043                         _this._synchronize(retries);
6044 
6045                         return this; // Allow cascading
6046                     },
6047                     error: function (err) {
6048                         _this._errorNotifier.notifyListeners(err);
6049                     }
6050                 });
6051             } else {
6052                 //Disallow GETs if object doesn't support it.
6053                 if (!this.supportsRequests) {
6054                     throw new Error("Object doesn't support request operations.");
6055                 }
6056 
6057                 return this._synchronize(retries);
6058             }
6059         },
6060 
6061         /**
6062          * Utility method to validate against the known schema of this RestBase
6063          * @param {Object} obj
6064          *     The object to validate
6065          * @returns {Boolean}
6066          *     True if the object fits the schema of this object. This usually
6067          *     means all required keys or nested objects are present.
6068          *     False otherwise.
6069          * @private
6070          */
6071         _validate: function (obj) {
6072             var valid = (typeof obj === "object" && this.hasProperty(obj, this.getRestType()));
6073             if (!valid)
6074             {
6075                 this._logger.error(this.getRestType() + " failed validation! Does your JS REST class return the correct string from getRestType()?");
6076             }
6077             return valid;
6078         },
6079 
6080         /**
6081          * Utility method to fetch this RestBase from the server
6082          * @param {finesse.interfaces.RequestHandlers} handlers
6083          *     An object containing the handlers for the request
6084          * @returns {Object}
6085          *     {
6086          *         abort: {function} Function that signifies the callback handler to NOT process the response of the rest request
6087          *     }
6088          * @private
6089          */
6090         _doGET: function (handlers) {
6091             return this.restRequest(this.getRestUrl(), handlers);
6092         },
6093 
6094         /**
6095          * Common update event handler used by the pubsub callback closure.
6096          * Processes the update event then notifies listeners.
6097          * @param {Object} scope
6098          *     An object containing callbacks to handle the asynchronous get
6099          * @param {Object} update
6100          *     An object containing callbacks to handle the asynchronous get
6101          * @private
6102          */
6103         _updateEventHandler: function (scope, update) {
6104             if (scope._processUpdate(update)) {
6105                 switch (update.object.Update.event) {
6106                 case "POST":
6107                     scope._addNotifier.notifyListeners(scope);
6108                     break;
6109                 case "PUT":
6110                     scope._changeNotifier.notifyListeners(scope);
6111                     break;
6112                 case "DELETE":
6113                     scope._deleteNotifier.notifyListeners(scope);
6114                     break;
6115                 }
6116             }   
6117         },
6118 
6119         /**
6120          * Utility method to create a callback to be given to OpenAjax to invoke when a message
6121          * is published on the topic of our REST URL (also XEP-0060 node).
6122          * This needs to be its own defined method so that subclasses can have their own implementation.
6123          * @returns {Function} callback(update)
6124          *     The callback to be invoked when an update event is received. This callback will
6125          *     process the update and notify listeners.
6126          * @private
6127          */
6128         _createPubsubCallback: function () {
6129             var _this = this;
6130             return function (update) {
6131                 _this._updateEventHandler(_this, update);
6132             };
6133         },
6134 
6135         /**
6136          * Subscribe to pubsub infra using the REST URL as the topic name.
6137          * @param {finesse.interfaces.RequestHandlers} handlers
6138          *     An object containing the handlers for the request
6139          * @private
6140          */
6141         subscribe: function (callbacks) {
6142             // Only need to do a subscription to client pubsub. No need to trigger
6143             // a subscription on the Finesse server due to implicit subscribe (at
6144             // least for now).
6145             var _this = this,
6146             topic = Topics.getTopic(this.getRestUrl()),
6147             handlers,
6148             successful = ClientServices.subscribe(topic, this._createPubsubCallback(), true);
6149 
6150             callbacks = callbacks || {};
6151 
6152             handlers = {
6153                 /** @private */
6154                 success: function () {
6155                     // Add item to the refresh list in ClientServices to refresh if
6156                     // we recover due to our resilient connection. However, do
6157                     // not add if doNotRefresh flag is set.
6158                     if (!_this.doNotRefresh) {
6159                         ClientServices.addToRefreshList(_this);
6160                     }
6161 
6162                     if (typeof callbacks.success === "function") {
6163                         callbacks.success();
6164                     }
6165                 },
6166                 /** @private */
6167                 error: function (err) {
6168                     if (successful) {
6169                         ClientServices.unsubscribe(topic);
6170                     }
6171 
6172                     if (typeof callbacks.error === "function") {
6173                         callbacks.error(err);
6174                     }
6175                 }
6176             };
6177 
6178             // Request a node subscription only if this object requires explicit subscriptions
6179             if (this.explicitSubscription === true) {
6180                 this._subscribeNode(handlers);
6181             } else {
6182                 if (successful) {
6183                     this._subid = "OpenAjaxOnly";
6184                     handlers.success();
6185                 } else {
6186                     handlers.error();
6187                 }
6188             }
6189 
6190             return this;
6191         },
6192 
6193         /**
6194          * Unsubscribe to pubsub infra using the REST URL as the topic name.
6195          * @param {finesse.interfaces.RequestHandlers} handlers
6196          *     An object containing the handlers for the request
6197          * @private
6198          */
6199         unsubscribe: function (callbacks) {
6200             // Only need to do a subscription to client pubsub. No need to trigger
6201             // a subscription on the Finesse server due to implicit subscribe (at
6202             // least for now).
6203             var _this = this,
6204             topic = Topics.getTopic(this.getRestUrl()),
6205             handlers;
6206 
6207             // no longer keep track of object to refresh on reconnect
6208             ClientServices.removeFromRefreshList(_this);
6209 
6210             callbacks = callbacks || {};
6211 
6212             handlers = {
6213                 /** @private */
6214                 success: function () {
6215                     if (typeof callbacks.success === "function") {
6216                         callbacks.success();
6217                     }
6218                 },
6219                 /** @private */
6220                 error: function (err) {
6221                     if (typeof callbacks.error === "function") {
6222                         callbacks.error(err);
6223                     }
6224                 }
6225             };
6226 
6227             if (this._subid) {
6228                 ClientServices.unsubscribe(topic);
6229                 // Request a node unsubscribe only if this object requires explicit subscriptions
6230                 if (this.explicitSubscription === true) {
6231                     this._unsubscribeNode(handlers);
6232                 } else {
6233                     this._subid = undefined;
6234                     handlers.success();
6235                 }
6236             } else {
6237                 handlers.success();
6238             }
6239 
6240             return this;
6241         },
6242 
6243         /**
6244          * Private utility to perform node subscribe requests for explicit subscriptions
6245          * @param {finesse.interfaces.RequestHandlers} handlers
6246          *     An object containing the handlers for the request
6247          * @private
6248          */
6249         _subscribeNode: function (callbacks) {
6250             var _this = this;
6251 
6252             // Protect against null dereferencing of callbacks allowing its (nonexistent) keys to be read as undefined
6253             callbacks = callbacks || {};
6254 
6255             ClientServices.subscribeNode(this.getXMPPNodePath(), function (subid, err) {
6256                 if (err) {
6257                     if (typeof callbacks.error === "function") {
6258                         callbacks.error(err);
6259                     }
6260                 } else {
6261                     // Store the subid on a successful subscribe
6262                     _this._subid = subid;
6263                     if (typeof callbacks.success === "function") {
6264                         callbacks.success();
6265                     }
6266                 }
6267             });
6268         },
6269 
6270         /**
6271          * Private utility to perform node unsubscribe requests for explicit subscriptions
6272          * @param {finesse.interfaces.RequestHandlers} handlers
6273          *     An object containing the handlers for the request
6274          * @private
6275          */
6276         _unsubscribeNode: function (callbacks) {
6277             var _this = this;
6278 
6279             // Protect against null dereferencing of callbacks allowing its (nonexistent) keys to be read as undefined
6280             callbacks = callbacks || {};
6281 
6282             ClientServices.unsubscribeNode(this.getXMPPNodePath(), this._subid, function (err) {
6283                 _this._subid = undefined;
6284                 if (err) {
6285                     if (typeof callbacks.error === "function") {
6286                         callbacks.error(err);
6287                     }
6288                 } else {
6289                     if (typeof callbacks.success === "function") {
6290                         callbacks.success();
6291                     }
6292                 }
6293             });
6294         },
6295 
6296         /**
6297          * Validate and store the object into the internal data store.
6298          * @param {Object} object
6299          *     The JavaScript object that should match of schema of this REST object.
6300          * @returns {Boolean}
6301          *     True if the object was validated and stored successfully.
6302          * @private
6303          */
6304         _processObject: function (object) {
6305             if (this._validate(object)) {
6306                 this._data = this.getProperty(object, this.getRestType()); // Should clone the object here?
6307 
6308                 // If loaded for the first time, call the load notifiers.
6309                 if (!this._loaded) {
6310                     this._loaded = true;
6311                     this._loadNotifier.notifyListeners(this);
6312                 }
6313 
6314                 return true;
6315             }
6316             return false;
6317         },
6318 
6319         /**
6320          * Normalize the object to mitigate the differences between the backend
6321          * and what this REST object should hold. For example, the backend sends
6322          * send an event with the root property name being lower case. In order to
6323          * match the GET, the property should be normalized to an upper case.
6324          * @param {Object} object
6325          *     The object which should be normalized.
6326          * @returns {Object}
6327          *     Return the normalized object.
6328          * @private
6329          */
6330         _normalize: function (object) {
6331             var
6332             restType = this.getRestType(),
6333             // Get the REST object name with first character being lower case.
6334             objRestType = restType.charAt(0).toLowerCase() + restType.slice(1);
6335 
6336             // Normalize payload to match REST object. The payload for an update
6337             // use a lower case object name as oppose to upper case. Only normalize
6338             // if necessary.
6339             if (!this.hasProperty(object, restType) && this.hasProperty(object, objRestType)) {
6340                 //Since the object is going to be modified, clone the object so that
6341                 //it doesn't affect others (due to OpenAjax publishing to other
6342                 //subscriber.
6343                 object = jQuery.extend(true, {}, object);
6344 
6345                 object[restType] = object[objRestType];
6346                 delete(object[objRestType]);
6347             }
6348             return object;
6349         },
6350 
6351         /**
6352          * Utility method to process the response of a successful get
6353          * @param {Object} rsp
6354          *     The response of a successful get
6355          * @returns {Boolean}
6356          *     True if the update was successfully processed (the response object
6357          *     passed the schema validation) and updated the internal data cache,
6358          *     false otherwise.
6359          * @private
6360          */
6361         _processResponse: function (rsp) {
6362             try {
6363                 if (this.keepRestResponse) {
6364                     this._restResponse = rsp.content;
6365                 }
6366                 return this._processObject(rsp.object);
6367             }
6368             catch (err) {
6369                 this._logger.error(this.getRestType() + ': ' + err);
6370             }
6371             return false;
6372         },
6373 
6374         /**
6375          * Utility method to process the update notification.
6376          * @param {Object} update
6377          *     The payload of an update notification.
6378          * @returns {Boolean}
6379          *     True if the update was successfully processed (the update object
6380          *     passed the schema validation) and updated the internal data cache,
6381          *     false otherwise.
6382          * @private
6383          */
6384         _processUpdate: function (update) {
6385             try {
6386                 var updateObj, requestId, fakeResponse, receivedError;
6387 
6388                 // The backend will send the data object with a lower case. To be
6389                 // consistent with what should be represented in this object, the
6390                 // object name should be upper case. This will normalize the object.
6391                 updateObj = this._normalize(update.object.Update.data);
6392 
6393                 // Store the last event.
6394                 this._lastUpdate = update.object;
6395 
6396                 requestId = this._lastUpdate.Update ? this._lastUpdate.Update.requestId : undefined;
6397 
6398                 if (requestId && this._pendingCallbacks[requestId]) {
6399 
6400                     /*
6401                      * The passed success/error callbacks are expecting to be passed an AJAX response, so construct
6402                      * a simulated/"fake" AJAX response object from the information in the received event.
6403                      * The constructed object should conform to the contract for response objects specified
6404                      * in _createAjaxHandler().
6405                      */
6406                     fakeResponse = {};
6407 
6408                     //The contract says that rsp.content should contain the raw text of the response so we simulate that here.
6409                     //For some reason json2xml has trouble with the native update object, so we serialize a clone of it by
6410                     //doing a parse(stringify(update)).
6411                     fakeResponse.content = this._util.json2xml(gadgets.json.parse(gadgets.json.stringify(update)));
6412 
6413                     fakeResponse.object = {};
6414 
6415                     if (updateObj.apiErrors && updateObj.apiErrors.apiError) { //Error case
6416 
6417                         //TODO: The lowercase -> uppercase ApiErrors translation method below is undesirable, can it be improved?
6418                         receivedError = updateObj.apiErrors.apiError;
6419                         fakeResponse.object.ApiErrors = {};
6420                         fakeResponse.object.ApiErrors.ApiError = {};
6421                         fakeResponse.object.ApiErrors.ApiError.ErrorData = receivedError.errorData || undefined;
6422                         fakeResponse.object.ApiErrors.ApiError.ErrorMessage = receivedError.errorMessage || undefined;
6423                         fakeResponse.object.ApiErrors.ApiError.ErrorType = receivedError.errorType || undefined;
6424 
6425                         /*
6426                          * Since this is the error case, supply the error callback with a '400 BAD REQUEST' status code. We don't know what the real
6427                          * status code should be since the event we're constructing fakeResponse from doesn't include a status code.
6428                          * This is just to conform to the contract for the error callback in _createAjaxHandler().
6429                          **/
6430                         fakeResponse.status = 400;
6431 
6432                     } else { //Success case
6433 
6434                         fakeResponse.object = this._lastUpdate;
6435 
6436                         /*
6437                          * Since this is the success case, supply the success callback with a '200 OK' status code. We don't know what the real
6438                          * status code should be since the event we're constructing fakeResponse from doesn't include a status code.
6439                          * This is just to conform to the contract for the success callback in _createAjaxHandler().
6440                          **/
6441                         fakeResponse.status = 200;
6442                     }
6443 
6444                     try {
6445 
6446                         if (fakeResponse.object.ApiErrors && this._pendingCallbacks[requestId].error) {
6447                             this._pendingCallbacks[requestId].error(fakeResponse);
6448                         } 
6449                         // HTTP 202 is handled as a success, besides, we cannot infer that a non-error is a success.
6450                         /*else if (this._pendingCallbacks[requestId].success) {
6451                             this._pendingCallbacks[requestId].success(fakeResponse);
6452                         }*/
6453 
6454                     } catch (callbackErr) {
6455 
6456                         this._logger.error(this.getRestType() + ": Caught error while firing callback: " + callbackErr);
6457 
6458                     }
6459 
6460                     //Clean up _pendingCallbacks now that we fired a callback corresponding to the received requestId.
6461                     delete this._pendingCallbacks[requestId];
6462 
6463                 } else {
6464                     this._logger.log(this.getRestType() + ": Received the following event with an invalid or unknown requestId:");
6465                     this._logger.log(gadgets.json.stringify(update));
6466                 }
6467 
6468                 return this._processObject(updateObj);
6469             }
6470             catch (err) {
6471                 this._logger.error(this.getRestType() + ': ' + err);
6472             }
6473             return false;
6474         },
6475 
6476         /**
6477          * Utility method to create ajax response handler closures around the
6478          * provided callbacks. Callbacks should be passed through from .ajax().
6479          * makeRequest is responsible for garbage collecting these closures.
6480          * @param {finesse.interfaces.RequestHandlers} handlers
6481          *     An object containing the handlers for the request
6482          * @returns {Object}
6483          *     {
6484          *         abort: {function} Function that signifies the callback handler to NOT process the response when the response returns
6485          *         callback: {function} Callback handler to be invoked when the response returns
6486          *     }
6487          * @private
6488          */
6489         _createAjaxHandler: function (options) {
6490             //We should not need to check this again since it has already been done in .restRequest()
6491             //options = options || {};
6492 
6493             //Flag to indicate whether or not to process the response
6494             var abort = false,
6495 
6496             //Get a reference to the parent User object
6497             _this = this;
6498 
6499             return {
6500 
6501                 abort: function () {
6502                     abort = true;
6503                 },
6504 
6505                 callback: function (rsp) {
6506              
6507                     if (abort) {
6508                         // Do not process the response
6509                         return;
6510                     }
6511 
6512                     var requestId, error = false, rspObj;
6513 
6514                     if (options.success || options.error) {
6515                         rspObj = {
6516                             status: rsp.rc,
6517                             content: rsp.text
6518                         };
6519 
6520                         if (!_this.doNotLog) {
6521                             _this._logger.log(_this.getRestType() + ": requestId='" + options.uuid + "', Returned with status=" + rspObj.status + ", content='" + rspObj.content + "'");
6522                         }
6523 
6524                         //Some responses may not have a body.
6525                         if (rsp.text && rsp.text.length > 0) {
6526                             try {
6527                                 rspObj.object = _this._util.xml2js(rsp.text);
6528                             } catch (e) {
6529                                 error = true;
6530                                 rspObj.error = {
6531                                     errorType: "parseError",
6532                                     errorMessage: "Could not serialize XML: " + e
6533                                 };
6534                             }
6535                         } else {
6536                             rspObj.object = {};
6537                         }
6538 
6539                         if (!error && rspObj.status >= 200 && rspObj.status < 300) {
6540                             if (options.success) {
6541                                 options.success(rspObj);
6542                             }
6543                         } else {
6544                             if (options.error) {
6545                                 options.error(rspObj);
6546                             }
6547                         }
6548 
6549                         /*
6550                          * If a synchronous error happened after a non-GET request (usually a validation error), we
6551                          * need to clean up the request's entry in _pendingCallbacks since no corresponding event
6552                          * will arrive later. The corresponding requestId should be present in the response headers.
6553                          *
6554                          * It appears Shindig changes the header keys to lower case, hence 'requestid' instead of
6555                          * 'requestId' below.
6556                          **/
6557                         if (rspObj.status !== 202 && rsp.headers && rsp.headers.requestid) {
6558                             requestId = rsp.headers.requestid[0];
6559                             if (_this._pendingCallbacks[requestId]) {
6560                                 delete _this._pendingCallbacks[requestId];
6561                             }
6562                         }
6563                     }
6564                 }
6565             };
6566         },
6567 
6568         /**
6569          * Utility method to make an asynchronous request
6570          * @param {String} url
6571          *     The unencoded URL to which the request is sent (will be encoded)
6572          * @param {Object} options
6573          *     An object containing additional options for the request.
6574          * @param {Object} options.content
6575          *     An object to send in the content body of the request. Will be
6576          *     serialized into XML before sending.
6577          * @param {String} options.method
6578          *     The type of request. Defaults to "GET" when none is specified.
6579          * @param {Function} options.success(rsp)
6580          *     A callback function to be invoked for a successful request.
6581          *     {
6582          *         status: {Number} The HTTP status code returned
6583          *         content: {String} Raw string of response
6584          *         object: {Object} Parsed object of response
6585          *     }
6586          * @param {Function} options.error(rsp)
6587          *     A callback function to be invoked for an unsuccessful request.
6588          *     {
6589          *         status: {Number} The HTTP status code returned
6590          *         content: {String} Raw string of response
6591          *         object: {Object} Parsed object of response
6592          *         error: {Object} Wrapped exception that was caught
6593          *         error.errorType: {String} Type of error that was caught
6594          *         error.errorMessage: {String} Message associated with error
6595          *     }
6596          * @returns {Object}
6597          *     {
6598          *         abort: {function} Function that signifies the callback handler to NOT process the response of this asynchronous request
6599          *     }
6600          * @private
6601         */
6602         restRequest: function (url, options) {
6603 
6604             var params, encodedUrl, ajaxHandler;
6605 
6606             params = {};
6607 
6608             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
6609             options = options || {};
6610             options.success = this._util.validateHandler(options.success);
6611             options.error = this._util.validateHandler(options.error);
6612 
6613             // Request Headers
6614             params[gadgets.io.RequestParameters.HEADERS] = {};
6615 
6616             // HTTP method is a passthrough to gadgets.io.makeRequest, makeRequest defaults to GET
6617             params[gadgets.io.RequestParameters.METHOD] = options.method;
6618 
6619             //true if this should be a GET request, false otherwise
6620             if (!options.method || options.method === "GET") {
6621                 //Disable caching for GETs
6622                 if (url.indexOf("?") > -1) {
6623                     url += "&";
6624                 } else {
6625                     url += "?";
6626                 }
6627                 url += "nocache=" + this._util.currentTimeMillis();
6628             } else {
6629                 /**
6630                  * If not GET, generate a requestID and add it to the headers, then wrap
6631                  * callbacks into an object and store it in _pendingCallbacks.
6632                  * If we receive a synchronous error response instead of a 202 as expected,
6633              * the AJAX handler will clean up _pendingCallbacks.
6634                  **/
6635                 /*
6636                  * TODO: Clean up _pendingCallbacks if an entry persists after a certain amount of time has passed.
6637                  * In the block below, can store the current time (new Date().getTime()) alongside the
6638                  * callbacks in the new _pendingCallbacks entry. Then iterate through a copty of _pendingCallbacks,
6639                  * deleting all entries inside _pendingCallbacks that are older than a certain threshold (2 minutes for example.)
6640                  * This solves a potential memory leak issue if we never receive an event for a given stored requestId;
6641                  * we don't want to store unfired callbacks forever.
6642                  */
6643                 /** @private */
6644                 options.uuid = this._util.generateUUID();
6645                 params[gadgets.io.RequestParameters.HEADERS].requestId = options.uuid;
6646                 //By default, Shindig strips nearly all of the response headers, but this parameter tells Shindig
6647                 //to send the headers through unmodified; we need to be able to read the 'requestId' header if we
6648                 //get a synchronous error as a result of a non-GET request. (See the bottom of _createAjaxHandler().)
6649                 params[gadgets.io.RequestParameters.GET_FULL_HEADERS] = "true";
6650                 this._pendingCallbacks[options.uuid] = {};
6651                 this._pendingCallbacks[options.uuid].success = options.success;
6652                 this._pendingCallbacks[options.uuid].error = options.error;
6653             }
6654 
6655             //debugger;
6656             // NAT: IGNORE this until you hit save after changing assignments, then
6657             // pause here and set window.errorOnRequest to true, step past the next line,
6658             // and then set it to false. True value will throw an error when saving assignments.
6659             encodedUrl = encodeURI(url) + (window.errorOnRestRequest ? "ERROR" : "");
6660 
6661             if (!this.doNotLog) {
6662                 this._logger.log(this.getRestType() + ": requestId='" + options.uuid + "', Making REST request: method=" + (options.method || "GET") + ", url='" + encodedUrl + "'");
6663             }
6664 
6665             // Content Body
6666             if (typeof options.content === "object") {
6667                 // Content Type
6668                 params[gadgets.io.RequestParameters.HEADERS]["Content-Type"] = "application/xml";
6669                 // Content
6670                 params[gadgets.io.RequestParameters.POST_DATA] = this._util.js2xml(options.content);
6671                 
6672                 if (!this.doNotLog) {
6673                     this._logger.log(this.getRestType() + ": requestId='" + options.uuid + "', POST_DATA='" + params[gadgets.io.RequestParameters.POST_DATA] + "'");
6674                 }
6675             }
6676 
6677             ajaxHandler = this._createAjaxHandler(options);
6678             ClientServices.makeRequest(encodedUrl, ajaxHandler.callback, params);
6679 
6680             return {
6681                 abort: ajaxHandler.abort
6682             };
6683         },
6684 
6685         /**
6686          * Retrieves a reference to a particular notifierType.
6687          * @param notifierType is a string which indicates the notifier to retrieve
6688          * ('load', 'change', 'add', 'delete', 'error')
6689          * @return {Notifier}
6690          * @private
6691          */
6692         _getNotifierReference: function (notifierType) {
6693             var notifierReference = null;
6694             if (notifierType === 'load') {
6695                 notifierReference = this._loadNotifier;
6696             } else if (notifierType === 'change') {
6697                 notifierReference = this._changeNotifier;
6698             } else if (notifierType === 'add') {
6699                 notifierReference = this._addNotifier;
6700             } else if (notifierType === 'delete') {
6701                 notifierReference = this._deleteNotifier;
6702             } else if (notifierType === 'error') {
6703                 notifierReference = this._errorNotifier;
6704             } else {
6705                 throw new Error("_getNotifierReference(): Trying to get unknown notifier(notifierType=" + notifierType + ")");
6706             }
6707 
6708             return notifierReference;
6709         }
6710     });
6711 
6712     window.finesse = window.finesse || {};
6713     window.finesse.restservices = window.finesse.restservices || {};
6714     window.finesse.restservices.RestBase = RestBase;
6715     
6716     return RestBase;
6717 });
6718 
6719 /** The following comment is to prevent jslint errors about 
6720  * using variables before they are defined.
6721  */
6722 /*global finesse*/
6723 
6724 /**
6725  * JavaScript base object that all REST collection objects should
6726  * inherit from because it encapsulates and provides the common functionality
6727  * that all REST objects need.
6728  *
6729  * @requires finesse.clientservices.ClientServices
6730  * @requires Class
6731  * @requires finesse.FinesseBase
6732  * @requires finesse.restservices.RestBase
6733  */
6734 
6735 /**
6736  * @class
6737  * JavaScript representation of a REST collection object.
6738  *
6739  * @constructor
6740  * @param {Function} callbacks.onCollectionAdd(this)
6741  *     Callback to invoke upon successful item addition to the collection.
6742  * @param {Function} callbacks.onCollectionDelete(this)
6743  *     Callback to invoke upon successful item deletion from the collection.
6744  * @borrows finesse.restservices.RestBase as finesse.restservices.RestCollectionBase
6745  */
6746 /** @private */
6747 define('restservices/RestCollectionBase',[
6748     'restservices/RestBase',
6749     'utilities/Utilities',
6750     'restservices/Notifier'
6751 ],
6752 function (RestBase, Utilities, Notifier) {
6753     var RestCollectionBase = RestBase.extend(/** @lends finesse.restservices.RestCollectionBase.prototype */{
6754 
6755         /**
6756          * Boolean function that specifies whether the collection handles subscribing
6757          * and propagation of events for the individual REST object items the
6758          * collection holds. False by default. Subclasses should override if true.
6759          * @private
6760          */
6761         supportsRestItemSubscriptions: false,
6762 
6763         /**
6764          * Gets the constructor the individual items that make of the collection.
6765          * For example, a Dialogs collection object will hold a list of Dialog items.
6766          * @throws Error because subtype must implement.
6767          * @private
6768          */
6769         getRestItemClass: function () {
6770             throw new Error("getRestItemClass(): Not implemented in subtype.");
6771         },
6772 
6773         /**
6774          * Gets the REST type of the individual items that make of the collection.
6775          * For example, a Dialogs collection object will hold a list of Dialog items.
6776          * @throws Error because subtype must implement.
6777          * @private
6778          */
6779         getRestItemType: function () {
6780             throw new Error("getRestItemType(): Not implemented in subtype.");
6781         },
6782 
6783         /**
6784          * The base REST URL in which items this object contains can be referenced.
6785          * @return {String}
6786          *     The REST URI for items this object contains.
6787          * @private
6788          */
6789         getRestItemBaseUrl: function () {
6790             var
6791             restUrl = "/finesse/api";
6792 
6793             //Append the REST type.
6794             restUrl += "/" + this.getRestItemType();
6795 
6796             return restUrl;
6797         },
6798 
6799          /*
6800          * Creates a new object from the given data
6801          * @param data - data object
6802          * @private
6803          */
6804         _objectCreator: function (data) {
6805             var objectId = this._extractId(data),
6806             newRestObj = this._collection[objectId],
6807             _this = this;
6808 
6809             //Prevent duplicate entries into collection.
6810             if (!newRestObj) {
6811                 //Create a new REST object using the subtype defined by the
6812                 //overridden method.
6813                 newRestObj = new (this.getRestItemClass())({
6814                     doNotSubscribe: this.handlesItemSubscription,
6815                     doNotRefresh: this.handlesItemRefresh,
6816                     id: objectId,
6817                     data: data,
6818                     onLoad: function (newObj) {
6819                         //Normalize and add  REST object to collection datastore.
6820                         _this._collection[objectId] = newObj;
6821                         _this._collectionAddNotifier.notifyListeners(newObj);
6822                         _this.length += 1;
6823                     }
6824                 });
6825             }
6826             else {
6827                 //If entry already exist in collection, process the new event,
6828                 //and notify all change listeners since an existing object has
6829                 //change. This could happen in the case when the Finesse server
6830                 //cycles, and sends a snapshot of the user's calls.
6831                 newRestObj._processObject(data);
6832                 newRestObj._changeNotifier.notifyListeners(newRestObj);
6833             }
6834         },
6835 
6836         /*
6837          * Deletes and object and notifies its handlers
6838          * @param data - data object
6839          * @private
6840          */
6841         _objectDeleter: function (data) {
6842             var objectId = this._extractId(data),
6843             object = this._collection[objectId];
6844             if (object) {
6845                 //Even though this is a delete, let's make sure the object we are passing has got good data
6846                 object._processObject(data);
6847                 //Notify listeners and delete from internal datastore.
6848                 this._collectionDeleteNotifier.notifyListeners(object);
6849                 delete this._collection[objectId];
6850                 this.length -= 1;
6851             }
6852         },
6853 
6854          /**
6855           * Creates an anonymous function for notifiying error listeners of a particular object
6856           * data.
6857           * @param obj - the objects whose error listeners to notify
6858           * @returns {Function}
6859           *     Callback for notifying of errors
6860           * @private
6861           */
6862         _createErrorNotifier: function (obj) {
6863             return function (err) {
6864                 obj._errorNotifier.notifyListeners(err);
6865             };
6866         },
6867 
6868          /**
6869           * Replaces the collection with a refreshed list using the passed in
6870           * data.
6871           * @param data - data object (usually this._data)
6872           * @private
6873           */
6874          _buildRefreshedCollection: function (data) {
6875             var i, dataObject, object, objectId, dataArray, newIds = [], foundFlag;
6876             if (data && this.getProperty(data, this.getRestItemType()) !== null) {
6877                 dataArray = Utilities.getArray(this.getProperty(data, this.getRestItemType()));
6878             } else {
6879                 dataArray = [];
6880             }
6881 
6882             // iterate through each item in the new data and add to or update collection
6883             for (i = 0; i < dataArray.length; i += 1) {
6884                 dataObject = {};
6885                 dataObject[this.getRestItemType()] = dataArray[i];
6886                 objectId = this._extractId(dataObject);
6887 
6888                 this._objectCreator(dataObject);
6889                 newIds.push(objectId);
6890 
6891                 // resubscribe if the object requires an explicit subscription
6892                 object = this._collection[objectId];
6893                 if (this.handlesItemRefresh && object.explicitSubscription) {
6894                     object._subscribeNode({
6895                         error: this._createErrorNotifier(object)
6896                     });
6897                 }
6898             }
6899 
6900             // now clean up items (if any) that were removed
6901             for (objectId in this._collection) {
6902                 if (this._collection.hasOwnProperty(objectId)) {
6903                     foundFlag = false;
6904                     for (i = newIds.length - 1; i >= 0; i -= 1) {
6905                         if (newIds[i] === objectId) {
6906                             foundFlag = true;
6907                             break;
6908                         }
6909                     }
6910                     // did not find in updated list, so delete it
6911                     if (!foundFlag) {
6912                         this._objectDeleter({'data': this._collection[objectId]._data});
6913                     }
6914                 }
6915             }
6916         },
6917 
6918          /**
6919           * The actual refresh operation, refactored out so we don't have to repeat code
6920           * @private
6921           */
6922         _RESTRefresh: function () {
6923             var _this = this;
6924             this._doGET({
6925                 success: function(rsp) {
6926                     if (_this._processResponse(rsp)) {
6927                         _this._buildRefreshedCollection(_this._data);
6928                     } else {
6929                         _this._errorNotifier.notifyListeners(_this);
6930                     }
6931                 },
6932                 error: function(rsp) {
6933                     _this._errorNotifier.notifyListeners(rsp);
6934                 }
6935             });            
6936         },
6937 
6938         /**
6939          * Force an update on this object. Since an asynchronous GET is performed,
6940          * it is necessary to have an onChange handler registered in order to be
6941          * notified when the response of this returns.
6942          * @returns {finesse.restservices.RestBaseCollection}
6943          *     This RestBaseCollection object to allow cascading
6944          */
6945          refresh: function() {
6946             var _this = this, isLoaded = this._loaded;
6947 
6948             // resubscribe if the collection requires an explicit subscription
6949             if (this.explicitSubscription) {
6950                 this._subscribeNode({
6951                     success: function () {
6952                         _this._RESTRefresh();
6953                     },
6954                     error: function (err) {
6955                         _this._errorNotifier.notifyListeners(err);
6956                     }
6957                 });
6958             } else {
6959                 this._RESTRefresh();
6960             }
6961 
6962             return this; // Allow cascading
6963          },
6964 
6965         /**
6966          * @private
6967          * The _addHandlerCb and _deleteHandlerCb require that data be passed in the
6968          * format of an array of {(Object Type): object} objects. For example, a
6969          * queues object would return [{Queue: queue1}, {Queue: queue2}, ...].
6970          * @param skipOuterObject If {true} is passed in for this param, then the "data"
6971          *                           property is returned instead of an object with the
6972          *                           data appended.
6973          * @return {Array}
6974          */
6975         extractCollectionData: function (skipOuterObject) {
6976             var restObjs,
6977             obj,
6978             result = [],
6979             _this = this;
6980             
6981             if (this._data)
6982             {
6983                 restObjs = this._data[this.getRestItemType()];
6984     
6985                 if (restObjs)
6986                 {
6987                     // check if there are multiple objects to pass
6988                     if (!$.isArray(restObjs))
6989                     {
6990                         restObjs = [restObjs];
6991                     }
6992     
6993                     // if so, create an object for each and add to result array
6994                     $.each(restObjs, function (id, object) {
6995                         if (skipOuterObject === true)
6996                         {
6997                             obj = object;
6998                         }
6999                         else
7000                         {
7001                             obj = {};
7002                             obj[_this.getRestItemType()] = object;
7003                         }
7004                         result.push(obj);
7005                     });
7006                 }
7007                 
7008             }
7009             
7010             return result;
7011         },
7012 
7013         /**
7014          * For Finesse, collections are handled uniquely on a POST and
7015          * doesn't necessary follow REST conventions. A POST on a collection
7016          * doesn't mean that the collection has been created, it means that an
7017          * item has been added to the collection. This function will generate
7018          * a closure which will handle this logic appropriately.
7019          * @param {Object} scope
7020          *     The scope of where the callback should be invoked.
7021          * @private
7022          */
7023         _addHandlerCb: function (scope) {
7024             return function (restItem) {
7025                 var data = restItem.extractCollectionData();               
7026 
7027                 $.each(data, function (id, object) {
7028                     scope._objectCreator(object);
7029                 });
7030             };
7031         },
7032 
7033         /**
7034          * For Finesse, collections are handled uniquely on a DELETE and
7035          * doesn't necessary follow REST conventions. A DELETE on a collection
7036          * doesn't mean that the collection has been deleted, it means that an
7037          * item has been deleted from the collection. This function will generate
7038          * a closure which will handle this logic appropriately.
7039          * @param {Object} scope
7040          *     The scope of where the callback should be invoked.
7041          * @private
7042          */
7043         _deleteHandlerCb: function (scope) {
7044             return function (restItem) {
7045                 var data = restItem.extractCollectionData();
7046 
7047                 $.each(data, function (id, obj) {
7048                     scope._objectDeleter(obj);
7049                 });
7050             };
7051         },
7052 
7053         /**
7054          * Utility method to process the update notification for Rest Items
7055          * that are children of the collection whose events are published to
7056          * the collection's node.
7057          * @param {Object} update
7058          *     The payload of an update notification.
7059          * @returns {Boolean}
7060          *     True if the update was successfully processed (the update object
7061          *     passed the schema validation) and updated the internal data cache,
7062          *     false otherwise.
7063          * @private
7064          */
7065         _processRestItemUpdate: function (update) {
7066             var object, objectId, updateObj = update.object.Update;
7067 
7068             //Extract the ID from the source if the Update was an error.
7069             if (updateObj.data.apiErrors) {
7070                 objectId = Utilities.getId(updateObj.source);
7071             }
7072             //Otherwise extract from the data object itself.
7073             else {
7074                 objectId = this._extractId(updateObj.data);
7075             }
7076 
7077             object = this._collection[objectId];
7078             if (object) {
7079                 if (object._processUpdate(update)) {
7080                     switch (updateObj.event) {
7081                     case "POST":
7082                         object._addNotifier.notifyListeners(object);
7083                         break;
7084                     case "PUT":
7085                         object._changeNotifier.notifyListeners(object);
7086                         break;
7087                     case "DELETE":
7088                         object._deleteNotifier.notifyListeners(object);
7089                         break;
7090                     }
7091                 }
7092             }
7093         },
7094 
7095         /**
7096          * SUBCLASS IMPLEMENTATION (override):
7097          * For collections, this callback has the additional responsibility of passing events
7098          * of collection item updates to the item objects themselves. The collection needs to
7099          * do this because item updates are published to the collection's node.
7100          * @returns {Function}
7101          *     The callback to be invoked when an update event is received
7102          * @private
7103          */
7104         _createPubsubCallback: function () {
7105             var _this = this;
7106             return function (update) {
7107                 //If the source of the update is our REST URL, this means the collection itself is modified
7108                 if (update.object.Update.source === _this.getRestUrl()) {
7109                     _this._updateEventHandler(_this, update);
7110                 } else {
7111                     //Otherwise, it is safe to assume that if we got an event on our topic, it must be a
7112                     //rest item update of one of our children that was published on our node (OpenAjax topic)
7113                     _this._processRestItemUpdate(update);
7114                 }
7115             };
7116         },
7117 
7118         /**
7119          * @class
7120          * This is the base collection object. 
7121          *
7122          * @constructs
7123          * @augments finesse.restservices.RestBase
7124          * @see finesse.restservices.Contacts
7125          * @see finesse.restservices.Dialogs
7126          * @see finesse.restservices.PhoneBooks
7127          * @see finesse.restservices.Queues
7128          * @see finesse.restservices.WorkflowActions
7129          * @see finesse.restservices.Workflows
7130          * @see finesse.restservices.WrapUpReasons
7131          */
7132         _fakeConstuctor: function () {
7133             /* This is here to hide the real init constructor from the public docs */
7134         },
7135         
7136        /**
7137          * @private
7138          * @param {Object} options
7139          *     An object with the following properties:<ul>
7140          *         <li><b>id:</b> The id of the object being constructed</li>
7141          *         <li><b>onCollectionAdd(this): (optional)</b> when an object is added to this collection</li>
7142          *         <li><b>onCollectionDelete(this): (optional)</b> when an object is removed from this collection</li>
7143          *         <li><b>onLoad(this): (optional)</b> when the collection is successfully loaded from the server</li>
7144          *         <li><b>onChange(this): (optional)</b> when an update notification of the collection is received. 
7145          *         This does not include adding and deleting members of the collection</li>
7146          *         <li><b>onAdd(this): (optional)</b> when a notification that the collection is created is received</li>
7147          *         <li><b>onDelete(this): (optional)</b> when a notification that the collection is deleted is received</li>
7148          *         <li><b>onError(rsp): (optional)</b> if loading of the collection fails, invoked with the error response object:<ul>
7149          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
7150          *             <li><b>content:</b> {String} Raw string of response</li>
7151          *             <li><b>object:</b> {Object} Parsed object of response</li>
7152          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
7153          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
7154          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
7155          *             </ul></li>
7156          *         </ul></li>
7157          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
7158          **/
7159         init: function (options) {
7160 
7161             options = options || {};
7162             options.id = "";
7163 
7164             //Make internal datastore collection to hold a list of objects.
7165             this._collection = {};
7166             this.length = 0;
7167 
7168             //Collections will have additional callbacks that will be invoked when
7169             //an item has been added/deleted.
7170             this._collectionAddNotifier = new Notifier();
7171             this._collectionDeleteNotifier = new Notifier();
7172 
7173             //Initialize the base class.
7174             this._super(options);
7175 
7176             this.addHandler('collectionAdd', options.onCollectionAdd);
7177             this.addHandler('collectionDelete', options.onCollectionDelete);
7178 
7179             //For Finesse, collections are handled uniquely on a POST/DELETE and
7180             //doesn't necessary follow REST conventions. A POST on a collection
7181             //doesn't mean that the collection has been created, it means that an
7182             //item has been added to the collection. A DELETE means that an item has
7183             //been removed from the collection. Due to this, we are attaching
7184             //special callbacks to the add/delete that will handle this logic.
7185             this.addHandler("add", this._addHandlerCb(this));
7186             this.addHandler("delete", this._deleteHandlerCb(this));
7187         },
7188 
7189         /**
7190          * Returns the collection.
7191          * @returns {Object}
7192          *     The collection as an object
7193          */
7194         getCollection: function () {
7195             //TODO: is this safe? or should we instead return protected functions such as .each(function)?
7196             return this._collection;
7197         },
7198 
7199         /**
7200          * Utility method to build the internal collection data structure (object) based on provided data
7201          * @param {Object} data
7202          *     The data to build the internal collection from
7203          * @private
7204          */
7205         _buildCollection: function (data) {
7206             var i, object, objectId, dataArray;
7207             if (data && this.getProperty(data, this.getRestItemType()) !== null) {
7208                 dataArray = Utilities.getArray(this.getProperty(data, this.getRestItemType()));
7209                 for (i = 0; i < dataArray.length; i += 1) {
7210 
7211                     object = {};
7212                     object[this.getRestItemType()] = dataArray[i];
7213                     objectId = this._extractId(object);
7214                     this._collection[objectId] = new (this.getRestItemClass())({
7215                         doNotSubscribe: this.handlesItemSubscription,
7216                         doNotRefresh: this.handlesItemRefresh,
7217                         id: objectId,
7218                         data: object
7219                     });
7220                     this.length += 1;
7221                 }
7222             }
7223         },
7224 
7225         /**
7226          * Called to know whether to include an item in the _collection and _data. Return false to keep it, true to filter out (discard) it.
7227          * Override this in subclasses if you need only object with certain attribute values.
7228          * @param  {Object} item Item to test.
7229          * @return {Boolean} False to keep, true to filter out (discard);
7230          */
7231         _filterOutItem: function (item) {
7232             return false;
7233         },
7234     
7235         /**
7236          * Validate and store the object into the internal data store.
7237          * SUBCLASS IMPLEMENTATION (override):
7238          * Performs collection specific logic to _buildCollection internally based on provided data
7239          * @param {Object} object
7240          *     The JavaScript object that should match of schema of this REST object.
7241          * @returns {Boolean}
7242          *     True if the object was validated and stored successfully.
7243          * @private
7244          */
7245         _processObject: function (object) {
7246             var i,
7247                 restItemType = this.getRestItemType(),
7248                 items;
7249             if (this._validate(object)) {
7250                 this._data = this.getProperty(object, this.getRestType()); // Should clone the object here?
7251     
7252                 // If a subclass has overriden _filterOutItem then we'll need to run through the items and remove them
7253                 if (this._data)
7254                 {
7255                     items = this._data[restItemType];
7256     
7257                     if (typeof(items) !== "undefined")
7258                     {
7259                         if (typeof(items.length) === "undefined")
7260                         {
7261                             // Single object
7262                             if (this._filterOutItem(items))
7263                             {
7264                                 this._data[restItemType] = items = [];
7265                             }
7266                             
7267                         }
7268                         else
7269                         {
7270                             // filter out objects
7271                             for (i = items.length - 1; i !== -1; i = i - 1)
7272                             {
7273                                 if (this._filterOutItem(items[i]))
7274                                 {
7275                                     items.splice(i, 1);
7276                                 }
7277                             }
7278                         }
7279                     }
7280                 }
7281     
7282                 // If loaded for the first time, call the load notifiers.
7283                 if (!this._loaded) {
7284                     this._buildCollection(this._data);
7285                     this._loaded = true;
7286                     this._loadNotifier.notifyListeners(this);
7287                 }
7288                 
7289                 return true;
7290                 
7291             }
7292             return false;
7293         },
7294 
7295         /**
7296          * Retrieves a reference to a particular notifierType.
7297          * @param {String} notifierType
7298          *      Specifies the notifier to retrieve (load, change, error, add, delete)
7299          * @return {Notifier} The notifier object.
7300          */
7301         _getNotifierReference: function (notifierType) {
7302             var notifierReference;
7303 
7304             try {
7305                 //Use the base method to get references for load/change/error.
7306                 notifierReference = this._super(notifierType);
7307             } catch (err) {
7308                 //Check for add/delete
7309                 if (notifierType === "collectionAdd") {
7310                     notifierReference = this._collectionAddNotifier;
7311                 } else if (notifierType === "collectionDelete") {
7312                     notifierReference = this._collectionDeleteNotifier;
7313                 } else {
7314                     //Rethrow exception from base class.
7315                     throw err;
7316                 }
7317             }
7318             return notifierReference;
7319         }
7320     });
7321     
7322     window.finesse = window.finesse || {};
7323     window.finesse.restservices = window.finesse.restservices || {};
7324     window.finesse.restservices.RestCollectionBase = RestCollectionBase;
7325     
7326     return RestCollectionBase;
7327 });
7328 
7329 /**
7330  * JavaScript representation of the Finesse Dialog object.
7331  *
7332  * @requires finesse.clientservices.ClientServices
7333  * @requires Class
7334  * @requires finesse.FinesseBase
7335  * @requires finesse.restservices.RestBase
7336  */
7337 
7338 /** @private */
7339 define('restservices/Dialog',[
7340     'restservices/RestBase',
7341     'utilities/Utilities'
7342 ],
7343 function (RestBase, Utilities) {
7344     var Dialog = RestBase.extend(/** @lends finesse.restservices.Dialog.prototype */{
7345 
7346         /**
7347          * @class
7348          * A Dialog is an attempted connection between or among multiple participants,
7349          * for example, a regular phone call, a conference, or a silent monitor session.
7350          * 
7351          * @augments finesse.restservices.RestBase
7352          * @constructs
7353          */
7354         _fakeConstuctor: function () {
7355             /* This is here to hide the real init constructor from the public docs */
7356         },
7357         
7358         /**
7359          * @private
7360          *
7361          * @param {Object} options
7362          *     An object with the following properties:<ul>
7363          *         <li><b>id:</b> The id of the object being constructed</li>
7364          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
7365          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
7366          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
7367          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
7368          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
7369          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
7370          *             <li><b>content:</b> {String} Raw string of response</li>
7371          *             <li><b>object:</b> {Object} Parsed object of response</li>
7372          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
7373          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
7374          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
7375          *             </ul></li>
7376          *         </ul></li>
7377          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
7378          **/
7379         init: function (options) {
7380             this._super(options);
7381         },
7382 
7383         /**
7384          * @private
7385          * Gets the REST class for the current object - this is the Dialog class.
7386          * @returns {Object} The Dialog class.
7387          */
7388         getRestClass: function () {
7389             return Dialog;
7390         },
7391         
7392         /**
7393          * @private
7394          * The constant for agent device.
7395          */
7396         _agentDeviceType: "AGENT_DEVICE",
7397         
7398         /**
7399          * @private
7400          * Gets the REST type for the current object - this is a "Dialog".
7401          * @returns {String} The Dialog string.
7402          */
7403         getRestType: function () {
7404             return "Dialog";
7405         },
7406 
7407         /**
7408          * @private
7409          * Override default to indicate that this object doesn't support making
7410          * requests.
7411          */
7412         supportsRequests: false,
7413 
7414         /**
7415          * @private
7416          * Override default to indicate that this object doesn't support subscriptions.
7417          */
7418         supportsSubscriptions: false,
7419 
7420         /**
7421          * Getter for the from address.
7422          * @returns {String} The from address.
7423          */
7424         getFromAddress: function () {
7425             this.isLoaded();
7426             return this.getData().fromAddress;
7427         },
7428 
7429         /**
7430          * Getter for the to address.
7431          * @returns {String} The to address.
7432          */
7433         getToAddress: function () {
7434             this.isLoaded();
7435             return this.getData().toAddress;
7436         },
7437         
7438         /**
7439          * Getter for the media type.
7440          * @returns {String} The media type.
7441          */
7442         getMediaType: function () {
7443             this.isLoaded();
7444             return this.getData().mediaType;
7445         },
7446         
7447         /**
7448          * @private
7449          * Getter for the uri.
7450          * @returns {String} The uri.
7451          */
7452         getDialogUri: function () {
7453             this.isLoaded();
7454             return this.getData().uri;
7455         },
7456 
7457         /**
7458          * Getter for the callType.
7459          * @deprecated Use getMediaProperties().callType instead.
7460          * @returns {String} The callType.
7461          */
7462         getCallType: function () {
7463             this.isLoaded();
7464             return this.getData().mediaProperties.callType;
7465         },
7466 
7467         /**
7468          * Getter for the DNIS. This is usually the actual number dialed.
7469          * @deprecated Use getMediaProperties().DNIS instead.
7470          * @returns {String} The callType.
7471          */
7472         getDNIS: function () {
7473             this.isLoaded();
7474             return this.getData().mediaProperties.DNIS;
7475         },
7476         
7477         /**
7478          * Getter for the Dialog state.
7479          * @returns {String} The Dialog state.
7480          */
7481         getState: function () {
7482             this.isLoaded();
7483             return this.getData().state;
7484         },
7485 
7486         /**
7487          * Retrieves a list of participants within the Dialog object.
7488          * @returns {Object} Array list of participants.
7489          */
7490         getParticipants: function () {
7491             this.isLoaded();
7492             var participants = this.getData().participants.Participant;
7493             //Due to the nature of the XML->JSO converter library, a single
7494             //element in the XML array will be considered to an object instead of
7495             //a real array. This will handle those cases to ensure that an array is
7496             //always returned.
7497 
7498             return Utilities.getArray(participants);
7499         },
7500 
7501        /**
7502          * gets the participant timer counters 
7503          *
7504          * @param {String} participantExt Extension of participant.
7505          * @returns {Object} which contains state, startTime, and stateChangeTime fields
7506          */
7507         getParticipantTimerCounters : function (participantExt) {
7508           var part, participantTimerCounters = {}, idx, participants;
7509           
7510           participants = this.getParticipants();
7511 
7512 
7513           //Loop through all the participants and find the right participant (based on participantExt)
7514           for(idx=0;idx<participants.length;idx=idx+1)
7515           {
7516             part = participants[idx];
7517             
7518             if (part.mediaAddress === participantExt)
7519             {
7520                 participantTimerCounters.startTime= part.startTime;
7521                 participantTimerCounters.stateChangeTime= part.stateChangeTime;
7522                 participantTimerCounters.state= part.state;
7523                 break;
7524             }
7525           }
7526           
7527           return participantTimerCounters;
7528         },
7529         
7530         /**
7531          * Determines the droppable participants.  A droppable participant is a participant that is an agent extension.   
7532          * (It is not a CTI Route Point, IVR Port, or the caller)
7533          * 
7534          * @param {String} filterExtension used to remove a single extension from the list
7535          * @returns {Array} Participants which is an array of all participants which can be dropped.
7536          */
7537         getDroppableParticipants: function (filterExtension) {
7538           this.isLoaded();
7539           var droppableParticipants = [], participants, index, idx, filterExtensionToRemove = "", callStateOk, part;
7540 
7541           participants = this.getParticipants();
7542 
7543           if (filterExtension)
7544           {
7545             filterExtensionToRemove = filterExtension;
7546           }
7547 
7548           //Loop through all the participants to remove non-agents & remove filterExtension
7549           //We could have removed filterExtension using splice, but we have to iterate through
7550           //the list anyway.
7551           for(idx=0;idx<participants.length;idx=idx+1)
7552           {
7553             part = participants[idx];
7554 
7555             //Skip the filterExtension
7556             if (part.mediaAddress !== filterExtensionToRemove)
7557             {
7558                 callStateOk = this._isParticipantStateDroppable(part);
7559 
7560                 //Remove non-agents & make sure callstate 
7561                 if (callStateOk === true && part.mediaAddressType === this._agentDeviceType)
7562                 {
7563                   droppableParticipants.push(part);
7564                 }
7565             }
7566         }
7567 
7568         return Utilities.getArray(droppableParticipants);
7569         },
7570 
7571         _isParticipantStateDroppable : function (part)
7572         {
7573           var isParticipantStateDroppable = false;
7574           if (part.state === Dialog.ParticipantStates.ACTIVE || part.state === Dialog.ParticipantStates.ACCEPTED || part.state === Dialog.ParticipantStates.HELD)
7575           {
7576             isParticipantStateDroppable = true;
7577           }
7578           
7579           return isParticipantStateDroppable;
7580         },
7581         
7582         /**
7583          * Is the participant droppable
7584          *
7585          * @param {String} participantExt Extension of participant.
7586          * @returns {Boolean} True is droppable.
7587          */
7588         isParticipantDroppable : function (participantExt) {
7589           var droppableParticipants = null, isDroppable = false, idx, part, callStateOk;
7590           
7591           droppableParticipants = this.getDroppableParticipants();
7592           
7593           if (droppableParticipants) 
7594           {
7595             for(idx=0;idx<droppableParticipants.length;idx=idx+1)
7596             {
7597               part = droppableParticipants[idx];
7598              
7599               if (part.mediaAddress === participantExt)
7600               {
7601                 callStateOk = this._isParticipantStateDroppable(part);
7602 
7603                 //Remove non-agents & make sure callstate 
7604                 if (callStateOk === true && part.mediaAddressType === this._agentDeviceType)
7605                 {
7606                   isDroppable = true;
7607                   break;
7608                 }
7609               }
7610             }
7611           }
7612           
7613           return isDroppable;
7614         },
7615         
7616         /**
7617          * Retrieves a list of media properties from the dialog object.
7618          * @returns {Object} Map of call variables; names mapped to values.
7619          * Variables may include the following:<ul>
7620          * <li>dialedNumber: The number dialed.
7621          * <li>callType: The type of call. Call types include:<ul>
7622          *     <li>ACD_IN
7623          *     <li>PREROUTE_ACD_IN
7624          *     <li>PREROUTE_DIRECT_AGENT
7625          *     <li>TRANSFER
7626          *     <li>OTHER_IN
7627          *     <li>OUT
7628          *     <li>AGENT_INSIDE
7629          *     <li>CONSULT
7630          *     <li>CONFERENCE
7631          *     <li>SUPERVISOR_MONITOR
7632          *     <li>OUTBOUND
7633          *     <li>OUTBOUND_PREVIEW</ul>
7634          * <li>DNIS: The DNIS provided. For routed calls, this is the route point.
7635          * <li>wrapUpReason: A description of the call.
7636          * <li>Call Variables, by name.  The name indicates whether it is a call variable or ECC variable.
7637          * Call variable names start with callVariable#, where # is 1-10. ECC variable names (both scalar and array) are prepended with "user".
7638          * ECC variable arrays include an index enclosed within square brackets located at the end of the ECC array name.
7639          * <li>The following call variables provide additional details about an Outbound Option call:<ul>
7640          *     <li>BACampaign
7641          *     <li>BAAccountNumber
7642          *     <li>BAResponse
7643          *     <li>BAStatus<ul>
7644          *         <li>PREDICTIVE_OUTBOUND: A predictive outbound call.
7645          *         <li>PROGRESSIVE_OUTBOUND: A progressive outbound call.
7646          *         <li>PREVIEW_OUTBOUND_RESERVATION: Agent is reserved for a preview outbound call.
7647          *         <li>PREVIEW_OUTBOUND: Agent is on a preview outbound call.</ul>
7648          *     <li>BADialedListID
7649          *     <li>BATimeZone
7650          *     <li>BABuddyName</ul></ul>
7651          *     
7652          */
7653         getMediaProperties: function () {
7654             var mpData, currentMediaPropertiesMap, thisMediaPropertiesJQuery;
7655 
7656             this.isLoaded();
7657             
7658             // We have to convert to jQuery object to do a proper compare
7659             thisMediaPropertiesJQuery = jQuery(this.getData().mediaProperties);
7660             
7661             if ((this._lastMediaPropertiesJQuery !== undefined) 
7662                     && (this._lastMediaPropertiesMap !== undefined) 
7663                     && (this._lastMediaPropertiesJQuery.is(thisMediaPropertiesJQuery))) {
7664 
7665                     return this._lastMediaPropertiesMap;
7666             }
7667             
7668             currentMediaPropertiesMap = {};
7669 
7670             mpData = this.getData().mediaProperties;
7671 
7672             if (mpData) {
7673                 if (mpData.callvariables && mpData.callvariables.CallVariable) {
7674                     jQuery.each(mpData.callvariables.CallVariable, function (i, callVariable) {
7675 			currentMediaPropertiesMap[callVariable.name] = callVariable.value;
7676                     });
7677                 }
7678 
7679                 jQuery.each(mpData, function (key, value) {
7680                     if (key !== 'callvariables') {
7681 			currentMediaPropertiesMap[key] = value;
7682                     }
7683                 });
7684             }
7685             
7686             this._lastMediaPropertiesMap = currentMediaPropertiesMap;
7687             this._lastMediaPropertiesJQuery = thisMediaPropertiesJQuery;
7688 
7689             return this._lastMediaPropertiesMap;
7690         },
7691 
7692         /**
7693          * Retrieves information about the currently scheduled callback, if any.
7694          * @returns {Object} If no callback has been set, will return undefined. If 
7695          * a callback has been set, it will return a map with one or more of the 
7696          * following entries, depending on what values have been set. 
7697          *    callbackTime   - the callback time, if it has been set.
7698          *    callbackNumber - the callback number, if it has been set.
7699          */
7700         getCallbackInfo: function() {
7701             this.isLoaded();
7702             return this.getData().scheduledCallbackInfo;
7703         },
7704 
7705         /**
7706          * @private
7707          * Invoke a request to the server given a content body and handlers.
7708          *
7709          * @param {Object} contentBody
7710          *     A JS object containing the body of the action request.
7711          * @param {finesse.interfaces.RequestHandlers} handlers
7712          *     An object containing the handlers for the request
7713          */
7714         _makeRequest: function (contentBody, handlers) {
7715             // Protect against null dereferencing of options allowing its
7716             // (nonexistent) keys to be read as undefined
7717             handlers = handlers || {};
7718 
7719             this.restRequest(this.getRestUrl(), {
7720                 method: 'PUT',
7721                 success: handlers.success,
7722                 error: handlers.error,
7723                 content: contentBody
7724             });
7725         },
7726 
7727         /**
7728          * Invoke a consult call out to a destination.
7729          *
7730          * @param {String} mediaAddress
7731          *     The media address of the user performing the consult call.
7732          * @param {String} toAddress
7733          *     The destination address of the consult call.
7734          * @param {finesse.interfaces.RequestHandlers} handlers
7735          *     An object containing the handlers for the request
7736          */
7737         makeConsultCall: function (mediaAddress, toAddress, handlers) {
7738             this.isLoaded();
7739             var contentBody = {};
7740             contentBody[this.getRestType()] = {
7741                 "targetMediaAddress": mediaAddress,
7742                 "toAddress": toAddress,
7743                 "requestedAction": Dialog.Actions.CONSULT_CALL
7744             };
7745             this._makeRequest(contentBody, handlers);
7746             return this; // Allow cascading
7747         },
7748         
7749         /**
7750          * Invoke a single step transfer request.
7751          *
7752          * @param {String} mediaAddress
7753          *     The media address of the user performing the single step transfer.
7754          * @param {String} toAddress
7755          *     The destination address of the single step transfer.
7756          * @param {finesse.interfaces.RequestHandlers} handlers
7757          *     An object containing the handlers for the request
7758          */
7759         initiateDirectTransfer: function (mediaAddress, toAddress, handlers) {
7760             this.isLoaded();
7761             var contentBody = {};
7762             contentBody[this.getRestType()] = {
7763                 "targetMediaAddress": mediaAddress,
7764                 "toAddress": toAddress,
7765                 "requestedAction": Dialog.Actions.TRANSFER_SST
7766             };
7767             this._makeRequest(contentBody, handlers);
7768             return this; // Allow cascading
7769         },
7770 
7771         /**
7772          * Update this dialog's wrap-up reason.
7773          *
7774          * @param {String} wrapUpReason
7775          *     The new wrap-up reason for this dialog
7776          * @param {finesse.interfaces.RequestHandlers} handlers
7777          *     An object containing the handlers for the request
7778          */
7779         updateWrapUpReason: function (wrapUpReason, options)
7780         {
7781             this.isLoaded();
7782             var mediaProperties =
7783             {
7784                 "wrapUpReason": wrapUpReason
7785             };
7786 
7787             options = options || {};
7788             options.content = {};
7789             options.content[this.getRestType()] =
7790             {
7791                 "mediaProperties": mediaProperties,
7792                 "requestedAction": Dialog.Actions.UPDATE_CALL_DATA
7793             };
7794             options.method = "PUT";
7795             this.restRequest(this.getRestUrl(), options);
7796 
7797             return this;
7798         },
7799 
7800         /**
7801          * Invoke a request to server based on the action given.
7802          * @param {String} mediaAddress
7803          *     The media address of the user performing the action.
7804          * @param {finesse.restservices.Dialog.Actions} action
7805          *     The action string indicating the action to invoke on dialog.
7806          * @param {finesse.interfaces.RequestHandlers} handlers
7807          *     An object containing the handlers for the request
7808          */
7809         requestAction: function (mediaAddress, action, handlers) {
7810             this.isLoaded();
7811             var contentBody = {};
7812             contentBody[this.getRestType()] = {
7813                 "targetMediaAddress": mediaAddress,
7814                 "requestedAction": action
7815             };
7816             this._makeRequest(contentBody, handlers);
7817             return this; // Allow cascading
7818         },
7819         
7820         /**
7821          * Wrapper around "requestAction" to request PARTICIPANT_DROP action.
7822          *
7823          * @param targetMediaAddress is the address to drop
7824          * @param {finesse.interfaces.RequestHandlers} handlers
7825          *     An object containing the handlers for the request
7826          */
7827         dropParticipant: function (targetMediaAddress, handlers) {
7828             this.requestAction(targetMediaAddress, Dialog.Actions.PARTICIPANT_DROP, handlers);
7829         },
7830         
7831         /**
7832          * Invoke a request to server to send DTMF digit tones.
7833          * @param {String} mediaAddress
7834          * @param {String} action
7835          *     The action string indicating the action to invoke on dialog.
7836          * @param {finesse.interfaces.RequestHandlers} handlers
7837          *     An object containing the handlers for the request
7838          */
7839         sendDTMFRequest: function (mediaAddress, handlers, digit) {
7840             this.isLoaded();
7841             var contentBody = {};
7842             contentBody[this.getRestType()] = {
7843                 "targetMediaAddress": mediaAddress,
7844                 "requestedAction": "SEND_DTMF",
7845                 "actionParams": {
7846                     "ActionParam": {
7847                         "name": "dtmfString",
7848                         "value": digit
7849                     }
7850                 }
7851             };
7852             this._makeRequest(contentBody, handlers);
7853             return this; // Allow cascading
7854         },
7855 
7856         /**
7857          * Invoke a request to server to set the time for a callback.
7858          * @param {String} mediaAddress
7859          * @param {String} callbackTime 
7860          *     The requested time for the callback, in YYYY-MM-DDTHH:MM format
7861          *     (ex: 2013-12-24T23:59)
7862          * @param {finesse.interfaces.RequestHandlers} handlers
7863          *     An object containing the handlers for the request
7864          */
7865         updateCallbackTime: function (mediaAddress, callbackTime, handlers) {
7866             this.isLoaded();
7867             var contentBody = {};
7868             contentBody[this.getRestType()] = {
7869                 "targetMediaAddress": mediaAddress,
7870                 "requestedAction": Dialog.Actions.UPDATE_SCHEDULED_CALLBACK,
7871                 "actionParams": {
7872                     "ActionParam": {
7873                         "name": "callbackTime",
7874                         "value": callbackTime
7875                     }
7876                 }
7877             };
7878             this._makeRequest(contentBody, handlers);
7879             return this; // Allow cascading
7880         },
7881 
7882         /**
7883          * Invoke a request to server to set the number for a callback.
7884          * @param {String} mediaAddress
7885          * @param {String} callbackNumber
7886          *     The requested number to call for the callback
7887          * @param {finesse.interfaces.RequestHandlers} handlers
7888          *     An object containing the handlers for the request
7889          */
7890         updateCallbackNumber: function (mediaAddress, callbackNumber, handlers) {
7891             this.isLoaded();
7892             var contentBody = {};
7893             contentBody[this.getRestType()] = {
7894                 "targetMediaAddress": mediaAddress,
7895                 "requestedAction": Dialog.Actions.UPDATE_SCHEDULED_CALLBACK,
7896                 "actionParams": {
7897                     "ActionParam": {
7898                         "name": "callbackNumber",
7899                         "value": callbackNumber
7900                     }
7901                 }
7902             };
7903             this._makeRequest(contentBody, handlers);
7904             return this; // Allow cascading
7905         },
7906 
7907         /**
7908          * Invoke a request to server to cancel a callback.
7909          * @param {String} mediaAddress
7910          * @param {finesse.interfaces.RequestHandlers} handlers
7911          *     An object containing the handlers for the request
7912          */
7913         cancelCallback: function (mediaAddress, handlers) {
7914             this.isLoaded();
7915             var contentBody = {};
7916             contentBody[this.getRestType()] = {
7917                 "targetMediaAddress": mediaAddress,
7918                 "requestedAction": Dialog.Actions.CANCEL_SCHEDULED_CALLBACK
7919             };
7920             this._makeRequest(contentBody, handlers);
7921             return this; // Allow cascading
7922         },
7923 
7924         /**
7925          * Invoke a request to server to reclassify the call type.
7926          * @param {String} mediaAddress
7927          * @param {String} callbackNumber
7928          *     The requested number to call for the callback
7929          * @param {finesse.interfaces.RequestHandlers} handlers
7930          *     An object containing the handlers for the request
7931          */
7932         reclassifyCall: function (mediaAddress, classification, handlers) {
7933             this.isLoaded();
7934             var contentBody = {};
7935             contentBody[this.getRestType()] = {
7936                 "targetMediaAddress": mediaAddress,
7937                 "requestedAction": Dialog.Actions.RECLASSIFY,
7938                 "actionParams": {
7939                     "ActionParam": {
7940                         "name": "outboundClassification",
7941                         "value": classification
7942                     }
7943                 }
7944             };
7945             this._makeRequest(contentBody, handlers);
7946             return this; // Allow cascading
7947         }       
7948 
7949 
7950     });
7951 
7952     Dialog.Actions = /** @lends finesse.restservices.Dialog.Actions.prototype */ {
7953             /**
7954              * Drops the Participant from the Dialog.
7955              */
7956             DROP: "DROP",
7957             /**
7958              * Answers a Dialog.
7959              */
7960             ANSWER: "ANSWER",
7961             /**
7962              * Holds the Dialog.
7963              */
7964             HOLD: "HOLD",
7965             /**
7966              * Barges into a Call Dialog.
7967              */
7968             BARGE_CALL: "BARGE_CALL",
7969             /**
7970              * Allow as Supervisor to Drop a Participant from the Dialog.
7971              */
7972             PARTICIPANT_DROP: "PARTICIPANT_DROP",
7973             /**
7974              * Makes a new Call Dialog.
7975              */
7976             MAKE_CALL: "MAKE_CALL",
7977             /**
7978              * Retrieves a Dialog that is on Hold.
7979              */
7980             RETRIEVE: "RETRIEVE",
7981             /**
7982              * Sets the time or number for a callback. Can be
7983              * either a new callback, or updating an existing one.
7984              */
7985             UPDATE_SCHEDULED_CALLBACK: "UPDATE_SCHEDULED_CALLBACK",
7986             /**
7987              * Cancels a callback.
7988              */
7989             CANCEL_SCHEDULED_CALLBACK: "CANCEL_SCHEDULED_CALLBACK",
7990             /**
7991              * Initiates a Consult Call.
7992              */
7993             CONSULT_CALL: "CONSULT_CALL",
7994             /**
7995              * Initiates a Transfer of a Dialog.
7996              */
7997             TRANSFER: "TRANSFER",
7998             /**
7999              * Initiates a Single-Step Transfer of a Dialog.
8000              */
8001             TRANSFER_SST: "TRANSFER_SST",
8002             /**
8003              * Initiates a Conference of a Dialog.
8004              */
8005             CONFERENCE: "CONFERENCE",
8006             /**
8007              * Changes classification for a call
8008              */
8009             RECLASSIFY: "RECLASSIFY", 
8010             /**
8011              * Updates data on a Call Dialog.
8012              */
8013             UPDATE_CALL_DATA: "UPDATE_CALL_DATA",
8014             /**
8015              * Initiates a Recording on a Call Dialog.
8016              */
8017             START_RECORDING : "START_RECORDING",
8018             /**
8019              * Sends DTMF (dialed digits) to a Call Dialog.
8020              */
8021             DTMF : "SEND_DTMF",            
8022             /**
8023              * Accepts a Dialog that is being Previewed.
8024              */
8025             ACCEPT: "ACCEPT",
8026             /**
8027              * Rejects a Dialog.
8028              */
8029             REJECT: "REJECT",
8030             /**
8031              * Closes a Dialog.
8032              */
8033             CLOSE : "CLOSE",
8034             /**
8035              * @class Set of action constants for a Dialog.  These should be used for
8036              * {@link finesse.restservices.Dialog#requestAction}.
8037              * @constructs
8038              */
8039             _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
8040         };  
8041 
8042     Dialog.States = /** @lends finesse.restservices.Dialog.States.prototype */ {
8043        /**
8044          * Indicates that the call is ringing at a device.
8045          */
8046         ALERTING: "ALERTING",
8047         /**
8048          * Indicates that the phone is off the hook at a device.
8049          */
8050         INITIATING: "INITIATING",
8051         /**
8052          * Indicates that the dialog has a least one active participant.
8053          */
8054         ACTIVE: "ACTIVE",
8055         /**
8056          * Indicates that the dialog has no active participants.
8057          */
8058         DROPPED: "DROPPED",
8059         /**
8060          * Indicates that the phone is dialing at the device.
8061          */
8062         INITIATED: "INITIATED",
8063         /**
8064          * Indicates that the dialog has failed.
8065          * @see Dialog.ReasonStates
8066          */
8067         FAILED: "FAILED",
8068         /**
8069          * Indicates that the user has accepted an OUTBOUND_PREVIEW dialog.
8070          */
8071         ACCEPTED: "ACCEPTED",
8072         /**
8073          * @class Possible Dialog State constants.
8074          * The State flow of a typical in-bound Dialog is as follows: INITIATING, INITIATED, ALERTING, ACTIVE, DROPPED.
8075          * @constructs
8076          */
8077         _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
8078     };
8079 
8080     Dialog.ParticipantStates = /** @lends finesse.restservices.Dialog.ParticipantStates.prototype */ {
8081         /**
8082           * Indicates that an incoming call is ringing on the device.
8083           */
8084          ALERTING: "ALERTING",
8085          /**
8086           * Indicates that an outgoing call, not yet active, exists on the device.
8087           */
8088          INITIATING: "INITIATING",
8089          /**
8090           * Indicates that the participant is active on the call.
8091           */
8092          ACTIVE: "ACTIVE",
8093          /**
8094           * Indicates that the participant has dropped from the call.
8095           */
8096          DROPPED: "DROPPED",
8097          /**
8098           * Indicates that the participant has held their connection to the call.
8099           */
8100          HELD: "HELD",
8101          /**
8102           * Indicates that the phone is dialing at a device.
8103           */
8104          INITIATED: "INITIATED",
8105          /**
8106           * Indicates that the call failed.
8107           * @see Dialog.ReasonStates
8108           */
8109          FAILED: "FAILED",
8110          /**
8111           * Indicates that the participant is not in active state on the call, but is wrapping up after the participant has dropped from the call.
8112           */
8113          WRAP_UP: "WRAP_UP",
8114          /**
8115           * Indicates that the participant has accepted the dialog.  This state is applicable to OUTBOUND_PREVIEW dialogs.
8116           */
8117          ACCEPTED: "ACCEPTED",
8118          /**
8119           * @class Possible Dialog Participant State constants.
8120           * @constructs
8121           */
8122          _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
8123      };
8124 
8125     Dialog.ReasonStates = /** @lends finesse.restservices.Dialog.ReasonStates.prototype */ {
8126        /**
8127         * Dialog was Busy.  This will typically be for a Failed Dialog.
8128         */
8129         BUSY: "BUSY",
8130         /**
8131          * Dialog reached a Bad Destination.  This will typically be for a Failed Dialog.
8132          */
8133         BAD_DESTINATION: "BAD_DESTINATION",
8134         /**
8135          * All Other Reasons.  This will typically be for a Failed Dialog.
8136          */
8137         OTHER: "OTHER",
8138         /**
8139          * The Device Resource for the Dialog was not available.
8140          */
8141         DEVICE_RESOURCE_NOT_AVAILABLE : "DEVICE_RESOURCE_NOT_AVAILABLE",
8142         /**
8143          * @class Possible dialog state reasons code constants.
8144          * @constructs
8145          */
8146         _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
8147     };
8148    
8149     window.finesse = window.finesse || {};
8150     window.finesse.restservices = window.finesse.restservices || {};
8151     window.finesse.restservices.Dialog = Dialog;
8152     
8153     
8154     return Dialog;
8155 });
8156 
8157 /**
8158  * JavaScript representation of the Finesse Dialogs collection
8159  * object which contains a list of Dialog objects.
8160  *
8161  * @requires finesse.clientservices.ClientServices
8162  * @requires Class
8163  * @requires finesse.FinesseBase
8164  * @requires finesse.restservices.RestBase
8165  * @requires finesse.restservices.Dialog
8166  */
8167 /** @private */
8168 define('restservices/Dialogs',[
8169     'restservices/RestCollectionBase',
8170     'restservices/Dialog'
8171 ],
8172 function (RestCollectionBase, Dialog) {
8173     var Dialogs = RestCollectionBase.extend(/** @lends finesse.restservices.Dialogs.prototype */{
8174 
8175         /**
8176          * @class
8177          * JavaScript representation of a Dialogs collection object. Also exposes
8178          * methods to operate on the object against the server.
8179          * @augments finesse.restservices.RestCollectionBase
8180          * @constructs
8181          * @see finesse.restservices.Dialog
8182          * @example
8183          *  _dialogs = _user.getDialogs( {
8184          *      onCollectionAdd : _handleDialogAdd,
8185          *      onCollectionDelete : _handleDialogDelete,
8186          *      onLoad : _handleDialogsLoaded
8187          *  });
8188          *  
8189          * _dialogCollection = _dialogs.getCollection();
8190          * for (var dialogId in _dialogCollection) {
8191          *     if (_dialogCollection.hasOwnProperty(dialogId)) {
8192          *         _dialog = _dialogCollection[dialogId];
8193          *         etc...
8194          *     }
8195          * }
8196          */
8197         _fakeConstuctor: function () {
8198             /* This is here to hide the real init constructor from the public docs */
8199         },
8200         
8201         /**
8202          * @private
8203          * @param {Object} options
8204          *     An object with the following properties:<ul>
8205          *         <li><b>id:</b> The id of the object being constructed</li>
8206          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
8207          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
8208          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
8209          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
8210          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
8211          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
8212          *             <li><b>content:</b> {String} Raw string of response</li>
8213          *             <li><b>object:</b> {Object} Parsed object of response</li>
8214          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
8215          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
8216          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
8217          *             </ul></li>
8218          *         </ul></li>
8219          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
8220          **/
8221         init: function (options) {
8222             this._super(options);
8223         },
8224 
8225         /**
8226          * @private
8227          * Gets the REST class for the current object - this is the Dialogs class.
8228          */
8229         getRestClass: function () {
8230             return Dialogs;
8231         },
8232 
8233         /**
8234          * @private
8235          * Gets the REST class for the objects that make up the collection. - this
8236          * is the Dialog class.
8237          */
8238         getRestItemClass: function () {
8239             return Dialog;
8240         },
8241 
8242         /**
8243          * @private
8244          * Gets the REST type for the current object - this is a "Dialogs".
8245          */
8246         getRestType: function () {
8247             return "Dialogs";
8248         },
8249 
8250         /**
8251          * @private
8252          * Gets the REST type for the objects that make up the collection - this is "Dialogs".
8253          */
8254         getRestItemType: function () {
8255             return "Dialog";
8256         },
8257 
8258         /**
8259          * @private
8260          * Override default to indicates that the collection doesn't support making
8261          * requests.
8262          */
8263         supportsRequests: true,
8264 
8265         /**
8266          * @private
8267          * Override default to indicates that the collection subscribes to its objects.
8268          */
8269         supportsRestItemSubscriptions: true,
8270 
8271         /**
8272          * @private
8273          * Create a new Dialog in this collection
8274          *
8275          * @param {String} toAddress
8276          *     The to address of the new Dialog
8277          * @param {String} fromAddress
8278          *     The from address of the new Dialog
8279          * @param {finesse.interfaces.RequestHandlers} handlers
8280          *     An object containing the (optional) handlers for the request.
8281          * @return {finesse.restservices.Dialogs}
8282          *     This Dialogs object, to allow cascading.
8283          */
8284         createNewCallDialog: function (toAddress, fromAddress, handlers)
8285         {
8286             var contentBody = {};
8287             contentBody[this.getRestItemType()] = {
8288                 "requestedAction": "MAKE_CALL",
8289                 "toAddress": toAddress,
8290                 "fromAddress": fromAddress
8291             };
8292 
8293             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
8294             handlers = handlers || {};
8295 
8296             this.restRequest(this.getRestUrl(), {
8297                 method: 'POST',
8298                 success: handlers.success,
8299                 error: handlers.error,
8300                 content: contentBody
8301             });
8302             return this; // Allow cascading
8303         },
8304 
8305         /**
8306          * @private
8307          * Create a new Dialog in this collection as a result of a requested action
8308          *
8309          * @param {String} toAddress
8310          *     The to address of the new Dialog
8311          * @param {String} fromAddress
8312          *     The from address of the new Dialog
8313          * @param {finesse.restservices.Dialog.Actions} actionType
8314          *     The associated action to request for creating this new dialog
8315          * @param {finesse.interfaces.RequestHandlers} handlers
8316          *     An object containing the (optional) handlers for the request.
8317          * @return {finesse.restservices.Dialogs}
8318          *     This Dialogs object, to allow cascading.
8319          */
8320         createNewSuperviseCallDialog: function (toAddress, fromAddress, actionType, handlers)
8321         {
8322             var contentBody = {};
8323             this._isLoaded = true;
8324 
8325             contentBody[this.getRestItemType()] = {
8326                 "requestedAction": actionType,
8327                 "toAddress": toAddress,
8328                 "fromAddress": fromAddress
8329             };
8330 
8331             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
8332             handlers = handlers || {};
8333 
8334             this.restRequest(this.getRestUrl(), {
8335                 method: 'POST',
8336                 success: handlers.success,
8337                 error: handlers.error,
8338                 content: contentBody
8339             });
8340             return this; // Allow cascading
8341         },
8342         
8343         /**
8344          * @private
8345          * Create a new Dialog in this collection as a result of a requested action
8346          * @param {String} fromAddress
8347          *     The from address of the new Dialog
8348          * @param {String} toAddress
8349          *     The to address of the new Dialog
8350          * @param {finesse.restservices.Dialog.Actions} actionType
8351          *     The associated action to request for creating this new dialog
8352          * @param {String} dialogUri
8353          *     The associated uri of SUPERVISOR_MONITOR call
8354          * @param {finesse.interfaces.RequestHandlers} handlers
8355          *     An object containing the (optional) handlers for the request.
8356          * @return {finesse.restservices.Dialogs}
8357          *     This Dialogs object, to allow cascading.
8358          */
8359         createNewBargeCall: function (fromAddress, toAddress, actionType, dialogURI, handlers) {
8360             this.isLoaded();
8361          
8362             var contentBody = {};
8363             contentBody[this.getRestItemType()] = {
8364                 "fromAddress": fromAddress,
8365                 "toAddress": toAddress,
8366                 "requestedAction": actionType,
8367                 "associatedDialogUri": dialogURI
8368                 
8369             };
8370             // (nonexistent) keys to be read as undefined
8371             handlers = handlers || {};  
8372             this.restRequest(this.getRestUrl(), {
8373                 method: 'POST',
8374                 success: handlers.success,
8375                 error: handlers.error,
8376                 content: contentBody
8377             });
8378             return this; // Allow cascading
8379         },
8380 
8381         /**
8382          * Utility method to get the number of dialogs in this collection.
8383          * 'excludeSilentMonitor' flag is provided as an option to exclude calls with type
8384          * 'SUPERVISOR_MONITOR' from the count.
8385          * @param  {Boolean} excludeSilentMonitor If true, calls with type of 'SUPERVISOR_MONITOR' will be excluded from the count.
8386          * @return {Number} The number of dialogs in this collection.
8387          */
8388         getDialogCount: function (excludeSilentMonitor) {
8389             this.isLoaded();
8390 
8391             var dialogId, count = 0;
8392             if (excludeSilentMonitor) {
8393                 for (dialogId in this._collection) {
8394                     if (this._collection.hasOwnProperty(dialogId)) {
8395                         if (this._collection[dialogId].getCallType() !== 'SUPERVISOR_MONITOR') {
8396                             count += 1;
8397                         }
8398                     }
8399                 }
8400 
8401                 return count;
8402             } else {
8403                 return this.length;
8404             }        
8405         }
8406 
8407     });
8408     
8409     window.finesse = window.finesse || {};
8410     window.finesse.restservices = window.finesse.restservices || {};
8411     window.finesse.restservices.Dialogs = Dialogs;
8412     
8413     return Dialogs;
8414 });
8415 
8416 /**
8417  * JavaScript representation of the Finesse ClientLog object
8418  *
8419  * @requires finesse.clientservices.ClientServices
8420  * @requires Class
8421  * @requires finesse.FinesseBase
8422  * @requires finesse.restservices.RestBase
8423  */
8424 
8425 /** The following comment is to prevent jslint errors about 
8426  * using variables before they are defined.
8427  */
8428 /** @private */
8429 /*global finesse*/
8430 
8431 define('restservices/ClientLog',["restservices/RestBase"], function (RestBase) {
8432     
8433     var ClientLog = RestBase.extend(/** @lends finesse.restservices.ClientLog.prototype */{    
8434         /**
8435          * @private
8436          * Returns whether this object supports transport logs
8437          */
8438         doNotLog : true,
8439 
8440         doNotRefresh: true,
8441         
8442         explicitSubscription : true,
8443         
8444         /**
8445          * @class
8446          * @private
8447          * JavaScript representation of a ClientLog object. Also exposes methods to operate
8448          * on the object against the server.
8449          *
8450          * @param {Object} options
8451          *     An object with the following properties:<ul>
8452          *         <li><b>id:</b> The id of the object being constructed</li>
8453          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
8454          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
8455          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
8456          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
8457          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
8458          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
8459          *             <li><b>content:</b> {String} Raw string of response</li>
8460          *             <li><b>object:</b> {Object} Parsed object of response</li>
8461          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
8462          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
8463          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
8464          *             </ul></li>
8465          *         </ul></li>
8466          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
8467          * @constructs
8468          * @augments finesse.restservices.RestBase
8469          **/
8470         init: function (options) {
8471             this._super({
8472                 id: "", 
8473                 data: {clientLog : null},
8474                 onAdd: options.onAdd,
8475                 onChange: options.onChange,
8476                 onLoad: options.onLoad,
8477                 onError: options.onError,
8478                 parentObj: options.parentObj
8479                 });
8480         },
8481 
8482         /**
8483          * @private
8484          * Gets the REST class for the current object - this is the ClientLog object.
8485          */
8486         getRestClass: function () {
8487             return ClientLog;
8488         },
8489 
8490         /**
8491          * @private
8492          * Gets the REST type for the current object - this is a "ClientLog".
8493          */
8494         getRestType: function ()
8495         {
8496             return "ClientLog";
8497         },
8498         
8499         /**
8500          * @private
8501          * Gets the node path for the current object
8502          * @returns {String} The node path
8503          */
8504         getXMPPNodePath: function () {
8505             return this.getRestUrl();
8506         },
8507            
8508         /**
8509          * @private
8510          * Invoke a request to the server given a content body and handlers.
8511          *
8512          * @param {Object} contentBody
8513          *     A JS object containing the body of the action request.
8514          * @param {Object} handlers
8515          *     An object containing the following (optional) handlers for the request:<ul>
8516          *         <li><b>success(rsp):</b> A callback function for a successful request to be invoked with the following
8517          *         response object as its only parameter:<ul>
8518          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
8519          *             <li><b>content:</b> {String} Raw string of response</li>
8520          *             <li><b>object:</b> {Object} Parsed object of response</li></ul>
8521          *         <li>A error callback function for an unsuccessful request to be invoked with the
8522          *         error response object as its only parameter:<ul>
8523          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
8524          *             <li><b>content:</b> {String} Raw string of response</li>
8525          *             <li><b>object:</b> {Object} Parsed object of response (HTTP errors)</li>
8526          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
8527          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
8528          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
8529          *             </ul></li>
8530          *         </ul>
8531          */
8532         sendLogs: function (contentBody, handlers) {
8533             // Protect against null dereferencing of options allowing its
8534             // (nonexistent) keys to be read as undefined
8535             handlers = handlers || {};
8536 
8537             this.restRequest(this.getRestUrl(), {
8538                 method: 'POST',
8539                 //success: handlers.success,
8540                 error: handlers.error,
8541                 content: contentBody
8542             });
8543         }
8544     });
8545     
8546     window.finesse = window.finesse || {};
8547     window.finesse.restservices = window.finesse.restservices || {};
8548     window.finesse.restservices.ClientLog = ClientLog;
8549     
8550     return ClientLog;
8551 });
8552 
8553 /**
8554  * JavaScript representation of the Finesse Queue object
8555  * @requires finesse.clientservices.ClientServices
8556  * @requires Class
8557  * @requires finesse.FinesseBase
8558  * @requires finesse.restservices.RestBase
8559  */
8560 
8561 /** @private */
8562 define('restservices/Queue',[
8563     'restservices/RestBase',
8564     'utilities/Utilities'
8565 ],
8566 function (RestBase, Utilities) {
8567     var Queue = RestBase.extend(/** @lends finesse.restservices.Queue.prototype */{
8568 
8569         /**
8570          * @class
8571          * A Queue is a list of Contacts available to a User for quick dial.
8572          * 
8573          * @augments finesse.restservices.RestBase
8574          * @constructs
8575          */
8576         _fakeConstuctor: function () {
8577             /* This is here to hide the real init constructor from the public docs */
8578         },
8579         
8580 		/**
8581 		 * @private
8582 		 * JavaScript representation of a Queue object. Also exposes methods to operate
8583 		 * on the object against the server.
8584 		 *
8585 		 * @constructor
8586 		 * @param {String} id
8587 		 *     Not required...
8588 		 * @param {Object} callbacks
8589 		 *     An object containing callbacks for instantiation and runtime
8590 		 * @param {Function} callbacks.onLoad(this)
8591 		 *     Callback to invoke upon successful instantiation
8592 		 * @param {Function} callbacks.onLoadError(rsp)
8593 		 *     Callback to invoke on instantiation REST request error
8594 		 *     as passed by finesse.clientservices.ClientServices.ajax()
8595 		 *     {
8596 		 *         status: {Number} The HTTP status code returned
8597 		 *         content: {String} Raw string of response
8598 		 *         object: {Object} Parsed object of response
8599 		 *         error: {Object} Wrapped exception that was caught
8600 		 *         error.errorType: {String} Type of error that was caught
8601 		 *         error.errorMessage: {String} Message associated with error
8602 		 *     }
8603 		 * @param {Function} callbacks.onChange(this)
8604 		 *     Callback to invoke upon successful update
8605 		 * @param {Function} callbacks.onError(rsp)
8606 		 *     Callback to invoke on update error (refresh or event)
8607 		 *     as passed by finesse.clientservices.ClientServices.ajax()
8608 		 *     {
8609 		 *         status: {Number} The HTTP status code returned
8610 		 *         content: {String} Raw string of response
8611 		 *         object: {Object} Parsed object of response
8612 		 *         error: {Object} Wrapped exception that was caught
8613 		 *         error.errorType: {String} Type of error that was caught
8614 		 *         error.errorMessage: {String} Message associated with error
8615 		 *     }
8616 		 *  
8617 		 */
8618         init: function (id, callbacks, restObj) {
8619             this._super(id, callbacks, restObj);
8620         },
8621 
8622         /**
8623          * @private
8624          * Gets the REST class for the current object - this is the Queue object.
8625          */
8626         getRestClass: function () {
8627             return Queue;
8628         },
8629 
8630         /**
8631          * @private
8632          * Gets the REST type for the current object - this is a "Queue".
8633          */
8634         getRestType: function () {
8635             return "Queue";
8636         },
8637 
8638         /**
8639          * @private
8640          * Returns whether this object supports subscriptions
8641          */
8642         supportsSubscriptions: function () {
8643             return true;
8644         },
8645         
8646         /**
8647          * @private
8648          * Specifies whether this object's subscriptions need to be explicitly requested
8649          */
8650         explicitSubscription: true,
8651         
8652         /**
8653          * @private
8654          * Gets the node path for the current object - this is the team Users node
8655          * @returns {String} The node path
8656          */
8657         getXMPPNodePath: function () {
8658             return this.getRestUrl();
8659         },
8660         
8661         /**
8662          * Getter for the queue id
8663          * @returns {String}
8664          *     The id of the Queue
8665          */
8666         getId: function () {
8667             this.isLoaded();
8668             return this._id;
8669         },
8670         
8671         /**
8672          * Getter for the queue name
8673          * @returns {String}
8674          *      The name of the Queue
8675          */
8676         getName: function () {
8677             this.isLoaded();
8678             return this.getData().name;
8679         },
8680         
8681         /**
8682          * Getter for the queue statistics.
8683          * Supported statistics include:<br>
8684          *  - callsInQueue<br>
8685          *  - startTimeOfLongestCallInQueue<br>
8686          *  <br>
8687          *  These statistics can be accessed via dot notation:<br>
8688          *  i.e.: getStatistics().callsInQueue
8689          * @returns {Object}
8690          *      The Object with different statistics as properties.
8691          */
8692         getStatistics: function () {
8693             this.isLoaded();
8694             return this.getData().statistics;       
8695         },
8696 
8697         /**
8698          * Parses a uriString to retrieve the id portion
8699          * @param {String} uriString
8700          * @return {String} id
8701          */
8702         _parseIdFromUriString : function (uriString) {
8703             return Utilities.getId(uriString);
8704         }
8705 
8706     });
8707 	
8708 	window.finesse = window.finesse || {};
8709     window.finesse.restservices = window.finesse.restservices || {};
8710     window.finesse.restservices.Queue = Queue;
8711     
8712     return Queue;
8713 });
8714 
8715 /**
8716  * JavaScript representation of the Finesse Queues collection
8717  * object which contains a list of Queue objects.
8718  * @requires finesse.clientservices.ClientServices
8719  * @requires Class
8720  * @requires finesse.FinesseBase
8721  * @requires finesse.restservices.RestBase
8722  * @requires finesse.restservices.RestCollectionBase
8723  */
8724 
8725 /**
8726  * @class
8727  * JavaScript representation of a Queues collection object.
8728  *
8729  * @constructor
8730  * @borrows finesse.restservices.RestCollectionBase as finesse.restservices.Queues
8731  */
8732 
8733 /** @private */
8734 define('restservices/Queues',[
8735     'restservices/RestCollectionBase',
8736     'restservices/Queue'
8737 ],
8738 function (RestCollectionBase, Queue) {
8739     var Queues = RestCollectionBase.extend(/** @lends finesse.restservices.Queues.prototype */{
8740 
8741         /**
8742          * @class
8743          * JavaScript representation of a Queues collection object. 
8744          * @augments finesse.restservices.RestCollectionBase
8745          * @constructs
8746          * @see finesse.restservices.Queue
8747          * @example
8748          *  _queues = _user.getQueues( {
8749          *      onCollectionAdd : _handleQueueAdd,
8750          *      onCollectionDelete : _handleQueueDelete,
8751          *      onLoad : _handleQueuesLoaded
8752          *  });
8753          *  
8754          * _queueCollection = _queues.getCollection();
8755          * for (var queueId in _queueCollection) {
8756          *     if (_queueCollection.hasOwnProperty(queueId)) {
8757          *         _queue = _queueCollection[queueId];
8758          *         etc...
8759          *     }
8760          * }
8761          */
8762         _fakeConstuctor: function () {
8763             /* This is here to hide the real init constructor from the public docs */
8764         },
8765 	    
8766          /**
8767          * @private
8768          * JavaScript representation of a Queues object. Also exposes
8769          * methods to operate on the object against the server.
8770          *
8771          * @param {Object} options
8772          *     An object with the following properties:<ul>
8773          *         <li><b>id:</b> The id of the object being constructed</li>
8774          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
8775          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
8776          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
8777          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
8778          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
8779          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
8780          *             <li><b>content:</b> {String} Raw string of response</li>
8781          *             <li><b>object:</b> {Object} Parsed object of response</li>
8782          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
8783          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
8784          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
8785          *             </ul></li>
8786          *         </ul></li>
8787          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
8788          **/
8789         init: function (options) {
8790             this._super(options);           
8791         },
8792 
8793         /**
8794          * @private
8795          * Gets xmpp node path.
8796          */
8797         getXMPPNodePath: function () {
8798             return this.getRestUrl();
8799         },
8800 
8801         /**
8802          * @private
8803          * Gets the REST class for the current object - this is the Queues class.
8804          */
8805         getRestClass: function () {
8806             return Queues;
8807         },
8808 
8809         /**
8810          * @private
8811          * Gets the REST class for the objects that make up the collection. - this
8812          * is the Queue class.
8813          */
8814         getRestItemClass: function () {
8815             return Queue;
8816         },
8817 
8818         /**
8819          * @private
8820          * Gets the REST type for the current object - this is a "Queues".
8821          */
8822         getRestType: function () {
8823             return "Queues";
8824         },
8825         
8826         /**
8827          * @private
8828          * Gets the REST type for the objects that make up the collection - this is "Queue".
8829          */
8830         getRestItemType: function () {
8831             return "Queue";
8832         },
8833 
8834         explicitSubscription: true,
8835         
8836         handlesItemRefresh: true
8837     });
8838     
8839     window.finesse = window.finesse || {};
8840     window.finesse.restservices = window.finesse.restservices || {};
8841     window.finesse.restservices.Queues = Queues;
8842     
8843     return Queues;
8844 });
8845 
8846 /**
8847  * JavaScript representation of the Finesse WrapUpReason object.
8848  *
8849  * @requires finesse.clientservices.ClientServices
8850  * @requires Class
8851  * @requires finesse.FinesseBase
8852  * @requires finesse.restservices.RestBase
8853  */
8854 
8855 /** @private */
8856 define('restservices/WrapUpReason',['restservices/RestBase'], function (RestBase) {
8857 
8858     var WrapUpReason = RestBase.extend(/** @lends finesse.restservices.WrapUpReason.prototype */{
8859 
8860         /**
8861          * @class
8862          * A WrapUpReason is a code and description identifying a particular reason that a
8863          * User is in WORK (WrapUp) mode.
8864          * 
8865          * @augments finesse.restservices.RestBase
8866          * @see finesse.restservices.User
8867          * @see finesse.restservices.User.States#WORK
8868          * @constructs
8869          */
8870         _fakeConstuctor: function () {
8871             /* This is here to hide the real init constructor from the public docs */
8872         },
8873         
8874         /** 
8875          * @private
8876          * JavaScript representation of a WrapUpReason object. Also exposes
8877          * methods to operate on the object against the server.
8878          *
8879          * @param {Object} options
8880          *     An object with the following properties:<ul>
8881          *         <li><b>id:</b> The id of the object being constructed</li>
8882          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
8883          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
8884          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
8885          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
8886          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
8887          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
8888          *             <li><b>content:</b> {String} Raw string of response</li>
8889          *             <li><b>object:</b> {Object} Parsed object of response</li>
8890          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
8891          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
8892          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
8893          *             </ul></li>
8894          *         </ul></li>
8895          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
8896          **/
8897         init: function (options) {
8898             this._super(options);
8899         },
8900 
8901         /**
8902          * @private
8903          * Gets the REST class for the current object - this is the WrapUpReason class.
8904          * @returns {Object} The WrapUpReason class.
8905          */
8906         getRestClass: function () {
8907             return WrapUpReason;
8908         },
8909 
8910         /**
8911          * @private
8912          * Gets the REST type for the current object - this is a "WrapUpReason".
8913          * @returns {String} The WrapUpReason string.
8914          */
8915         getRestType: function () {
8916             return "WrapUpReason";
8917         },
8918 
8919         /**
8920          * @private
8921          * Gets the REST type for the current object - this is a "WrapUpReasons".
8922          * @returns {String} The WrapUpReasons string.
8923          */
8924         getParentRestType: function () {
8925             return "WrapUpReasons";
8926         },
8927 
8928         /**
8929          * @private
8930          * Override default to indicate that this object doesn't support making
8931          * requests.
8932          */
8933         supportsRequests: false,
8934 
8935         /**
8936          * @private
8937          * Override default to indicate that this object doesn't support subscriptions.
8938          */
8939         supportsSubscriptions: false,
8940 
8941         /**
8942          * Getter for the label.
8943          * @returns {String} The label.
8944          */
8945         getLabel: function () {
8946             this.isLoaded();
8947             return this.getData().label;
8948         },
8949 
8950         /**
8951          * @private
8952          * Getter for the forAll flag.
8953          * @returns {Boolean} True if global.
8954          */
8955         getForAll: function () {
8956             this.isLoaded();
8957             return this.getData().forAll;
8958         },
8959 
8960         /**
8961          * @private
8962          * Getter for the Uri value.
8963          * @returns {String} The Uri.
8964          */
8965         getUri: function () {
8966             this.isLoaded();
8967             return this.getData().uri;
8968         }
8969     });
8970 
8971     window.finesse = window.finesse || {};
8972     window.finesse.restservices = window.finesse.restservices || {};
8973     window.finesse.restservices.WrapUpReason = WrapUpReason;
8974         
8975     return WrapUpReason;
8976 });
8977 
8978 /**
8979 * JavaScript representation of the Finesse WrapUpReasons collection
8980 * object which contains a list of WrapUpReason objects.
8981  *
8982  * @requires finesse.clientservices.ClientServices
8983  * @requires Class
8984  * @requires finesse.FinesseBase
8985  * @requires finesse.restservices.RestBase
8986  * @requires finesse.restservices.Dialog
8987  * @requires finesse.restservices.RestCollectionBase
8988  */
8989 
8990 /** @private */
8991 define('restservices/WrapUpReasons',[
8992     'restservices/RestCollectionBase',
8993     'restservices/WrapUpReason'
8994 ],
8995 function (RestCollectionBase, WrapUpReason) {
8996 
8997     var WrapUpReasons = RestCollectionBase.extend(/** @lends finesse.restservices.WrapUpReasons.prototype */{
8998         
8999         /**
9000          * @class
9001          * JavaScript representation of a WrapUpReasons collection object. 
9002          * @augments finesse.restservices.RestCollectionBase
9003          * @constructs
9004          * @see finesse.restservices.WrapUpReason
9005          * @example
9006          *  _wrapUpReasons = _user.getWrapUpReasons ( {
9007          *      onCollectionAdd : _handleWrapUpReasonAdd,
9008          *      onCollectionDelete : _handleWrapUpReasonDelete,
9009          *      onLoad : _handleWrapUpReasonsLoaded
9010          *  });
9011          *  
9012          * _wrapUpReasonCollection = _wrapUpReasons.getCollection();
9013          * for (var wrapUpReasonId in _wrapUpReasonCollection) {
9014          *     if (_wrapUpReasonCollection.hasOwnProperty(wrapUpReasonId)) {
9015          *         _wrapUpReason = _wrapUpReasonCollection[wrapUpReasonId];
9016          *         etc...
9017          *     }
9018          * }
9019         */
9020         _fakeConstuctor: function () {
9021             /* This is here to hide the real init constructor from the public docs */
9022         },
9023         
9024         /** 
9025          * @private
9026          * JavaScript representation of a WrapUpReasons collection object. Also exposes
9027          * methods to operate on the object against the server.
9028          *
9029          * @param {Object} options
9030          *     An object with the following properties:<ul>
9031          *         <li><b>id:</b> The id of the object being constructed</li>
9032          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
9033          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
9034          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
9035          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
9036          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
9037          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
9038          *             <li><b>content:</b> {String} Raw string of response</li>
9039          *             <li><b>object:</b> {Object} Parsed object of response</li>
9040          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
9041          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
9042          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
9043          *             </ul></li>
9044          *         </ul></li>
9045          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
9046          **/
9047         init: function (options) {
9048             this._super(options);           
9049         },
9050 
9051         /**
9052          * @private
9053          * Gets the REST class for the current object - this is the WrapUpReasons class.
9054          */
9055         getRestClass: function () {
9056             return WrapUpReasons;
9057         },
9058 
9059         /**
9060          * @private
9061          * Gets the REST class for the objects that make up the collection. - this
9062          * is the WrapUpReason class.
9063          */
9064         getRestItemClass: function () {
9065             return WrapUpReason;
9066         },
9067 
9068         /**
9069          * @private
9070          * Gets the REST type for the current object - this is a "WrapUpReasons".
9071          */
9072         getRestType: function () {
9073             return "WrapUpReasons";
9074         },
9075         
9076         /**
9077          * @private
9078          * Gets the REST type for the objects that make up the collection - this is "WrapUpReason".
9079          */
9080         getRestItemType: function () {
9081             return "WrapUpReason";
9082         },
9083 
9084         /**
9085          * @private
9086          * Override default to indicates that the collection supports making
9087          * requests.
9088          */
9089         supportsRequests: true,
9090 
9091         /**
9092          * @private
9093          * Override default to indicate that this object doesn't support subscriptions.
9094          */
9095         supportsRestItemSubscriptions: false,
9096 
9097         /**
9098          * @private
9099          * Retrieve the Wrap-Up Reason Codes. This call will re-query the server and refresh the collection.
9100          *
9101          * @returns {finesse.restservices.WrapUpReasons}
9102          *     This ReadyReasonCodes object to allow cascading.
9103          */
9104         get: function () {
9105             // set loaded to false so it will rebuild the collection after the get
9106             this._loaded = false;
9107             // reset collection
9108             this._collection = {};
9109             // perform get
9110             this._synchronize();
9111             return this;
9112         }
9113         
9114     });
9115  
9116     window.finesse = window.finesse || {};
9117     window.finesse.restservices = window.finesse.restservices || {};
9118     window.finesse.restservices.WrapUpReasons = WrapUpReasons;
9119        
9120     return WrapUpReasons;
9121 });
9122 
9123 /**
9124  * JavaScript representation of the Finesse Contact object.
9125  * @requires finesse.clientservices.ClientServices
9126  * @requires Class
9127  * @requires finesse.FinesseBase
9128  * @requires finesse.restservices.RestBase
9129  */
9130 /** @private */
9131 define('restservices/Contact',['restservices/RestBase'], function (RestBase) {
9132 
9133     var Contact = RestBase.extend(/** @lends finesse.restservices.Contact.prototype */{
9134 
9135         /**
9136          * @class
9137          * A Contact is a single entry in a PhoneBook, consisting of a First and Last Name,
9138          * a Phone Number, and a Description.
9139          * 
9140          * @augments finesse.restservices.RestBase
9141          * @see finesse.restservices.PhoneBook
9142          * @constructs
9143          */
9144         _fakeConstuctor: function () {
9145             /* This is here to hide the real init constructor from the public docs */
9146         },
9147         
9148         /**
9149          * @private
9150          * @param {Object} options
9151          *     An object with the following properties:<ul>
9152          *         <li><b>id:</b> The id of the object being constructed</li>
9153          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
9154          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
9155          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
9156          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
9157          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
9158          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
9159          *             <li><b>content:</b> {String} Raw string of response</li>
9160          *             <li><b>object:</b> {Object} Parsed object of response</li>
9161          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
9162          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
9163          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
9164          *             </ul></li>
9165          *         </ul></li>
9166          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
9167          **/
9168         init: function (options) {
9169             this._super(options);
9170         },
9171 
9172         /**
9173          * @private
9174          * Gets the REST class for the current object - this is the Contact class.
9175          * @returns {Object} The Contact class.
9176          */
9177         getRestClass: function () {
9178             return Contact;
9179         },
9180 
9181         /**
9182          * @private
9183          * Gets the REST type for the current object - this is a "Contact".
9184          * @returns {String} The Contact string.
9185          */
9186         getRestType: function () {
9187             return "Contact";
9188         },
9189 
9190         /**
9191          * @private
9192          * Override default to indicate that this object doesn't support making
9193          * requests.
9194          */
9195         supportsRequests: false,
9196 
9197         /**
9198          * @private
9199          * Override default to indicate that this object doesn't support subscriptions.
9200          */
9201         supportsSubscriptions: false,
9202 
9203         /**
9204          * Getter for the firstName.
9205          * @returns {String} The firstName.
9206          */
9207         getFirstName: function () {
9208             this.isLoaded();
9209             return this.getData().firstName;
9210         },
9211 
9212         /**
9213          * Getter for the lastName.
9214          * @returns {String} The lastName.
9215          */
9216         getLastName: function () {
9217             this.isLoaded();
9218             return this.getData().lastName;
9219         },
9220 
9221         /**
9222          * Getter for the phoneNumber.
9223          * @returns {String} The phoneNumber.
9224          */
9225         getPhoneNumber: function () {
9226             this.isLoaded();
9227             return this.getData().phoneNumber;
9228         },
9229 
9230         /**
9231          * Getter for the description.
9232          * @returns {String} The description.
9233          */
9234         getDescription: function () {
9235             this.isLoaded();
9236             return this.getData().description;
9237         },
9238 
9239         /** @private */
9240         createPutSuccessHandler: function(contact, contentBody, successHandler){
9241             return function (rsp) {
9242                 // Update internal structure based on response. Here we
9243                 // inject the contentBody from the PUT request into the
9244                 // rsp.object element to mimic a GET as a way to take
9245                 // advantage of the existing _processResponse method.
9246                 rsp.object = contentBody;
9247                 contact._processResponse(rsp);
9248 
9249                 //Remove the injected Contact object before cascading response
9250                 rsp.object = {};
9251                 
9252                 //cascade response back to consumer's response handler
9253                 successHandler(rsp);
9254             };
9255         },
9256 
9257         /** @private */
9258         createPostSuccessHandler: function (contact, contentBody, successHandler) {
9259             return function (rsp) {
9260                 rsp.object = contentBody;
9261                 contact._processResponse(rsp);
9262 
9263                 //Remove the injected Contact object before cascading response
9264                 rsp.object = {};
9265 
9266                 //cascade response back to consumer's response handler
9267                 successHandler(rsp);
9268             };
9269         },
9270 
9271         /**
9272          * Add
9273          * @private
9274          */
9275         add: function (newValues, handlers) {
9276             // this.isLoaded();
9277             var contentBody = {};
9278 
9279             contentBody[this.getRestType()] = {
9280                 "firstName": newValues.firstName,
9281                 "lastName": newValues.lastName,
9282                 "phoneNumber": newValues.phoneNumber,
9283                 "description": newValues.description
9284             };
9285 
9286             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
9287             handlers = handlers || {};
9288 
9289             this.restRequest(this.getRestUrl(), {
9290                 method: 'POST',
9291                 success: this.createPostSuccessHandler(this, contentBody, handlers.success),
9292                 error: handlers.error,
9293                 content: contentBody
9294             });
9295 
9296             return this; // Allow cascading
9297         },
9298 
9299         /**
9300          * Update
9301          * @private
9302          */
9303         update: function (newValues, handlers) {
9304             this.isLoaded();
9305             var contentBody = {};
9306 
9307             contentBody[this.getRestType()] = {
9308                 "uri": this.getId(),
9309                 "firstName": newValues.firstName,
9310                 "lastName": newValues.lastName,
9311                 "phoneNumber": newValues.phoneNumber,
9312                 "description": newValues.description
9313             };
9314 
9315             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
9316             handlers = handlers || {};
9317 
9318             this.restRequest(this.getRestUrl(), {
9319                 method: 'PUT',
9320                 success: this.createPutSuccessHandler(this, contentBody, handlers.success),
9321                 error: handlers.error,
9322                 content: contentBody
9323             });
9324 
9325             return this; // Allow cascading
9326         },
9327 
9328 
9329         /**
9330          * Delete
9331          * @private
9332          */
9333         "delete": function ( handlers) {
9334             this.isLoaded();
9335 
9336             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
9337             handlers = handlers || {};
9338 
9339             this.restRequest(this.getRestUrl(), {
9340                 method: 'DELETE',
9341                 success: this.createPutSuccessHandler(this, {}, handlers.success),
9342                 error: handlers.error,
9343                 content: undefined
9344             });
9345 
9346             return this; // Allow cascading
9347         }
9348     });
9349 
9350     window.finesse = window.finesse || {};
9351     window.finesse.restservices = window.finesse.restservices || {};
9352     window.finesse.restservices.Contact = Contact;
9353     
9354     return Contact;
9355 });
9356 
9357 /**
9358 * JavaScript representation of the Finesse Contacts collection
9359 * object which contains a list of Contact objects.
9360  *
9361  * @requires finesse.clientservices.ClientServices
9362  * @requires Class
9363  * @requires finesse.FinesseBase
9364  * @requires finesse.restservices.RestBase
9365  * @requires finesse.restservices.Dialog
9366  * @requires finesse.restservices.RestCollectionBase
9367  */
9368 /** @private */
9369 define('restservices/Contacts',[
9370     'restservices/RestCollectionBase',
9371     'restservices/Contact'
9372 ],
9373 function (RestCollectionBase, Contact) {
9374     var Contacts = RestCollectionBase.extend(/** @lends finesse.restservices.Contacts.prototype */{
9375         
9376         /**
9377          * @class
9378          * JavaScript representation of a Contacts collection object. Also exposes
9379          * methods to operate on the object against the server.
9380          * @augments finesse.restservices.RestCollectionBase
9381          * @constructs
9382          * @see finesse.restservices.Contact
9383          * @see finesse.restservices.PhoneBook
9384          * @example
9385          *  _contacts = _phonebook.getContacts( {
9386          *      onCollectionAdd : _handleContactAdd,
9387          *      onCollectionDelete : _handleContactDelete,
9388          *      onLoad : _handleContactsLoaded
9389          *  });
9390          *  
9391          * _contactCollection = _contacts.getCollection();
9392          * for (var contactId in _contactCollection) {
9393          *     if (_contactCollection.hasOwnProperty(contactId)) {
9394          *         _contact = _contactCollection[contactId];
9395          *         etc...
9396          *     }
9397          * }
9398          */
9399         _fakeConstuctor: function () {
9400             /* This is here to hide the real init constructor from the public docs */
9401         },
9402         
9403         /** 
9404          * @private
9405          * JavaScript representation of a Contacts collection object. Also exposes
9406          * methods to operate on the object against the server.
9407          *
9408          * @param {Object} options
9409          *     An object with the following properties:<ul>
9410          *         <li><b>id:</b> The id of the object being constructed</li>
9411          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
9412          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
9413          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
9414          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
9415          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
9416          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
9417          *             <li><b>content:</b> {String} Raw string of response</li>
9418          *             <li><b>object:</b> {Object} Parsed object of response</li>
9419          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
9420          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
9421          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
9422          *             </ul></li>
9423          *         </ul></li>
9424          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
9425          **/
9426         init: function (options) {
9427             this._super(options);           
9428         },
9429 
9430         /**
9431          * @private
9432          * Gets the REST class for the current object - this is the Contacts class.
9433          */
9434         getRestClass: function () {
9435             return Contacts;
9436         },
9437 
9438         /**
9439          * @private
9440          * Gets the REST class for the objects that make up the collection. - this
9441          * is the Contact class.
9442          */
9443         getRestItemClass: function () {
9444             return Contact;
9445         },
9446 
9447         /**
9448          * @private
9449          * Gets the REST type for the current object - this is a "Contacts".
9450          */
9451         getRestType: function () {
9452             return "Contacts";
9453         },
9454         
9455         /**
9456          * @private
9457          * Gets the REST type for the objects that make up the collection - this is "Contacts".
9458          */
9459         getRestItemType: function () {
9460             return "Contact";
9461         },
9462 
9463         /**
9464          * @private
9465          * Override default to indicates that the collection supports making
9466          * requests.
9467          */
9468         supportsRequests: true,
9469 
9470         /**
9471          * @private
9472          * Override default to indicates that the collection subscribes to its objects.
9473          */
9474         supportsRestItemSubscriptions: false,
9475         
9476         /**
9477          * @private
9478          * Retrieve the Contacts.  This call will re-query the server and refresh the collection.
9479          *
9480          * @returns {finesse.restservices.Contacts}
9481          *     This Contacts object, to allow cascading.
9482          */
9483         get: function () {
9484             // set loaded to false so it will rebuild the collection after the get
9485             this._loaded = false;
9486             // reset collection
9487             this._collection = {};
9488             // perform get
9489             this._synchronize();
9490             return this;
9491         }
9492         
9493     });
9494     
9495     window.finesse = window.finesse || {};
9496     window.finesse.restservices = window.finesse.restservices || {};
9497     window.finesse.restservices.Contacts = Contacts;
9498     
9499     
9500     return Contacts;
9501 });
9502 
9503 /**
9504  * JavaScript representation of the Finesse PhoneBook object.
9505  *
9506  * @requires finesse.clientservices.ClientServices
9507  * @requires Class
9508  * @requires finesse.FinesseBase
9509  * @requires finesse.restservices.RestBase
9510  */
9511 
9512 /** @private */
9513 define('restservices/PhoneBook',[
9514     'restservices/RestBase',
9515     'restservices/Contacts'
9516 ],
9517 function (RestBase, Contacts) {
9518     var PhoneBook = RestBase.extend(/** @lends finesse.restservices.PhoneBook.prototype */{
9519 
9520         _contacts: null,
9521 
9522         /**
9523          * @class
9524          * A PhoneBook is a list of Contacts available to a User for quick dial.
9525          * 
9526          * @augments finesse.restservices.RestBase
9527          * @see finesse.restservices.Contacts
9528          * @constructs
9529          */
9530         _fakeConstuctor: function () {
9531             /* This is here to hide the real init constructor from the public docs */
9532         },
9533         
9534         /** 
9535          * @private
9536          * JavaScript representation of a PhoneBook object. Also exposes
9537          * methods to operate on the object against the server.
9538          *
9539          * @param {Object} options
9540          *     An object with the following properties:<ul>
9541          *         <li><b>id:</b> The id of the object being constructed</li>
9542          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
9543          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
9544          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
9545          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
9546          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
9547          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
9548          *             <li><b>content:</b> {String} Raw string of response</li>
9549          *             <li><b>object:</b> {Object} Parsed object of response</li>
9550          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
9551          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
9552          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
9553          *             </ul></li>
9554          *         </ul></li>
9555          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
9556          **/
9557         init: function (options) {
9558             this._super(options);
9559         },
9560 
9561         /**
9562          * @private
9563          * Gets the REST class for the current object - this is the PhoneBook class.
9564          * @returns {Object} The PhoneBook class.
9565          */
9566         getRestClass: function () {
9567             return PhoneBook;
9568         },
9569 
9570         /**
9571          * @private
9572          * Gets the REST type for the current object - this is a "PhoneBook".
9573          * @returns {String} The PhoneBook string.
9574          */
9575         getRestType: function () {
9576             return "PhoneBook";
9577         },
9578 
9579         /**
9580          * @private
9581          * Override default to indicate that this object doesn't support making
9582          * requests.
9583          */
9584         supportsRequests: false,
9585 
9586         /**
9587          * @private
9588          * Override default to indicate that this object doesn't support subscriptions.
9589          */
9590         supportsSubscriptions: false,
9591 
9592         /**
9593          * Getter for the name of the Phone Book.
9594          * @returns {String} The name.
9595          */
9596         getName: function () {
9597             this.isLoaded();
9598             return this.getData().name;
9599         },
9600 
9601         /**
9602          * Getter for the type flag.
9603          * @returns {String} The type.
9604          */
9605         getType: function () {
9606             this.isLoaded();
9607             return this.getData().type;
9608         },
9609 
9610         /**
9611          * @private
9612          * Getter for the Uri value.
9613          * @returns {String} The Uri.
9614          */
9615         getUri: function () {
9616             this.isLoaded();
9617             return this.getData().uri;
9618         },
9619 
9620         /**
9621          * Getter for a Contacts collection object that is associated with PhoneBook.
9622          * @param {finesse.interfaces.RequestHandlers} handlers
9623          *     An object containing the handlers for the request
9624          * @returns {finesse.restservices.Contacts}
9625          *     A Contacts collection object.
9626          */
9627         getContacts: function (callbacks) {
9628             var options = callbacks || {};
9629             options.parentObj = this;
9630             this.isLoaded();
9631 
9632             if (this._contacts === null) {
9633                 this._contacts = new Contacts(options);
9634             }
9635 
9636             return this._contacts;
9637         },
9638 
9639         /**
9640          * Getter for <contacts> node within PhoneBook - sometimes it's just a URI, sometimes it is a Contacts collection
9641          * @returns {String} uri to contacts
9642          *          or {finesse.restservices.Contacts} collection
9643          */
9644         getEmbeddedContacts: function(){
9645             this.isLoaded();
9646             return this.getData().contacts;
9647         },
9648 
9649         /** @private */
9650         createPutSuccessHandler: function(phonebook, contentBody, successHandler){
9651             return function (rsp) {
9652                 // Update internal structure based on response. Here we
9653                 // inject the contentBody from the PUT request into the
9654                 // rsp.object element to mimic a GET as a way to take
9655                 // advantage of the existing _processResponse method.
9656                 rsp.object = contentBody;
9657                 phonebook._processResponse(rsp);
9658 
9659                 //Remove the injected PhoneBook object before cascading response
9660                 rsp.object = {};
9661                 
9662                 //cascade response back to consumer's response handler
9663                 successHandler(rsp);
9664             };
9665         },
9666 
9667         /** @private */
9668         createPostSuccessHandler: function (phonebook, contentBody, successHandler) {
9669             return function (rsp) {
9670                 rsp.object = contentBody;
9671                 phonebook._processResponse(rsp);
9672 
9673                 //Remove the injected PhoneBook object before cascading response
9674                 rsp.object = {};
9675 
9676                 //cascade response back to consumer's response handler
9677                 successHandler(rsp);
9678             };
9679         },
9680 
9681         /**
9682          * @private
9683          * Add a PhoneBook.
9684          * @param {Object} newValues
9685          * @param {String} newValues.name Name of PhoneBook
9686          * @param {String} newValues.type Type of PhoneBook
9687          * @param {finesse.interfaces.RequestHandlers} handlers
9688          *     An object containing the handlers for the request
9689          * @returns {finesse.restservices.PhoneBook}
9690          *     This PhoneBook object, to allow cascading
9691          */
9692         add: function (newValues, handlers) {
9693             // this.isLoaded();
9694             var contentBody = {};
9695 
9696             contentBody[this.getRestType()] = {
9697                 "name": newValues.name,
9698                 "type": newValues.type
9699             };
9700 
9701             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
9702             handlers = handlers || {};
9703 
9704             this.restRequest(this.getRestUrl(), {
9705                 method: 'POST',
9706                 success: this.createPostSuccessHandler(this, contentBody, handlers.success),
9707                 error: handlers.error,
9708                 content: contentBody
9709             });
9710 
9711             return this; // Allow cascading
9712         },
9713 
9714         /**
9715          * @private
9716          * Update a PhoneBook.
9717          * @param {Object} newValues
9718          * @param {String} newValues.name Name of PhoneBook
9719          * @param {String} newValues.type Type of PhoneBook
9720          * @param {finesse.interfaces.RequestHandlers} handlers
9721          *     An object containing the handlers for the request
9722          * @returns {finesse.restservices.PhoneBook}
9723          *     This PhoneBook object, to allow cascading
9724          */
9725         update: function (newValues, handlers) {
9726             this.isLoaded();
9727             var contentBody = {};
9728 
9729             contentBody[this.getRestType()] = {
9730                 "uri": this.getId(),
9731                 "name": newValues.name,
9732                 "type": newValues.type
9733             };
9734 
9735             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
9736             handlers = handlers || {};
9737 
9738             this.restRequest(this.getRestUrl(), {
9739                 method: 'PUT',
9740                 success: this.createPutSuccessHandler(this, contentBody, handlers.success),
9741                 error: handlers.error,
9742                 content: contentBody
9743             });
9744 
9745             return this; // Allow cascading
9746         },
9747 
9748 
9749         /**
9750          * Delete a PhoneBook.
9751          * @param {finesse.interfaces.RequestHandlers} handlers
9752          *     An object containing the handlers for the request
9753          * @returns {finesse.restservices.PhoneBook}
9754          *     This PhoneBook object, to allow cascading
9755          */
9756         "delete": function ( handlers) {
9757             this.isLoaded();
9758 
9759             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
9760             handlers = handlers || {};
9761 
9762             this.restRequest(this.getRestUrl(), {
9763                 method: 'DELETE',
9764                 success: this.createPutSuccessHandler(this, {}, handlers.success),
9765                 error: handlers.error,
9766                 content: undefined
9767             });
9768 
9769             return this; // Allow cascading
9770         }
9771 
9772 
9773 
9774     });
9775     
9776     window.finesse = window.finesse || {};
9777     window.finesse.restservices = window.finesse.restservices || {};
9778     window.finesse.restservices.PhoneBook = PhoneBook;
9779     
9780     return PhoneBook;
9781 });
9782 
9783 /**
9784 * JavaScript representation of the Finesse PhoneBooks collection
9785 * object which contains a list of PhoneBook objects.
9786  *
9787  * @requires finesse.clientservices.ClientServices
9788  * @requires Class
9789  * @requires finesse.FinesseBase
9790  * @requires finesse.restservices.RestBase
9791  * @requires finesse.restservices.Dialog
9792  * @requires finesse.restservices.RestCollectionBase
9793  */
9794 /** @private */
9795 define('restservices/PhoneBooks',[
9796     'restservices/RestCollectionBase',
9797     'restservices/PhoneBook'
9798 ],
9799 function (RestCollectionBase, PhoneBook) {
9800     var PhoneBooks = RestCollectionBase.extend(/** @lends finesse.restservices.PhoneBooks.prototype */{
9801         
9802         /**
9803          * @class
9804          * JavaScript representation of a PhoneBooks collection object. 
9805          * @augments finesse.restservices.RestCollectionBase
9806          * @constructs
9807          * @see finesse.restservices.PhoneBook
9808          * @see finesse.restservices.Contacts
9809          * @see finesse.restservices.Contact
9810          * @example
9811          *  _phoneBooks = _user.getPhoneBooks( {
9812          *      onCollectionAdd : _handlePhoneBookAdd,
9813          *      onCollectionDelete : _handlePhoneBookDelete,
9814          *      onLoad : _handlePhoneBooksLoaded
9815          *  });
9816          *  
9817          * _phoneBookCollection = _phoneBooks.getCollection();
9818          * for (var phoneBookId in _phoneBookCollection) {
9819          *     if (_phoneBookCollection.hasOwnProperty(phoneBookId)) {
9820          *         _phoneBook = _phoneBookCollection[phoneBookId];
9821          *         etc...
9822          *     }
9823          * }
9824         */
9825         _fakeConstuctor: function () {
9826             /* This is here to hide the real init constructor from the public docs */
9827         },
9828         
9829        /**
9830          * @private
9831          *
9832          * @param {Object} options
9833          *     An object with the following properties:<ul>
9834          *         <li><b>id:</b> The id of the object being constructed</li>
9835          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
9836          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
9837          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
9838          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
9839          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
9840          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
9841          *             <li><b>content:</b> {String} Raw string of response</li>
9842          *             <li><b>object:</b> {Object} Parsed object of response</li>
9843          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
9844          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
9845          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
9846          *             </ul></li>
9847          *         </ul></li>
9848          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
9849          **/
9850         init: function (options) {
9851             this._super(options);           
9852         },
9853 
9854         /**
9855          * @private
9856          * Gets the REST class for the current object - this is the PhoneBooks class.
9857          */
9858         getRestClass: function () {
9859             return PhoneBooks;
9860         },
9861 
9862         /**
9863          * @private
9864          * Gets the REST class for the objects that make up the collection. - this
9865          * is the PhoneBook class.
9866          */
9867         getRestItemClass: function () {
9868             return PhoneBook;
9869         },
9870 
9871         /**
9872          * @private
9873          * Gets the REST type for the current object - this is a "PhoneBooks".
9874          */
9875         getRestType: function () {
9876             return "PhoneBooks";
9877         },
9878         
9879         /**
9880          * @private
9881          * Gets the REST type for the objects that make up the collection - this is "PhoneBooks".
9882          */
9883         getRestItemType: function () {
9884             return "PhoneBook";
9885         },
9886 
9887         /**
9888          * @private
9889          * Override default to indicates that the collection supports making
9890          * requests.
9891          */
9892         supportsRequests: true,
9893 
9894         /**
9895          * @private
9896          * Override default to indicates that the collection subscribes to its objects.
9897          */
9898         supportsRestItemSubscriptions: false,
9899         
9900         /**
9901          * @private
9902          * Retrieve the PhoneBooks.  This call will re-query the server and refresh the collection.
9903          *
9904          * @returns {finesse.restservices.PhoneBooks}
9905          *     This PhoneBooks object, to allow cascading.
9906          */
9907         get: function () {
9908             // set loaded to false so it will rebuild the collection after the get
9909             this._loaded = false;
9910             // reset collection
9911             this._collection = {};
9912             // perform get
9913             this._synchronize();
9914             return this;
9915         }
9916         
9917     });
9918     
9919     window.finesse = window.finesse || {};
9920     window.finesse.restservices = window.finesse.restservices || {};
9921     window.finesse.restservices.PhoneBooks = PhoneBooks;
9922     
9923     return PhoneBooks;
9924 });
9925 
9926 /**
9927  * JavaScript representation of the Finesse WorkflowAction object.
9928  *
9929  * @requires finesse.clientservices.ClientServices
9930  * @requires Class
9931  * @requires finesse.FinesseBase
9932  * @requires finesse.restservices.RestBase
9933  */
9934 
9935 /*jslint browser: true, nomen: true, sloppy: true, forin: true */
9936 /*global define,finesse */
9937 
9938 /** @private */
9939 define('restservices/WorkflowAction',['restservices/RestBase'], function (RestBase) {
9940 
9941     var WorkflowAction = RestBase.extend({
9942 
9943         _contacts: null,
9944 
9945         actionTypes: [
9946             {
9947                 name: 'BROWSER_POP',
9948                 params: [
9949                     {
9950                         name: 'windowName',
9951                         type: 'text'
9952                     },
9953                     {
9954                         name: 'path',
9955                         type: 'systemVariableSingleLineEditor'
9956                     }
9957                 ]
9958             },
9959             {
9960                 name: 'HTTP_REQUEST',
9961                 params: [
9962                     {
9963                         name: 'method',
9964                         type: 'dropdown',
9965                         values: ['POST', 'PUT']
9966                     },
9967                     {
9968                         name: 'location',
9969                         type: 'dropdown',
9970                         values: ['FINESSE', 'OTHER']
9971                     },
9972                     {
9973                         name: 'contentType',
9974                         type: 'text'
9975                     },
9976                     {
9977                         name: 'path',
9978                         type: 'systemVariableSingleLineEditor'
9979                     },
9980                     {
9981                         name: 'body',
9982                         type: 'systemVariableMultiLineEditor'
9983                     }
9984                 ]
9985             }            
9986             // more action type definitions here
9987         ],
9988 
9989         /**
9990          * @class
9991          * A WorkflowAction is an action (e.g. Browser Pop, Rest Request) defined in a
9992          * Workflow and triggered by a system event (Call Received, Call Ended, etc.).
9993          * 
9994          * @augments finesse.restservices.RestBase
9995          * @see finesse.restservices.Workflow
9996          * @constructs
9997          */
9998         _fakeConstuctor: function () {
9999             /* This is here to hide the real init constructor from the public docs */
10000         },
10001         
10002         /**
10003          * @private
10004          * JavaScript representation of a WorkflowAction object. Also exposes
10005          * methods to operate on the object against the server.
10006          *
10007          * @param {Object} options
10008          *     An object with the following properties:<ul>
10009          *         <li><b>id:</b> The id of the object being constructed</li>
10010          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
10011          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
10012          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
10013          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
10014          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
10015          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
10016          *             <li><b>content:</b> {String} Raw string of response</li>
10017          *             <li><b>object:</b> {Object} Parsed object of response</li>
10018          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
10019          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
10020          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
10021          *             </ul></li>
10022          *         </ul></li>
10023          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
10024          **/
10025         init: function (options) {
10026             this._super(options);
10027         },
10028 
10029         /**
10030          * @private
10031          * Gets the REST class for the current object - this is the WorkflowAction class.
10032          * @returns {Object} The WorkflowAction class.
10033          */
10034         getRestClass: function () {
10035             return finesse.restservices.WorkflowAction;
10036         },
10037 
10038         /**
10039          * @private
10040          * Gets the REST type for the current object - this is a "WorkflowAction".
10041          * @returns {String} The WorkflowAction string.
10042          */
10043         getRestType: function () {
10044             return "WorkflowAction";
10045         },
10046 
10047         /**
10048          * @private
10049          * Override default to indicate that this object doesn't support making
10050          * requests.
10051          */
10052         supportsRequests: false,
10053 
10054         /**
10055          * @private
10056          * Override default to indicate that this object doesn't support subscriptions.
10057          */
10058         supportsSubscriptions: false,
10059 
10060         /**
10061          * Getter for the name.
10062          * @returns {String} The name.
10063          */
10064         getName: function () {
10065             this.isLoaded();
10066             return this.getData().name;
10067         },
10068 
10069         /**
10070          * Getter for the type flag.
10071          * @returns {String} The type.
10072          */
10073         getType: function () {
10074             this.isLoaded();
10075             return this.getData().type;
10076         },
10077 
10078         /**
10079          * @private
10080          * Getter for the Uri value.
10081          * @returns {String} The Uri.
10082          */
10083         getUri: function () {
10084             this.isLoaded();
10085             return this.getData().uri;
10086         },
10087 
10088         /**
10089          * @private
10090          * Getter for the handledBy value.
10091          * @returns {String} handledBy.
10092          */
10093         getHandledBy: function () {
10094             this.isLoaded();
10095             return this.getData().handledBy;
10096         },
10097 
10098         /**
10099          * Getter for the parameters.
10100          * @returns {Object} key = param name, value = param value
10101          */
10102         getParams: function () {
10103             var map = {},
10104                 params = this.getData().params.Param,
10105                 i,
10106                 param;
10107 
10108             for(i=0; i<params.length; i+=1){
10109                 param = params[i];
10110                 map[param.name] = param.value || "";
10111             }
10112 
10113             return map;
10114         },
10115 
10116         /**
10117          * Getter for the ActionVariables
10118          * @returns {Object} key = action variable name, value = Object{name, type, node, testValue}
10119          */
10120         getActionVariables: function() {
10121             var map = {},
10122                 actionVariablesParent = this.getData().actionVariables,
10123                 actionVariables,
10124                 i,
10125                 actionVariable;
10126 
10127             if (actionVariablesParent === null ||  typeof(actionVariablesParent) === "undefined" || actionVariablesParent.length === 0){
10128                 return map;
10129             }
10130             actionVariables = actionVariablesParent.ActionVariable;
10131 
10132             if(actionVariables.length > 0){
10133                 for(i=0; i<actionVariables.length; i+=1){
10134                     actionVariable = actionVariables[i];
10135                     // escape nulls to empty string
10136                     actionVariable.name = actionVariable.name || "";
10137                     actionVariable.type = actionVariable.type || "";
10138                     actionVariable.node = actionVariable.node || "";
10139                     actionVariable.testValue = actionVariable.testValue || "";
10140                     map[actionVariable.name] = actionVariable;
10141                 }
10142             } else {
10143                 map[actionVariables.name] = actionVariables;
10144             }
10145 
10146             return map;
10147         },
10148 
10149         /** @private */
10150         createPutSuccessHandler: function(action, contentBody, successHandler){
10151             return function (rsp) {
10152                 // Update internal structure based on response. Here we
10153                 // inject the contentBody from the PUT request into the
10154                 // rsp.object element to mimic a GET as a way to take
10155                 // advantage of the existing _processResponse method.
10156                 rsp.object = contentBody;
10157                 action._processResponse(rsp);
10158 
10159                 //Remove the injected WorkflowAction object before cascading response
10160                 rsp.object = {};
10161                 
10162                 //cascade response back to consumer's response handler
10163                 successHandler(rsp);
10164             };
10165         },
10166 
10167         /** @private */
10168         createPostSuccessHandler: function (action, contentBody, successHandler) {
10169             return function (rsp) {
10170                 rsp.object = contentBody;
10171                 action._processResponse(rsp);
10172 
10173                 //Remove the injected WorkflowAction object before cascading response
10174                 rsp.object = {};
10175 
10176                 //cascade response back to consumer's response handler
10177                 successHandler(rsp);
10178             };
10179         },
10180 
10181         /**
10182          * @private
10183          * Build params array out of all the values coming into add or update methods
10184          * paramMap is a map of params.. we need to translate it into an array of Param objects
10185          * where path and windowName are params for the BROWSER_POP type
10186          */
10187         buildParamsForRest: function(paramMap){
10188             var params = {"Param": []},
10189                 i;
10190             for(i in paramMap){
10191                 if(paramMap.hasOwnProperty(i)){
10192                     params.Param.push({name: i, value: paramMap[i]});
10193                 }
10194             }
10195             return params;
10196         },
10197 
10198         /**
10199          * @private
10200          * Build actionVariables array out of all the values coming into add or update methods
10201          * actionVariableMap is a map of actionVariables.. we need to translate it into an array of ActionVariable objects
10202          * where path and windowName are params for the BROWSER_POP type
10203          */
10204         buildActionVariablesForRest: function(actionVariableMap){
10205             var actionVariables = {"ActionVariable": []},
10206                 i,
10207                 actionVariable;
10208             for(i in actionVariableMap){
10209                 if(actionVariableMap.hasOwnProperty(i)){
10210                     // {name: "callVariable1", type: "SYSTEM", node: "", testValue: "<blink>"}
10211                     actionVariable = {
10212                         "name": actionVariableMap[i].name,
10213                         "type": actionVariableMap[i].type,
10214                         "node": actionVariableMap[i].node,
10215                         "testValue": actionVariableMap[i].testValue
10216                     };
10217                     actionVariables.ActionVariable.push(actionVariable);
10218                 }
10219             }
10220             return actionVariables;
10221         },
10222 
10223         /**
10224          * Add
10225          */
10226         add: function (newValues, handlers) {
10227             var contentBody = {};
10228 
10229             contentBody[this.getRestType()] = {
10230                 "name": newValues.name,
10231                 "type": newValues.type,
10232                 "handledBy": newValues.handledBy,
10233                 "params": this.buildParamsForRest(newValues.params),
10234                 "actionVariables": this.buildActionVariablesForRest(newValues.actionVariables)
10235             };
10236 
10237             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
10238             handlers = handlers || {};
10239 
10240             this.restRequest(this.getRestUrl(), {
10241                 method: 'POST',
10242                 success: this.createPostSuccessHandler(this, contentBody, handlers.success),
10243                 error: handlers.error,
10244                 content: contentBody
10245             });
10246 
10247             return this; // Allow cascading
10248         },
10249 
10250         /**
10251          * @private
10252          * Update
10253          */
10254         update: function (newValues, handlers) {
10255             this.isLoaded();
10256             var contentBody = {};
10257             
10258             contentBody[this.getRestType()] = {
10259                 "uri": this.getId(),
10260                 "name": newValues.name,
10261                 "type": newValues.type,
10262                 "handledBy": newValues.handledBy,
10263                 "params": this.buildParamsForRest(newValues.params),
10264                 "actionVariables": this.buildActionVariablesForRest(newValues.actionVariables)
10265             };
10266 
10267             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
10268             handlers = handlers || {};
10269 
10270             this.restRequest(this.getRestUrl(), {
10271                 method: 'PUT',
10272                 success: this.createPutSuccessHandler(this, contentBody, handlers.success),
10273                 error: handlers.error,
10274                 content: contentBody
10275             });
10276 
10277             return this; // Allow cascading
10278         },
10279 
10280 
10281         /**
10282          * @private
10283          * Delete
10284          */
10285         "delete": function ( handlers) {
10286             this.isLoaded();
10287 
10288             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
10289             handlers = handlers || {};
10290 
10291             this.restRequest(this.getRestUrl(), {
10292                 method: 'DELETE',
10293                 success: this.createPutSuccessHandler(this, {}, handlers.success),
10294                 error: handlers.error,
10295                 content: undefined
10296             });
10297 
10298             return this; // Allow cascading
10299         }
10300 
10301 
10302 
10303     });
10304 
10305     window.finesse = window.finesse || {};
10306     window.finesse.restservices = window.finesse.restservices || {};
10307     window.finesse.restservices.WorkflowAction = WorkflowAction;
10308     
10309     return WorkflowAction;
10310 });
10311 
10312 /**
10313 * JavaScript representation of the Finesse WorkflowActions collection
10314 * object which contains a list of WorkflowAction objects.
10315  *
10316  * @requires finesse.clientservices.ClientServices
10317  * @requires Class
10318  * @requires finesse.FinesseBase
10319  * @requires finesse.restservices.RestBase
10320  * @requires finesse.restservices.Dialog
10321  * @requires finesse.restservices.RestCollectionBase
10322  */
10323 
10324 /** @private */
10325 define('restservices/WorkflowActions',[
10326     'restservices/RestCollectionBase',
10327     'restservices/RestBase',
10328     'restservices/WorkflowAction'
10329 ],
10330 function (RestCollectionBase, RestBase, WorkflowAction) {
10331 
10332     var WorkflowActions = RestCollectionBase.extend({
10333         
10334         /**
10335          * @class
10336          * JavaScript representation of a WorkflowActions collection object. 
10337          * @augments finesse.restservices.RestCollectionBase
10338          * @constructs
10339          * @see finesse.restservices.WorkflowAction
10340          * @see finesse.restservices.Workflow
10341          * @see finesse.restservices.Workflows
10342          * @example
10343          *  _workflowActions = _user.getWorkflowActions( {
10344          *      onCollectionAdd : _handleWorkflowActionAdd,
10345          *      onCollectionDelete : _handleWorkflowActionDelete,
10346          *      onLoad : _handleWorkflowActionsLoaded
10347          *  });
10348         */
10349         _fakeConstuctor: function () {
10350             /* This is here to hide the real init constructor from the public docs */
10351         },
10352         
10353         /**
10354          * @private
10355          * JavaScript representation of a WorkflowActions collection object. Also exposes
10356          * methods to operate on the object against the server.
10357          *
10358          * @param {Object} options
10359          *     An object with the following properties:<ul>
10360          *         <li><b>id:</b> The id of the object being constructed</li>
10361          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
10362          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
10363          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
10364          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
10365          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
10366          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
10367          *             <li><b>content:</b> {String} Raw string of response</li>
10368          *             <li><b>object:</b> {Object} Parsed object of response</li>
10369          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
10370          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
10371          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
10372          *             </ul></li>
10373          *         </ul></li>
10374          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
10375          **/
10376         init: function (options) {
10377             this._super(options);           
10378         },
10379 
10380         /**
10381          * @private
10382          * Gets the REST class for the current object - this is the WorkflowActions class.
10383          */
10384         getRestClass: function () {
10385             return WorkflowActions;
10386         },
10387 
10388         /**
10389          * @private
10390          * Gets the REST class for the objects that make up the collection. - this
10391          * is the WorkflowAction class.
10392          */
10393         getRestItemClass: function () {
10394             return WorkflowAction;
10395         },
10396 
10397         /**
10398          * @private
10399          * Gets the REST type for the current object - this is a "WorkflowActions".
10400          */
10401         getRestType: function () {
10402             return "WorkflowActions";
10403         },
10404         
10405         /**
10406          * @private
10407          * Gets the REST type for the objects that make up the collection - this is "WorkflowActions".
10408          */
10409         getRestItemType: function () {
10410             return "WorkflowAction";
10411         },
10412 
10413         /**
10414          * @private
10415          * Override default to indicates that the collection supports making
10416          * requests.
10417          */
10418         supportsRequests: true,
10419 
10420         /**
10421          * @private
10422          * Override default to indicates that the collection subscribes to its objects.
10423          */
10424         supportsRestItemSubscriptions: false,
10425         
10426         /**
10427          * @private
10428          * Retrieve the WorkflowActions.
10429          *
10430          * @returns {finesse.restservices.WorkflowActions}
10431          *     This WorkflowActions object to allow cascading.
10432          */
10433         get: function () {
10434             // set loaded to false so it will rebuild the collection after the get
10435             this._loaded = false;
10436             // reset collection
10437             this._collection = {};
10438             // perform get
10439             this._synchronize();
10440             return this;
10441         }
10442     });
10443 
10444     window.finesse = window.finesse || {};
10445     window.finesse.restservices = window.finesse.restservices || {};
10446     window.finesse.restservices.WorkflowActions = WorkflowActions;
10447         
10448     return WorkflowActions;
10449 });
10450 
10451 /**
10452  * JavaScript representation of the Finesse Workflow object.
10453  *
10454  * @requires finesse.clientservices.ClientServices
10455  * @requires Class
10456  * @requires finesse.FinesseBase
10457  * @requires finesse.restservices.RestBase
10458  */
10459 
10460 /*jslint browser: true, nomen: true, sloppy: true, forin: true */
10461 /*global define,finesse */
10462 
10463 /** @private */
10464 define('restservices/Workflow',[
10465     'restservices/RestBase',
10466     'restservices/WorkflowActions'
10467 ],
10468 function (RestBase, WorkflowActions) {
10469 
10470     var Workflow = RestBase.extend({
10471 
10472         /**
10473          * @class
10474          * JavaScript representation of a Workflow object. Also exposes
10475          * methods to operate on the object against the server.
10476          *
10477          * @param {Object} options
10478          *     An object with the following properties:<ul>
10479          *         <li><b>id:</b> The id of the object being constructed</li>
10480          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
10481          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
10482          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
10483          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
10484          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
10485          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
10486          *             <li><b>content:</b> {String} Raw string of response</li>
10487          *             <li><b>object:</b> {Object} Parsed object of response</li>
10488          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
10489          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
10490          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
10491          *             </ul></li>
10492          *         </ul></li>
10493          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
10494          * @constructs
10495          **/
10496         init: function (options) {
10497             this._super(options);
10498         },
10499 
10500         /**
10501          * @private
10502          * Gets the REST class for the current object - this is the Workflow class.
10503          * @returns {Object} The Workflow class.
10504          */
10505         getRestClass: function () {
10506             return Workflow;
10507         },
10508 
10509         /**
10510          * @private
10511          * Gets the REST type for the current object - this is a "Workflow".
10512          * @returns {String} The Workflow string.
10513          */
10514         getRestType: function () {
10515             return "Workflow";
10516         },
10517 
10518         /**
10519          * @private
10520          * Override default to indicate that this object doesn't support making
10521          * requests.
10522          */
10523         supportsRequests: false,
10524 
10525         /**
10526          * @private
10527          * Override default to indicate that this object doesn't support subscriptions.
10528          */
10529         supportsSubscriptions: false,
10530 
10531         /**
10532          * @private
10533          * Getter for the Uri value.
10534          * @returns {String} The Uri.
10535          */
10536         getUri: function () {
10537             this.isLoaded();
10538             return this.getData().uri;
10539         },
10540 
10541         /**
10542          * Getter for the name.
10543          * @returns {String} The name.
10544          */
10545         getName: function () {
10546             this.isLoaded();
10547             return this.getData().name;
10548         },
10549 
10550         /**
10551          * Getter for the description.
10552          * @returns {String} The description.
10553          */
10554         getDescription: function () {
10555             this.isLoaded();
10556             return this.getData().description;
10557         },
10558 
10559         /**
10560          * Getter for the trigger set.
10561          * @returns {String} The trigger set.
10562          */
10563         getTriggerSet: function () {
10564             this.isLoaded();
10565             return this.getData().TriggerSet;
10566         },
10567 
10568         /**
10569          * Getter for the condition set.
10570          * @returns {String} The condition set.
10571          */
10572         getConditionSet: function () {
10573             this.isLoaded();
10574             return this.getData().ConditionSet;
10575         },
10576         
10577         /**
10578          * Getter for the assigned workflowActions.
10579          * @returns {String} The workflowActions object.
10580          */
10581         getWorkflowActions: function () {
10582             this.isLoaded();
10583             var workflowActions = this.getData().workflowActions;
10584             if (workflowActions === null) {
10585                 workflowActions = "";
10586             }
10587             return workflowActions;
10588         },
10589 
10590         createPutSuccessHandler: function (workflow, contentBody, successHandler) {
10591             return function (rsp) {
10592                 // Update internal structure based on response. Here we
10593                 // inject the contentBody from the PUT request into the
10594                 // rsp.object element to mimic a GET as a way to take
10595                 // advantage of the existing _processResponse method.
10596                 rsp.object = contentBody;
10597                 workflow._processResponse(rsp);
10598 
10599                 //Remove the injected Workflow object before cascading response
10600                 rsp.object = {};
10601 
10602                 //cascade response back to consumer's response handler
10603                 successHandler(rsp);
10604             };
10605         },
10606 
10607         createPostSuccessHandler: function (workflow, contentBody, successHandler) {
10608             return function (rsp) {
10609                 rsp.object = contentBody;
10610                 workflow._processResponse(rsp);
10611 
10612                 //Remove the injected Workflow object before cascading response
10613                 rsp.object = {};
10614 
10615                 //cascade response back to consumer's response handler
10616                 successHandler(rsp);
10617             };
10618         },
10619 
10620         /**
10621          * @private
10622          * Add
10623          */
10624         add: function (newValues, handlers) {
10625             // this.isLoaded();
10626             var contentBody = {};
10627 
10628             contentBody[this.getRestType()] = {
10629                 "name": newValues.name,
10630                 "description": newValues.description,
10631                 "TriggerSet" : newValues.TriggerSet,
10632                 "ConditionSet" : newValues.ConditionSet,
10633                 "workflowActions" : newValues.workflowActions
10634             };
10635 
10636             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
10637             handlers = handlers || {};
10638 
10639             this.restRequest(this.getRestUrl(), {
10640                 method: 'POST',
10641                 success: this.createPostSuccessHandler(this, contentBody, handlers.success),
10642                 error: handlers.error,
10643                 content: contentBody
10644             });
10645 
10646             return this; // Allow cascading
10647         },
10648 
10649         /**
10650          * @private
10651          * Update
10652          */
10653         update: function (newValues, handlers) {
10654             this.isLoaded();
10655             var contentBody = {};
10656 
10657             contentBody[this.getRestType()] = {
10658                 "uri": this.getId(),
10659                 "name": newValues.name,
10660                 "description": newValues.description,
10661                 "TriggerSet" : newValues.TriggerSet,
10662                 "ConditionSet" : newValues.ConditionSet,
10663                 "workflowActions" : newValues.workflowActions
10664             };
10665 
10666             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
10667             handlers = handlers || {};
10668 
10669             this.restRequest(this.getRestUrl(), {
10670                 method: 'PUT',
10671                 success: this.createPutSuccessHandler(this, contentBody, handlers.success),
10672                 error: handlers.error,
10673                 content: contentBody
10674             });
10675 
10676             return this; // Allow cascading
10677         },
10678 
10679 
10680         /**
10681          * @private
10682          * Delete
10683          */
10684         "delete": function (handlers) {
10685             this.isLoaded();
10686 
10687             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
10688             handlers = handlers || {};
10689 
10690             this.restRequest(this.getRestUrl(), {
10691                 method: 'DELETE',
10692                 success: this.createPutSuccessHandler(this, {}, handlers.success),
10693                 error: handlers.error,
10694                 content: undefined
10695             });
10696 
10697             return this; // Allow cascading
10698         }
10699 
10700 
10701 
10702     });
10703 
10704     window.finesse = window.finesse || {};
10705     window.finesse.restservices = window.finesse.restservices || {};
10706     window.finesse.restservices.Workflow = Workflow;
10707 
10708     return Workflow;
10709 });
10710 
10711 /**
10712 * JavaScript representation of the Finesse workflows collection
10713 * object which contains a list of workflow objects.
10714  *
10715  * @requires finesse.clientservices.ClientServices
10716  * @requires Class
10717  * @requires finesse.FinesseBase
10718  * @requires finesse.restservices.RestBase
10719  * @requires finesse.restservices.Dialog
10720  * @requires finesse.restservices.RestCollectionBase
10721  */
10722 
10723 /** @private */
10724 define('restservices/Workflows',[
10725     'restservices/RestCollectionBase',
10726     'restservices/RestBase',
10727     'restservices/Workflow'
10728 ],
10729 function (RestCollectionBase, RestBase, Workflow) {
10730 
10731     var Workflows = RestCollectionBase.extend({
10732 
10733         /**
10734          * @class
10735          * JavaScript representation of a workflows collection object. Also exposes
10736          * methods to operate on the object against the server.
10737          *
10738          * @param {Object} options
10739          *     An object with the following properties:<ul>
10740          *         <li><b>id:</b> The id of the object being constructed</li>
10741          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
10742          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
10743          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
10744          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
10745          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
10746          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
10747          *             <li><b>content:</b> {String} Raw string of response</li>
10748          *             <li><b>object:</b> {Object} Parsed object of response</li>
10749          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
10750          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
10751          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
10752          *             </ul></li>
10753          *         </ul></li>
10754          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
10755          *  @constructs
10756          **/
10757         init: function (options) {
10758             this._super(options);
10759         },
10760 
10761         /**
10762          * @private
10763          * Gets the REST class for the current object - this is the workflows class.
10764          */
10765         getRestClass: function () {
10766             return Workflows;
10767         },
10768 
10769         /**
10770          * @private
10771          * Gets the REST class for the objects that make up the collection. - this
10772          * is the workflow class.
10773          */
10774         getRestItemClass: function () {
10775             return Workflow;
10776         },
10777 
10778         /**
10779          * @private
10780          * Gets the REST type for the current object - this is a "workflows".
10781          */
10782         getRestType: function () {
10783             return "Workflows";
10784         },
10785 
10786         /**
10787          * @private
10788          * Gets the REST type for the objects that make up the collection - this is "workflows".
10789          */
10790         getRestItemType: function () {
10791             return "Workflow";
10792         },
10793 
10794         /**
10795          * @private
10796          * Override default to indicates that the collection supports making requests.
10797          */
10798         supportsRequests: true,
10799 
10800         /**
10801          * @private
10802          * Override default to indicates that the collection does not subscribe to its objects.
10803          */
10804         supportsRestItemSubscriptions: false,
10805 
10806         /**
10807          * @private
10808          * Retrieve the workflows. This call will re-query the server and refresh the collection.
10809          *
10810          * @returns {finesse.restservices.workflows}
10811          *     This workflows object to allow cascading.
10812          */
10813         get: function () {
10814             // set loaded to false so it will rebuild the collection after the get
10815             this._loaded = false;
10816             // reset collection
10817             this._collection = {};
10818             // perform get
10819             this._synchronize();
10820             return this;
10821         }
10822     });
10823 
10824     window.finesse = window.finesse || {};
10825     window.finesse.restservices = window.finesse.restservices || {};
10826     window.finesse.restservices.Workflows = Workflows;
10827         
10828     return Workflows;
10829 });
10830 
10831 /**
10832  * JavaScript representation of the Finesse MediaPropertiesLayout object
10833  *
10834  * @requires finesse.clientservices.ClientServices
10835  * @requires Class
10836  * @requires finesse.FinesseBase
10837  * @requires finesse.restservices.RestBase
10838  */
10839 
10840 /** The following comment is to prevent jslint errors about 
10841  * using variables before they are defined.
10842  */
10843 /*global finesse*/
10844 
10845 /** @private */
10846 define('restservices/MediaPropertiesLayout',['restservices/RestBase'], function (RestBase) {
10847     var MediaPropertiesLayout = RestBase.extend(/** @lends finesse.restservices.MediaPropertiesLayout.prototype */{
10848 
10849         /**
10850          * @class
10851          * The MediaPropertiesLayout handles which call variables are associated with Dialogs.
10852          * 
10853          * @augments finesse.restservices.RestBase
10854          * @see finesse.restservices.Dialog#getMediaProperties
10855          * @see finesse.restservices.User#getMediaPropertiesLayout
10856          * @constructs
10857          */
10858         _fakeConstuctor: function () {
10859             /* This is here to hide the real init constructor from the public docs */
10860         },
10861         
10862         /**
10863          * @private
10864          * JavaScript representation of a MediaPropertiesLayout object. Also exposes
10865          * methods to operate on the object against the server.
10866          *
10867          * @param {Object} options
10868          *     An object with the following properties:<ul>
10869          *         <li><b>id:</b> The id of the object being constructed</li>
10870          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
10871          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
10872          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
10873          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
10874          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
10875          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
10876          *             <li><b>content:</b> {String} Raw string of response</li>
10877          *             <li><b>object:</b> {Object} Parsed object of response</li>
10878          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
10879          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
10880          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
10881          *             </ul></li>
10882          *         </ul></li>
10883          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
10884          **/
10885         init: function (options) {
10886             this._super(options);
10887         },
10888 
10889         /**
10890          * @private
10891          * Gets the REST class for the current object - this is the MediaPropertiesLayout object.
10892          */
10893         getRestClass: function () {
10894             return MediaPropertiesLayout;
10895         },
10896 
10897         /**
10898          * @private
10899          * Gets the REST type for the current object - this is a "MediaPropertiesLayout".
10900          */
10901         getRestType: function () {
10902             return "MediaPropertiesLayout";
10903         },
10904 
10905         /**
10906          * @private
10907          * Overrides the parent class.  Returns the url for the MediaPropertiesLayout resource
10908          */
10909         getRestUrl: function () {
10910             return ("/finesse/api/User/" + this.getId() + "/" + this.getRestType());
10911         },
10912 
10913         /**
10914          * @private
10915          * Returns whether this object supports subscriptions
10916          */
10917         supportsSubscriptions: false,
10918 
10919         /**
10920          * Retrieve the media properties layout. This call will re-query the server and refresh the layout object.
10921          * @returns {finesse.restservices.MediaPropertiesLayout}
10922          *     This MediaPropertiesLayout object to allow cascading
10923          */
10924         get: function () {
10925             this._synchronize();
10926 
10927             return this; //Allow cascading
10928         },
10929 
10930         /**
10931          * Gets the data for this object.
10932          * 
10933          * Performs safe conversion from raw API data to ensure that the returned layout object
10934          * always has a header with correct entry fields, and exactly two columns with lists of entries.
10935          *
10936          * @returns {finesse.restservices.MediaPropertiesLayout.Object} Data in columns (unless only one defined).
10937          */
10938         getData: function () {
10939 
10940             var layout = this._data, result, _addColumnData;
10941 
10942             result = this.getEmptyData();
10943 
10944             /**
10945              * @private
10946              */
10947             _addColumnData = function (entryData, colIndex) {
10948 
10949                 if (!entryData) {
10950                     //If there's no entry data at all, rewrite entryData to be an empty collection of entries
10951                     entryData = {};
10952                 } else if (entryData.mediaProperty) {
10953                     //If entryData contains the keys for a single entry rather than being a collection of entries,
10954                     //rewrite it to be a collection containing a single entry
10955                     entryData = { "": entryData };
10956                 }
10957 
10958                 //Add each of the entries in the list to the column
10959                 jQuery.each(entryData, function (i, entryData) {
10960 
10961                     //If the entry has no displayName specified, explicitly set it to the empty string
10962                     if (!entryData.displayName) {
10963                         entryData.displayName = "";
10964                     }
10965 
10966                     result.columns[colIndex].push(entryData);
10967 
10968                 });
10969 
10970             };
10971 
10972             //The header should only contain a single entry
10973             if (layout.header && layout.header.entry) {
10974 
10975                 //If the entry has no displayName specified, explicitly set it to the empty string
10976                 if (!layout.header.entry.displayName) {
10977                     layout.header.entry.displayName = "";
10978                 }
10979 
10980                 result.header = layout.header.entry;
10981 
10982             } else {
10983 
10984                 throw "MediaPropertiesLayout.getData() - Header does not contain an entry";
10985 
10986             }
10987 
10988             //If the column object contains an entry object that wasn't part of a list of entries,
10989             //it must be a single right-hand entry object (left-hand entry object would be part of a list.)
10990             //Force the entry object to be the 2nd element in an otherwise-empty list.
10991             if (layout.column && layout.column.entry) {
10992                 layout.column = [
10993                     null,
10994                     { "entry": layout.column.entry }
10995                 ];
10996             }
10997 
10998             if (layout.column && layout.column.length > 0 && layout.column.length <= 2) {
10999 
11000                 //Render left column entries
11001                 if (layout.column[0] && layout.column[0].entry) {
11002                     _addColumnData(layout.column[0].entry, 0);
11003                 }
11004 
11005                 //Render right column entries
11006                 if (layout.column[1] && layout.column[1].entry) {
11007                     _addColumnData(layout.column[1].entry, 1);
11008                 }
11009 
11010             }
11011 
11012             return result;
11013 
11014         },
11015 
11016         /**
11017          * @private
11018          * Empty/template version of getData().
11019          *
11020          * Used by getData(), and by callers of getData() in error cases.
11021          */
11022         getEmptyData: function () {
11023 
11024             return {
11025                 header : {
11026                     displayName: null,
11027                     mediaProperty: null
11028                 },
11029                 columns : [[], []]
11030             };
11031 
11032         },
11033 
11034         /**
11035          * @private
11036          * Set the layout of this MediaPropertiesLayout.
11037          * @param {String} layout
11038          *     The layout you are setting
11039          * @param {finesse.interfaces.RequestHandlers} handlers
11040          *     An object containing the handlers for the request
11041          * @returns {finesse.restservices.MediaPropertiesLayout}
11042          *     This MediaPropertiesLayout object to allow cascading
11043          */
11044         setLayout: function (layout, handlers) {
11045 
11046             var contentBody = {};
11047 
11048             contentBody[this.getRestType()] = layout;
11049 
11050             //Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
11051             handlers = handlers || {};
11052 
11053             this.restRequest(this.getRestUrl(), {
11054                 method: 'PUT',
11055                 success: handlers.success,
11056                 error: handlers.error,
11057                 content: contentBody
11058             });
11059 
11060             return this; // Allow cascading
11061         }
11062 
11063     });
11064     
11065     MediaPropertiesLayout.Object = /** @lends finesse.restservices.MediaPropertiesLayout.Object.prototype */ {
11066         /**
11067          * @class Format of MediaPropertiesLayout Object.<br>
11068          * Object { <ul>
11069          *      <li>header : { <ul>
11070          *          <li>dispayName {String} 
11071          *          <li>mediaProperty {String}</ul>}
11072          *      <li>columns : { <ul>
11073          *          <li>[ [] , [] ]
11074          *          </ul>
11075          *      where column arrays consists of the same Object format as header.<br>
11076          *          }</ul>
11077          *      }<br>         
11078          * @constructs
11079          */
11080         _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
11081         
11082     };
11083 
11084 	window.finesse = window.finesse || {};
11085     window.finesse.restservices = window.finesse.restservices || {};
11086     window.finesse.restservices.MediaPropertiesLayout = MediaPropertiesLayout;
11087     
11088     return MediaPropertiesLayout;
11089 });
11090 
11091 /**
11092  * JavaScript representation of the Finesse User object
11093  *
11094  * @requires finesse.clientservices.ClientServices
11095  * @requires Class
11096  * @requires finesse.FinesseBase
11097  * @requires finesse.restservices.RestBase
11098  */
11099 
11100 /** @private */
11101 define('restservices/User',[
11102     'restservices/RestBase',
11103     'restservices/Dialogs',
11104     'restservices/ClientLog',
11105     'restservices/Queues',
11106     'restservices/WrapUpReasons',
11107     'restservices/PhoneBooks',
11108     'restservices/Workflows',
11109     'restservices/MediaPropertiesLayout',
11110     'utilities/Utilities'
11111 ],
11112 function (RestBase, Dialogs, ClientLog, Queues, WrapUpReasons, PhoneBooks, Workflows, MediaPropertiesLayout, Utilities) {
11113     
11114     var User = RestBase.extend(/** @lends finesse.restservices.User.prototype */{
11115 
11116         _dialogs : null,
11117         _clientLogObj : null,
11118         _wrapUpReasons : null,
11119         _phoneBooks : null,
11120         _workflows : null,
11121         _mediaPropertiesLayout : null,
11122         _queues : null,
11123         
11124         /**
11125          * @class
11126          * The User represents a Finesse Agent or Supervisor.
11127          *
11128          * @param {Object} options
11129          *     An object with the following properties:<ul>
11130          *         <li><b>id:</b> The id of the object being constructed</li>
11131          *         <li><b>onLoad(this): (optional)</b> callback handler for when the object is successfully loaded from the server</li>
11132          *         <li><b>onChange(this): (optional)</b> callback handler for when an update notification of the object is received</li>
11133          *         <li><b>onAdd(this): (optional)</b> callback handler for when a notification that the object is created is received</li>
11134          *         <li><b>onDelete(this): (optional)</b> callback handler for when a notification that the object is deleted is received</li>
11135          *         <li><b>onError(rsp): (optional)</b> callback handler for if loading of the object fails, invoked with the error response object:<ul>
11136          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
11137          *             <li><b>content:</b> {String} Raw string of response</li>
11138          *             <li><b>object:</b> {Object} Parsed object of response</li>
11139          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
11140          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
11141          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
11142          *             </ul></li>
11143          *         </ul></li>
11144          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
11145          * @augments finesse.restservices.RestBase
11146          * @constructs
11147          * @example
11148          *      _user = new finesse.restservices.User({
11149          *                      id: _id, 
11150          *                      onLoad : _handleUserLoad,
11151          *                      onChange : _handleUserChange
11152          *      });
11153          **/
11154         init: function (options) {
11155             this._super(options);
11156         },
11157         
11158         Callbacks: {},
11159     
11160         /**
11161          * @private
11162          * Gets the REST class for the current object - this is the User object.
11163          */
11164         getRestClass: function () {
11165             return User;
11166         },
11167     
11168         /**
11169         * @private
11170          * Gets the REST type for the current object - this is a "User".
11171          */
11172         getRestType: function () {
11173             return "User";
11174         },
11175         /**
11176          * @private
11177          * overloading this to return URI
11178          */
11179         getXMPPNodePath: function () {
11180             return this.getRestUrl();
11181         },
11182         /**
11183         * @private
11184          * Returns whether this object supports subscriptions
11185          */
11186         supportsSubscriptions: function () {
11187             return true;
11188         },
11189     
11190         /**
11191          * Getter for the firstName of this User.
11192          * @returns {String}
11193          *     The firstName for this User
11194          */
11195         getFirstName: function () {
11196             this.isLoaded();
11197             return Utilities.convertNullToEmptyString(this.getData().firstName);
11198         },
11199     
11200         /**
11201          * Getter for the lastName of this User.
11202          * @returns {String}
11203          *     The lastName for this User
11204          */
11205         getLastName: function () {
11206             this.isLoaded();
11207             return Utilities.convertNullToEmptyString(this.getData().lastName);
11208         },
11209     
11210         /**
11211          * Getter for the extension of this User.
11212          * @returns {String}
11213          *     The extension, if any, of this User
11214          */
11215         getExtension: function () {
11216             this.isLoaded();
11217             return Utilities.convertNullToEmptyString(this.getData().extension);
11218         },
11219         
11220         /**
11221          * Getter for the id of the Team of this User
11222          * @returns {String}
11223          *     The current (or last fetched) id of the Team of this User
11224          */
11225         getTeamId: function () {
11226             this.isLoaded();
11227             return this.getData().teamId;
11228         },
11229         
11230         /**
11231          * Getter for the name of the Team of this User
11232          * @returns {String}
11233          *     The current (or last fetched) name of the Team of this User
11234          */
11235         getTeamName: function () {
11236             this.isLoaded();
11237             return this.getData().teamName;
11238         },
11239         
11240         /**
11241          * Is user an agent?
11242          * @returns {Boolean} True if user has role of agent, else false.
11243          */
11244         hasAgentRole: function () {
11245             this.isLoaded();
11246             return this.hasRole("Agent");
11247         },
11248     
11249         /**
11250          * Is user a supervisor?
11251          * @returns {Boolean} True if user has role of supervisor, else false.
11252          */
11253         hasSupervisorRole: function () {
11254             this.isLoaded();
11255             return this.hasRole("Supervisor");
11256         },
11257     
11258         /**
11259          * @private
11260          * Checks to see if user has "theRole"
11261          * @returns {Boolean}
11262          */
11263         hasRole: function (theRole) {
11264             this.isLoaded();
11265             var result = false, i, roles, len;
11266     
11267             roles = this.getData().roles.role;
11268             len = roles.length;
11269             if (typeof roles === 'string') {
11270                 if (roles === theRole) {
11271                     result = true;
11272                 }
11273             } else {
11274                 for (i = 0; i < len ; i = i + 1) {
11275                     if (roles[i] === theRole) {
11276                         result = true;
11277                         break;
11278                     }
11279                 }
11280             }
11281     
11282             return result;
11283         },
11284 
11285         /**
11286          * Getter for the pending state of this User.
11287          * @returns {String}
11288          *     The pending state of this User
11289          * @see finesse.restservices.User.States
11290          */
11291         getPendingState: function () {
11292             this.isLoaded();
11293             return Utilities.convertNullToEmptyString(this.getData().pendingState);
11294         },
11295     
11296         /**
11297          * Getter for the state of this User.
11298          * @returns {String}
11299          *     The current (or last fetched) state of this User
11300          * @see finesse.restservices.User.States
11301          */
11302         getState: function () {
11303             this.isLoaded();
11304             return this.getData().state;
11305         },
11306 
11307         /**
11308          * Getter for the state change time of this User.
11309          * @returns {String}
11310          *     The state change time of this User
11311          */
11312         getStateChangeTime: function () {
11313             this.isLoaded();
11314             return this.getData().stateChangeTime;
11315         },
11316         
11317         /**
11318          * Getter for the wrap-up mode of this User.
11319          * @see finesse.restservices.User.WrapUpMode
11320          * @returns {String} The wrap-up mode of this user that is a value of {@link finesse.restservices.User.WrapUpMode}
11321          */
11322         getWrapUpOnIncoming: function () {
11323             this.isLoaded();
11324             return this.getData().settings.wrapUpOnIncoming;
11325         },
11326         
11327         /**
11328          * Is User required to go into wrap-up?
11329          * @see finesse.restservices.User.WrapUpMode
11330          * @return {Boolean}
11331          *      True if this agent is required to go into wrap-up.
11332          */
11333         isWrapUpRequired: function () {
11334             return (this.getWrapUpOnIncoming() === User.WrapUpMode.REQUIRED || 
11335                     this.getWrapUpOnIncoming() === User.WrapUpMode.REQUIRED_WITH_WRAP_UP_DATA);
11336         },
11337     
11338         /**
11339          * Checks to see if the user is considered a mobile agent by checking for
11340          * the existence of the mobileAgent node.
11341          * @returns {Boolean}
11342          *      True if this agent is a mobile agent.
11343          */
11344         isMobileAgent: function () {
11345             this.isLoaded();
11346             var ma = this.getData().mobileAgent;
11347             return ma !== null && typeof ma === "object";
11348         },
11349     
11350         /**
11351          * Getter for the mobile agent work mode.
11352          * @returns {finesse.restservices.User.WorkMode}
11353          *      If available, return the mobile agent work mode, otherwise null.
11354          */
11355         getMobileAgentMode: function () {
11356             this.isLoaded();
11357             if (this.isMobileAgent()) {
11358                 return this.getData().mobileAgent.mode;
11359             }
11360             return null;
11361         },
11362     
11363         /**
11364          * Getter for the mobile agent dial number.
11365          * @returns {String}
11366          *      If available, return the mobile agent dial number, otherwise null.
11367          */
11368         getMobileAgentDialNumber: function () {
11369             this.isLoaded();
11370             if (this.isMobileAgent()) {
11371                 return this.getData().mobileAgent.dialNumber;
11372             }
11373             return null;
11374         },
11375     
11376         /**
11377          * Getter for a Dialogs collection object that is associated with User.
11378          * @param {finesse.interfaces.RestObjectHandlers} [handlers] Object that sets callback handlers (only 
11379          * applicable when Object has not been previously created).
11380          * @returns {finesse.restservices.Dialogs}
11381          *     A Dialogs collection object.
11382          */
11383         getDialogs: function (callbacks) {
11384             var options = callbacks || {};
11385             options.parentObj = this;
11386             this.isLoaded();
11387     
11388             if (this._dialogs === null) {
11389                 this._dialogs = new Dialogs(options);
11390             }
11391     
11392             return this._dialogs;
11393         },
11394         
11395         /**
11396          * @private
11397          * Getter for a ClientLog object that is associated with User.
11398          * @param {finesse.interfaces.RestObjectHandlers} [handlers] Object that sets callback handlers (only 
11399          * applicable when Object has not been previously created).
11400          * @returns {finesse.restservices.ClientLog}
11401          *     A ClientLog collection object.
11402          */
11403         getClientLog: function (callbacks) {
11404             var options = callbacks || {};
11405             options.parentObj = this;
11406             this.isLoaded();
11407            
11408             if (this._clientLogObj === null) {
11409                 this._clientLogObj = new ClientLog(options);
11410             }
11411             else {
11412                 if(options.onLoad && typeof options.onLoad === "function") {
11413                 options.onLoad(this._clientLogObj);
11414                 }
11415             }
11416             return this._clientLogObj;
11417         },
11418        
11419         /**
11420          * Getter for a Queues collection object that is associated with User.
11421          * @param {finesse.interfaces.RestObjectHandlers} [handlers] Object that sets callback handlers (only 
11422          * applicable when Object has not been previously created).
11423          * @returns {finesse.restservices.Queues}
11424          *     A Queues collection object.
11425          */
11426         getQueues: function (callbacks) {
11427             var options = callbacks || {};
11428             options.parentObj = this;
11429             this.isLoaded();
11430     
11431             if (this._queues === null) {
11432                 this._queues = new Queues(options);
11433             }
11434     
11435             return this._queues;
11436         },
11437 
11438         /**
11439          * Getter for a WrapUpReasons collection object that is associated with User.
11440          * @param {finesse.interfaces.RestObjectHandlers} [handlers] Object that sets callback handlers (only 
11441          * applicable when Object has not been previously created).
11442          * @returns {finesse.restservices.WrapUpReasons}
11443          *     A WrapUpReasons collection object.
11444          */
11445         getWrapUpReasons: function (callbacks) {
11446             var options = callbacks || {};
11447             options.parentObj = this;
11448             this.isLoaded();
11449     
11450             if (this._wrapUpReasons === null) {
11451                 this._wrapUpReasons = new WrapUpReasons(options);
11452             }
11453     
11454             return this._wrapUpReasons;
11455         },
11456 
11457         /**
11458          * Getter for a PhoneBooks collection object that is associated with User.
11459          * @param {finesse.interfaces.RestObjectHandlers} [handlers] Object that sets callback handlers (only 
11460          * applicable when Object has not been previously created).
11461          * @returns {finesse.restservices.PhoneBooks}
11462          *     A PhoneBooks collection object.
11463          */
11464         getPhoneBooks: function (callbacks) {
11465             var options = callbacks || {};
11466             options.parentObj = this;
11467             this.isLoaded();
11468     
11469             if (this._phoneBooks === null) {
11470                 this._phoneBooks = new PhoneBooks(options);
11471             }
11472     
11473             return this._phoneBooks;
11474         },
11475 
11476         /**
11477          * @private
11478          * Loads the Workflows collection object that is associated with User and
11479          * 'returns' them to the caller via the handlers.
11480          * @param {finesse.interfaces.RestObjectHandlers} [handlers] Object that sets callback handlers (only 
11481          * applicable when Object has not been previously created).
11482          * @see finesse.restservices.Workflow
11483          * @see finesse.restservices.Workflows
11484          * @see finesse.restservices.RestCollectionBase
11485          */
11486         loadWorkflows: function (callbacks) {
11487             var options = callbacks || {};
11488             options.parentObj = this;
11489             this.isLoaded();
11490 
11491             if (this._workflows === null) {
11492                 this._workflows = new Workflows(options);
11493             } else {
11494                 this._workflows.refresh();
11495             }
11496 
11497         },
11498 
11499         /**
11500          * Getter for a MediaPropertiesLayout object that is associated with User.
11501          * @param {finesse.interfaces.RestObjectHandlers} [handlers] Object that sets callback handlers (only 
11502          * applicable when Object has not been previously created).
11503          * @returns {finesse.restservices.MediaPropertiesLayout}
11504          *     The MediaPropertiesLayout object associated with this user
11505          */
11506         getMediaPropertiesLayout: function (callbacks) {
11507             var options = callbacks || {};
11508             options.parentObj = this;
11509             options.id = this._id;
11510     
11511             this.isLoaded();
11512             if (this._mediaPropertiesLayout === null) {
11513                 this._mediaPropertiesLayout = new MediaPropertiesLayout(options);
11514             }
11515             return this._mediaPropertiesLayout;
11516         },
11517     
11518         /**
11519          * Getter for the supervised Teams this User (Supervisor) supervises, if any.
11520          * @see finesse.restservices.Team
11521          * @returns {Array}
11522          *     An array of Teams supervised by this User (Supervisor)
11523          */
11524         getSupervisedTeams: function () {
11525             this.isLoaded();
11526     
11527             try {
11528                 return Utilities.getArray(this.getData().teams.Team);
11529             } catch (e) {
11530                 return [];
11531             }
11532     
11533         },
11534     
11535         /**
11536          * Perform an agent login for this user, associating him with the
11537          * specified extension.
11538          * @param {Object} params
11539          *     An object containing properties for agent login.
11540          * @param {String} params.extension
11541          *     The extension to associate with this user
11542          * @param {Object} [params.mobileAgent]
11543          *     A mobile agent object containing the mode and dial number properties.
11544          * @param {finesse.interfaces.RequestHandlers} params.handlers
11545          * @see finesse.interfaces.RequestHandlers
11546          * @returns {finesse.restservices.User}
11547          *     This User object, to allow cascading
11548          * @private
11549          */
11550         _login: function (params) {
11551             var handlers, contentBody = {},
11552             restType = this.getRestType();
11553             
11554             // Protect against null dereferencing.
11555             params = params || {};
11556     
11557             contentBody[restType] = {
11558                 "state": User.States.LOGIN,
11559                 "extension": params.extension
11560             };
11561     
11562             // Create mobile agent node if available.
11563             if (typeof params.mobileAgent === "object") {
11564                 contentBody[restType].mobileAgent = {
11565                     "mode": params.mobileAgent.mode,
11566                     "dialNumber": params.mobileAgent.dialNumber
11567                 };
11568             }
11569     
11570             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
11571             handlers = params.handlers || {};
11572     
11573             this.restRequest(this.getRestUrl(), {
11574                 method: 'PUT',
11575                 success: handlers.success,
11576                 error: handlers.error,
11577                 content: contentBody
11578             });
11579     
11580             return this; // Allow cascading
11581         },
11582     
11583         /**
11584          * Perform an agent login for this user, associating him with the
11585          * specified extension.
11586          * @param {String} extension
11587          *     The extension to associate with this user
11588          * @param {finesse.interfaces.RequestHandlers} handlers
11589          *     An object containing the handlers for the request
11590          * @returns {finesse.restservices.User}
11591          *     This User object, to allow cascading
11592          */
11593         login: function (extension, handlers) {
11594             this.isLoaded();
11595             var params = {
11596                 "extension": extension,
11597                 "handlers": handlers
11598             };
11599             return this._login(params);
11600         },
11601     
11602         /**
11603          * Perform an agent login for this user, associating him with the
11604          * specified extension.
11605          * @param {String} extension
11606          *     The extension to associate with this user
11607          * @param {String} mode
11608          *     The mobile agent work mode as defined in finesse.restservices.User.WorkMode.
11609          * @param {String} extension
11610          *     The external dial number desired to be used by the mobile agent.
11611          * @param {finesse.interfaces.RequestHandlers} handlers
11612          *     An object containing the handlers for the request
11613          * @returns {finesse.restservices.User}
11614          *     This User object, to allow cascading
11615          */
11616         loginMobileAgent: function (extension, mode, dialNumber, handlers) {
11617             this.isLoaded();
11618             var params = {
11619                 "extension": extension,
11620                 "mobileAgent": {
11621                     "mode": mode,
11622                     "dialNumber": dialNumber
11623                 },
11624                 "handlers": handlers
11625             };
11626             return this._login(params);
11627         },
11628     
11629         /**
11630          * Perform an agent logout for this user.
11631          * @param {String} reasonCode
11632          *     The reason this user is logging out.  Pass null for no reason.
11633          * @param {finesse.interfaces.RequestHandlers} handlers
11634          *     An object containing the handlers for the request
11635          * @returns {finesse.restservices.User}
11636          *     This User object, to allow cascading
11637          */
11638         logout: function (reasonCode, handlers) {
11639             return this.setState("LOGOUT", reasonCode, handlers);
11640         },
11641     
11642         /**
11643          * Set the state of the user.
11644          * @param {String} newState
11645          *     The state you are setting
11646          * @param {ReasonCode} reasonCode
11647          *     The reason this user is logging out.  Pass null for no reason.
11648          * @param {finesse.interfaces.RequestHandlers} handlers
11649          *     An object containing the handlers for the request
11650          * @see finesse.restservices.User.States
11651          * @returns {finesse.restservices.User}
11652          *     This User object, to allow cascading
11653          */
11654         setState: function (newState, reasonCode, handlers) {
11655             this.isLoaded();
11656     
11657             var options, contentBody = {};
11658     
11659             if (!reasonCode) {
11660                 contentBody[this.getRestType()] = {
11661                     "state": newState
11662                 };
11663             } else {
11664                 contentBody[this.getRestType()] = {
11665                     "state": newState,
11666                     "reasonCodeId": reasonCode.id
11667                 };
11668             }
11669     
11670             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
11671             handlers = handlers || {};
11672     
11673             options = {
11674                 method: 'PUT',
11675                 success: handlers.success,
11676                 error: handlers.error,
11677                 content: contentBody
11678             };
11679     
11680             // After removing the selective 202 handling, we should be able to just use restRequest
11681             this.restRequest(this.getRestUrl(), options);
11682     
11683             return this; // Allow cascading
11684         },
11685     
11686         /**
11687          * Make call to a particular phone number.
11688          *
11689          * @param {String} 
11690          *     The number to call
11691          * @param {finesse.interfaces.RequestHandlers} handlers
11692          *     An object containing the handlers for the request
11693          * @returns {finesse.restservices.User}
11694          *     This User object, to allow cascading
11695          */ 
11696         makeCall: function (number, handlers) {
11697             this.isLoaded();
11698     
11699             this.getDialogs().createNewCallDialog(number, this.getExtension(), handlers);
11700     
11701             return this; // Allow cascading
11702         },
11703     
11704         /**
11705          * Make a silent monitor call to a particular agent's phone number.
11706          *
11707          * @param {String} 
11708          *     The number to call
11709          * @param {finesse.interfaces.RequestHandlers} handlers
11710          *     An object containing the handlers for the request
11711          * @returns {finesse.restservices.User}
11712          *     This User object, to allow cascading
11713          */
11714         makeSMCall: function (number, handlers) {
11715             this.isLoaded();
11716     
11717             var actionType = "SILENT_MONITOR";
11718     
11719             this.getDialogs().createNewSuperviseCallDialog(number, this.getExtension(), actionType, handlers);
11720     
11721             return this; // Allow cascading
11722         },
11723         
11724     
11725         /**
11726          * Make a silent monitor call to a particular agent's phone number.
11727          *
11728          * @param {String}
11729          *     The number to call
11730          * @param {String} dialogUri
11731          *     The associated dialog uri of SUPERVISOR_MONITOR call
11732          * @param {finesse.interfaces.RequestHandlers} handlers
11733          *     An object containing the handlers for the request
11734          * @see finesse.restservices.dialog
11735          * @returns {finesse.restservices.User}
11736          *     This User object, to allow cascading
11737          */
11738         makeBargeCall:function (number, dialogURI, handlers) {
11739             this.isLoaded();
11740             var actionType = "BARGE_CALL";
11741             this.getDialogs().createNewBargeCall( this.getExtension(), number, actionType, dialogURI,handlers);
11742     
11743             return this; // Allow cascading
11744         },
11745         
11746         /**
11747          * Returns true if the user's current state will result in a pending state change. A pending state
11748          * change is a request to change state that does not result in an immediate state change. For
11749          * example if an agent attempts to change to the NOT_READY state while in the TALKING state, the
11750          * agent will not change state until the call ends.
11751          *
11752          * The current set of states that result in pending state changes is as follows:
11753          *     TALKING
11754          *     HOLD
11755          *     RESERVED_OUTBOUND_PREVIEW
11756          *  @returns {Boolean} True if there is a pending state change.
11757          *  @see finesse.restservices.User.States
11758          */
11759         isPendingStateChange: function () {
11760             var state = this.getState();
11761             return state && ((state === User.States.TALKING) || (state === User.States.HOLD) || (state === User.States.RESERVED_OUTBOUND_PREVIEW));
11762         },
11763         
11764         /**
11765          * Returns true if the user's current state is WORK or WORK_READY. This is used so
11766          * that a pending state is not cleared when moving into wrap up (work) mode. 
11767          * Note that we don't add this as a pending state, since changes while in wrap up
11768          * occur immediately (and we don't want any "pending state" to flash on screen.
11769          * 
11770          * @see finesse.restservices.User.States
11771          * @returns {Boolean} True if user is in wrap-up mode.
11772          */
11773         isWrapUp: function () {
11774             var state = this.getState();
11775             return state && ((state === User.States.WORK) || (state === User.States.WORK_READY));
11776         },
11777     
11778         /**
11779          * @private
11780          * Parses a uriString to retrieve the id portion
11781          * @param {String} uriString
11782          * @return {String} id
11783          */
11784         _parseIdFromUriString : function (uriString) {
11785             return Utilities.getId(uriString);
11786         },
11787 
11788         /**
11789          * Gets the user's Reason Code label.
11790          * Works for both Not Ready and Logout reason codes
11791          * @return {String} the reason code label, or empty string if none
11792          */
11793         getReasonCodeLabel : function () {
11794             this.isLoaded();
11795 
11796             if (this.getData().reasonCode) {
11797                 return this.getData().reasonCode.label;
11798             } else {
11799                 return "";
11800             }
11801         },
11802     
11803         /**
11804          * Gets the user's Not Ready reason code.
11805          * @return {String} Reason Code Id, or undefined if not set or indeterminate
11806          */
11807         getNotReadyReasonCodeId : function () {
11808             this.isLoaded();
11809     
11810             var reasoncodeIdResult, finesseServerReasonCodeId;
11811             finesseServerReasonCodeId = this.getData().reasonCodeId;
11812     
11813             //FinesseServer will give "-l" => we will set to undefined (for convenience)
11814             if (finesseServerReasonCodeId !== "-1") {
11815                 reasoncodeIdResult = finesseServerReasonCodeId;
11816             }
11817     
11818             return reasoncodeIdResult;
11819         },
11820     
11821         /**
11822          * Performs a GET against the Finesse server looking up the reasonCodeId specified.
11823          * Note that there is no return value; use the success handler to process a
11824          * valid return.
11825          * @param {finesse.interfaces.RequestHandlers} handlers
11826          *     An object containing the handlers for the request
11827          * @param {String} reasonCodeId The id for the reason code to lookup
11828          * 
11829          */
11830         getReasonCodeById : function (handlers, reasonCodeId)
11831         {
11832             var self = this, contentBody, reasonCode, url;
11833             contentBody = {};
11834     
11835             url = this.getRestUrl() + "/ReasonCode/" + reasonCodeId;
11836             this.restRequest(url, {
11837                 method: 'GET',
11838                 success: function (rsp) {
11839                     reasonCode = {
11840                         uri: rsp.object.ReasonCode.uri,
11841                         label: rsp.object.ReasonCode.label,
11842                         id: self._parseIdFromUriString(rsp.object.ReasonCode.uri)
11843                     };
11844                     handlers.success(reasonCode);
11845                 },
11846                 error: function (rsp) {
11847                     handlers.error(rsp);
11848                 },
11849                 content: contentBody
11850             });
11851         },
11852     
11853         /**
11854          * Performs a GET against Finesse server retrieving all the specified type of reason codes.
11855          * @param {String} type (LOGOUT or NOT_READY)
11856          * @param {finesse.interfaces.RequestHandlers} handlers
11857          *     An object containing the handlers for the request
11858          */
11859         _getReasonCodesByType : function (type, handlers)
11860         {
11861             var self = this, contentBody = {}, url, reasonCodes, i, reasonCodeArray;
11862     
11863             url = this.getRestUrl() + "/ReasonCodes?category=" + type;
11864             this.restRequest(url, {
11865                 method: 'GET',
11866                 success: function (rsp) {
11867                     reasonCodes = [];
11868     
11869                     reasonCodeArray = rsp.object.ReasonCodes.ReasonCode;
11870                     if (reasonCodeArray === undefined) {
11871                         reasonCodes = undefined;
11872                     } else if (reasonCodeArray[0] !== undefined) {
11873                         for (i = 0; i < reasonCodeArray.length; i = i + 1) {
11874                             reasonCodes[i] = {
11875                                 label: rsp.object.ReasonCodes.ReasonCode[i].label,
11876                                 id: self._parseIdFromUriString(rsp.object.ReasonCodes.ReasonCode[i].uri)
11877                             };
11878                         }
11879                     } else {
11880                         reasonCodes[0] = {
11881                             label: rsp.object.ReasonCodes.ReasonCode.label,
11882                             id: self._parseIdFromUriString(rsp.object.ReasonCodes.ReasonCode.uri)
11883                         };
11884                     }
11885                     handlers.success(reasonCodes);
11886                 },
11887                 error: function (rsp) {
11888                     handlers.error(rsp);
11889                 },
11890                 content: contentBody
11891             });
11892         },
11893     
11894         /**
11895          * Performs a GET against Finesse server retrieving all the Signout reason codes.
11896          * Note that there is no return value; use the success handler to process a
11897          * valid return.
11898          * @param {finesse.interfaces.RequestHandlers} handlers
11899          *     An object containing the handlers for the request
11900          */
11901         getSignoutReasonCodes : function (handlers)
11902         {
11903             this._getReasonCodesByType("LOGOUT", handlers);
11904         },
11905     
11906         /**
11907          * Performs a GET against Finesse server retrieving all the Not Ready reason codes.
11908          * Note that there is no return value; use the success handler to process a
11909          * valid return.
11910          * @param {finesse.interfaces.RequestHandlers} handlers
11911          *     An object containing the handlers for the request
11912          */
11913         getNotReadyReasonCodes : function (handlers)
11914         {
11915             this._getReasonCodesByType("NOT_READY", handlers);
11916         }
11917     });
11918     
11919     User.States = /** @lends finesse.restservices.User.States.prototype */ {
11920             /**
11921              * User Login.  Note that while this is an action, is not technically a state, since a 
11922              * logged-in User will always be in a specific state (READY, NOT_READY, TALKING, etc.).
11923              */
11924             LOGIN: "LOGIN",
11925             /**
11926              * User is logged out.
11927              */
11928             LOGOUT: "LOGOUT",
11929             /**
11930              * User is not ready. Note that in UCCX implementations, the user is in this state while on a non-routed call.
11931              */
11932             NOT_READY: "NOT_READY",
11933             /**
11934              * User is ready for calls.
11935              */
11936             READY: "READY",
11937             /**
11938              * User has a call coming in, but has not answered it.
11939              */
11940             RESERVED: "RESERVED",
11941             /**
11942              * User has an outbound call being made, but has not been connected to it.
11943              */
11944             RESERVED_OUTBOUND: "RESERVED_OUTBOUND",
11945             /**
11946              * User has an outbound call's preview information being displayed, but has not acted on it.
11947              */
11948             RESERVED_OUTBOUND_PREVIEW: "RESERVED_OUTBOUND_PREVIEW",
11949             /**
11950              * User is on a call.  Note that in UCCX implementations, this is for routed calls only.
11951              */
11952             TALKING: "TALKING",
11953             /**
11954              * User is on hold.  Note that in UCCX implementations, the user remains in TALKING state while on hold.
11955              */
11956             HOLD: "HOLD",
11957             /**
11958              * User is wrap-up/work mode.  This mode is typically configured to time out, after which the user becomes NOT_READY.
11959              */
11960             WORK: "WORK",
11961             /**
11962              * This is the same as WORK, except that after time out user becomes READY.
11963              */
11964             WORK_READY: "WORK_READY",
11965             /**
11966              * @class Possible User state values.
11967              * @constructs
11968              */
11969             _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
11970           
11971         };
11972     
11973     User.WorkMode = { /** @lends finesse.restservices.User.WorkMode.prototype */
11974         /**
11975          * Mobile agent is connected (dialed) for each incoming call received.
11976          */
11977         CALL_BY_CALL: "CALL_BY_CALL",
11978         /**
11979          * Mobile agent is connected (dialed) at login.
11980          */
11981         NAILED_CONNECTION: "NAILED_CONNECTION",
11982         /**
11983          * @class Possible Mobile Agent Work Mode Types.
11984          * @constructs
11985          */
11986         _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
11987         
11988     };
11989 
11990     User.WrapUpMode = { /** @lends finesse.restservices.User.WrapUpMode.prototype */
11991         /**
11992          * Agent must go into wrap-up when call ends
11993          */
11994         REQUIRED: "REQUIRED",
11995         /**
11996          * Agent must go into wrap-up when call ends and must enter wrap-up data
11997          */
11998         REQUIRED_WITH_WRAP_UP_DATA: "REQUIRED_WITH_WRAP_UP_DATA",
11999         /**
12000          * Agent can choose to go into wrap-up on a call-by-call basis when the call ends
12001          */
12002         OPTIONAL: "OPTIONAL",
12003         /**
12004          * Agent is not allowed to go into wrap-up when call ends.
12005          */
12006         NOT_ALLOWED: "NOT_ALLOWED",
12007         /**
12008          * @class Possible Wrap-up Mode Types.
12009          * @constructs
12010          */
12011         _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
12012         
12013     };
12014 
12015     window.finesse = window.finesse || {};
12016     window.finesse.restservices = window.finesse.restservices || {};
12017     window.finesse.restservices.User = User;
12018         
12019     return User;
12020 });
12021 
12022 /**
12023  * JavaScript representation of the Finesse Users collection
12024  * object which contains a list of Users objects.
12025  *
12026  * @requires finesse.clientservices.ClientServices
12027  * @requires Class
12028  * @requires finesse.FinesseBase
12029  * @requires finesse.restservices.RestBase
12030  * @requires finesse.restservices.RestCollectionBase
12031  * @requires finesse.restservices.User
12032  */
12033 
12034 /** @private */
12035 define('restservices/Users',[
12036     'restservices/RestCollectionBase',
12037     'restservices/RestBase',
12038     'restservices/User'
12039 ],
12040 function (RestCollectionBase, RestBase, User) {
12041 
12042     var Users = RestCollectionBase.extend(/** @lends finesse.restservices.Users.prototype */{
12043 
12044     /**
12045      * @class
12046      * JavaScript representation of a Users collection object. 
12047      * While there is no method provided to retrieve all Users, this collection is
12048      * used to return the Users in a supervised Team.
12049      * @augments finesse.restservices.RestCollectionBase
12050      * @constructs
12051      * @see finesse.restservices.Team
12052      * @see finesse.restservices.User
12053      * @see finesse.restservices.User#getSupervisedTeams
12054      * @example
12055      *  // Note: The following method gets an Array of Teams, not a Collection.
12056      *  _teams = _user.getSupervisedTeams();
12057      *  if (_teams.length > 0) {
12058      *      _team0Users = _teams[0].getUsers();
12059      *  }
12060      */
12061     _fakeConstuctor: function () {
12062         /* This is here to hide the real init constructor from the public docs */
12063     },
12064         
12065     /**
12066      * @private
12067      * JavaScript representation of the Finesse Users collection
12068      * object which contains a list of Users objects.
12069      *
12070 	 * @param {Object} options
12071 	 *     An object with the following properties:<ul>
12072      *         <li><b>id:</b> The id of the object being constructed</li>
12073      *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
12074      *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
12075      *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
12076      *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
12077      *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
12078      *             <li><b>status:</b> {Number} The HTTP status code returned</li>
12079      *             <li><b>content:</b> {String} Raw string of response</li>
12080      *             <li><b>object:</b> {Object} Parsed object of response</li>
12081      *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
12082      *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
12083      *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
12084      *             </ul></li>
12085      *         </ul></li>
12086      *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
12087      **/
12088 	init: function (options) {
12089 		this._super(options);
12090 	},
12091 
12092 	/**
12093      * @private
12094 	 * Gets the REST class for the current object - this is the Users class.
12095 	 */
12096 	getRestClass: function () {
12097 	    return Users;
12098 	},
12099 
12100 	/**
12101      * @private
12102 	 * Gets the REST class for the objects that make up the collection. - this
12103 	 * is the User class.
12104 	 */
12105 	getRestItemClass: function () {
12106 		return User;
12107 	},
12108 
12109 	/**
12110      * @private
12111 	 * Gets the REST type for the current object - this is a "Users".
12112 	 */
12113 	getRestType: function () {
12114 	    return "Users";
12115 	},
12116 
12117 	/**
12118      * @private
12119 	 * Gets the REST type for the objects that make up the collection - this is "User".
12120 	 */
12121 	getRestItemType: function () {
12122 	    return "User";
12123 	},
12124 
12125 	/**
12126      * @private
12127      * Gets the node path for the current object - this is the team Users node
12128      * @returns {String} The node path
12129      */
12130     getXMPPNodePath: function () {
12131 		return this.getRestUrl();
12132     },
12133 
12134     /**
12135      * @private
12136      * Overloading _doGET to reroute the GET to /Team/id from /Team/id/Users
12137      * This needs to be done because the GET /Team/id/Users API is missing
12138      * @returns {Users} This Users (collection) object to allow cascading
12139      */
12140     _doGET: function (handlers) {
12141         var _this = this;
12142         handlers = handlers || {};
12143         // Only do this for /Team/id/Users
12144         if (this._restObj && this._restObj.getRestType() === "Team") {
12145             this._restObj._doGET({
12146                 success: function (rspObj) {
12147                     // Making sure the response was a valid Team
12148                     if (_this._restObj._validate(rspObj.object)) {
12149                         // Shimmying the response to look like a Users collection by extracting it from the Team response
12150                         rspObj.object[_this.getRestType()] = rspObj.object[_this._restObj.getRestType()][_this.getRestType().toLowerCase()];
12151                         handlers.success(rspObj);
12152                     } else {
12153                         handlers.error(rspObj);
12154                     }
12155                 },
12156                 error: handlers.error
12157             });
12158             return this; // Allow cascading
12159         } else {
12160             return this._super(handlers);
12161         }
12162     },
12163 
12164 	/**
12165      * @private
12166      * Override default to indicates that the collection doesn't support making
12167 	 * requests.
12168 	 */
12169 	supportsRequests: false,
12170 
12171     /**
12172      * @private
12173      * Indicates that this collection handles the subscription for its items
12174      */
12175     handlesItemSubscription: true,
12176 	
12177     /**
12178      * @private
12179      * Override default to indicate that we need to subscribe explicitly
12180      */
12181     explicitSubscription: true
12182     
12183 	});
12184 
12185     window.finesse = window.finesse || {};
12186     window.finesse.restservices = window.finesse.restservices || {};
12187     window.finesse.restservices.Users = Users;
12188 
12189     return Users;
12190 });
12191 
12192 /**
12193  * JavaScript representation of the Finesse Team Not Ready Reason Code Assignment object.
12194  *
12195  * @requires finesse.clientservices.ClientServices
12196  * @requires Class
12197  * @requires finesse.FinesseBase
12198  * @requires finesse.restservices.RestBase
12199  */
12200 
12201 /** @private */
12202 define('restservices/TeamNotReadyReasonCode',['restservices/RestBase'], function (RestBase) {
12203     
12204     var TeamNotReadyReasonCode = RestBase.extend( {
12205 
12206         /**
12207          * @class
12208          * JavaScript representation of a Team Not Ready ReasonCode object. Also exposes
12209          * methods to operate on the object against the server.
12210          *
12211          * @param {Object} options
12212          *     An object with the following properties:<ul>
12213          *         <li><b>id:</b> The id of the object being constructed</li>
12214          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
12215          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
12216          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
12217          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
12218          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
12219          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
12220          *             <li><b>content:</b> {String} Raw string of response</li>
12221          *             <li><b>object:</b> {Object} Parsed object of response</li>
12222          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
12223          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
12224          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
12225          *             </ul></li>
12226          *         </ul></li>
12227          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
12228          * @constructs
12229          **/
12230         init: function (options) {
12231             this._super(options);
12232         },
12233     
12234         /**
12235          * @private
12236          * Gets the REST class for the current object - this is the TeamNotReadyReasonCode class.
12237          * @returns {Object} The TeamNotReadyReasonCode class.
12238          */
12239         getRestClass: function () {
12240             return TeamNotReadyReasonCode;
12241         },
12242     
12243         /**
12244          * @private
12245          * Gets the REST type for the current object - this is a "ReasonCode".
12246          * @returns {String} The ReasonCode string.
12247          */
12248         getRestType: function () {
12249             return "ReasonCode";
12250         },
12251     
12252         /**
12253          * @private
12254          * Override default to indicate that this object doesn't support making
12255          * requests.
12256          */
12257         supportsRequests: false,
12258     
12259         /**
12260          * @private
12261          * Override default to indicate that this object doesn't support subscriptions.
12262          */
12263         supportsSubscriptions: false,
12264     
12265         /**
12266          * Getter for the category.
12267          * @returns {String} The category.
12268          */
12269         getCategory: function () {
12270             this.isLoaded();
12271             return this.getData().category;
12272         },
12273     
12274         /**
12275          * Getter for the code.
12276          * @returns {String} The code.
12277          */
12278         getCode: function () {
12279             this.isLoaded();
12280             return this.getData().code;
12281         },
12282     
12283         /**
12284          * Getter for the label.
12285          * @returns {String} The label.
12286          */
12287         getLabel: function () {
12288             this.isLoaded();
12289             return this.getData().label;
12290         },
12291     
12292         /**
12293          * Getter for the forAll value.
12294          * @returns {String} The forAll.
12295          */
12296         getForAll: function () {
12297             this.isLoaded();
12298             return this.getData().forAll;
12299         },
12300     
12301         /**
12302          * Getter for the Uri value.
12303          * @returns {String} The Uri.
12304          */
12305         getUri: function () {
12306             this.isLoaded();
12307             return this.getData().uri;
12308         }
12309 
12310     });
12311     
12312     window.finesse = window.finesse || {};
12313     window.finesse.restservices = window.finesse.restservices || {};
12314     window.finesse.restservices.TeamNotReadyReasonCode = TeamNotReadyReasonCode;
12315         
12316     return TeamNotReadyReasonCode;
12317 });
12318 
12319 /**
12320 * JavaScript representation of the Finesse TeamNotReadyReasonCodes collection
12321 * object which contains a list of TeamNotReadyReasonCode objects.
12322  *
12323  * @requires finesse.clientservices.ClientServices
12324  * @requires Class
12325  * @requires finesse.FinesseBase
12326  * @requires finesse.restservices.RestBase
12327  * @requires finesse.restservices.Dialog
12328  * @requires finesse.restservices.RestCollectionBase
12329  */
12330 
12331 /** @private */
12332 define('restservices/TeamNotReadyReasonCodes',[
12333     'restservices/RestCollectionBase',
12334     'restservices/RestBase',
12335     'restservices/TeamNotReadyReasonCode'
12336 ],
12337 function (RestCollectionBase, RestBase, TeamNotReadyReasonCode) {
12338 
12339     var TeamNotReadyReasonCodes = RestCollectionBase.extend( {
12340 
12341       /**
12342        * @class
12343        * JavaScript representation of a TeamNotReadyReasonCodes collection object. Also exposes
12344        * methods to operate on the object against the server.
12345        *
12346        * @param {Object} options
12347        *     An object with the following properties:<ul>
12348        *         <li><b>id:</b> The id of the object being constructed</li>
12349        *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
12350        *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
12351        *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
12352        *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
12353        *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
12354        *             <li><b>status:</b> {Number} The HTTP status code returned</li>
12355        *             <li><b>content:</b> {String} Raw string of response</li>
12356        *             <li><b>object:</b> {Object} Parsed object of response</li>
12357        *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
12358        *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
12359        *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
12360        *             </ul></li>
12361        *         </ul></li>
12362        *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
12363        * @augments finesse.restservices.RestCollectionBase
12364        * @constructs
12365        **/
12366       init: function (options) {
12367           this._super(options);
12368       },
12369     
12370       /**
12371        * @private
12372        * Gets the REST class for the current object - this is the TeamNotReadyReasonCodes class.
12373        */
12374       getRestClass: function () {
12375           return TeamNotReadyReasonCodes;
12376       },
12377     
12378       /**
12379        * @private
12380        * Gets the REST class for the objects that make up the collection. - this
12381        * is the TeamNotReadyReasonCode class.
12382        */
12383       getRestItemClass: function () {
12384           return TeamNotReadyReasonCode;
12385       },
12386     
12387       /**
12388        * @private
12389        * Gets the REST type for the current object - this is a "ReasonCodes".
12390        */
12391       getRestType: function () {
12392           return "ReasonCodes";
12393       },
12394     
12395       /**
12396        * @private
12397        * Overrides the parent class.  Returns the url for the NotReadyReasonCodes resource
12398        */
12399       getRestUrl: function () {
12400           // return ("/finesse/api/" + this.getRestType() + "?category=NOT_READY");
12401           var restObj = this._restObj,
12402               restUrl = "";
12403           //Prepend the base REST object if one was provided.
12404           //Otherwise prepend with the default webapp name.
12405           if (restObj instanceof RestBase) {
12406               restUrl += restObj.getRestUrl();
12407           }
12408           else {
12409               restUrl += "/finesse/api";
12410           }
12411           //Append the REST type.
12412           restUrl += "/ReasonCodes?category=NOT_READY";
12413           //Append ID if it is not undefined, null, or empty.
12414           if (this._id) {
12415               restUrl += "/" + this._id;
12416           }
12417           return restUrl;
12418       },
12419     
12420       /**
12421        * @private
12422        * Gets the REST type for the objects that make up the collection - this is "ReasonCode".
12423        */
12424       getRestItemType: function () {
12425           return "ReasonCode";
12426       },
12427     
12428       /**
12429        * @private
12430        * Override default to indicates that the collection supports making
12431        * requests.
12432        */
12433       supportsRequests: true,
12434     
12435       /**
12436        * @private
12437        * Override default to indicate that this object doesn't support subscriptions.
12438        */
12439       supportsRestItemSubscriptions: false,
12440     
12441       /**
12442        * @private
12443        * Retrieve the Not Ready Reason Codes.
12444        *
12445        * @returns {TeamNotReadyReasonCodes}
12446        *     This TeamNotReadyReasonCodes object to allow cascading.
12447        */
12448       get: function () {
12449           // set loaded to false so it will rebuild the collection after the get
12450           this._loaded = false;
12451           // reset collection
12452           this._collection = {};
12453           // perform get
12454           this._synchronize();
12455           return this;
12456       },
12457     
12458       /**
12459        * @private
12460        * Set up the PutSuccessHandler for TeamNotReadyReasonCodes
12461        * @param {Object} reasonCodes
12462        * @param {String} contentBody
12463        * @param successHandler    
12464        * @return {function}
12465        */
12466       createPutSuccessHandler: function (reasonCodes, contentBody, successHandler) {
12467           return function (rsp) {
12468               // Update internal structure based on response. Here we
12469               // inject the contentBody from the PUT request into the
12470               // rsp.object element to mimic a GET as a way to take
12471               // advantage of the existing _processResponse method.
12472               rsp.object = contentBody;
12473               reasonCodes._processResponse(rsp);
12474     
12475               //Remove the injected contentBody object before cascading response
12476               rsp.object = {};
12477     
12478               //cascade response back to consumer's response handler
12479               successHandler(rsp);
12480           };
12481       },
12482     
12483       /**
12484        * @private
12485        * Perform the REST API PUT call to update the reason code assignments for the team
12486        * @param {string[]} newValues
12487        * @param handlers     
12488        */
12489       update: function (newValues, handlers) {
12490           this.isLoaded();
12491           var contentBody = {}, contentBodyInner = [], i, innerObject = {};
12492     
12493           contentBody[this.getRestType()] = {
12494           };
12495     
12496           for (i in newValues) {
12497               if (newValues.hasOwnProperty(i)) {
12498                   innerObject = {
12499                       "uri": newValues[i]
12500                   };
12501                   contentBodyInner.push(innerObject);
12502               }
12503           }
12504     
12505           contentBody[this.getRestType()] = {
12506               "ReasonCode" : contentBodyInner
12507           };
12508     
12509           // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
12510           handlers = handlers || {};
12511     
12512           this.restRequest(this.getRestUrl(), {
12513               method: 'PUT',
12514               success: this.createPutSuccessHandler(this, contentBody, handlers.success),
12515               error: handlers.error,
12516               content: contentBody
12517           });
12518     
12519           return this; // Allow cascading
12520       }
12521   });
12522   
12523     window.finesse = window.finesse || {};
12524     window.finesse.restservices = window.finesse.restservices || {};
12525     window.finesse.restservices.TeamNotReadyReasonCodes = TeamNotReadyReasonCodes;
12526     
12527   return TeamNotReadyReasonCodes;
12528 });
12529 
12530 /**
12531  * JavaScript representation of the Finesse Team Wrap Up Reason object.
12532  *
12533  * @requires finesse.clientservices.ClientServices
12534  * @requires Class
12535  * @requires finesse.FinesseBase
12536  * @requires finesse.restservices.RestBase
12537  */
12538 /** @private */
12539 define('restservices/TeamWrapUpReason',['restservices/RestBase'], function (RestBase) {
12540 
12541     var TeamWrapUpReason = RestBase.extend({
12542 
12543     /**
12544      * @class
12545      * JavaScript representation of a TeamWrapUpReason object. Also exposes
12546      * methods to operate on the object against the server.
12547      *
12548      * @param {Object} options
12549      *     An object with the following properties:<ul>
12550      *         <li><b>id:</b> The id of the object being constructed</li>
12551      *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
12552      *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
12553      *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
12554      *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
12555      *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
12556      *             <li><b>status:</b> {Number} The HTTP status code returned</li>
12557      *             <li><b>content:</b> {String} Raw string of response</li>
12558      *             <li><b>object:</b> {Object} Parsed object of response</li>
12559      *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
12560      *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
12561      *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
12562      *             </ul></li>
12563      *         </ul></li>
12564      *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
12565      * @constructs
12566      **/
12567     init: function (options) {
12568         this._super(options);
12569     },
12570 
12571     /**
12572      * @private
12573      * Gets the REST class for the current object - this is the TeamWrapUpReason class.
12574      * @returns {Object} The TeamWrapUpReason class.
12575      */
12576     getRestClass: function () {
12577         return TeamWrapUpReason;
12578     },
12579 
12580     /**
12581      * @private
12582      * Gets the REST type for the current object - this is a "WrapUpReason".
12583      * @returns {String} The WrapUpReason string.
12584      */
12585     getRestType: function () {
12586         return "WrapUpReason";
12587     },
12588 
12589     /**
12590      * @private
12591      * Override default to indicate that this object doesn't support making
12592      * requests.
12593      */
12594     supportsRequests: false,
12595 
12596     /**
12597      * @private
12598      * Override default to indicate that this object doesn't support subscriptions.
12599      */
12600     supportsSubscriptions: false,
12601 
12602     /**
12603      * Getter for the label.
12604      * @returns {String} The label.
12605      */
12606     getLabel: function () {
12607         this.isLoaded();
12608         return this.getData().label;
12609     },
12610 
12611     /**
12612      * @private
12613      * Getter for the forAll value.
12614      * @returns {Boolean} True if global
12615      */
12616     getForAll: function () {
12617         this.isLoaded();
12618         return this.getData().forAll;
12619     },
12620 
12621     /**
12622      * @private
12623      * Getter for the Uri value.
12624      * @returns {String} The Uri.
12625      */
12626     getUri: function () {
12627         this.isLoaded();
12628         return this.getData().uri;
12629     }
12630 	});
12631 
12632     window.finesse = window.finesse || {};
12633     window.finesse.restservices = window.finesse.restservices || {};
12634     window.finesse.restservices.TeamWrapUpReason = TeamWrapUpReason;
12635 
12636     return TeamWrapUpReason;
12637 });
12638 
12639 /**
12640 * JavaScript representation of the Finesse Team Wrap-Up Reasons collection
12641 * object which contains a list of Wrap-Up Reasons objects.
12642  *
12643  * @requires finesse.clientservices.ClientServices
12644  * @requires Class
12645  * @requires finesse.FinesseBase
12646  * @requires finesse.restservices.RestBase
12647  * @requires finesse.restservices.Dialog
12648  * @requires finesse.restservices.RestCollectionBase
12649  */
12650 /** @private */
12651 define('restservices/TeamWrapUpReasons',[
12652     'restservices/RestCollectionBase',
12653     'restservices/RestBase',
12654     'restservices/TeamWrapUpReason'
12655 ],
12656 function (RestCollectionBase, RestBase, TeamWrapUpReason) {
12657 
12658     var TeamWrapUpReasons = RestCollectionBase.extend({
12659 
12660     /**
12661      * @class
12662      * JavaScript representation of a TeamWrapUpReasons collection object. Also exposes
12663      * methods to operate on the object against the server.
12664      *
12665      * @param {Object} options
12666      *     An object with the following properties:<ul>
12667      *         <li><b>id:</b> The id of the object being constructed</li>
12668      *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
12669      *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
12670      *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
12671      *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
12672      *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
12673      *             <li><b>status:</b> {Number} The HTTP status code returned</li>
12674      *             <li><b>content:</b> {String} Raw string of response</li>
12675      *             <li><b>object:</b> {Object} Parsed object of response</li>
12676      *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
12677      *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
12678      *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
12679      *             </ul></li>
12680      *         </ul></li>
12681      *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
12682      * @constructs
12683      **/
12684     init: function (options) {
12685         this._super(options);
12686     },
12687 
12688     /**
12689      * @private
12690      * Gets the REST class for the current object - this is the TeamWrapUpReasons class.
12691      */
12692     getRestClass: function () {
12693         return TeamWrapUpReasons;
12694     },
12695 
12696     /**
12697      * @private
12698      * Gets the REST class for the objects that make up the collection. - this
12699      * is the TeamWrapUpReason class.
12700      */
12701     getRestItemClass: function () {
12702         return TeamWrapUpReason;
12703     },
12704 
12705     /**
12706      * @private
12707      * Gets the REST type for the current object - this is a "WrapUpReasons".
12708      */
12709     getRestType: function () {
12710         return "WrapUpReasons";
12711     },
12712 
12713     /**
12714      * @private
12715      * Gets the REST type for the objects that make up the collection - this is "WrapUpReason".
12716      */
12717     getRestItemType: function () {
12718         return "WrapUpReason";
12719     },
12720 
12721     /**
12722      * @private
12723      * Override default to indicates that the collection supports making
12724      * requests.
12725      */
12726     supportsRequests: true,
12727 
12728     /**
12729      * @private
12730      * Override default to indicate that this object doesn't support subscriptions.
12731      */
12732     supportsRestItemSubscriptions: false,
12733 
12734     /**
12735      * Retrieve the Team Wrap Up Reasons.
12736      *
12737      * @returns {finesse.restservices.TeamWrapUpReasons}
12738      *     This TeamWrapUpReasons object to allow cascading.
12739      */
12740     get: function () {
12741         // set loaded to false so it will rebuild the collection after the get
12742         this._loaded = false;
12743         // reset collection
12744         this._collection = {};
12745         // perform get
12746         this._synchronize();
12747         return this;
12748     },
12749 
12750     /**
12751      * Set up the PutSuccessHandler for TeamWrapUpReasons
12752      * @param {Object} wrapUpReasons
12753      * @param {Object} contentBody
12754      * @param successHandler
12755      * @returns response
12756      */
12757     createPutSuccessHandler: function (wrapUpReasons, contentBody, successHandler) {
12758         return function (rsp) {
12759             // Update internal structure based on response. Here we
12760             // inject the contentBody from the PUT request into the
12761             // rsp.object element to mimic a GET as a way to take
12762             // advantage of the existing _processResponse method.
12763             rsp.object = contentBody;
12764             
12765             wrapUpReasons._processResponse(rsp);
12766 
12767             //Remove the injected contentBody object before cascading response
12768             rsp.object = {};
12769 
12770             //cascade response back to consumer's response handler
12771             successHandler(rsp);
12772         };
12773     },
12774 
12775     /**    
12776      * Perform the REST API PUT call to update the reason code assignments for the team
12777      * @param {String Array} newValues
12778      * @param handlers
12779      * @returns {Object} this
12780      */
12781     update: function (newValues, handlers) {
12782         this.isLoaded();
12783         var contentBody = {}, contentBodyInner = [], i, innerObject = {};
12784 
12785         contentBody[this.getRestType()] = {
12786         };
12787 
12788         for (i in newValues) {
12789             if (newValues.hasOwnProperty(i)) {
12790                 innerObject = {
12791                     "uri": newValues[i]
12792                 };
12793                 contentBodyInner.push(innerObject);
12794             }
12795         }
12796 
12797         contentBody[this.getRestType()] = {
12798             "WrapUpReason" : contentBodyInner
12799         };
12800 
12801         // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
12802         handlers = handlers || {};
12803 
12804         this.restRequest(this.getRestUrl(), {
12805             method: 'PUT',
12806             success: this.createPutSuccessHandler(this, contentBody, handlers.success),
12807             error: handlers.error,
12808             content: contentBody
12809         });
12810 
12811         return this; // Allow cascading
12812     }
12813 	});
12814 
12815     window.finesse = window.finesse || {};
12816     window.finesse.restservices = window.finesse.restservices || {};
12817     window.finesse.restservices.TeamWrapUpReasons = TeamWrapUpReasons;
12818 
12819     return TeamWrapUpReasons;
12820 });
12821 
12822 /**
12823  * JavaScript representation of a TeamSignOutReasonCode.
12824  *
12825  * @requires finesse.clientservices.ClientServices
12826  * @requires Class
12827  * @requires finesse.FinesseBase
12828  * @requires finesse.restservices.RestBase
12829  */
12830 
12831 /** @private */
12832 define('restservices/TeamSignOutReasonCode',['restservices/RestBase'], function (RestBase) {
12833     var TeamSignOutReasonCode = RestBase.extend({
12834 
12835         /**
12836          * @class
12837          * JavaScript representation of a TeamSignOutReasonCode object. Also exposes
12838          * methods to operate on the object against the server.
12839          *
12840          * @param {Object} options
12841          *     An object with the following properties:<ul>
12842          *         <li><b>id:</b> The id of the object being constructed</li>
12843          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
12844          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
12845          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
12846          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
12847          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
12848          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
12849          *             <li><b>content:</b> {String} Raw string of response</li>
12850          *             <li><b>object:</b> {Object} Parsed object of response</li>
12851          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
12852          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
12853          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
12854          *             </ul></li>
12855          *         </ul></li>
12856          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
12857          * @constructs
12858          * @ignore
12859          **/
12860         init: function (options) {
12861             this._super(options);
12862         },
12863 
12864         /**
12865          * @private
12866          * Gets the REST class for the current object - this is the TeamSignOutReasonCode class.
12867          * @returns {Object} The TeamSignOutReasonCode class.
12868          */
12869         getRestClass: function () {
12870             return TeamSignOutReasonCode;
12871         },
12872 
12873         /**
12874          * @private
12875          * Gets the REST type for the current object - this is a "ReasonCode".
12876          * @returns {String} The ReasonCode string.
12877          */
12878         getRestType: function () {
12879             return "ReasonCode";
12880         },
12881 
12882         /**
12883          * @private
12884          * Override default to indicate that this object doesn't support making
12885          * requests.
12886          */
12887         supportsRequests: false,
12888 
12889         /**
12890          * @private
12891          * Override default to indicate that this object doesn't support subscriptions.
12892          */
12893         supportsSubscriptions: false,
12894 
12895         /**
12896          * Getter for the category.
12897          * @returns {String} The category.
12898          */
12899         getCategory: function () {
12900             this.isLoaded();
12901             return this.getData().category;
12902         },
12903 
12904         /**
12905          * Getter for the code.
12906          * @returns {String} The code.
12907          */
12908         getCode: function () {
12909             this.isLoaded();
12910             return this.getData().code;
12911         },
12912 
12913         /**
12914          * Getter for the label.
12915          * @returns {String} The label.
12916          */
12917         getLabel: function () {
12918             this.isLoaded();
12919             return this.getData().label;
12920         },
12921 
12922         /**
12923          * Getter for the forAll value.
12924          * @returns {String} The forAll.
12925          */
12926         getForAll: function () {
12927             this.isLoaded();
12928             return this.getData().forAll;
12929         },
12930 
12931         /**
12932          * Getter for the Uri value.
12933          * @returns {String} The Uri.
12934          */
12935         getUri: function () {
12936             this.isLoaded();
12937             return this.getData().uri;
12938         }
12939 
12940     });
12941 
12942     window.finesse = window.finesse || {};
12943     window.finesse.restservices = window.finesse.restservices || {};
12944     window.finesse.restservices.TeamSignOutReasonCode = TeamSignOutReasonCode;
12945     
12946     return TeamSignOutReasonCode;
12947 });
12948 
12949 /**
12950 * JavaScript representation of the TeamSignOutReasonCodes collection
12951 * object which contains a list of TeamSignOutReasonCode objects.
12952  *
12953  * @requires finesse.clientservices.ClientServices
12954  * @requires Class
12955  * @requires finesse.FinesseBase
12956  * @requires finesse.restservices.RestBase
12957  * @requires finesse.restservices.Dialog
12958  * @requires finesse.restservices.RestCollectionBase
12959  */
12960 
12961 /** @private */
12962 define('restservices/TeamSignOutReasonCodes',[
12963     'restservices/RestCollectionBase',
12964     'restservices/RestBase',
12965     'restservices/TeamSignOutReasonCode'
12966 ],
12967 function (RestCollectionBase, RestBase, TeamSignOutReasonCode) {
12968     
12969     var TeamSignOutReasonCodes = RestCollectionBase.extend({
12970         /**
12971          * @class
12972          * JavaScript representation of a TeamSignOutReasonCodes collection object. Also exposes
12973          * methods to operate on the object against the server.
12974          *
12975          * @param {Object} options
12976          *     An object with the following properties:<ul>
12977          *         <li><b>id:</b> The id of the object being constructed</li>
12978          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
12979          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
12980          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
12981          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
12982          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
12983          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
12984          *             <li><b>content:</b> {String} Raw string of response</li>
12985          *             <li><b>object:</b> {Object} Parsed object of response</li>
12986          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
12987          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
12988          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
12989          *             </ul></li>
12990          *         </ul></li>
12991          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
12992          * @constructs
12993          **/
12994         init: function (options) {
12995             this._super(options);
12996         },
12997 
12998         /**
12999          * @private
13000          * Gets the REST class for the current object - this is the TeamSignOutReasonCodes class.
13001          */
13002         getRestClass: function () {
13003             return TeamSignOutReasonCodes;
13004         },
13005 
13006         /**
13007          * @private
13008          * Gets the REST class for the objects that make up the collection. - this
13009          * is the TeamSignOutReasonCode class.
13010          */
13011         getRestItemClass: function () {
13012             return TeamSignOutReasonCode;
13013         },
13014 
13015         /**
13016          * @private
13017          * Gets the REST type for the current object - this is a "ReasonCodes".
13018          */
13019         getRestType: function () {
13020             return "ReasonCodes";
13021         },
13022 
13023         /**
13024          * Overrides the parent class.  Returns the url for the SignOutReasonCodes resource
13025          */
13026         getRestUrl: function () {
13027             var restObj = this._restObj, restUrl = "";
13028 
13029             //Prepend the base REST object if one was provided.
13030             //Otherwise prepend with the default webapp name.
13031             if (restObj instanceof RestBase) {
13032                 restUrl += restObj.getRestUrl();
13033             } else {
13034                 restUrl += "/finesse/api";
13035             }
13036             //Append the REST type.
13037             restUrl += "/ReasonCodes?category=LOGOUT";
13038             //Append ID if it is not undefined, null, or empty.
13039             if (this._id) {
13040                 restUrl += "/" + this._id;
13041             }
13042             return restUrl;
13043         },
13044 
13045         /**
13046          * @private
13047          * Gets the REST type for the objects that make up the collection - this is "ReasonCode".
13048          */
13049         getRestItemType: function () {
13050             return "ReasonCode";
13051         },
13052 
13053         /**
13054          * @private
13055          * Override default to indicates that the collection supports making requests.
13056          */
13057         supportsRequests: true,
13058 
13059         /**
13060          * @private
13061          * Override default to indicates that the collection does not subscribe to its objects.
13062          */
13063         supportsRestItemSubscriptions: false,
13064 
13065         /**
13066          * Retrieve the Sign Out Reason Codes.
13067          *
13068          * @returns {finesse.restservices.TeamSignOutReasonCodes}
13069          *     This TeamSignOutReasonCodes object to allow cascading.
13070          */
13071         get: function () {
13072             // set loaded to false so it will rebuild the collection after the get
13073             this._loaded = false;
13074             // reset collection
13075             this._collection = {};
13076             // perform get
13077             this._synchronize();
13078             return this;
13079         },
13080 
13081         /* We only use PUT and GET on Reason Code team assignments
13082          * @param {Object} contact
13083          * @param {Object} contentBody
13084          * @param {Function} successHandler
13085          */
13086         createPutSuccessHandler: function (contact, contentBody, successHandler) {
13087             return function (rsp) {
13088                 // Update internal structure based on response. Here we
13089                 // inject the contentBody from the PUT request into the
13090                 // rsp.object element to mimic a GET as a way to take
13091                 // advantage of the existing _processResponse method.
13092                 rsp.object = contentBody;
13093                 contact._processResponse(rsp);
13094 
13095                 //Remove the injected contentBody object before cascading response
13096                 rsp.object = {};
13097 
13098                 //cascade response back to consumer's response handler
13099                 successHandler(rsp);
13100             };
13101         },
13102 
13103         /**
13104          * Update - This should be all that is needed.
13105          * @param {Object} newValues
13106          * @param {Object} handlers
13107          * @returns {finesse.restservices.TeamSignOutReasonCodes}
13108          *     This TeamSignOutReasonCodes object to allow cascading.
13109          */
13110         update: function (newValues, handlers) {
13111             this.isLoaded();
13112             var contentBody = {}, contentBodyInner = [], i, innerObject = {};
13113 
13114             contentBody[this.getRestType()] = {
13115             };
13116 
13117             for (i in newValues) {
13118                 if (newValues.hasOwnProperty(i)) {
13119                     innerObject = {
13120                         "uri": newValues[i]
13121                     };
13122                     contentBodyInner.push(innerObject);
13123                 }
13124             }
13125 
13126             contentBody[this.getRestType()] = {
13127                 "ReasonCode" : contentBodyInner
13128             };
13129 
13130             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
13131             handlers = handlers || {};
13132 
13133             this.restRequest(this.getRestUrl(), {
13134                 method: 'PUT',
13135                 success: this.createPutSuccessHandler(this, contentBody, handlers.success),
13136                 error: handlers.error,
13137                 content: contentBody
13138             });
13139 
13140             return this; // Allow cascading
13141         }
13142 
13143     });
13144     
13145     window.finesse = window.finesse || {};
13146     window.finesse.restservices = window.finesse.restservices || {};
13147     window.finesse.restservices.TeamSignOutReasonCodes = TeamSignOutReasonCodes;
13148     
13149     return TeamSignOutReasonCodes;
13150 });
13151 
13152 /**
13153  * JavaScript representation of the Finesse PhoneBook Assignment object.
13154  *
13155  * @requires finesse.clientservices.ClientServices
13156  * @requires Class
13157  * @requires finesse.FinesseBase
13158  * @requires finesse.restservices.RestBase
13159  */
13160 
13161 /**
13162  * The following comment prevents JSLint errors concerning undefined global variables.
13163  * It tells JSLint that these identifiers are defined elsewhere.
13164  */
13165 /*jslint bitwise:true, browser:true, nomen:true, regexp:true, sloppy:true, white:true */
13166 
13167 /** The following comment is to prevent jslint errors about 
13168  * using variables before they are defined.
13169  */
13170 /*global $, jQuery, Handlebars, dojox, dojo, finesse */
13171 
13172 /** @private */
13173 define('restservices/TeamPhoneBook',['restservices/RestBase'], function (RestBase) {
13174     var TeamPhoneBook = RestBase.extend({
13175 
13176         /**
13177          * @class
13178          * JavaScript representation of a PhoneBook object. Also exposes
13179          * methods to operate on the object against the server.
13180          *
13181          * @param {Object} options
13182          *     An object with the following properties:<ul>
13183          *         <li><b>id:</b> The id of the object being constructed</li>
13184          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
13185          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
13186          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
13187          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
13188          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
13189          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
13190          *             <li><b>content:</b> {String} Raw string of response</li>
13191          *             <li><b>object:</b> {Object} Parsed object of response</li>
13192          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
13193          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
13194          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
13195          *             </ul></li>
13196          *         </ul></li>
13197          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
13198          * @constructs
13199          **/
13200         init: function (options) {
13201             this._super(options);
13202         },
13203 
13204         /**
13205          * @private
13206          * Gets the REST class for the current object - this is the PhoneBooks class.
13207          * @returns {Object} The PhoneBooks class.
13208          */
13209         getRestClass: function () {
13210             return TeamPhoneBook;
13211         },
13212 
13213         /**
13214          * @private
13215          * Gets the REST type for the current object - this is a "PhoneBook".
13216          * @returns {String} The PhoneBook string.
13217          */
13218         getRestType: function () {
13219             return "PhoneBook";
13220         },
13221 
13222         /**
13223          * @private
13224          * Override default to indicate that this object doesn't support making
13225          * requests.
13226          */
13227         supportsRequests: false,
13228 
13229         /**
13230          * @private
13231          * Override default to indicate that this object doesn't support subscriptions.
13232          */
13233         supportsSubscriptions: false,
13234 
13235         /**
13236          * Getter for the name.
13237          * @returns {String} The name.
13238          */
13239         getName: function () {
13240             this.isLoaded();
13241             return this.getData().name;
13242         },
13243 
13244         /**
13245          * Getter for the Uri value.
13246          * @returns {String} The Uri.
13247          */
13248         getUri: function () {
13249             this.isLoaded();
13250             return this.getData().uri;
13251         }
13252 
13253     });
13254 
13255     window.finesse = window.finesse || {};
13256     window.finesse.restservices = window.finesse.restservices || {};
13257     window.finesse.restservices.TeamPhoneBook = TeamPhoneBook;
13258     
13259     return TeamPhoneBook;
13260 });
13261 
13262 /**
13263 * JavaScript representation of the Finesse PhoneBook Assignments collection
13264 * object which contains a list of Not Ready Reason Codes objects.
13265  *
13266  * @requires finesse.clientservices.ClientServices
13267  * @requires Class
13268  * @requires finesse.FinesseBase
13269  * @requires finesse.restservices.RestBase
13270  * @requires finesse.restservices.Dialog
13271  * @requires finesse.restservices.RestCollectionBase
13272  */
13273 
13274 /**
13275  * The following comment prevents JSLint errors concerning undefined global variables.
13276  * It tells JSLint that these identifiers are defined elsewhere.
13277  */
13278 /*jslint bitwise:true, browser:true, nomen:true, regexp:true, sloppy:true, white:true */
13279 
13280 /** The following comment is to prevent jslint errors about 
13281  * using variables before they are defined.
13282  */
13283 /*global $, jQuery, Handlebars, dojox, dojo, finesse */
13284 
13285 /** @private */
13286 define('restservices/TeamPhoneBooks',[
13287     'restservices/RestCollectionBase',
13288     'restservices/RestBase',
13289     'restservices/TeamPhoneBook'
13290 ],
13291 function (RestCollectionBase, RestBase, TeamPhoneBook) {
13292     var TeamPhoneBooks = RestCollectionBase.extend({
13293         
13294         /**
13295          * @class
13296          * JavaScript representation of a TeamPhoneBooks collection object. Also exposes
13297          * methods to operate on the object against the server.
13298          *
13299          * @param {Object} options
13300          *     An object with the following properties:<ul>
13301          *         <li><b>id:</b> The id of the object being constructed</li>
13302          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
13303          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
13304          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
13305          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
13306          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
13307          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
13308          *             <li><b>content:</b> {String} Raw string of response</li>
13309          *             <li><b>object:</b> {Object} Parsed object of response</li>
13310          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
13311          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
13312          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
13313          *             </ul></li>
13314          *         </ul></li>
13315          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
13316          * @constructs
13317          **/
13318         init: function (options) {
13319             this._super(options);           
13320         },
13321 
13322         /**
13323          * @private
13324          * Gets the REST class for the current object - this is the TeamPhoneBooks class.
13325          */
13326         getRestClass: function () {
13327             return TeamPhoneBooks;
13328         },
13329 
13330         /**
13331          * @private
13332          * Gets the REST class for the objects that make up the collection. - this
13333          * is the TeamPhoneBooks class.
13334          */
13335         getRestItemClass: function () {
13336             return TeamPhoneBook;
13337         },
13338 
13339         /**
13340          * @private
13341          * Gets the REST type for the current object - this is a "ReasonCodes".
13342          */
13343         getRestType: function () {
13344             return "PhoneBooks";
13345         },
13346         
13347         /**
13348          * Overrides the parent class.  Returns the url for the PhoneBooks resource
13349          */
13350         getRestUrl: function () {
13351             // return ("/finesse/api/" + this.getRestType() + "?category=NOT_READY");
13352             var restObj = this._restObj,
13353             restUrl = "";
13354             //Prepend the base REST object if one was provided.
13355             if (restObj instanceof RestBase) {
13356                 restUrl += restObj.getRestUrl();
13357             }
13358             //Otherwise prepend with the default webapp name.
13359             else {
13360                 restUrl += "/finesse/api";
13361             }
13362             //Append the REST type.
13363             restUrl += "/PhoneBooks";
13364             //Append ID if it is not undefined, null, or empty.
13365             if (this._id) {
13366                 restUrl += "/" + this._id;
13367             }
13368             return restUrl;        
13369         },
13370         
13371         /**
13372          * @private
13373          * Gets the REST type for the objects that make up the collection - this is "ReasonCode".
13374          */
13375         getRestItemType: function () {
13376             return "PhoneBook";
13377         },
13378 
13379         /**
13380          * @private
13381          * Override default to indicates that the collection supports making
13382          * requests.
13383          */
13384         supportsRequests: true,
13385 
13386         /**
13387          * @private
13388          * Override default to indicates that the collection subscribes to its objects.
13389          */
13390         supportsRestItemSubscriptions: false,
13391         
13392         /**
13393          * Retrieve the Not Ready Reason Codes.
13394          *
13395          * @returns {finesse.restservices.TeamPhoneBooks}
13396          *     This TeamPhoneBooks object to allow cascading.
13397          */
13398         get: function () {
13399             // set loaded to false so it will rebuild the collection after the get
13400             /** @private */
13401             this._loaded = false;
13402             // reset collection
13403             /** @private */
13404             this._collection = {};
13405             // perform get
13406             this._synchronize();
13407             return this;
13408         },
13409 
13410         /* We only use PUT and GET on Reason Code team assignments 
13411          */
13412         createPutSuccessHandler: function(contact, contentBody, successHandler){
13413             return function (rsp) {
13414                 // Update internal structure based on response. Here we
13415                 // inject the contentBody from the PUT request into the
13416                 // rsp.object element to mimic a GET as a way to take
13417                 // advantage of the existing _processResponse method.
13418                 rsp.object = contentBody;
13419                 contact._processResponse(rsp);
13420 
13421                 //Remove the injected Contact object before cascading response
13422                 rsp.object = {};
13423                 
13424                 //cascade response back to consumer's response handler
13425                 successHandler(rsp);
13426             };
13427         },
13428 
13429         /**
13430          * Update - This should be all that is needed.
13431          */
13432         update: function (newValues, handlers) {
13433             this.isLoaded();
13434             var contentBody = {}, contentBodyInner = [], i, innerObject;
13435 
13436             contentBody[this.getRestType()] = {
13437             };
13438         
13439             for (i in newValues) {
13440                 if (newValues.hasOwnProperty(i)) {
13441                     innerObject = {};
13442                     innerObject = {
13443                         "uri": newValues[i]
13444                     };
13445                     contentBodyInner.push(innerObject);
13446                 }
13447             }
13448 
13449             contentBody[this.getRestType()] = {
13450                 "PhoneBook" : contentBodyInner
13451             };
13452 
13453             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
13454             handlers = handlers || {};
13455 
13456             this.restRequest(this.getRestUrl(), {
13457                 method: 'PUT',
13458                 success: this.createPutSuccessHandler(this, contentBody, handlers.success),
13459                 error: handlers.error,
13460                 content: contentBody
13461             });
13462 
13463             return this; // Allow cascading
13464         }       
13465         
13466     });
13467         
13468     window.finesse = window.finesse || {};
13469     window.finesse.restservices = window.finesse.restservices || {};
13470     window.finesse.restservices.TeamPhoneBooks = TeamPhoneBooks;
13471     
13472     return TeamPhoneBooks;
13473 });
13474 
13475 /**
13476  * JavaScript representation of the Finesse LayoutConfig object
13477  * @requires ClientServices
13478  * @requires finesse.FinesseBase
13479  * @requires finesse.restservices.RestBase
13480  */
13481 
13482 /** @private */
13483 define('restservices/LayoutConfig',['restservices/RestBase'], function (RestBase) {
13484     /** @private */
13485 	var LayoutConfig = RestBase.extend({
13486 
13487 		/**
13488 		 * @class
13489 		 * JavaScript representation of a LayoutConfig object. Also exposes methods to operate
13490 		 * on the object against the server.
13491 		 *
13492 		 * @param {String} id
13493 		 *     Not required...
13494 		 * @param {Object} callbacks
13495 		 *     An object containing callbacks for instantiation and runtime
13496 		 * @param {Function} callbacks.onLoad(this)
13497 		 *     Callback to invoke upon successful instantiation
13498 		 * @param {Function} callbacks.onLoadError(rsp)
13499 		 *     Callback to invoke on instantiation REST request error
13500 		 *     as passed by finesse.clientservices.ClientServices.ajax()
13501 		 *     {
13502 		 *         status: {Number} The HTTP status code returned
13503 		 *         content: {String} Raw string of response
13504 		 *         object: {Object} Parsed object of response
13505 		 *         error: {Object} Wrapped exception that was caught
13506 		 *         error.errorType: {String} Type of error that was caught
13507 		 *         error.errorMessage: {String} Message associated with error
13508 		 *     }
13509 		 * @param {Function} callbacks.onChange(this)
13510 		 *     Callback to invoke upon successful update
13511 		 * @param {Function} callbacks.onError(rsp)
13512 		 *     Callback to invoke on update error (refresh or event)
13513 		 *     as passed by finesse.clientservices.ClientServices.ajax()
13514 		 *     {
13515 		 *         status: {Number} The HTTP status code returned
13516 		 *         content: {String} Raw string of response
13517 		 *         object: {Object} Parsed object of response
13518 		 *         error: {Object} Wrapped exception that was caught
13519 		 *         error.errorType: {String} Type of error that was caught
13520 		 *         error.errorMessage: {String} Message associated with error
13521 		 *     }
13522 		 *  
13523 	     * @constructs
13524 		 */
13525 		init: function (callbacks) {
13526 			this._super("", callbacks);
13527 			//when post is performed and id is empty
13528 			/*if (id === "") {
13529 				this._loaded = true;
13530 			}*/
13531 	        this._layoutxml = {};
13532 		},
13533 	
13534 		/**
13535 		 * Returns REST class of LayoutConfig object
13536 		 */
13537 		getRestClass: function () {
13538 			return LayoutConfig;
13539 		},
13540 	
13541 		/**
13542 		 * The type of this REST object is LayoutConfig
13543 		 */
13544 		getRestType: function () {
13545 			return "LayoutConfig";
13546 		},
13547 
13548 		/**
13549 		 * Gets the REST URL of this object.
13550 		 * 
13551 		 * If the parent has an id, the id is appended.
13552 		 * On occasions of POST, it will not have an id.
13553 		 */
13554 		getRestUrl: function () {
13555 			var layoutUri = "/finesse/api/" + this.getRestType() + "/default";
13556 			/*if (this._id) {
13557 				layoutUri = layoutUri + "/" + this._id;
13558 			}*/
13559 			return layoutUri;
13560 		},
13561 	
13562 		/**
13563 		 * This API does not support subscription
13564 		 */
13565 		supportsSubscriptions: false,
13566 		
13567 		keepRestResponse: true,
13568 
13569 
13570 		/**
13571 		 * Gets finesselayout.xml retrieved from the API call
13572 		 */
13573 		getLayoutxml: function () {
13574 			this.isLoaded();
13575 			var layoutxml = this.getData().layoutxml;
13576 
13577             // We need to unescape everything that is unallowed in xml so consumers don't have to deal with it (used in tandem with update())
13578             layoutxml = layoutxml.replace(/&/g,"&");
13579 
13580             return layoutxml;
13581 		},
13582 	
13583 		/**
13584 		 * Gets the type of this LayoutConfig object
13585 		 */
13586 		/*
13587 		getType: function () {
13588 			this.isLoaded();
13589 			return this.getData().type;
13590 		},*/
13591 	
13592 		/**
13593 		 * Retrieve the LayoutConfig settings.
13594 		 * If the id is not provided the API call will fail.
13595 		 * @returns {LayoutConfig}
13596 		 *     This LayoutConfig object to allow cascading.
13597 		 */
13598 		get: function () {      
13599 			this._synchronize();
13600 			return this;
13601 		},
13602 
13603 		/**
13604 		 * Closure handle updating of the internal data for the LayoutConfig object
13605 		 * upon a successful update (PUT) request before calling the intended
13606 		 * success handler provided by the consumer
13607 		 * 
13608 		 * @param {Object}
13609 		 *            layoutconfig Reference to this LayoutConfig object
13610 		 * @param {Object}
13611 		 *            LayoutConfig Object that contains the  settings to be
13612 		 *            submitted in the api request
13613 		 * @param {Function}
13614 		 *            successHandler The success handler specified by the consumer
13615 		 *            of this object
13616 		 * @returns {LayoutConfig} This LayoutConfig object to allow cascading
13617 		 */
13618 	
13619 		createPutSuccessHandler: function (layoutconfig, contentBody, successHandler) {
13620 			return function (rsp) {			
13621 				// Update internal structure based on response. Here we
13622 				// inject the contentBody from the PUT request into the
13623 				// rsp.object element to mimic a GET as a way to take
13624 				// advantage of the existing _processResponse method.
13625 				rsp.content = contentBody;
13626 				rsp.object.LayoutConfig = {};
13627 				rsp.object.LayoutConfig.finesseLayout = contentBody;
13628 				layoutconfig._processResponse(rsp);
13629 	
13630 				//Remove the injected layoutConfig object before cascading response
13631 				rsp.object.LayoutConfig = {};
13632 	
13633 				//cascade response back to consumer's response handler
13634 				successHandler(rsp);
13635 			};
13636 		},
13637 	
13638 		/**
13639 		 *  Update LayoutConfig
13640 		 * @param {Object} finesselayout
13641 		 *     The XML for FinesseLayout being stored
13642 		 * 
13643 		 * @param {Object} handlers
13644 		 *     An object containing callback handlers for the request. Optional.
13645 		 * @param {Function} options.success(rsp)
13646 		 *     A callback function to be invoked for a successful request.
13647 		 *     {
13648 		 *         status: {Number} The HTTP status code returned
13649 		 *         content: {String} Raw string of response
13650 		 *         object: {Object} Parsed object of response
13651 		 *     }
13652 		 * @param {Function} options.error(rsp)
13653 		 *     A callback function to be invoked for an unsuccessful request.
13654 		 *     {
13655 		 *         status: {Number} The HTTP status code returned
13656 		 *         content: {String} Raw string of response
13657 		 *         object: {Object} Parsed object of response (HTTP errors)
13658 		 *         error: {Object} Wrapped exception that was caught
13659 		 *         error.errorType: {String} Type of error that was caught
13660 		 *         error.errorMessage: {String} Message associated with error
13661 		 *     }
13662 		 * @returns {finesse.restservices.LayoutConfig}
13663 		 *     This LayoutConfig object to allow cascading
13664 		 */
13665 	
13666 		update: function (layoutxml, handlers) {
13667 			this.isLoaded();
13668 			var contentBody = {};
13669 
13670 			// We need to escape everything that is unallowed in xml so consumers don't have to deal with it (used in tandem with getLayoutxml())
13671 			layoutxml = layoutxml.replace(/&(?!amp;)/g, "&");
13672 
13673 			contentBody[this.getRestType()] = {
13674 				"layoutxml": finesse.utilities.Utilities.translateHTMLEntities(layoutxml, true)
13675 			};
13676 
13677 			// Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
13678 			handlers = handlers || {};
13679 
13680 			this.restRequest(this.getRestUrl(), {
13681 				method: 'PUT',
13682 				success: this.createPutSuccessHandler(this, layoutxml, handlers.success),
13683 				error: handlers.error,
13684 				content: contentBody
13685 			});
13686 
13687 			return this; // Allow cascading
13688 		}
13689 	
13690 		/**
13691 		 *TODO createPostSuccessHandler needs to be debugged to make it working
13692 		 * Closure handle creating new  LayoutConfig object
13693 		 * upon a successful create (POST) request before calling the intended
13694 		 * success handler provided by the consumer
13695 		 * 
13696 		 * @param {Object}
13697 		 *            layoutconfig Reference to this LayoutConfig object
13698 		 * @param {Object}
13699 		 *            LayoutConfig Object that contains the  settings to be
13700 		 *            submitted in the api request
13701 		 * @param {Function}
13702 		 *            successHandler The success handler specified by the consumer
13703 		 *            of this object
13704 		 * @returns {finesse.restservices.LayoutConfig} This LayoutConfig object to allow cascading
13705 		 */
13706 	/*
13707 		createPostSuccessHandler: function (layoutconfig, contentBody, successHandler) {
13708 			return function (rsp) {
13709 	
13710 				rsp.object = contentBody;
13711 				layoutconfig._processResponse(rsp);
13712 	
13713 				//Remove the injected layoutConfig object before cascading response
13714 				rsp.object = {};
13715 	
13716 				//cascade response back to consumer's response handler
13717 				successHandler(rsp);
13718 			};
13719 		}, */
13720 	
13721 		/**
13722 		 * TODO Method needs to be debugged to make POST working
13723 		 *  Add LayoutConfig
13724 		 * @param {Object} finesselayout
13725 		 *     The XML for FinesseLayout being stored
13726 		 * 
13727 		 * @param {Object} handlers
13728 		 *     An object containing callback handlers for the request. Optional.
13729 		 * @param {Function} options.success(rsp)
13730 		 *     A callback function to be invoked for a successful request.
13731 		 *     {
13732 		 *         status: {Number} The HTTP status code returned
13733 		 *         content: {String} Raw string of response
13734 		 *         object: {Object} Parsed object of response
13735 		 *     }
13736 		 * @param {Function} options.error(rsp)
13737 		 *     A callback function to be invoked for an unsuccessful request.
13738 		 *     {
13739 		 *         status: {Number} The HTTP status code returned
13740 		 *         content: {String} Raw string of response
13741 		 *         object: {Object} Parsed object of response (HTTP errors)
13742 		 *         error: {Object} Wrapped exception that was caught
13743 		 *         error.errorType: {String} Type of error that was caught
13744 		 *         error.errorMessage: {String} Message associated with error
13745 		 *     }
13746 		 * @returns {finesse.restservices.LayoutConfig}
13747 		 *     This LayoutConfig object to allow cascading
13748 		 */
13749 	/*
13750 		add: function (layoutxml, handlers) {
13751 			this.isLoaded();
13752 			var contentBody = {};
13753 	
13754 	
13755 			contentBody[this.getRestType()] = {
13756 					"layoutxml": layoutxml,
13757 					"type": "current"
13758 			    };
13759 	
13760 			// Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
13761 			handlers = handlers || {};
13762 	
13763 			this.restRequest(this.getRestUrl(), {
13764 				method: 'POST',
13765 				success: this.createPostSuccessHandler(this, contentBody, handlers.success),
13766 				error: handlers.error,
13767 				content: contentBody
13768 			});
13769 	
13770 			return this; // Allow cascading
13771 		} */
13772 	});
13773 	
13774 	window.finesse = window.finesse || {};
13775     window.finesse.restservices = window.finesse.restservices || {};
13776     window.finesse.restservices.LayoutConfig = LayoutConfig;
13777     
13778 	return LayoutConfig;
13779 	
13780 });
13781 
13782 /**
13783  * JavaScript representation of the Finesse LayoutConfig object for a Team.
13784  *
13785  * @requires finesse.clientservices.ClientServices
13786  * @requires Class
13787  * @requires finesse.FinesseBase
13788  * @requires finesse.restservices.RestBase
13789  * @requires finesse.utilities.Utilities
13790  * @requires finesse.restservices.LayoutConfig
13791  */
13792 
13793 /** The following comment is to prevent jslint errors about 
13794  * using variables before they are defined.
13795  */
13796 /*global Exception */
13797 
13798 /** @private */
13799 define('restservices/TeamLayoutConfig',[
13800     'restservices/RestBase',
13801     'utilities/Utilities',
13802     'restservices/LayoutConfig'
13803 ],
13804 function (RestBase, Utilities, LayoutConfig) {
13805     
13806     var TeamLayoutConfig = RestBase.extend({
13807       // Keep the restresponse so we can parse the layoutxml out of it in getLayoutXML()
13808       keepRestResponse: true,
13809     
13810       /**
13811        * @class
13812        * JavaScript representation of a LayoutConfig object for a Team. Also exposes
13813        * methods to operate on the object against the server.
13814        *
13815        * @param {Object} options
13816        *     An object with the following properties:<ul>
13817        *         <li><b>id:</b> The id of the object being constructed</li>
13818        *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
13819        *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
13820        *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
13821        *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
13822        *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
13823        *             <li><b>status:</b> {Number} The HTTP status code returned</li>
13824        *             <li><b>content:</b> {String} Raw string of response</li>
13825        *             <li><b>object:</b> {Object} Parsed object of response</li>
13826        *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
13827        *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
13828        *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
13829        *             </ul></li>
13830        *         </ul></li>
13831        *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
13832        * @constructs
13833        **/
13834       init: function (options) {
13835           this._super(options);
13836       },
13837     
13838       /**
13839        * @private
13840        * Gets the REST class for the current object - this is the LayoutConfigs class.
13841        * @returns {Object} The LayoutConfigs class.
13842        */
13843       getRestClass: function () {
13844           return TeamLayoutConfig;
13845       },
13846     
13847       /**
13848        * @private
13849        * Gets the REST type for the current object - this is a "LayoutConfig".
13850        * @returns {String} The LayoutConfig string.
13851        */
13852       getRestType: function () {
13853           return "TeamLayoutConfig";
13854       },
13855     
13856       /**
13857        * @private
13858        * Override default to indicate that this object doesn't support making
13859        * requests.
13860        */
13861       supportsRequests: false,
13862     
13863       /**
13864        * @private
13865        * Override default to indicate that this object doesn't support subscriptions.
13866        */
13867       supportsSubscriptions: false,
13868     
13869       /**
13870        * Getter for the category.
13871        * @returns {String} The category.
13872        */
13873       getLayoutXML: function () {
13874           this.isLoaded();
13875           var layoutxml = this.getData().layoutxml;
13876 
13877           // We need to unescape everything that is unallowed in xml so consumers don't have to deal with it (used in tandem with put())
13878           layoutxml = layoutxml.replace(/&/g,"&");
13879 
13880           return layoutxml;
13881       },
13882     
13883       /**
13884        * Getter for the code.
13885        * @returns {String} The code.
13886        */
13887       getUseDefault: function () {
13888           this.isLoaded();
13889           return this.getData().useDefault;
13890       },
13891       
13892       /**
13893        * Retrieve the TeamLayoutConfig.
13894        *
13895        * @returns {finesse.restservices.TeamLayoutConfig}
13896        */
13897       get: function () {
13898           // this._id is needed, but is not used in this object.. we're overriding getRestUrl anyway
13899           this._id = "0";
13900           // set loaded to false so it will rebuild the collection after the get
13901           this._loaded = false;
13902           // reset collection
13903           this._collection = {};
13904           // perform get
13905           this._synchronize();
13906           return this;
13907       },
13908     
13909       createPutSuccessHandler: function(contact, contentBody, successHandler){
13910           return function (rsp) {
13911               // Update internal structure based on response. Here we
13912               // inject the contentBody from the PUT request into the
13913               // rsp.object element to mimic a GET as a way to take
13914               // advantage of the existing _processResponse method.
13915               rsp.object = contentBody;
13916               contact._processResponse(rsp);
13917     
13918               //Remove the injected Contact object before cascading response
13919               rsp.object = {};
13920               
13921               //cascade response back to consumer's response handler
13922               successHandler(rsp);
13923           };
13924       },
13925       
13926       put: function (newValues, handlers) {
13927           // this._id is needed, but is not used in this object.. we're overriding getRestUrl anyway
13928           this._id = "0";
13929           this.isLoaded();
13930 
13931           // We need to escape everything that is unallowed in xml so consumers don't have to deal with it (used in tandem with getLayoutxml())
13932           var layoutxml = newValues.layoutXML.replace(/&(?!amp;)/g, "&"),
13933               contentBody = {};
13934           
13935           contentBody[this.getRestType()] = {
13936               "useDefault": newValues.useDefault,
13937               // The LayoutConfig restservice javascript class only translates ampersands, so we'll do that also
13938               "layoutxml": finesse.utilities.Utilities.translateHTMLEntities(layoutxml, true)
13939           };
13940     
13941           // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
13942           handlers = handlers || {};
13943     
13944           this.restRequest(this.getRestUrl(), {
13945               method: 'PUT',
13946               success: this.createPutSuccessHandler(this, contentBody, handlers.success),
13947               error: handlers.error,
13948               content: contentBody
13949           });
13950     
13951           return this; // Allow cascading
13952       },
13953     
13954       getRestUrl: function(){
13955           // return team's url + /LayoutConfig
13956           // eg: /api/Team/1/LayoutConfig
13957           if(this._restObj === undefined){
13958               throw new Exception("TeamLayoutConfig instances must have a parent team object.");
13959           }
13960           return this._restObj.getRestUrl() + '/LayoutConfig';
13961       }
13962     
13963       });
13964         
13965     window.finesse = window.finesse || {};
13966     window.finesse.restservices = window.finesse.restservices || {};
13967     window.finesse.restservices.TeamLayoutConfig = TeamLayoutConfig;
13968       
13969     return TeamLayoutConfig;
13970 });
13971 
13972 /**
13973  * JavaScript representation of a TeamWorkflow.
13974  *
13975  * @requires finesse.clientservices.ClientServices
13976  * @requires Class
13977  * @requires finesse.FinesseBase
13978  * @requires finesse.restservices.RestBase
13979  */
13980 /** @private */
13981 define('restservices/TeamWorkflow',['restservices/RestBase'], function (RestBase) {
13982 
13983     var TeamWorkflow = RestBase.extend({
13984 
13985         /**
13986          * @class
13987          * JavaScript representation of a TeamWorkflow object. Also exposes
13988          * methods to operate on the object against the server.
13989          *
13990          * @param {Object} options
13991          *     An object with the following properties:<ul>
13992          *         <li><b>id:</b> The id of the object being constructed</li>
13993          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
13994          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
13995          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
13996          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
13997          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
13998          *             <li><b>status:</b> {Number} The HTTP status description returned</li>
13999          *             <li><b>content:</b> {String} Raw string of response</li>
14000          *             <li><b>object:</b> {Object} Parsed object of response</li>
14001          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
14002          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
14003          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
14004          *             </ul></li>
14005          *         </ul></li>
14006          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
14007          * @constructs
14008          **/
14009         init: function (options) {
14010             this._super(options);
14011         },
14012 
14013         /**
14014          * @private
14015          * Gets the REST class for the current object - this is the TeamWorkflow class.
14016          * @returns {Object} The TeamWorkflow class.
14017          */
14018         getRestClass: function () {
14019             return TeamWorkflow;
14020         },
14021 
14022         /**
14023          * @private
14024          * Gets the REST type for the current object - this is a "Workflow".
14025          * @returns {String} The Workflow string.
14026          */
14027         getRestType: function () {
14028             return "Workflow";
14029         },
14030 
14031         /**
14032          * @private
14033          * Override default to indicate that this object doesn't support making
14034          * requests.
14035          */
14036         supportsRequests: false,
14037 
14038         /**
14039          * @private
14040          * Override default to indicate that this object doesn't support subscriptions.
14041          */
14042         supportsSubscriptions: false,
14043 
14044         /**
14045          * Getter for the name.
14046          * @returns {String} The name.
14047          */
14048         getName: function () {
14049             this.isLoaded();
14050             return this.getData().name;
14051         },
14052 
14053         /**
14054          * Getter for the description.
14055          * @returns {String} The description.
14056          */
14057         getDescription: function () {
14058             this.isLoaded();
14059             return this.getData().description;
14060         },
14061 
14062         /**
14063          * Getter for the Uri value.
14064          * @returns {String} The Uri.
14065          */
14066         getUri: function () {
14067             this.isLoaded();
14068             return this.getData().uri;
14069         }
14070 
14071     });
14072     
14073 	window.finesse = window.finesse || {};
14074     window.finesse.restservices = window.finesse.restservices || {};
14075     window.finesse.restservices.TeamWorkflow = TeamWorkflow;
14076 
14077     return TeamWorkflow;
14078 });
14079 
14080 /**
14081 * JavaScript representation of the TeamWorkflows collection
14082 * object which contains a list of TeamWorkflow objects.
14083  *
14084  * @requires finesse.clientservices.ClientServices
14085  * @requires Class
14086  * @requires finesse.FinesseBase
14087  * @requires finesse.restservices.RestBase
14088  * @requires finesse.restservices.Dialog
14089  * @requires finesse.restservices.RestCollectionBase
14090  */
14091 /** @private */
14092 define('restservices/TeamWorkflows',[
14093     'restservices/RestCollectionBase',
14094     'restservices/TeamWorkflow',
14095     'restservices/RestBase'
14096 ],
14097 function (RestCollectionBase, TeamWorkflow, RestBase) {
14098 
14099     var TeamWorkflows = RestCollectionBase.extend({
14100     
14101         /**
14102          * @class
14103          * JavaScript representation of a TeamWorkflows collection object. Also exposes
14104          * methods to operate on the object against the server.
14105          *
14106          * @param {Object} options
14107          *     An object with the following properties:<ul>
14108          *         <li><b>id:</b> The id of the object being constructed</li>
14109          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
14110          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
14111          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
14112          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
14113          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
14114          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
14115          *             <li><b>content:</b> {String} Raw string of response</li>
14116          *             <li><b>object:</b> {Object} Parsed object of response</li>
14117          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
14118          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
14119          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
14120          *             </ul></li>
14121          *         </ul></li>
14122          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
14123          * @constructs
14124          **/
14125         init: function (options) {
14126             this._super(options);
14127         },
14128 
14129         /**
14130          * @private
14131          * Gets the REST class for the current object - this is the TeamWorkflows class.
14132          */
14133         getRestClass: function () {
14134             return TeamWorkflows;
14135         },
14136 
14137         /**
14138          * @private
14139          * Gets the REST class for the objects that make up the collection. - this
14140          * is the TeamWorkflow class.
14141          */
14142         getRestItemClass: function () {
14143             return TeamWorkflow;
14144         },
14145 
14146         /**
14147          * @private
14148          * Gets the REST type for the current object - this is a "Workflows".
14149          */
14150         getRestType: function () {
14151             return "Workflows";
14152         },
14153 
14154         /**
14155          * Overrides the parent class.  Returns the url for the Workflows resource
14156          */
14157         getRestUrl: function () {
14158             var restObj = this._restObj, restUrl = "";
14159 
14160             //Prepend the base REST object if one was provided.
14161             //Otherwise prepend with the default webapp name.
14162             if (restObj instanceof RestBase) {
14163                 restUrl += restObj.getRestUrl();
14164             } else {
14165                 restUrl += "/finesse/api/Team";
14166             }
14167             //Append ID if it is not undefined, null, or empty.
14168             if (this._id) {
14169                 restUrl += "/" + this._id;
14170             }
14171             //Append the REST type.
14172             restUrl += "/Workflows";
14173             
14174             return restUrl;
14175         },
14176 
14177         /**
14178          * @private
14179          * Gets the REST type for the objects that make up the collection - this is "Workflow".
14180          */
14181         getRestItemType: function () {
14182             return "Workflow";
14183         },
14184 
14185         /**
14186          * @private
14187          * Override default to indicates that the collection supports making requests.
14188          */
14189         supportsRequests: true,
14190 
14191         /**
14192          * @private
14193          * Override default to indicates that the collection does not subscribe to its objects.
14194          */
14195         supportsRestItemSubscriptions: false,
14196 
14197         /**
14198          * Retrieve the Sign Out Reason Codes.
14199          *
14200          * @returns {finesse.restservices.TeamWorkflows}
14201          *     This TeamWorkflows object to allow cascading.
14202          */
14203         get: function () {
14204             // set loaded to false so it will rebuild the collection after the get
14205             this._loaded = false;
14206             // reset collection
14207             this._collection = {};
14208             // perform get
14209             this._synchronize();
14210             return this;
14211         },
14212 
14213         /* We only use PUT and GET on Reason Code team assignments
14214          * @param {Object} contact
14215          * @param {Object} contentBody
14216          * @param {Function} successHandler
14217          */
14218         createPutSuccessHandler: function (contact, contentBody, successHandler) {
14219             return function (rsp) {
14220                 // Update internal structure based on response. Here we
14221                 // inject the contentBody from the PUT request into the
14222                 // rsp.object element to mimic a GET as a way to take
14223                 // advantage of the existing _processResponse method.
14224                 rsp.object = contentBody;
14225                 contact._processResponse(rsp);
14226 
14227                 //Remove the injected contentBody object before cascading response
14228                 rsp.object = {};
14229 
14230                 //cascade response back to consumer's response handler
14231                 successHandler(rsp);
14232             };
14233         },
14234 
14235         /**
14236          * Update - This should be all that is needed.
14237          * @param {Object} newValues
14238          * @param {Object} handlers
14239          * @returns {finesse.restservices.TeamWorkflows}
14240          *     This TeamWorkflows object to allow cascading.
14241          */
14242         update: function (newValues, handlers) {
14243             this.isLoaded();
14244             var contentBody = {}, contentBodyInner = [], i, innerObject = {};
14245 
14246             contentBody[this.getRestType()] = {
14247             };
14248 
14249             for (i in newValues) {
14250                 if (newValues.hasOwnProperty(i)) {
14251                     innerObject = {
14252                         "uri": newValues[i]
14253                     };
14254                     contentBodyInner.push(innerObject);
14255                 }
14256             }
14257 
14258             contentBody[this.getRestType()] = {
14259                 "Workflow" : contentBodyInner
14260             };
14261 
14262             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
14263             handlers = handlers || {};
14264 
14265             this.restRequest(this.getRestUrl(), {
14266                 method: 'PUT',
14267                 success: this.createPutSuccessHandler(this, contentBody, handlers.success),
14268                 error: handlers.error,
14269                 content: contentBody
14270             });
14271 
14272             return this; // Allow cascading
14273         }
14274 
14275     });
14276     
14277 	window.finesse = window.finesse || {};
14278     window.finesse.restservices = window.finesse.restservices || {};
14279     window.finesse.restservices.TeamWorkflows = TeamWorkflows;
14280     
14281     return TeamWorkflows;
14282 });
14283 
14284 /**
14285  * JavaScript representation of the Finesse Team REST object.
14286  *
14287  * @requires finesse.clientservices.ClientServices
14288  * @requires Class
14289  * @requires finesse.FinesseBase
14290  * @requires finesse.restservices.RestBase
14291  * @requires finesse.restservices.RestCollectionBase
14292  * @requires finesse.restservices.User
14293  * @requires finesse.restservices.Users
14294  */
14295 
14296 /**
14297  * The following comment prevents JSLint errors concerning undefined global variables.
14298  * It tells JSLint that these identifiers are defined elsewhere.
14299  */
14300 /*jslint bitwise:true, browser:true, nomen:true, regexp:true, sloppy:true, white:true */
14301 
14302 /** The following comment is to prevent jslint errors about 
14303  * using variables before they are defined.
14304  */
14305 /*global $, jQuery, Handlebars, dojox, dojo, finesse */
14306 
14307 /** @private */
14308 define('restservices/Team',[
14309     'restservices/RestBase',
14310     'utilities/Utilities',
14311     'restservices/Users',
14312     'restservices/TeamNotReadyReasonCodes',
14313     'restservices/TeamWrapUpReasons',
14314     'restservices/TeamSignOutReasonCodes',
14315     'restservices/TeamPhoneBooks',
14316     'restservices/TeamLayoutConfig',
14317     'restservices/TeamWorkflows'
14318 ],
14319 function (RestBase, Utilities, Users, TeamNotReadyReasonCodes, TeamWrapUpReasons, TeamSignOutReasonCodes, TeamPhoneBooks, TeamLayoutConfig, TeamWorkflows) {
14320     var Team = RestBase.extend(/** @lends finesse.restservices.Team.prototype */{
14321         
14322         _teamLayoutConfig: null,
14323 
14324         /**
14325          * @class
14326          * A Team is a set of Agent Users, typically supervised by one or more Supervisor Users.
14327          *
14328          * @augments finesse.restservices.RestBase
14329          * @see finesse.restservices.User#getSupervisedTeams
14330          * @see finesse.restservices.Users
14331          * @constructs
14332          */
14333         _fakeConstuctor: function () {
14334             /* This is here to hide the real init constructor from the public docs */
14335         },
14336         
14337         /**
14338          * @private
14339          * @class
14340          * JavaScript representation of a Team object. Also exposes methods to operate
14341          * on the object against the server.
14342          *
14343          * @param {Object} options
14344          *     An object with the following properties:<ul>
14345          *         <li><b>id:</b> The id of the object being constructed</li>
14346          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
14347          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
14348          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
14349          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
14350          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
14351          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
14352          *             <li><b>content:</b> {String} Raw string of response</li>
14353          *             <li><b>object:</b> {Object} Parsed object of response</li>
14354          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
14355          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
14356          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
14357          *             </ul></li>
14358          *         </ul></li>
14359          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
14360          **/
14361         init: function (options) {
14362             this._super(options);
14363         },
14364     
14365         /**
14366          * @private
14367          * Gets the REST class for the current object - this is the Team class.
14368          * @returns {Object} The Team constructor.
14369          */
14370         getRestClass: function () {
14371             return finesse.restesrvices.Team;
14372         },
14373     
14374         /**
14375          * @private
14376          * Gets the REST type for the current object - this is a "Team".
14377          * @returns {String} The Team string.
14378          */
14379         getRestType: function () {
14380             return "Team";
14381         },
14382     
14383         /**
14384          * @private
14385          * Override default to indicate that this object doesn't support making
14386          * requests.
14387          */
14388         supportsSubscriptions: false,
14389     
14390         /**
14391          * Getter for the team id.
14392          * @returns {String} The team id.
14393          */
14394         getId: function () {
14395             this.isLoaded();
14396             return this.getData().id;
14397         },
14398     
14399         /**
14400          * Getter for the team name.
14401          * @returns {String} The team name
14402          */
14403         getName: function () {
14404             this.isLoaded();
14405             return this.getData().name;
14406         },
14407     
14408         /**
14409          * @private
14410          * Getter for the team uri.
14411          * @returns {String} The team uri
14412          */
14413         getUri: function () {
14414             this.isLoaded();
14415             return this.getData().uri;        
14416         },
14417     
14418         /**
14419          * Constructs and returns a collection of Users.
14420          * @param {finesse.interfaces.RestObjectHandlers} [handlers] Object that sets callback handlers.
14421          * @returns {finesse.restservices.Users} Users collection of User objects.
14422          */
14423         getUsers: function (options) {
14424             this.isLoaded();
14425             options = options || {};
14426     
14427             options.parentObj = this;
14428             // We are using getData() instead of getData.Users because the superclass (RestCollectionBase)
14429             // for Users needs the "Users" key to validate the provided payload matches the class type.
14430             options.data = this.getData();
14431     
14432             return new Users(options);
14433         },
14434     
14435         /**
14436          * @private
14437          * Getter for a teamNotReadyReasonCodes collection object that is associated with Team.
14438          * @param callbacks
14439          * @returns {teamNotReadyReasonCodes}
14440          *     A teamNotReadyReasonCodes collection object.
14441          */
14442         getTeamNotReadyReasonCodes: function (callbacks) {
14443             var options = callbacks || {};
14444             options.parentObj = this;
14445             this.isLoaded();
14446     
14447             if (!this._teamNotReadyReasonCodes) {
14448                 this._teamNotReadyReasonCodes = new TeamNotReadyReasonCodes(options);
14449             }
14450     
14451             return this._teamNotReadyReasonCodes;
14452         },
14453     
14454         /**
14455          * @private
14456          * Getter for a teamWrapUpReasons collection object that is associated with Team.
14457          * @param callbacks
14458          * @returns {teamWrapUpReasons}
14459          *     A teamWrapUpReasons collection object.
14460          */
14461         getTeamWrapUpReasons: function (callbacks) {
14462             var options = callbacks || {};
14463             options.parentObj = this;
14464             this.isLoaded();
14465     
14466             if (!this._teamWrapUpReasons) {
14467                 this._teamWrapUpReasons = new TeamWrapUpReasons(options);
14468             }
14469     
14470             return this._teamWrapUpReasons;
14471         },
14472     
14473         /**
14474          * @private
14475          * Getter for a teamSignOutReasonCodes collection object that is associated with Team.
14476          * @param callbacks
14477          * @returns {teamSignOutReasonCodes}
14478          *     A teamSignOutReasonCodes collection object.
14479          */
14480     
14481         getTeamSignOutReasonCodes: function (callbacks) {
14482             var options = callbacks || {};
14483             options.parentObj = this;
14484             this.isLoaded();
14485     
14486             if (!this._teamSignOutReasonCodes) {
14487                 this._teamSignOutReasonCodes = new TeamSignOutReasonCodes(options);
14488             }
14489     
14490             return this._teamSignOutReasonCodes;
14491         },
14492     
14493         /**
14494          * @private
14495          * Getter for a teamPhoneBooks collection object that is associated with Team.
14496          * @param callbacks
14497          * @returns {teamPhoneBooks}
14498          *     A teamPhoneBooks collection object.
14499          */
14500         getTeamPhoneBooks: function (callbacks) {
14501             var options = callbacks || {};
14502             options.parentObj = this;
14503             this.isLoaded();
14504     
14505             if (!this._phonebooks) {
14506                 this._phonebooks = new TeamPhoneBooks(options);
14507             }
14508     
14509             return this._phonebooks;
14510         },
14511     
14512         /**
14513          * @private
14514          * Getter for a teamWorkflows collection object that is associated with Team.
14515          * @param callbacks
14516          * @returns {teamWorkflows}
14517          *     A teamWorkflows collection object.
14518          */
14519         getTeamWorkflows: function (callbacks) {
14520             var options = callbacks || {};
14521             options.parentObj = this;
14522             this.isLoaded();
14523     
14524             if (!this._workflows) {
14525                 this._workflows = new TeamWorkflows(options);
14526             }
14527     
14528             return this._workflows;
14529         },
14530     
14531         /**
14532          * @private
14533          * Getter for a teamLayoutConfig object that is associated with Team.
14534          * @param callbacks
14535          * @returns {teamLayoutConfig}
14536          */
14537         getTeamLayoutConfig: function (callbacks) {
14538             var options = callbacks || {};
14539             options.parentObj = this;
14540             this.isLoaded();
14541     
14542             if (this._teamLayoutConfig === null) {
14543                 this._teamLayoutConfig = new TeamLayoutConfig(options);
14544             }
14545     
14546             return this._teamLayoutConfig;
14547         }
14548     
14549     });
14550     
14551     window.finesse = window.finesse || {};
14552     window.finesse.restservices = window.finesse.restservices || {};
14553     window.finesse.restservices.Team = Team;
14554     
14555     return Team;    
14556 });
14557 
14558 /**
14559  * JavaScript representation of the Finesse Teams collection.
14560  * object which contains a list of Team objects
14561  * @requires finesse.clientservices.ClientServices
14562  * @requires Class
14563  * @requires finesse.FinesseBase
14564  * @requires finesse.restservices.RestBase
14565  * @requires finesse.restservices.RestCollectionBase
14566  */
14567 
14568 /** @private */
14569 define('restservices/Teams',[
14570     'restservices/RestCollectionBase',
14571     'restservices/Team'
14572 ],
14573 function (RestCollectionBase, Team) {
14574     /** @private */
14575     var Teams = RestCollectionBase.extend({
14576 
14577         /**
14578          * @class
14579          * JavaScript representation of a Teams collection object. Also exposes methods to operate
14580          * on the object against the server.
14581          *
14582          * @param {Object} options
14583          *     An object with the following properties:<ul>
14584          *         <li><b>id:</b> The id of the object being constructed</li>
14585          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
14586          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
14587          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
14588          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
14589          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
14590          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
14591          *             <li><b>content:</b> {String} Raw string of response</li>
14592          *             <li><b>object:</b> {Object} Parsed object of response</li>
14593          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
14594          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
14595          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
14596          *             </ul></li>
14597          *         </ul></li>
14598          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
14599          * @constructs
14600          **/
14601         init: function (options) {
14602             this._super(options);
14603         },
14604 
14605         /**
14606          * @private
14607          * Gets the REST class for the current object - this is the Teams class.
14608          * @returns {Object} The Teams constructor.
14609          */
14610         getRestClass: function () {
14611             return Teams;
14612         },
14613 
14614         /**
14615          * @private
14616          * Gets the REST class for the objects that make up the collection. - this
14617          * is the Team class.
14618          */
14619         getRestItemClass: function () {
14620             return Team;
14621         },
14622 
14623         /**
14624          * @private
14625          * Gets the REST type for the current object - this is a "Teams".
14626          * @returns {String} The Teams string.
14627          */
14628         getRestType: function () {
14629             return "Teams";
14630         },
14631         
14632         /**
14633          * @private
14634          * Gets the REST type for the objects that make up the collection - this is "Team".
14635          */
14636         getRestItemType: function () {
14637             return "Team";
14638         },
14639 
14640         /**
14641          * @private
14642          * Override default to indicates that the collection supports making
14643          * requests.
14644          */
14645         supportsRequests: true,
14646 
14647         /**
14648          * @private
14649          * Override default to indicate that this object doesn't support subscriptions.
14650          */
14651         supportsRestItemSubscriptions: false,
14652         
14653         /**
14654          * @private
14655          * Retrieve the Teams.  This call will re-query the server and refresh the collection.
14656          *
14657          * @returns {finesse.restservices.Teams}
14658          *     This Teams object to allow cascading.
14659          */
14660         get: function () {
14661             // set loaded to false so it will rebuild the collection after the get
14662             this._loaded = false;
14663             // reset collection
14664             this._collection = {};
14665             // perform get
14666             this._synchronize();
14667             return this;
14668         }
14669 
14670     });
14671 
14672     window.finesse = window.finesse || {};
14673     window.finesse.restservices = window.finesse.restservices || {};
14674     window.finesse.restservices.Teams = Teams;
14675     
14676     return Teams;
14677 });
14678 
14679 /**
14680  * JavaScript representation of the Finesse SystemInfo object
14681  *
14682  * @requires finesse.clientservices.ClientServices
14683  * @requires Class
14684  * @requires finesse.FinesseBase
14685  * @requires finesse.restservices.RestBase
14686  */
14687 
14688 /** @private */
14689 define('restservices/SystemInfo',['restservices/RestBase'], function (RestBase) {
14690     
14691     var SystemInfo = RestBase.extend(/** @lends finesse.restservices.SystemInfo.prototype */{
14692         /**
14693          * @private
14694          * Returns whether this object supports subscriptions
14695          */
14696         supportsSubscriptions: false,
14697 
14698         doNotRefresh: true,
14699       
14700         /**
14701          * @class
14702          * JavaScript representation of a SystemInfo object.
14703          * 
14704          * @augments finesse.restservices.RestBase
14705          * @see finesse.restservices.SystemInfo.Statuses
14706          * @constructs
14707          */
14708         _fakeConstuctor: function () {
14709             /* This is here to hide the real init constructor from the public docs */
14710         },
14711         
14712          /**
14713          * @private
14714          * JavaScript representation of a SystemInfo object. Also exposes methods to operate
14715          * on the object against the server.
14716          *
14717          * @param {Object} options
14718          *     An object with the following properties:<ul>
14719          *         <li><b>id:</b> The id of the object being constructed</li>
14720          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
14721          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
14722          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
14723          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
14724          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
14725          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
14726          *             <li><b>content:</b> {String} Raw string of response</li>
14727          *             <li><b>object:</b> {Object} Parsed object of response</li>
14728          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
14729          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
14730          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
14731          *             </ul></li>
14732          *         </ul></li>
14733          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
14734          **/
14735         init: function (id, callbacks, restObj)
14736         {
14737             this._super(id, callbacks, restObj);
14738         },
14739 
14740         /**
14741          * @private
14742          * Gets the REST class for the current object - this is the SystemInfo object.
14743          */
14744         getRestClass: function () {
14745             return SystemInfo;
14746         },
14747 
14748         /**
14749          * @private
14750          * Gets the REST type for the current object - this is a "SystemInfo".
14751          */
14752         getRestType: function ()
14753         {
14754             return "SystemInfo";
14755         },
14756         
14757         _validate: function (obj)
14758         {
14759             return true;
14760         },
14761         
14762         /**
14763          * Returns the status of the Finesse system.
14764          *   IN_SERVICE if the Finesse API reports that it is in service,
14765          *   OUT_OF_SERVICE otherwise.
14766          * @returns {finesse.restservices.SystemInfo.Statuses} System Status
14767          */
14768         getStatus: function () {
14769             this.isLoaded();
14770             return this.getData().status;
14771         },
14772         
14773         /**
14774          * Returns the current timestamp from this SystemInfo object.
14775          *   This is used to calculate time drift delta between server and client.
14776          *  @returns {String} Time (GMT): yyyy-MM-dd'T'HH:mm:ss'Z'
14777          */
14778         getCurrentTimestamp: function () {
14779             this.isLoaded();
14780             return this.getData().currentTimestamp;
14781         },
14782         
14783         /**
14784          * Getter for the xmpp domain of the system.
14785          * @returns {String} The xmpp domain corresponding to this SystemInfo object.
14786          */
14787         getXmppDomain: function () {
14788             this.isLoaded();
14789             return this.getData().xmppDomain;
14790         },
14791         
14792         /**
14793          * Getter for the xmpp pubsub domain of the system.
14794          * @returns {String} The xmpp pubsub domain corresponding to this SystemInfo object.
14795          */
14796         getXmppPubSubDomain: function () {
14797             this.isLoaded();
14798             return this.getData().xmppPubSubDomain;
14799         },
14800 
14801         /**
14802          * Getter for the deployment type (UCCE or UCCX).
14803          * @returns {String} "UCCE" or "UCCX"
14804          */ 
14805         getDeploymentType: function () {
14806             this.isLoaded();
14807             return this.getData().deploymentType;
14808         },
14809 
14810         /**
14811          * Returns whether this is a single node deployment or not by checking for the existence of the secondary node in SystemInfo.
14812          * @returns {Boolean} True for single node deployments, false otherwise.
14813          */ 
14814         isSingleNode: function () {
14815             var secondary = this.getData().secondaryNode;
14816             if (secondary && secondary.host) {
14817                 return false;
14818             }
14819             return true;
14820         },
14821 
14822         /**
14823          * Checks all arguments against the primary and secondary hosts (FQDN) and returns the match.
14824          * This is useful for getting the FQDN of the current Finesse server.
14825          * @param {String} ...arguments[]... - any number of arguments to match against
14826          * @returns {String} FQDN (if properly configured) of the matched host of the primary or secondary node, or undefined if no match is found.
14827          */ 
14828         getThisHost: function () {
14829             var i,
14830             primary = this.getData().primaryNode,
14831             secondary = this.getData().secondaryNode;
14832 
14833             for (i = 0; (i < arguments.length); i = i + 1) {
14834                 if (primary && arguments[i] === primary.host) {
14835                     return primary.host;
14836                 } else if (secondary && arguments[i] === secondary.host) {
14837                     return secondary.host;
14838                 }
14839             }
14840         },
14841 
14842         /**
14843          * Checks all arguments against the primary and secondary hosts (FQDN) and returns the other node.
14844          * This is useful for getting the FQDN of the other Finesse server, i.e. for failover purposes.
14845          * @param {String} arguments - any number of arguments to match against
14846          * @returns {String} FQDN (if properly configured) of the alternate node, defaults to primary if no match is found, undefined for single node deployments.
14847          */ 
14848         getAlternateHost: function () {
14849             var i,
14850             isPrimary = false,
14851             primary = this.getData().primaryNode,
14852             secondary = this.getData().secondaryNode,
14853             xmppDomain = this.getData().xmppDomain,
14854             alternateHost;
14855 
14856             if (primary && primary.host) {
14857                     if (xmppDomain === primary.host) {
14858                         isPrimary = true;
14859                     }
14860                 if (secondary && secondary.host) {
14861                     if (isPrimary) {
14862                         return secondary.host;
14863                     }
14864                     return primary.host;
14865                 }
14866             }
14867         }
14868     });
14869     
14870     SystemInfo.Statuses = /** @lends finesse.restservices.SystemInfo.Statuses.prototype */ { 
14871         /** 
14872          * Finesse is in service. 
14873          */
14874         IN_SERVICE: "IN_SERVICE",
14875         /** 
14876          * Finesse is not in service. 
14877          */
14878         OUT_OF_SERVICE: "OUT_OF_SERVICE",
14879         /**
14880          * @class SystemInfo status values.
14881          * @constructs
14882          */
14883         _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
14884 
14885     };
14886     
14887     window.finesse = window.finesse || {};
14888     window.finesse.restservices = window.finesse.restservices || {};
14889     window.finesse.restservices.SystemInfo = SystemInfo;
14890     
14891     return SystemInfo;
14892 });
14893 
14894 /**
14895  * Provides standard way resolve message keys with substitution
14896  *
14897  * @requires finesse.container.I18n or gadgets.Prefs
14898  */
14899 
14900 // Add Utilities to the finesse.utilities namespace
14901 define('utilities/I18n',[], function () {
14902     var I18n = (function () {
14903 
14904         /**
14905          * Shortcut to finesse.container.I18n.getMsg or gadgets.Prefs.getMsg
14906          * @private
14907          */
14908         var _getMsg;
14909 
14910         return {
14911             /**
14912              * Provides a message resolver for this utility singleton.
14913              * @param {Function} getMsg
14914              *     A function that returns a string given a message key.
14915              *     If the key is not found, this function must return 
14916              *     something that tests false (i.e. undefined or "").
14917              */
14918             setGetter : function (getMsg) {
14919                 _getMsg = getMsg;
14920             },
14921 
14922             /**
14923              * Resolves the given message key, also performing substitution.
14924              * This generic utility will use a custom function to resolve the key
14925              * provided by finesse.utilities.I18n.setGetter. Otherwise, it will 
14926              * discover either finesse.container.I18n.getMsg or gadgets.Prefs.getMsg
14927              * upon the first invocation and store that reference for efficiency.
14928              * 
14929              * Since this will construct a new gadgets.Prefs object, it is recommended
14930              * for gadgets to explicitly provide the setter to prevent duplicate
14931              * gadgets.Prefs objects. This does not apply if your gadget does not need
14932              * access to gadgets.Prefs other than getMsg. 
14933              * 
14934              * @param {String} key
14935              *     The key to lookup
14936              * @param {String} arguments
14937              *     Arguments for substitution
14938              * @returns {String/Function}
14939              *     The resolved string if successful, otherwise a function that returns
14940              *     a '???' string that can also be casted into a string.
14941              */
14942             getString : function (key) {
14943                 var prefs, i, retStr, noMsg, getFailed = "";
14944                 if (!_getMsg) {
14945                     if (finesse.container && finesse.container.I18n) {
14946                         _getMsg = finesse.container.I18n.getMsg;
14947                     } else if (gadgets) {
14948                         prefs = new gadgets.Prefs();
14949                         _getMsg = prefs.getMsg;
14950                     }
14951                 }
14952                 
14953                 try {
14954                     retStr = _getMsg(key);
14955                 } catch (e) {
14956                     getFailed = "finesse.utilities.I18n.getString(): invalid _getMsg";
14957                 }
14958                 
14959                 if (retStr) { // Lookup was successful, perform substitution (if any)
14960                     for (i = 1; i < arguments.length; i += 1) {
14961                         retStr = retStr.replace(new RegExp("\\{" + (i - 1) + "\\}", "g"), arguments[i]);
14962                     }
14963                     //in order to fix French text with single quotes in it, we need to replace \' with '
14964                     return retStr.replace(/\\'/g, "'");
14965                 }
14966                 // We want a function because jQuery.html() and jQuery.text() is smart enough to invoke it.
14967                 /** @private */
14968                 noMsg = function () {
14969                     return "???" + key + "???" + getFailed;
14970                 };
14971                 // We overload the toString() of this "function" to allow JavaScript to cast it into a string
14972                 // For example, var myMsg = "something " + finesse.utilities.I18n.getMsg("unresolvable.key");
14973                 /** @private */
14974                 noMsg.toString = function () {
14975                     return "???" + key + "???" + getFailed;
14976                 };
14977                 return noMsg;
14978 
14979             }
14980         };
14981     }());
14982     
14983     window.finesse = window.finesse || {};
14984     window.finesse.utilities = window.finesse.utilities || {};
14985     window.finesse.utilities.I18n = I18n;
14986 
14987     return I18n;
14988 });
14989 
14990 /**
14991  * Logging.js: provides simple logging for clients to use and overrides synchronous native methods: alert(), confirm(), and prompt().
14992  * 
14993  * On Firefox, it will hook into console for logging.  On IE, it will log to the status bar. 
14994  */
14995 // Add Utilities to the finesse.utilities namespace
14996 define('utilities/Logger',[], function () {
14997     var Logger = (function () {
14998         
14999         var
15000         
15001         /** @private **/
15002         debugOn,
15003         
15004         /**
15005          * Pads a single digit number for display purposes (e.g. '4' shows as '04')
15006          * @param num is the number to pad to 2 digits
15007          * @returns a two digit padded string
15008          * @private
15009          */
15010         padTwoDigits = function (num) {        
15011             return (num < 10) ? '0' + num : num;  
15012         },
15013         
15014         /**
15015          * Checks to see if we have a console - this allows us to support Firefox or IE.
15016          * @returns {Boolean} True for Firefox, False for IE
15017          * @private
15018          */
15019         hasConsole = function () {
15020             var retval = false;
15021             try
15022             {
15023                 if (window.console !== undefined) 
15024                 {
15025                     retval = true;
15026                 }
15027             } 
15028             catch (err)
15029             {
15030                 retval = false;
15031             }
15032               
15033             return retval;
15034         },
15035         
15036         /**
15037          * Gets a timestamp.
15038          * @returns {String} is a timestamp in the following format: HH:MM:SS
15039          * @private
15040          */
15041         getTimeStamp = function () {
15042             var date = new Date(), timeStr;
15043             timeStr = padTwoDigits(date.getHours()) + ":" + padTwoDigits(date.getMinutes()) + ":" + padTwoDigits(date.getSeconds());
15044 
15045             return timeStr;
15046         };
15047         
15048         return {
15049             /**
15050              * Enable debug mode. Debug mode may impact performance on the UI.
15051              *
15052              * @param {Boolean} enable
15053              *      True to enable debug logging.
15054              * @private
15055              */
15056             setDebug : function (enable) {
15057                 debugOn = enable;
15058             },
15059             
15060             /**
15061              * Logs a string as DEBUG.
15062              * 
15063              * @param str is the string to log. 
15064              * @private
15065              */
15066             log : function (str) {
15067                 var timeStr = getTimeStamp();
15068                 
15069                 if (debugOn) {
15070                     if (hasConsole())
15071                     {
15072                         window.console.log(timeStr + ": " + "DEBUG" + " - " + str);
15073                     }
15074                 }
15075             },
15076             
15077             /**
15078              * Logs a string as INFO.
15079              * 
15080              * @param str is the string to log. 
15081              * @private
15082              */
15083             info : function (str) {
15084                 var timeStr = getTimeStamp();
15085                 
15086                 if (hasConsole())
15087                 {
15088                     window.console.info(timeStr + ": " + "INFO" + " - " + str);
15089                 }
15090             },
15091             
15092             /**
15093              * Logs a string as WARN.
15094              * 
15095              * @param str is the string to log. 
15096              * @private
15097              */
15098             warn : function (str) {
15099                 var timeStr = getTimeStamp();
15100                 
15101                 if (hasConsole())
15102                 {
15103                     window.console.warn(timeStr + ": " + "WARN" + " - " + str);
15104                 }
15105             },
15106             /**
15107              * Logs a string as ERROR.
15108              * 
15109              * @param str is the string to log. 
15110              * @private
15111              */
15112             error : function (str) {
15113                 var timeStr = getTimeStamp();
15114                 
15115                 if (hasConsole())
15116                 {
15117                     window.console.error(timeStr + ": " + "ERROR" + " - " + str);
15118                 }
15119             }
15120         };
15121     }());
15122     
15123     return Logger;
15124 });
15125 
15126 /**
15127  * Allows gadgets to call the log function to publish client logging messages over the hub.
15128  *
15129  * @requires OpenAjax
15130  */
15131 /** @private */
15132 define('cslogger/ClientLogger',[], function () {
15133 
15134     var ClientLogger = ( function () { /** @lends finesse.cslogger.ClientLogger.prototype */
15135         var _hub, _logTopic, _originId, _sessId, _host,
15136             MONTH = { 0 : "Jan", 1 : "Feb", 2 : "Mar", 3 : "Apr", 4 : "May", 5 : "Jun", 
15137                       6 : "Jul", 7 : "Aug", 8 : "Sep", 9 : "Oct", 10 : "Nov", 11 : "Dec"},
15138  
15139         /**
15140          * Gets timestamp drift stored in sessionStorage
15141          * @returns drift in seconds if it is set in sessionStorage otherwise returns undefined.
15142          * @private
15143         */
15144         getTsDrift = function() {
15145             if (window.sessionStorage.getItem('clientTimestampDrift') !== null) {
15146                 return parseInt(window.sessionStorage.getItem('clientTimestampDrift'), 10);
15147             }
15148             else { 
15149                 return undefined;
15150             }
15151         },
15152          
15153         /**
15154           * Sets timestamp drift in sessionStorage
15155           * @param delta is the timestamp drift between server.and client.
15156           * @private
15157          */
15158         setTsDrift = function(delta) {
15159              window.sessionStorage.setItem('clientTimestampDrift', delta.toString());
15160         },
15161           
15162         /**
15163          * Gets Finesse server timezone offset from GMT in seconds 
15164          * @returns offset in seconds if it is set in sessionStorage otherwise returns undefined.
15165          * @private
15166         */
15167         getServerOffset = function() {
15168             if (window.sessionStorage.getItem('serverTimezoneOffset') !== null) {
15169                 return parseInt(window.sessionStorage.getItem('serverTimezoneOffset'), 10);
15170             }
15171             else { 
15172                 return undefined;
15173             }
15174         },
15175          
15176         /**
15177           * Sets server timezone offset 
15178           * @param sec is the server timezone GMT offset in seconds.
15179           * @private
15180          */
15181         setServerOffset = function(sec) {
15182              window.sessionStorage.setItem('serverTimezoneOffset', sec.toString());
15183         },
15184  
15185         /**
15186          * Checks to see if we have a console.
15187          * @returns Whether the console object exists.
15188          * @private
15189          */
15190         hasConsole = function () {
15191             try {
15192                 if (window.console !== undefined) {
15193                     return true;
15194                 }
15195             } 
15196             catch (err) {
15197               // ignore and return false
15198             }
15199     
15200             return false;
15201         },
15202         
15203         /**
15204          * Gets a short form (6 character) session ID from sessionStorage
15205          * @private
15206         */
15207         getSessId = function() {
15208             if (!_sessId) {
15209                //when _sessId not defined yet, initiate it
15210                if (window.sessionStorage.getItem('enableLocalLog') === 'true') {
15211                   _sessId= " "+window.sessionStorage.getItem('finSessKey');
15212                }
15213                else {
15214                   _sessId=" ";
15215                }
15216             }
15217             return _sessId;
15218          },
15219 
15220         /**
15221          * Pads a single digit number for display purposes (e.g. '4' shows as '04')
15222          * @param num is the number to pad to 2 digits
15223          * @returns a two digit padded string
15224          * @private
15225          */
15226         padTwoDigits = function (num)
15227         {
15228             return (num < 10) ? '0' + num : num;
15229         },
15230         
15231         /**
15232          * Pads a single digit number for display purposes (e.g. '4' shows as '004')
15233          * @param num is the number to pad to 3 digits
15234          * @returns a three digit padded string
15235          * @private
15236          */
15237         padThreeDigits = function (num)
15238         {
15239             if (num < 10)
15240             {
15241               return '00'+num;
15242             }
15243             else if (num < 100)
15244             {
15245               return '0'+num;
15246             }
15247             else  
15248             {
15249                return num;
15250             }
15251         },
15252               
15253         /**
15254          * Compute the "hour"
15255          * 
15256          * @param s is time in seconds
15257          * @returns {String} which is the hour
15258          * @private
15259          */
15260         ho = function (s) {
15261              return ((s/60).toString()).split(".")[0];
15262         },
15263           
15264         /**
15265          * Gets local timezone offset string.
15266          * 
15267          * @param t is the time in seconds
15268          * @param s is the separator character between hours and minutes, e.g. ':'
15269          * @returns {String} is local timezone GMT offset in the following format: [+|-]hh[|:]MM
15270          * @private
15271          */
15272         getGmtOffString = function (min,s) {
15273             var t, sign;
15274             if (min<0) {
15275                t = -min;
15276                sign = "-";
15277             }
15278             else {
15279                t = min;
15280                sign = "+";
15281             }
15282             
15283             if (s===':') {
15284                 return sign+padTwoDigits(ho(t))+s+padTwoDigits(t%60);
15285             }
15286             else {
15287                 return sign+padTwoDigits(ho(t))+padTwoDigits(t%60);
15288             }    
15289         },
15290 
15291         /**
15292          * Gets short form of a month name in English 
15293          * 
15294          * @param monthNum is zero-based month number 
15295          * @returns {String} is short form of month name in English
15296          * @private
15297          */
15298         getMonthShortStr = function (monthNum) {
15299             var result;
15300             try {
15301                 result = MONTH[monthNum];
15302             } 
15303             catch (err) {
15304                 if (hasConsole()) {
15305                     window.console.log("Month must be between 0 and 11");
15306                 }
15307             }
15308             return result;
15309         },
15310           
15311         /**
15312           * Gets a timestamp.
15313           * @param aDate is a javascript Date object
15314           * @returns {String} is a timestamp in the following format: yyyy-mm-ddTHH:MM:ss.SSS [+|-]HH:MM
15315           * @private
15316           */
15317         getDateTimeStamp = function (aDate)
15318         {
15319             var date, off, timeStr;
15320             if (aDate === null) {
15321                 date = new Date();
15322             }
15323             else {
15324                 date = aDate;
15325             }
15326             off = -1*date.getTimezoneOffset();
15327             timeStr = date.getFullYear().toString() + "-" +
15328                       padTwoDigits(date.getMonth()+1) + "-" +
15329                       padTwoDigits (date.getDate()) + "T"+
15330                       padTwoDigits(date.getHours()) + ":" + 
15331                       padTwoDigits(date.getMinutes()) + ":" +
15332                       padTwoDigits(date.getSeconds())+"." + 
15333                       padThreeDigits(date.getMilliseconds()) + " "+
15334                       getGmtOffString(off, ':');
15335     
15336             return timeStr;
15337         },
15338         
15339         /**
15340          * Gets drift-adjusted timestamp.
15341          * @param aTimestamp is a timestamp in milliseconds
15342          * @param drift is a timestamp drift in milliseconds
15343          * @param serverOffset is a timezone GMT offset in minutes
15344          * @returns {String} is a timestamp in the Finesse server log format, e.g. Jan 07 2104 HH:MM:ss.SSS -0500
15345          * @private
15346          */
15347         getDriftedDateTimeStamp = function (aTimestamp, drift, serverOffset)
15348         {
15349            var date, timeStr, localOffset;
15350            if (aTimestamp === null) {
15351                return "--- -- ---- --:--:--.--- -----";
15352            }
15353            else if (drift === undefined || serverOffset === undefined) {
15354                if (hasConsole()) {
15355                    window.console.log("drift or serverOffset must be a number");
15356                }
15357                return "--- -- ---- --:--:--.--- -----";
15358            }
15359            else {
15360                //need to get a zone diff in minutes
15361                localOffset = (new Date()).getTimezoneOffset();
15362                date = new Date(aTimestamp+drift+(localOffset+serverOffset)*60000);
15363                timeStr = getMonthShortStr(date.getMonth()) + " "+
15364                          padTwoDigits (date.getDate())+ " "+
15365                          date.getFullYear().toString() + " "+
15366                          padTwoDigits(date.getHours()) + ":" + 
15367                          padTwoDigits(date.getMinutes()) + ":" +
15368                          padTwoDigits(date.getSeconds())+"." + 
15369                          padThreeDigits(date.getMilliseconds())+" "+
15370                          getGmtOffString(serverOffset, '');
15371                 return timeStr;
15372             }
15373         },
15374     
15375         /**
15376         * Logs a message to a hidden textarea element on the page
15377         *
15378         * @param msg is the string to log.
15379         * @private
15380         */
15381         writeToLogOutput = function (msg) {
15382             var logOutput = document.getElementById("finesseLogOutput");
15383     
15384             if (logOutput === null)
15385             {
15386                 logOutput = document.createElement("textarea");
15387                 logOutput.id = "finesseLogOutput";
15388                 logOutput.style.display = "none";
15389                 document.body.appendChild(logOutput);
15390             }
15391     
15392             if (logOutput.value === "")
15393             {
15394                 logOutput.value = msg;
15395             }
15396             else
15397             {
15398                 logOutput.value = logOutput.value + "\n" + msg;
15399             }
15400         },
15401 
15402         /*
15403          * Logs a message to console 
15404         * @param str is the string to log.         * @private
15405          */
15406         logToConsole = function (str)
15407         {
15408             var now, msg, timeStr, driftedTimeStr, sessKey=getSessId();
15409             now = new Date();
15410             timeStr = getDateTimeStamp(now);
15411             if (getTsDrift() !== undefined) {
15412                 driftedTimeStr = getDriftedDateTimeStamp(now.getTime(), getTsDrift(), getServerOffset());
15413             }
15414             else {
15415                driftedTimeStr = getDriftedDateTimeStamp(null, 0, 0);
15416             }
15417             msg = timeStr + ":"+sessKey+": "+ _host + ": "+driftedTimeStr+ ": " + str;
15418             // Log to console
15419             if (hasConsole()) {
15420                 window.console.log(msg);
15421             }
15422     
15423             //Uncomment to print logs to hidden textarea.
15424             //writeToLogOutput(msg);
15425     
15426             return msg;
15427         };
15428         return {
15429     
15430             /**
15431              * Publishes a Log Message over the hub.
15432              *
15433              * @param {String} message
15434              *     The string to log.
15435              * @example
15436              * _clientLogger.log("This is some important message for MyGadget");
15437              * 
15438              */
15439             log : function (message) {
15440                 if(_hub) {
15441                     _hub.publish(_logTopic, logToConsole(_originId + message));
15442                 }
15443             },
15444             
15445             /**
15446              * @class
15447              * Allows gadgets to call the log function to publish client logging messages over the hub.
15448              * 
15449              * @constructs
15450              */
15451             _fakeConstuctor: function () {
15452                 /* This is here so we can document init() as a method rather than as a constructor. */
15453             },
15454             
15455             /**
15456              * Initiates the client logger with a hub a gadgetId and gadget's config object.
15457              * @param {Object} hub
15458              *      The hub to communicate with.
15459              * @param {String} gadgetId
15460              *      A unique string to identify which gadget is doing the logging.
15461              * @param {Object} config
15462              *      The config object used to get host name for that thirdparty gadget
15463              * @example
15464              * var _clientLogger = finesse.cslogger.ClientLogger;
15465              * _clientLogger.init(gadgets.Hub, "MyGadgetId", config);
15466              * 
15467              */
15468             init: function (hub, gadgetId, config) {
15469                 _hub = hub;
15470                 _logTopic = "finesse.clientLogging." + gadgetId;
15471                 _originId = gadgetId + " : ";
15472                 if ((config === undefined) || (config === "undefined")) 
15473                 {
15474                      _host = ((finesse.container && finesse.container.Config && finesse.container.Config.host)?finesse.container.Config.host : "?.?.?.?");
15475                  } 
15476                 else 
15477                 {
15478                      _host = ((config && config.host)?config.host : "?.?.?.?");
15479                  }
15480             }
15481         };
15482     }());
15483     
15484     window.finesse = window.finesse || {};
15485     window.finesse.cslogger = window.finesse.cslogger || {};
15486     window.finesse.cslogger.ClientLogger = ClientLogger;
15487     
15488     finesse = finesse || {};
15489     /** @namespace Supports writing messages to a central log. */
15490     finesse.cslogger = finesse.cslogger || {};
15491 
15492     return ClientLogger;
15493 });
15494 
15495 /* using variables before they are defined.
15496  */
15497 /*global navigator,unescape,sessionStorage,localStorage,_initSessionList,_initSessionListComplete */
15498 
15499 /**
15500  * Allows each gadget to communicate with the server to send logs.
15501  */
15502 
15503 /**
15504  * @class
15505  * @private
15506  * Allows each product to initialize its method of storage
15507  */
15508 define('cslogger/FinesseLogger',["clientservices/ClientServices", "utilities/Utilities"], function (ClientServices, Utilities) {
15509     
15510     var FinesseLogger = (function () { 
15511 
15512         var
15513 
15514         /**
15515          * Array use to collect ongoing logs in memory
15516          * @private
15517          */
15518         _logArray = [],
15519 
15520         /**
15521          * The final data string sent to the server, =_logArray.join
15522          * @private
15523          */
15524         _logStr = "",
15525 
15526         /**
15527          * Keep track of size of log
15528          * @private
15529          */
15530         _logSize = 0,
15531 
15532         /**
15533          * Flag to keep track show/hide of send log link
15534          * @private
15535          */
15536         _sendLogShown = false,
15537 
15538         /**
15539          * Flag to keep track if local log initialized
15540          * @private
15541          */
15542         _loggingInitialized = false,
15543         
15544 
15545         /**
15546          * local log size limit
15547          * @private
15548          */
15549         _maxLocalStorageSize = 5000000,
15550 
15551         /**
15552          * half local log size limit
15553          * @private
15554          */
15555         _halfMaxLocalStorageSize = 0.5*_maxLocalStorageSize,
15556 
15557         
15558         /**
15559          * threshold for purge 
15560          * @private
15561          */
15562         _purgeStartPercent = 0.75,
15563         
15564         /**
15565          * log item prefix 
15566          * @private
15567          */
15568         _linePrefix = null,
15569         
15570         /**
15571          * locallog session 
15572          * @private
15573          */
15574         _session = null,
15575         
15576         /**
15577          * Flag to keep track show/hide of send log link
15578          * @private
15579          */
15580         _sessionKey = null,
15581         /**
15582          * Log session metadata 
15583          * @private
15584          */
15585         _logInfo = {},
15586         
15587         /**
15588          * Flag to find sessions 
15589          * @private
15590          */
15591         _findSessionsObj = null,
15592 
15593         /**
15594          * Wrap up console.log esp. for IE9 
15595          * @private
15596          */
15597         _myConsoleLog = function (str) {
15598             if (window.console !== undefined) {
15599               window.console.log(str);
15600             }
15601         },
15602         /**
15603          * Initialize the Local Logging
15604          * @private
15605          */
15606         _initLogging = function () {
15607             if (_loggingInitialized) {
15608                 return;
15609             }
15610             //Build a new store
15611             _session = sessionStorage.getItem("finSessKey");
15612             //if the _session is null or empty, skip the init
15613             if (!_session) {
15614               return;
15615             }
15616             _sessionKey = "Fi"+_session;
15617             _linePrefix = _sessionKey + "_";
15618             _logInfo = {};
15619             _logInfo.name = _session;
15620             _logInfo.size = 0;
15621             _logInfo.head = 0;
15622             _logInfo.tail = 0;
15623             _logInfo.startTime = new Date().getTime();
15624             _loggingInitialized = true;
15625             _initSessionList();
15626         },
15627         
15628         /**
15629          * get total data size 
15630          *
15631          * @return {Integer} which is the amount of data stored in local storage.
15632          * @private
15633          */
15634         _getTotalData = function ()
15635         {
15636             var sessName, sessLogInfoStr,sessLogInfoObj, sessionsInfoObj, totalData = 0,
15637             sessionsInfoStr = localStorage.getItem("FinesseSessionsInfo");
15638             if (!sessionsInfoStr) {
15639                  return 0;
15640             }
15641             sessionsInfoObj = JSON.parse(sessionsInfoStr);
15642 
15643             for (sessName in sessionsInfoObj.sessions)
15644             {
15645                 if (sessionsInfoObj.sessions.hasOwnProperty(sessName)) {
15646                     sessLogInfoStr = localStorage.getItem("Fi" + sessName);
15647                     if (!sessLogInfoStr) {
15648                         _myConsoleLog("_getTotalData failed to get log info for "+sessName);
15649                     }
15650                     else {
15651                        sessLogInfoObj = JSON.parse(sessLogInfoStr);
15652                        totalData = totalData + sessLogInfoObj.size;
15653                     }
15654                 }
15655             }
15656 
15657               return totalData;
15658         },
15659         
15660         /**
15661          * Remove lines from tail up until store size decreases to half of max size limit.
15662          *
15663          * @private
15664          */
15665         _purgeCurrentSession = function() {
15666             var curStoreSize, purgedSize=0, line, tailKey, secLogInfoStr, logInfoStr, theLogInfo;
15667             curStoreSize = _getTotalData();
15668             if (curStoreSize < _halfMaxLocalStorageSize) {
15669                return;
15670             }
15671             logInfoStr = localStorage.getItem(_sessionKey);
15672             if (!logInfoStr) {
15673                return;
15674             }
15675             theLogInfo = JSON.parse(logInfoStr);
15676             //_myConsoleLog("Starting _purgeCurrentSession() - currentStoreSize=" + curStoreSize);
15677             while(curStoreSize > _halfMaxLocalStorageSize) {
15678                try {
15679                    tailKey = _sessionKey+"_"+theLogInfo.tail;
15680                    line = localStorage.getItem(tailKey);
15681                    if (line) {
15682                        purgedSize = purgedSize +line.length;
15683                        localStorage.removeItem(tailKey);
15684                        curStoreSize = curStoreSize - line.length;
15685                        theLogInfo.size = theLogInfo.size - line.length;
15686                    }
15687                }
15688                catch (err) {
15689                    _myConsoleLog("purgeCurrentSession encountered err="+err);
15690                }
15691                if (theLogInfo.tail < theLogInfo.head) {
15692                    theLogInfo.tail = theLogInfo.tail  + 1;
15693                }
15694                else {
15695                    break;
15696                }
15697             }
15698             //purge stops here, we need to update session's meta data in storage
15699             secLogInfoStr = localStorage.getItem(_sessionKey);
15700             if (!secLogInfoStr) {
15701                 //somebody cleared the localStorage
15702                 return;
15703             }
15704             
15705             //_myConsoleLog("In _purgeCurrentSession() - after purging current session, currentStoreSize=" + curStoreSize);
15706             //_myConsoleLog("In _purgeCurrentSession() - after purging purgedSize=" + purgedSize);
15707             //_myConsoleLog("In _purgeCurrentSession() - after purging logInfo.size=" + theLogInfo.size);
15708             //_myConsoleLog("In _purgeCurrentSession() - after purging logInfo.tail=" + theLogInfo.tail);
15709             localStorage.setItem(_sessionKey, JSON.stringify(theLogInfo));
15710             _myConsoleLog("Done _purgeCurrentSession() - currentStoreSize=" + curStoreSize);
15711         },
15712        
15713         /**
15714          * Purge a session 
15715          *
15716          * @param sessionName is the name of the session
15717          * @return {Integer} which is the current amount of data purged
15718          * @private
15719          */
15720         _purgeSession = function (sessionName) {
15721               var theLogInfo, logInfoStr, sessionsInfoStr, sessionsInfoObj;
15722               //Get the session logInfo
15723               logInfoStr = localStorage.getItem("Fi" + sessionName);
15724               if (!logInfoStr) {
15725                  _myConsoleLog("_purgeSession failed to get logInfo for "+sessionName);
15726                  return 0;
15727               }
15728               theLogInfo = JSON.parse(logInfoStr);
15729               
15730               //Note: This assumes that we don't crash in the middle of purging
15731               //=> if we do then it should get deleted next time
15732               //Purge tail->head
15733               while (theLogInfo.tail <= theLogInfo.head)
15734               {
15735                   try {
15736                       localStorage.removeItem("Fi" + sessionName + "_" + theLogInfo.tail);
15737                       theLogInfo.tail = theLogInfo.tail + 1;
15738                   }
15739                   catch (err) {
15740                       _myConsoleLog("In _purgeSession err="+err);
15741                       break;
15742                   }
15743               }
15744 
15745               //Remove the entire session
15746               localStorage.removeItem("Fi" + sessionName);
15747 
15748               //Update FinesseSessionsInfo
15749               sessionsInfoStr = localStorage.getItem("FinesseSessionsInfo");
15750               if (!sessionsInfoStr) {
15751                  _myConsoleLog("_purgeSession could not get sessions Info, it was cleared?");
15752                  return 0;
15753               }
15754               sessionsInfoObj = JSON.parse(sessionsInfoStr);
15755               if (sessionsInfoObj.sessions !== null)
15756               {
15757                  delete sessionsInfoObj.sessions[sessionName];
15758               
15759                  sessionsInfoObj.total = sessionsInfoObj.total - 1;
15760                  sessionsInfoObj.lastWrittenBy = _session;
15761                  localStorage.setItem("FinesseSessionsInfo", JSON.stringify(sessionsInfoObj));
15762               }
15763               
15764               return theLogInfo.size;
15765         },
15766         
15767          /**
15768           * purge old sessions
15769           * 
15770           * @param storeSize
15771 	  * @return {Boolean} whether purging reaches its target
15772           * @private
15773          */
15774          _purgeOldSessions = function (storeSize) {
15775              var sessionsInfoStr, purgedSize = 0, sessName, sessions, curStoreSize, activeSession, sessionsInfoObj;
15776              sessionsInfoStr = localStorage.getItem("FinesseSessionsInfo");
15777              if (!sessionsInfoStr) {
15778                 _myConsoleLog("Could not get FinesseSessionsInfo");
15779                 return true;
15780              }
15781              sessionsInfoObj = JSON.parse(sessionsInfoStr);
15782              curStoreSize = _getTotalData();
15783              
15784              activeSession = _session;
15785              sessions = sessionsInfoObj.sessions;
15786              for (sessName in sessions) {
15787                 if (sessions.hasOwnProperty(sessName)) {
15788                     if (sessName !== activeSession) {
15789                         purgedSize = purgedSize + _purgeSession(sessName);
15790                         if ((curStoreSize-purgedSize) < _halfMaxLocalStorageSize) {
15791                             return true;
15792                         }
15793                     }
15794                 }
15795              }
15796             //purge is not done, so return false
15797             return false;
15798          },
15799          
15800        /**
15801         * handle insert error
15802         *
15803         * @param error
15804         * @private
15805         */
15806         _insertLineHandleError = function (error) {
15807             _myConsoleLog(error);
15808         },
15809 
15810         /**
15811          * check storage data size and if need purge
15812          * @private
15813          */
15814         _checkSizeAndPurge = function () {
15815             var purgeIsDone=false, totalSize = _getTotalData();
15816             if (totalSize > 0.75*_maxLocalStorageSize) {
15817                _myConsoleLog("in _checkSizeAndPurge, totalSize ("+totalSize+") exceeds limit");
15818                purgeIsDone = _purgeOldSessions(totalSize);
15819                if (purgeIsDone) {
15820                   _myConsoleLog("in _checkSizeAndPurge after purging old session, purge is done");
15821                }
15822                else {
15823                   //after all old sessions purged, still need purge
15824                   totalSize = _getTotalData();
15825                   if (totalSize > 0.75*_maxLocalStorageSize) {
15826                       _myConsoleLog("in _checkSizeAndPurge after purging old session,still needs purging, now storeSize ("+totalSize+")");
15827                      _purgeCurrentSession();
15828                      _myConsoleLog("in _checkSizeAndPurge done purging current session.");
15829                   }
15830                }
15831             }
15832         },
15833         
15834         /**
15835          * check if the session is already in meta data  
15836          * 
15837          * @param metaData
15838          * @param sessionName
15839          * @return {Boolean} true if session has metaData (false otherwise)
15840          * @private
15841          */
15842         _sessionsInfoContains = function (metaData, sessionName) {
15843            if (metaData && metaData.sessions && metaData.sessions.hasOwnProperty(sessionName)) {
15844               return true;
15845            }
15846            return false;
15847         },
15848         
15849         
15850         /**
15851          * setup sessions in local storage 
15852          * 
15853          * @param logInfo
15854          * @private
15855          */
15856         _getAndSetNumberOfSessions = function (logInfo) {
15857             var numOfSessionsPass1, numOfSessionsPass2, l;
15858             numOfSessionsPass1 = localStorage.getItem("FinesseSessionsInfo");
15859             if (numOfSessionsPass1 === null) {
15860                 //Init first time
15861                 numOfSessionsPass1 = {};
15862                 numOfSessionsPass1.total = 1;
15863                 numOfSessionsPass1.sessions = {};
15864                 numOfSessionsPass1.sessions[logInfo.name] = logInfo.startTime;
15865                 numOfSessionsPass1.lastWrittenBy = logInfo.name;
15866                 localStorage.setItem("FinesseSessionsInfo", JSON.stringify(numOfSessionsPass1));
15867             }
15868             else {
15869                 numOfSessionsPass1 = JSON.parse(numOfSessionsPass1);
15870                 //check if the session is already in the FinesseSessionSInfo
15871                 if (_sessionsInfoContains(numOfSessionsPass1, logInfo.name)) {
15872                     return;
15873                 }             
15874                 //Save numOfSessionsPass1
15875                 numOfSessionsPass1.total = parseInt(numOfSessionsPass1.total, 10) + 1;
15876                 numOfSessionsPass1.sessions[logInfo.name] = logInfo.startTime;
15877                 numOfSessionsPass1.lastWrittenBy = logInfo.name;
15878                 localStorage.setItem("FinesseSessionsInfo", JSON.stringify(numOfSessionsPass1));
15879                 numOfSessionsPass2 = localStorage.getItem("FinesseSessionsInfo");
15880                 if (!numOfSessionsPass2) {
15881                    _myConsoleLog("Could not get FinesseSessionsInfo");
15882                    return;
15883                 }
15884                 numOfSessionsPass2 = JSON.parse(numOfSessionsPass2);
15885                 //in future we need to confirm the numOfSessionsPass2 is the same as numOfSessionsPass1
15886                 ////if (numOfSessionsPass1.lastWrittenBy !== numOfSessionsPass2.lastWrittenBy) {
15887                 ////    _myConsoleLog("Rebuild sessions");
15888                 ////    _sessionTimerId = setTimeout(_initSessionList, 10000);
15889                 ////}
15890                 ////else {
15891                 ////    _sessionTimerId = null;
15892                 ////callback(numOfSessionsPass2.sessions);
15893                 ////}
15894             }
15895             if (!localStorage.getItem(_sessionKey)) {
15896                 localStorage.setItem(_sessionKey, JSON.stringify(_logInfo));
15897             }
15898         },
15899         
15900         
15901         /**
15902          * init session list 
15903          * @private
15904          */
15905         _initSessionList = function () {
15906             _getAndSetNumberOfSessions(_logInfo);
15907         },
15908         
15909        /**
15910         * do the real store of log line
15911         * 
15912         * @param line
15913         * @private
15914         */
15915         _persistLine = function (line) {
15916             var key, logInfoStr;
15917             logInfoStr = localStorage.getItem(_sessionKey);
15918             if (logInfoStr === null) {
15919                return;
15920             }
15921             _logInfo = JSON.parse(logInfoStr);
15922             _logInfo.head = _logInfo.head + 1;
15923             key = _linePrefix + _logInfo.head;
15924             localStorage.setItem(key, line);
15925             //Save the size
15926             _logInfo.size = _logInfo.size + line.length;
15927             if (_logInfo.tail === 0) {
15928                 _logInfo.tail = _logInfo.head;
15929             }
15930         
15931             localStorage.setItem(_sessionKey, JSON.stringify(_logInfo));
15932             _checkSizeAndPurge();
15933         },
15934         
15935         /**
15936          * Insert a line into the localStorage.
15937          *
15938          * @param line line to be inserted 
15939          * @private
15940         */
15941         _insertLine = function (line) {
15942             //_myConsoleLog("_insertLine: [" + line + "]");
15943             //Write the next line to localStorage
15944             try {
15945                //Persist the line 
15946                _persistLine(line);
15947             }
15948             catch (err) {
15949                _myConsoleLog("error in _insertLine(), err="+err);
15950                //_insertLineHandleError(err);
15951             }
15952         },
15953          
15954         
15955         /**
15956          * Clear the local storage
15957          * @private
15958          */
15959         _clearLocalStorage = function() {
15960             localStorage.clear();
15961 
15962         },
15963 
15964         /**
15965          * Collect logs when onCollect called
15966          *
15967          * @param data
15968          * @private
15969          */
15970         _collectMethod = function(data) {
15971           //Size of log should not exceed 1.5MB
15972           var info, maxLength = 1572864;
15973           
15974           //add size buffer equal to the size of info to be added when publish
15975           info = Utilities.getSanitizedUserAgentString() + "
";
15976           info = escape(info);
15977 
15978             //If log was empty previously, fade in buttons
15979             if (!_sendLogShown) {
15980                 //call the fadeInSendLog() in Footer
15981                 finesse.modules.Footer.sendLogAppear();
15982                 _sendLogShown = true;
15983                 _logSize = info.length;
15984             }
15985             
15986             //if local storage logging is enabled, then insert the log into local storage
15987             if (window.sessionStorage.getItem('enableLocalLog')==='true') {
15988                 if (data) {
15989                    if (data.length>0 && data.substring(0,1) === '\n') {
15990                       _insertLine(data.substring(1));
15991                    }
15992                    else {
15993                       _insertLine(data);
15994                    }
15995                 }
15996             }
15997               
15998             //escape all data to get accurate size (shindig will escape when it builds request)
15999             //escape 6 special chars for XML: &<>"'\n
16000             data = data.replace(/&/g, "&").replace(/"/g, """).replace(/'/g, "'").replace(/>/g, ">").replace(/</g, "<").replace(/\n/g, "
");
16001             data = escape(data+"\n");
16002 
16003             if (data.length < maxLength){
16004                 //make room for new data if log is exceeding max length
16005                 while (_logSize + data.length > maxLength) {
16006                     _logSize -= (_logArray.shift()).length;
16007                 }
16008             }
16009 
16010             //Else push the log into memory, increment the log size
16011             _logArray.push(data);
16012 
16013             //inc the size accordingly
16014             _logSize+=data.length;
16015 
16016         };
16017 
16018         return {
16019 
16020             /**
16021              * @private
16022              * Initiate FinesseLogger.
16023              */
16024             init: function () {
16025                 ClientServices.subscribe("finesse.clientLogging.*", _collectMethod);
16026                 _initLogging();
16027             },
16028 
16029             /**
16030              * @private
16031              * Clear all items stored in localStorage.
16032             */
16033             clear : function () {
16034                _clearLocalStorage();
16035             },
16036 
16037             /**
16038              * @private
16039              * Initialize the local storage logging.
16040             */
16041             initLocalLog: function () {
16042                _initLogging();
16043             },
16044 
16045             /**
16046              * @private
16047              * Inserts a line into the localStorage.
16048              * @param line to insert
16049             */
16050             localLog : function (line) {
16051                _insertLine(line);
16052             },
16053 
16054            /**
16055             * @ignore
16056             * Publish logs to server and clear the memory
16057             *
16058             * @param userObj
16059             * @param options
16060             * @param callBack
16061             */
16062             publish: function(userObj, options, callBack) {
16063                 // Avoid null references.
16064                 options = options || {};
16065                 callBack = callBack || {};
16066 
16067                 if (callBack.sending === "function") {
16068                     callBack.sending();
16069                 }
16070 
16071                 //logs the basic version and machine info and escaped new line
16072                 _logStr = Utilities.getSanitizedUserAgentString() + "
";
16073                 
16074                 //join the logs to correct string format
16075                 _logStr += unescape(_logArray.join(""));
16076 
16077                 //turning log string to JSON obj
16078                 var logObj = {
16079                         ClientLog: {
16080                         logData : _logStr //_logStr
16081                     }
16082                 },
16083                 tmpOnAdd = (options.onAdd && typeof options.onAdd === "function")? options.onAdd : function(){};
16084                 /** @private */
16085                 options.onAdd = function(){
16086                     tmpOnAdd();
16087                     _logArray.length = 0; _logSize =0;
16088                     _sendLogShown = false;
16089                     };
16090                 //adding onLoad to the callbacks, this is the subscribe success case for the first time user subscribe to the client log node
16091                 /** @private */
16092                 options.onLoad = function (clientLogObj) {
16093                     clientLogObj.sendLogs(logObj,{
16094                             error: callBack.error
16095                         });
16096                     };
16097 
16098                 userObj.getClientLog(options);
16099             }
16100         };
16101     }());
16102 
16103     window.finesse = window.finesse || {};
16104     window.finesse.cslogger = window.finesse.cslogger || {};
16105     /** @private */
16106     window.finesse.cslogger.FinesseLogger = FinesseLogger;
16107 
16108     return FinesseLogger;
16109 });
16110 
16111 /**
16112  *  Contains a list of topics used for containerservices pubsub.
16113  *
16114  */
16115 
16116 /**
16117  * @class
16118  * Contains a list of topics with some utility functions.
16119  */
16120 /** @private */
16121 define('containerservices/Topics',[], function () {
16122 
16123     var Topics = (function () {
16124 
16125     /**
16126      * The namespace prepended to all Finesse topics.
16127      */
16128     this.namespace = "finesse.containerservices";
16129 
16130     /**
16131      * @private
16132      * Gets the full topic name with the ContainerServices namespace prepended.
16133      * @param {String} topic
16134      *     The topic category.
16135      * @returns {String}
16136      *     The full topic name with prepended namespace.
16137      */
16138     var _getNSTopic = function (topic) {
16139         return this.namespace + "." + topic;
16140     };
16141 
16142 
16143 
16144     /** @scope finesse.containerservices.Topics */
16145     return {
16146         /** 
16147          * @private
16148          * request channel. */
16149         REQUESTS: _getNSTopic("requests"),
16150 
16151         /** 
16152          * @private
16153          * reload gadget channel. */
16154         RELOAD_GADGET: _getNSTopic("reloadGadget"),
16155 
16156         /**
16157          * @private
16158          * Convert a Finesse REST URI to a OpenAjax compatible topic name.
16159          */
16160         getTopic: function (restUri) {
16161             //The topic should not start with '/' else it will get replaced with
16162             //'.' which is invalid.
16163             //Thus, remove '/' if it is at the beginning of the string
16164             if (restUri.indexOf('/') === 0) {
16165                 restUri = restUri.substr(1);
16166             }
16167 
16168             //Replace every instance of "/" with ".". This is done to follow the
16169             //OpenAjaxHub topic name convention.
16170             return restUri.replace(/\//g, ".");
16171         }
16172     };
16173 	}());
16174 	
16175 	window.finesse = window.finesse || {};
16176     window.finesse.containerservices = window.finesse.containerservices || {};
16177     window.finesse.containerservices.Topics = Topics;
16178     
16179     /** @namespace JavaScript class objects and methods to handle gadget container services.*/
16180     finesse.containerservices = finesse.containerservices || {};
16181 
16182     return Topics;
16183  });
16184 
16185 /** The following comment is to prevent jslint errors about 
16186  * using variables before they are defined.
16187  */
16188 /*global finesse*/
16189 
16190 /**
16191  * Per containerservices request, publish to the OpenAjax gadget pubsub infrastructure.
16192  *
16193  * @requires OpenAjax, finesse.containerservices.Topics
16194  */
16195 
16196 /** @private */
16197 define('containerservices/MasterPublisher',[
16198     "utilities/Utilities",
16199     "containerservices/Topics"
16200 ],
16201 function (Utilities, Topics) {
16202 
16203     var MasterPublisher = function () {
16204 
16205     var
16206     
16207     /**
16208      * Reference to the gadget pubsub Hub instance.
16209      * @private
16210      */
16211     _hub = gadgets.Hub,
16212 
16213     /**
16214      * Reference to the Topics class.
16215      * @private
16216      */
16217     _topics = Topics,
16218     
16219     /**
16220      * Reference to conversion utilities class.
16221      * @private
16222      */
16223     _utils = Utilities,
16224     
16225     /**
16226      * References to ClientServices logger methods
16227      * @private
16228      */
16229     _logger = {
16230         log: finesse.clientservices.ClientServices.log
16231     },
16232     
16233    /**
16234      * The types of possible request types supported when listening to the
16235      * requests channel. Each request type could result in different operations.
16236      * @private
16237      */
16238     _REQTYPES = {
16239 		ACTIVETAB: "ActiveTabReq",
16240 		SET_ACTIVETAB: "SetActiveTabReq",
16241         RELOAD_GADGET: "ReloadGadgetReq"
16242     },
16243 
16244     /**
16245      * Handles client requests made to the request topic. The type of the
16246      * request is described in the "type" property within the data payload. Each
16247      * type can result in a different operation.
16248      * @param {String} topic
16249      *     The topic which data was published to.
16250      * @param {Object} data
16251      *     The data containing requests information published by clients.
16252      * @param {String} data.type
16253      *     The type of the request. Supported: "ActiveTabReq", "SetActiveTabReq", "ReloadGadgetReq"
16254      * @param {Object} data.data
16255      *     May contain data relevant for the particular requests.
16256      * @param {String} [data.invokeID]
16257      *     The ID used to identify the request with the response. The invoke ID
16258      *     will be included in the data in the publish to the topic. It is the
16259      *     responsibility of the client to correlate the published data to the
16260      *     request made by using the invoke ID.
16261      * @private
16262      */
16263     _clientRequestHandler = function (topic, data) {
16264     
16265         //Ensure a valid data object with "type" and "data" properties.
16266         if (typeof data === "object" &&
16267                 typeof data.type === "string" &&
16268                 typeof data.data === "object") {
16269 			switch (data.type) {
16270 			case _REQTYPES.ACTIVETAB:
16271                 _hub.publish("finesse.containerservices.activeTab", finesse.container.Tabs.getActiveTab());
16272                 break;
16273             case _REQTYPES.SET_ACTIVETAB:
16274                 if (typeof data.data.id === "string") {
16275                     _logger.log("Handling request to activate tab: " + data.data.id);
16276                     if (!finesse.container.Tabs.activateTab(data.data.id)) {
16277                         _logger.log("No tab found with id: " + data.data.id);
16278                     }
16279                 }
16280                 break;
16281             case _REQTYPES.RELOAD_GADGET:
16282                 _hub.publish("finesse.containerservices.reloadGadget", data.data);
16283                 break;
16284 			default:
16285 				break;
16286 			}
16287         }
16288     };
16289 
16290     (function () {
16291 
16292         //Listen to a request channel to respond to any requests made by other
16293         //clients because the Master may have access to useful information.
16294         _hub.subscribe(_topics.REQUESTS, _clientRequestHandler);
16295     }());
16296 
16297     //BEGIN TEST CODE//
16298     /**
16299      * Test code added to expose private functions that are used by unit test
16300      * framework. This section of code is removed during the build process
16301      * before packaging production code. The [begin|end]TestSection are used
16302      * by the build to identify the section to strip.
16303      * @ignore
16304      */
16305     this.beginTestSection = 0;
16306 
16307     /**
16308      * @ignore
16309      */
16310     this.getTestObject = function () {
16311         //Load mock dependencies.
16312         var _mock = new MockControl();
16313         _hub = _mock.createMock(gadgets.Hub);
16314 
16315         return {
16316             //Expose mock dependencies
16317             mock: _mock,
16318             hub: _hub,
16319 			
16320             //Expose internal private functions
16321             reqtypes: _REQTYPES,
16322             
16323             clientRequestHandler: _clientRequestHandler
16324 
16325         };
16326     };
16327 
16328 
16329     /**
16330      * @ignore
16331      */
16332     this.endTestSection = 0;
16333     //END TEST CODE//
16334 	};
16335 	
16336 	window.finesse = window.finesse || {};
16337     window.finesse.containerservices = window.finesse.containerservices || {};
16338     window.finesse.containerservices.MasterPublisher = MasterPublisher;
16339 	
16340     return MasterPublisher;
16341 });
16342 
16343 /**
16344  * JavaScript representation of the Finesse WorkflowActionEvent object.
16345  *
16346  * @requires finesse.FinesseBase
16347  */
16348 
16349 /** The following comment is to prevent jslint errors about 
16350  * using variables before they are defined.
16351  */
16352 /*global FinesseBase: true, publisher:true, define:true, finesse:true, window:true */
16353 /** @private */
16354 define('containerservices/WorkflowActionEvent', ["FinesseBase"], function (FinesseBase) {
16355     var WorkflowActionEvent = FinesseBase.extend(/** @lends finesse.containerservices.WorkflowActionEvent.prototype */{
16356         /**
16357          * Reference to the WorkflowActionEvent name
16358          * This will be set by setWorkflowActionEvent
16359          * @private
16360          */
16361         _name: null,
16362 
16363         /**
16364          * Reference to the WorkflowActionEvent type
16365          * This will be set by setWorkflowActionEvent
16366          * @private
16367          */
16368         _type: null,
16369 
16370         /**
16371          * Reference to the WorkflowActionEvent handledBy value
16372          * This will be set by setWorkflowActionEvent
16373          * @private
16374          */
16375         _handledBy: null,
16376 
16377         /**
16378          * Reference to the WorkflowActionEvent params array
16379          * This will be set by setWorkflowActionEvent
16380          * @private
16381          */
16382         _params: [],
16383 
16384         /**
16385          * Reference to the WorkflowActionEvent actionVariables array
16386          * This will be set by setWorkflowActionEvent
16387          * @private
16388          */            
16389         _actionVariables: [], 
16390         
16391         /**
16392          * @class
16393          * JavaScript representation of a WorkflowActionEvent object.
16394          * The WorkflowActionEvent object is delivered as the payload of
16395          * a WorkflowAction callback.  This can be subscribed to by using
16396          * {@link finesse.containerservices.ContainerServices#addHandler} with a 
16397          * topic of {@link finesse.containerservices.ContainerServices.Topics#WORKFLOW_ACTION_EVENT}. 
16398          * Gadgets should key on events with a handleBy value of "OTHER".
16399          * 
16400          * @constructs
16401          **/
16402         init: function () {
16403             this._super();
16404         },        
16405 
16406         /**
16407 	     * Validate that the passed in object is a WorkflowActionEvent object
16408 	     * and sets the variables if it is
16409 	     * @param maybeWorkflowActionEvent A possible WorkflowActionEvent object to be evaluated and set if 
16410 	     *                                 it validates successfully.
16411 	     * @returns {Boolean} Whether it is valid or not.
16412          * @private
16413 	     */
16414 	    setWorkflowActionEvent: function(maybeWorkflowActionEvent) {
16415 	        var returnValue;
16416 	
16417 	        if (maybeWorkflowActionEvent.hasOwnProperty("name") === true &&
16418 	                maybeWorkflowActionEvent.hasOwnProperty("type") === true &&
16419                     maybeWorkflowActionEvent.hasOwnProperty("handledBy") === true &&
16420 	                maybeWorkflowActionEvent.hasOwnProperty("params") === true &&
16421 	                maybeWorkflowActionEvent.hasOwnProperty("actionVariables") === true) {
16422 	            this._name = maybeWorkflowActionEvent.name;
16423 	            this._type = maybeWorkflowActionEvent.type;
16424                 this._handledBy = maybeWorkflowActionEvent.handledBy;
16425 	            this._params = maybeWorkflowActionEvent.params;
16426 	            this._actionVariables = maybeWorkflowActionEvent.actionVariables;
16427 	            returnValue = true;
16428 	        } else {
16429 	            returnValue = false;
16430 	        }
16431 	
16432 	        return returnValue;
16433 	    },
16434 	
16435 	    /**
16436 	     * Getter for the WorkflowActionEvent name.
16437 	     * @returns {String} The name of the WorkflowAction.
16438 	     */
16439 	    getName: function () {
16440 	        // escape nulls to empty string
16441 	        return this._name || "";
16442 	    },
16443 	
16444 	    /**
16445 	     * Getter for the WorkflowActionEvent type.
16446 	     * @returns {String} The type of the WorkflowAction (BROWSER_POP, HTTP_REQUEST).
16447 	     */
16448 	    getType: function () {
16449 	        // escape nulls to empty string
16450 	        return this._type || "";
16451 	    },
16452 	
16453         /**
16454          * Getter for the WorkflowActionEvent handledBy value. Gadgets should look for
16455          * events with a handleBy of "OTHER".
16456          * @see finesse.containerservices.WorkflowActionEvent.HandledBy
16457          * @returns {String} The handledBy value of the WorkflowAction that is a value of {@link finesse.containerservices.WorkflowActionEvent.HandledBy}.
16458          */
16459         getHandledBy: function () {
16460             // escape nulls to empty string
16461             return this._handledBy || "";
16462         },
16463 
16464 
16465 	    /**
16466 	     * Getter for the WorkflowActionEvent Params map.
16467 	     * @returns {Object} key = param name, value = Object{name, value, expandedValue}
16468 	     * BROWSER_POP<ul>
16469 	     * <li>windowName : Name of window to pop into, or blank to always open new window.
16470 	     * <li>path : URL to open.</ul>
16471 	     * HTTP_REQUEST<ul>
16472 	     * <li>method : "PUT" or "POST".
16473 	     * <li>location : "FINESSE" or "OTHER".
16474 	     * <li>contentType : MIME type of request body, if applicable, e.g. "text/plain".
16475 	     * <li>path : Request URL.
16476 	     * <li>body : Request content for POST requests.</ul>
16477 	     */
16478 	    getParams: function () {
16479 	        var map = {},
16480 	            params = this._params,
16481 	            i,
16482 	            param;
16483 	
16484 	        if (params === null || params.length === 0) {
16485 	            return map;
16486 	        }
16487 	
16488 	        for (i = 0; i < params.length; i += 1) {
16489 	            param = params[i];
16490 	            // escape nulls to empty string
16491 	            param.name = param.name || "";
16492 	            param.value = param.value || "";
16493 	            param.expandedValue = param.expandedValue || "";
16494 	            map[param.name] = param;
16495 	        }
16496 	
16497 	        return map;
16498 	    },
16499 	    
16500 	    /**
16501 	     * Getter for the WorkflowActionEvent ActionVariables map
16502 	     * @returns {Object} key = action variable name, value = Object{name, type, node, testValue, actualValue}
16503 	     */
16504 	    getActionVariables: function() {
16505 	        var map = {},
16506 	            actionVariables = this._actionVariables,
16507 	            i,
16508 	            actionVariable;
16509 	
16510 	        if (actionVariables === null || actionVariables.length === 0) {
16511 	            return map;
16512 	        }
16513 	
16514 	        for (i = 0; i < actionVariables.length; i += 1) {
16515 	            actionVariable = actionVariables[i];
16516 	            // escape nulls to empty string
16517 	            actionVariable.name = actionVariable.name || "";
16518 	            actionVariable.type = actionVariable.type || "";
16519 	            actionVariable.node = actionVariable.node || "";
16520 	            actionVariable.testValue = actionVariable.testValue || "";
16521 	            actionVariable.actualValue = actionVariable.actualValue || "";
16522 	            map[actionVariable.name] = actionVariable;
16523 	        }
16524 	
16525 	        return map;
16526 	    }
16527     }); 
16528     
16529     
16530     WorkflowActionEvent.HandledBy = /** @lends finesse.containerservices.WorkflowActionEvent.HandledBy.prototype */ {
16531         /**
16532          * This specifies that Finesse will handle this WorkflowActionEvent.  A 3rd Party can do additional processing
16533          * with the action, but first and foremost Finesse will handle this WorkflowAction.
16534          */
16535         FINESSE: "FINESSE",
16536 
16537         /**
16538          * This specifies that a 3rd Party will handle this WorkflowActionEvent.  Finesse's Workflow Engine Executor will 
16539          * ignore this action and expects Gadget Developers to take action.
16540          */
16541         OTHER: "OTHER",
16542         
16543         /**
16544          * @class This is the set of possible HandledBy values used for WorkflowActionEvent from ContainerServices.  This
16545          * is provided from the {@link finesse.containerservices.WorkflowActionEvent#getHandledBy} method.
16546          * @constructs
16547          */
16548         _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
16549     };    
16550     
16551     window.finesse = window.finesse || {};
16552     window.finesse.containerservices = window.finesse.containerservices || {};
16553     window.finesse.containerservices.WorkflowActionEvent = WorkflowActionEvent;
16554     
16555     return WorkflowActionEvent;
16556 });
16557 
16558 /**
16559  * JavaScript representation of the Finesse TimerTickEvent
16560  *
16561  * @requires finesse.FinesseBase
16562  */
16563 
16564 /** The following comment is to prevent jslint errors about 
16565  * using variables before they are defined.
16566  */
16567 /*global FinesseBase: true, publisher:true, define:true, finesse:true, window:true */
16568 /** @private */
16569 define('containerservices/TimerTickEvent',[
16570     "FinesseBase"
16571 ],
16572 function (FinesseBase) {
16573     var TimerTickEvent = FinesseBase.extend(/** @lends finesse.containerservices.TimerTickEvent.prototype */{
16574         /**
16575          * date the TimerTickEvent was queued 
16576          * @private
16577          */
16578         _dateQueued: null,
16579 
16580         /**
16581          * the frequency of the timer tick (in miiliseconds)
16582          * @private
16583          */
16584         _tickFrequency: 1000,
16585 
16586         /**
16587          * @class
16588          * JavaScript representation of a TimerTickEvent object.
16589          * The TimerTickEvent object is delivered as the payload of
16590          * a TimerTickEvent callback.  This can be subscribed to by using
16591          * {@link finesse.containerservices.ContainerServices#addHandler} with a 
16592          * topic of {@link finesse.containerservices.ContainerServices.Topics#TIMER_TICK_EVENT}. 
16593          * 
16594          * @constructs
16595          **/
16596         init: function (tickFrequency, dateQueued) {
16597             this._super();
16598             
16599             this._tickFrequency = tickFrequency;
16600             this._dateQueued = dateQueued;
16601         },
16602 
16603        /**
16604          * Get the "tickFrequency" field
16605          * @param {int} which is the "TickFrequency" field
16606          * @private
16607          */
16608         getTickFrequency: function () {
16609             return this._tickFrequency;
16610         },
16611 
16612         /**
16613          * Getter for the TimerTickEvent "DateQueued" field. 
16614          * @returns {Date} which is a Date object when the TimerTickEvent was queued
16615          */
16616         getDateQueued: function () {
16617             return this._dateQueued;
16618         }
16619 
16620     });
16621     
16622     window.finesse = window.finesse || {};
16623     window.finesse.containerservices = window.finesse.containerservices || {};
16624     window.finesse.containerservices.TimerTickEvent = TimerTickEvent;
16625     
16626     return TimerTickEvent;
16627 });
16628 
16629 /**
16630  * JavaScript representation of the Finesse GadgetViewChangedEvent object.
16631  *
16632  * @requires finesse.FinesseBase
16633  */
16634 
16635 /** The following comment is to prevent jslint errors about 
16636  * using variables before they are defined.
16637  */
16638 /*global FinesseBase: true, publisher:true, define:true, finesse:true, window:true */
16639 /** @private */
16640 define('containerservices/GadgetViewChangedEvent',[
16641     "FinesseBase"
16642 ],
16643 function (FinesseBase) {
16644     var GadgetViewChangedEvent = FinesseBase.extend(/** @lends finesse.containerservices.GadgetViewChangedEvent.prototype */{
16645         /**
16646          * Reference to the gadget id
16647          * @private
16648          */
16649         _gadgetId: null,
16650 
16651         /**
16652          * Reference to the tab id
16653          * @private
16654          */
16655         _tabId: null,
16656 
16657         /**
16658          * Reference to the maxAvailableHeight
16659          * @private
16660          */
16661         _maxAvailableHeight: null,
16662 
16663         /**
16664          * Reference to the view
16665          * E.g. 'default' or 'canvas'
16666          * @private
16667          */
16668         _view: null,
16669         
16670         /**
16671          * @class
16672          * JavaScript representation of a GadgetViewChangedEvent object.
16673          * The GadgetViewChangedEvent object is delivered as the payload of
16674          * a GadgetViewChangedEvent callback.  This can be subscribed to by using
16675          * {@link finesse.containerservices.ContainerServices#addHandler} with a 
16676          * topic of {@link finesse.containerservices.ContainerServices.Topics#GADGET_VIEW_CHANGED_EVENT}. 
16677          * 
16678          * @constructs
16679          **/
16680         init: function (gadgetId, tabId, maxAvailableHeight, view) {
16681             this._super();
16682 
16683             this._gadgetId = gadgetId;
16684             this._tabId = tabId;
16685             this._maxAvailableHeight = maxAvailableHeight;
16686             this._view = view;
16687         },
16688     
16689         /**
16690          * Getter for the gadget id.
16691          * @returns {String} The identifier for the gadget changing view.
16692          */
16693         getGadgetId: function () {
16694             // escape nulls to empty string
16695             return this._gadgetId || "";
16696         },
16697     
16698         /**
16699          * Getter for the maximum available height.
16700          * @returns {String} The maximum available height for the gadget's view.
16701          */
16702         getMaxAvailableHeight: function () {
16703             // escape nulls to empty string
16704             return this._maxAvailableHeight || "";
16705         },
16706 
16707         /**
16708          * Getter for the tab id.
16709          * @returns {String} The identifier for the tab where the gadget changing view resides.
16710          */
16711         getTabId: function () {
16712             // escape nulls to empty string
16713             return this._tabId || "";
16714         },
16715 
16716         /**
16717          * Getter for the view.
16718          * @returns {String} The view type the gadget is changing to.
16719          */
16720         getView: function () {
16721             // escape nulls to empty string
16722             return this._view || "";
16723         }
16724     });
16725     
16726     window.finesse = window.finesse || {};
16727     window.finesse.containerservices = window.finesse.containerservices || {};
16728     window.finesse.containerservices.GadgetViewChangedEvent = GadgetViewChangedEvent;
16729     
16730     return GadgetViewChangedEvent;
16731 });
16732 
16733 /**
16734  * JavaScript representation of the Finesse MaxAvailableHeightChangedEvent object.
16735  *
16736  * @requires finesse.FinesseBase
16737  */
16738 
16739 /** The following comment is to prevent jslint errors about 
16740  * using variables before they are defined.
16741  */
16742 /*global FinesseBase: true, publisher:true, define:true, finesse:true, window:true */
16743 /** @private */
16744 define('containerservices/MaxAvailableHeightChangedEvent',[
16745     "FinesseBase"
16746 ],
16747 function (FinesseBase) {
16748     var MaxAvailableHeightChangedEvent = FinesseBase.extend(/** @lends finesse.containerservices.MaxAvailableHeightChangedEvent.prototype */{
16749 
16750         /**
16751          * Reference to the maxAvailableHeight
16752          * @private
16753          */
16754         _maxAvailableHeight: null,
16755         
16756         /**
16757          * @class
16758          * JavaScript representation of a MaxAvailableHeightChangedEvent object.
16759          * The MaxAvailableHeightChangedEvent object is delivered as the payload of
16760          * a MaxAvailableHeightChangedEvent callback.  This can be subscribed to by using
16761          * {@link finesse.containerservices.ContainerServices#addHandler} with a 
16762          * topic of {@link finesse.containerservices.ContainerServices.Topics#MAX_AVAILABLE_HEIGHT_CHANGED_EVENT}. 
16763          * 
16764          * @constructs
16765          **/
16766         init: function (maxAvailableHeight) {
16767             this._super();
16768 
16769             this._maxAvailableHeight = maxAvailableHeight;
16770         },
16771     
16772         /**
16773          * Getter for the maximum available height.
16774          * @returns {String} The maximum available height for a gadget in canvas view
16775          */
16776         getMaxAvailableHeight: function () {
16777             // escape nulls to empty string
16778             return this._maxAvailableHeight || "";
16779         }
16780     });
16781     
16782     window.finesse = window.finesse || {};
16783     window.finesse.containerservices = window.finesse.containerservices || {};
16784     window.finesse.containerservices.MaxAvailableHeightChangedEvent = MaxAvailableHeightChangedEvent;
16785     
16786     return MaxAvailableHeightChangedEvent;
16787 });
16788 
16789 /**
16790  * Exposes a set of API wrappers that will hide the dirty work of
16791  *     constructing Finesse API requests and consuming Finesse events.
16792  *
16793  * @requires OpenAjax, jQuery 1.5, finesse.utilities.Utilities
16794  */
16795 
16796 /** The following comment is to prevent jslint errors about using variables before they are defined. */
16797 /*global window:true, gadgets:true, publisher:true, define:true, finesse:true, _tabTracker:true, _workflowActionEventTracker:true, _masterReloader:true, frameElement:true, $:true, parent:true, MockControl:true, _getNotifierReference:true, _gadgetViewChanged:true, _maxAvailableHeightChanged:true */
16798 /*jslint nomen: true, unparam: true, sloppy: true, white: true */
16799 /** @private */
16800 define('containerservices/ContainerServices',[
16801     "utilities/Utilities",
16802     "restservices/Notifier",
16803     "containerservices/Topics",
16804     "containerservices/MasterPublisher",
16805     "containerservices/WorkflowActionEvent",
16806     "containerservices/TimerTickEvent",
16807     "containerservices/GadgetViewChangedEvent",
16808     "containerservices/MaxAvailableHeightChangedEvent"
16809 ],
16810 function (Utilities, Notifier, Topics, MasterPublisher, WorkflowActionEvent) {
16811 
16812     var ContainerServices = ( function () { /** @lends finesse.containerservices.ContainerServices.prototype */
16813 
16814     var
16815 
16816     /**
16817      * Shortcut reference to the Utilities singleton
16818      * This will be set by init()
16819      * @private
16820      */
16821     _util,
16822 
16823     /**
16824      * Shortcut reference to the gadget pubsub Hub instance.
16825      * This will be set by init()
16826      * @private
16827      */
16828     _hub,
16829 
16830     /**
16831      * Boolean whether this instance is master or not
16832      * @private
16833      */
16834     _master = false,
16835 
16836     /**
16837      * Whether the Client Services have been initiated yet.
16838      * @private
16839      */
16840     _inited = false,
16841     
16842     /**
16843      * References to ClientServices logger methods
16844      * @private
16845      */
16846     _logger = {
16847         log: finesse.clientservices.ClientServices.log
16848     },
16849     
16850      /**
16851      * Stores the list of subscription IDs for all subscriptions so that it
16852      * could be retrieve for unsubscriptions.
16853      * @private
16854      */
16855     _subscriptionID = {},
16856     
16857     /**
16858      * Reference to the gadget's parent container
16859      * @private
16860      */
16861     _container,
16862 
16863     /**
16864      * Reference to the MasterPublisher
16865      * @private
16866      */
16867     _publisher,
16868     
16869     /**
16870      * Object that will contain the Notifiers
16871      * @private
16872      */
16873     _notifiers = {},
16874 
16875     /**
16876      * Reference to the tabId that is associated with the gadget
16877      * @private
16878      */
16879     _myTab = null,
16880     
16881     /**
16882      * Reference to the visibility of current gadget
16883      * @private
16884      */
16885     _visible = false,
16886     
16887     /**
16888      * Shortcut reference to the Topics class.
16889      * This will be set by init()
16890      * @private
16891      */
16892     _topics,
16893 
16894     /**
16895      * Associates a topic name with the private handler function.
16896      * Adding a new topic requires that you add this association here 
16897      *  in to keep addHandler generic.
16898      * @param {String} topic : Specifies the callback to retrieve
16899      * @return {Function} The callback function associated with the topic param.
16900      * @private
16901      */
16902     _topicCallback = function (topic) {
16903         var callback, notifier;
16904         switch (topic)
16905         {
16906             case finesse.containerservices.ContainerServices.Topics.ACTIVE_TAB:
16907                 callback = _tabTracker;
16908                 break;
16909             case finesse.containerservices.ContainerServices.Topics.WORKFLOW_ACTION_EVENT:
16910                 callback = _workflowActionEventTracker;
16911                 break;
16912             case finesse.containerservices.ContainerServices.Topics.RELOAD_GADGET_EVENT:
16913                 callback = _masterReloader;
16914                 break;
16915             case finesse.containerservices.ContainerServices.Topics.GADGET_VIEW_CHANGED_EVENT:
16916                 callback = _gadgetViewChanged;
16917                 break;
16918             case finesse.containerservices.ContainerServices.Topics.MAX_AVAILABLE_HEIGHT_CHANGED_EVENT:
16919                 callback = _maxAvailableHeightChanged;
16920                 break;
16921             default:
16922                 callback = function (param) {
16923                      var data = null;
16924                      
16925                      notifier = _getNotifierReference(topic);
16926                      
16927                      if (arguments.length === 1) {
16928                         data = param;
16929                      } else {
16930                         data = arguments;
16931                      }
16932                      notifier.notifyListeners(data);
16933                 };
16934         }
16935         return callback;
16936     },
16937 
16938     /**
16939      * Ensure that ClientServices have been inited.
16940      * @private
16941      */
16942     _isInited = function () {
16943         if (!_inited) {
16944             throw new Error("ContainerServices needs to be inited.");
16945         }
16946     },
16947 
16948     /**
16949      * Retrieves a Notifier reference to a particular topic, and creates one if it doesn't exist.
16950      * @param {String} topic : Specifies the notifier to retrieve
16951      * @return {Notifier} The notifier object.
16952      * @private
16953      */
16954     _getNotifierReference = function (topic) {
16955         if (!_notifiers.hasOwnProperty(topic))
16956         {
16957             _notifiers[topic] = new Notifier();
16958         }
16959 
16960         return _notifiers[topic];
16961     },
16962 
16963     /**
16964      * Utility function to make a subscription to a particular topic. Only one
16965      * callback function is registered to a particular topic at any time.
16966      * @param {String} topic
16967      *     The full topic name. The topic name should follow the OpenAjax
16968      *     convention using dot notation (ex: finesse.api.User.1000).
16969      * @param {Function} callback
16970      *     The function that should be invoked with the data when an event
16971      *     is delivered to the specific topic.
16972      * @returns {Boolean}
16973      *     True if the subscription was made successfully and the callback was
16974      *     been registered. False if the subscription already exist, the
16975      *     callback was not overwritten.
16976      * @private
16977      */
16978     _subscribe = function (topic, callback) {
16979         _isInited();
16980 
16981         //Ensure that the same subscription isn't made twice.
16982         if (!_subscriptionID[topic]) {
16983             //Store the subscription ID using the topic name as the key.
16984             _subscriptionID[topic] = _hub.subscribe(topic,
16985                 //Invoke the callback just with the data object.
16986                 function (topic, data) {
16987                     callback(data);
16988                 });
16989             return true;
16990         }
16991         return false;
16992     },
16993 
16994     /**
16995      * Unsubscribe from a particular topic.
16996      * @param {String} topic : The full topic name.
16997      * @private
16998      */
16999     _unsubscribe = function (topic) {
17000         _isInited();
17001 
17002         //Unsubscribe from the topic using the subscription ID recorded when
17003         //the subscription was made, then delete the ID from data structure.
17004         _hub.unsubscribe(_subscriptionID[topic]);
17005         delete _subscriptionID[topic];
17006     },
17007 
17008     /**
17009      * Get my tab id.
17010      * @returns {String} tabid : The tabid of this container/gadget.
17011      * @private
17012      */
17013     _getMyTab = function () {
17014         if (_myTab === null)
17015         {
17016             try {
17017             _myTab = $(frameElement).closest("div.tab-panel").attr("id").replace("panel_", "");
17018             } catch (err) {
17019                 _logger.log("Error accessing current tab: " + err.message);
17020                _myTab = null;
17021             }
17022         }
17023         return _myTab;
17024     },
17025     
17026     /**
17027      * Callback function that is called when an activeTab message is posted to the Hub.
17028      * Notifies listener functions if this tab is the one that was just made active.
17029      * @param {String} tabId : The tabId which was just made visible.
17030      * @private
17031      */
17032     _tabTracker = function(tabId) {
17033         if (tabId === _getMyTab()) {
17034             if(!_visible) {
17035                 _visible = true;
17036                 _notifiers[finesse.containerservices.ContainerServices.Topics.ACTIVE_TAB].notifyListeners(this);
17037             }
17038         } else {
17039             _visible = false;
17040         }
17041     },
17042     
17043     /**
17044      * Make a request to set a particular tab active. This
17045      * method should be called after {@link finesse.containerservices.ContainerServices#addHandler}
17046      * to ensure the gadget gets properly initialized.
17047      * @param {String} tabId
17048      *    The tabId (not the label text) of the tab to make active.  If the id is invalid, no action will occur.
17049      * @private
17050      */
17051     _activateTab = function ( tabId ) {
17052         _logger.log("Sending request to activate tab: " + tabId);
17053         if(_hub){
17054             var data = {
17055                 type: "SetActiveTabReq",
17056                 data: { id: tabId },
17057                 invokeID: (new Date()).getTime()          
17058             };
17059             _hub.publish(_topics.REQUESTS, data);
17060         } else {
17061             throw new Error("Hub is not defined.");
17062         }
17063         
17064     },
17065 
17066     /**
17067      * Callback function that is called when a gadget view changed message is posted to the Hub.
17068      * @private
17069      */
17070     _gadgetViewChanged = function (data) {
17071         if (data) {
17072             var gadgetViewChangedEvent = new finesse.containerservices.GadgetViewChangedEvent(
17073                 data.gadgetId,
17074                 data.tabId,
17075                 data.maxAvailableHeight,
17076                 data.view);
17077 
17078             _notifiers[finesse.containerservices.ContainerServices.Topics.GADGET_VIEW_CHANGED_EVENT].notifyListeners(gadgetViewChangedEvent);
17079         }
17080     },
17081 
17082     /**
17083      * Callback function that is called when a max available height changed message is posted to the Hub.
17084      * @private
17085      */
17086     _maxAvailableHeightChanged = function (data) {
17087         if (data) {
17088             var maxAvailableHeightChangedEvent = new finesse.containerservices.MaxAvailableHeightChangedEvent(
17089                 data.maxAvailableHeight);
17090 
17091             _notifiers[finesse.containerservices.ContainerServices.Topics.MAX_AVAILABLE_HEIGHT_CHANGED_EVENT].notifyListeners(maxAvailableHeightChangedEvent);
17092         }
17093     },
17094 
17095     /**
17096      * Callback function that is called when a workflowActionEvent message is posted to the Hub.
17097      * Notifies listener functions if the posted object can be converted to a proper WorkflowActionEvent object.
17098      * @param {String} workflowActionEvent : The workflowActionEvent that was posted to the Hub
17099      * @private
17100      */
17101     _workflowActionEventTracker = function(workflowActionEvent) {
17102         var vWorkflowActionEvent = new finesse.containerservices.WorkflowActionEvent();
17103                 
17104         if (vWorkflowActionEvent.setWorkflowActionEvent(workflowActionEvent)) {
17105             _notifiers[finesse.containerservices.ContainerServices.Topics.WORKFLOW_ACTION_EVENT].notifyListeners(vWorkflowActionEvent);
17106         }
17107         // else
17108         // {
17109             //?console.log("Error in ContainerServices : _workflowActionEventTracker - could not map published HUB object to WorkflowActionEvent");
17110         // }
17111 
17112     },
17113 
17114     /**
17115      * Callback function that is called when a reloadGadget event message is posted to the Hub.
17116      *
17117      * Grabs the id of the gadget we want to reload from the data and reload it!
17118      *
17119      * @param {String} topic
17120      *      which topic the event came on (unused)
17121      * @param {Object} data
17122      *      the data published with the event
17123      * @private
17124      */
17125     _masterReloader = function (topic, data) {
17126         var gadgetId = data.gadgetId;
17127         if (gadgetId) {
17128             _container.reloadGadget(gadgetId);
17129         }
17130     },
17131     
17132     /**
17133      * Pulls the gadget id from the url parameters
17134      * @return {String} id of the gadget
17135      * @private
17136      */
17137     _findMyGadgetId = function () {
17138         if (gadgets && gadgets.util && gadgets.util.getUrlParameters()) {
17139             return gadgets.util.getUrlParameters().mid;
17140         }
17141     };
17142 
17143     return {
17144         /**
17145          * @class
17146          * This class provides container-level services for gadget developers, exposing container events by
17147          * calling a set of exposed functions. Gadgets can utilize the container dialogs and 
17148          * event handling (add/remove).
17149          * @example
17150          *    containerServices = finesse.containerservices.ContainerServices.init();
17151          *    containerServices.addHandler(
17152          *      finesse.containerservices.ContainerServices.Topics.ACTIVE_TAB, 
17153          *      function() {
17154          *          clientLogs.log("Gadget is now visible");  // log to Finesse logger
17155          *          // automatically adjust the height of the gadget to show the html
17156          *          gadgets.window.adjustHeight();
17157          *      });
17158          *    containerServices.makeActiveTabReq();
17159          *    
17160          * @constructs
17161          */
17162         _fakeConstuctor: function () {
17163             /* This is here so we can document init() as a method rather than as a constructor. */
17164         },
17165         
17166         /**
17167          * Initialize ContainerServices for use in gadget.
17168          * @param {Boolean} [master=false] Do not use this parameter from your gadget.
17169          * @returns ContainerServices instance.
17170          */
17171         init: function (master) {
17172             if (!_inited) {
17173                 _inited = true;
17174                 // Set shortcuts
17175                 _util = Utilities;
17176 
17177                 //init the hub only when it's available
17178                 if(gadgets.Hub) {
17179                     _hub = gadgets.Hub;
17180                 }
17181 
17182                 if(Topics) {
17183                     _topics = Topics;
17184                 }
17185 
17186                 if (master) {
17187                     _master = true;
17188                     _container = finesse.container.Container;
17189                     _publisher = new MasterPublisher();
17190 
17191                     // subscribe for reloading gadget events
17192                     // we only want the master ContainerServices handling these events
17193                     _hub.subscribe(_topics.RELOAD_GADGET, _topicCallback(_topics.RELOAD_GADGET));
17194                 } else {
17195                     _container = parent.finesse.container.Container;
17196                 }
17197             }
17198             
17199             this.makeActiveTabReq();
17200 
17201             //Return the CS object for object chaining.
17202             return this;
17203         },
17204 
17205         /**
17206          * Shows the jQuery UI Dialog with the specified parameters. The following are the
17207          * default parameters: <ul>
17208          *     <li> Title of "Cisco Finesse".</li>
17209          *     <li>Message of "A generic error has occured".</li>
17210          *     <li>The only button, "Ok", closes the dialog.</li>
17211          *     <li>Modal (blocks other dialogs).</li>
17212          *     <li>Not draggable.</li>
17213          *     <li>Fixed size.</li></ul>
17214          * @param {Object} options
17215          *  An object containing additional options for the dialog.
17216          * @param {String/Boolean} options.title
17217          *  Title to use. undefined defaults to "Cisco Finesse". false to hide
17218          * @param {Function} options.close
17219          *  A function to invoke when the dialog is closed.
17220          * @param {String} options.message
17221          *  The message to display in the dialog.
17222          *  Defaults to "A generic error has occurred."
17223          * @param {Boolean} options.isBlocking
17224          *  Flag indicating whether this dialog will block other dialogs from being shown (Modal).
17225          * @returns {jQuery} JQuery wrapped object of the dialog DOM element.
17226          * @see finesse.containerservices.ContainerServices#hideDialog
17227          */
17228         showDialog: function(options) {
17229             if ((_container.showDialog !== undefined) && (_container.showDialog !== this.showDialog)) {
17230                 return _container.showDialog(options);
17231             }
17232         },
17233         
17234         /**
17235          * Hides the jQuery UI Dialog.
17236          * @returns {jQuery} jQuery wrapped object of the dialog DOM element
17237          * @see finesse.containerservices.ContainerServices#showDialog
17238          */
17239         hideDialog: function() {
17240             if ((_container.hideDialog !== undefined) && (_container.hideDialog !== this.hideDialog)) {
17241                 return _container.hideDialog();
17242             }
17243         },
17244 
17245         /**
17246          *  Reloads the current gadget. 
17247          *  For use from within a gadget only.
17248          */
17249         reloadMyGadget: function () {
17250             var topic, gadgetId, data;
17251 
17252             if (!_master) {
17253                 // first unsubscribe this gadget from all topics on the hub
17254                 for (topic in _notifiers) {
17255                     if (_notifiers.hasOwnProperty(topic)) {
17256                         _unsubscribe(topic);
17257                         delete _notifiers[topic];
17258                     }
17259                 }
17260 
17261                 // send an asynch request to the hub to tell the master container
17262                 // services that we want to refresh this gadget
17263                 gadgetId = _findMyGadgetId();
17264                 data = {
17265                     type: "ReloadGadgetReq",
17266                     data: {gadgetId: gadgetId},
17267                     invokeID: (new Date()).getTime()          
17268                 };
17269                 _hub.publish(_topics.REQUESTS, data);
17270             }            
17271         },
17272 
17273         /**
17274          * Updates the url for this gadget and then reload it.
17275          * 
17276          * This allows the gadget to be reloaded from a different location
17277          * than what is uploaded to the current server. For example, this
17278          * would be useful for 3rd party gadgets to implement their own failover
17279          * mechanisms.
17280          *
17281          * For use from within a gadget only.
17282          *
17283          * @param {String} url
17284          *      url from which to reload gadget
17285          */
17286         reloadMyGadgetFromUrl: function (url) {
17287             if (!_master) {
17288                 var gadgetId = _findMyGadgetId();
17289 
17290                 // update the url in the container
17291                 _container.modifyGadgetUrl(gadgetId, url);
17292 
17293                 // reload it
17294                 this.reloadMyGadget();
17295             }
17296         },
17297         
17298         /**
17299          * Adds a handler for one of the supported topics provided by ContainerServices.  The callbacks provided
17300          * will be invoked when that topic is notified.  
17301          * @param {String} topic
17302          *  The Hub topic to which we are listening.
17303          * @param {Function} callback
17304          *  The callback function to invoke.
17305          * @see finesse.containerservices.ContainerServices.Topics
17306          * @see finesse.containerservices.ContainerServices#removeHandler
17307          */
17308         addHandler: function (topic, callback) {
17309             _isInited();
17310             var notifier = null;
17311             
17312             try {    
17313                 // For backwards compatibility...
17314                 if (topic === "tabVisible") {
17315                     if (window.console && typeof window.console.log === "function") {
17316                         window.console.log("WARNING - Using tabVisible as topic.  This is deprecated.  Use finesse.containerservices.ContainerServices.Topics.ACTIVE_TAB now!");
17317                     }
17318                     
17319                     topic = finesse.containerservices.ContainerServices.Topics.ACTIVE_TAB;
17320                 }
17321                 
17322                 // Add the callback to the notifier.
17323                 _util.validateHandler(callback);
17324             
17325                 notifier = _getNotifierReference(topic);
17326             
17327                 notifier.addListener(callback);
17328             
17329                 // Subscribe to the topic. _subscribe ensures that a topic is only subscribed to once,
17330                 // so attempt to subscribe each time a handler is added. This ensures that a topic is subscribed
17331                 // to only when necessary.
17332                 _subscribe(topic, _topicCallback(topic));
17333             
17334             } catch (err) {
17335                 throw new Error("addHandler(): " + err);
17336             }
17337         }, 
17338         
17339         /**
17340          * Removes a previously-added handler for one of the supported topics.
17341          * @param {String} topic
17342          *  The Hub topic from which we are removing the callback.
17343          * @param {Function} callback
17344          *  The name of the callback function to remove.
17345          * @see finesse.containerservices.ContainerServices.Topics
17346          * @see finesse.containerservices.ContainerServices#addHandler
17347          */
17348         removeHandler: function(topic, callback) {
17349             var notifier = null;
17350             
17351             try {
17352                 _util.validateHandler(callback);
17353     
17354                 notifier = _getNotifierReference(topic);
17355     
17356                 notifier.removeListener(callback);
17357             } catch (err) {
17358                 throw new Error("removeHandler(): " + err);
17359             }
17360         },
17361 
17362         /**
17363          * Returns the visibility of current gadget.  Note that this 
17364          * will not be set until after the initialization of the gadget.
17365          * @return {Boolean} The visibility of current gadget.
17366          */
17367         tabVisible: function(){
17368             return _visible;
17369         },
17370         
17371         /**
17372          * Make a request to check the current tab.  The 
17373          * activeTab event will be invoked if on the active tab.  This
17374          * method should be called after {@link finesse.containerservices.ContainerServices#addHandler}
17375          * to ensure the gadget gets properly initialized.
17376          */
17377         makeActiveTabReq : function () {
17378             if(_hub){
17379                 var data = {
17380                     type: "ActiveTabReq",
17381                     data: {},
17382                     invokeID: (new Date()).getTime()          
17383                 };
17384                 _hub.publish(_topics.REQUESTS, data);
17385             } else {
17386                 throw new Error("Hub is not defined.");
17387             }
17388             
17389         },
17390 
17391         /**
17392          * Make a request to set a particular tab active. This
17393          * method should be called after {@link finesse.containerservices.ContainerServices#addHandler}
17394          * to ensure the gadget gets properly initialized.
17395          * @param {String} tabId
17396          *    The tabId (not the label text) of the tab to make active.  If the id is invalid, no action will occur.
17397          */
17398         activateTab : function (tabId) {
17399             _activateTab(tabId);
17400         },
17401         
17402         /**
17403          * Make a request to set this container's tab active. This
17404          * method should be called after {@link finesse.containerservices.ContainerServices#addHandler}
17405          * to ensure the gadget gets properly initialized.
17406          */
17407         activateMyTab : function () {
17408             _activateTab( _getMyTab() );
17409         },
17410         
17411         /**
17412          * Get the tabId of my container/gadget.
17413          * @returns {String} tabid : The tabid of this container/gadget.
17414          */
17415         getMyTabId : function () {
17416             return _getMyTab();
17417         },
17418 
17419         /**
17420          * Gets the id of the gadget.
17421          * @returns {number} the id of the gadget
17422          */
17423         getMyGadgetId : function () {
17424             return _findMyGadgetId();
17425         },
17426 
17427         //BEGIN TEST CODE//
17428         /**
17429          * Test code added to expose private functions that are used by unit test
17430          * framework. This section of code is removed during the build process
17431          * before packaging production code. The [begin|end]TestSection are used
17432          * by the build to identify the section to strip.
17433          * @ignore
17434          */
17435         beginTestSection : 0,
17436 
17437         /**
17438          * @ignore
17439          */
17440         getTestObject: function () {
17441             //Load mock dependencies.
17442             var _mock = new MockControl();
17443             _util = _mock.createMock(Utilities);
17444             _hub = _mock.createMock(gadgets.Hub);
17445             _inited = true;
17446             return {
17447                 //Expose mock dependencies
17448                 mock: _mock,
17449                 hub: _hub,
17450                 util: _util,
17451                 addHandler: this.addHandler,
17452                 removeHandler: this.removeHandler
17453             };
17454         },
17455 
17456         /**
17457          * @ignore
17458          */
17459        endTestSection: 0
17460         //END TEST CODE//
17461     };
17462     }());
17463     
17464     ContainerServices.Topics = /** @lends finesse.containerservices.ContainerServices.Topics.prototype */ {
17465         /**
17466          * Topic for subscribing to be notified when the active tab changes.
17467          * The provided callback will be invoked when the tab that the gadget 
17468          * that subscribes with this becomes active.  To ensure code is called
17469          * when the gadget is already on the active tab use the 
17470          * {@link finesse.containerservices.ContainerServices#makeActiveTabReq}
17471          * method.
17472          */
17473         ACTIVE_TAB: "finesse.containerservices.activeTab",
17474 
17475         /**
17476          * Topic for WorkflowAction events traffic.
17477          * The provided callback will be invoked when a WorkflowAction needs
17478          * to be handled.  The callback will be passed a {@link finesse.containerservices.WorkflowActionEvent}
17479          * that can be used to interrogate the WorkflowAction and determine to use or not.
17480          */
17481         WORKFLOW_ACTION_EVENT: "finesse.containerservices.workflowActionEvent",
17482         
17483         /**
17484          * Topic for Timer Tick event.
17485          * The provided callback will be invoked when this event is fired.
17486          * The callback will be passed a {@link finesse.containerservices.TimerTickEvent}.
17487          */
17488         TIMER_TICK_EVENT : "finesse.containerservices.timerTickEvent",
17489 
17490         /**
17491          * Topic for Reload Gadget events traffic.
17492          * Only the master ContainerServices instance will handle this event.
17493          */
17494         RELOAD_GADGET_EVENT: "finesse.containerservices.reloadGadget",
17495         
17496         /**
17497          * Topic for listening to gadget view changed events.
17498          * The provided callback will be invoked when a gadget changes view.
17499          * The callback will be passed a {@link finesse.containerservices.GadgetViewChangedEvent}.
17500          */
17501         GADGET_VIEW_CHANGED_EVENT: "finesse.containerservices.gadgetViewChangedEvent",
17502 
17503         /**
17504          * Topic for listening to max available height changed events.
17505          * The provided callback will be invoked when the maximum height available to a maximized gadget changes.
17506          * This event is only meant for maximized gadgets and will not be published unless a maximized gadget exists.
17507          * The callback will be passed a {@link finesse.containerservices.MaxAvailableHeightChangedEvent}.
17508          */
17509         MAX_AVAILABLE_HEIGHT_CHANGED_EVENT: "finesse.containerservices.maxAvailableHeightChangedEvent",
17510 
17511         /**
17512          * @class This is the set of Topics used for subscribing for events from ContainerServices.
17513          * Use {@link finesse.containerservices.ContainerServices#addHandler} to subscribe to the topic.
17514          * 
17515          * @constructs
17516          */
17517         _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
17518     };
17519     
17520     window.finesse = window.finesse || {};
17521     window.finesse.containerservices = window.finesse.containerservices || {};
17522     window.finesse.containerservices.ContainerServices = ContainerServices;
17523 
17524     return ContainerServices;
17525  });
17526 
17527 /**
17528  * This "interface" is just a way to easily jsdoc the Object callback handlers.
17529  *
17530  * @requires finesse.clientservices.ClientServices
17531  * @requires Class
17532  */
17533 /** @private */
17534 define('interfaces/RestObjectHandlers',[
17535     "FinesseBase",
17536      "utilities/Utilities",
17537      "restservices/Notifier",
17538      "clientservices/ClientServices",
17539      "clientservices/Topics"
17540 ],
17541 function () {
17542 
17543     var RestObjectHandlers = ( function () { /** @lends finesse.interfaces.RestObjectHandlers.prototype */
17544         
17545         return {
17546 
17547             /**
17548              * @class
17549              * This "interface" defines REST Object callback handlers, passed as an argument to
17550              * Object getter methods in cases where the Object is going to be created.
17551              * 
17552              * @param {Object} [handlers]
17553              *     An object containing callback handlers for instantiation and runtime
17554              *     Callback to invoke upon successful instantiation, passes in REST object.
17555              * @param {Function} [handlers.onLoad(this)]
17556              *     Callback to invoke upon loading the data for the first time.
17557              * @param {Function} [handlers.onChange(this)]
17558              *     Callback to invoke upon successful update object (PUT)
17559              * @param {Function} [handlers.onAdd(this)]
17560              *     Callback to invoke upon successful update to add object (POST)
17561              * @param {Function} [handlers.onDelete(this)]
17562              *     Callback to invoke upon successful update to delete object (DELETE)
17563              * @param {Function} [handlers.onError(rsp)]
17564              *     Callback to invoke on update error (refresh or event)
17565              *     as passed by finesse.restservices.RestBase.restRequest()<br>
17566              *     {<br>
17567              *         status: {Number} The HTTP status code returned<br>
17568              *         content: {String} Raw string of response<br>
17569              *         object: {Object} Parsed object of response<br>
17570              *         error: {Object} Wrapped exception that was caught<br>
17571              *         error.errorType: {String} Type of error that was caught<br>
17572              *         error.errorMessage: {String} Message associated with error<br>
17573              *     }<br>
17574              *     <br>
17575              * Note that RestCollections have two additional callback handlers:<br>
17576              * <br>
17577              * @param {Function} [handlers.onCollectionAdd(this)]: when an object is added to this collection
17578              * @param {Function} [handlers.onCollectionDelete(this)]: when an object is removed from this collection
17579 
17580              * @constructs
17581              */
17582             _fakeConstuctor: function () {
17583                 /* This is here to enable jsdoc to document this as a class. */
17584             }
17585         };
17586     }());
17587 
17588 window.finesse = window.finesse || {};
17589 window.finesse.interfaces = window.finesse.interfaces || {};
17590 window.finesse.interfaces.RestObjectHandlers = RestObjectHandlers;
17591 
17592 return RestObjectHandlers;
17593 
17594 });
17595 
17596 
17597 /**
17598  * This "interface" is just a way to easily jsdoc the REST request handlers.
17599  *
17600  * @requires finesse.clientservices.ClientServices
17601  * @requires Class
17602  */
17603 /** @private */
17604 define('interfaces/RequestHandlers',[
17605     "FinesseBase",
17606      "utilities/Utilities",
17607      "restservices/Notifier",
17608      "clientservices/ClientServices",
17609      "clientservices/Topics"
17610 ],
17611 function () {
17612 
17613     var RequestHandlers = ( function () { /** @lends finesse.interfaces.RequestHandlers.prototype */
17614         
17615         return {
17616 
17617             /**
17618              * @class
17619              * This "interface" defines REST Object callback handlers, passed as an argument to
17620              * Object getter methods in cases where the Object is going to be created.
17621              * 
17622              * @param {Object} handlers
17623              *     An object containing the following (optional) handlers for the request:<ul>
17624              *         <li><b>success(rsp):</b> A callback function for a successful request to be invoked with the following
17625              *         response object as its only parameter:<ul>
17626              *             <li><b>status:</b> {Number} The HTTP status code returned</li>
17627              *             <li><b>content:</b> {String} Raw string of response</li>
17628              *             <li><b>object:</b> {Object} Parsed object of response</li></ul>
17629              *         <li><b>error(rsp):</b> An error callback function for an unsuccessful request to be invoked with the
17630              *         error response object as its only parameter:<ul>
17631              *             <li><b>status:</b> {Number} The HTTP status code returned</li>
17632              *             <li><b>content:</b> {String} Raw string of response</li>
17633              *             <li><b>object:</b> {Object} Parsed object of response (HTTP errors)</li>
17634              *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
17635              *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
17636              *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
17637              *             </ul></li>
17638              *         </ul>
17639 
17640              * @constructs 
17641              */
17642             _fakeConstuctor: function () {
17643                 /* This is here to enable jsdoc to document this as a class. */
17644             }
17645         };
17646     }());
17647 
17648 window.finesse = window.finesse || {};
17649 window.finesse.interfaces = window.finesse.interfaces || {};
17650 window.finesse.interfaces.RequestHandlers = RequestHandlers;
17651 
17652 finesse = finesse || {};
17653 /** @namespace These interfaces are just a convenience for documenting common parameter structures. */
17654 finesse.interfaces = finesse.interfaces || {};
17655 
17656 return RequestHandlers;
17657 
17658 });
17659 
17660 
17661 define('finesse',[
17662     'restservices/Users',
17663     'restservices/Teams',
17664     'restservices/SystemInfo',
17665     'utilities/I18n',
17666     'utilities/Logger',
17667     'utilities/SaxParser',
17668     'cslogger/ClientLogger',
17669     'cslogger/FinesseLogger',
17670     'containerservices/ContainerServices',
17671     'interfaces/RestObjectHandlers',
17672     'interfaces/RequestHandlers'
17673 ],
17674 function () {
17675 
17676     // If being used in a gadget, stuff the auth string into the gadget prefs
17677     if (gadgets.Prefs) {
17678         var _prefs = new gadgets.Prefs(),
17679         authString = finesse.utilities.Utilities.getUserAuthString();
17680         _prefs.set("authorization", authString);
17681     }
17682 
17683     // window.finesse = finesse;
17684     //TODO: For now, we are preserving the dependency tree order to maintain structural integrity
17685     // Once verified, we can clean up all window object references and only assign finesse from here
17686 
17687     return window.finesse;
17688 
17689 });
17690 
17691 require(["finesse"]);
17692 return require('finesse'); }));
17693 
17694 // Prevent other JS files from wiping out window.finesse from the namespace
17695 var finesse = window.finesse;