1 /**
  2  * Cisco Finesse - JavaScript Library
  3  * Version 11.0(1)
  4  * Cisco Systems, Inc.
  5  * http://www.cisco.com/
  6  *
  7  * Portions created or assigned to Cisco Systems, Inc. are
  8  * Copyright (c) 2015 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          * Use this utility to disable the tab stop for a Dojo Firebug iframe within a gadget.
2903          *
2904          * Dojo sometimes adds a hidden iframe for enabling a firebug lite console in older
2905          * browsers. Unfortunately, this adds an additional tab stop that impacts accessibility.
2906          */
2907         disableTabStopForDojoFirebugIframe: function () {
2908             var iframe = $("iframe[src*='loadFirebugConsole']");
2909 
2910             if ((iframe.length) && (iframe.attr("tabIndex") !== "-1")) {
2911                 iframe.attr('tabIndex', '-1'); 
2912             }
2913         },
2914 
2915         /**
2916          * @private
2917          * Measures the given text using the supplied fontFamily and fontSize
2918          * @param  {string} text       text to measure
2919          * @param  {string} fontFamily
2920          * @param  {string} fontSize
2921          * @return {number} pixel width
2922          */
2923         measureText: function (text, fontFamily, fontSize) {
2924             var width,
2925                 element = $("<div></div>").text(text).css({
2926                     "fontSize": fontSize,
2927                     "fontFamily": fontFamily
2928                 }).addClass("offscreen").appendTo(document.body);
2929 
2930             width = element.width();
2931             element.remove();
2932 
2933             return width;
2934         },
2935 
2936         /**
2937          * Adjusts the gadget height. Shindig's gadgets.window.adjustHeight fails when
2938          * needing to resize down in IE. This gets around that by calculating the height
2939          * manually and passing it in.
2940          * @return {undefined}
2941          */
2942         "adjustGadgetHeight": function () {
2943             var bScrollHeight = $("body").height() + 20;
2944             gadgets.window.adjustHeight(bScrollHeight);
2945         },
2946 
2947         /**
2948         * Private helper method for converting a javascript object to xml, where the values of the elements are
2949         * appropriately escaped for XML.
2950         * This is a simple implementation that does not implement cdata or attributes. It is also 'unformatted' in that
2951         * there is no whitespace between elements.
2952         * @param object The javascript object to convert to XML.
2953         * @returns The XML string.
2954         * @private
2955         */
2956         _json2xmlWithEscape: function(object) {
2957             var that = this,
2958                 xml = "",
2959                 m,
2960                 /** @private **/
2961                 toXmlHelper = function(value, name) {
2962                 var xml = "",
2963                     i,
2964                     m;
2965                 if (value instanceof Array) {
2966                     for (i = 0; i < value.length; ++i) {
2967                         xml += toXmlHelper(value[i], name);
2968                     }
2969                 }
2970                 else if (typeof value === "object") {
2971                     xml += "<" + name + ">";
2972                     for (m in value) {
2973                         if (value.hasOwnProperty(m)) {
2974                            xml += toXmlHelper(value[m], m);
2975                         }
2976                     }
2977                     xml += "</" + name + ">";
2978                 }
2979                 else {
2980                     // is a leaf node
2981                     xml += "<" + name + ">" + that.translateHTMLEntities(value.toString(), true) +
2982                         "</" + name + ">";
2983                 }
2984                 return xml;
2985             };
2986             for (m in object) {
2987                 if (object.hasOwnProperty(m)) {
2988                     xml += toXmlHelper(object[m], m);
2989                 }
2990             }
2991             return xml;
2992         },
2993 
2994         /**
2995          * Private method for returning a sanitized version of the user agent string.
2996          * @returns the user agent string, but sanitized!
2997          * @private
2998          */
2999         getSanitizedUserAgentString: function () {
3000             return this.translateXMLEntities(navigator.userAgent, true);
3001         },
3002 
3003         /**
3004          * Use JQuery's implementation of Promises (Deferred) to execute code when 
3005          * multiple async processes have finished. An example use:
3006          *
3007          * var asyncProcess1 = $.Deferred(),
3008          *     asyncProcess2 = $.Deferred();
3009          *     
3010          * finesse.utilities.Utilities.whenAllDone(asyncProcess1, asyncProcess2) // WHEN both asyncProcess1 and asyncProcess2 are resolved or rejected ...
3011          *     .then(
3012          *         // First function passed to then() is called when all async processes are complete, regardless of errors
3013          *         function () {
3014          *             console.log("all processes completed");
3015          *         },
3016          *         // Second function passed to then() is called if any async processed threw an exception
3017          *         function (failures) { // Array of failure messages
3018          *             console.log("Number of failed async processes: " + failures.length);
3019          *         });
3020          *
3021          * Found at:
3022          * http://stackoverflow.com/a/15094263/1244030
3023          *
3024          * Pass in any number of $.Deferred instances.
3025          * @returns {Object}
3026          */
3027         whenAllDone: function () {
3028             var deferreds = [],
3029                 result = $.Deferred();
3030 
3031             $.each(arguments, function(i, current) {
3032                 var currentDeferred = $.Deferred();
3033                 current.then(function() {
3034                     currentDeferred.resolve(false, arguments);
3035                 }, function() {
3036                     currentDeferred.resolve(true, arguments);
3037                 });
3038                 deferreds.push(currentDeferred);
3039             });
3040 
3041             $.when.apply($, deferreds).then(function() {
3042                 var failures = [],
3043                     successes = [];
3044 
3045                 $.each(arguments, function(i, args) {
3046                     // If we resolved with `true` as the first parameter
3047                     // we have a failure, a success otherwise
3048                     var target = args[0] ? failures : successes,
3049                         data = args[1];
3050                     // Push either all arguments or the only one
3051                     target.push(data.length === 1 ? data[0] : args);
3052                 });
3053 
3054                 if(failures.length) {
3055                     return result.reject.apply(result, failures);
3056                 }
3057 
3058                 return result.resolve.apply(result, successes);
3059             });
3060 
3061             return result;
3062         }
3063     };    
3064 
3065     window.finesse = window.finesse || {};
3066     window.finesse.utilities = window.finesse.utilities || {};
3067     window.finesse.utilities.Utilities = Utilities;
3068     
3069     return Utilities;
3070 });
3071 
3072 /** The following comment is to prevent jslint errors about 
3073  * using variables before they are defined.
3074  */
3075 /*global finesse*/
3076 
3077 /**
3078  * Initiated by the Master to create a shared BOSH connection.
3079  *
3080  * @requires Utilities
3081  */
3082 
3083 /**
3084  * @class
3085  * Establishes a shared event connection by creating a communication tunnel
3086  * with the notification server and consume events which could be published.
3087  * Public functions are exposed to register to the connection status information
3088  * and events.
3089  * @constructor
3090  * @param {String} host
3091  *     The host name/ip of the Finesse server.
3092  * @throws {Error} If required constructor parameter is missing.
3093  */
3094 /** @private */
3095 define('clientservices/MasterTunnel',["utilities/Utilities"], function (Utilities) {
3096      var MasterTunnel = function (host, scheme) { 
3097         if (typeof host !== "string" || host.length === 0) {
3098             throw new Error("Required host parameter missing.");
3099         }
3100 
3101         var
3102 
3103         /**
3104          * Flag to indicate whether the tunnel frame is loaded.
3105          * @private
3106          */
3107         _isTunnelLoaded = false,
3108 
3109         /**
3110          * Short reference to the Finesse utility.
3111          * @private
3112          */
3113         _util = Utilities,
3114 
3115         /**
3116          * The URL with host and port to the Finesse server.
3117          * @private
3118          */
3119         _tunnelOrigin,
3120 
3121         /**
3122          * Location of the tunnel HTML URL.
3123          * @private
3124          */
3125         _tunnelURL,
3126         
3127         /**
3128          * The port on which to connect to the Finesse server to load the eventing resources.
3129          * @private
3130          */
3131         _tunnelOriginPort,
3132         
3133         /**
3134          * Flag to indicate whether we have processed the tunnel config yet.
3135          * @private
3136          */
3137         _isTunnelConfigInit = false,
3138 
3139         /**
3140          * The tunnel frame window object.
3141          * @private
3142          */
3143         _tunnelFrame,
3144 
3145         /**
3146          * The handler registered with the object to be invoked when an event is
3147          * delivered by the notification server.
3148          * @private
3149          */
3150         _eventHandler,
3151         
3152         /**
3153          * The handler registered with the object to be invoked when presence is
3154          * delivered by the notification server.
3155          * @private
3156          */
3157         _presenceHandler,
3158 
3159         /**
3160          * The handler registered with the object to be invoked when the BOSH
3161          * connection has changed states. The object will contain the "status"
3162          * property and a "resourceID" property only if "status" is "connected".
3163          * @private
3164          */
3165         _connInfoHandler,
3166 
3167         /**
3168          * The last connection status published by the JabberWerx library.
3169          * @private
3170          */
3171         _statusCache,
3172 
3173         /**
3174          * The last event sent by notification server.
3175          * @private
3176          */
3177         _eventCache,
3178 
3179         /**
3180          * The ID of the user logged into notification server.
3181          * @private
3182          */
3183         _id,
3184 
3185         /**
3186          * The domain of the XMPP server, representing the portion of the JID
3187          * following '@': userid@domain.com
3188          * @private
3189          */
3190         _xmppDomain,
3191 
3192         /**
3193          * The password of the user logged into notification server.
3194          * @private
3195          */
3196         _password,
3197 
3198         /**
3199          * The jid of the pubsub service on the XMPP server
3200          * @private
3201          */
3202         _pubsubDomain,
3203 
3204         /**
3205          * The resource to use for the BOSH connection.
3206          * @private
3207          */
3208         _resource,
3209 
3210         /**
3211          * The resource ID identifying the client device (that we receive from the server).
3212          * @private
3213          */
3214         _resourceID,
3215 
3216         /**
3217          * The different types of messages that could be sent to the parent frame.
3218          * The types here should be understood by the parent frame and used to
3219          * identify how the message is formatted.
3220          * @private
3221          */
3222         _TYPES = {
3223             EVENT: 0,
3224             ID: 1,
3225             PASSWORD: 2,
3226             RESOURCEID: 3,
3227             STATUS: 4,
3228             XMPPDOMAIN: 5,
3229             PUBSUBDOMAIN: 6,
3230             SUBSCRIBE: 7,
3231             UNSUBSCRIBE: 8,
3232             PRESENCE: 9,
3233             CONNECT_REQ: 10
3234         },
3235 
3236         _handlers = {
3237             subscribe: {},
3238             unsubscribe: {}
3239         },
3240         
3241 
3242         /**
3243          * Create a connection info object.
3244          * @returns {Object}
3245          *     A connection info object containing a "status" and "resourceID".
3246          * @private
3247          */
3248         _createConnInfoObj = function () {
3249             return {
3250                 status: _statusCache,
3251                 resourceID: _resourceID
3252             };
3253         },
3254 
3255         /**
3256          * Utility function which sends a message to the dynamic tunnel frame
3257          * event frame formatted as follows: "type|message".
3258          * @param {Number} type
3259          *     The category type of the message.
3260          * @param {String} message
3261          *     The message to be sent to the tunnel frame.
3262          * @private
3263          */
3264         _sendMessage = function (type, message) {
3265             message = type + "|" + message;
3266             _util.sendMessage(message, _tunnelFrame, _tunnelOrigin);
3267         },
3268 
3269         /**
3270          * Utility to process the response of a subscribe request from
3271          * the tunnel frame, then invoking the stored callback handler
3272          * with the respective data (error, when applicable)
3273          * @param {String} data
3274          *     The response in the format of "node[|error]"
3275          * @private
3276          */
3277         _processSubscribeResponse = function (data) {
3278             var dataArray = data.split("|"),
3279             node = dataArray[0],
3280             err;
3281             
3282             //Error is optionally the second item in the array
3283             if (dataArray.length) {
3284                 err = dataArray[1];
3285             }
3286             
3287             // These response handlers are short lived and should be removed and cleaned up immediately after invocation.
3288             if (_handlers.subscribe[node]) {
3289                 _handlers.subscribe[node](err);
3290                 delete _handlers.subscribe[node];
3291             }
3292         },
3293 
3294         /**
3295          * Utility to process the response of an unsubscribe request from
3296          * the tunnel frame, then invoking the stored callback handler
3297          * with the respective data (error, when applicable)
3298          * @param {String} data
3299          *     The response in the format of "node[|error]"
3300          * @private
3301          */
3302         _processUnsubscribeResponse = function (data) {
3303             var dataArray = data.split("|"),
3304             node = dataArray[0],
3305             err;
3306             
3307             //Error is optionally the second item in the array
3308             if (dataArray.length) {
3309                 err = dataArray[1];
3310             }
3311             
3312             // These response handlers are short lived and should be removed and cleaned up immediately after invocation.
3313             if (_handlers.unsubscribe[node]) {
3314                 _handlers.unsubscribe[node](err);
3315                 delete _handlers.unsubscribe[node];
3316             }
3317         },
3318 
3319         /**
3320          * Handler for messages delivered by window.postMessage. Listens for events
3321          * published by the notification server, connection status published by
3322          * the JabberWerx library, and the resource ID created when the BOSH
3323          * connection has been established.
3324          * @param {Object} e
3325          *     The message object as provided by the window.postMessage feature.
3326          * @private
3327          */
3328         _messageHandler = function (e) {
3329             var
3330 
3331             //Extract the message type and message data. The expected format is
3332             //"type|data" where type is a number represented by the TYPES object.
3333             delimPos = e.data.indexOf("|"),
3334             type = Number(e.data.substr(0, delimPos)),
3335             data =  e.data.substr(delimPos + 1);
3336             
3337             //Accepts messages and invoke the correct registered handlers.
3338             switch (type) {
3339             case _TYPES.EVENT:
3340                 _eventCache = data;
3341                 if (typeof _eventHandler === "function") {
3342                     _eventHandler(data);
3343                 }
3344                 break;
3345             case _TYPES.STATUS:
3346                 _statusCache = data;
3347 
3348                 //A "loaded" status means that the frame is ready to accept
3349                 //credentials for establishing a BOSH connection.
3350                 if (data === "loaded") {
3351                     _isTunnelLoaded = true;
3352                     if(_resource) {
3353                         _sendMessage(_TYPES.RESOURCEID, _resource);
3354                     }
3355                     _sendMessage(_TYPES.ID, _id);
3356                     _sendMessage(_TYPES.XMPPDOMAIN, _xmppDomain);
3357                     _sendMessage(_TYPES.PASSWORD, _password);
3358                     _sendMessage(_TYPES.PUBSUBDOMAIN, _pubsubDomain);
3359                 } else if (typeof _connInfoHandler === "function") {
3360                     _connInfoHandler(_createConnInfoObj());
3361                 }
3362                 break;
3363             case _TYPES.RESOURCEID:
3364                 _resourceID = data;
3365                 break;
3366             case _TYPES.SUBSCRIBE:
3367                 _processSubscribeResponse(data);
3368                 break;
3369             case _TYPES.UNSUBSCRIBE:
3370                 _processUnsubscribeResponse(data);
3371                 break;
3372             case _TYPES.PRESENCE:
3373                 if (typeof _presenceHandler === "function") {
3374                     _presenceHandler(data);
3375                 }
3376                 break;
3377             default:
3378                 break;
3379             }
3380         },
3381 
3382         /**
3383          * Initialize the tunnel config so that the url can be http or https with the appropriate port
3384          * @private
3385          */
3386         _initTunnelConfig = function () {
3387             if (_isTunnelConfigInit === true) {
3388                 return;
3389             }
3390             
3391             //Initialize tunnel origin
3392             //Determine tunnel origin based on host and scheme
3393             _tunnelOriginPort = (scheme && scheme.indexOf("https") !== -1) ? "7443" : "7071";
3394             if (scheme) {
3395                 _tunnelOrigin = scheme + "://" + host + ":" + _tunnelOriginPort;
3396             } else {
3397                 _tunnelOrigin = "http://" + host + ":" + _tunnelOriginPort;
3398             }
3399             _tunnelURL = _tunnelOrigin + "/tunnel/";
3400             
3401             _isTunnelConfigInit = true;
3402         },
3403 
3404         /**
3405          * Create the tunnel iframe which establishes the shared BOSH connection.
3406          * Messages are sent across frames using window.postMessage.
3407          * @private
3408          */
3409         _createTunnel = function () {
3410             var tunnelID = ((self === parent) ? "tunnel-frame" : "autopilot-tunnel-frame"),
3411             iframe = document.createElement("iframe");         
3412             iframe.style.display = "none";
3413             iframe.setAttribute("id", tunnelID);
3414             iframe.setAttribute("name", tunnelID);
3415             iframe.setAttribute("src", _tunnelURL);
3416             document.body.appendChild(iframe);
3417             _tunnelFrame = window.frames[tunnelID];
3418         };
3419 
3420         /**
3421          * Sends a message via postmessage to the EventTunnel to attempt to connect to the XMPP server
3422          * @private
3423          */
3424         this.makeConnectReq = function () {
3425             _sendMessage(_TYPES.PASSWORD, _password);
3426         };
3427         
3428         /**
3429          * @private
3430          * Returns the host of the Finesse server.
3431          * @returns {String}
3432          *     The host specified during the creation of the object.
3433          */
3434         this.getHost = function () {
3435             return host;
3436         };
3437 
3438         /**
3439          * @private
3440          * The resource ID of the user who is logged into the notification server.
3441          * @returns {String}
3442          *     The resource ID generated by the notification server.
3443          */
3444         this.getResourceID = function () {
3445             return _resourceID;
3446         };
3447 
3448         /**
3449          * @private
3450          * Indicates whether the tunnel frame is loaded.
3451          * @returns {Boolean}
3452          *     True if the tunnel frame is loaded, false otherwise.
3453          */
3454         this.isTunnelLoaded = function () {
3455             return _isTunnelLoaded;
3456         };
3457 
3458         /**
3459          * @private
3460          * The location of the tunnel HTML URL.
3461          * @returns {String}
3462          *     The location of the tunnel HTML URL.
3463          */
3464         this.getTunnelURL = function () {
3465             return _tunnelURL;
3466         };
3467 
3468         /**
3469          * @private
3470          * Tunnels a subscribe request to the eventing iframe.
3471          * @param {String} node
3472          *     The node to subscribe to
3473          * @param {Function} handler
3474          *     Handler to invoke upon success or failure
3475          */
3476         this.subscribe = function (node, handler) {
3477             if (handler && typeof handler !== "function") {
3478                 throw new Error("Parameter is not a function.");
3479             }
3480             _handlers.subscribe[node] = handler;
3481             _sendMessage(_TYPES.SUBSCRIBE, node);
3482         };
3483 
3484         /**
3485          * @private
3486          * Tunnels an unsubscribe request to the eventing iframe.
3487          * @param {String} node
3488          *     The node to unsubscribe from
3489          * @param {Function} handler
3490          *     Handler to invoke upon success or failure
3491          */
3492         this.unsubscribe = function (node, handler) {
3493             if (handler && typeof handler !== "function") {
3494                 throw new Error("Parameter is not a function.");
3495             }
3496             _handlers.unsubscribe[node] = handler;
3497             _sendMessage(_TYPES.UNSUBSCRIBE, node);
3498         };
3499 
3500         /**
3501          * @private
3502          * Registers a handler to be invoked when an event is delivered. Only one
3503          * is registered at a time. If there has already been an event that was
3504          * delivered, the handler will be invoked immediately.
3505          * @param {Function} handler
3506          *     Invoked when an event is delivered through the event connection.
3507          */
3508         this.registerEventHandler = function (handler) {
3509             if (typeof handler !== "function") {
3510                 throw new Error("Parameter is not a function.");
3511             }
3512             _eventHandler = handler;
3513             if (_eventCache) {
3514                 handler(_eventCache);
3515             }
3516         };
3517 
3518         /**
3519          * @private
3520          * Unregisters the event handler completely.
3521          */
3522         this.unregisterEventHandler = function () {
3523             _eventHandler = undefined;
3524         };
3525         
3526         /**
3527          * @private
3528          * Registers a handler to be invoked when a presence event is delivered. Only one
3529          * is registered at a time. 
3530          * @param {Function} handler
3531          *     Invoked when a presence event is delivered through the event connection.
3532          */
3533         this.registerPresenceHandler = function (handler) {
3534             if (typeof handler !== "function") {
3535                 throw new Error("Parameter is not a function.");
3536             }
3537             _presenceHandler = handler;
3538         };
3539         
3540         /**
3541          * @private
3542          * Unregisters the presence event handler completely.
3543          */
3544         this.unregisterPresenceHandler = function () {
3545             _presenceHandler = undefined;
3546         };
3547 
3548         /**
3549          * @private
3550          * Registers a handler to be invoked when a connection status changes. The
3551          * object passed will contain a "status" property, and a "resourceID"
3552          * property, which will contain the most current resource ID assigned to
3553          * the client. If there has already been an event that was delivered, the
3554          * handler will be invoked immediately.
3555          * @param {Function} handler
3556          *     Invoked when a connection status changes.
3557          */
3558         this.registerConnectionInfoHandler = function (handler) {
3559             if (typeof handler !== "function") {
3560                 throw new Error("Parameter is not a function.");
3561             }
3562             _connInfoHandler = handler;
3563             if (_statusCache) {
3564                 handler(_createConnInfoObj());
3565             }
3566         };
3567 
3568         /**
3569          * @private
3570          * Unregisters the connection information handler.
3571          */
3572         this.unregisterConnectionInfoHandler = function () {
3573             _connInfoHandler = undefined;
3574         };
3575 
3576         /**
3577          * @private
3578          * Start listening for events and create a event tunnel for the shared BOSH
3579          * connection.
3580          * @param {String} id
3581          *     The ID of the user for the notification server.
3582          * @param {String} password
3583          *     The password of the user for the notification server.
3584          * @param {String} xmppDomain
3585          *     The XMPP domain of the notification server
3586          * @param {String} pubsubDomain
3587          *     The location (JID) of the XEP-0060 PubSub service
3588          * @param {String} resource
3589          *     The resource to connect to the notification servier with.
3590          */
3591         this.init = function (id, password, xmppDomain, pubsubDomain, resource) {
3592             
3593             if (typeof id !== "string" || typeof password !== "string" || typeof xmppDomain !== "string" || typeof pubsubDomain !== "string") {
3594                 throw new Error("Invalid or missing required parameters.");
3595             }
3596 
3597             _initTunnelConfig();
3598             
3599             _id = id;
3600             _password = password;
3601             _xmppDomain = xmppDomain;
3602             _pubsubDomain = pubsubDomain;
3603             _resource = resource;
3604 
3605             //Attach a listener for messages sent from tunnel frame.
3606             _util.receiveMessage(_messageHandler, _tunnelOrigin);
3607 
3608             //Create the tunnel iframe which will establish the shared connection.
3609             _createTunnel();
3610         };
3611 
3612         //BEGIN TEST CODE//
3613 //        /**
3614 //         * Test code added to expose private functions that are used by unit test
3615 //         * framework. This section of code is removed during the build process
3616 //         * before packaging production code. The [begin|end]TestSection are used
3617 //         * by the build to identify the section to strip.
3618 //         * @ignore
3619 //         */
3620 //        this.beginTestSection = 0;
3621 //
3622 //        /**
3623 //         * @ignore
3624 //         */
3625 //        this.getTestObject = function () {
3626 //            //Load mock dependencies.
3627 //            var _mock = new MockControl();
3628 //            _util = _mock.createMock(finesse.utilities.Utilities);
3629 //
3630 //            return {
3631 //                //Expose mock dependencies
3632 //                mock: _mock,
3633 //                util: _util,
3634 //
3635 //                //Expose internal private functions
3636 //                types: _TYPES,
3637 //                createConnInfoObj: _createConnInfoObj,
3638 //                sendMessage: _sendMessage,
3639 //                messageHandler: _messageHandler,
3640 //                createTunnel: _createTunnel,
3641 //                handlers: _handlers,
3642 //                initTunnelConfig : _initTunnelConfig
3643 //            };
3644 //        };
3645 //
3646 //        /**
3647 //         * @ignore
3648 //         */
3649 //        this.endTestSection = 0;
3650 //        //END TEST CODE//
3651     };
3652     
3653     /** @namespace JavaScript class objects and methods to handle the subscription to Finesse events.*/
3654     finesse.clientservices = finesse.clientservices || {};
3655 
3656     window.finesse = window.finesse || {};
3657     window.finesse.clientservices = window.finesse.clientservices || {};
3658     window.finesse.clientservices.MasterTunnel = MasterTunnel;
3659 
3660     return MasterTunnel;
3661 
3662 });
3663 
3664 /**
3665  * Contains a list of topics used for client side pubsub.
3666  *
3667  */
3668 
3669 /** @private */
3670 define('clientservices/Topics',[], function () {
3671     
3672    var Topics = (function () {
3673 
3674         /**
3675          * @private
3676          * The namespace prepended to all Finesse topics.
3677          */
3678         this.namespace = "finesse.info";
3679     
3680         /**
3681          * @private
3682          * Gets the full topic name with the Finesse namespace prepended.
3683          * @param {String} topic
3684          *     The topic category.
3685          * @returns {String}
3686          *     The full topic name with prepended namespace.
3687          */
3688         var _getNSTopic = function (topic) {
3689             return this.namespace + "." + topic;
3690         };
3691         
3692         /** @scope finesse.clientservices.Topics */
3693         return {
3694             /** 
3695              * @private
3696              * Client side request channel. 
3697              */
3698             REQUESTS: _getNSTopic("requests"),
3699     
3700             /** 
3701              * @private
3702              * Client side response channel. 
3703              */
3704             RESPONSES: _getNSTopic("responses"),
3705 
3706             /** 
3707              * @private
3708              * Connection status. 
3709              */
3710             EVENTS_CONNECTION_INFO: _getNSTopic("connection"),
3711             
3712             /** 
3713              * @private
3714              * Presence channel 
3715              */
3716             PRESENCE: _getNSTopic("presence"),
3717     
3718             /**
3719              * @private
3720              * Convert a Finesse REST URI to a OpenAjax compatible topic name.
3721              */
3722             getTopic: function (restUri) {
3723                 //The topic should not start with '/' else it will get replaced with
3724                 //'.' which is invalid.
3725                 //Thus, remove '/' if it is at the beginning of the string
3726                 if (restUri.indexOf('/') === 0) {
3727                     restUri = restUri.substr(1);
3728                 }
3729     
3730                 //Replace every instance of "/" with ".". This is done to follow the
3731                 //OpenAjaxHub topic name convention.
3732                 return restUri.replace(/\//g, ".");
3733             }
3734         };
3735     }());
3736     window.finesse = window.finesse || {};
3737     window.finesse.clientservices = window.finesse.clientservices || {};
3738     /** @private */
3739     window.finesse.clientservices.Topics = Topics;
3740     
3741     return Topics;
3742 });
3743 
3744 /** The following comment is to prevent jslint errors about 
3745  * using variables before they are defined.
3746  */
3747 /*global finesse*/
3748 
3749 /**
3750  * Registers with the MasterTunnel to receive events, which it
3751  *     could publish to the OpenAjax gadget pubsub infrastructure.
3752  *
3753  * @requires OpenAjax, finesse.clientservices.MasterTunnel, finesse.clientservices.Topics
3754  */
3755 
3756 /** @private */
3757 define('clientservices/MasterPublisher',[
3758     "clientservices/MasterTunnel",
3759     "clientservices/Topics",
3760     "utilities/Utilities"
3761 ],
3762 function (MasterTunnel, Topics, Utilities) {
3763     
3764      var MasterPublisher = function (tunnel, hub) {
3765         if (!(tunnel instanceof MasterTunnel)) {
3766             throw new Error("Required tunnel object missing or invalid.");
3767         }
3768 
3769         var
3770         
3771         ClientServices = finesse.clientservices.ClientServices,
3772 
3773         /**
3774          * Reference to the gadget pubsub Hub instance.
3775          * @private
3776          */
3777         _hub = hub,
3778 
3779         /**
3780          * Reference to the Topics class.
3781          * @private
3782          */
3783         _topics = Topics,
3784         
3785         /**
3786          * Reference to conversion utilities class.
3787          * @private
3788          */
3789         _utils = Utilities,
3790         
3791         /**
3792          * References to ClientServices logger methods
3793          * @private
3794          */
3795         _logger = {
3796             log: ClientServices.log
3797         },
3798         
3799         /**
3800          * Store the passed in tunnel.
3801          * @private
3802          */
3803         _tunnel = tunnel,
3804 
3805         /**
3806          * Caches the connection info event so that it could be published if there
3807          * is a request for it.
3808          * @private
3809          */
3810         _connInfoCache = {},
3811 
3812         /**
3813          * The types of possible request types supported when listening to the
3814          * requests channel. Each request type could result in different operations.
3815          * @private
3816          */
3817         _REQTYPES = {
3818             CONNECTIONINFO: "ConnectionInfoReq",
3819             SUBSCRIBE: "SubscribeNodeReq",
3820             UNSUBSCRIBE: "UnsubscribeNodeReq",
3821             CONNECT: "ConnectionReq"
3822         },
3823 
3824         /**
3825          * Will store list of nodes that have OF subscriptions created
3826          *     _nodesList[node][subscribing].reqIds[subid]
3827          *     _nodesList[node][active].reqIds[subid]
3828          *     _nodesList[node][unsubscribing].reqIds[subid]
3829          *     _nodesList[node][holding].reqIds[subid]
3830          * @private
3831          */
3832         _nodesList = {},
3833         
3834         /**
3835          * The states that a subscription can be in
3836          * @private
3837          */
3838         _CHANNELSTATES = {
3839             UNINITIALIZED: "Uninitialized",
3840             PENDING: "Pending",
3841             OPERATIONAL: "Operational"
3842         },
3843 
3844         /**
3845           * Checks if the payload is JSON 
3846           * @returns {Boolean}
3847           * @private
3848           */
3849         _isJsonPayload = function(event) {
3850             var delimStart, delimEnd, retval = false;
3851             
3852             try { 
3853               delimStart = event.indexOf('{');
3854               delimEnd = event.lastIndexOf('}');
3855 
3856               if ((delimStart !== -1 ) && (delimEnd === (event.length - 1))) {
3857                 retval = true;  //event contains JSON payload
3858               }
3859             } catch (err) {
3860               _logger.log("MasterPublisher._isJsonPayload() - Caught error: " + err);
3861             }
3862             return retval;
3863         },
3864         
3865                 /**
3866           * Parses a JSON event and then publishes.
3867           *
3868           * @param {String} event
3869           *     The full event payload.
3870           * @throws {Error} If the payload object is malformed.
3871           * @private
3872           */
3873         _parseAndPublishJSONEvent = function(event) {
3874             var topic, eventObj, publishEvent,
3875             delimPos = event.indexOf("{"),
3876             node, parser,
3877             eventJson = event,
3878             returnObj = {node: null, data: null};
3879 
3880             try {
3881                //Extract and strip the node path from the message
3882                if (delimPos > 0) 
3883                {
3884                   //We need to decode the URI encoded node path
3885                   //TODO: make sure this is kosher with OpenAjax topic naming
3886                   node = decodeURI(event.substr(0, delimPos));
3887                   eventJson = event.substr(delimPos);
3888                   
3889                   //Converting the node path to openAjaxhub topic
3890                   topic = _topics.getTopic(node);
3891                   
3892                   returnObj.node = node;
3893                   returnObj.payload = eventJson;
3894                } 
3895                else 
3896                {
3897                   _logger.log("MasterPublisher._parseAndPublishJSONEvent() - [ERROR] node is not given in postMessage: " + eventJson);
3898                   throw new Error("node is not given in postMessage: " + eventJson);
3899                }
3900 
3901                parser = _utils.getJSONParser();
3902 
3903                eventObj = parser.parse(eventJson);
3904                returnObj.data = eventObj;
3905 
3906             } catch (err) {
3907                _logger.log("MasterPublisher._parseAndPublishJSONEvent() - [ERROR] Malformed event payload: " + err);
3908                throw new Error("Malformed event payload : " + err);
3909             }
3910             
3911             _logger.log("MasterPublisher._parseAndPublishJSONEvent() - Received JSON event on node '" + node + "': " + eventJson); 
3912             
3913             publishEvent = {content : event, object : eventObj };
3914 
3915             //Publish event to proper event topic.
3916             if (topic && eventObj) {
3917                _hub.publish(topic, publishEvent);
3918             }
3919         },
3920         
3921         /**
3922           * Parses an XML event and then publishes.
3923           *
3924           * @param {String} event
3925           *     The full event payload.
3926           * @throws {Error} If the payload object is malformed.
3927           * @private
3928           */
3929         _parseAndPublishXMLEvent = function(event) {
3930             var topic, eventObj, publishEvent, restTopic,
3931             delimPos = event.indexOf("<"),
3932             node,
3933             eventXml = event;
3934             
3935             try {
3936                //Extract and strip the node path from the message
3937                if (delimPos > 0) {
3938                   //We need to decode the URI encoded node path
3939                   //TODO: make sure this is kosher with OpenAjax topic naming
3940                   node = decodeURI(event.substr(0, delimPos));
3941                   eventXml = event.substr(delimPos);
3942                   //Converting the node path to openAjaxhub topic
3943                   topic = _topics.getTopic(node);
3944                } else {
3945                   _logger.log("MasterPublisher._parseAndPublishXMLEvent() - [ERROR] node is not given in postMessage: " + eventXml);
3946                   throw new Error("node is not given in postMessage: " + eventXml);
3947                }
3948 
3949                eventObj = _utils.xml2JsObj(eventXml);
3950                   
3951            } catch (err) {
3952                _logger.log("MasterPublisher._parseAndPublishXMLEvent() - [ERROR] Malformed event payload: " + err);
3953                throw new Error("Malformed event payload : " + err);
3954            }
3955            
3956            _logger.log("MasterPublisher._parseAndPublishXMLEvent() - Received XML event on node '" + node + "': " + eventXml);
3957            
3958            publishEvent = {content : event, object : eventObj };
3959 
3960            //Publish event to proper event topic.
3961            if (topic && eventObj) {
3962                _hub.publish(topic, publishEvent);
3963            }
3964         },
3965         
3966         /**
3967          * Publishes events to the appropriate topic. The topic name is determined
3968          * by fetching the source value from the event.
3969          * @param {String} event
3970          *     The full event payload.
3971          * @throws {Error} If the payload object is malformed.
3972          * @private
3973          */
3974         _eventHandler = function (event) {
3975             
3976             //Handle JSON or XML events
3977             if (!_isJsonPayload(event))
3978             {
3979                //XML
3980                _parseAndPublishXMLEvent(event);
3981             }
3982             else
3983             {
3984                //JSON
3985                _parseAndPublishJSONEvent(event);
3986             }
3987         },
3988         
3989         
3990         /**
3991          * Handler for when presence events are sent through the MasterTunnel.
3992          * @returns {Object}
3993          *     A presence xml event.
3994          * @private
3995          */
3996         _presenceHandler = function (event) {
3997             var eventObj = _utils.xml2JsObj(event), publishEvent;
3998             
3999             publishEvent = {content : event, object : eventObj};
4000             
4001             if (eventObj) {
4002                 _hub.publish(_topics.PRESENCE, publishEvent);
4003             }
4004         },
4005 
4006         /**
4007          * Clone the connection info object from cache.
4008          * @returns {Object}
4009          *     A connection info object containing a "status" and "resourceID".
4010          * @private
4011          */
4012         _cloneConnInfoObj = function () {
4013             if (_connInfoCache) {
4014                 return {
4015                     status: _connInfoCache.status,
4016                     resourceID: _connInfoCache.resourceID
4017                 };
4018             } else {
4019                 return null;
4020             }
4021         },
4022 
4023         /**
4024          * Cleans up any outstanding subscribe/unsubscribe requests and notifies them of errors.
4025          * This is done if we get disconnected because we cleanup explicit subscriptions on disconnect.
4026          * @private
4027          */
4028         _cleanupPendingRequests = function () {
4029             var node, curSubid, errObj = {
4030                 error: {
4031                     errorType: "Disconnected",
4032                     errorMessage: "Outstanding request will never complete."
4033                 }
4034             };
4035 
4036             // Iterate through all outstanding subscribe requests to notify them that it will never return
4037             for (node in _nodesList) {
4038                 if (_nodesList.hasOwnProperty(node)) {
4039                     for (curSubid in _nodesList[node].subscribing.reqIds) {
4040                         if (_nodesList[node].subscribing.reqIds.hasOwnProperty(curSubid)) {
4041                             // Notify this outstanding subscribe request to give up and error out
4042                             _hub.publish(_topics.RESPONSES + "." + curSubid, errObj); 
4043                         }
4044                     }
4045                     for (curSubid in _nodesList[node].unsubscribing.reqIds) {
4046                         if (_nodesList[node].unsubscribing.reqIds.hasOwnProperty(curSubid)) {
4047                             // Notify this outstanding unsubscribe request to give up and error out
4048                             _hub.publish(_topics.RESPONSES + "." + curSubid, errObj); 
4049                         }
4050                     }
4051                 }
4052             }
4053         },
4054 
4055         /**
4056          * Publishes the connection info to the connection info topic.
4057          * @param {Object} connInfo
4058          *     The connection info object containing the status and resource ID.
4059          * @private
4060          */
4061         _connInfoHandler = function (connInfo) {
4062             var before = _connInfoCache.status;
4063             _connInfoCache = connInfo;
4064             _logger.log("MasterPublisher._connInfoHandler() - Connection status: " + connInfo.status);
4065             _hub.publish(_topics.EVENTS_CONNECTION_INFO, _cloneConnInfoObj());
4066             if (before === "connected" && connInfo.status !== "connected") {
4067                 // Fail all pending requests when we transition to disconnected
4068                 _cleanupPendingRequests();
4069             }
4070         },
4071 
4072         
4073         /**
4074          * Utility method to bookkeep node subscription requests and determine
4075          * whehter it is necessary to tunnel the request to JabberWerx.
4076          * @param {String} node
4077          *     The node of interest
4078          * @param {String} reqId
4079          *     A unique string identifying the request/subscription
4080          * @private
4081          */
4082         _subscribeNode = function (node, subid) {
4083             if (_connInfoCache.status !== "connected") {
4084                 _hub.publish(_topics.RESPONSES + "." + subid, {
4085                     error: {
4086                         errorType: "Not connected",
4087                         errorMessage: "Cannot subscribe without connection."
4088                     }
4089                 });
4090                 return;
4091             }
4092             // NODE DOES NOT YET EXIST
4093             if (!_nodesList[node]) {
4094                 _nodesList[node] = {
4095                     "subscribing": {
4096                         "reqIds": {},
4097                         "length": 0
4098                     },
4099                     "active": {
4100                         "reqIds": {},
4101                         "length": 0
4102                     },
4103                     "unsubscribing": {
4104                         "reqIds": {},
4105                         "length": 0
4106                     },
4107                     "holding": {
4108                         "reqIds": {},
4109                         "length": 0
4110                     }
4111                 };
4112             }
4113             if (_nodesList[node].active.length === 0) {
4114                 if (_nodesList[node].unsubscribing.length === 0) {
4115                     if (_nodesList[node].subscribing.length === 0) {
4116                         _nodesList[node].subscribing.reqIds[subid] = true;
4117                         _nodesList[node].subscribing.length += 1;
4118 
4119                         _logger.log("MasterPublisher._subscribeNode() - Attempting to subscribe to node '" + node + "'");
4120                         _tunnel.subscribe(node, function (err) {
4121                             var errObj, curSubid;
4122                             if (err) {
4123                                 errObj = {
4124                                     subscribe: {
4125                                         content: err
4126                                     }
4127                                 };
4128 
4129                                 try {
4130                                     errObj.subscribe.object = gadgets.json.parse((_utils.xml2json(jQuery.parseXML(err), "")));
4131                                 } catch (e) {
4132                                     errObj.error = {
4133                                         errorType: "parseError",
4134                                         errorMessage: "Could not serialize XML: " + e
4135                                     };
4136                                 }
4137                                 _logger.log("MasterPublisher._subscribeNode() - Error subscribing to node '" + node + "': " + err);
4138                             } else {
4139                                 _logger.log("MasterPublisher._subscribeNode() - Subscribed to node '" + node + "'");
4140                             }
4141 
4142                             for (curSubid in _nodesList[node].subscribing.reqIds) {
4143                                 if (_nodesList[node].subscribing.reqIds.hasOwnProperty(curSubid)) {
4144                                     _hub.publish(_topics.RESPONSES + "." + curSubid, errObj);
4145                                     if (!err) {
4146                                         _nodesList[node].active.reqIds[curSubid] = true;
4147                                         _nodesList[node].active.length += 1;
4148                                     }
4149                                     delete _nodesList[node].subscribing.reqIds[curSubid];
4150                                     _nodesList[node].subscribing.length -= 1;
4151                                 }
4152                             }
4153                         });
4154                         
4155                     } else { //other ids are subscribing
4156                         _nodesList[node].subscribing.reqIds[subid] = true;
4157                         _nodesList[node].subscribing.length += 1;
4158                     }                       
4159                 } else { //An unsubscribe request is pending, hold onto these subscribes until it is done
4160                     _nodesList[node].holding.reqIds[subid] = true;
4161                     _nodesList[node].holding.length += 1;
4162                 }
4163             } else { // The node has active subscriptions; add this subid and return successful response
4164                 _nodesList[node].active.reqIds[subid] = true;
4165                 _nodesList[node].active.length += 1;
4166                 _hub.publish(_topics.RESPONSES + "." + subid, undefined); 
4167             }
4168         },
4169 
4170         /**
4171          * Utility method to bookkeep node unsubscribe requests and determine
4172          * whehter it is necessary to tunnel the request to JabberWerx.
4173          * @param {String} node
4174          *     The node to unsubscribe from
4175          * @param {String} reqId
4176          *     A unique string identifying the subscription to remove
4177          * @private
4178          */
4179         _unsubscribeNode = function (node, subid) {
4180             if (!_nodesList[node]) { //node DNE, publish success response
4181                 _hub.publish(_topics.RESPONSES + "." + subid, undefined); 
4182             } else {
4183                 if (_connInfoCache.status !== "connected") {
4184                     _hub.publish(_topics.RESPONSES + "." + subid, {
4185                         error: {
4186                             errorType: "Not connected",
4187                             errorMessage: "Cannot unsubscribe without connection."
4188                         }
4189                     });
4190                     return;
4191                 }
4192                 if (_nodesList[node].active.length > 1) {
4193                     delete _nodesList[node].active.reqIds[subid];
4194                     _hub.publish(_topics.RESPONSES + "." + subid, undefined); 
4195                     _nodesList[node].active.length -= 1;
4196                 } else if (_nodesList[node].active.length === 1) { // transition subid from active category to unsubscribing category
4197                     _nodesList[node].unsubscribing.reqIds[subid] = true;
4198                     _nodesList[node].unsubscribing.length += 1;
4199                     delete _nodesList[node].active.reqIds[subid];
4200                     _nodesList[node].active.length -= 1;
4201 
4202                     _logger.log("MasterPublisher._unsubscribeNode() - Attempting to unsubscribe from node '" + node + "'");
4203                     _tunnel.unsubscribe(node, function (err) {
4204                         var errObj, curSubid;
4205                         if (err) {
4206                             errObj = {
4207                                 subscribe: {
4208                                     content: err
4209                                 }
4210                             };
4211 
4212                             try {
4213                                 errObj.subscribe.object = gadgets.json.parse((_utils.xml2json(jQuery.parseXML(err), "")));
4214                             } catch (e) {
4215                                 errObj.error = {
4216                                     errorType: "parseError",
4217                                     errorMessage: "Could not serialize XML: " + e
4218                                 };
4219                             }
4220                             _logger.log("MasterPublisher._unsubscribeNode() - Error unsubscribing from node '" + node + "': " + err);
4221                         } else {
4222                             _logger.log("MasterPublisher._unsubscribeNode() - Unsubscribed from node '" + node + "'");
4223                         }
4224 
4225                         for (curSubid in _nodesList[node].unsubscribing.reqIds) {
4226                             if (_nodesList[node].unsubscribing.reqIds.hasOwnProperty(curSubid)) {
4227                                 // publish to all subids whether unsubscribe failed or succeeded
4228                                 _hub.publish(_topics.RESPONSES + "." + curSubid, errObj); 
4229                                 if (!err) {
4230                                     delete _nodesList[node].unsubscribing.reqIds[curSubid];
4231                                     _nodesList[node].unsubscribing.length -= 1;
4232                                 } else { // Just remove the subid from unsubscribing; the next subscribe request will operate with node already created
4233                                     delete _nodesList[node].unsubscribing.reqIds[curSubid];
4234                                     _nodesList[node].unsubscribing.length -= 1;
4235                                 }   
4236                             }
4237                         }
4238                         
4239                         if (!err && _nodesList[node].holding.length > 0) { // if any subscribe requests came in while unsubscribing from OF, now transition from holding to subscribing
4240                             for (curSubid in _nodesList[node].holding.reqIds) {
4241                                 if (_nodesList[node].holding.reqIds.hasOwnProperty(curSubid)) {
4242                                     delete _nodesList[node].holding.reqIds[curSubid];
4243                                     _nodesList[node].holding.length -= 1;
4244                                     _subscribeNode(node, curSubid);                             
4245                                 }
4246                             }
4247                         }
4248                     });
4249                 } else { // length <= 0?
4250                     _hub.publish(_topics.RESPONSES + "." + subid, undefined);
4251                 }
4252             }
4253         },
4254         
4255         /**
4256          * Handles client requests to establish a BOSH connection.
4257          * @param {String} id
4258          *     id of the xmpp user
4259          * @param {String} password
4260          *     password of the xmpp user
4261          * @param {String} xmppDomain
4262          *     xmppDomain of the xmpp user account
4263          * @private
4264          */
4265         _connect = function (id, password, xmppDomain) {
4266             _tunnel.makeConnectReq(id, password, xmppDomain);
4267         },
4268 
4269         /**
4270          * Handles client requests made to the request topic. The type of the
4271          * request is described in the "type" property within the data payload. Each
4272          * type can result in a different operation.
4273          * @param {String} topic
4274          *     The topic which data was published to.
4275          * @param {Object} data
4276          *     The data containing requests information published by clients.
4277          * @param {String} data.type
4278          *     The type of the request. Supported: "ConnectionInfoReq"
4279          * @param {Object} data.data
4280          *     May contain data relevant for the particular requests.
4281          * @param {String} [data.invokeID]
4282          *     The ID used to identify the request with the response. The invoke ID
4283          *     will be included in the data in the publish to the topic. It is the
4284          *     responsibility of the client to correlate the published data to the
4285          *     request made by using the invoke ID.
4286          * @private
4287          */
4288         _clientRequestHandler = function (topic, data) {
4289             var dataCopy;
4290 
4291             //Ensure a valid data object with "type" and "data" properties.
4292             if (typeof data === "object" &&
4293                     typeof data.type === "string" &&
4294                     typeof data.data === "object") {
4295                 switch (data.type) {
4296                 case _REQTYPES.CONNECTIONINFO:
4297                     //It is possible that Slave clients come up before the Master
4298                     //client. If that is the case, the Slaves will need to make a
4299                     //request for the Master to send the latest connection info to the
4300                     //connectionInfo topic.
4301                     dataCopy = _cloneConnInfoObj();
4302                     if (dataCopy) {
4303                         if (data.invokeID !== undefined) {
4304                             dataCopy.invokeID = data.invokeID;
4305                         }
4306                         _hub.publish(_topics.EVENTS_CONNECTION_INFO, dataCopy);
4307                     }
4308                     break;
4309                 case _REQTYPES.SUBSCRIBE:
4310                     if (typeof data.data.node === "string") {
4311                         _subscribeNode(data.data.node, data.invokeID);
4312                     }
4313                     break;
4314                 case _REQTYPES.UNSUBSCRIBE:
4315                     if (typeof data.data.node === "string") {
4316                         _unsubscribeNode(data.data.node, data.invokeID);
4317                     }
4318                     break;
4319                 case _REQTYPES.CONNECT:
4320                     // Deprecated: Disallow others (non-masters) from administering BOSH connections that are not theirs
4321                     _logger.log("MasterPublisher._clientRequestHandler(): F403: Access denied. Non-master ClientService instances do not have the clearance level to make this request: makeConnectionReq");
4322                     break;
4323                 default:
4324                     break;
4325                 }
4326             }
4327         };
4328 
4329         (function () {
4330             //Register to receive events and connection status from tunnel.
4331             _tunnel.registerEventHandler(_eventHandler);
4332             _tunnel.registerPresenceHandler(_presenceHandler);
4333             _tunnel.registerConnectionInfoHandler(_connInfoHandler);
4334 
4335             //Listen to a request channel to respond to any requests made by other
4336             //clients because the Master may have access to useful information.
4337             _hub.subscribe(_topics.REQUESTS, _clientRequestHandler);
4338         }());
4339 
4340         /**
4341          * @private
4342          * Handles client requests to establish a BOSH connection.
4343          * @param {String} id
4344          *     id of the xmpp user
4345          * @param {String} password
4346          *     password of the xmpp user
4347          * @param {String} xmppDomain
4348          *     xmppDomain of the xmpp user account
4349          */
4350         this.connect = function (id, password, xmppDomain) {
4351           _connect(id, password, xmppDomain);
4352         };
4353 
4354         /**
4355          * @private
4356          * Resets the list of explicit subscriptions
4357          */
4358         this.wipeout = function () {
4359             _cleanupPendingRequests();
4360             _nodesList = {};
4361        };
4362 
4363         //BEGIN TEST CODE//
4364        /**
4365         * Test code added to expose private functions that are used by unit test
4366         * framework. This section of code is removed during the build process
4367         * before packaging production code. The [begin|end]TestSection are used
4368         * by the build to identify the section to strip.
4369         * @ignore
4370         */
4371        this.beginTestSection = 0;
4372 
4373        /**
4374         * @ignore
4375         */
4376        this.getTestObject = function () {
4377            //Load mock dependencies.
4378            var _mock = new MockControl();
4379            _hub = _mock.createMock(gadgets.Hub);
4380            _tunnel = _mock.createMock();
4381 
4382            return {
4383                //Expose mock dependencies
4384                mock: _mock,
4385                hub: _hub,
4386                tunnel: _tunnel,
4387                setTunnel: function (tunnel) {
4388                    _tunnel = tunnel;
4389                },
4390                getTunnel: function () {
4391                    return _tunnel;
4392                },
4393 
4394                //Expose internal private functions
4395                reqtypes: _REQTYPES,
4396                eventHandler: _eventHandler,
4397                presenceHandler: _presenceHandler,
4398                
4399                subscribeNode: _subscribeNode,
4400                unsubscribeNode: _unsubscribeNode,
4401                
4402                getNodeList: function () {
4403                    return _nodesList;
4404                },
4405                setNodeList: function (nodelist) {
4406                    _nodesList = nodelist;
4407                },
4408                
4409                cloneConnInfoObj: _cloneConnInfoObj,
4410                connInfoHandler: _connInfoHandler,
4411                clientRequestHandler: _clientRequestHandler
4412 
4413            };
4414        };
4415 
4416 
4417        /**
4418         * @ignore
4419         */
4420        this.endTestSection = 0;
4421        //END TEST CODE//
4422 
4423     };
4424     
4425     window.finesse = window.finesse || {};
4426     window.finesse.clientservices = window.finesse.clientservices || {};
4427     window.finesse.clientservices.MasterPublisher = MasterPublisher;
4428 
4429     return MasterPublisher;
4430 });
4431 
4432 /** The following comment is to prevent jslint errors about 
4433  * using variables before they are defined.
4434  */
4435 /*global publisher:true */
4436 
4437 /**
4438  * Exposes a set of API wrappers that will hide the dirty work of
4439  *     constructing Finesse API requests and consuming Finesse events.
4440  *
4441  * @requires OpenAjax, jQuery 1.5, finesse.utilities.Utilities
4442  */
4443 
4444 
4445 /**
4446  * Allow clients to make Finesse API requests and consume Finesse events by
4447  * calling a set of exposed functions. The Services layer will do the dirty
4448  * work of establishing a shared BOSH connection (for designated Master
4449  * modules), consuming events for client subscriptions, and constructing API
4450  * requests.
4451  */
4452 /** @private */
4453 define('clientservices/ClientServices',[
4454     "clientservices/MasterTunnel",
4455     "clientservices/MasterPublisher",
4456     "clientservices/Topics",
4457     "utilities/Utilities"
4458 ],
4459 function (MasterTunnel, MasterPublisher, Topics, Utilities) {
4460     
4461      var ClientServices = (function () {/** @lends finesse.clientservices.ClientServices.prototype */
4462         var
4463 
4464         /**
4465          * Shortcut reference to the master tunnel
4466          * @private
4467          */
4468         _tunnel,
4469 
4470         _publisher,
4471 
4472         /**
4473          * Shortcut reference to the finesse.utilities.Utilities singleton
4474          * This will be set by init()
4475          * @private
4476          */
4477         _util,
4478 
4479         /**
4480          * Shortcut reference to the gadgets.io object.
4481          * This will be set by init()
4482          * @private
4483          */
4484         _io,
4485 
4486         /**
4487          * Shortcut reference to the gadget pubsub Hub instance.
4488          * This will be set by init()
4489          * @private
4490          */
4491         _hub,
4492 
4493         /**
4494          * Logger object set externally by setLogger, defaults to nothing.
4495          * @private
4496          */
4497         _logger = {},
4498 
4499         /**
4500          * Shortcut reference to the Topics class.
4501          * This will be set by init()
4502          * @private
4503          */
4504         _topics,
4505 
4506         /**
4507          * Config object needed to initialize this library
4508          * This must be set by init()
4509          * @private
4510          */
4511         _config,
4512 
4513         /**
4514          * @private
4515          * Whether or not this ClientService instance is a Master.
4516          */
4517         _isMaster = false,
4518 
4519         /**
4520          * @private
4521          * Whether the Client Services have been initiated yet.
4522          */
4523         _inited = false,
4524 
4525         /**
4526          * Stores the list of subscription IDs for all subscriptions so that it
4527          * could be retrieve for unsubscriptions.
4528          * @private
4529          */
4530         _subscriptionID = {},
4531 
4532         /**
4533          * The possible states of the JabberWerx BOSH connection.
4534          * @private
4535          */
4536         _STATUS = {
4537             CONNECTING: "connecting",
4538             CONNECTED: "connected",
4539             DISCONNECTED: "disconnected",
4540             DISCONNECTED_CONFLICT: "conflict",
4541             DISCONNECTED_UNAUTHORIZED: "unauthorized",
4542             DISCONNECTING: "disconnecting",
4543             RECONNECTING: "reconnecting",
4544             UNLOADING: "unloading",
4545             FAILING: "failing",
4546             RECOVERED: "recovered"
4547         },
4548 
4549         _failoverMode = false,
4550 
4551         /**
4552          * Handler function to be invoked when BOSH connection is connecting.
4553          * @private
4554          */
4555         _onConnectingHandler,
4556 
4557         /**
4558          * Handler function to be invoked when BOSH connection is connected
4559          * @private
4560          */
4561         _onConnectHandler,
4562 
4563         /**
4564          * Handler function to be invoked when BOSH connection is disconnecting.
4565          * @private
4566          */
4567         _onDisconnectingHandler,
4568 
4569         /**
4570          * Handler function to be invoked when the BOSH is disconnected.
4571          * @private
4572          */
4573         _onDisconnectHandler,
4574 
4575         /**
4576          * Handler function to be invoked when the BOSH is reconnecting.
4577          * @private
4578          */
4579         _onReconnectingHandler,
4580         
4581         /**
4582          * Handler function to be invoked when the BOSH is unloading.
4583          * @private
4584          */
4585         _onUnloadingHandler,
4586 
4587         /**
4588          * Contains a cache of the latest connection info containing the current
4589          * state of the BOSH connection and the resource ID.
4590          * @private
4591          */
4592         _connInfo,
4593 
4594         /**
4595          * Keeps track of all the objects that need to be refreshed when we recover
4596          * due to our resilient connection. Only objects that we subscribe to will
4597          * be added to this list.
4598          * @private
4599          */
4600         _refreshList = [],
4601 
4602         /**
4603          * @private
4604          * Centralized logger.log method for external logger
4605          * @param {String} msg
4606          *     Message to log
4607          */
4608         _log = function (msg) {
4609             // If the external logger throws up, it stops here.
4610             try {
4611                 if (_logger.log) {
4612                     _logger.log("[ClientServices] " + msg);
4613                 }
4614             } catch (e) { }
4615         },
4616 
4617         /**
4618          * Go through each object in the _refreshList and call its refresh() function
4619          * @private
4620          */
4621         _refreshObjects = function () {
4622             var i;
4623 
4624             // wipe out the explicit subscription list before we refresh objects
4625             if (_publisher) {
4626                 _publisher.wipeout();
4627             }
4628 
4629             // refresh each item in the refresh list
4630             for (i = _refreshList.length - 1; i >= 0; i -= 1) {
4631                 _log("Refreshing " + _refreshList[i].getRestUrl());
4632                 _refreshList[i].refresh(10);
4633             }
4634         },
4635 
4636         /**
4637          * Handler to process connection info publishes.
4638          * @param {Object} data
4639          *     The connection info data object.
4640          * @param {String} data.status
4641          *     The BOSH connection status.
4642          * @param {String} data.resourceID
4643          *     The resource ID for the connection.
4644          * @private
4645          */
4646         _connInfoHandler =  function (data) {
4647 
4648             //Invoke registered handler depending on status received. Due to the
4649             //request topic where clients can make request for the Master to publish
4650             //the connection info, there is a chance that duplicate connection info
4651             //events may be sent, so ensure that there has been a state change
4652             //before invoking the handlers.
4653             if (_connInfo === undefined || _connInfo.status !== data.status) {
4654                 _connInfo = data;
4655                 switch (data.status) {
4656                 case _STATUS.CONNECTING:
4657                     if (_isMaster && _onConnectingHandler) {
4658                         _onConnectingHandler();
4659                     }
4660                     break;
4661                 case _STATUS.CONNECTED:
4662                     if ((_isMaster || !_failoverMode) && _onConnectHandler) {
4663                         _onConnectHandler();
4664                     }
4665                     break;
4666                 case _STATUS.DISCONNECTED:
4667                     if (_isMaster && _onDisconnectHandler) {
4668                         _onDisconnectHandler();
4669                     }
4670                     break;
4671                 case _STATUS.DISCONNECTED_CONFLICT:
4672                     if (_isMaster && _onDisconnectHandler) {
4673                         _onDisconnectHandler("conflict");
4674                     }
4675                     break;
4676                 case _STATUS.DISCONNECTED_UNAUTHORIZED:
4677                     if (_isMaster && _onDisconnectHandler) {
4678                         _onDisconnectHandler("unauthorized");
4679                     }
4680                     break;
4681                 case _STATUS.DISCONNECTING:
4682                     if (_isMaster && _onDisconnectingHandler) {
4683                         _onDisconnectingHandler();
4684                     }
4685                     break;
4686                 case _STATUS.RECONNECTING:
4687                     if (_isMaster && _onReconnectingHandler) {
4688                         _onReconnectingHandler();
4689                     }
4690                     break;
4691                 case _STATUS.UNLOADING:
4692                     if (_isMaster && _onUnloadingHandler) {
4693                         _onUnloadingHandler();
4694                     }
4695                     break;
4696                 case _STATUS.FAILING:
4697                     if (!_isMaster) {
4698                         // Stop
4699                         _failoverMode = true;
4700                         if (_onDisconnectHandler) {
4701                             _onDisconnectHandler();
4702                         }
4703                     }
4704                     break;
4705                 case _STATUS.RECOVERED:
4706                     if (!_isMaster) {
4707                         _failoverMode = false;
4708                         if (_onConnectHandler) {
4709                             _onConnectHandler();
4710                         }
4711                     }
4712                     // Whenever we are recovered, we need to refresh any objects
4713                     // that are stored.
4714                     _refreshObjects();
4715                     break;
4716                 }
4717             }
4718         },
4719 
4720         /**
4721          * Ensure that ClientServices have been inited.
4722          * @private
4723          */
4724         _isInited = function () {
4725             if (!_inited) {
4726                 throw new Error("ClientServices needs to be inited.");
4727             }
4728         },
4729 
4730         /**
4731          * Have the client become the Master by initiating a tunnel to a shared
4732          * event BOSH connection. The Master is responsible for publishing all
4733          * events to the pubsub infrastructure.
4734          * @private
4735          */
4736         _becomeMaster = function () {
4737             _tunnel = new MasterTunnel(_config.host, _config.scheme);
4738             _publisher = new MasterPublisher(_tunnel, _hub);
4739             _tunnel.init(_config.id, _config.password, _config.xmppDomain, _config.pubsubDomain, _config.resource);
4740             _isMaster = true;
4741         },
4742 
4743         /**
4744          * Make a request to the request channel to have the Master publish the
4745          * connection info object.
4746          * @private
4747          */
4748         _makeConnectionInfoReq = function () {
4749             var data = {
4750                 type: "ConnectionInfoReq",
4751                 data: {},
4752                 invokeID: (new Date()).getTime()
4753             };
4754             _hub.publish(_topics.REQUESTS, data);
4755         },
4756 
4757         /**
4758          * Utility method to register a handler which is associated with a
4759          * particular connection status.
4760          * @param {String} status
4761          *     The connection status string.
4762          * @param {Function} handler
4763          *     The handler to associate with a particular connection status.
4764          * @throws {Error}
4765          *     If the handler provided is not a function.
4766          * @private
4767          */
4768         _registerHandler = function (status, handler) {
4769             if (typeof handler === "function") {
4770                 if (_connInfo && _connInfo.status === status) {
4771                     handler();
4772                 }
4773                 switch (status) {
4774                 case _STATUS.CONNECTING:
4775                     _onConnectingHandler = handler;
4776                     break;
4777                 case _STATUS.CONNECTED:
4778                     _onConnectHandler = handler;
4779                     break;
4780                 case _STATUS.DISCONNECTED:
4781                     _onDisconnectHandler = handler;
4782                     break;
4783                 case _STATUS.DISCONNECTING:
4784                     _onDisconnectingHandler = handler;
4785                     break;
4786                 case _STATUS.RECONNECTING:
4787                     _onReconnectingHandler = handler;
4788                     break;
4789                 case _STATUS.UNLOADING:
4790                     _onUnloadingHandler = handler;
4791                     break;
4792                 }
4793 
4794             } else {
4795                 throw new Error("Callback is not a function");
4796             }
4797         };
4798 
4799         return {
4800 
4801             /**
4802              * @private
4803              * Adds an item to the list to be refreshed upon reconnect
4804              * @param {RestBase} object - rest object to be refreshed
4805              */
4806             addToRefreshList: function (object) {
4807                 _refreshList.push(object);
4808             },
4809 
4810             /**
4811              * @private
4812              * Removes the given item from the refresh list
4813              * @param  {RestBase} object - rest object to be removed
4814              */
4815             removeFromRefreshList: function (object) {
4816                 var i;
4817                 for (i = _refreshList.length - 1; i >= 0; i -= 1) {
4818                     if (_refreshList[i] === object) {
4819                         _refreshList.splice(i, 1);
4820                         break;
4821                     }
4822                 }
4823             },
4824 
4825             /**
4826              * @private
4827              * The location of the tunnel HTML URL.
4828              * @returns {String}
4829              *     The location of the tunnel HTML URL.
4830              */
4831             getTunnelURL: function () {
4832                 return _tunnel.getTunnelURL();            
4833             },
4834             
4835             /**
4836              * @private
4837              * Indicates whether the tunnel frame is loaded.
4838              * @returns {Boolean}
4839              *     True if the tunnel frame is loaded, false otherwise.
4840              */
4841             isTunnelLoaded: function () {
4842                 return _tunnel.isTunnelLoaded();            
4843             },
4844             
4845             /**
4846              * @private
4847              * Indicates whether the ClientServices instance is a Master.
4848              * @returns {Boolean}
4849              *     True if this instance of ClientServices is a Master, false otherwise.
4850              */
4851             isMaster: function () {
4852                 return _isMaster;
4853             },
4854 
4855             /**
4856              * @private
4857              * Get the resource ID. An ID is only available if the BOSH connection has
4858              * been able to connect successfully.
4859              * @returns {String}
4860              *     The resource ID string. Null if the BOSH connection was never
4861              *     successfully created and/or the resource ID has not been associated.
4862              */
4863             getResourceID: function () {
4864                 if (_connInfo !== undefined) {
4865                     return _connInfo.resourceID;
4866                 }
4867                 return null;
4868             },
4869             
4870             /*
4871             getHub: function () {
4872                 return _hub;
4873             },
4874         */
4875             /**
4876              * @private
4877              * Add a callback to be invoked when the BOSH connection is attempting
4878              * to connect. If the connection is already trying to connect, the
4879              * callback will be invoked immediately.
4880              * @param {Function} handler
4881              *      An empty param function to be invoked on connecting. Only one
4882              *      handler can be registered at a time. Handlers already registered
4883              *      will be overwritten.
4884              */
4885             registerOnConnectingHandler: function (handler) {
4886                 _registerHandler(_STATUS.CONNECTING, handler);
4887             },
4888 
4889             /**
4890              * @private
4891              * Removes the on connecting callback that was registered.
4892              */
4893             unregisterOnConnectingHandler: function () {
4894                 _onConnectingHandler = undefined;
4895             },
4896 
4897             /**
4898              * Add a callback to be invoked when all of the following conditions are met:
4899              * <ul>
4900              *   <li>When Finesse goes IN_SERVICE</li>
4901              *   <li>The BOSH connection is established</li>
4902              *   <li>The Finesse user presence becomes available</li>
4903              * </ul>
4904              * If all these conditions are met at the time this function is called, then
4905              * the handler will be invoked immediately.
4906              * @param {Function} handler
4907              *      An empty param function to be invoked on connect. Only one handler
4908              *      can be registered at a time. Handlers already registered will be
4909              *      overwritten.
4910              * @example
4911              *      finesse.clientservices.ClientServices.registerOnConnectHandler(gadget.myCallback);
4912              */
4913             registerOnConnectHandler: function (handler) {
4914                 _registerHandler(_STATUS.CONNECTED, handler);
4915             },
4916 
4917             /**
4918              * @private
4919              * Removes the on connect callback that was registered.
4920              */
4921             unregisterOnConnectHandler: function () {
4922                 _onConnectHandler = undefined;
4923             },
4924 
4925             /**
4926              * Add a callback to be invoked when any of the following occurs:
4927              * <ul>
4928              *   <li>Finesse is no longer IN_SERVICE</li>
4929              *   <li>The BOSH connection is lost</li>
4930              *   <li>The presence of the Finesse user is no longer available</li>
4931              * </ul>
4932              * If any of these conditions are met at the time this function is
4933              * called, the callback will be invoked immediately.
4934              * @param {Function} handler
4935              *      An empty param function to be invoked on disconnected. Only one
4936              *      handler can be registered at a time. Handlers already registered
4937              *      will be overwritten.
4938              * @example
4939              *      finesse.clientservices.ClientServices.registerOnDisconnectHandler(gadget.myCallback);
4940              */
4941             registerOnDisconnectHandler: function (handler) {
4942                 _registerHandler(_STATUS.DISCONNECTED, handler);
4943             },
4944 
4945             /**
4946              * @private
4947              * Removes the on disconnect callback that was registered.
4948              */
4949             unregisterOnDisconnectHandler: function () {
4950                 _onDisconnectHandler = undefined;
4951             },
4952 
4953             /**
4954              * @private
4955              * Add a callback to be invoked when the BOSH is currently disconnecting. If
4956              * the connection is already disconnecting, invoke the callback immediately.
4957              * @param {Function} handler
4958              *      An empty param function to be invoked on disconnected. Only one
4959              *      handler can be registered at a time. Handlers already registered
4960              *      will be overwritten.
4961              */
4962             registerOnDisconnectingHandler: function (handler) {
4963                 _registerHandler(_STATUS.DISCONNECTING, handler);
4964             },
4965 
4966             /**
4967              * @private
4968              * Removes the on disconnecting callback that was registered.
4969              */
4970             unregisterOnDisconnectingHandler: function () {
4971                 _onDisconnectingHandler = undefined;
4972             },
4973 
4974             /**
4975              * @private
4976              * Add a callback to be invoked when the BOSH connection is attempting
4977              * to connect. If the connection is already trying to connect, the
4978              * callback will be invoked immediately.
4979              * @param {Function} handler
4980              *      An empty param function to be invoked on connecting. Only one
4981              *      handler can be registered at a time. Handlers already registered
4982              *      will be overwritten.
4983              */
4984             registerOnReconnectingHandler: function (handler) {
4985                 _registerHandler(_STATUS.RECONNECTING, handler);
4986             },
4987 
4988             /**
4989              * @private
4990              * Removes the on reconnecting callback that was registered.
4991              */
4992             unregisterOnReconnectingHandler: function () {
4993                 _onReconnectingHandler = undefined;
4994             },
4995             
4996             /**
4997              * @private
4998              * Add a callback to be invoked when the BOSH connection is unloading
4999              * 
5000              * @param {Function} handler
5001              *      An empty param function to be invoked on connecting. Only one
5002              *      handler can be registered at a time. Handlers already registered
5003              *      will be overwritten.
5004              */
5005             registerOnUnloadingHandler: function (handler) {
5006                 _registerHandler(_STATUS.UNLOADING, handler);
5007             },
5008             
5009             /**
5010              * @private
5011              * Removes the on unloading callback that was registered.
5012              */
5013             unregisterOnUnloadingHandler: function () {
5014                 _onUnloadingHandler = undefined;
5015             },
5016 
5017             /**
5018              * @private
5019              * Proxy method for gadgets.io.makeRequest. The will be identical to gadgets.io.makeRequest
5020              * ClientServices will mixin the BASIC Auth string, locale, and host, since the
5021              * configuration is encapsulated in here anyways.
5022              * This removes the dependency
5023              * @param {String} url
5024              *     The relative url to make the request to (the host from the passed in config will be
5025              *     appended). It is expected that any encoding to the URL is already done.
5026              * @param {Function} handler
5027              *     Callback handler for makeRequest to invoke when the response returns.
5028              *     Completely passed through to gadgets.io.makeRequest
5029              * @param {Object} params
5030              *     The params object that gadgets.io.makeRequest expects. Authorization and locale
5031              *     headers are mixed in.
5032              */
5033             makeRequest: function (url, handler, params) {
5034                 var requestedScheme, scheme = "http";
5035                 
5036                 // ClientServices needs to be initialized with a config for restHost, auth, and locale
5037                 _isInited();
5038                 
5039                 // Allow mixin of auth and locale headers
5040                 params = params || {};
5041 
5042                 // Override refresh interval to 0 instead of default 3600 as a way to workaround makeRequest 
5043                 // using GET http method because then the params are added to the url as query params, which 
5044                 // exposes the authorization string in the url. This is a placeholder until oauth comes in
5045                 params[gadgets.io.RequestParameters.REFRESH_INTERVAL] = params[gadgets.io.RequestParameters.REFRESH_INTERVAL] || 0;
5046                 
5047                 params[gadgets.io.RequestParameters.HEADERS] = params[gadgets.io.RequestParameters.HEADERS] || {};
5048                 
5049                 // Add Basic auth to request header
5050                 params[gadgets.io.RequestParameters.HEADERS].Authorization = "Basic " + _config.authorization;
5051                 //Locale
5052                 params[gadgets.io.RequestParameters.HEADERS].locale = _config.locale;
5053 
5054                 //Allow clients to override the scheme:
5055                 //  - If not specified  => we use HTTP
5056                 //  - If null specified => we use _config.scheme
5057                 //  - Otherwise         => we use whatever they provide
5058                 requestedScheme = params.SCHEME; 
5059                 if (!(requestedScheme === undefined || requestedScheme === "undefined")) {
5060                     if (requestedScheme === null) {
5061                        scheme = _config.scheme;
5062                     } else {
5063                        scheme = requestedScheme;
5064                     }
5065                 }
5066                 scheme = _config.restScheme || scheme;
5067                 
5068                 _log("RequestedScheme: " + requestedScheme + "; Scheme: " + scheme);
5069                 gadgets.io.makeRequest(encodeURI(scheme + "://" + _config.restHost + ":" + _config.localhostPort) + url, handler, params);
5070             },
5071 
5072             /**
5073              * @private
5074              * Utility function to make a subscription to a particular topic. Only one
5075              * callback function is registered to a particular topic at any time.
5076              * @param {String} topic
5077              *     The full topic name. The topic name should follow the OpenAjax
5078              *     convention using dot notation (ex: finesse.api.User.1000).
5079              * @param {Function} callback
5080              *     The function that should be invoked with the data when an event
5081              *     is delivered to the specific topic.
5082              * @returns {Boolean}
5083              *     True if the subscription was made successfully and the callback was
5084              *     been registered. False if the subscription already exist, the
5085              *     callback was not overwritten.
5086              */
5087             subscribe: function (topic, callback, disableDuringFailover) {
5088                 _isInited();
5089 
5090                 //Ensure that the same subscription isn't made twice.
5091                 if (!_subscriptionID[topic]) {
5092                     //Store the subscription ID using the topic name as the key.
5093                     _subscriptionID[topic] = _hub.subscribe(topic,
5094                         //Invoke the callback just with the data object.
5095                         function (topic, data) {
5096                             if (!disableDuringFailover || _isMaster || !_failoverMode) {
5097                                 // Halt all intermediate event processing while the master instance attempts to rebuild the connection. This only occurs:
5098                                 // - For RestBase objects (which pass the disableDuringFailover flag), so that intermediary events while recovering are hidden away until everything is all good
5099                                 //    - We shouldn't be halting anything else because we have infrastructure requests that use the hub pub sub
5100                                 // - Master instance will get all events regardless, because it is responsible for managing failover
5101                                 // - If we are not in a failover mode, everything goes
5102                                 // _refreshObjects will reconcile anything that was missed once we are back in action
5103                                 callback(data);
5104                             } 
5105                         });
5106                     return true;
5107                 }
5108                 return false;
5109             },
5110 
5111             /**
5112              * @private
5113              * Unsubscribe from a particular topic.
5114              * @param {String} topic
5115              *     The full topic name.
5116              */
5117             unsubscribe: function (topic) {
5118                 _isInited();
5119 
5120                 //Unsubscribe from the topic using the subscription ID recorded when
5121                 //the subscription was made, then delete the ID from data structure.
5122                 if (_subscriptionID[topic]) {
5123                     _hub.unsubscribe(_subscriptionID[topic]);
5124                     delete _subscriptionID[topic];
5125                 }
5126             },
5127 
5128             /**
5129              * @private
5130              * Make a request to the request channel to have the Master subscribe
5131              * to a node.
5132              * @param {String} node
5133              *     The node to subscribe to.
5134              */
5135             subscribeNode: function (node, handler) {
5136                 if (handler && typeof handler !== "function") {
5137                     throw new Error("ClientServices.subscribeNode: handler is not a function");
5138                 }
5139                 
5140                 // Construct the request to send to MasterPublisher through the OpenAjax Hub
5141                 var data = {
5142                     type: "SubscribeNodeReq",
5143                     data: {node: node},
5144                     invokeID: _util.generateUUID()
5145                 },
5146                 responseTopic = _topics.RESPONSES + "." + data.invokeID,
5147                 _this = this;
5148 
5149                 // We need to first subscribe to the response channel
5150                 this.subscribe(responseTopic, function (rsp) {
5151                     // Since this channel is only used for this singular request,
5152                     // we are not interested anymore.
5153                     // This is also critical to not leaking memory by having OpenAjax
5154                     // store a bunch of orphaned callback handlers that enclose on
5155                     // our entire ClientServices singleton
5156                     _this.unsubscribe(responseTopic);
5157                     if (handler) {
5158                         handler(data.invokeID, rsp);
5159                     }
5160                 });
5161                 // Then publish the request on the request channel
5162                 _hub.publish(_topics.REQUESTS, data);
5163             },
5164 
5165             /**
5166              * @private
5167              * Make a request to the request channel to have the Master unsubscribe
5168              * from a node.
5169              * @param {String} node
5170              *     The node to unsubscribe from.
5171              */
5172             unsubscribeNode: function (node, subid, handler) {
5173                 if (handler && typeof handler !== "function") {
5174                     throw new Error("ClientServices.unsubscribeNode: handler is not a function");
5175                 }
5176                 
5177                 // Construct the request to send to MasterPublisher through the OpenAjax Hub
5178                 var data = {
5179                     type: "UnsubscribeNodeReq",
5180                     data: {
5181                         node: node,
5182                         subid: subid
5183                     },
5184                     invokeID: _util.generateUUID()
5185                 },
5186                 responseTopic = _topics.RESPONSES + "." + data.invokeID,
5187                 _this = this;
5188 
5189                 // We need to first subscribe to the response channel
5190                 this.subscribe(responseTopic, function (rsp) {
5191                     // Since this channel is only used for this singular request,
5192                     // we are not interested anymore.
5193                     // This is also critical to not leaking memory by having OpenAjax
5194                     // store a bunch of orphaned callback handlers that enclose on
5195                     // our entire ClientServices singleton
5196                     _this.unsubscribe(responseTopic);
5197                     if (handler) {
5198                         handler(rsp);
5199                     }
5200                 });
5201                 // Then publish the request on the request channel
5202                 _hub.publish(_topics.REQUESTS, data);
5203             },
5204             
5205             /**
5206              * @private
5207              * Make a request to the request channel to have the Master connect to the XMPP server via BOSH
5208              */
5209             makeConnectionReq : function () {
5210                 // Disallow others (non-masters) from administering BOSH connections that are not theirs
5211                 if (_isMaster && _publisher) {
5212                     _publisher.connect(_config.id, _config.password, _config.xmppDomain);
5213                 } else {
5214                     _log("F403: Access denied. Non-master ClientService instances do not have the clearance level to make this request: makeConnectionReq");
5215                 }
5216             },
5217         
5218             /**
5219              * @private
5220              * Set's the global logger for this Client Services instance.
5221              * @param {Object} logger
5222              *     Logger object with the following attributes defined:<ul>
5223              *         <li><b>log:</b> function (msg) to simply log a message
5224              *     </ul>
5225              */
5226             setLogger: function (logger) {
5227                 // We want to check the logger coming in so we don't have to check every time it is called.
5228                 if (logger && typeof logger === "object" && typeof logger.log === "function") {
5229                     _logger = logger;
5230                 } else {
5231                     // We are resetting it to an empty object so that _logger.log in .log is falsy.
5232                     _logger = {};
5233                 }
5234             },
5235             
5236             /**
5237              * @private
5238              * Centralized logger.log method for external logger
5239              * @param {String} msg
5240              *     Message to log
5241              */
5242             log: _log,
5243 
5244             /**
5245              * @class
5246              * Allow clients to make Finesse API requests and consume Finesse events by
5247              * calling a set of exposed functions. The Services layer will do the dirty
5248              * work of establishing a shared BOSH connection (for designated Master
5249              * modules), consuming events for client subscriptions, and constructing API
5250              * requests.
5251              * 
5252              * @constructs
5253              */
5254             _fakeConstuctor: function () {
5255                 /* This is here so we can document init() as a method rather than as a constructor. */
5256             },
5257             
5258             /**
5259              * Initiates the Client Services with the specified config parameters.
5260              * Enabling the Client Services as Master will trigger the establishment
5261              * of a BOSH event connection.
5262              * @param {finesse.gadget.Config} config
5263              *     Configuration object containing properties used for making REST requests:<ul>
5264              *         <li><b>host:</b> The Finesse server IP/host as reachable from the browser
5265              *         <li><b>restHost:</b> The Finesse API IP/host as reachable from the gadget container
5266              *         <li><b>id:</b> The ID of the user. This is an optional param as long as the
5267              *         appropriate authorization string is provided, otherwise it is
5268              *         required.</li>
5269              *         <li><b>password:</b> The password belonging to the user. This is an optional param as
5270              *         long as the appropriate authorization string is provided,
5271              *         otherwise it is required.</li>
5272              *         <li><b>authorization:</b> The base64 encoded "id:password" authentication string. This
5273              *         param is provided to allow the ability to hide the password
5274              *         param. If provided, the id and the password extracted from this
5275              *         string will be used over the config.id and config.password.</li>
5276              *     </ul>
5277              * @throws {Error} If required constructor parameter is missing.
5278              * @example
5279              *      finesse.clientservices.ClientServices.init(finesse.gadget.Config);
5280              */
5281             init: function (config) {
5282                 if (!_inited) {
5283                     //Validate the properties within the config object if one is provided.
5284                     if (!(typeof config === "object" &&
5285                          typeof config.host === "string" && config.host.length > 0 && config.restHost && 
5286                          (typeof config.authorization === "string" ||
5287                                  (typeof config.id === "string" &&
5288                                          typeof config.password === "string")))) {
5289                         throw new Error("Config object contains invalid properties.");
5290                     }
5291 
5292                     // Initialize configuration
5293                     _config = config;
5294 
5295                     // Set shortcuts
5296                     _util = Utilities;
5297                     _topics = Topics;
5298                     
5299                     //TODO: document when this is properly supported
5300                     // Allows hub and io dependencies to be passed in. Currently only used for unit tests.
5301                     _hub = config.hub || gadgets.Hub;
5302                     _io = config.io || gadgets.io;
5303 
5304                     //If the authorization string is provided, then use that to
5305                     //extract the ID and the password. Otherwise use the ID and
5306                     //password from the respective ID and password params.
5307                     if (_config.authorization) {
5308                         var creds = _util.getCredentials(_config.authorization);
5309                         _config.id = creds.id;
5310                         _config.password = creds.password;
5311                     }
5312                     else {
5313                         _config.authorization = _util.b64Encode(
5314                                 _config.id + ":" + _config.password);
5315                     }
5316 
5317                     _inited = true;
5318 
5319                     if (_hub) {
5320                         //Subscribe to receive connection information. Since it is possible that
5321                         //the client comes up after the Master comes up, the client will need
5322                         //to make a request to have the Master send the latest connection info.
5323                         //It would be possible that all clients get connection info again.
5324                         this.subscribe(_topics.EVENTS_CONNECTION_INFO, _connInfoHandler);
5325                         _makeConnectionInfoReq();
5326                     }
5327                 }
5328 
5329                 //Return the CS object for object chaining.
5330                 return this;
5331             },
5332 
5333             /**
5334              * @private
5335              * Initializes the BOSH component of this ClientServices instance. This establishes
5336              * the BOSH connection and will trigger the registered handlers as the connection
5337              * status changes respectively:<ul>
5338              *     <li>registerOnConnectingHandler</li>
5339              *     <li>registerOnConnectHandler</li>
5340              *     <li>registerOnDisconnectHandler</li>
5341              *     <li>registerOnDisconnectingHandler</li>
5342              *     <li>registerOnReconnectingHandler</li>
5343              *     <li>registerOnUnloadingHandler</li>
5344              * <ul>
5345              *
5346              * @param {Object} config
5347              *     An object containing the following (optional) handlers for the request:<ul>
5348              *         <li><b>xmppDomain:</b> {String} The domain of the XMPP server. Available from the SystemInfo object.
5349              *         This is used to construct the JID: user@domain.com</li>
5350              *         <li><b>pubsubDomain:</b> {String} The pub sub domain where the pub sub service is running.
5351              *         Available from the SystemInfo object.
5352              *         This is used for creating or removing subscriptions.</li>
5353              *         <li><b>resource:</b> {String} The resource to connect to the notification server with.</li>
5354              *     </ul>
5355              */
5356             initBosh: function (config) {
5357                 //Validate the properties within the config object if one is provided.
5358                 if (!(typeof config === "object" && typeof config.xmppDomain === "string" && typeof config.pubsubDomain === "string")) {
5359                     throw new Error("Config object contains invalid properties.");
5360                 }
5361                 
5362                 // Mixin the required information for establishing the BOSH connection
5363                 _config.xmppDomain = config.xmppDomain;
5364                 _config.pubsubDomain = config.pubsubDomain;
5365                 _config.resource = config.resource;
5366                 
5367                 //Initiate Master launch sequence
5368                 _becomeMaster(); 
5369             },
5370 
5371             /**
5372              * @private
5373              * Sets the failover mode to either FAILING or RECOVERED. This will only occur in the master instance and is meant to be
5374              * used by a stereotypical failover monitor type module to notify non-master instances (i.e. in gadgets)
5375              * @param {Object} failoverMode
5376              *     true if failing, false or something falsy when recovered
5377              */
5378             setFailoverMode: function (failoverMode) {
5379                 if (_isMaster) {
5380                     _hub.publish(_topics.EVENTS_CONNECTION_INFO, {
5381                         status: (failoverMode ? _STATUS.FAILING : _STATUS.RECOVERED)
5382                     });
5383                 }
5384             },
5385 
5386             /**
5387              * @private
5388              * Private accessor used to inject mocked private dependencies for unit testing
5389              */
5390             _getTestObj: function () {
5391                 return {
5392                     setPublisher: function (publisher) {
5393                         _publisher = publisher;
5394                     }
5395                 };
5396             }
5397         };
5398     }());
5399      
5400     window.finesse = window.finesse || {};
5401     window.finesse.clientservices = window.finesse.clientservices || {};
5402     window.finesse.clientservices.ClientServices = ClientServices;
5403     
5404     return ClientServices;
5405 
5406 });
5407 
5408 /**
5409  * The following comment prevents JSLint errors concerning undefined global variables.
5410  * It tells JSLint that these identifiers are defined elsewhere.
5411  */
5412 /*jslint bitwise:true, browser:true, nomen:true, regexp:true, sloppy:true, white:true */
5413 
5414 /** The following comment is to prevent jslint errors about 
5415  * using variables before they are defined.
5416  */
5417 /*global Handlebars */
5418 
5419 /**
5420  * JavaScript class to implement common notification
5421  *               functionality.
5422  * 
5423  * @requires Class
5424  * @requires finesse.FinesseBase
5425  */
5426 /** @private */
5427 define('restservices/Notifier',[
5428     'FinesseBase',
5429     'clientservices/ClientServices'
5430 ],
5431 function (FinesseBase, ClientServices) {
5432     var Notifier = FinesseBase.extend({
5433 		/**
5434          * Initializes the notifier object.
5435          */
5436         init : function () {
5437             this._super();
5438             this._listenerCallback = [];
5439         },
5440 
5441         /**
5442          * Add a listener.
5443          * 
5444          * @param callback_function
5445          * @param scope
5446          *            is the callback function to add
5447          */
5448         addListener : function (callback_function, scope) {
5449             var len = this._listenerCallback.length, i, cb, add = true;
5450             for (i = 0; i < len; i += 1) {
5451                 cb = this._listenerCallback[i].callback;
5452                 if (cb === callback_function) {
5453                     // this callback already exists
5454                     add = false;
5455                     break;
5456                 }
5457             }
5458             if (add) {
5459                 this._listenerCallback.push({ "callback": this._isAFunction(callback_function), "scope": (scope || window) });
5460             }            
5461         },
5462 
5463         /**
5464          * Remove a listener.
5465          * 
5466          * @param callback_function
5467          *            is the callback function to remove
5468          * @return {Boolean} true if removed
5469          */
5470         removeListener : function (callback_function) {
5471 
5472             var result = false, len = this._listenerCallback.length, i, cb;
5473             for (i = len - 1; i >= 0; i -=1) {
5474                 cb = this._listenerCallback[i].callback;
5475                 if (cb === callback_function) {
5476                     this._listenerCallback[i] = undefined;
5477                     this._listenerCallback.splice(i, 1);
5478                     result = true;
5479                     break;
5480                 }
5481             }
5482             
5483             return result;
5484         },
5485 
5486         /**
5487 	 * Removes all listeners
5488 	 * @return {undefined}
5489 	 */
5490 	reset: function () {
5491 		this._listenerCallback = [];
5492 	},
5493 
5494 	/**
5495          * Notify all listeners.
5496          * 
5497          * @param obj
5498          *            is the object that has changed
5499          */
5500         notifyListeners : function (obj) {
5501             var len = this._listenerCallback.length, i, callbackFunction, scope;
5502 
5503             for (i = 0; i < len; i += 1) {
5504                 // Be sure that one bad callback does not prevent other listeners
5505                 // from receiving.
5506                 try {
5507                     callbackFunction = this._listenerCallback[i].callback;
5508                     scope = this._listenerCallback[i].scope;
5509                     if (typeof callbackFunction === 'function') {
5510                         callbackFunction.call(scope, obj);
5511                     }
5512                 } catch (err) {
5513                     ClientServices.log("Exception caught: " + err);
5514                 }
5515             }
5516         },
5517 
5518         /**
5519          * Gets a copy of the listeners.
5520          * @return changeListenerCopy (array of callbacks)
5521          */
5522         getListeners : function () {
5523             var changeListenerCopy = [], len = this._listenerCallback.length, i;
5524 
5525             for (i = 0; i < len; i += 1) {
5526                 changeListenerCopy.push(this._listenerCallback[i].callback);
5527             }
5528 
5529             return changeListenerCopy;
5530         },
5531         
5532         /**
5533          * Verifies that the handler is function.
5534          * @param handler to verify
5535          * @return the handler 
5536          * @throws Error if not a function
5537          */
5538         _isAFunction : function (handler) {
5539             if (handler === undefined || typeof handler === "function") {
5540                 return handler;
5541             } else {
5542                 throw new Error("handler must be a function");
5543             }
5544         }
5545 	});
5546 	
5547 	window.finesse = window.finesse || {};
5548 	window.finesse.restservices = window.finesse.restservices || {};
5549 	window.finesse.restservices.Notifier = Notifier;
5550 
5551 	/** @namespace JavaScript classes and methods that represent REST objects and collections. */
5552     finesse.restservices = finesse.restservices || {};
5553 	
5554     return Notifier;
5555 });
5556 
5557 /**
5558  * JavaScript base object that all REST objects should inherit
5559  * from because it encapsulates and provides the common functionality that
5560  * all REST objects need.
5561  *
5562  * @requires finesse.clientservices.ClientServices
5563  * @requires Class
5564  */
5565 
5566 /** @private */
5567 define('restservices/RestBase',[
5568     "FinesseBase",
5569     "utilities/Utilities",
5570     "restservices/Notifier",
5571     "clientservices/ClientServices",
5572     "clientservices/Topics"
5573 ],
5574 function (FinesseBase, Utilities, Notifier, ClientServices, Topics) {
5575     
5576     var RestBase = FinesseBase.extend(/** @lends finesse.restservices.RestBase.prototype */{
5577 
5578         doNotLog: false,        
5579 
5580         /**
5581          * Used by _processUpdate() and restRequest().
5582          * Maps requestIds to object-wrapped callbacks passed to restRequest(),
5583          * so that one of the callbacks can be fired when a corresponding event is
5584          * received inside _processUpdate().
5585          * @private
5586          */
5587         _pendingCallbacks: {},
5588         
5589         /**
5590          * Gets the REST class for the current object.  This object throws an
5591          * exception because subtype must implement.
5592          * @throws {Error} because subtype must implement
5593          * @private
5594          */
5595         getRestClass: function () {
5596             throw new Error("getRestClass(): Not implemented in subtype.");
5597         },
5598 
5599         /**
5600          * Gets the REST type for the current object.  This object throws an
5601          * exception because subtype must implement.
5602          * @throws {Error} because subtype must implement.
5603          * @private
5604          */
5605         getRestType: function () {
5606             throw new Error("getRestType(): Not implemented in subtype.");
5607         },
5608 
5609         /**
5610          * Gets the node path for the current object.  This object throws an
5611          * exception because subtype must implement.
5612          * @throws {Error} because subtype must implement.
5613          * @private
5614          */
5615         getXMPPNodePath: function () {
5616             throw new Error("getXMPPNodePath(): Not implemented in subtype.");
5617         },
5618         
5619         /**
5620          * Boolean function that specifies whether the REST object supports
5621          * requests. True by default. Subclasses should override if false.
5622          * @private
5623          */
5624         supportsRequests: true,
5625 
5626         /**
5627          * Boolean function that specifies whether the REST object supports
5628          * subscriptions. True by default. Subclasses should override if false.
5629          * @private
5630          */
5631         supportsSubscriptions: true,
5632         
5633         /**
5634          * Boolean function that specifies whether the REST object should retain
5635          * a copy of the REST response. False by default. Subclasses should override if true.
5636          * @private
5637          */
5638         keepRestResponse: false,
5639 
5640         /**
5641          * Number that represents the REST Response status that is returned. Only
5642          * set if keepRestResponse is set to true.
5643          * @public
5644          */
5645         restResponseStatus: null,
5646 
5647         /**
5648          * Object to store additional headers to be sent with the REST Request, null by default.
5649          * @private
5650          */
5651         extraHeaders: null,
5652 
5653         /**
5654          * Boolean function that specifies whether the REST object explicitly
5655          * subscribes. False by default. Subclasses should override if true.
5656          * @private
5657          */
5658         explicitSubscription: false,
5659 
5660         /**
5661          * Boolean function that specifies whether subscribing should be
5662          * automatically done at construction. Defaults to true.
5663          * This be overridden at object construction, not by implementing subclasses
5664          * @private
5665          */
5666         autoSubscribe: true,
5667         
5668         /**
5669          * Private reference to default logger
5670          * @private
5671          */
5672         _logger: {
5673             log: ClientServices.log,
5674             error: ClientServices.log
5675         },
5676 
5677         /**
5678          * @class
5679          * JavaScript representation of a REST object. Also exposes methods to operate
5680          * on the object against the server.  This object is typically extended into individual
5681          * REST Objects (like Dialog, User, etc...), and shouldn't be used directly.
5682          *
5683          * @constructor
5684          * @param {String} id
5685          *     The ID that uniquely identifies the REST object.
5686          * @param {Object} callbacks
5687          *     An object containing callbacks for instantiation and runtime
5688          *     Callback to invoke upon successful instantiation, passes in REST object.
5689          * @param {Function} callbacks.onLoad(this)
5690          *     Callback to invoke upon loading the data for the first time.
5691          * @param {Function} callbacks.onChange(this)
5692          *     Callback to invoke upon successful update object (PUT)
5693          * @param {Function} callbacks.onAdd(this)
5694          *     Callback to invoke upon successful update to add object (POST)
5695          * @param {Function} callbacks.onDelete(this)
5696          *     Callback to invoke upon successful update to delete object (DELETE)
5697          * @param {Function} callbacks.onError(rsp)
5698          *     Callback to invoke on update error (refresh or event)
5699          *     as passed by finesse.restservices.RestBase.restRequest()
5700          *     {
5701          *         status: {Number} The HTTP status code returned
5702          *         content: {String} Raw string of response
5703          *         object: {Object} Parsed object of response
5704          *         error: {Object} Wrapped exception that was caught
5705          *         error.errorType: {String} Type of error that was caught
5706          *         error.errorMessage: {String} Message associated with error
5707          *     }
5708          * @param {RestBase} [restObj]
5709          *     A RestBase parent object which this object has an association with.
5710          * @constructs
5711          */
5712         init: function (options, callbacks, restObj) {
5713             /**
5714               * Initialize the base class
5715               */
5716             var _this = this;
5717 
5718             this._super();
5719 
5720             if (typeof options === "object") {
5721                 this._id = options.id;
5722                 this._restObj = options.parentObj;
5723                 this.autoSubscribe = (options.autoSubscribe === false) ? false : true;
5724                 this.doNotSubscribe = options.doNotSubscribe;
5725                 this.doNotRefresh = this.doNotRefresh || options.doNotRefresh;
5726                 callbacks = {
5727                     onLoad: options.onLoad,
5728                     onChange: options.onChange,
5729                     onAdd: options.onAdd,
5730                     onDelete: options.onDelete,
5731                     onError: options.onError
5732                 };
5733             } else {
5734                 this._id = options;
5735                 this._restObj = restObj;
5736             }
5737             
5738             // Common stuff
5739             
5740             this._data = {};
5741             
5742             //Contains the full rest response to be processed by upper layers if needed
5743             this._restResponse = undefined;
5744 
5745             this._lastUpdate = {};
5746 
5747             this._util = Utilities;
5748 
5749             //Should be correctly initialized in either a window OR gadget context
5750             this._config = finesse.container.Config;
5751 
5752             // Setup all the notifiers - change, load and error.
5753             this._changeNotifier = new Notifier();
5754             this._loadNotifier = new Notifier();
5755             this._addNotifier = new Notifier();
5756             this._deleteNotifier = new Notifier();
5757             this._errorNotifier = new Notifier();
5758 
5759             this._loaded = false;
5760 
5761             // Protect against null dereferencing of options allowing its
5762             // (nonexistent) keys to be read as undefined
5763             callbacks = callbacks || {};
5764 
5765             this.addHandler('load', callbacks.onLoad);
5766             this.addHandler('change', callbacks.onChange);
5767             this.addHandler('add', callbacks.onAdd);
5768             this.addHandler('delete', callbacks.onDelete);
5769             this.addHandler('error', callbacks.onError);
5770 
5771             // Attempt to get the RestType then synchronize
5772             try {
5773                 this.getRestType();
5774 
5775                 // Only subscribe if this REST object supports subscriptions
5776                 // and autoSubscribe was not requested to be disabled as a construction option
5777                 if (this.supportsSubscriptions && this.autoSubscribe && !this.doNotSubscribe) {
5778                     this.subscribe({
5779                         success: function () {
5780                             //TODO: figure out how to use Function.call() or Function.apply() here...
5781                             //this is exactly the same as the below else case other than the scope of "this"
5782                             if (typeof options === "object" && options.data) {
5783                                 if (!_this._processObject(_this._normalize(options.data))) {
5784                                     // notify of error if we fail to construct
5785                                     _this._errorNotifier.notifyListeners(_this);
5786                                 }
5787                             } else {
5788                                 // Only subscribe if this REST object supports requests
5789                                 if (_this.supportsRequests) {
5790                                     _this._synchronize();
5791                                 }
5792                             }
5793                         },
5794                         error: function (err) {
5795                             _this._errorNotifier.notifyListeners(err);
5796                         }
5797                     });
5798                 } else {
5799                     if (typeof options === "object" && options.data) {
5800                         if (!this._processObject(this._normalize(options.data))) {
5801                             // notify of error if we fail to construct
5802                             this._errorNotifier.notifyListeners(this);
5803                         }
5804                     } else {
5805                         // Only subscribe if this REST object supports requests
5806                         if (this.supportsRequests) {
5807                             this._synchronize();
5808                         }
5809                     }
5810                 }
5811 
5812             } catch (err) {
5813                 this._logger.error('id=' + this._id + ': ' + err);
5814             }
5815         },
5816 
5817         /**
5818          * Determines if the object has a particular property.
5819          * @param obj is the object to examine
5820          * @param property is the property to check for
5821          * @returns {Boolean}
5822          */
5823         hasProperty: function (obj, prop) {
5824             return (obj !== null) && (obj.hasOwnProperty(prop));
5825         },
5826 
5827         /**
5828          * Gets a property from the object.
5829          * @param obj is the object to examine
5830          * @param property is the property to get
5831          * @returns {Property Value} or {Null} if not found
5832          */
5833         getProperty: function (obj, property) {
5834             var result = null;
5835 
5836             if (this.hasProperty(obj, property) === false) {
5837                 result = null;
5838             } else {
5839                 result = obj[property];
5840             }
5841             return result;
5842         },
5843 
5844         /**
5845          * Utility to extracts the ID from the specified REST URI. This is with the
5846          * assumption that the ID is always the last element in the URI after the
5847          * "/" delimiter.
5848          * @param {String} restUri
5849          *     The REST uri (i.e. /finesse/api/User/1000).
5850          * @private
5851          */
5852         _extractId: function (restObj) {
5853             var obj, restUri = "", strLoc;
5854             for (obj in restObj) {
5855                 if (restObj.hasOwnProperty(obj)) {
5856                     restUri = restObj[obj].uri;
5857                     break;
5858                 }
5859             }
5860             return Utilities.getId(restUri);
5861         },
5862 
5863         /**
5864          * Gets the data for this object.
5865          * @returns {Object} which is contained in data
5866          */
5867         getData: function () {
5868             return this._data;
5869         },
5870         
5871         /**
5872          * Gets the complete REST response to the request made
5873          * @returns {Object} which is contained in data
5874          * @private
5875          */
5876         getRestResponse: function () {
5877             return this._restResponse;
5878         },
5879 
5880         /**
5881          * The REST URL in which this object can be referenced.
5882          * @return {String}
5883          *     The REST URI for this object.
5884          * @private
5885          */
5886         getRestUrl: function () {
5887             var
5888             restObj = this._restObj,
5889             restUrl = "";
5890 
5891             //Prepend the base REST object if one was provided.
5892             if (restObj instanceof RestBase) {
5893                 restUrl += restObj.getRestUrl();
5894             }
5895             //Otherwise prepend with the default webapp name.
5896             else {
5897                 restUrl += "/finesse/api";
5898             }
5899 
5900             //Append the REST type.
5901             restUrl += "/" + this.getRestType();
5902 
5903             //Append ID if it is not undefined, null, or empty.
5904             if (this._id) {
5905                 restUrl += "/" + this._id;
5906             }
5907             return restUrl;
5908         },
5909 
5910         /**
5911          * Getter for the id of this RestBase
5912          * @returns {String}
5913          *     The id of this RestBase
5914          */
5915         getId: function () {
5916             return this._id;
5917         },
5918 
5919         /**
5920          * Synchronize this object with the server using REST GET request.
5921          * @returns {Object}
5922          *     {
5923          *         abort: {function} Function that signifies the callback handler to NOT process the response of the rest request
5924          *     }
5925          * @private
5926          */
5927         _synchronize: function (retries) {
5928             // Fetch this REST object
5929             if (typeof this._id === "string") {
5930                 var _this = this, isLoaded = this._loaded, _RETRY_INTERVAL = 10 * 1000;
5931 
5932                 return this._doGET(
5933                     {
5934                         success: function (rsp) {
5935                             if (!_this._processResponse(rsp)) {
5936                                 if (retries > 0) {
5937                                     setTimeout(function () {
5938                                         _this._synchronize(retries - 1);
5939                                     }, _RETRY_INTERVAL);
5940                                 } else {
5941                                     _this._errorNotifier.notifyListeners(_this);
5942                                 }
5943                             } else {
5944                                 // If this object was already "loaded" prior to
5945                                 // the _doGET request, then call the
5946                                 // changeNotifier
5947                                 if (isLoaded) {
5948                                     _this._changeNotifier.notifyListeners(_this);
5949                                 }
5950                             }
5951                         },
5952                         error: function (rsp) {
5953                             if (retries > 0) {
5954                                 setTimeout(function () {
5955                                     _this._synchronize(retries - 1);
5956                                 }, _RETRY_INTERVAL);
5957                                 
5958                             } else {
5959                                 _this._errorNotifier.notifyListeners(rsp);
5960                             }
5961                         }
5962                     }
5963                 );
5964 
5965             } else {
5966                 throw new Error("Can't construct a <" + this.getRestType() + "> due to invalid id type.");
5967             }
5968         },
5969 
5970         /**
5971          * Adds an handler to this object.
5972          * If notifierType is 'load' and the object has already loaded, the callback is invoked immediately
5973          * @param {String} notifierType
5974          *     The type of notifier to add to ('load', 'change', 'add', 'delete', 'error')
5975          * @param {Function} callback
5976          *     The function callback to invoke.
5977          * @example
5978          *   // Handler for additions to the Dialogs collection object.  
5979          *   // When Dialog (a RestBase object) is created, add a change handler.
5980          *   _handleDialogAdd = function(dialog) {
5981          *      dialog.addHandler('change', _handleDialogChange);
5982          *   }
5983          */
5984         addHandler: function (notifierType, callback, scope) {
5985             var notifier = null;
5986             try {
5987                 Utilities.validateHandler(callback);
5988 
5989                 notifier = this._getNotifierReference(notifierType);
5990 
5991                 notifier.addListener(callback, scope);
5992                 
5993                 // If load handler is added and object has
5994                 // already been loaded, invoke callback
5995                 // immediately
5996                 if (notifierType === 'load' && this._loaded) {
5997                     callback.call((scope || window), this);
5998                 }
5999             } catch (err) {
6000                 this._logger.error('id=' + this._id + ': ' + err);
6001             }
6002         },
6003 
6004         /**
6005          * Removes a handler from this object.
6006          * @param {String} notifierType
6007          *     The type of notifier to remove ('load', 'change', 'add', 'delete', 'error')
6008          * @param {Function} callback
6009          *     The function to remove.
6010          */
6011         removeHandler: function (notifierType, callback) {
6012             var notifier = null;
6013             try {
6014                 Utilities.validateHandler(callback);
6015 
6016                 notifier = this._getNotifierReference(notifierType);
6017 
6018                 if (typeof(callback) === "undefined")
6019                 {
6020                     // Remove all listeners for the type
6021                     notifier.reset();
6022                 }
6023                 else
6024                 {
6025                     // Remove the specified listener
6026                     finesse.utilities.Utilities.validateHandler(callback);
6027                     notifier.removeListener(callback);
6028                 }
6029             } catch (err) {
6030                 this._logger.error('id=' + this._id + ': ' + err);
6031             }
6032         },
6033 
6034         /**
6035          * Utility method gating any operations that require complete instantiation
6036          * @throws Error
6037          *     If this object was not fully instantiated yet
6038          * @returns {finesse.restservices.RestBase}
6039          *     This RestBase object to allow cascading
6040          */
6041         isLoaded: function () {
6042             if (!this._loaded) {
6043                 throw new Error("Cannot operate on object that is not fully instantiated, use onLoad/onLoadError handlers");
6044             }
6045             return this; // Allow cascading
6046         },
6047 
6048 
6049 
6050         /**
6051          * Force an update on this object. Since an asynchronous GET is performed,
6052          * it is necessary to have an onChange handler registered in order to be
6053          * notified when the response of this returns.
6054          * @param {Integer} retries
6055          *    The number or retry attempts to make.
6056          * @returns {Object}
6057          *     {
6058          *         abort: {function} Function that signifies the callback handler to NOT process the response of the asynchronous request
6059          *     }
6060          */
6061         refresh: function (retries) {
6062             var _this = this;
6063 
6064             if (this.explicitSubscription) {
6065                 this._subscribeNode({
6066                     success: function () {
6067                         //Disallow GETs if object doesn't support it.
6068                         if (!_this.supportsRequests) {
6069                             throw new Error("Object doesn't support request operations.");
6070                         }
6071 
6072                         _this._synchronize(retries);
6073 
6074                         return this; // Allow cascading
6075                     },
6076                     error: function (err) {
6077                         _this._errorNotifier.notifyListeners(err);
6078                     }
6079                 });
6080             } else {
6081                 //Disallow GETs if object doesn't support it.
6082                 if (!this.supportsRequests) {
6083                     throw new Error("Object doesn't support request operations.");
6084                 }
6085 
6086                 return this._synchronize(retries);
6087             }
6088         },
6089 
6090         /**
6091          * Utility method to validate against the known schema of this RestBase
6092          * @param {Object} obj
6093          *     The object to validate
6094          * @returns {Boolean}
6095          *     True if the object fits the schema of this object. This usually
6096          *     means all required keys or nested objects are present.
6097          *     False otherwise.
6098          * @private
6099          */
6100         _validate: function (obj) {
6101             var valid = (typeof obj === "object" && this.hasProperty(obj, this.getRestType()));
6102             if (!valid)
6103             {
6104                 this._logger.error(this.getRestType() + " failed validation! Does your JS REST class return the correct string from getRestType()?");
6105             }
6106             return valid;
6107         },
6108 
6109         /**
6110          * Utility method to fetch this RestBase from the server
6111          * @param {finesse.interfaces.RequestHandlers} handlers
6112          *     An object containing the handlers for the request
6113          * @returns {Object}
6114          *     {
6115          *         abort: {function} Function that signifies the callback handler to NOT process the response of the rest request
6116          *     }
6117          * @private
6118          */
6119         _doGET: function (handlers) {
6120             return this.restRequest(this.getRestUrl(), handlers);
6121         },
6122 
6123         /**
6124          * Common update event handler used by the pubsub callback closure.
6125          * Processes the update event then notifies listeners.
6126          * @param {Object} scope
6127          *     An object containing callbacks to handle the asynchronous get
6128          * @param {Object} update
6129          *     An object containing callbacks to handle the asynchronous get
6130          * @private
6131          */
6132         _updateEventHandler: function (scope, update) {
6133             if (scope._processUpdate(update)) {
6134                 switch (update.object.Update.event) {
6135                 case "POST":
6136                     scope._addNotifier.notifyListeners(scope);
6137                     break;
6138                 case "PUT":
6139                     scope._changeNotifier.notifyListeners(scope);
6140                     break;
6141                 case "DELETE":
6142                     scope._deleteNotifier.notifyListeners(scope);
6143                     break;
6144                 }
6145             }   
6146         },
6147 
6148         /**
6149          * Utility method to create a callback to be given to OpenAjax to invoke when a message
6150          * is published on the topic of our REST URL (also XEP-0060 node).
6151          * This needs to be its own defined method so that subclasses can have their own implementation.
6152          * @returns {Function} callback(update)
6153          *     The callback to be invoked when an update event is received. This callback will
6154          *     process the update and notify listeners.
6155          * @private
6156          */
6157         _createPubsubCallback: function () {
6158             var _this = this;
6159             return function (update) {
6160                 _this._updateEventHandler(_this, update);
6161             };
6162         },
6163 
6164         /**
6165          * Subscribe to pubsub infra using the REST URL as the topic name.
6166          * @param {finesse.interfaces.RequestHandlers} handlers
6167          *     An object containing the handlers for the request
6168          * @private
6169          */
6170         subscribe: function (callbacks) {
6171             // Only need to do a subscription to client pubsub. No need to trigger
6172             // a subscription on the Finesse server due to implicit subscribe (at
6173             // least for now).
6174             var _this = this,
6175             topic = Topics.getTopic(this.getRestUrl()),
6176             handlers,
6177             successful = ClientServices.subscribe(topic, this._createPubsubCallback(), true);
6178 
6179             callbacks = callbacks || {};
6180 
6181             handlers = {
6182                 /** @private */
6183                 success: function () {
6184                     // Add item to the refresh list in ClientServices to refresh if
6185                     // we recover due to our resilient connection. However, do
6186                     // not add if doNotRefresh flag is set.
6187                     if (!_this.doNotRefresh) {
6188                         ClientServices.addToRefreshList(_this);
6189                     }
6190 
6191                     if (typeof callbacks.success === "function") {
6192                         callbacks.success();
6193                     }
6194                 },
6195                 /** @private */
6196                 error: function (err) {
6197                     if (successful) {
6198                         ClientServices.unsubscribe(topic);
6199                     }
6200 
6201                     if (typeof callbacks.error === "function") {
6202                         callbacks.error(err);
6203                     }
6204                 }
6205             };
6206 
6207             // Request a node subscription only if this object requires explicit subscriptions
6208             if (this.explicitSubscription === true) {
6209                 this._subscribeNode(handlers);
6210             } else {
6211                 if (successful) {
6212                     this._subid = "OpenAjaxOnly";
6213                     handlers.success();
6214                 } else {
6215                     handlers.error();
6216                 }
6217             }
6218 
6219             return this;
6220         },
6221 
6222         /**
6223          * Unsubscribe to pubsub infra using the REST URL as the topic name.
6224          * @param {finesse.interfaces.RequestHandlers} handlers
6225          *     An object containing the handlers for the request
6226          * @private
6227          */
6228         unsubscribe: function (callbacks) {
6229             // Only need to do a subscription to client pubsub. No need to trigger
6230             // a subscription on the Finesse server due to implicit subscribe (at
6231             // least for now).
6232             var _this = this,
6233             topic = Topics.getTopic(this.getRestUrl()),
6234             handlers;
6235 
6236             // no longer keep track of object to refresh on reconnect
6237             ClientServices.removeFromRefreshList(_this);
6238 
6239             callbacks = callbacks || {};
6240 
6241             handlers = {
6242                 /** @private */
6243                 success: function () {
6244                     if (typeof callbacks.success === "function") {
6245                         callbacks.success();
6246                     }
6247                 },
6248                 /** @private */
6249                 error: function (err) {
6250                     if (typeof callbacks.error === "function") {
6251                         callbacks.error(err);
6252                     }
6253                 }
6254             };
6255 
6256             if (this._subid) {
6257                 ClientServices.unsubscribe(topic);
6258                 // Request a node unsubscribe only if this object requires explicit subscriptions
6259                 if (this.explicitSubscription === true) {
6260                     this._unsubscribeNode(handlers);
6261                 } else {
6262                     this._subid = undefined;
6263                     handlers.success();
6264                 }
6265             } else {
6266                 handlers.success();
6267             }
6268 
6269             return this;
6270         },
6271 
6272         /**
6273          * Private utility to perform node subscribe requests for explicit subscriptions
6274          * @param {finesse.interfaces.RequestHandlers} handlers
6275          *     An object containing the handlers for the request
6276          * @private
6277          */
6278         _subscribeNode: function (callbacks) {
6279             var _this = this;
6280 
6281             // Protect against null dereferencing of callbacks allowing its (nonexistent) keys to be read as undefined
6282             callbacks = callbacks || {};
6283 
6284             ClientServices.subscribeNode(this.getXMPPNodePath(), function (subid, err) {
6285                 if (err) {
6286                     if (typeof callbacks.error === "function") {
6287                         callbacks.error(err);
6288                     }
6289                 } else {
6290                     // Store the subid on a successful subscribe
6291                     _this._subid = subid;
6292                     if (typeof callbacks.success === "function") {
6293                         callbacks.success();
6294                     }
6295                 }
6296             });
6297         },
6298 
6299         /**
6300          * Private utility to perform node unsubscribe requests for explicit subscriptions
6301          * @param {finesse.interfaces.RequestHandlers} handlers
6302          *     An object containing the handlers for the request
6303          * @private
6304          */
6305         _unsubscribeNode: function (callbacks) {
6306             var _this = this;
6307 
6308             // Protect against null dereferencing of callbacks allowing its (nonexistent) keys to be read as undefined
6309             callbacks = callbacks || {};
6310 
6311             ClientServices.unsubscribeNode(this.getXMPPNodePath(), this._subid, function (err) {
6312                 _this._subid = undefined;
6313                 if (err) {
6314                     if (typeof callbacks.error === "function") {
6315                         callbacks.error(err);
6316                     }
6317                 } else {
6318                     if (typeof callbacks.success === "function") {
6319                         callbacks.success();
6320                     }
6321                 }
6322             });
6323         },
6324 
6325         /**
6326          * Validate and store the object into the internal data store.
6327          * @param {Object} object
6328          *     The JavaScript object that should match of schema of this REST object.
6329          * @returns {Boolean}
6330          *     True if the object was validated and stored successfully.
6331          * @private
6332          */
6333         _processObject: function (object) {
6334             if (this._validate(object)) {
6335                 this._data = this.getProperty(object, this.getRestType()); // Should clone the object here?
6336 
6337                 // If loaded for the first time, call the load notifiers.
6338                 if (!this._loaded) {
6339                     this._loaded = true;
6340                     this._loadNotifier.notifyListeners(this);
6341                 }
6342 
6343                 return true;
6344             }
6345             return false;
6346         },
6347 
6348         /**
6349          * Normalize the object to mitigate the differences between the backend
6350          * and what this REST object should hold. For example, the backend sends
6351          * send an event with the root property name being lower case. In order to
6352          * match the GET, the property should be normalized to an upper case.
6353          * @param {Object} object
6354          *     The object which should be normalized.
6355          * @returns {Object}
6356          *     Return the normalized object.
6357          * @private
6358          */
6359         _normalize: function (object) {
6360             var
6361             restType = this.getRestType(),
6362             // Get the REST object name with first character being lower case.
6363             objRestType = restType.charAt(0).toLowerCase() + restType.slice(1);
6364 
6365             // Normalize payload to match REST object. The payload for an update
6366             // use a lower case object name as oppose to upper case. Only normalize
6367             // if necessary.
6368             if (!this.hasProperty(object, restType) && this.hasProperty(object, objRestType)) {
6369                 //Since the object is going to be modified, clone the object so that
6370                 //it doesn't affect others (due to OpenAjax publishing to other
6371                 //subscriber.
6372                 object = jQuery.extend(true, {}, object);
6373 
6374                 object[restType] = object[objRestType];
6375                 delete(object[objRestType]);
6376             }
6377             return object;
6378         },
6379 
6380         /**
6381          * Utility method to process the response of a successful get
6382          * @param {Object} rsp
6383          *     The response of a successful get
6384          * @returns {Boolean}
6385          *     True if the update was successfully processed (the response object
6386          *     passed the schema validation) and updated the internal data cache,
6387          *     false otherwise.
6388          * @private
6389          */
6390         _processResponse: function (rsp) {
6391             try {
6392                 if (this.keepRestResponse) {
6393                     this._restResponse = rsp.content;
6394                     this.restResponseStatus = rsp.status;
6395                 }
6396                 return this._processObject(rsp.object);
6397             }
6398             catch (err) {
6399                 this._logger.error(this.getRestType() + ': ' + err);
6400             }
6401             return false;
6402         },
6403 
6404         /**
6405          * Method that is called at the end of _processUpdate() which by default
6406          * will just delete the requestId-to-callbacks mapping but can be overridden.
6407          * @param  {String} requestId The requestId of the event
6408          */
6409         _postProcessUpdateStrategy: function (requestId) {
6410             //Clean up _pendingCallbacks now that we fired a callback corresponding to the received requestId.
6411             delete this._pendingCallbacks[requestId];
6412         },
6413 
6414         /**
6415          * Utility method to process the update notification.
6416          * @param {Object} update
6417          *     The payload of an update notification.
6418          * @returns {Boolean}
6419          *     True if the update was successfully processed (the update object
6420          *     passed the schema validation) and updated the internal data cache,
6421          *     false otherwise.
6422          * @private
6423          */
6424         _processUpdate: function (update) {
6425             try {
6426                 var updateObj, requestId, fakeResponse, receivedError;
6427 
6428                 // The backend will send the data object with a lower case. To be
6429                 // consistent with what should be represented in this object, the
6430                 // object name should be upper case. This will normalize the object.
6431                 updateObj = this._normalize(update.object.Update.data);
6432 
6433                 // Store the last event.
6434                 this._lastUpdate = update.object;
6435 
6436                 requestId = this._lastUpdate.Update ? this._lastUpdate.Update.requestId : undefined;
6437 
6438                 if (requestId && this._pendingCallbacks[requestId]) {
6439 
6440                     /*
6441                      * The passed success/error callbacks are expecting to be passed an AJAX response, so construct
6442                      * a simulated/"fake" AJAX response object from the information in the received event.
6443                      * The constructed object should conform to the contract for response objects specified
6444                      * in _createAjaxHandler().
6445                      */
6446                     fakeResponse = {};
6447 
6448                     //The contract says that rsp.content should contain the raw text of the response so we simulate that here.
6449                     //For some reason json2xml has trouble with the native update object, so we serialize a clone of it by
6450                     //doing a parse(stringify(update)).
6451                     fakeResponse.content = this._util.json2xml(gadgets.json.parse(gadgets.json.stringify(update)));
6452 
6453                     fakeResponse.object = {};
6454 
6455                     if (updateObj.apiErrors && updateObj.apiErrors.apiError) { //Error case
6456 
6457                         //TODO: The lowercase -> uppercase ApiErrors translation method below is undesirable, can it be improved?
6458                         receivedError = updateObj.apiErrors.apiError;
6459                         fakeResponse.object.ApiErrors = {};
6460                         fakeResponse.object.ApiErrors.ApiError = {};
6461                         fakeResponse.object.ApiErrors.ApiError.ErrorData = receivedError.errorData || undefined;
6462                         fakeResponse.object.ApiErrors.ApiError.ErrorMessage = receivedError.errorMessage || undefined;
6463                         fakeResponse.object.ApiErrors.ApiError.ErrorType = receivedError.errorType || undefined;
6464 
6465                         /*
6466                          * Since this is the error case, supply the error callback with a '400 BAD REQUEST' status code. We don't know what the real
6467                          * status code should be since the event we're constructing fakeResponse from doesn't include a status code.
6468                          * This is just to conform to the contract for the error callback in _createAjaxHandler().
6469                          **/
6470                         fakeResponse.status = 400;
6471 
6472                     } else { //Success case
6473 
6474                         fakeResponse.object = this._lastUpdate;
6475 
6476                         /*
6477                          * Since this is the success case, supply the success callback with a '200 OK' status code. We don't know what the real
6478                          * status code should be since the event we're constructing fakeResponse from doesn't include a status code.
6479                          * This is just to conform to the contract for the success callback in _createAjaxHandler().
6480                          **/
6481                         fakeResponse.status = 200;
6482                     }
6483 
6484                     try {
6485 
6486                         if (fakeResponse.object.ApiErrors && this._pendingCallbacks[requestId].error) {
6487                             this._pendingCallbacks[requestId].error(fakeResponse);
6488                         } 
6489                         // HTTP 202 is handled as a success, besides, we cannot infer that a non-error is a success.
6490                         /*else if (this._pendingCallbacks[requestId].success) {
6491                             this._pendingCallbacks[requestId].success(fakeResponse);
6492                         }*/
6493 
6494                     } catch (callbackErr) {
6495 
6496                         this._logger.error(this.getRestType() + ": Caught error while firing callback: " + callbackErr);
6497 
6498                     }
6499 
6500                     this._postProcessUpdateStrategy(requestId);
6501 
6502                 } 
6503 
6504                 return this._processObject(updateObj);
6505             }
6506             catch (err) {
6507                 this._logger.error(this.getRestType() + ': ' + err);
6508             }
6509             return false;
6510         },
6511 
6512         /**
6513          * Utility method to create ajax response handler closures around the
6514          * provided callbacks. Callbacks should be passed through from .ajax().
6515          * makeRequest is responsible for garbage collecting these closures.
6516          * @param {finesse.interfaces.RequestHandlers} handlers
6517          *     An object containing the handlers for the request
6518          * @returns {Object}
6519          *     {
6520          *         abort: {function} Function that signifies the callback handler to NOT process the response when the response returns
6521          *         callback: {function} Callback handler to be invoked when the response returns
6522          *     }
6523          * @private
6524          */
6525         _createAjaxHandler: function (options) {
6526             //We should not need to check this again since it has already been done in .restRequest()
6527             //options = options || {};
6528 
6529             //Flag to indicate whether or not to process the response
6530             var abort = false,
6531 
6532             //Get a reference to the parent User object
6533             _this = this;
6534 
6535             return {
6536 
6537                 abort: function () {
6538                     abort = true;
6539                 },
6540 
6541                 callback: function (rsp) {
6542              
6543                     if (abort) {
6544                         // Do not process the response
6545                         return;
6546                     }
6547 
6548                     var requestId, error = false, rspObj;
6549 
6550                     if (options.success || options.error) {
6551                         rspObj = {
6552                             status: rsp.rc,
6553                             content: rsp.text
6554                         };
6555 
6556                         if (!_this.doNotLog) {
6557                             _this._logger.log(_this.getRestType() + ": requestId='" + options.uuid + "', Returned with status=" + rspObj.status + ", content='" + rspObj.content + "'");
6558                         }
6559 
6560                         //Some responses may not have a body.
6561                         if (rsp.text && rsp.text.length > 0) {
6562                             try {
6563                                 rspObj.object = _this._util.xml2js(rsp.text);
6564                             } catch (e) {
6565                                 error = true;
6566                                 rspObj.error = {
6567                                     errorType: "parseError",
6568                                     errorMessage: "Could not serialize XML: " + e
6569                                 };
6570                             }
6571                         } else {
6572                             rspObj.object = {};
6573                         }
6574 
6575                         if (!error && rspObj.status >= 200 && rspObj.status < 300) {
6576                             if (options.success) {
6577                                 options.success(rspObj);
6578                             }
6579                         } else {
6580                             if (options.error) {
6581                                 options.error(rspObj);
6582                             }
6583                         }
6584 
6585                         /*
6586                          * If a synchronous error happened after a non-GET request (usually a validation error), we
6587                          * need to clean up the request's entry in _pendingCallbacks since no corresponding event
6588                          * will arrive later. The corresponding requestId should be present in the response headers.
6589                          *
6590                          * It appears Shindig changes the header keys to lower case, hence 'requestid' instead of
6591                          * 'requestId' below.
6592                          **/
6593                         if (rspObj.status !== 202 && rsp.headers && rsp.headers.requestid) {
6594                             requestId = rsp.headers.requestid[0];
6595                             if (_this._pendingCallbacks[requestId]) {
6596                                 delete _this._pendingCallbacks[requestId];
6597                             }
6598                         }
6599                     }
6600                 }
6601             };
6602         },
6603 
6604         /**
6605          * Utility method to make an asynchronous request
6606          * @param {String} url
6607          *     The unencoded URL to which the request is sent (will be encoded)
6608          * @param {Object} options
6609          *     An object containing additional options for the request.
6610          * @param {Object} options.content
6611          *     An object to send in the content body of the request. Will be
6612          *     serialized into XML before sending.
6613          * @param {String} options.method
6614          *     The type of request. Defaults to "GET" when none is specified.
6615          * @param {Function} options.success(rsp)
6616          *     A callback function to be invoked for a successful request.
6617          *     {
6618          *         status: {Number} The HTTP status code returned
6619          *         content: {String} Raw string of response
6620          *         object: {Object} Parsed object of response
6621          *     }
6622          * @param {Function} options.error(rsp)
6623          *     A callback function to be invoked for an unsuccessful request.
6624          *     {
6625          *         status: {Number} The HTTP status code returned
6626          *         content: {String} Raw string of response
6627          *         object: {Object} Parsed object of response
6628          *         error: {Object} Wrapped exception that was caught
6629          *         error.errorType: {String} Type of error that was caught
6630          *         error.errorMessage: {String} Message associated with error
6631          *     }
6632          * @returns {Object}
6633          *     {
6634          *         abort: {function} Function that signifies the callback handler to NOT process the response of this asynchronous request
6635          *     }
6636          * @private
6637         */
6638         restRequest: function (url, options) {
6639 
6640             var params, encodedUrl, ajaxHandler;
6641 
6642             params = {};
6643 
6644             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
6645             options = options || {};
6646             options.success = this._util.validateHandler(options.success);
6647             options.error = this._util.validateHandler(options.error);
6648 
6649             // Request Headers
6650             params[gadgets.io.RequestParameters.HEADERS] = this.extraHeaders || {};
6651 
6652             // HTTP method is a passthrough to gadgets.io.makeRequest, makeRequest defaults to GET
6653             params[gadgets.io.RequestParameters.METHOD] = options.method;
6654 
6655             //true if this should be a GET request, false otherwise
6656             if (!options.method || options.method === "GET") {
6657                 //Disable caching for GETs
6658                 if (url.indexOf("?") > -1) {
6659                     url += "&";
6660                 } else {
6661                     url += "?";
6662                 }
6663                 url += "nocache=" + this._util.currentTimeMillis();
6664             } else {
6665                 /**
6666                  * If not GET, generate a requestID and add it to the headers, then wrap
6667                  * callbacks into an object and store it in _pendingCallbacks.
6668                  * If we receive a synchronous error response instead of a 202 as expected,
6669              * the AJAX handler will clean up _pendingCallbacks.
6670                  **/
6671                 /*
6672                  * TODO: Clean up _pendingCallbacks if an entry persists after a certain amount of time has passed.
6673                  * In the block below, can store the current time (new Date().getTime()) alongside the
6674                  * callbacks in the new _pendingCallbacks entry. Then iterate through a copty of _pendingCallbacks,
6675                  * deleting all entries inside _pendingCallbacks that are older than a certain threshold (2 minutes for example.)
6676                  * This solves a potential memory leak issue if we never receive an event for a given stored requestId;
6677                  * we don't want to store unfired callbacks forever.
6678                  */
6679                 /** @private */
6680                 options.uuid = this._util.generateUUID();
6681                 params[gadgets.io.RequestParameters.HEADERS].requestId = options.uuid;
6682                 //By default, Shindig strips nearly all of the response headers, but this parameter tells Shindig
6683                 //to send the headers through unmodified; we need to be able to read the 'requestId' header if we
6684                 //get a synchronous error as a result of a non-GET request. (See the bottom of _createAjaxHandler().)
6685                 params[gadgets.io.RequestParameters.GET_FULL_HEADERS] = "true";
6686                 this._pendingCallbacks[options.uuid] = {};
6687                 this._pendingCallbacks[options.uuid].success = options.success;
6688                 this._pendingCallbacks[options.uuid].error = options.error;
6689             }
6690 
6691             encodedUrl = encodeURI(url) + (window.errorOnRestRequest ? "ERROR" : "");
6692 
6693             if (!this.doNotLog) {
6694                 this._logger.log(this.getRestType() + ": requestId='" + options.uuid + "', Making REST request: method=" + (options.method || "GET") + ", url='" + encodedUrl + "'");
6695             }
6696 
6697             // Content Body
6698             if (typeof options.content === "object") {
6699                 // Content Type
6700                 params[gadgets.io.RequestParameters.HEADERS]["Content-Type"] = "application/xml";
6701                 // Content
6702                 params[gadgets.io.RequestParameters.POST_DATA] = this._util.js2xml(options.content);
6703                 
6704                 if (!this.doNotLog) {
6705                     this._logger.log(this.getRestType() + ": requestId='" + options.uuid + "', POST_DATA='" + params[gadgets.io.RequestParameters.POST_DATA] + "'");
6706                 }
6707             }
6708 
6709             ajaxHandler = this._createAjaxHandler(options);
6710             ClientServices.makeRequest(encodedUrl, ajaxHandler.callback, params);
6711 
6712             return {
6713                 abort: ajaxHandler.abort
6714             };
6715         },
6716 
6717         /**
6718          * Retrieves a reference to a particular notifierType.
6719          * @param notifierType is a string which indicates the notifier to retrieve
6720          * ('load', 'change', 'add', 'delete', 'error')
6721          * @return {Notifier}
6722          * @private
6723          */
6724         _getNotifierReference: function (notifierType) {
6725             var notifierReference = null;
6726             if (notifierType === 'load') {
6727                 notifierReference = this._loadNotifier;
6728             } else if (notifierType === 'change') {
6729                 notifierReference = this._changeNotifier;
6730             } else if (notifierType === 'add') {
6731                 notifierReference = this._addNotifier;
6732             } else if (notifierType === 'delete') {
6733                 notifierReference = this._deleteNotifier;
6734             } else if (notifierType === 'error') {
6735                 notifierReference = this._errorNotifier;
6736             } else {
6737                 throw new Error("_getNotifierReference(): Trying to get unknown notifier(notifierType=" + notifierType + ")");
6738             }
6739 
6740             return notifierReference;
6741         }
6742     });
6743 
6744     window.finesse = window.finesse || {};
6745     window.finesse.restservices = window.finesse.restservices || {};
6746     window.finesse.restservices.RestBase = RestBase;
6747     
6748     return RestBase;
6749 });
6750 
6751 /** The following comment is to prevent jslint errors about 
6752  * using variables before they are defined.
6753  */
6754 /*global finesse*/
6755 
6756 /**
6757  * JavaScript base object that all REST collection objects should
6758  * inherit from because it encapsulates and provides the common functionality
6759  * that all REST objects need.
6760  *
6761  * @requires finesse.clientservices.ClientServices
6762  * @requires Class
6763  * @requires finesse.FinesseBase
6764  * @requires finesse.restservices.RestBase
6765  */
6766 
6767 /**
6768  * @class
6769  * JavaScript representation of a REST collection object.
6770  *
6771  * @constructor
6772  * @param {Function} callbacks.onCollectionAdd(this)
6773  *     Callback to invoke upon successful item addition to the collection.
6774  * @param {Function} callbacks.onCollectionDelete(this)
6775  *     Callback to invoke upon successful item deletion from the collection.
6776  * @borrows finesse.restservices.RestBase as finesse.restservices.RestCollectionBase
6777  */
6778 /** @private */
6779 define('restservices/RestCollectionBase',[
6780     'restservices/RestBase',
6781     'utilities/Utilities',
6782     'restservices/Notifier'
6783 ],
6784 function (RestBase, Utilities, Notifier) {
6785     var RestCollectionBase = RestBase.extend(/** @lends finesse.restservices.RestCollectionBase.prototype */{
6786 
6787         /**
6788          * Boolean function that specifies whether the collection handles subscribing
6789          * and propagation of events for the individual REST object items the
6790          * collection holds. False by default. Subclasses should override if true.
6791          * @private
6792          */
6793         supportsRestItemSubscriptions: false,
6794 
6795         /**
6796          * Gets the constructor the individual items that make of the collection.
6797          * For example, a Dialogs collection object will hold a list of Dialog items.
6798          * @throws Error because subtype must implement.
6799          * @private
6800          */
6801         getRestItemClass: function () {
6802             throw new Error("getRestItemClass(): Not implemented in subtype.");
6803         },
6804 
6805         /**
6806          * Gets the REST type of the individual items that make of the collection.
6807          * For example, a Dialogs collection object will hold a list of Dialog items.
6808          * @throws Error because subtype must implement.
6809          * @private
6810          */
6811         getRestItemType: function () {
6812             throw new Error("getRestItemType(): Not implemented in subtype.");
6813         },
6814 
6815         /**
6816          * The base REST URL in which items this object contains can be referenced.
6817          * @return {String}
6818          *     The REST URI for items this object contains.
6819          * @private
6820          */
6821         getRestItemBaseUrl: function () {
6822             var
6823             restUrl = "/finesse/api";
6824 
6825             //Append the REST type.
6826             restUrl += "/" + this.getRestItemType();
6827 
6828             return restUrl;
6829         },
6830 
6831          /*
6832          * Creates a new object from the given data
6833          * @param data - data object
6834          * @private
6835          */
6836         _objectCreator: function (data) {
6837             var objectId = this._extractId(data),
6838             newRestObj = this._collection[objectId],
6839             _this = this;
6840 
6841             //Prevent duplicate entries into collection.
6842             if (!newRestObj) {
6843                 //Create a new REST object using the subtype defined by the
6844                 //overridden method.
6845                 newRestObj = new (this.getRestItemClass())({
6846                     doNotSubscribe: this.handlesItemSubscription,
6847                     doNotRefresh: this.handlesItemRefresh,
6848                     id: objectId,
6849                     data: data,
6850                     onLoad: function (newObj) {
6851                         //Normalize and add  REST object to collection datastore.
6852                         _this._collection[objectId] = newObj;
6853                         _this._collectionAddNotifier.notifyListeners(newObj);
6854                         _this.length += 1;
6855                     }
6856                 });
6857             }
6858             else {
6859                 //If entry already exist in collection, process the new event,
6860                 //and notify all change listeners since an existing object has
6861                 //change. This could happen in the case when the Finesse server
6862                 //cycles, and sends a snapshot of the user's calls.
6863                 newRestObj._processObject(data);
6864                 newRestObj._changeNotifier.notifyListeners(newRestObj);
6865             }
6866         },
6867 
6868         /*
6869          * Deletes and object and notifies its handlers
6870          * @param data - data object
6871          * @private
6872          */
6873         _objectDeleter: function (data) {
6874             var objectId = this._extractId(data),
6875             object = this._collection[objectId];
6876             if (object) {
6877                 //Even though this is a delete, let's make sure the object we are passing has got good data
6878                 object._processObject(data);
6879                 //Notify listeners and delete from internal datastore.
6880                 this._collectionDeleteNotifier.notifyListeners(object);
6881                 delete this._collection[objectId];
6882                 this.length -= 1;
6883             }
6884         },
6885 
6886          /**
6887           * Creates an anonymous function for notifiying error listeners of a particular object
6888           * data.
6889           * @param obj - the objects whose error listeners to notify
6890           * @returns {Function}
6891           *     Callback for notifying of errors
6892           * @private
6893           */
6894         _createErrorNotifier: function (obj) {
6895             return function (err) {
6896                 obj._errorNotifier.notifyListeners(err);
6897             };
6898         },
6899 
6900          /**
6901           * Replaces the collection with a refreshed list using the passed in
6902           * data.
6903           * @param data - data object (usually this._data)
6904           * @private
6905           */
6906          _buildRefreshedCollection: function (data) {
6907             var i, dataObject, object, objectId, dataArray, newIds = [], foundFlag;
6908             if (data && this.getProperty(data, this.getRestItemType()) !== null) {
6909                 dataArray = Utilities.getArray(this.getProperty(data, this.getRestItemType()));
6910             } else {
6911                 dataArray = [];
6912             }
6913 
6914             // iterate through each item in the new data and add to or update collection
6915             for (i = 0; i < dataArray.length; i += 1) {
6916                 dataObject = {};
6917                 dataObject[this.getRestItemType()] = dataArray[i];
6918                 objectId = this._extractId(dataObject);
6919 
6920                 this._objectCreator(dataObject);
6921                 newIds.push(objectId);
6922 
6923                 // resubscribe if the object requires an explicit subscription
6924                 object = this._collection[objectId];
6925                 if (this.handlesItemRefresh && object.explicitSubscription) {
6926                     object._subscribeNode({
6927                         error: this._createErrorNotifier(object)
6928                     });
6929                 }
6930             }
6931 
6932             // now clean up items (if any) that were removed
6933             for (objectId in this._collection) {
6934                 if (this._collection.hasOwnProperty(objectId)) {
6935                     foundFlag = false;
6936                     for (i = newIds.length - 1; i >= 0; i -= 1) {
6937                         if (newIds[i] === objectId) {
6938                             foundFlag = true;
6939                             break;
6940                         }
6941                     }
6942                     // did not find in updated list, so delete it
6943                     if (!foundFlag) {
6944                         this._objectDeleter({'data': this._collection[objectId]._data});
6945                     }
6946                 }
6947             }
6948         },
6949 
6950          /**
6951           * The actual refresh operation, refactored out so we don't have to repeat code
6952           * @private
6953           */
6954         _RESTRefresh: function () {
6955             var _this = this;
6956             this._doGET({
6957                 success: function(rsp) {
6958                     if (_this._processResponse(rsp)) {
6959                         _this._buildRefreshedCollection(_this._data);
6960                     } else {
6961                         _this._errorNotifier.notifyListeners(_this);
6962                     }
6963                 },
6964                 error: function(rsp) {
6965                     _this._errorNotifier.notifyListeners(rsp);
6966                 }
6967             });            
6968         },
6969 
6970         /**
6971          * Force an update on this object. Since an asynchronous GET is performed,
6972          * it is necessary to have an onChange handler registered in order to be
6973          * notified when the response of this returns.
6974          * @returns {finesse.restservices.RestBaseCollection}
6975          *     This RestBaseCollection object to allow cascading
6976          */
6977          refresh: function() {
6978             var _this = this, isLoaded = this._loaded;
6979 
6980             // resubscribe if the collection requires an explicit subscription
6981             if (this.explicitSubscription) {
6982                 this._subscribeNode({
6983                     success: function () {
6984                         _this._RESTRefresh();
6985                     },
6986                     error: function (err) {
6987                         _this._errorNotifier.notifyListeners(err);
6988                     }
6989                 });
6990             } else {
6991                 this._RESTRefresh();
6992             }
6993 
6994             return this; // Allow cascading
6995          },
6996 
6997         /**
6998          * @private
6999          * The _addHandlerCb and _deleteHandlerCb require that data be passed in the
7000          * format of an array of {(Object Type): object} objects. For example, a
7001          * queues object would return [{Queue: queue1}, {Queue: queue2}, ...].
7002          * @param skipOuterObject If {true} is passed in for this param, then the "data"
7003          *                           property is returned instead of an object with the
7004          *                           data appended.
7005          * @return {Array}
7006          */
7007         extractCollectionData: function (skipOuterObject) {
7008             var restObjs,
7009             obj,
7010             result = [],
7011             _this = this;
7012             
7013             if (this._data)
7014             {
7015                 restObjs = this._data[this.getRestItemType()];
7016     
7017                 if (restObjs)
7018                 {
7019                     // check if there are multiple objects to pass
7020                     if (!$.isArray(restObjs))
7021                     {
7022                         restObjs = [restObjs];
7023                     }
7024     
7025                     // if so, create an object for each and add to result array
7026                     $.each(restObjs, function (id, object) {
7027                         if (skipOuterObject === true)
7028                         {
7029                             obj = object;
7030                         }
7031                         else
7032                         {
7033                             obj = {};
7034                             obj[_this.getRestItemType()] = object;
7035                         }
7036                         result.push(obj);
7037                     });
7038                 }
7039                 
7040             }
7041             
7042             return result;
7043         },
7044 
7045         /**
7046          * For Finesse, collections are handled uniquely on a POST and
7047          * doesn't necessary follow REST conventions. A POST on a collection
7048          * doesn't mean that the collection has been created, it means that an
7049          * item has been added to the collection. This function will generate
7050          * a closure which will handle this logic appropriately.
7051          * @param {Object} scope
7052          *     The scope of where the callback should be invoked.
7053          * @private
7054          */
7055         _addHandlerCb: function (scope) {
7056             return function (restItem) {
7057                 var data = restItem.extractCollectionData();               
7058 
7059                 $.each(data, function (id, object) {
7060                     scope._objectCreator(object);
7061                 });
7062             };
7063         },
7064 
7065         /**
7066          * For Finesse, collections are handled uniquely on a DELETE and
7067          * doesn't necessary follow REST conventions. A DELETE on a collection
7068          * doesn't mean that the collection has been deleted, it means that an
7069          * item has been deleted from the collection. This function will generate
7070          * a closure which will handle this logic appropriately.
7071          * @param {Object} scope
7072          *     The scope of where the callback should be invoked.
7073          * @private
7074          */
7075         _deleteHandlerCb: function (scope) {
7076             return function (restItem) {
7077                 var data = restItem.extractCollectionData();
7078 
7079                 $.each(data, function (id, obj) {
7080                     scope._objectDeleter(obj);
7081                 });
7082             };
7083         },
7084 
7085         /**
7086          * Utility method to process the update notification for Rest Items
7087          * that are children of the collection whose events are published to
7088          * the collection's node.
7089          * @param {Object} update
7090          *     The payload of an update notification.
7091          * @returns {Boolean}
7092          *     True if the update was successfully processed (the update object
7093          *     passed the schema validation) and updated the internal data cache,
7094          *     false otherwise.
7095          * @private
7096          */
7097         _processRestItemUpdate: function (update) {
7098             var object, objectId, updateObj = update.object.Update;
7099 
7100             //Extract the ID from the source if the Update was an error.
7101             if (updateObj.data.apiErrors) {
7102                 objectId = Utilities.getId(updateObj.source);
7103             }
7104             //Otherwise extract from the data object itself.
7105             else {
7106                 objectId = this._extractId(updateObj.data);
7107             }
7108 
7109             object = this._collection[objectId];
7110             if (object) {
7111                 if (object._processUpdate(update)) {
7112                     switch (updateObj.event) {
7113                     case "POST":
7114                         object._addNotifier.notifyListeners(object);
7115                         break;
7116                     case "PUT":
7117                         object._changeNotifier.notifyListeners(object);
7118                         break;
7119                     case "DELETE":
7120                         object._deleteNotifier.notifyListeners(object);
7121                         break;
7122                     }
7123                 }
7124             }
7125         },
7126 
7127         /**
7128          * SUBCLASS IMPLEMENTATION (override):
7129          * For collections, this callback has the additional responsibility of passing events
7130          * of collection item updates to the item objects themselves. The collection needs to
7131          * do this because item updates are published to the collection's node.
7132          * @returns {Function}
7133          *     The callback to be invoked when an update event is received
7134          * @private
7135          */
7136         _createPubsubCallback: function () {
7137             var _this = this;
7138             return function (update) {
7139                 //If the source of the update is our REST URL, this means the collection itself is modified
7140                 if (update.object.Update.source === _this.getRestUrl()) {
7141                     _this._updateEventHandler(_this, update);
7142                 } else {
7143                     //Otherwise, it is safe to assume that if we got an event on our topic, it must be a
7144                     //rest item update of one of our children that was published on our node (OpenAjax topic)
7145                     _this._processRestItemUpdate(update);
7146                 }
7147             };
7148         },
7149 
7150         /**
7151          * @class
7152          * This is the base collection object. 
7153          *
7154          * @constructs
7155          * @augments finesse.restservices.RestBase
7156          * @see finesse.restservices.Contacts
7157          * @see finesse.restservices.Dialogs
7158          * @see finesse.restservices.PhoneBooks
7159          * @see finesse.restservices.Queues
7160          * @see finesse.restservices.WorkflowActions
7161          * @see finesse.restservices.Workflows
7162          * @see finesse.restservices.WrapUpReasons
7163          */
7164         _fakeConstuctor: function () {
7165             /* This is here to hide the real init constructor from the public docs */
7166         },
7167         
7168        /**
7169          * @private
7170          * @param {Object} options
7171          *     An object with the following properties:<ul>
7172          *         <li><b>id:</b> The id of the object being constructed</li>
7173          *         <li><b>onCollectionAdd(this): (optional)</b> when an object is added to this collection</li>
7174          *         <li><b>onCollectionDelete(this): (optional)</b> when an object is removed from this collection</li>
7175          *         <li><b>onLoad(this): (optional)</b> when the collection is successfully loaded from the server</li>
7176          *         <li><b>onChange(this): (optional)</b> when an update notification of the collection is received. 
7177          *         This does not include adding and deleting members of the collection</li>
7178          *         <li><b>onAdd(this): (optional)</b> when a notification that the collection is created is received</li>
7179          *         <li><b>onDelete(this): (optional)</b> when a notification that the collection is deleted is received</li>
7180          *         <li><b>onError(rsp): (optional)</b> if loading of the collection fails, invoked with the error response object:<ul>
7181          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
7182          *             <li><b>content:</b> {String} Raw string of response</li>
7183          *             <li><b>object:</b> {Object} Parsed object of response</li>
7184          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
7185          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
7186          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
7187          *             </ul></li>
7188          *         </ul></li>
7189          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
7190          **/
7191         init: function (options) {
7192 
7193             options = options || {};
7194             options.id = "";
7195 
7196             //Make internal datastore collection to hold a list of objects.
7197             this._collection = {};
7198             this.length = 0;
7199 
7200             //Collections will have additional callbacks that will be invoked when
7201             //an item has been added/deleted.
7202             this._collectionAddNotifier = new Notifier();
7203             this._collectionDeleteNotifier = new Notifier();
7204 
7205             //Initialize the base class.
7206             this._super(options);
7207 
7208             this.addHandler('collectionAdd', options.onCollectionAdd);
7209             this.addHandler('collectionDelete', options.onCollectionDelete);
7210 
7211             //For Finesse, collections are handled uniquely on a POST/DELETE and
7212             //doesn't necessary follow REST conventions. A POST on a collection
7213             //doesn't mean that the collection has been created, it means that an
7214             //item has been added to the collection. A DELETE means that an item has
7215             //been removed from the collection. Due to this, we are attaching
7216             //special callbacks to the add/delete that will handle this logic.
7217             this.addHandler("add", this._addHandlerCb(this));
7218             this.addHandler("delete", this._deleteHandlerCb(this));
7219         },
7220 
7221         /**
7222          * Returns the collection.
7223          * @returns {Object}
7224          *     The collection as an object
7225          */
7226         getCollection: function () {
7227             //TODO: is this safe? or should we instead return protected functions such as .each(function)?
7228             return this._collection;
7229         },
7230 
7231         /**
7232          * Utility method to build the internal collection data structure (object) based on provided data
7233          * @param {Object} data
7234          *     The data to build the internal collection from
7235          * @private
7236          */
7237         _buildCollection: function (data) {
7238             var i, object, objectId, dataArray;
7239             if (data && this.getProperty(data, this.getRestItemType()) !== null) {
7240                 dataArray = Utilities.getArray(this.getProperty(data, this.getRestItemType()));
7241                 for (i = 0; i < dataArray.length; i += 1) {
7242 
7243                     object = {};
7244                     object[this.getRestItemType()] = dataArray[i];
7245                     objectId = this._extractId(object);
7246                     this._collection[objectId] = new (this.getRestItemClass())({
7247                         doNotSubscribe: this.handlesItemSubscription,
7248                         doNotRefresh: this.handlesItemRefresh,
7249                         id: objectId,
7250                         data: object
7251                     });
7252                     this.length += 1;
7253                 }
7254             }
7255         },
7256 
7257         /**
7258          * Called to know whether to include an item in the _collection and _data. Return false to keep it, true to filter out (discard) it.
7259          * Override this in subclasses if you need only object with certain attribute values.
7260          * @param  {Object} item Item to test.
7261          * @return {Boolean} False to keep, true to filter out (discard);
7262          */
7263         _filterOutItem: function (item) {
7264             return false;
7265         },
7266     
7267         /**
7268          * Validate and store the object into the internal data store.
7269          * SUBCLASS IMPLEMENTATION (override):
7270          * Performs collection specific logic to _buildCollection internally based on provided data
7271          * @param {Object} object
7272          *     The JavaScript object that should match of schema of this REST object.
7273          * @returns {Boolean}
7274          *     True if the object was validated and stored successfully.
7275          * @private
7276          */
7277         _processObject: function (object) {
7278             var i,
7279                 restItemType = this.getRestItemType(),
7280                 items;
7281             if (this._validate(object)) {
7282                 this._data = this.getProperty(object, this.getRestType()); // Should clone the object here?
7283     
7284                 // If a subclass has overriden _filterOutItem then we'll need to run through the items and remove them
7285                 if (this._data)
7286                 {
7287                     items = this._data[restItemType];
7288     
7289                     if (typeof(items) !== "undefined")
7290                     {
7291                         if (typeof(items.length) === "undefined")
7292                         {
7293                             // Single object
7294                             if (this._filterOutItem(items))
7295                             {
7296                                 this._data[restItemType] = items = [];
7297                             }
7298                             
7299                         }
7300                         else
7301                         {
7302                             // filter out objects
7303                             for (i = items.length - 1; i !== -1; i = i - 1)
7304                             {
7305                                 if (this._filterOutItem(items[i]))
7306                                 {
7307                                     items.splice(i, 1);
7308                                 }
7309                             }
7310                         }
7311                     }
7312                 }
7313     
7314                 // If loaded for the first time, call the load notifiers.
7315                 if (!this._loaded) {
7316                     this._buildCollection(this._data);
7317                     this._loaded = true;
7318                     this._loadNotifier.notifyListeners(this);
7319                 }
7320                 
7321                 return true;
7322                 
7323             }
7324             return false;
7325         },
7326 
7327         /**
7328          * Retrieves a reference to a particular notifierType.
7329          * @param {String} notifierType
7330          *      Specifies the notifier to retrieve (load, change, error, add, delete)
7331          * @return {Notifier} The notifier object.
7332          */
7333         _getNotifierReference: function (notifierType) {
7334             var notifierReference;
7335 
7336             try {
7337                 //Use the base method to get references for load/change/error.
7338                 notifierReference = this._super(notifierType);
7339             } catch (err) {
7340                 //Check for add/delete
7341                 if (notifierType === "collectionAdd") {
7342                     notifierReference = this._collectionAddNotifier;
7343                 } else if (notifierType === "collectionDelete") {
7344                     notifierReference = this._collectionDeleteNotifier;
7345                 } else {
7346                     //Rethrow exception from base class.
7347                     throw err;
7348                 }
7349             }
7350             return notifierReference;
7351         }
7352     });
7353     
7354     window.finesse = window.finesse || {};
7355     window.finesse.restservices = window.finesse.restservices || {};
7356     window.finesse.restservices.RestCollectionBase = RestCollectionBase;
7357     
7358     return RestCollectionBase;
7359 });
7360 
7361 /**
7362  * JavaScript representation of the Finesse Dialog object.
7363  *
7364  * @requires finesse.clientservices.ClientServices
7365  * @requires Class
7366  * @requires finesse.FinesseBase
7367  * @requires finesse.restservices.RestBase
7368  */
7369 
7370 /** @private */
7371 define('restservices/Dialog',[
7372     'restservices/RestBase',
7373     'utilities/Utilities'
7374 ],
7375 function (RestBase, Utilities) {
7376     var Dialog = RestBase.extend(/** @lends finesse.restservices.Dialog.prototype */{
7377 
7378         /**
7379          * @class
7380          * A Dialog is an attempted connection between or among multiple participants,
7381          * for example, a regular phone call, a conference, or a silent monitor session.
7382          * 
7383          * @augments finesse.restservices.RestBase
7384          * @constructs
7385          */
7386         _fakeConstuctor: function () {
7387             /* This is here to hide the real init constructor from the public docs */
7388         },
7389         
7390         /**
7391          * @private
7392          *
7393          * @param {Object} options
7394          *     An object with the following properties:<ul>
7395          *         <li><b>id:</b> The id of the object being constructed</li>
7396          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
7397          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
7398          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
7399          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
7400          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
7401          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
7402          *             <li><b>content:</b> {String} Raw string of response</li>
7403          *             <li><b>object:</b> {Object} Parsed object of response</li>
7404          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
7405          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
7406          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
7407          *             </ul></li>
7408          *         </ul></li>
7409          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
7410          **/
7411         init: function (options) {
7412             this._super(options);
7413         },
7414 
7415         /**
7416          * @private
7417          * Gets the REST class for the current object - this is the Dialog class.
7418          * @returns {Object} The Dialog class.
7419          */
7420         getRestClass: function () {
7421             return Dialog;
7422         },
7423         
7424         /**
7425          * @private
7426          * The constant for agent device.
7427          */
7428         _agentDeviceType: "AGENT_DEVICE",
7429         
7430         /**
7431          * @private
7432          * Gets the REST type for the current object - this is a "Dialog".
7433          * @returns {String} The Dialog string.
7434          */
7435         getRestType: function () {
7436             return "Dialog";
7437         },
7438 
7439         /**
7440          * @private
7441          * Override default to indicate that this object doesn't support making
7442          * requests.
7443          */
7444         supportsRequests: false,
7445 
7446         /**
7447          * @private
7448          * Override default to indicate that this object doesn't support subscriptions.
7449          */
7450         supportsSubscriptions: false,
7451 
7452         /**
7453          * The requestId reaper timeout in ms
7454          */
7455         REQUESTID_REAPER_TIMEOUT: 5000,
7456 
7457         /**
7458          * Getter for the from address.
7459          * @returns {String} The from address.
7460          */
7461         getFromAddress: function () {
7462             this.isLoaded();
7463             return this.getData().fromAddress;
7464         },
7465 
7466         /**
7467          * Getter for the to address.
7468          * @returns {String} The to address.
7469          */
7470         getToAddress: function () {
7471             this.isLoaded();
7472             return this.getData().toAddress;
7473         },
7474         
7475         /**
7476          * Getter for the media type.
7477          * @returns {String} The media type.
7478          */
7479         getMediaType: function () {
7480             this.isLoaded();
7481             return this.getData().mediaType;
7482         },
7483         
7484         /**
7485          * @private
7486          * Getter for the uri.
7487          * @returns {String} The uri.
7488          */
7489         getDialogUri: function () {
7490             this.isLoaded();
7491             return this.getData().uri;
7492         },
7493 
7494         /**
7495          * Getter for the callType.
7496          * @deprecated Use getMediaProperties().callType instead.
7497          * @returns {String} The callType.
7498          */
7499         getCallType: function () {
7500             this.isLoaded();
7501             return this.getData().mediaProperties.callType;
7502         },
7503 
7504         /**
7505          * Getter for the DNIS. This is usually the actual number dialed.
7506          * @deprecated Use getMediaProperties().DNIS instead.
7507          * @returns {String} The callType.
7508          */
7509         getDNIS: function () {
7510             this.isLoaded();
7511             return this.getData().mediaProperties.DNIS;
7512         },
7513         
7514         /**
7515          * Getter for the Dialog state.
7516          * @returns {String} The Dialog state.
7517          */
7518         getState: function () {
7519             this.isLoaded();
7520             return this.getData().state;
7521         },
7522 
7523         /**
7524          * Retrieves a list of participants within the Dialog object.
7525          * @returns {Object} Array list of participants.
7526          */
7527         getParticipants: function () {
7528             this.isLoaded();
7529             var participants = this.getData().participants.Participant;
7530             //Due to the nature of the XML->JSO converter library, a single
7531             //element in the XML array will be considered to an object instead of
7532             //a real array. This will handle those cases to ensure that an array is
7533             //always returned.
7534 
7535             return Utilities.getArray(participants);
7536         },
7537 
7538        /**
7539          * gets the participant timer counters 
7540          *
7541          * @param {String} participantExt Extension of participant.
7542          * @returns {Object} which contains state, startTime, and stateChangeTime fields
7543          */
7544         getParticipantTimerCounters : function (participantExt) {
7545           var part, participantTimerCounters = {}, idx, participants;
7546           
7547           participants = this.getParticipants();
7548 
7549 
7550           //Loop through all the participants and find the right participant (based on participantExt)
7551           for(idx=0;idx<participants.length;idx=idx+1)
7552           {
7553             part = participants[idx];
7554             
7555             if (part.mediaAddress === participantExt)
7556             {
7557                 participantTimerCounters.startTime= part.startTime;
7558                 participantTimerCounters.stateChangeTime= part.stateChangeTime;
7559                 participantTimerCounters.state= part.state;
7560                 break;
7561             }
7562           }
7563           
7564           return participantTimerCounters;
7565         },
7566         
7567         /**
7568          * Determines the droppable participants.  A droppable participant is a participant that is an agent extension.   
7569          * (It is not a CTI Route Point, IVR Port, or the caller)
7570          * 
7571          * @param {String} filterExtension used to remove a single extension from the list
7572          * @returns {Array} Participants which is an array of all participants which can be dropped.
7573          */
7574         getDroppableParticipants: function (filterExtension) {
7575           this.isLoaded();
7576           var droppableParticipants = [], participants, index, idx, filterExtensionToRemove = "", callStateOk, part;
7577 
7578           participants = this.getParticipants();
7579 
7580           if (filterExtension)
7581           {
7582             filterExtensionToRemove = filterExtension;
7583           }
7584 
7585           //Loop through all the participants to remove non-agents & remove filterExtension
7586           //We could have removed filterExtension using splice, but we have to iterate through
7587           //the list anyway.
7588           for(idx=0;idx<participants.length;idx=idx+1)
7589           {
7590             part = participants[idx];
7591 
7592             //Skip the filterExtension
7593             if (part.mediaAddress !== filterExtensionToRemove)
7594             {
7595                 callStateOk = this._isParticipantStateDroppable(part);
7596 
7597                 //Remove non-agents & make sure callstate 
7598                 if (callStateOk === true && part.mediaAddressType === this._agentDeviceType)
7599                 {
7600                   droppableParticipants.push(part);
7601                 }
7602             }
7603         }
7604 
7605         return Utilities.getArray(droppableParticipants);
7606         },
7607 
7608         _isParticipantStateDroppable : function (part)
7609         {
7610           var isParticipantStateDroppable = false;
7611           if (part.state === Dialog.ParticipantStates.ACTIVE || part.state === Dialog.ParticipantStates.ACCEPTED || part.state === Dialog.ParticipantStates.HELD)
7612           {
7613             isParticipantStateDroppable = true;
7614           }
7615           
7616           return isParticipantStateDroppable;
7617         },
7618         
7619         /**
7620          * Is the participant droppable
7621          *
7622          * @param {String} participantExt Extension of participant.
7623          * @returns {Boolean} True is droppable.
7624          */
7625         isParticipantDroppable : function (participantExt) {
7626           var droppableParticipants = null, isDroppable = false, idx, part, callStateOk;
7627           
7628           droppableParticipants = this.getDroppableParticipants();
7629           
7630           if (droppableParticipants) 
7631           {
7632             for(idx=0;idx<droppableParticipants.length;idx=idx+1)
7633             {
7634               part = droppableParticipants[idx];
7635              
7636               if (part.mediaAddress === participantExt)
7637               {
7638                 callStateOk = this._isParticipantStateDroppable(part);
7639 
7640                 //Remove non-agents & make sure callstate 
7641                 if (callStateOk === true && part.mediaAddressType === this._agentDeviceType)
7642                 {
7643                   isDroppable = true;
7644                   break;
7645                 }
7646               }
7647             }
7648           }
7649           
7650           return isDroppable;
7651         },
7652         
7653         /**
7654          * Retrieves a list of media properties from the dialog object.
7655          * @returns {Object} Map of call variables; names mapped to values.
7656          * Variables may include the following:<ul>
7657          * <li>dialedNumber: The number dialed.
7658          * <li>callType: The type of call. Call types include:<ul>
7659          *     <li>ACD_IN
7660          *     <li>PREROUTE_ACD_IN
7661          *     <li>PREROUTE_DIRECT_AGENT
7662          *     <li>TRANSFER
7663          *     <li>OTHER_IN
7664          *     <li>OUT
7665          *     <li>AGENT_INSIDE
7666          *     <li>CONSULT
7667          *     <li>CONFERENCE
7668          *     <li>SUPERVISOR_MONITOR
7669          *     <li>OUTBOUND
7670          *     <li>OUTBOUND_PREVIEW</ul>
7671          * <li>DNIS: The DNIS provided. For routed calls, this is the route point.
7672          * <li>wrapUpReason: A description of the call.
7673          * <li>Call Variables, by name.  The name indicates whether it is a call variable or ECC variable.
7674          * Call variable names start with callVariable#, where # is 1-10. ECC variable names (both scalar and array) are prepended with "user".
7675          * ECC variable arrays include an index enclosed within square brackets located at the end of the ECC array name.
7676          * <li>The following call variables provide additional details about an Outbound Option call:<ul>
7677          *     <li>BACampaign
7678          *     <li>BAAccountNumber
7679          *     <li>BAResponse
7680          *     <li>BAStatus<ul>
7681          *         <li>PREDICTIVE_OUTBOUND: A predictive outbound call.
7682          *         <li>PROGRESSIVE_OUTBOUND: A progressive outbound call.
7683          *         <li>PREVIEW_OUTBOUND_RESERVATION: Agent is reserved for a preview outbound call.
7684          *         <li>PREVIEW_OUTBOUND: Agent is on a preview outbound call.</ul>
7685          *     <li>BADialedListID
7686          *     <li>BATimeZone
7687          *     <li>BABuddyName</ul></ul>
7688          *     
7689          */
7690         getMediaProperties: function () {
7691             var mpData, currentMediaPropertiesMap, thisMediaPropertiesJQuery;
7692 
7693             this.isLoaded();
7694             
7695             // We have to convert to jQuery object to do a proper compare
7696             thisMediaPropertiesJQuery = jQuery(this.getData().mediaProperties);
7697             
7698             if ((this._lastMediaPropertiesJQuery !== undefined) 
7699                     && (this._lastMediaPropertiesMap !== undefined) 
7700                     && (this._lastMediaPropertiesJQuery.is(thisMediaPropertiesJQuery))) {
7701 
7702                     return this._lastMediaPropertiesMap;
7703             }
7704             
7705             currentMediaPropertiesMap = {};
7706 
7707             mpData = this.getData().mediaProperties;
7708 
7709             if (mpData) {
7710                 if (mpData.callvariables && mpData.callvariables.CallVariable) {
7711                     jQuery.each(mpData.callvariables.CallVariable, function (i, callVariable) {
7712 			currentMediaPropertiesMap[callVariable.name] = callVariable.value;
7713                     });
7714                 }
7715 
7716                 jQuery.each(mpData, function (key, value) {
7717                     if (key !== 'callvariables') {
7718 			currentMediaPropertiesMap[key] = value;
7719                     }
7720                 });
7721             }
7722             
7723             this._lastMediaPropertiesMap = currentMediaPropertiesMap;
7724             this._lastMediaPropertiesJQuery = thisMediaPropertiesJQuery;
7725 
7726             return this._lastMediaPropertiesMap;
7727         },
7728 
7729         /**
7730          * Retrieves information about the currently scheduled callback, if any.
7731          * @returns {Object} If no callback has been set, will return undefined. If 
7732          * a callback has been set, it will return a map with one or more of the 
7733          * following entries, depending on what values have been set. 
7734          *    callbackTime   - the callback time, if it has been set.
7735          *    callbackNumber - the callback number, if it has been set.
7736          */
7737         getCallbackInfo: function() {
7738             this.isLoaded();
7739             return this.getData().scheduledCallbackInfo;
7740         },
7741 
7742         /**
7743          * @private
7744          * Invoke a request to the server given a content body and handlers.
7745          *
7746          * @param {Object} contentBody
7747          *     A JS object containing the body of the action request.
7748          * @param {finesse.interfaces.RequestHandlers} handlers
7749          *     An object containing the handlers for the request
7750          */
7751         _makeRequest: function (contentBody, handlers) {
7752             // Protect against null dereferencing of options allowing its
7753             // (nonexistent) keys to be read as undefined
7754             handlers = handlers || {};
7755 
7756             this.restRequest(this.getRestUrl(), {
7757                 method: 'PUT',
7758                 success: handlers.success,
7759                 error: handlers.error,
7760                 content: contentBody
7761             });
7762         },
7763 
7764         /**
7765          * Invoke a consult call out to a destination.
7766          *
7767          * @param {String} mediaAddress
7768          *     The media address of the user performing the consult call.
7769          * @param {String} toAddress
7770          *     The destination address of the consult call.
7771          * @param {finesse.interfaces.RequestHandlers} handlers
7772          *     An object containing the handlers for the request
7773          */
7774         makeConsultCall: function (mediaAddress, toAddress, handlers) {
7775             this.isLoaded();
7776             var contentBody = {};
7777             contentBody[this.getRestType()] = {
7778                 "targetMediaAddress": mediaAddress,
7779                 "toAddress": toAddress,
7780                 "requestedAction": Dialog.Actions.CONSULT_CALL
7781             };
7782             this._makeRequest(contentBody, handlers);
7783             return this; // Allow cascading
7784         },
7785         
7786         /**
7787          * Invoke a single step transfer request.
7788          *
7789          * @param {String} mediaAddress
7790          *     The media address of the user performing the single step transfer.
7791          * @param {String} toAddress
7792          *     The destination address of the single step transfer.
7793          * @param {finesse.interfaces.RequestHandlers} handlers
7794          *     An object containing the handlers for the request
7795          */
7796         initiateDirectTransfer: function (mediaAddress, toAddress, handlers) {
7797             this.isLoaded();
7798             var contentBody = {};
7799             contentBody[this.getRestType()] = {
7800                 "targetMediaAddress": mediaAddress,
7801                 "toAddress": toAddress,
7802                 "requestedAction": Dialog.Actions.TRANSFER_SST
7803             };
7804             this._makeRequest(contentBody, handlers);
7805             return this; // Allow cascading
7806         },
7807 
7808         /**
7809          * Update this dialog's wrap-up reason.
7810          *
7811          * @param {String} wrapUpReason
7812          *     The new wrap-up reason for this dialog
7813          * @param {finesse.interfaces.RequestHandlers} handlers
7814          *     An object containing the handlers for the request
7815          */
7816         updateWrapUpReason: function (wrapUpReason, options)
7817         {
7818             this.isLoaded();
7819             var mediaProperties =
7820             {
7821                 "wrapUpReason": wrapUpReason
7822             };
7823 
7824             options = options || {};
7825             options.content = {};
7826             options.content[this.getRestType()] =
7827             {
7828                 "mediaProperties": mediaProperties,
7829                 "requestedAction": Dialog.Actions.UPDATE_CALL_DATA
7830             };
7831             options.method = "PUT";
7832             this.restRequest(this.getRestUrl(), options);
7833 
7834             return this;
7835         },
7836 
7837         /**
7838          * Invoke a request to server based on the action given.
7839          * @param {String} mediaAddress
7840          *     The media address of the user performing the action.
7841          * @param {finesse.restservices.Dialog.Actions} action
7842          *     The action string indicating the action to invoke on dialog.
7843          * @param {finesse.interfaces.RequestHandlers} handlers
7844          *     An object containing the handlers for the request
7845          */
7846         requestAction: function (mediaAddress, action, handlers) {
7847             this.isLoaded();
7848             var contentBody = {};
7849             contentBody[this.getRestType()] = {
7850                 "targetMediaAddress": mediaAddress,
7851                 "requestedAction": action
7852             };
7853             this._makeRequest(contentBody, handlers);
7854             return this; // Allow cascading
7855         },
7856         
7857         /**
7858          * Wrapper around "requestAction" to request PARTICIPANT_DROP action.
7859          *
7860          * @param targetMediaAddress is the address to drop
7861          * @param {finesse.interfaces.RequestHandlers} handlers
7862          *     An object containing the handlers for the request
7863          */
7864         dropParticipant: function (targetMediaAddress, handlers) {
7865             this.requestAction(targetMediaAddress, Dialog.Actions.PARTICIPANT_DROP, handlers);
7866         },
7867         
7868         /**
7869          * Invoke a request to server to send DTMF digit tones.
7870          * @param {String} mediaAddress
7871          * @param {String} action
7872          *     The action string indicating the action to invoke on dialog.
7873          * @param {finesse.interfaces.RequestHandlers} handlers
7874          *     An object containing the handlers for the request
7875          */
7876         sendDTMFRequest: function (mediaAddress, handlers, digit) {
7877             this.isLoaded();
7878             var contentBody = {};
7879             contentBody[this.getRestType()] = {
7880                 "targetMediaAddress": mediaAddress,
7881                 "requestedAction": "SEND_DTMF",
7882                 "actionParams": {
7883                     "ActionParam": {
7884                         "name": "dtmfString",
7885                         "value": digit
7886                     }
7887                 }
7888             };
7889             this._makeRequest(contentBody, handlers);
7890             return this; // Allow cascading
7891         },
7892 
7893         /**
7894          * Invoke a request to server to set the time for a callback.
7895          * @param {String} mediaAddress
7896          * @param {String} callbackTime 
7897          *     The requested time for the callback, in YYYY-MM-DDTHH:MM format
7898          *     (ex: 2013-12-24T23:59)
7899          * @param {finesse.interfaces.RequestHandlers} handlers
7900          *     An object containing the handlers for the request
7901          */
7902         updateCallbackTime: function (mediaAddress, callbackTime, handlers) {
7903             this.isLoaded();
7904             var contentBody = {};
7905             contentBody[this.getRestType()] = {
7906                 "targetMediaAddress": mediaAddress,
7907                 "requestedAction": Dialog.Actions.UPDATE_SCHEDULED_CALLBACK,
7908                 "actionParams": {
7909                     "ActionParam": {
7910                         "name": "callbackTime",
7911                         "value": callbackTime
7912                     }
7913                 }
7914             };
7915             this._makeRequest(contentBody, handlers);
7916             return this; // Allow cascading
7917         },
7918 
7919         /**
7920          * Invoke a request to server to set the number for a callback.
7921          * @param {String} mediaAddress
7922          * @param {String} callbackNumber
7923          *     The requested number to call for the callback
7924          * @param {finesse.interfaces.RequestHandlers} handlers
7925          *     An object containing the handlers for the request
7926          */
7927         updateCallbackNumber: function (mediaAddress, callbackNumber, handlers) {
7928             this.isLoaded();
7929             var contentBody = {};
7930             contentBody[this.getRestType()] = {
7931                 "targetMediaAddress": mediaAddress,
7932                 "requestedAction": Dialog.Actions.UPDATE_SCHEDULED_CALLBACK,
7933                 "actionParams": {
7934                     "ActionParam": {
7935                         "name": "callbackNumber",
7936                         "value": callbackNumber
7937                     }
7938                 }
7939             };
7940             this._makeRequest(contentBody, handlers);
7941             return this; // Allow cascading
7942         },
7943 
7944         /**
7945          * Invoke a request to server to cancel a callback.
7946          * @param {String} mediaAddress
7947          * @param {finesse.interfaces.RequestHandlers} handlers
7948          *     An object containing the handlers for the request
7949          */
7950         cancelCallback: function (mediaAddress, handlers) {
7951             this.isLoaded();
7952             var contentBody = {};
7953             contentBody[this.getRestType()] = {
7954                 "targetMediaAddress": mediaAddress,
7955                 "requestedAction": Dialog.Actions.CANCEL_SCHEDULED_CALLBACK
7956             };
7957             this._makeRequest(contentBody, handlers);
7958             return this; // Allow cascading
7959         },
7960 
7961         /**
7962          * Invoke a request to server to reclassify the call type.
7963          * @param {String} mediaAddress
7964          *     The media address of the user performing the consult call.
7965          * @param {String} classification
7966          *     The classification to assign to the call. Valid values are "VOICE", "FAX",
7967          *     "ANS_MACHINE", "INVALID", "BUSY" (CCX only), and "DO_NOT_CALL".
7968          * @param {finesse.interfaces.RequestHandlers} handlers
7969          *     An object containing the handlers for the request
7970          */
7971         reclassifyCall: function (mediaAddress, classification, handlers) {
7972             this.isLoaded();
7973             var contentBody = {};
7974             contentBody[this.getRestType()] = {
7975                 "targetMediaAddress": mediaAddress,
7976                 "requestedAction": Dialog.Actions.RECLASSIFY,
7977                 "actionParams": {
7978                     "ActionParam": {
7979                         "name": "outboundClassification",
7980                         "value": classification
7981                     }
7982                 }
7983             };
7984             this._makeRequest(contentBody, handlers);
7985             return this; // Allow cascading
7986         },
7987 
7988         /**
7989          * Utility method to create a closure containing the requestId and the Dialogs object so 
7990          * that the _pendingCallbacks map can be manipulated when the timer task is executed.
7991          * @param  {String} requestId The requestId of the event
7992          * @return {Function}           The function to be executed by setTimeout
7993          */
7994         _createRequestIdReaper: function (requestId) {
7995             var that = this;
7996             return function () {
7997                 that._logger.log("Dialog: clearing the requestId-to-callbacks mapping for requestId=" + requestId);
7998                 delete that._pendingCallbacks[requestId];
7999             };
8000         },
8001 
8002         /**
8003          * Overriding implementation of the one in RestBase.js
8004          * This determines the strategy that Dialogs will take after processing an event that contains a requestId.
8005          * @param  {String} requestId The requestId of the event
8006          */
8007         _postProcessUpdateStrategy: function (requestId) {
8008             this._logger.log("Dialog: determining whether to set timeout for clearing requestId-to-callbacks mapping requestId=" + requestId);
8009             var callbacksObj = this._pendingCallbacks[requestId];
8010             if (callbacksObj && !callbacksObj.used) {
8011                 this._logger.log("Dialog: setting timeout for clearing requestId-to-callbacks mapping requestId=" + requestId);
8012                 setTimeout(this._createRequestIdReaper(requestId), this.REQUESTID_REAPER_TIMEOUT);
8013                 callbacksObj.used = true;
8014             }            
8015         }
8016     });
8017 
8018     Dialog.Actions = /** @lends finesse.restservices.Dialog.Actions.prototype */ {
8019             /**
8020              * Drops the Participant from the Dialog.
8021              */
8022             DROP: "DROP",
8023             /**
8024              * Answers a Dialog.
8025              */
8026             ANSWER: "ANSWER",
8027             /**
8028              * Holds the Dialog.
8029              */
8030             HOLD: "HOLD",
8031             /**
8032              * Barges into a Call Dialog.
8033              */
8034             BARGE_CALL: "BARGE_CALL",
8035             /**
8036              * Allow as Supervisor to Drop a Participant from the Dialog.
8037              */
8038             PARTICIPANT_DROP: "PARTICIPANT_DROP",
8039             /**
8040              * Makes a new Call Dialog.
8041              */
8042             MAKE_CALL: "MAKE_CALL",
8043             /**
8044              * Retrieves a Dialog that is on Hold.
8045              */
8046             RETRIEVE: "RETRIEVE",
8047             /**
8048              * Sets the time or number for a callback. Can be
8049              * either a new callback, or updating an existing one.
8050              */
8051             UPDATE_SCHEDULED_CALLBACK: "UPDATE_SCHEDULED_CALLBACK",
8052             /**
8053              * Cancels a callback.
8054              */
8055             CANCEL_SCHEDULED_CALLBACK: "CANCEL_SCHEDULED_CALLBACK",
8056             /**
8057              * Initiates a Consult Call.
8058              */
8059             CONSULT_CALL: "CONSULT_CALL",
8060             /**
8061              * Initiates a Transfer of a Dialog.
8062              */
8063             TRANSFER: "TRANSFER",
8064             /**
8065              * Initiates a Single-Step Transfer of a Dialog.
8066              */
8067             TRANSFER_SST: "TRANSFER_SST",
8068             /**
8069              * Initiates a Conference of a Dialog.
8070              */
8071             CONFERENCE: "CONFERENCE",
8072             /**
8073              * Changes classification for a call
8074              */
8075             RECLASSIFY: "RECLASSIFY", 
8076             /**
8077              * Updates data on a Call Dialog.
8078              */
8079             UPDATE_CALL_DATA: "UPDATE_CALL_DATA",
8080             /**
8081              * Initiates a Recording on a Call Dialog.
8082              */
8083             START_RECORDING : "START_RECORDING",
8084             /**
8085              * Sends DTMF (dialed digits) to a Call Dialog.
8086              */
8087             DTMF : "SEND_DTMF",            
8088             /**
8089              * Accepts a Dialog that is being Previewed.
8090              */
8091             ACCEPT: "ACCEPT",
8092             /**
8093              * Rejects a Dialog.
8094              */
8095             REJECT: "REJECT",
8096             /**
8097              * Closes a Dialog.
8098              */
8099             CLOSE : "CLOSE",
8100             /**
8101              * @class Set of action constants for a Dialog.  These should be used for
8102              * {@link finesse.restservices.Dialog#requestAction}.
8103              * @constructs
8104              */
8105             _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
8106         };  
8107 
8108     Dialog.States = /** @lends finesse.restservices.Dialog.States.prototype */ {
8109        /**
8110          * Indicates that the call is ringing at a device.
8111          */
8112         ALERTING: "ALERTING",
8113         /**
8114          * Indicates that the phone is off the hook at a device.
8115          */
8116         INITIATING: "INITIATING",
8117         /**
8118          * Indicates that the dialog has a least one active participant.
8119          */
8120         ACTIVE: "ACTIVE",
8121         /**
8122          * Indicates that the dialog has no active participants.
8123          */
8124         DROPPED: "DROPPED",
8125         /**
8126          * Indicates that the phone is dialing at the device.
8127          */
8128         INITIATED: "INITIATED",
8129         /**
8130          * Indicates that the dialog has failed.
8131          * @see Dialog.ReasonStates
8132          */
8133         FAILED: "FAILED",
8134         /**
8135          * Indicates that the user has accepted an OUTBOUND_PREVIEW dialog.
8136          */
8137         ACCEPTED: "ACCEPTED",
8138         /**
8139          * @class Possible Dialog State constants.
8140          * The State flow of a typical in-bound Dialog is as follows: INITIATING, INITIATED, ALERTING, ACTIVE, DROPPED.
8141          * @constructs
8142          */
8143         _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
8144     };
8145 
8146     Dialog.ParticipantStates = /** @lends finesse.restservices.Dialog.ParticipantStates.prototype */ {
8147         /**
8148           * Indicates that an incoming call is ringing on the device.
8149           */
8150          ALERTING: "ALERTING",
8151          /**
8152           * Indicates that an outgoing call, not yet active, exists on the device.
8153           */
8154          INITIATING: "INITIATING",
8155          /**
8156           * Indicates that the participant is active on the call.
8157           */
8158          ACTIVE: "ACTIVE",
8159          /**
8160           * Indicates that the participant has dropped from the call.
8161           */
8162          DROPPED: "DROPPED",
8163          /**
8164           * Indicates that the participant has held their connection to the call.
8165           */
8166          HELD: "HELD",
8167          /**
8168           * Indicates that the phone is dialing at a device.
8169           */
8170          INITIATED: "INITIATED",
8171          /**
8172           * Indicates that the call failed.
8173           * @see Dialog.ReasonStates
8174           */
8175          FAILED: "FAILED",
8176          /**
8177           * Indicates that the participant is not in active state on the call, but is wrapping up after the participant has dropped from the call.
8178           */
8179          WRAP_UP: "WRAP_UP",
8180          /**
8181           * Indicates that the participant has accepted the dialog.  This state is applicable to OUTBOUND_PREVIEW dialogs.
8182           */
8183          ACCEPTED: "ACCEPTED",
8184          /**
8185           * @class Possible Dialog Participant State constants.
8186           * @constructs
8187           */
8188          _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
8189      };
8190 
8191     Dialog.ReasonStates = /** @lends finesse.restservices.Dialog.ReasonStates.prototype */ {
8192        /**
8193         * Dialog was Busy.  This will typically be for a Failed Dialog.
8194         */
8195         BUSY: "BUSY",
8196         /**
8197          * Dialog reached a Bad Destination.  This will typically be for a Failed Dialog.
8198          */
8199         BAD_DESTINATION: "BAD_DESTINATION",
8200         /**
8201          * All Other Reasons.  This will typically be for a Failed Dialog.
8202          */
8203         OTHER: "OTHER",
8204         /**
8205          * The Device Resource for the Dialog was not available.
8206          */
8207         DEVICE_RESOURCE_NOT_AVAILABLE : "DEVICE_RESOURCE_NOT_AVAILABLE",
8208         /**
8209          * @class Possible dialog state reasons code constants.
8210          * @constructs
8211          */
8212         _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
8213     };
8214    
8215     window.finesse = window.finesse || {};
8216     window.finesse.restservices = window.finesse.restservices || {};
8217     window.finesse.restservices.Dialog = Dialog;
8218     
8219     
8220     return Dialog;
8221 });
8222 
8223 /**
8224  * JavaScript representation of the Finesse Dialogs collection
8225  * object which contains a list of Dialog objects.
8226  *
8227  * @requires finesse.clientservices.ClientServices
8228  * @requires Class
8229  * @requires finesse.FinesseBase
8230  * @requires finesse.restservices.RestBase
8231  * @requires finesse.restservices.Dialog
8232  */
8233 /** @private */
8234 define('restservices/Dialogs',[
8235     'restservices/RestCollectionBase',
8236     'restservices/Dialog'
8237 ],
8238 function (RestCollectionBase, Dialog) {
8239     var Dialogs = RestCollectionBase.extend(/** @lends finesse.restservices.Dialogs.prototype */{
8240 
8241         /**
8242          * @class
8243          * JavaScript representation of a Dialogs collection object. Also exposes
8244          * methods to operate on the object against the server.
8245          * @augments finesse.restservices.RestCollectionBase
8246          * @constructs
8247          * @see finesse.restservices.Dialog
8248          * @example
8249          *  _dialogs = _user.getDialogs( {
8250          *      onCollectionAdd : _handleDialogAdd,
8251          *      onCollectionDelete : _handleDialogDelete,
8252          *      onLoad : _handleDialogsLoaded
8253          *  });
8254          *  
8255          * _dialogCollection = _dialogs.getCollection();
8256          * for (var dialogId in _dialogCollection) {
8257          *     if (_dialogCollection.hasOwnProperty(dialogId)) {
8258          *         _dialog = _dialogCollection[dialogId];
8259          *         etc...
8260          *     }
8261          * }
8262          */
8263         _fakeConstuctor: function () {
8264             /* This is here to hide the real init constructor from the public docs */
8265         },
8266         
8267         /**
8268          * @private
8269          * @param {Object} options
8270          *     An object with the following properties:<ul>
8271          *         <li><b>id:</b> The id of the object being constructed</li>
8272          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
8273          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
8274          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
8275          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
8276          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
8277          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
8278          *             <li><b>content:</b> {String} Raw string of response</li>
8279          *             <li><b>object:</b> {Object} Parsed object of response</li>
8280          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
8281          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
8282          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
8283          *             </ul></li>
8284          *         </ul></li>
8285          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
8286          **/
8287         init: function (options) {
8288             this._super(options);
8289         },
8290 
8291         /**
8292          * @private
8293          * Gets the REST class for the current object - this is the Dialogs class.
8294          */
8295         getRestClass: function () {
8296             return Dialogs;
8297         },
8298 
8299         /**
8300          * @private
8301          * Gets the REST class for the objects that make up the collection. - this
8302          * is the Dialog class.
8303          */
8304         getRestItemClass: function () {
8305             return Dialog;
8306         },
8307 
8308         /**
8309          * @private
8310          * Gets the REST type for the current object - this is a "Dialogs".
8311          */
8312         getRestType: function () {
8313             return "Dialogs";
8314         },
8315 
8316         /**
8317          * @private
8318          * Gets the REST type for the objects that make up the collection - this is "Dialogs".
8319          */
8320         getRestItemType: function () {
8321             return "Dialog";
8322         },
8323 
8324         /**
8325          * @private
8326          * Override default to indicates that the collection doesn't support making
8327          * requests.
8328          */
8329         supportsRequests: true,
8330 
8331         /**
8332          * @private
8333          * Override default to indicates that the collection subscribes to its objects.
8334          */
8335         supportsRestItemSubscriptions: true,
8336 
8337         /**
8338          * The requestId reaper timeout in ms
8339          */
8340         REQUESTID_REAPER_TIMEOUT: 5000,
8341 
8342         /**
8343          * @private
8344          * Create a new Dialog in this collection
8345          *
8346          * @param {String} toAddress
8347          *     The to address of the new Dialog
8348          * @param {String} fromAddress
8349          *     The from address of the new Dialog
8350          * @param {finesse.interfaces.RequestHandlers} handlers
8351          *     An object containing the (optional) handlers for the request.
8352          * @return {finesse.restservices.Dialogs}
8353          *     This Dialogs object, to allow cascading.
8354          */
8355         createNewCallDialog: function (toAddress, fromAddress, handlers)
8356         {
8357             var contentBody = {};
8358             contentBody[this.getRestItemType()] = {
8359                 "requestedAction": "MAKE_CALL",
8360                 "toAddress": toAddress,
8361                 "fromAddress": fromAddress
8362             };
8363 
8364             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
8365             handlers = handlers || {};
8366 
8367             this.restRequest(this.getRestUrl(), {
8368                 method: 'POST',
8369                 success: handlers.success,
8370                 error: handlers.error,
8371                 content: contentBody
8372             });
8373             return this; // Allow cascading
8374         },
8375 
8376         /**
8377          * @private
8378          * Create a new Dialog in this collection as a result of a requested action
8379          *
8380          * @param {String} toAddress
8381          *     The to address of the new Dialog
8382          * @param {String} fromAddress
8383          *     The from address of the new Dialog
8384          * @param {finesse.restservices.Dialog.Actions} actionType
8385          *     The associated action to request for creating this new dialog
8386          * @param {finesse.interfaces.RequestHandlers} handlers
8387          *     An object containing the (optional) handlers for the request.
8388          * @return {finesse.restservices.Dialogs}
8389          *     This Dialogs object, to allow cascading.
8390          */
8391         createNewSuperviseCallDialog: function (toAddress, fromAddress, actionType, handlers)
8392         {
8393             var contentBody = {};
8394             this._isLoaded = true;
8395 
8396             contentBody[this.getRestItemType()] = {
8397                 "requestedAction": actionType,
8398                 "toAddress": toAddress,
8399                 "fromAddress": fromAddress
8400             };
8401 
8402             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
8403             handlers = handlers || {};
8404 
8405             this.restRequest(this.getRestUrl(), {
8406                 method: 'POST',
8407                 success: handlers.success,
8408                 error: handlers.error,
8409                 content: contentBody
8410             });
8411             return this; // Allow cascading
8412         },
8413         
8414         /**
8415          * @private
8416          * Create a new Dialog in this collection as a result of a requested action
8417          * @param {String} fromAddress
8418          *     The from address of the new Dialog
8419          * @param {String} toAddress
8420          *     The to address of the new Dialog
8421          * @param {finesse.restservices.Dialog.Actions} actionType
8422          *     The associated action to request for creating this new dialog
8423          * @param {String} dialogUri
8424          *     The associated uri of SUPERVISOR_MONITOR call
8425          * @param {finesse.interfaces.RequestHandlers} handlers
8426          *     An object containing the (optional) handlers for the request.
8427          * @return {finesse.restservices.Dialogs}
8428          *     This Dialogs object, to allow cascading.
8429          */
8430         createNewBargeCall: function (fromAddress, toAddress, actionType, dialogURI, handlers) {
8431             this.isLoaded();
8432          
8433             var contentBody = {};
8434             contentBody[this.getRestItemType()] = {
8435                 "fromAddress": fromAddress,
8436                 "toAddress": toAddress,
8437                 "requestedAction": actionType,
8438                 "associatedDialogUri": dialogURI
8439                 
8440             };
8441             // (nonexistent) keys to be read as undefined
8442             handlers = handlers || {};  
8443             this.restRequest(this.getRestUrl(), {
8444                 method: 'POST',
8445                 success: handlers.success,
8446                 error: handlers.error,
8447                 content: contentBody
8448             });
8449             return this; // Allow cascading
8450         },
8451 
8452         /**
8453          * Utility method to get the number of dialogs in this collection.
8454          * 'excludeSilentMonitor' flag is provided as an option to exclude calls with type
8455          * 'SUPERVISOR_MONITOR' from the count.
8456          * @param  {Boolean} excludeSilentMonitor If true, calls with type of 'SUPERVISOR_MONITOR' will be excluded from the count.
8457          * @return {Number} The number of dialogs in this collection.
8458          */
8459         getDialogCount: function (excludeSilentMonitor) {
8460             this.isLoaded();
8461 
8462             var dialogId, count = 0;
8463             if (excludeSilentMonitor) {
8464                 for (dialogId in this._collection) {
8465                     if (this._collection.hasOwnProperty(dialogId)) {
8466                         if (this._collection[dialogId].getCallType() !== 'SUPERVISOR_MONITOR') {
8467                             count += 1;
8468                         }
8469                     }
8470                 }
8471 
8472                 return count;
8473             } else {
8474                 return this.length;
8475             }        
8476         },
8477 
8478         /**
8479          * Utility method to create a closure containing the requestId and the Dialogs object so 
8480          * that the _pendingCallbacks map can be manipulated when the timer task is executed.
8481          * @param  {String} requestId The requestId of the event
8482          * @return {Function}           The function to be executed by setTimeout
8483          */
8484         _createRequestIdReaper: function (requestId) {
8485             var that = this;
8486             return function () {
8487                 that._logger.log("Dialogs: clearing the requestId-to-callbacks mapping for requestId=" + requestId);
8488                 delete that._pendingCallbacks[requestId];
8489             };
8490         },
8491 
8492         /**
8493          * Overriding implementation of the one in RestBase.js
8494          * This determines the strategy that Dialogs will take after processing an event that contains a requestId.
8495          * @param  {String} requestId The requestId of the event
8496          */
8497         _postProcessUpdateStrategy: function (requestId) {
8498             this._logger.log("Dialogs: determining whether to set timeout for clearing requestId-to-callbacks mapping requestId=" + requestId);
8499             var callbacksObj = this._pendingCallbacks[requestId];
8500             if (callbacksObj && !callbacksObj.used) {
8501                 this._logger.log("Dialogs: setting timeout for clearing requestId-to-callbacks mapping requestId=" + requestId);
8502                 setTimeout(this._createRequestIdReaper(requestId), this.REQUESTID_REAPER_TIMEOUT);
8503                 callbacksObj.used = true;
8504             }            
8505         }
8506 
8507     });
8508     
8509     window.finesse = window.finesse || {};
8510     window.finesse.restservices = window.finesse.restservices || {};
8511     window.finesse.restservices.Dialogs = Dialogs;
8512     
8513     return Dialogs;
8514 });
8515 
8516 /**
8517  * JavaScript representation of the Finesse ClientLog object
8518  *
8519  * @requires finesse.clientservices.ClientServices
8520  * @requires Class
8521  * @requires finesse.FinesseBase
8522  * @requires finesse.restservices.RestBase
8523  */
8524 
8525 /** The following comment is to prevent jslint errors about 
8526  * using variables before they are defined.
8527  */
8528 /** @private */
8529 /*global finesse*/
8530 
8531 define('restservices/ClientLog',["restservices/RestBase"], function (RestBase) {
8532     
8533     var ClientLog = RestBase.extend(/** @lends finesse.restservices.ClientLog.prototype */{    
8534         /**
8535          * @private
8536          * Returns whether this object supports transport logs
8537          */
8538         doNotLog : true,
8539         
8540         explicitSubscription : true,
8541         
8542         /**
8543          * @class
8544          * @private
8545          * JavaScript representation of a ClientLog object. Also exposes methods to operate
8546          * on the object against the server.
8547          *
8548          * @param {Object} options
8549          *     An object with the following properties:<ul>
8550          *         <li><b>id:</b> The id of the object being constructed</li>
8551          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
8552          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
8553          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
8554          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
8555          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
8556          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
8557          *             <li><b>content:</b> {String} Raw string of response</li>
8558          *             <li><b>object:</b> {Object} Parsed object of response</li>
8559          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
8560          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
8561          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
8562          *             </ul></li>
8563          *         </ul></li>
8564          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
8565          * @constructs
8566          * @augments finesse.restservices.RestBase
8567          **/
8568         init: function (options) {
8569             this._super({
8570                 id: "", 
8571                 data: {clientLog : null},
8572                 onAdd: options.onAdd,
8573                 onChange: options.onChange,
8574                 onLoad: options.onLoad,
8575                 onError: options.onError,
8576                 parentObj: options.parentObj
8577                 });
8578         },
8579 
8580         /**
8581          * @private
8582          * Gets the REST class for the current object - this is the ClientLog object.
8583          */
8584         getRestClass: function () {
8585             return ClientLog;
8586         },
8587 
8588         /**
8589          * @private
8590          * Gets the REST type for the current object - this is a "ClientLog".
8591          */
8592         getRestType: function ()
8593         {
8594             return "ClientLog";
8595         },
8596         
8597         /**
8598          * @private
8599          * Gets the node path for the current object
8600          * @returns {String} The node path
8601          */
8602         getXMPPNodePath: function () {
8603             return this.getRestUrl();
8604         },
8605 
8606         /**
8607          * @private
8608          * Utility method to fetch this object from the server, however we
8609          * override it for ClientLog to not do anything because GET is not supported
8610          * for ClientLog object.
8611          */
8612         _doGET: function (handlers) {
8613             return;
8614         },
8615            
8616         /**
8617          * @private
8618          * Invoke a request to the server given a content body and handlers.
8619          *
8620          * @param {Object} contentBody
8621          *     A JS object containing the body of the action request.
8622          * @param {Object} handlers
8623          *     An object containing the following (optional) handlers for the request:<ul>
8624          *         <li><b>success(rsp):</b> A callback function for a successful request to be invoked with the following
8625          *         response object as its only parameter:<ul>
8626          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
8627          *             <li><b>content:</b> {String} Raw string of response</li>
8628          *             <li><b>object:</b> {Object} Parsed object of response</li></ul>
8629          *         <li>A error callback function for an unsuccessful request to be invoked with the
8630          *         error response object as its only parameter:<ul>
8631          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
8632          *             <li><b>content:</b> {String} Raw string of response</li>
8633          *             <li><b>object:</b> {Object} Parsed object of response (HTTP errors)</li>
8634          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
8635          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
8636          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
8637          *             </ul></li>
8638          *         </ul>
8639          */
8640         sendLogs: function (contentBody, handlers) {
8641             // Protect against null dereferencing of options allowing its
8642             // (nonexistent) keys to be read as undefined
8643             handlers = handlers || {};
8644 
8645             this.restRequest(this.getRestUrl(), {
8646                 method: 'POST',
8647                 //success: handlers.success,
8648                 error: handlers.error,
8649                 content: contentBody
8650             });
8651         }
8652     });
8653     
8654     window.finesse = window.finesse || {};
8655     window.finesse.restservices = window.finesse.restservices || {};
8656     window.finesse.restservices.ClientLog = ClientLog;
8657     
8658     return ClientLog;
8659 });
8660 
8661 /**
8662  * JavaScript representation of the Finesse Queue object
8663  * @requires finesse.clientservices.ClientServices
8664  * @requires Class
8665  * @requires finesse.FinesseBase
8666  * @requires finesse.restservices.RestBase
8667  */
8668 
8669 /** @private */
8670 define('restservices/Queue',[
8671     'restservices/RestBase',
8672     'utilities/Utilities'
8673 ],
8674 function (RestBase, Utilities) {
8675     var Queue = RestBase.extend(/** @lends finesse.restservices.Queue.prototype */{
8676 
8677         /**
8678          * @class
8679          * A Queue is a list of Contacts available to a User for quick dial.
8680          * 
8681          * @augments finesse.restservices.RestBase
8682          * @constructs
8683          */
8684         _fakeConstuctor: function () {
8685             /* This is here to hide the real init constructor from the public docs */
8686         },
8687         
8688 		/**
8689 		 * @private
8690 		 * JavaScript representation of a Queue object. Also exposes methods to operate
8691 		 * on the object against the server.
8692 		 *
8693 		 * @constructor
8694 		 * @param {String} id
8695 		 *     Not required...
8696 		 * @param {Object} callbacks
8697 		 *     An object containing callbacks for instantiation and runtime
8698 		 * @param {Function} callbacks.onLoad(this)
8699 		 *     Callback to invoke upon successful instantiation
8700 		 * @param {Function} callbacks.onLoadError(rsp)
8701 		 *     Callback to invoke on instantiation REST request error
8702 		 *     as passed by finesse.clientservices.ClientServices.ajax()
8703 		 *     {
8704 		 *         status: {Number} The HTTP status code returned
8705 		 *         content: {String} Raw string of response
8706 		 *         object: {Object} Parsed object of response
8707 		 *         error: {Object} Wrapped exception that was caught
8708 		 *         error.errorType: {String} Type of error that was caught
8709 		 *         error.errorMessage: {String} Message associated with error
8710 		 *     }
8711 		 * @param {Function} callbacks.onChange(this)
8712 		 *     Callback to invoke upon successful update
8713 		 * @param {Function} callbacks.onError(rsp)
8714 		 *     Callback to invoke on update error (refresh or event)
8715 		 *     as passed by finesse.clientservices.ClientServices.ajax()
8716 		 *     {
8717 		 *         status: {Number} The HTTP status code returned
8718 		 *         content: {String} Raw string of response
8719 		 *         object: {Object} Parsed object of response
8720 		 *         error: {Object} Wrapped exception that was caught
8721 		 *         error.errorType: {String} Type of error that was caught
8722 		 *         error.errorMessage: {String} Message associated with error
8723 		 *     }
8724 		 *  
8725 		 */
8726         init: function (id, callbacks, restObj) {
8727             this._super(id, callbacks, restObj);
8728         },
8729 
8730         /**
8731          * @private
8732          * Gets the REST class for the current object - this is the Queue object.
8733          */
8734         getRestClass: function () {
8735             return Queue;
8736         },
8737 
8738         /**
8739          * @private
8740          * Gets the REST type for the current object - this is a "Queue".
8741          */
8742         getRestType: function () {
8743             return "Queue";
8744         },
8745 
8746         /**
8747          * @private
8748          * Returns whether this object supports subscriptions
8749          */
8750         supportsSubscriptions: function () {
8751             return true;
8752         },
8753         
8754         /**
8755          * @private
8756          * Specifies whether this object's subscriptions need to be explicitly requested
8757          */
8758         explicitSubscription: true,
8759         
8760         /**
8761          * @private
8762          * Gets the node path for the current object - this is the team Users node
8763          * @returns {String} The node path
8764          */
8765         getXMPPNodePath: function () {
8766             return this.getRestUrl();
8767         },
8768         
8769         /**
8770          * Getter for the queue id
8771          * @returns {String}
8772          *     The id of the Queue
8773          */
8774         getId: function () {
8775             this.isLoaded();
8776             return this._id;
8777         },
8778         
8779         /**
8780          * Getter for the queue name
8781          * @returns {String}
8782          *      The name of the Queue
8783          */
8784         getName: function () {
8785             this.isLoaded();
8786             return this.getData().name;
8787         },
8788         
8789         /**
8790          * Getter for the queue statistics.
8791          * Supported statistics include:<br>
8792          *  - callsInQueue<br>
8793          *  - startTimeOfLongestCallInQueue<br>
8794          *  <br>
8795          *  These statistics can be accessed via dot notation:<br>
8796          *  i.e.: getStatistics().callsInQueue
8797          * @returns {Object}
8798          *      The Object with different statistics as properties.
8799          */
8800         getStatistics: function () {
8801             this.isLoaded();
8802             return this.getData().statistics;       
8803         },
8804 
8805         /**
8806          * Parses a uriString to retrieve the id portion
8807          * @param {String} uriString
8808          * @return {String} id
8809          */
8810         _parseIdFromUriString : function (uriString) {
8811             return Utilities.getId(uriString);
8812         }
8813 
8814     });
8815 	
8816 	window.finesse = window.finesse || {};
8817     window.finesse.restservices = window.finesse.restservices || {};
8818     window.finesse.restservices.Queue = Queue;
8819     
8820     return Queue;
8821 });
8822 
8823 /**
8824  * JavaScript representation of the Finesse Queues collection
8825  * object which contains a list of Queue objects.
8826  * @requires finesse.clientservices.ClientServices
8827  * @requires Class
8828  * @requires finesse.FinesseBase
8829  * @requires finesse.restservices.RestBase
8830  * @requires finesse.restservices.RestCollectionBase
8831  */
8832 
8833 /**
8834  * @class
8835  * JavaScript representation of a Queues collection object.
8836  *
8837  * @constructor
8838  * @borrows finesse.restservices.RestCollectionBase as finesse.restservices.Queues
8839  */
8840 
8841 /** @private */
8842 define('restservices/Queues',[
8843     'restservices/RestCollectionBase',
8844     'restservices/Queue'
8845 ],
8846 function (RestCollectionBase, Queue) {
8847     var Queues = RestCollectionBase.extend(/** @lends finesse.restservices.Queues.prototype */{
8848 
8849         /**
8850          * @class
8851          * JavaScript representation of a Queues collection object. 
8852          * @augments finesse.restservices.RestCollectionBase
8853          * @constructs
8854          * @see finesse.restservices.Queue
8855          * @example
8856          *  _queues = _user.getQueues( {
8857          *      onCollectionAdd : _handleQueueAdd,
8858          *      onCollectionDelete : _handleQueueDelete,
8859          *      onLoad : _handleQueuesLoaded
8860          *  });
8861          *  
8862          * _queueCollection = _queues.getCollection();
8863          * for (var queueId in _queueCollection) {
8864          *     if (_queueCollection.hasOwnProperty(queueId)) {
8865          *         _queue = _queueCollection[queueId];
8866          *         etc...
8867          *     }
8868          * }
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 Queues 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 xmpp node path.
8904          */
8905         getXMPPNodePath: function () {
8906             return this.getRestUrl();
8907         },
8908 
8909         /**
8910          * @private
8911          * Gets the REST class for the current object - this is the Queues class.
8912          */
8913         getRestClass: function () {
8914             return Queues;
8915         },
8916 
8917         /**
8918          * @private
8919          * Gets the REST class for the objects that make up the collection. - this
8920          * is the Queue class.
8921          */
8922         getRestItemClass: function () {
8923             return Queue;
8924         },
8925 
8926         /**
8927          * @private
8928          * Gets the REST type for the current object - this is a "Queues".
8929          */
8930         getRestType: function () {
8931             return "Queues";
8932         },
8933         
8934         /**
8935          * @private
8936          * Gets the REST type for the objects that make up the collection - this is "Queue".
8937          */
8938         getRestItemType: function () {
8939             return "Queue";
8940         },
8941 
8942         explicitSubscription: true,
8943         
8944         handlesItemRefresh: true
8945     });
8946     
8947     window.finesse = window.finesse || {};
8948     window.finesse.restservices = window.finesse.restservices || {};
8949     window.finesse.restservices.Queues = Queues;
8950     
8951     return Queues;
8952 });
8953 
8954 /**
8955  * JavaScript representation of the Finesse WrapUpReason object.
8956  *
8957  * @requires finesse.clientservices.ClientServices
8958  * @requires Class
8959  * @requires finesse.FinesseBase
8960  * @requires finesse.restservices.RestBase
8961  */
8962 
8963 /** @private */
8964 define('restservices/WrapUpReason',['restservices/RestBase'], function (RestBase) {
8965 
8966     var WrapUpReason = RestBase.extend(/** @lends finesse.restservices.WrapUpReason.prototype */{
8967 
8968         /**
8969          * @class
8970          * A WrapUpReason is a code and description identifying a particular reason that a
8971          * User is in WORK (WrapUp) mode.
8972          * 
8973          * @augments finesse.restservices.RestBase
8974          * @see finesse.restservices.User
8975          * @see finesse.restservices.User.States#WORK
8976          * @constructs
8977          */
8978         _fakeConstuctor: function () {
8979             /* This is here to hide the real init constructor from the public docs */
8980         },
8981         
8982         /** 
8983          * @private
8984          * JavaScript representation of a WrapUpReason object. Also exposes
8985          * methods to operate on the object against the server.
8986          *
8987          * @param {Object} options
8988          *     An object with the following properties:<ul>
8989          *         <li><b>id:</b> The id of the object being constructed</li>
8990          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
8991          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
8992          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
8993          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
8994          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
8995          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
8996          *             <li><b>content:</b> {String} Raw string of response</li>
8997          *             <li><b>object:</b> {Object} Parsed object of response</li>
8998          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
8999          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
9000          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
9001          *             </ul></li>
9002          *         </ul></li>
9003          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
9004          **/
9005         init: function (options) {
9006             this._super(options);
9007         },
9008 
9009         /**
9010          * @private
9011          * Gets the REST class for the current object - this is the WrapUpReason class.
9012          * @returns {Object} The WrapUpReason class.
9013          */
9014         getRestClass: function () {
9015             return WrapUpReason;
9016         },
9017 
9018         /**
9019          * @private
9020          * Gets the REST type for the current object - this is a "WrapUpReason".
9021          * @returns {String} The WrapUpReason string.
9022          */
9023         getRestType: function () {
9024             return "WrapUpReason";
9025         },
9026 
9027         /**
9028          * @private
9029          * Gets the REST type for the current object - this is a "WrapUpReasons".
9030          * @returns {String} The WrapUpReasons string.
9031          */
9032         getParentRestType: function () {
9033             return "WrapUpReasons";
9034         },
9035 
9036         /**
9037          * @private
9038          * Override default to indicate that this object doesn't support making
9039          * requests.
9040          */
9041         supportsRequests: false,
9042 
9043         /**
9044          * @private
9045          * Override default to indicate that this object doesn't support subscriptions.
9046          */
9047         supportsSubscriptions: false,
9048 
9049         /**
9050          * Getter for the label.
9051          * @returns {String} The label.
9052          */
9053         getLabel: function () {
9054             this.isLoaded();
9055             return this.getData().label;
9056         },
9057 
9058         /**
9059          * @private
9060          * Getter for the forAll flag.
9061          * @returns {Boolean} True if global.
9062          */
9063         getForAll: function () {
9064             this.isLoaded();
9065             return this.getData().forAll;
9066         },
9067 
9068         /**
9069          * @private
9070          * Getter for the Uri value.
9071          * @returns {String} The Uri.
9072          */
9073         getUri: function () {
9074             this.isLoaded();
9075             return this.getData().uri;
9076         }
9077     });
9078 
9079     window.finesse = window.finesse || {};
9080     window.finesse.restservices = window.finesse.restservices || {};
9081     window.finesse.restservices.WrapUpReason = WrapUpReason;
9082         
9083     return WrapUpReason;
9084 });
9085 
9086 /**
9087 * JavaScript representation of the Finesse WrapUpReasons collection
9088 * object which contains a list of WrapUpReason objects.
9089  *
9090  * @requires finesse.clientservices.ClientServices
9091  * @requires Class
9092  * @requires finesse.FinesseBase
9093  * @requires finesse.restservices.RestBase
9094  * @requires finesse.restservices.Dialog
9095  * @requires finesse.restservices.RestCollectionBase
9096  */
9097 
9098 /** @private */
9099 define('restservices/WrapUpReasons',[
9100     'restservices/RestCollectionBase',
9101     'restservices/WrapUpReason'
9102 ],
9103 function (RestCollectionBase, WrapUpReason) {
9104 
9105     var WrapUpReasons = RestCollectionBase.extend(/** @lends finesse.restservices.WrapUpReasons.prototype */{
9106         
9107         /**
9108          * @class
9109          * JavaScript representation of a WrapUpReasons collection object. 
9110          * @augments finesse.restservices.RestCollectionBase
9111          * @constructs
9112          * @see finesse.restservices.WrapUpReason
9113          * @example
9114          *  _wrapUpReasons = _user.getWrapUpReasons ( {
9115          *      onCollectionAdd : _handleWrapUpReasonAdd,
9116          *      onCollectionDelete : _handleWrapUpReasonDelete,
9117          *      onLoad : _handleWrapUpReasonsLoaded
9118          *  });
9119          *  
9120          * _wrapUpReasonCollection = _wrapUpReasons.getCollection();
9121          * for (var wrapUpReasonId in _wrapUpReasonCollection) {
9122          *     if (_wrapUpReasonCollection.hasOwnProperty(wrapUpReasonId)) {
9123          *         _wrapUpReason = _wrapUpReasonCollection[wrapUpReasonId];
9124          *         etc...
9125          *     }
9126          * }
9127         */
9128         _fakeConstuctor: function () {
9129             /* This is here to hide the real init constructor from the public docs */
9130         },
9131         
9132         /** 
9133          * @private
9134          * JavaScript representation of a WrapUpReasons collection object. Also exposes
9135          * methods to operate on the object against the server.
9136          *
9137          * @param {Object} options
9138          *     An object with the following properties:<ul>
9139          *         <li><b>id:</b> The id of the object being constructed</li>
9140          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
9141          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
9142          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
9143          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
9144          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
9145          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
9146          *             <li><b>content:</b> {String} Raw string of response</li>
9147          *             <li><b>object:</b> {Object} Parsed object of response</li>
9148          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
9149          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
9150          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
9151          *             </ul></li>
9152          *         </ul></li>
9153          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
9154          **/
9155         init: function (options) {
9156             this._super(options);           
9157         },
9158 
9159         /**
9160          * @private
9161          * Gets the REST class for the current object - this is the WrapUpReasons class.
9162          */
9163         getRestClass: function () {
9164             return WrapUpReasons;
9165         },
9166 
9167         /**
9168          * @private
9169          * Gets the REST class for the objects that make up the collection. - this
9170          * is the WrapUpReason class.
9171          */
9172         getRestItemClass: function () {
9173             return WrapUpReason;
9174         },
9175 
9176         /**
9177          * @private
9178          * Gets the REST type for the current object - this is a "WrapUpReasons".
9179          */
9180         getRestType: function () {
9181             return "WrapUpReasons";
9182         },
9183         
9184         /**
9185          * @private
9186          * Gets the REST type for the objects that make up the collection - this is "WrapUpReason".
9187          */
9188         getRestItemType: function () {
9189             return "WrapUpReason";
9190         },
9191 
9192         /**
9193          * @private
9194          * Override default to indicates that the collection supports making
9195          * requests.
9196          */
9197         supportsRequests: true,
9198 
9199         /**
9200          * @private
9201          * Override default to indicate that this object doesn't support subscriptions.
9202          */
9203         supportsRestItemSubscriptions: false,
9204 
9205         /**
9206          * @private
9207          * Retrieve the Wrap-Up Reason Codes. This call will re-query the server and refresh the collection.
9208          *
9209          * @returns {finesse.restservices.WrapUpReasons}
9210          *     This ReadyReasonCodes object to allow cascading.
9211          */
9212         get: function () {
9213             // set loaded to false so it will rebuild the collection after the get
9214             this._loaded = false;
9215             // reset collection
9216             this._collection = {};
9217             // perform get
9218             this._synchronize();
9219             return this;
9220         }
9221         
9222     });
9223  
9224     window.finesse = window.finesse || {};
9225     window.finesse.restservices = window.finesse.restservices || {};
9226     window.finesse.restservices.WrapUpReasons = WrapUpReasons;
9227        
9228     return WrapUpReasons;
9229 });
9230 
9231 /**
9232  * JavaScript representation of the Finesse Contact object.
9233  * @requires finesse.clientservices.ClientServices
9234  * @requires Class
9235  * @requires finesse.FinesseBase
9236  * @requires finesse.restservices.RestBase
9237  */
9238 /** @private */
9239 define('restservices/Contact',['restservices/RestBase'], function (RestBase) {
9240 
9241     var Contact = RestBase.extend(/** @lends finesse.restservices.Contact.prototype */{
9242 
9243         /**
9244          * @class
9245          * A Contact is a single entry in a PhoneBook, consisting of a First and Last Name,
9246          * a Phone Number, and a Description.
9247          * 
9248          * @augments finesse.restservices.RestBase
9249          * @see finesse.restservices.PhoneBook
9250          * @constructs
9251          */
9252         _fakeConstuctor: function () {
9253             /* This is here to hide the real init constructor from the public docs */
9254         },
9255         
9256         /**
9257          * @private
9258          * @param {Object} options
9259          *     An object with the following properties:<ul>
9260          *         <li><b>id:</b> The id of the object being constructed</li>
9261          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
9262          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
9263          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
9264          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
9265          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
9266          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
9267          *             <li><b>content:</b> {String} Raw string of response</li>
9268          *             <li><b>object:</b> {Object} Parsed object of response</li>
9269          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
9270          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
9271          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
9272          *             </ul></li>
9273          *         </ul></li>
9274          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
9275          **/
9276         init: function (options) {
9277             this._super(options);
9278         },
9279 
9280         /**
9281          * @private
9282          * Gets the REST class for the current object - this is the Contact class.
9283          * @returns {Object} The Contact class.
9284          */
9285         getRestClass: function () {
9286             return Contact;
9287         },
9288 
9289         /**
9290          * @private
9291          * Gets the REST type for the current object - this is a "Contact".
9292          * @returns {String} The Contact string.
9293          */
9294         getRestType: function () {
9295             return "Contact";
9296         },
9297 
9298         /**
9299          * @private
9300          * Override default to indicate that this object doesn't support making
9301          * requests.
9302          */
9303         supportsRequests: false,
9304 
9305         /**
9306          * @private
9307          * Override default to indicate that this object doesn't support subscriptions.
9308          */
9309         supportsSubscriptions: false,
9310 
9311         /**
9312          * Getter for the firstName.
9313          * @returns {String} The firstName.
9314          */
9315         getFirstName: function () {
9316             this.isLoaded();
9317             return this.getData().firstName;
9318         },
9319 
9320         /**
9321          * Getter for the lastName.
9322          * @returns {String} The lastName.
9323          */
9324         getLastName: function () {
9325             this.isLoaded();
9326             return this.getData().lastName;
9327         },
9328 
9329         /**
9330          * Getter for the phoneNumber.
9331          * @returns {String} The phoneNumber.
9332          */
9333         getPhoneNumber: function () {
9334             this.isLoaded();
9335             return this.getData().phoneNumber;
9336         },
9337 
9338         /**
9339          * Getter for the description.
9340          * @returns {String} The description.
9341          */
9342         getDescription: function () {
9343             this.isLoaded();
9344             return this.getData().description;
9345         },
9346 
9347         /** @private */
9348         createPutSuccessHandler: function(contact, contentBody, successHandler){
9349             return function (rsp) {
9350                 // Update internal structure based on response. Here we
9351                 // inject the contentBody from the PUT request into the
9352                 // rsp.object element to mimic a GET as a way to take
9353                 // advantage of the existing _processResponse method.
9354                 rsp.object = contentBody;
9355                 contact._processResponse(rsp);
9356 
9357                 //Remove the injected Contact object before cascading response
9358                 rsp.object = {};
9359                 
9360                 //cascade response back to consumer's response handler
9361                 successHandler(rsp);
9362             };
9363         },
9364 
9365         /** @private */
9366         createPostSuccessHandler: function (contact, contentBody, successHandler) {
9367             return function (rsp) {
9368                 rsp.object = contentBody;
9369                 contact._processResponse(rsp);
9370 
9371                 //Remove the injected Contact object before cascading response
9372                 rsp.object = {};
9373 
9374                 //cascade response back to consumer's response handler
9375                 successHandler(rsp);
9376             };
9377         },
9378 
9379         /**
9380          * Add
9381          * @private
9382          */
9383         add: function (newValues, handlers) {
9384             // this.isLoaded();
9385             var contentBody = {};
9386 
9387             contentBody[this.getRestType()] = {
9388                 "firstName": newValues.firstName,
9389                 "lastName": newValues.lastName,
9390                 "phoneNumber": newValues.phoneNumber,
9391                 "description": newValues.description
9392             };
9393 
9394             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
9395             handlers = handlers || {};
9396 
9397             this.restRequest(this.getRestUrl(), {
9398                 method: 'POST',
9399                 success: this.createPostSuccessHandler(this, contentBody, handlers.success),
9400                 error: handlers.error,
9401                 content: contentBody
9402             });
9403 
9404             return this; // Allow cascading
9405         },
9406 
9407         /**
9408          * Update
9409          * @private
9410          */
9411         update: function (newValues, handlers) {
9412             this.isLoaded();
9413             var contentBody = {};
9414 
9415             contentBody[this.getRestType()] = {
9416                 "uri": this.getId(),
9417                 "firstName": newValues.firstName,
9418                 "lastName": newValues.lastName,
9419                 "phoneNumber": newValues.phoneNumber,
9420                 "description": newValues.description
9421             };
9422 
9423             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
9424             handlers = handlers || {};
9425 
9426             this.restRequest(this.getRestUrl(), {
9427                 method: 'PUT',
9428                 success: this.createPutSuccessHandler(this, contentBody, handlers.success),
9429                 error: handlers.error,
9430                 content: contentBody
9431             });
9432 
9433             return this; // Allow cascading
9434         },
9435 
9436 
9437         /**
9438          * Delete
9439          * @private
9440          */
9441         "delete": function ( handlers) {
9442             this.isLoaded();
9443 
9444             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
9445             handlers = handlers || {};
9446 
9447             this.restRequest(this.getRestUrl(), {
9448                 method: 'DELETE',
9449                 success: this.createPutSuccessHandler(this, {}, handlers.success),
9450                 error: handlers.error,
9451                 content: undefined
9452             });
9453 
9454             return this; // Allow cascading
9455         }
9456     });
9457 
9458     window.finesse = window.finesse || {};
9459     window.finesse.restservices = window.finesse.restservices || {};
9460     window.finesse.restservices.Contact = Contact;
9461     
9462     return Contact;
9463 });
9464 
9465 /**
9466 * JavaScript representation of the Finesse Contacts collection
9467 * object which contains a list of Contact objects.
9468  *
9469  * @requires finesse.clientservices.ClientServices
9470  * @requires Class
9471  * @requires finesse.FinesseBase
9472  * @requires finesse.restservices.RestBase
9473  * @requires finesse.restservices.Dialog
9474  * @requires finesse.restservices.RestCollectionBase
9475  */
9476 /** @private */
9477 define('restservices/Contacts',[
9478     'restservices/RestCollectionBase',
9479     'restservices/Contact'
9480 ],
9481 function (RestCollectionBase, Contact) {
9482     var Contacts = RestCollectionBase.extend(/** @lends finesse.restservices.Contacts.prototype */{
9483         
9484         /**
9485          * @class
9486          * JavaScript representation of a Contacts collection object. Also exposes
9487          * methods to operate on the object against the server.
9488          * @augments finesse.restservices.RestCollectionBase
9489          * @constructs
9490          * @see finesse.restservices.Contact
9491          * @see finesse.restservices.PhoneBook
9492          * @example
9493          *  _contacts = _phonebook.getContacts( {
9494          *      onCollectionAdd : _handleContactAdd,
9495          *      onCollectionDelete : _handleContactDelete,
9496          *      onLoad : _handleContactsLoaded
9497          *  });
9498          *  
9499          * _contactCollection = _contacts.getCollection();
9500          * for (var contactId in _contactCollection) {
9501          *     if (_contactCollection.hasOwnProperty(contactId)) {
9502          *         _contact = _contactCollection[contactId];
9503          *         etc...
9504          *     }
9505          * }
9506          */
9507         _fakeConstuctor: function () {
9508             /* This is here to hide the real init constructor from the public docs */
9509         },
9510         
9511         /** 
9512          * @private
9513          * JavaScript representation of a Contacts collection object. Also exposes
9514          * methods to operate on the object against the server.
9515          *
9516          * @param {Object} options
9517          *     An object with the following properties:<ul>
9518          *         <li><b>id:</b> The id of the object being constructed</li>
9519          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
9520          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
9521          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
9522          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
9523          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
9524          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
9525          *             <li><b>content:</b> {String} Raw string of response</li>
9526          *             <li><b>object:</b> {Object} Parsed object of response</li>
9527          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
9528          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
9529          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
9530          *             </ul></li>
9531          *         </ul></li>
9532          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
9533          **/
9534         init: function (options) {
9535             this._super(options);           
9536         },
9537 
9538         /**
9539          * @private
9540          * Gets the REST class for the current object - this is the Contacts class.
9541          */
9542         getRestClass: function () {
9543             return Contacts;
9544         },
9545 
9546         /**
9547          * @private
9548          * Gets the REST class for the objects that make up the collection. - this
9549          * is the Contact class.
9550          */
9551         getRestItemClass: function () {
9552             return Contact;
9553         },
9554 
9555         /**
9556          * @private
9557          * Gets the REST type for the current object - this is a "Contacts".
9558          */
9559         getRestType: function () {
9560             return "Contacts";
9561         },
9562         
9563         /**
9564          * @private
9565          * Gets the REST type for the objects that make up the collection - this is "Contacts".
9566          */
9567         getRestItemType: function () {
9568             return "Contact";
9569         },
9570 
9571         /**
9572          * @private
9573          * Override default to indicates that the collection supports making
9574          * requests.
9575          */
9576         supportsRequests: true,
9577 
9578         /**
9579          * @private
9580          * Override default to indicates that the collection subscribes to its objects.
9581          */
9582         supportsRestItemSubscriptions: false,
9583         
9584         /**
9585          * @private
9586          * Retrieve the Contacts.  This call will re-query the server and refresh the collection.
9587          *
9588          * @returns {finesse.restservices.Contacts}
9589          *     This Contacts object, to allow cascading.
9590          */
9591         get: function () {
9592             // set loaded to false so it will rebuild the collection after the get
9593             this._loaded = false;
9594             // reset collection
9595             this._collection = {};
9596             // perform get
9597             this._synchronize();
9598             return this;
9599         }
9600         
9601     });
9602     
9603     window.finesse = window.finesse || {};
9604     window.finesse.restservices = window.finesse.restservices || {};
9605     window.finesse.restservices.Contacts = Contacts;
9606     
9607     
9608     return Contacts;
9609 });
9610 
9611 /**
9612  * JavaScript representation of the Finesse PhoneBook object.
9613  *
9614  * @requires finesse.clientservices.ClientServices
9615  * @requires Class
9616  * @requires finesse.FinesseBase
9617  * @requires finesse.restservices.RestBase
9618  */
9619 
9620 /** @private */
9621 define('restservices/PhoneBook',[
9622     'restservices/RestBase',
9623     'restservices/Contacts'
9624 ],
9625 function (RestBase, Contacts) {
9626     var PhoneBook = RestBase.extend(/** @lends finesse.restservices.PhoneBook.prototype */{
9627 
9628         _contacts: null,
9629 
9630         /**
9631          * @class
9632          * A PhoneBook is a list of Contacts available to a User for quick dial.
9633          * 
9634          * @augments finesse.restservices.RestBase
9635          * @see finesse.restservices.Contacts
9636          * @constructs
9637          */
9638         _fakeConstuctor: function () {
9639             /* This is here to hide the real init constructor from the public docs */
9640         },
9641         
9642         /** 
9643          * @private
9644          * JavaScript representation of a PhoneBook object. Also exposes
9645          * methods to operate on the object against the server.
9646          *
9647          * @param {Object} options
9648          *     An object with the following properties:<ul>
9649          *         <li><b>id:</b> The id of the object being constructed</li>
9650          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
9651          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
9652          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
9653          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
9654          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
9655          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
9656          *             <li><b>content:</b> {String} Raw string of response</li>
9657          *             <li><b>object:</b> {Object} Parsed object of response</li>
9658          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
9659          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
9660          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
9661          *             </ul></li>
9662          *         </ul></li>
9663          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
9664          **/
9665         init: function (options) {
9666             this._super(options);
9667         },
9668 
9669         /**
9670          * @private
9671          * Gets the REST class for the current object - this is the PhoneBook class.
9672          * @returns {Object} The PhoneBook class.
9673          */
9674         getRestClass: function () {
9675             return PhoneBook;
9676         },
9677 
9678         /**
9679          * @private
9680          * Gets the REST type for the current object - this is a "PhoneBook".
9681          * @returns {String} The PhoneBook string.
9682          */
9683         getRestType: function () {
9684             return "PhoneBook";
9685         },
9686 
9687         /**
9688          * @private
9689          * Override default to indicate that this object doesn't support making
9690          * requests.
9691          */
9692         supportsRequests: false,
9693 
9694         /**
9695          * @private
9696          * Override default to indicate that this object doesn't support subscriptions.
9697          */
9698         supportsSubscriptions: false,
9699 
9700         /**
9701          * Getter for the name of the Phone Book.
9702          * @returns {String} The name.
9703          */
9704         getName: function () {
9705             this.isLoaded();
9706             return this.getData().name;
9707         },
9708 
9709         /**
9710          * Getter for the type flag.
9711          * @returns {String} The type.
9712          */
9713         getType: function () {
9714             this.isLoaded();
9715             return this.getData().type;
9716         },
9717 
9718         /**
9719          * @private
9720          * Getter for the Uri value.
9721          * @returns {String} The Uri.
9722          */
9723         getUri: function () {
9724             this.isLoaded();
9725             return this.getData().uri;
9726         },
9727 
9728         /**
9729          * Getter for a Contacts collection object that is associated with PhoneBook.
9730          * @param {finesse.interfaces.RequestHandlers} handlers
9731          *     An object containing the handlers for the request
9732          * @returns {finesse.restservices.Contacts}
9733          *     A Contacts collection object.
9734          */
9735         getContacts: function (callbacks) {
9736             var options = callbacks || {};
9737             options.parentObj = this;
9738             this.isLoaded();
9739 
9740             if (this._contacts === null) {
9741                 this._contacts = new Contacts(options);
9742             }
9743 
9744             return this._contacts;
9745         },
9746 
9747         /**
9748          * Getter for <contacts> node within PhoneBook - sometimes it's just a URI, sometimes it is a Contacts collection
9749          * @returns {String} uri to contacts
9750          *          or {finesse.restservices.Contacts} collection
9751          */
9752         getEmbeddedContacts: function(){
9753             this.isLoaded();
9754             return this.getData().contacts;
9755         },
9756 
9757         /** @private */
9758         createPutSuccessHandler: function(phonebook, contentBody, successHandler){
9759             return function (rsp) {
9760                 // Update internal structure based on response. Here we
9761                 // inject the contentBody from the PUT request into the
9762                 // rsp.object element to mimic a GET as a way to take
9763                 // advantage of the existing _processResponse method.
9764                 rsp.object = contentBody;
9765                 phonebook._processResponse(rsp);
9766 
9767                 //Remove the injected PhoneBook object before cascading response
9768                 rsp.object = {};
9769                 
9770                 //cascade response back to consumer's response handler
9771                 successHandler(rsp);
9772             };
9773         },
9774 
9775         /** @private */
9776         createPostSuccessHandler: function (phonebook, contentBody, successHandler) {
9777             return function (rsp) {
9778                 rsp.object = contentBody;
9779                 phonebook._processResponse(rsp);
9780 
9781                 //Remove the injected PhoneBook object before cascading response
9782                 rsp.object = {};
9783 
9784                 //cascade response back to consumer's response handler
9785                 successHandler(rsp);
9786             };
9787         },
9788 
9789         /**
9790          * @private
9791          * Add a PhoneBook.
9792          * @param {Object} newValues
9793          * @param {String} newValues.name Name of PhoneBook
9794          * @param {String} newValues.type Type of PhoneBook
9795          * @param {finesse.interfaces.RequestHandlers} handlers
9796          *     An object containing the handlers for the request
9797          * @returns {finesse.restservices.PhoneBook}
9798          *     This PhoneBook object, to allow cascading
9799          */
9800         add: function (newValues, handlers) {
9801             // this.isLoaded();
9802             var contentBody = {};
9803 
9804             contentBody[this.getRestType()] = {
9805                 "name": newValues.name,
9806                 "type": newValues.type
9807             };
9808 
9809             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
9810             handlers = handlers || {};
9811 
9812             this.restRequest(this.getRestUrl(), {
9813                 method: 'POST',
9814                 success: this.createPostSuccessHandler(this, contentBody, handlers.success),
9815                 error: handlers.error,
9816                 content: contentBody
9817             });
9818 
9819             return this; // Allow cascading
9820         },
9821 
9822         /**
9823          * @private
9824          * Update a PhoneBook.
9825          * @param {Object} newValues
9826          * @param {String} newValues.name Name of PhoneBook
9827          * @param {String} newValues.type Type of PhoneBook
9828          * @param {finesse.interfaces.RequestHandlers} handlers
9829          *     An object containing the handlers for the request
9830          * @returns {finesse.restservices.PhoneBook}
9831          *     This PhoneBook object, to allow cascading
9832          */
9833         update: function (newValues, handlers) {
9834             this.isLoaded();
9835             var contentBody = {};
9836 
9837             contentBody[this.getRestType()] = {
9838                 "uri": this.getId(),
9839                 "name": newValues.name,
9840                 "type": newValues.type
9841             };
9842 
9843             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
9844             handlers = handlers || {};
9845 
9846             this.restRequest(this.getRestUrl(), {
9847                 method: 'PUT',
9848                 success: this.createPutSuccessHandler(this, contentBody, handlers.success),
9849                 error: handlers.error,
9850                 content: contentBody
9851             });
9852 
9853             return this; // Allow cascading
9854         },
9855 
9856 
9857         /**
9858          * Delete a PhoneBook.
9859          * @param {finesse.interfaces.RequestHandlers} handlers
9860          *     An object containing the handlers for the request
9861          * @returns {finesse.restservices.PhoneBook}
9862          *     This PhoneBook object, to allow cascading
9863          */
9864         "delete": function ( handlers) {
9865             this.isLoaded();
9866 
9867             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
9868             handlers = handlers || {};
9869 
9870             this.restRequest(this.getRestUrl(), {
9871                 method: 'DELETE',
9872                 success: this.createPutSuccessHandler(this, {}, handlers.success),
9873                 error: handlers.error,
9874                 content: undefined
9875             });
9876 
9877             return this; // Allow cascading
9878         }
9879 
9880 
9881 
9882     });
9883     
9884     window.finesse = window.finesse || {};
9885     window.finesse.restservices = window.finesse.restservices || {};
9886     window.finesse.restservices.PhoneBook = PhoneBook;
9887     
9888     return PhoneBook;
9889 });
9890 
9891 /**
9892 * JavaScript representation of the Finesse PhoneBooks collection
9893 * object which contains a list of PhoneBook objects.
9894  *
9895  * @requires finesse.clientservices.ClientServices
9896  * @requires Class
9897  * @requires finesse.FinesseBase
9898  * @requires finesse.restservices.RestBase
9899  * @requires finesse.restservices.Dialog
9900  * @requires finesse.restservices.RestCollectionBase
9901  */
9902 /** @private */
9903 define('restservices/PhoneBooks',[
9904     'restservices/RestCollectionBase',
9905     'restservices/PhoneBook'
9906 ],
9907 function (RestCollectionBase, PhoneBook) {
9908     var PhoneBooks = RestCollectionBase.extend(/** @lends finesse.restservices.PhoneBooks.prototype */{
9909         
9910         /**
9911          * @class
9912          * JavaScript representation of a PhoneBooks collection object. 
9913          * @augments finesse.restservices.RestCollectionBase
9914          * @constructs
9915          * @see finesse.restservices.PhoneBook
9916          * @see finesse.restservices.Contacts
9917          * @see finesse.restservices.Contact
9918          * @example
9919          *  _phoneBooks = _user.getPhoneBooks( {
9920          *      onCollectionAdd : _handlePhoneBookAdd,
9921          *      onCollectionDelete : _handlePhoneBookDelete,
9922          *      onLoad : _handlePhoneBooksLoaded
9923          *  });
9924          *  
9925          * _phoneBookCollection = _phoneBooks.getCollection();
9926          * for (var phoneBookId in _phoneBookCollection) {
9927          *     if (_phoneBookCollection.hasOwnProperty(phoneBookId)) {
9928          *         _phoneBook = _phoneBookCollection[phoneBookId];
9929          *         etc...
9930          *     }
9931          * }
9932         */
9933         _fakeConstuctor: function () {
9934             /* This is here to hide the real init constructor from the public docs */
9935         },
9936         
9937        /**
9938          * @private
9939          *
9940          * @param {Object} options
9941          *     An object with the following properties:<ul>
9942          *         <li><b>id:</b> The id of the object being constructed</li>
9943          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
9944          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
9945          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
9946          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
9947          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
9948          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
9949          *             <li><b>content:</b> {String} Raw string of response</li>
9950          *             <li><b>object:</b> {Object} Parsed object of response</li>
9951          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
9952          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
9953          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
9954          *             </ul></li>
9955          *         </ul></li>
9956          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
9957          **/
9958         init: function (options) {
9959             // Keep the REST response for PhoneBooks to check for 206 Partial Content.
9960             this.keepRestResponse = true;
9961             // Add in the Range header which is required for PhoneBooks API.
9962             this.extraHeaders = { "Range": "objects=1-1500" };
9963             this._super(options);
9964         },
9965 
9966         /**
9967          * @private
9968          * Gets the REST class for the current object - this is the PhoneBooks class.
9969          */
9970         getRestClass: function () {
9971             return PhoneBooks;
9972         },
9973 
9974         /**
9975          * @private
9976          * Gets the REST class for the objects that make up the collection. - this
9977          * is the PhoneBook class.
9978          */
9979         getRestItemClass: function () {
9980             return PhoneBook;
9981         },
9982 
9983         /**
9984          * @private
9985          * Gets the REST type for the current object - this is a "PhoneBooks".
9986          */
9987         getRestType: function () {
9988             return "PhoneBooks";
9989         },
9990         
9991         /**
9992          * @private
9993          * Gets the REST type for the objects that make up the collection - this is "PhoneBooks".
9994          */
9995         getRestItemType: function () {
9996             return "PhoneBook";
9997         },
9998 
9999         /**
10000          * @private
10001          * Override default to indicates that the collection supports making
10002          * requests.
10003          */
10004         supportsRequests: true,
10005 
10006         /**
10007          * @private
10008          * Override default to indicates that the collection subscribes to its objects.
10009          */
10010         supportsRestItemSubscriptions: false,
10011         
10012         /**
10013          * @private
10014          * Retrieve the PhoneBooks.  This call will re-query the server and refresh the collection.
10015          *
10016          * @returns {finesse.restservices.PhoneBooks}
10017          *     This PhoneBooks object, to allow cascading.
10018          */
10019         get: function () {
10020             // set loaded to false so it will rebuild the collection after the get
10021             this._loaded = false;
10022             // reset collection
10023             this._collection = {};
10024             // perform get
10025             this._synchronize();
10026             return this;
10027         }
10028         
10029     });
10030     
10031     window.finesse = window.finesse || {};
10032     window.finesse.restservices = window.finesse.restservices || {};
10033     window.finesse.restservices.PhoneBooks = PhoneBooks;
10034     
10035     return PhoneBooks;
10036 });
10037 
10038 /**
10039  * JavaScript representation of the Finesse WorkflowAction object.
10040  *
10041  * @requires finesse.clientservices.ClientServices
10042  * @requires Class
10043  * @requires finesse.FinesseBase
10044  * @requires finesse.restservices.RestBase
10045  */
10046 
10047 /*jslint browser: true, nomen: true, sloppy: true, forin: true */
10048 /*global define,finesse */
10049 
10050 /** @private */
10051 define('restservices/WorkflowAction',['restservices/RestBase'], function (RestBase) {
10052 
10053     var WorkflowAction = RestBase.extend({
10054 
10055         _contacts: null,
10056 
10057         actionTypes: [
10058             {
10059                 name: 'BROWSER_POP',
10060                 params: [
10061                     {
10062                         name: 'windowName',
10063                         type: 'text'
10064                     },
10065                     {
10066                         name: 'path',
10067                         type: 'systemVariableSingleLineEditor'
10068                     }
10069                 ]
10070             },
10071             {
10072                 name: 'HTTP_REQUEST',
10073                 params: [
10074                     {
10075                         name: 'method',
10076                         type: 'dropdown',
10077                         values: ['POST', 'PUT']
10078                     },
10079                     {
10080                         name: 'location',
10081                         type: 'dropdown',
10082                         values: ['FINESSE', 'OTHER']
10083                     },
10084                     {
10085                         name: 'contentType',
10086                         type: 'text'
10087                     },
10088                     {
10089                         name: 'path',
10090                         type: 'systemVariableSingleLineEditor'
10091                     },
10092                     {
10093                         name: 'body',
10094                         type: 'systemVariableMultiLineEditor'
10095                     }
10096                 ]
10097             }            
10098             // more action type definitions here
10099         ],
10100 
10101         /**
10102          * @class
10103          * A WorkflowAction is an action (e.g. Browser Pop, Rest Request) defined in a
10104          * Workflow and triggered by a system event (Call Received, Call Ended, etc.).
10105          * 
10106          * @augments finesse.restservices.RestBase
10107          * @see finesse.restservices.Workflow
10108          * @constructs
10109          */
10110         _fakeConstuctor: function () {
10111             /* This is here to hide the real init constructor from the public docs */
10112         },
10113         
10114         /**
10115          * @private
10116          * JavaScript representation of a WorkflowAction object. Also exposes
10117          * methods to operate on the object against the server.
10118          *
10119          * @param {Object} options
10120          *     An object with the following properties:<ul>
10121          *         <li><b>id:</b> The id of the object being constructed</li>
10122          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
10123          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
10124          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
10125          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
10126          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
10127          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
10128          *             <li><b>content:</b> {String} Raw string of response</li>
10129          *             <li><b>object:</b> {Object} Parsed object of response</li>
10130          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
10131          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
10132          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
10133          *             </ul></li>
10134          *         </ul></li>
10135          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
10136          **/
10137         init: function (options) {
10138             this._super(options);
10139         },
10140 
10141         /**
10142          * @private
10143          * Gets the REST class for the current object - this is the WorkflowAction class.
10144          * @returns {Object} The WorkflowAction class.
10145          */
10146         getRestClass: function () {
10147             return finesse.restservices.WorkflowAction;
10148         },
10149 
10150         /**
10151          * @private
10152          * Gets the REST type for the current object - this is a "WorkflowAction".
10153          * @returns {String} The WorkflowAction string.
10154          */
10155         getRestType: function () {
10156             return "WorkflowAction";
10157         },
10158 
10159         /**
10160          * @private
10161          * Override default to indicate that this object doesn't support making
10162          * requests.
10163          */
10164         supportsRequests: false,
10165 
10166         /**
10167          * @private
10168          * Override default to indicate that this object doesn't support subscriptions.
10169          */
10170         supportsSubscriptions: false,
10171 
10172         /**
10173          * Getter for the name.
10174          * @returns {String} The name.
10175          */
10176         getName: function () {
10177             this.isLoaded();
10178             return this.getData().name;
10179         },
10180 
10181         /**
10182          * Getter for the type flag.
10183          * @returns {String} The type.
10184          */
10185         getType: function () {
10186             this.isLoaded();
10187             return this.getData().type;
10188         },
10189 
10190         /**
10191          * @private
10192          * Getter for the Uri value.
10193          * @returns {String} The Uri.
10194          */
10195         getUri: function () {
10196             this.isLoaded();
10197             return this.getData().uri;
10198         },
10199 
10200         /**
10201          * @private
10202          * Getter for the handledBy value.
10203          * @returns {String} handledBy.
10204          */
10205         getHandledBy: function () {
10206             this.isLoaded();
10207             return this.getData().handledBy;
10208         },
10209 
10210         /**
10211          * Getter for the parameters.
10212          * @returns {Object} key = param name, value = param value
10213          */
10214         getParams: function () {
10215             var map = {},
10216                 params = this.getData().params.Param,
10217                 i,
10218                 param;
10219 
10220             for(i=0; i<params.length; i+=1){
10221                 param = params[i];
10222                 map[param.name] = param.value || "";
10223             }
10224 
10225             return map;
10226         },
10227 
10228         /**
10229          * Getter for the ActionVariables
10230          * @returns {Object} key = action variable name, value = Object{name, type, node, testValue}
10231          */
10232         getActionVariables: function() {
10233             var map = {},
10234                 actionVariablesParent = this.getData().actionVariables,
10235                 actionVariables,
10236                 i,
10237                 actionVariable;
10238 
10239             if (actionVariablesParent === null ||  typeof(actionVariablesParent) === "undefined" || actionVariablesParent.length === 0){
10240                 return map;
10241             }
10242             actionVariables = actionVariablesParent.ActionVariable;
10243 
10244             if(actionVariables.length > 0){
10245                 for(i=0; i<actionVariables.length; i+=1){
10246                     actionVariable = actionVariables[i];
10247                     // escape nulls to empty string
10248                     actionVariable.name = actionVariable.name || "";
10249                     actionVariable.type = actionVariable.type || "";
10250                     actionVariable.node = actionVariable.node || "";
10251                     actionVariable.testValue = actionVariable.testValue || "";
10252                     map[actionVariable.name] = actionVariable;
10253                 }
10254             } else {
10255                 map[actionVariables.name] = actionVariables;
10256             }
10257 
10258             return map;
10259         },
10260 
10261         /** @private */
10262         createPutSuccessHandler: function(action, contentBody, successHandler){
10263             return function (rsp) {
10264                 // Update internal structure based on response. Here we
10265                 // inject the contentBody from the PUT request into the
10266                 // rsp.object element to mimic a GET as a way to take
10267                 // advantage of the existing _processResponse method.
10268                 rsp.object = contentBody;
10269                 action._processResponse(rsp);
10270 
10271                 //Remove the injected WorkflowAction object before cascading response
10272                 rsp.object = {};
10273                 
10274                 //cascade response back to consumer's response handler
10275                 successHandler(rsp);
10276             };
10277         },
10278 
10279         /** @private */
10280         createPostSuccessHandler: function (action, contentBody, successHandler) {
10281             return function (rsp) {
10282                 rsp.object = contentBody;
10283                 action._processResponse(rsp);
10284 
10285                 //Remove the injected WorkflowAction object before cascading response
10286                 rsp.object = {};
10287 
10288                 //cascade response back to consumer's response handler
10289                 successHandler(rsp);
10290             };
10291         },
10292 
10293         /**
10294          * @private
10295          * Build params array out of all the values coming into add or update methods
10296          * paramMap is a map of params.. we need to translate it into an array of Param objects
10297          * where path and windowName are params for the BROWSER_POP type
10298          */
10299         buildParamsForRest: function(paramMap){
10300             var params = {"Param": []},
10301                 i;
10302             for(i in paramMap){
10303                 if(paramMap.hasOwnProperty(i)){
10304                     params.Param.push({name: i, value: paramMap[i]});
10305                 }
10306             }
10307             return params;
10308         },
10309 
10310         /**
10311          * @private
10312          * Build actionVariables array out of all the values coming into add or update methods
10313          * actionVariableMap is a map of actionVariables.. we need to translate it into an array of ActionVariable objects
10314          * where path and windowName are params for the BROWSER_POP type
10315          */
10316         buildActionVariablesForRest: function(actionVariableMap){
10317             var actionVariables = {"ActionVariable": []},
10318                 i,
10319                 actionVariable;
10320             for(i in actionVariableMap){
10321                 if(actionVariableMap.hasOwnProperty(i)){
10322                     // {name: "callVariable1", type: "SYSTEM", node: "", testValue: "<blink>"}
10323                     actionVariable = {
10324                         "name": actionVariableMap[i].name,
10325                         "type": actionVariableMap[i].type,
10326                         "node": actionVariableMap[i].node,
10327                         "testValue": actionVariableMap[i].testValue
10328                     };
10329                     actionVariables.ActionVariable.push(actionVariable);
10330                 }
10331             }
10332             return actionVariables;
10333         },
10334 
10335         /**
10336          * Add
10337          */
10338         add: function (newValues, handlers) {
10339             var contentBody = {};
10340 
10341             contentBody[this.getRestType()] = {
10342                 "name": newValues.name,
10343                 "type": newValues.type,
10344                 "handledBy": newValues.handledBy,
10345                 "params": this.buildParamsForRest(newValues.params),
10346                 "actionVariables": this.buildActionVariablesForRest(newValues.actionVariables)
10347             };
10348 
10349             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
10350             handlers = handlers || {};
10351 
10352             this.restRequest(this.getRestUrl(), {
10353                 method: 'POST',
10354                 success: this.createPostSuccessHandler(this, contentBody, handlers.success),
10355                 error: handlers.error,
10356                 content: contentBody
10357             });
10358 
10359             return this; // Allow cascading
10360         },
10361 
10362         /**
10363          * @private
10364          * Update
10365          */
10366         update: function (newValues, handlers) {
10367             this.isLoaded();
10368             var contentBody = {};
10369             
10370             contentBody[this.getRestType()] = {
10371                 "uri": this.getId(),
10372                 "name": newValues.name,
10373                 "type": newValues.type,
10374                 "handledBy": newValues.handledBy,
10375                 "params": this.buildParamsForRest(newValues.params),
10376                 "actionVariables": this.buildActionVariablesForRest(newValues.actionVariables)
10377             };
10378 
10379             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
10380             handlers = handlers || {};
10381 
10382             this.restRequest(this.getRestUrl(), {
10383                 method: 'PUT',
10384                 success: this.createPutSuccessHandler(this, contentBody, handlers.success),
10385                 error: handlers.error,
10386                 content: contentBody
10387             });
10388 
10389             return this; // Allow cascading
10390         },
10391 
10392 
10393         /**
10394          * @private
10395          * Delete
10396          */
10397         "delete": function ( handlers) {
10398             this.isLoaded();
10399 
10400             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
10401             handlers = handlers || {};
10402 
10403             this.restRequest(this.getRestUrl(), {
10404                 method: 'DELETE',
10405                 success: this.createPutSuccessHandler(this, {}, handlers.success),
10406                 error: handlers.error,
10407                 content: undefined
10408             });
10409 
10410             return this; // Allow cascading
10411         }
10412 
10413 
10414 
10415     });
10416 
10417     window.finesse = window.finesse || {};
10418     window.finesse.restservices = window.finesse.restservices || {};
10419     window.finesse.restservices.WorkflowAction = WorkflowAction;
10420     
10421     return WorkflowAction;
10422 });
10423 
10424 /**
10425 * JavaScript representation of the Finesse WorkflowActions collection
10426 * object which contains a list of WorkflowAction objects.
10427  *
10428  * @requires finesse.clientservices.ClientServices
10429  * @requires Class
10430  * @requires finesse.FinesseBase
10431  * @requires finesse.restservices.RestBase
10432  * @requires finesse.restservices.Dialog
10433  * @requires finesse.restservices.RestCollectionBase
10434  */
10435 
10436 /** @private */
10437 define('restservices/WorkflowActions',[
10438     'restservices/RestCollectionBase',
10439     'restservices/RestBase',
10440     'restservices/WorkflowAction'
10441 ],
10442 function (RestCollectionBase, RestBase, WorkflowAction) {
10443 
10444     var WorkflowActions = RestCollectionBase.extend({
10445         
10446         /**
10447          * @class
10448          * JavaScript representation of a WorkflowActions collection object. 
10449          * @augments finesse.restservices.RestCollectionBase
10450          * @constructs
10451          * @see finesse.restservices.WorkflowAction
10452          * @see finesse.restservices.Workflow
10453          * @see finesse.restservices.Workflows
10454          * @example
10455          *  _workflowActions = _user.getWorkflowActions( {
10456          *      onCollectionAdd : _handleWorkflowActionAdd,
10457          *      onCollectionDelete : _handleWorkflowActionDelete,
10458          *      onLoad : _handleWorkflowActionsLoaded
10459          *  });
10460         */
10461         _fakeConstuctor: function () {
10462             /* This is here to hide the real init constructor from the public docs */
10463         },
10464         
10465         /**
10466          * @private
10467          * JavaScript representation of a WorkflowActions collection object. Also exposes
10468          * methods to operate on the object against the server.
10469          *
10470          * @param {Object} options
10471          *     An object with the following properties:<ul>
10472          *         <li><b>id:</b> The id of the object being constructed</li>
10473          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
10474          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
10475          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
10476          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
10477          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
10478          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
10479          *             <li><b>content:</b> {String} Raw string of response</li>
10480          *             <li><b>object:</b> {Object} Parsed object of response</li>
10481          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
10482          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
10483          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
10484          *             </ul></li>
10485          *         </ul></li>
10486          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
10487          **/
10488         init: function (options) {
10489             this._super(options);           
10490         },
10491 
10492         /**
10493          * @private
10494          * Gets the REST class for the current object - this is the WorkflowActions class.
10495          */
10496         getRestClass: function () {
10497             return WorkflowActions;
10498         },
10499 
10500         /**
10501          * @private
10502          * Gets the REST class for the objects that make up the collection. - this
10503          * is the WorkflowAction class.
10504          */
10505         getRestItemClass: function () {
10506             return WorkflowAction;
10507         },
10508 
10509         /**
10510          * @private
10511          * Gets the REST type for the current object - this is a "WorkflowActions".
10512          */
10513         getRestType: function () {
10514             return "WorkflowActions";
10515         },
10516         
10517         /**
10518          * @private
10519          * Gets the REST type for the objects that make up the collection - this is "WorkflowActions".
10520          */
10521         getRestItemType: function () {
10522             return "WorkflowAction";
10523         },
10524 
10525         /**
10526          * @private
10527          * Override default to indicates that the collection supports making
10528          * requests.
10529          */
10530         supportsRequests: true,
10531 
10532         /**
10533          * @private
10534          * Override default to indicates that the collection subscribes to its objects.
10535          */
10536         supportsRestItemSubscriptions: false,
10537         
10538         /**
10539          * @private
10540          * Retrieve the WorkflowActions.
10541          *
10542          * @returns {finesse.restservices.WorkflowActions}
10543          *     This WorkflowActions object to allow cascading.
10544          */
10545         get: function () {
10546             // set loaded to false so it will rebuild the collection after the get
10547             this._loaded = false;
10548             // reset collection
10549             this._collection = {};
10550             // perform get
10551             this._synchronize();
10552             return this;
10553         }
10554     });
10555 
10556     window.finesse = window.finesse || {};
10557     window.finesse.restservices = window.finesse.restservices || {};
10558     window.finesse.restservices.WorkflowActions = WorkflowActions;
10559         
10560     return WorkflowActions;
10561 });
10562 
10563 /**
10564  * JavaScript representation of the Finesse Workflow object.
10565  *
10566  * @requires finesse.clientservices.ClientServices
10567  * @requires Class
10568  * @requires finesse.FinesseBase
10569  * @requires finesse.restservices.RestBase
10570  */
10571 
10572 /*jslint browser: true, nomen: true, sloppy: true, forin: true */
10573 /*global define,finesse */
10574 
10575 /** @private */
10576 define('restservices/Workflow',[
10577     'restservices/RestBase',
10578     'restservices/WorkflowActions'
10579 ],
10580 function (RestBase, WorkflowActions) {
10581 
10582     var Workflow = RestBase.extend({
10583 
10584         /**
10585          * @class
10586          * JavaScript representation of a Workflow object. Also exposes
10587          * methods to operate on the object against the server.
10588          *
10589          * @param {Object} options
10590          *     An object with the following properties:<ul>
10591          *         <li><b>id:</b> The id of the object being constructed</li>
10592          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
10593          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
10594          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
10595          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
10596          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
10597          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
10598          *             <li><b>content:</b> {String} Raw string of response</li>
10599          *             <li><b>object:</b> {Object} Parsed object of response</li>
10600          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
10601          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
10602          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
10603          *             </ul></li>
10604          *         </ul></li>
10605          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
10606          * @constructs
10607          **/
10608         init: function (options) {
10609             this._super(options);
10610         },
10611 
10612         /**
10613          * @private
10614          * Gets the REST class for the current object - this is the Workflow class.
10615          * @returns {Object} The Workflow class.
10616          */
10617         getRestClass: function () {
10618             return Workflow;
10619         },
10620 
10621         /**
10622          * @private
10623          * Gets the REST type for the current object - this is a "Workflow".
10624          * @returns {String} The Workflow string.
10625          */
10626         getRestType: function () {
10627             return "Workflow";
10628         },
10629 
10630         /**
10631          * @private
10632          * Override default to indicate that this object doesn't support making
10633          * requests.
10634          */
10635         supportsRequests: false,
10636 
10637         /**
10638          * @private
10639          * Override default to indicate that this object doesn't support subscriptions.
10640          */
10641         supportsSubscriptions: false,
10642 
10643         /**
10644          * @private
10645          * Getter for the Uri value.
10646          * @returns {String} The Uri.
10647          */
10648         getUri: function () {
10649             this.isLoaded();
10650             return this.getData().uri;
10651         },
10652 
10653         /**
10654          * Getter for the name.
10655          * @returns {String} The name.
10656          */
10657         getName: function () {
10658             this.isLoaded();
10659             return this.getData().name;
10660         },
10661 
10662         /**
10663          * Getter for the description.
10664          * @returns {String} The description.
10665          */
10666         getDescription: function () {
10667             this.isLoaded();
10668             return this.getData().description;
10669         },
10670 
10671         /**
10672          * Getter for the trigger set.
10673          * @returns {String} The trigger set.
10674          */
10675         getTriggerSet: function () {
10676             this.isLoaded();
10677             return this.getData().TriggerSet;
10678         },
10679 
10680         /**
10681          * Getter for the condition set.
10682          * @returns {String} The condition set.
10683          */
10684         getConditionSet: function () {
10685             this.isLoaded();
10686             return this.getData().ConditionSet;
10687         },
10688         
10689         /**
10690          * Getter for the assigned workflowActions.
10691          * @returns {String} The workflowActions object.
10692          */
10693         getWorkflowActions: function () {
10694             this.isLoaded();
10695             var workflowActions = this.getData().workflowActions;
10696             if (workflowActions === null) {
10697                 workflowActions = "";
10698             }
10699             return workflowActions;
10700         },
10701 
10702         createPutSuccessHandler: function (workflow, contentBody, successHandler) {
10703             return function (rsp) {
10704                 // Update internal structure based on response. Here we
10705                 // inject the contentBody from the PUT request into the
10706                 // rsp.object element to mimic a GET as a way to take
10707                 // advantage of the existing _processResponse method.
10708                 rsp.object = contentBody;
10709                 workflow._processResponse(rsp);
10710 
10711                 //Remove the injected Workflow object before cascading response
10712                 rsp.object = {};
10713 
10714                 //cascade response back to consumer's response handler
10715                 successHandler(rsp);
10716             };
10717         },
10718 
10719         createPostSuccessHandler: function (workflow, contentBody, successHandler) {
10720             return function (rsp) {
10721                 rsp.object = contentBody;
10722                 workflow._processResponse(rsp);
10723 
10724                 //Remove the injected Workflow object before cascading response
10725                 rsp.object = {};
10726 
10727                 //cascade response back to consumer's response handler
10728                 successHandler(rsp);
10729             };
10730         },
10731 
10732         /**
10733          * @private
10734          * Add
10735          */
10736         add: function (newValues, handlers) {
10737             // this.isLoaded();
10738             var contentBody = {};
10739 
10740             contentBody[this.getRestType()] = {
10741                 "name": newValues.name,
10742                 "description": newValues.description,
10743                 "TriggerSet" : newValues.TriggerSet,
10744                 "ConditionSet" : newValues.ConditionSet,
10745                 "workflowActions" : newValues.workflowActions
10746             };
10747 
10748             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
10749             handlers = handlers || {};
10750 
10751             this.restRequest(this.getRestUrl(), {
10752                 method: 'POST',
10753                 success: this.createPostSuccessHandler(this, contentBody, handlers.success),
10754                 error: handlers.error,
10755                 content: contentBody
10756             });
10757 
10758             return this; // Allow cascading
10759         },
10760 
10761         /**
10762          * @private
10763          * Update
10764          */
10765         update: function (newValues, handlers) {
10766             this.isLoaded();
10767             var contentBody = {};
10768 
10769             contentBody[this.getRestType()] = {
10770                 "uri": this.getId(),
10771                 "name": newValues.name,
10772                 "description": newValues.description,
10773                 "TriggerSet" : newValues.TriggerSet,
10774                 "ConditionSet" : newValues.ConditionSet,
10775                 "workflowActions" : newValues.workflowActions
10776             };
10777 
10778             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
10779             handlers = handlers || {};
10780 
10781             this.restRequest(this.getRestUrl(), {
10782                 method: 'PUT',
10783                 success: this.createPutSuccessHandler(this, contentBody, handlers.success),
10784                 error: handlers.error,
10785                 content: contentBody
10786             });
10787 
10788             return this; // Allow cascading
10789         },
10790 
10791 
10792         /**
10793          * @private
10794          * Delete
10795          */
10796         "delete": function (handlers) {
10797             this.isLoaded();
10798 
10799             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
10800             handlers = handlers || {};
10801 
10802             this.restRequest(this.getRestUrl(), {
10803                 method: 'DELETE',
10804                 success: this.createPutSuccessHandler(this, {}, handlers.success),
10805                 error: handlers.error,
10806                 content: undefined
10807             });
10808 
10809             return this; // Allow cascading
10810         }
10811 
10812 
10813 
10814     });
10815 
10816     window.finesse = window.finesse || {};
10817     window.finesse.restservices = window.finesse.restservices || {};
10818     window.finesse.restservices.Workflow = Workflow;
10819 
10820     return Workflow;
10821 });
10822 
10823 /**
10824 * JavaScript representation of the Finesse workflows collection
10825 * object which contains a list of workflow objects.
10826  *
10827  * @requires finesse.clientservices.ClientServices
10828  * @requires Class
10829  * @requires finesse.FinesseBase
10830  * @requires finesse.restservices.RestBase
10831  * @requires finesse.restservices.Dialog
10832  * @requires finesse.restservices.RestCollectionBase
10833  */
10834 
10835 /** @private */
10836 define('restservices/Workflows',[
10837     'restservices/RestCollectionBase',
10838     'restservices/RestBase',
10839     'restservices/Workflow'
10840 ],
10841 function (RestCollectionBase, RestBase, Workflow) {
10842 
10843     var Workflows = RestCollectionBase.extend({
10844 
10845         /**
10846          * @class
10847          * JavaScript representation of a workflows collection object. Also exposes
10848          * methods to operate on the object against the server.
10849          *
10850          * @param {Object} options
10851          *     An object with the following properties:<ul>
10852          *         <li><b>id:</b> The id of the object being constructed</li>
10853          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
10854          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
10855          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
10856          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
10857          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
10858          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
10859          *             <li><b>content:</b> {String} Raw string of response</li>
10860          *             <li><b>object:</b> {Object} Parsed object of response</li>
10861          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
10862          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
10863          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
10864          *             </ul></li>
10865          *         </ul></li>
10866          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
10867          *  @constructs
10868          **/
10869         init: function (options) {
10870             this._super(options);
10871         },
10872 
10873         /**
10874          * @private
10875          * Gets the REST class for the current object - this is the workflows class.
10876          */
10877         getRestClass: function () {
10878             return Workflows;
10879         },
10880 
10881         /**
10882          * @private
10883          * Gets the REST class for the objects that make up the collection. - this
10884          * is the workflow class.
10885          */
10886         getRestItemClass: function () {
10887             return Workflow;
10888         },
10889 
10890         /**
10891          * @private
10892          * Gets the REST type for the current object - this is a "workflows".
10893          */
10894         getRestType: function () {
10895             return "Workflows";
10896         },
10897 
10898         /**
10899          * @private
10900          * Gets the REST type for the objects that make up the collection - this is "workflows".
10901          */
10902         getRestItemType: function () {
10903             return "Workflow";
10904         },
10905 
10906         /**
10907          * @private
10908          * Override default to indicates that the collection supports making requests.
10909          */
10910         supportsRequests: true,
10911 
10912         /**
10913          * @private
10914          * Override default to indicates that the collection does not subscribe to its objects.
10915          */
10916         supportsRestItemSubscriptions: false,
10917 
10918         /**
10919          * @private
10920          * Retrieve the workflows. This call will re-query the server and refresh the collection.
10921          *
10922          * @returns {finesse.restservices.workflows}
10923          *     This workflows object to allow cascading.
10924          */
10925         get: function () {
10926             // set loaded to false so it will rebuild the collection after the get
10927             this._loaded = false;
10928             // reset collection
10929             this._collection = {};
10930             // perform get
10931             this._synchronize();
10932             return this;
10933         }
10934     });
10935 
10936     window.finesse = window.finesse || {};
10937     window.finesse.restservices = window.finesse.restservices || {};
10938     window.finesse.restservices.Workflows = Workflows;
10939         
10940     return Workflows;
10941 });
10942 
10943 /**
10944  * JavaScript representation of the Finesse MediaPropertiesLayout object for the Admin webapp.
10945  * @requires finesse.clientservices.ClientServices
10946  * @requires Class
10947  * @requires finesse.FinesseBase
10948  * @requires finesse.restservices.RestBase
10949  */
10950 
10951 /** The following comment is to prevent jslint errors about 
10952  * using variables before they are defined.
10953  */
10954 /*global finesse*/
10955 
10956 /**
10957  * @class
10958  * JavaScript representation of a MediaPropertiesLayout object for the Admin webapp. Also exposes
10959  * methods to operate on the object against the server.
10960  *
10961  * @constructor
10962  * @param {String} id
10963  *     Not required...
10964  * @param {Object} callbacks
10965  *     An object containing callbacks for instantiation and runtime
10966  * @param {Function} callbacks.onLoad(this)
10967  *     Callback to invoke upon successful instantiation, passes in MediaPropertiesLayout object
10968  * @param {Function} callbacks.onLoadError(rsp)
10969  *     Callback to invoke on instantiation REST request error
10970  *     as passed by finesse.clientservices.ClientServices.ajax()
10971  *     {
10972  *         status: {Number} The HTTP status code returned
10973  *         content: {String} Raw string of response
10974  *         object: {Object} Parsed object of response
10975  *         error: {Object} Wrapped exception that was caught
10976  *         error.errorType: {String} Type of error that was caught
10977  *         error.errorMessage: {String} Message associated with error
10978  *     }
10979  * @param {Function} callbacks.onChange(this)
10980  *     Callback to invoke upon successful update, passes in MediaPropertiesLayout object
10981  * @param {Function} callbacks.onError(rsp)
10982  *     Callback to invoke on update error (refresh or event)
10983  *     as passed by finesse.clientservices.ClientServices.ajax()
10984  *     {
10985  *         status: {Number} The HTTP status code returned
10986  *         content: {String} Raw string of response
10987  *         object: {Object} Parsed object of response
10988  *         error: {Object} Wrapped exception that was caught
10989  *         error.errorType: {String} Type of error that was caught
10990  *         error.errorMessage: {String} Message associated with error
10991  *     }
10992  */
10993 
10994 /** @private */
10995 define('restservices/MediaPropertiesLayout',['restservices/RestBase'], function (RestBase) {
10996     var MediaPropertiesLayout = RestBase.extend(/** @lends finesse.restservices.MediaPropertiesLayout.prototype */{
10997 
10998         /**
10999          * @class
11000          * The MediaPropertiesLayout handles which call variables are associated with Dialogs.
11001          * 
11002          * @augments finesse.restservices.RestBase
11003          * @see finesse.restservices.Dialog#getMediaProperties
11004          * @see finesse.restservices.User#getMediaPropertiesLayout
11005          * @constructs
11006          */
11007         _fakeConstuctor: function () {
11008             /* This is here to hide the real init constructor from the public docs */
11009         },
11010         
11011         /**
11012          * @private
11013          * JavaScript representation of a MediaPropertiesLayout object. Also exposes
11014          * methods to operate on the object against the server.
11015          *
11016          * @param {Object} options
11017          *     An object with the following properties:<ul>
11018          *         <li><b>id:</b> The id of the object being constructed</li>
11019          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
11020          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
11021          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
11022          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
11023          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
11024          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
11025          *             <li><b>content:</b> {String} Raw string of response</li>
11026          *             <li><b>object:</b> {Object} Parsed object of response</li>
11027          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
11028          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
11029          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
11030          *             </ul></li>
11031          *         </ul></li>
11032          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
11033          **/
11034         init: function (options) {
11035             this._super(options);
11036         },
11037 
11038         /**
11039          * @private
11040          * Gets the REST class for the current object - this is the MediaPropertiesLayout object.
11041          */
11042         getRestClass: function () {
11043             return MediaPropertiesLayout;
11044         },
11045 
11046         /**
11047          * @private
11048          * Gets the REST type for the current object - this is a "MediaPropertiesLayout".
11049          */
11050         getRestType: function () {
11051             return "MediaPropertiesLayout";
11052         },
11053 
11054         /**
11055          * @private
11056          * Returns whether this object supports subscriptions
11057          */
11058         supportsSubscriptions: false,
11059 
11060         /**
11061          * Getter for the name.
11062          * @returns {String} The name.
11063          */
11064         getName: function () {
11065             this.isLoaded();
11066             return this._data.name;
11067         },
11068 
11069         /**
11070          * Getter for the description.
11071          * @returns {String} The description.
11072          */
11073         getDescription: function () {
11074             this.isLoaded();
11075             return this._data.description || "";
11076         },
11077 
11078         /**
11079          * Getter for the layout type (should be DEFAULT or CUSTOM).
11080          * @returns {String} The layout type.
11081          */
11082         getType: function () {
11083             this.isLoaded();
11084             return this._data.type || "";
11085         },
11086 
11087         /**
11088          * Retrieve the media properties layout. This call will re-query the server and refresh the layout object.
11089          * @returns {finesse.restservices.MediaPropertiesLayout}
11090          *     This MediaPropertiesLayout object to allow cascading
11091          */
11092         get: function () {
11093             this._synchronize();
11094 
11095             return this; //Allow cascading
11096         },
11097 
11098         /**
11099          * Gets the data for this object.
11100          * 
11101          * Performs safe conversion from raw API data to ensure that the returned layout object
11102          * always has a header with correct entry fields, and exactly two columns with lists of entries.
11103          *
11104          * @returns {finesse.restservices.MediaPropertiesLayout.Object} Data in columns (unless only one defined).
11105          */
11106         getData: function () {
11107 
11108             var layout = this._data, result, _addColumnData;
11109 
11110             result = this.getEmptyData();
11111             result.name = layout.name;
11112             result.description = layout.description;
11113             result.type = layout.type;
11114 
11115             /**
11116              * @private
11117              */
11118             _addColumnData = function (entryData, colIndex) {
11119 
11120                 if (!entryData) {
11121                     //If there's no entry data at all, rewrite entryData to be an empty collection of entries
11122                     entryData = {};
11123                 } else if (entryData.mediaProperty) {
11124                     //If entryData contains the keys for a single entry rather than being a collection of entries,
11125                     //rewrite it to be a collection containing a single entry
11126                     entryData = { "": entryData };
11127                 }
11128 
11129                 //Add each of the entries in the list to the column
11130                 jQuery.each(entryData, function (i, entryData) {
11131 
11132                     //If the entry has no displayName specified, explicitly set it to the empty string
11133                     if (!entryData.displayName) {
11134                         entryData.displayName = "";
11135                     }
11136 
11137                     result.columns[colIndex].push(entryData);
11138 
11139                 });
11140 
11141             };
11142 
11143             //The header should only contain a single entry
11144             if (layout.header && layout.header.entry) {
11145 
11146                 //If the entry has no displayName specified, explicitly set it to the empty string
11147                 if (!layout.header.entry.displayName) {
11148                     layout.header.entry.displayName = "";
11149                 }
11150 
11151                 result.header = layout.header.entry;
11152 
11153             } else {
11154 
11155                 throw "MediaPropertiesLayout.getData() - Header does not contain an entry";
11156 
11157             }
11158 
11159             //If the column object contains an entry object that wasn't part of a list of entries,
11160             //it must be a single right-hand entry object (left-hand entry object would be part of a list.)
11161             //Force the entry object to be the 2nd element in an otherwise-empty list.
11162             if (layout.column && layout.column.entry) {
11163                 layout.column = [
11164                     null,
11165                     { "entry": layout.column.entry }
11166                 ];
11167             }
11168 
11169             if (layout.column && layout.column.length > 0 && layout.column.length <= 2) {
11170 
11171                 //Render left column entries
11172                 if (layout.column[0] && layout.column[0].entry) {
11173                     _addColumnData(layout.column[0].entry, 0);
11174                 }
11175 
11176                 //Render right column entries
11177                 if (layout.column[1] && layout.column[1].entry) {
11178                     _addColumnData(layout.column[1].entry, 1);
11179                 }
11180 
11181             }
11182 
11183             return result;
11184 
11185         },
11186 
11187         /**
11188          * @private
11189          * Empty/template version of getData().
11190          *
11191          * Used by getData(), and by callers of getData() in error cases.
11192          */
11193         getEmptyData: function () {
11194 
11195             return {
11196                 header : {
11197                     displayName: null,
11198                     mediaProperty: null
11199                 },
11200                 columns : [[], []]
11201             };
11202 
11203         },
11204 
11205         /**
11206          * Update the layout of this MediaPropertiesLayout
11207          * @param {Object} layout
11208          *      The object representation of the layout you are setting
11209          * @param {finesse.interfaces.RequestHandlers} handlers
11210          *      An object containing the handlers for the request
11211          * @returns {finesse.restservices.MediaPropertiesLayout}
11212          *      This MediaPropertiesLayout object to allow cascading
11213          * @private
11214          */
11215         update: function (newLayoutObject, handlers) {
11216             var contentBody = {};
11217 
11218             // Make sure type is kept the same
11219             newLayoutObject.type = this.getType();
11220 
11221             contentBody[this.getRestType()] = newLayoutObject;
11222 
11223             //Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
11224             handlers = handlers || {};
11225 
11226             this.restRequest(this.getRestUrl(), {
11227                 method: 'PUT',
11228                 success: handlers.success,
11229                 error: handlers.error,
11230                 content: contentBody
11231             });
11232 
11233             return this; // Allow cascading
11234         },
11235 
11236         /**
11237          * Create a new MediaPropertiesLayout object with the layout passed in
11238          * @param {Object} layout
11239          *      The object representation of the layout you are creating
11240          * @param {finesse.interfaces.RequestHandlers} handlers
11241          *      An object containing the handlers for the request
11242          * @returns {finesse.restservices.MediaPropertiesLayout}
11243          *      This MediaPropertiesLayout object to allow cascading
11244          * @private
11245          */
11246         add: function (layout, handlers) {
11247             var contentBody = {};
11248 
11249             contentBody[this.getRestType()] = layout;
11250 
11251             handlers = handlers || {};
11252 
11253             this.restRequest(this.getRestUrl(), {
11254                 method: 'POST',
11255                 success: handlers.success,
11256                 error: handlers.error,
11257                 content: contentBody
11258             });
11259 
11260             return this; // Allow cascading
11261         },
11262 
11263         /**
11264          * Delete this MediaPropertiesLayout
11265          * @param {finesse.interfaces.RequestHandlers} handlers
11266          *      An object containing the handlers for the request
11267          * @returns {finesse.restservices.MediaPropertiesLayout}
11268          *      This MediaPropertiesLayout object to allow cascading
11269          * @private
11270          */
11271         "delete": function (handlers) {
11272             handlers = handlers || {};
11273 
11274             this.restRequest(this.getRestUrl(), {
11275                 method: 'DELETE',
11276                 success: handlers.success,
11277                 error: handlers.error,
11278                 content: undefined
11279             });
11280 
11281             return this; // Allow cascading
11282         }
11283 
11284     });
11285     
11286     MediaPropertiesLayout.Object = /** @lends finesse.restservices.MediaPropertiesLayout.Object.prototype */ {
11287         /**
11288          * @class Format of MediaPropertiesLayout Object.<br>
11289          * Object { <ul>
11290          *      <li>header : { <ul>
11291          *          <li>dispayName {String} 
11292          *          <li>mediaProperty {String}</ul>}
11293          *      <li>columns : { <ul>
11294          *          <li>[ [] , [] ]
11295          *          </ul>
11296          *      where column arrays consists of the same Object format as header.<br>
11297          *          }</ul>
11298          *      }<br>         
11299          * @constructs
11300          */
11301         _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
11302         
11303     };
11304 
11305 	window.finesse = window.finesse || {};
11306     window.finesse.restservices = window.finesse.restservices || {};
11307     window.finesse.restservices.MediaPropertiesLayout = MediaPropertiesLayout;
11308     
11309     return MediaPropertiesLayout;
11310 });
11311 
11312 /**
11313  * JavaScript representation of the Finesse MediaPropertiesLayout object for a User
11314  *
11315  * @requires MediaPropertiesLayout
11316  * @requires ClientServices
11317  * @requires finesse.FinesseBase
11318  * @requires finesse.restservices.RestBase
11319  */
11320 
11321 /** The following comment is to prevent jslint errors about 
11322  * using variables before they are defined.
11323  */
11324 /*global finesse*/
11325 
11326 /** @private */
11327 define('restservices/UserMediaPropertiesLayout',['restservices/MediaPropertiesLayout'], function (MediaPropertiesLayout) {
11328      var UserMediaPropertiesLayout = MediaPropertiesLayout.extend(/** @lends finesse.restservices.UserMediaPropertiesLayout.prototype */{
11329 
11330 		/**
11331 		 * @class
11332 		 * JavaScript representation of a UserMediaPropertiesLayout collection object. Also exposes
11333 		 * methods to operate on the object against the server.
11334 		 * 
11335 		 * @param {Object} options
11336 		 * An object with the following properties:<ul>
11337 		 *        <li><b>id:</b> The id of the object being constructed</li>
11338 		 *        <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
11339 		 *        <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
11340 		 *        <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
11341 		 *        <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
11342 		 *        <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
11343 		 *            <li><b>status:</b> {Number} The HTTP status code returned</li>
11344 		 *            <li><b>content:</b> {String} Raw string of response</li>
11345 		 *            <li><b>object:</b> {Object} Parsed object of response</li>
11346 		 *            <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
11347 		 *                <li><b>errorType:</b> {String} Type of error that was caught</li>
11348 		 *                <li><b>errorMessage:</b> {String} Message associated with error</li>
11349 		 *            </ul></li>
11350 		 *        </ul></li>
11351 		 *        <li><b>parentObj: (optional)</b> The parent object</li></ul>
11352 		 * @constructs
11353 		**/
11354 		init: function (options) {
11355 		    this._super(options);
11356 		},
11357 		
11358 		/**
11359 		 * @private
11360 		 * Gets the REST class for the current object - this is the UserMediaPropertiesLayout class.
11361 		 */
11362 		getRestClass: function () {
11363 		    return UserMediaPropertiesLayout;
11364 		},
11365 
11366         /**
11367          * Overrides the parent class.  Returns the url for the UserMediaPropertiesLayout resource
11368          */
11369         getRestUrl: function () {
11370             return ("/finesse/api/User/" + this.getId() + "/" + this.getRestType());
11371         },
11372 
11373         /**
11374          * @private
11375          * Override to throw an error because we cannot do an update on the User's
11376          * MediaPropertiesLayout node
11377          */
11378         update: function (layout, handlers) {
11379             throw new Error("update(): Cannot update layout for User's MediaPropertiesLayout");
11380         },
11381 
11382         /**
11383          * @private
11384          * Override to throw an error because we cannot create a new layout on the User's
11385          * MediaPropertiesLayout node
11386          */
11387         add: function (layout, handlers) {
11388             throw new Error("add(): Cannot create a new layout for User's MediaPropertiesLayout");
11389         },
11390 
11391         /**
11392          * @private
11393          * Override to throw an error because we cannot delete the layout on the User's
11394          * MediaPropertiesLayout node
11395          */
11396         "delete": function (layout, handlers) {
11397             throw new Error("delete(): Cannot delete the layout for User's MediaPropertiesLayout");
11398         }
11399 
11400     });
11401 	
11402 	window.finesse = window.finesse || {};
11403     window.finesse.restservices = window.finesse.restservices || {};
11404     window.finesse.restservices.UserMediaPropertiesLayout = UserMediaPropertiesLayout;
11405     
11406     return UserMediaPropertiesLayout;
11407 });
11408 
11409 /**
11410 * JavaScript representation of the Finesse mediaPropertiesLayouts collection
11411 * object which contains a list of mediaPropertiesLayout objects.
11412  *
11413  * @requires finesse.clientservices.ClientServices
11414  * @requires Class
11415  * @requires finesse.FinesseBase
11416  * @requires finesse.restservices.RestBase
11417  * @requires finesse.restservices.Dialog
11418  * @requires finesse.restservices.RestCollectionBase
11419  */
11420 
11421 /** @private */
11422 define('restservices/MediaPropertiesLayouts',[
11423     'restservices/RestCollectionBase',
11424     'restservices/RestBase',
11425     'restservices/MediaPropertiesLayout'
11426 ],
11427 function (RestCollectionBase, RestBase, MediaPropertiesLayout) {
11428 
11429     var MediaPropertiesLayouts = RestCollectionBase.extend({
11430 
11431         /**
11432          * @class
11433          * JavaScript representation of a mediaPropertiesLayouts collection object. Also exposes
11434          * methods to operate on the object against the server.
11435          *
11436          * @param {Object} options
11437          *     An object with the following properties:<ul>
11438          *         <li><b>id:</b> The id of the object being constructed</li>
11439          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
11440          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
11441          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
11442          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
11443          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
11444          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
11445          *             <li><b>content:</b> {String} Raw string of response</li>
11446          *             <li><b>object:</b> {Object} Parsed object of response</li>
11447          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
11448          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
11449          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
11450          *             </ul></li>
11451          *         </ul></li>
11452          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
11453          *  @constructs
11454          **/
11455         init: function (options) {
11456             this._super(options);
11457         },
11458 
11459         /**
11460          * @private
11461          * Gets the REST class for the current object - this is the mediaPropertiesLayouts class.
11462          */
11463         getRestClass: function () {
11464             return MediaPropertiesLayouts;
11465         },
11466 
11467         /**
11468          * @private
11469          * Gets the REST class for the objects that make up the collection. - this
11470          * is the mediaPropertiesLayout class.
11471          */
11472         getRestItemClass: function () {
11473             return MediaPropertiesLayout;
11474         },
11475 
11476         /**
11477          * @private
11478          * Gets the REST type for the current object - this is a "mediaPropertiesLayouts".
11479          */
11480         getRestType: function () {
11481             return "MediaPropertiesLayouts";
11482         },
11483 
11484         /**
11485          * @private
11486          * Gets the REST type for the objects that make up the collection - this is "mediaPropertiesLayouts".
11487          */
11488         getRestItemType: function () {
11489             return "MediaPropertiesLayout";
11490         },
11491 
11492         /**
11493          * @private
11494          * Override default to indicates that the collection supports making requests.
11495          */
11496         supportsRequests: true,
11497 
11498         /**
11499          * @private
11500          * Override default to indicates that the collection does not subscribe to its objects.
11501          */
11502         supportsRestItemSubscriptions: false,
11503 
11504         /**
11505          * @private
11506          * Retrieve the MediaPropertiesLayouts. This call will re-query the server and refresh the collection.
11507          *
11508          * @returns {finesse.restservices.MediaPropertiesLayouts}
11509          *     This MediaPropertiesLayouts object to allow cascading.
11510          */
11511         get: function () {
11512             // set loaded to false so it will rebuild the collection after the get
11513             this._loaded = false;
11514             // reset collection
11515             this._collection = {};
11516             // perform get
11517             this._synchronize();
11518             return this;
11519         }
11520     });
11521 
11522     window.finesse = window.finesse || {};
11523     window.finesse.restservices = window.finesse.restservices || {};
11524     window.finesse.restservices.MediaPropertiesLayouts = MediaPropertiesLayouts;
11525         
11526     return MediaPropertiesLayouts;
11527 });
11528 
11529 /**
11530  * JavaScript representation of the Finesse MediaPropertiesLayout object for a User
11531  *
11532  * @requires MediaPropertiesLayout
11533  * @requires ClientServices
11534  * @requires finesse.FinesseBase
11535  * @requires finesse.restservices.RestBase
11536  */
11537 
11538 /** The following comment is to prevent jslint errors about 
11539  * using variables before they are defined.
11540  */
11541 /*global finesse*/
11542 
11543 /** @private */
11544 define('restservices/UserMediaPropertiesLayouts',[
11545 	'restservices/MediaPropertiesLayouts',
11546 	'restservices/UserMediaPropertiesLayout'
11547 ],
11548 function (MediaPropertiesLayouts, UserMediaPropertiesLayout) {
11549      var UserMediaPropertiesLayouts = MediaPropertiesLayouts.extend(/** @lends finesse.restservices.UserMediaPropertiesLayouts.prototype */{
11550 
11551 		/**
11552 		 * @class
11553 		 * JavaScript representation of a UserMediaPropertiesLayouts collection object. Also exposes
11554 		 * methods to operate on the object against the server.
11555 		 * 
11556 		 * @param {Object} options
11557 		 * An object with the following properties:<ul>
11558 		 *        <li><b>id:</b> The id of the object being constructed</li>
11559 		 *        <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
11560 		 *        <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
11561 		 *        <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
11562 		 *        <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
11563 		 *        <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
11564 		 *            <li><b>status:</b> {Number} The HTTP status code returned</li>
11565 		 *            <li><b>content:</b> {String} Raw string of response</li>
11566 		 *            <li><b>object:</b> {Object} Parsed object of response</li>
11567 		 *            <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
11568 		 *                <li><b>errorType:</b> {String} Type of error that was caught</li>
11569 		 *                <li><b>errorMessage:</b> {String} Message associated with error</li>
11570 		 *            </ul></li>
11571 		 *        </ul></li>
11572 		 *        <li><b>parentObj: (optional)</b> The parent object</li></ul>
11573 		 * @constructs
11574 		**/
11575 		init: function (options) {
11576 		    this._super(options);
11577 		},
11578 		
11579 		/**
11580 		 * @private
11581 		 * Gets the REST class for the current object - this is the UserMediaPropertiesLayouts class.
11582 		 */
11583 		getRestClass: function () {
11584 		    return UserMediaPropertiesLayouts;
11585 		},
11586 
11587         /**
11588          * @private
11589          * Gets the REST class for the objects that make up the collection. - this
11590          * is the UserMediaPropertiesLayout class.
11591          */
11592 		getRestItemClass: function() {
11593 			return UserMediaPropertiesLayout;
11594 		}
11595     });
11596 	
11597 	window.finesse = window.finesse || {};
11598     window.finesse.restservices = window.finesse.restservices || {};
11599     window.finesse.restservices.UserMediaPropertiesLayouts = UserMediaPropertiesLayouts;
11600     
11601     return UserMediaPropertiesLayouts;
11602 });
11603 
11604 /**
11605  * JavaScript representation of the Finesse User object
11606  *
11607  * @requires finesse.clientservices.ClientServices
11608  * @requires Class
11609  * @requires finesse.FinesseBase
11610  * @requires finesse.restservices.RestBase
11611  */
11612 
11613 /** @private */
11614 define('restservices/User',[
11615     'restservices/RestBase',
11616     'restservices/Dialogs',
11617     'restservices/ClientLog',
11618     'restservices/Queues',
11619     'restservices/WrapUpReasons',
11620     'restservices/PhoneBooks',
11621     'restservices/Workflows',
11622     'restservices/UserMediaPropertiesLayout',
11623     'restservices/UserMediaPropertiesLayouts',
11624     'utilities/Utilities'
11625 ],
11626 function (RestBase, Dialogs, ClientLog, Queues, WrapUpReasons, PhoneBooks, Workflows, UserMediaPropertiesLayout, UserMediaPropertiesLayouts, Utilities) {
11627     
11628     var User = RestBase.extend(/** @lends finesse.restservices.User.prototype */{
11629 
11630         _dialogs : null,
11631         _clientLogObj : null,
11632         _wrapUpReasons : null,
11633         _phoneBooks : null,
11634         _workflows : null,
11635         _mediaPropertiesLayout : null,
11636         _mediaPropertiesLayouts : null,
11637         _queues : null,
11638         
11639         /**
11640          * @class
11641          * The User represents a Finesse Agent or Supervisor.
11642          *
11643          * @param {Object} options
11644          *     An object with the following properties:<ul>
11645          *         <li><b>id:</b> The id of the object being constructed</li>
11646          *         <li><b>onLoad(this): (optional)</b> callback handler for when the object is successfully loaded from the server</li>
11647          *         <li><b>onChange(this): (optional)</b> callback handler for when an update notification of the object is received</li>
11648          *         <li><b>onAdd(this): (optional)</b> callback handler for when a notification that the object is created is received</li>
11649          *         <li><b>onDelete(this): (optional)</b> callback handler for when a notification that the object is deleted is received</li>
11650          *         <li><b>onError(rsp): (optional)</b> callback handler for if loading of the object fails, invoked with the error response object:<ul>
11651          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
11652          *             <li><b>content:</b> {String} Raw string of response</li>
11653          *             <li><b>object:</b> {Object} Parsed object of response</li>
11654          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
11655          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
11656          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
11657          *             </ul></li>
11658          *         </ul></li>
11659          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
11660          * @augments finesse.restservices.RestBase
11661          * @constructs
11662          * @example
11663          *      _user = new finesse.restservices.User({
11664          *                      id: _id, 
11665          *                      onLoad : _handleUserLoad,
11666          *                      onChange : _handleUserChange
11667          *      });
11668          **/
11669         init: function (options) {
11670             this._super(options);
11671         },
11672         
11673         Callbacks: {},
11674     
11675         /**
11676          * @private
11677          * Gets the REST class for the current object - this is the User object.
11678          */
11679         getRestClass: function () {
11680             return User;
11681         },
11682     
11683         /**
11684         * @private
11685          * Gets the REST type for the current object - this is a "User".
11686          */
11687         getRestType: function () {
11688             return "User";
11689         },
11690         /**
11691          * @private
11692          * overloading this to return URI
11693          */
11694         getXMPPNodePath: function () {
11695             return this.getRestUrl();
11696         },
11697         /**
11698         * @private
11699          * Returns whether this object supports subscriptions
11700          */
11701         supportsSubscriptions: function () {
11702             return true;
11703         },
11704     
11705         /**
11706          * Getter for the firstName of this User.
11707          * @returns {String}
11708          *     The firstName for this User
11709          */
11710         getFirstName: function () {
11711             this.isLoaded();
11712             return Utilities.convertNullToEmptyString(this.getData().firstName);
11713         },
11714     
11715         /**
11716          * Getter for the lastName of this User.
11717          * @returns {String}
11718          *     The lastName for this User
11719          */
11720         getLastName: function () {
11721             this.isLoaded();
11722             return Utilities.convertNullToEmptyString(this.getData().lastName);
11723         },
11724     
11725         /**
11726          * Getter for the extension of this User.
11727          * @returns {String}
11728          *     The extension, if any, of this User
11729          */
11730         getExtension: function () {
11731             this.isLoaded();
11732             return Utilities.convertNullToEmptyString(this.getData().extension);
11733         },
11734         
11735         /**
11736          * Getter for the id of the Team of this User
11737          * @returns {String}
11738          *     The current (or last fetched) id of the Team of this User
11739          */
11740         getTeamId: function () {
11741             this.isLoaded();
11742             return this.getData().teamId;
11743         },
11744         
11745         /**
11746          * Getter for the name of the Team of this User
11747          * @returns {String}
11748          *     The current (or last fetched) name of the Team of this User
11749          */
11750         getTeamName: function () {
11751             this.isLoaded();
11752             return this.getData().teamName;
11753         },
11754         
11755         /**
11756          * Is user an agent?
11757          * @returns {Boolean} True if user has role of agent, else false.
11758          */
11759         hasAgentRole: function () {
11760             this.isLoaded();
11761             return this.hasRole("Agent");
11762         },
11763     
11764         /**
11765          * Is user a supervisor?
11766          * @returns {Boolean} True if user has role of supervisor, else false.
11767          */
11768         hasSupervisorRole: function () {
11769             this.isLoaded();
11770             return this.hasRole("Supervisor");
11771         },
11772     
11773         /**
11774          * @private
11775          * Checks to see if user has "theRole"
11776          * @returns {Boolean}
11777          */
11778         hasRole: function (theRole) {
11779             this.isLoaded();
11780             var result = false, i, roles, len;
11781     
11782             roles = this.getData().roles.role;
11783             len = roles.length;
11784             if (typeof roles === 'string') {
11785                 if (roles === theRole) {
11786                     result = true;
11787                 }
11788             } else {
11789                 for (i = 0; i < len ; i = i + 1) {
11790                     if (roles[i] === theRole) {
11791                         result = true;
11792                         break;
11793                     }
11794                 }
11795             }
11796     
11797             return result;
11798         },
11799 
11800         /**
11801          * Getter for the pending state of this User.
11802          * @returns {String}
11803          *     The pending state of this User
11804          * @see finesse.restservices.User.States
11805          */
11806         getPendingState: function () {
11807             this.isLoaded();
11808             return Utilities.convertNullToEmptyString(this.getData().pendingState);
11809         },
11810     
11811         /**
11812          * Getter for the state of this User.
11813          * @returns {String}
11814          *     The current (or last fetched) state of this User
11815          * @see finesse.restservices.User.States
11816          */
11817         getState: function () {
11818             this.isLoaded();
11819             return this.getData().state;
11820         },
11821 
11822         /**
11823          * Getter for the state change time of this User.
11824          * @returns {String}
11825          *     The state change time of this User
11826          */
11827         getStateChangeTime: function () {
11828             this.isLoaded();
11829             return this.getData().stateChangeTime;
11830         },
11831         
11832         /**
11833          * Getter for the wrap-up mode of this User.
11834          * @see finesse.restservices.User.WrapUpMode
11835          * @returns {String} The wrap-up mode of this user that is a value of {@link finesse.restservices.User.WrapUpMode}
11836          */
11837         getWrapUpOnIncoming: function () {
11838             this.isLoaded();
11839             return this.getData().settings.wrapUpOnIncoming;
11840         },
11841         
11842         /**
11843          * Is User required to go into wrap-up?
11844          * @see finesse.restservices.User.WrapUpMode
11845          * @return {Boolean}
11846          *      True if this agent is required to go into wrap-up.
11847          */
11848         isWrapUpRequired: function () {
11849             return (this.getWrapUpOnIncoming() === User.WrapUpMode.REQUIRED || 
11850                     this.getWrapUpOnIncoming() === User.WrapUpMode.REQUIRED_WITH_WRAP_UP_DATA);
11851         },
11852     
11853         /**
11854          * Checks to see if the user is considered a mobile agent by checking for
11855          * the existence of the mobileAgent node.
11856          * @returns {Boolean}
11857          *      True if this agent is a mobile agent.
11858          */
11859         isMobileAgent: function () {
11860             this.isLoaded();
11861             var ma = this.getData().mobileAgent;
11862             return ma !== null && typeof ma === "object";
11863         },
11864     
11865         /**
11866          * Getter for the mobile agent work mode.
11867          * @returns {finesse.restservices.User.WorkMode}
11868          *      If available, return the mobile agent work mode, otherwise null.
11869          */
11870         getMobileAgentMode: function () {
11871             this.isLoaded();
11872             if (this.isMobileAgent()) {
11873                 return this.getData().mobileAgent.mode;
11874             }
11875             return null;
11876         },
11877     
11878         /**
11879          * Getter for the mobile agent dial number.
11880          * @returns {String}
11881          *      If available, return the mobile agent dial number, otherwise null.
11882          */
11883         getMobileAgentDialNumber: function () {
11884             this.isLoaded();
11885             if (this.isMobileAgent()) {
11886                 return this.getData().mobileAgent.dialNumber;
11887             }
11888             return null;
11889         },
11890     
11891         /**
11892          * Getter for a Dialogs collection object that is associated with User.
11893          * @param {finesse.interfaces.RestObjectHandlers} [handlers] Object that sets callback handlers (only 
11894          * applicable when Object has not been previously created).
11895          * @returns {finesse.restservices.Dialogs}
11896          *     A Dialogs collection object.
11897          */
11898         getDialogs: function (callbacks) {
11899             var options = callbacks || {};
11900             options.parentObj = this;
11901             this.isLoaded();
11902     
11903             if (this._dialogs === null) {
11904                 this._dialogs = new Dialogs(options);
11905             }
11906     
11907             return this._dialogs;
11908         },
11909         
11910         /**
11911          * @private
11912          * Getter for a ClientLog object that is associated with User.
11913          * @param {finesse.interfaces.RestObjectHandlers} [handlers] Object that sets callback handlers (only 
11914          * applicable when Object has not been previously created).
11915          * @returns {finesse.restservices.ClientLog}
11916          *     A ClientLog collection object.
11917          */
11918         getClientLog: function (callbacks) {
11919             var options = callbacks || {};
11920             options.parentObj = this;
11921             this.isLoaded();
11922            
11923             if (this._clientLogObj === null) {
11924                 this._clientLogObj = new ClientLog(options);
11925             }
11926             else {
11927                 if(options.onLoad && typeof options.onLoad === "function") {
11928                 options.onLoad(this._clientLogObj);
11929                 }
11930             }
11931             return this._clientLogObj;
11932         },
11933        
11934         /**
11935          * Getter for a Queues collection object that is associated with User.
11936          * @param {finesse.interfaces.RestObjectHandlers} [handlers] Object that sets callback handlers (only 
11937          * applicable when Object has not been previously created).
11938          * @returns {finesse.restservices.Queues}
11939          *     A Queues collection object.
11940          */
11941         getQueues: function (callbacks) {
11942             var options = callbacks || {};
11943             options.parentObj = this;
11944             this.isLoaded();
11945     
11946             if (this._queues === null) {
11947                 this._queues = new Queues(options);
11948             }
11949     
11950             return this._queues;
11951         },
11952 
11953         /**
11954          * Getter for a WrapUpReasons collection object that is associated with User.
11955          * @param {finesse.interfaces.RestObjectHandlers} [handlers] Object that sets callback handlers (only 
11956          * applicable when Object has not been previously created).
11957          * @returns {finesse.restservices.WrapUpReasons}
11958          *     A WrapUpReasons collection object.
11959          */
11960         getWrapUpReasons: function (callbacks) {
11961             var options = callbacks || {};
11962             options.parentObj = this;
11963             this.isLoaded();
11964     
11965             if (this._wrapUpReasons === null) {
11966                 this._wrapUpReasons = new WrapUpReasons(options);
11967             }
11968     
11969             return this._wrapUpReasons;
11970         },
11971 
11972         /**
11973          * Getter for a PhoneBooks collection object that is associated with User.
11974          * @param {finesse.interfaces.RestObjectHandlers} [handlers] Object that sets callback handlers (only 
11975          * applicable when Object has not been previously created).
11976          * @returns {finesse.restservices.PhoneBooks}
11977          *     A PhoneBooks collection object.
11978          */
11979         getPhoneBooks: function (callbacks) {
11980             var options = callbacks || {};
11981             options.parentObj = this;
11982             this.isLoaded();
11983     
11984             if (this._phoneBooks === null) {
11985                 this._phoneBooks = new PhoneBooks(options);
11986             }
11987     
11988             return this._phoneBooks;
11989         },
11990 
11991         /**
11992          * @private
11993          * Loads the Workflows collection object that is associated with User and
11994          * 'returns' them to the caller via the handlers.
11995          * @param {finesse.interfaces.RestObjectHandlers} [handlers] Object that sets callback handlers (only 
11996          * applicable when Object has not been previously created).
11997          * @see finesse.restservices.Workflow
11998          * @see finesse.restservices.Workflows
11999          * @see finesse.restservices.RestCollectionBase
12000          */
12001         loadWorkflows: function (callbacks) {
12002             var options = callbacks || {};
12003             options.parentObj = this;
12004             this.isLoaded();
12005 
12006             if (this._workflows === null) {
12007                 this._workflows = new Workflows(options);
12008             } else {
12009                 this._workflows.refresh();
12010             }
12011 
12012         },
12013 
12014         /**
12015          * Getter for a UserMediaPropertiesLayout object that is associated with User.
12016          * @param {finesse.interfaces.RestObjectHandlers} [handlers] Object that sets callback handlers (only 
12017          * applicable when Object has not been previously created).
12018          * @returns {finesse.restservices.UserMediaPropertiesLayout}
12019          *     The UserMediaPropertiesLayout object associated with this user
12020          */
12021         getMediaPropertiesLayout: function (callbacks) {
12022             var options = callbacks || {};
12023             options.parentObj = this;
12024             options.id = this._id;
12025     
12026             this.isLoaded();
12027             if (this._mediaPropertiesLayout === null) {
12028                 this._mediaPropertiesLayout = new UserMediaPropertiesLayout(options);
12029             }
12030             return this._mediaPropertiesLayout;
12031         },
12032 
12033         /**
12034          * Getter for a UserMediaPropertiesLayouts object that is associated with User.
12035          * @param {finesse.interfaces.RestObjectHandlers} [handlers] Object that sets callback handlers (only 
12036          * applicable when Object has not been previously created).
12037          * @returns {finesse.restservices.UserMediaPropertiesLayout}
12038          *     The UserMediaPropertiesLayout object associated with this user
12039          */
12040         getMediaPropertiesLayouts: function (callbacks) {
12041             var options = callbacks || {};
12042             options.parentObj = this;
12043     
12044             this.isLoaded();
12045             if (this._mediaPropertiesLayouts === null) {
12046                 this._mediaPropertiesLayouts = new UserMediaPropertiesLayouts(options);
12047             }
12048             return this._mediaPropertiesLayouts;
12049         },
12050     
12051         /**
12052          * Getter for the supervised Teams this User (Supervisor) supervises, if any.
12053          * @see finesse.restservices.Team
12054          * @returns {Array}
12055          *     An array of Teams supervised by this User (Supervisor)
12056          */
12057         getSupervisedTeams: function () {
12058             this.isLoaded();
12059     
12060             try {
12061                 return Utilities.getArray(this.getData().teams.Team);
12062             } catch (e) {
12063                 return [];
12064             }
12065     
12066         },
12067     
12068         /**
12069          * Perform an agent login for this user, associating him with the
12070          * specified extension.
12071          * @param {Object} params
12072          *     An object containing properties for agent login.
12073          * @param {String} params.extension
12074          *     The extension to associate with this user
12075          * @param {Object} [params.mobileAgent]
12076          *     A mobile agent object containing the mode and dial number properties.
12077          * @param {finesse.interfaces.RequestHandlers} params.handlers
12078          * @see finesse.interfaces.RequestHandlers
12079          * @returns {finesse.restservices.User}
12080          *     This User object, to allow cascading
12081          * @private
12082          */
12083         _login: function (params) {
12084             var handlers, contentBody = {},
12085             restType = this.getRestType();
12086             
12087             // Protect against null dereferencing.
12088             params = params || {};
12089     
12090             contentBody[restType] = {
12091                 "state": User.States.LOGIN,
12092                 "extension": params.extension
12093             };
12094     
12095             // Create mobile agent node if available.
12096             if (typeof params.mobileAgent === "object") {
12097                 contentBody[restType].mobileAgent = {
12098                     "mode": params.mobileAgent.mode,
12099                     "dialNumber": params.mobileAgent.dialNumber
12100                 };
12101             }
12102     
12103             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
12104             handlers = params.handlers || {};
12105     
12106             this.restRequest(this.getRestUrl(), {
12107                 method: 'PUT',
12108                 success: handlers.success,
12109                 error: handlers.error,
12110                 content: contentBody
12111             });
12112     
12113             return this; // Allow cascading
12114         },
12115     
12116         /**
12117          * Perform an agent login for this user, associating him with the
12118          * specified extension.
12119          * @param {String} extension
12120          *     The extension to associate with this user
12121          * @param {finesse.interfaces.RequestHandlers} handlers
12122          *     An object containing the handlers for the request
12123          * @returns {finesse.restservices.User}
12124          *     This User object, to allow cascading
12125          */
12126         login: function (extension, handlers) {
12127             this.isLoaded();
12128             var params = {
12129                 "extension": extension,
12130                 "handlers": handlers
12131             };
12132             return this._login(params);
12133         },
12134     
12135         /**
12136          * Perform an agent login for this user, associating him with the
12137          * specified extension.
12138          * @param {String} extension
12139          *     The extension to associate with this user
12140          * @param {String} mode
12141          *     The mobile agent work mode as defined in finesse.restservices.User.WorkMode.
12142          * @param {String} extension
12143          *     The external dial number desired to be used by the mobile agent.
12144          * @param {finesse.interfaces.RequestHandlers} handlers
12145          *     An object containing the handlers for the request
12146          * @returns {finesse.restservices.User}
12147          *     This User object, to allow cascading
12148          */
12149         loginMobileAgent: function (extension, mode, dialNumber, handlers) {
12150             this.isLoaded();
12151             var params = {
12152                 "extension": extension,
12153                 "mobileAgent": {
12154                     "mode": mode,
12155                     "dialNumber": dialNumber
12156                 },
12157                 "handlers": handlers
12158             };
12159             return this._login(params);
12160         },
12161     
12162         /**
12163          * Perform an agent logout for this user.
12164          * @param {String} reasonCode
12165          *     The reason this user is logging out.  Pass null for no reason.
12166          * @param {finesse.interfaces.RequestHandlers} handlers
12167          *     An object containing the handlers for the request
12168          * @returns {finesse.restservices.User}
12169          *     This User object, to allow cascading
12170          */
12171         logout: function (reasonCode, handlers) {
12172             return this.setState("LOGOUT", reasonCode, handlers);
12173         },
12174     
12175         /**
12176          * Set the state of the user.
12177          * @param {String} newState
12178          *     The state you are setting
12179          * @param {ReasonCode} reasonCode
12180          *     The reason this user is logging out.  Pass null for no reason.
12181          * @param {finesse.interfaces.RequestHandlers} handlers
12182          *     An object containing the handlers for the request
12183          * @see finesse.restservices.User.States
12184          * @returns {finesse.restservices.User}
12185          *     This User object, to allow cascading
12186          */
12187         setState: function (newState, reasonCode, handlers) {
12188             this.isLoaded();
12189     
12190             var options, contentBody = {};
12191     
12192             if (!reasonCode) {
12193                 contentBody[this.getRestType()] = {
12194                     "state": newState
12195                 };
12196             } else {
12197                 contentBody[this.getRestType()] = {
12198                     "state": newState,
12199                     "reasonCodeId": reasonCode.id
12200                 };
12201             }
12202     
12203             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
12204             handlers = handlers || {};
12205     
12206             options = {
12207                 method: 'PUT',
12208                 success: handlers.success,
12209                 error: handlers.error,
12210                 content: contentBody
12211             };
12212     
12213             // After removing the selective 202 handling, we should be able to just use restRequest
12214             this.restRequest(this.getRestUrl(), options);
12215     
12216             return this; // Allow cascading
12217         },
12218     
12219         /**
12220          * Make call to a particular phone number.
12221          *
12222          * @param {String} 
12223          *     The number to call
12224          * @param {finesse.interfaces.RequestHandlers} handlers
12225          *     An object containing the handlers for the request
12226          * @returns {finesse.restservices.User}
12227          *     This User object, to allow cascading
12228          */ 
12229         makeCall: function (number, handlers) {
12230             this.isLoaded();
12231     
12232             this.getDialogs().createNewCallDialog(number, this.getExtension(), handlers);
12233     
12234             return this; // Allow cascading
12235         },
12236     
12237         /**
12238          * Make a silent monitor call to a particular agent's phone number.
12239          *
12240          * @param {String} 
12241          *     The number to call
12242          * @param {finesse.interfaces.RequestHandlers} handlers
12243          *     An object containing the handlers for the request
12244          * @returns {finesse.restservices.User}
12245          *     This User object, to allow cascading
12246          */
12247         makeSMCall: function (number, handlers) {
12248             this.isLoaded();
12249     
12250             var actionType = "SILENT_MONITOR";
12251     
12252             this.getDialogs().createNewSuperviseCallDialog(number, this.getExtension(), actionType, handlers);
12253     
12254             return this; // Allow cascading
12255         },
12256         
12257     
12258         /**
12259          * Make a silent monitor call to a particular agent's phone number.
12260          *
12261          * @param {String}
12262          *     The number to call
12263          * @param {String} dialogUri
12264          *     The associated dialog uri of SUPERVISOR_MONITOR call
12265          * @param {finesse.interfaces.RequestHandlers} handlers
12266          *     An object containing the handlers for the request
12267          * @see finesse.restservices.dialog
12268          * @returns {finesse.restservices.User}
12269          *     This User object, to allow cascading
12270          */
12271         makeBargeCall:function (number, dialogURI, handlers) {
12272             this.isLoaded();
12273             var actionType = "BARGE_CALL";
12274             this.getDialogs().createNewBargeCall( this.getExtension(), number, actionType, dialogURI,handlers);
12275     
12276             return this; // Allow cascading
12277         },
12278         
12279         /**
12280          * Returns true if the user's current state will result in a pending state change. A pending state
12281          * change is a request to change state that does not result in an immediate state change. For
12282          * example if an agent attempts to change to the NOT_READY state while in the TALKING state, the
12283          * agent will not change state until the call ends.
12284          *
12285          * The current set of states that result in pending state changes is as follows:
12286          *     TALKING
12287          *     HOLD
12288          *     RESERVED_OUTBOUND_PREVIEW
12289          *  @returns {Boolean} True if there is a pending state change.
12290          *  @see finesse.restservices.User.States
12291          */
12292         isPendingStateChange: function () {
12293             var state = this.getState();
12294             return state && ((state === User.States.TALKING) || (state === User.States.HOLD) || (state === User.States.RESERVED_OUTBOUND_PREVIEW));
12295         },
12296         
12297         /**
12298          * Returns true if the user's current state is WORK or WORK_READY. This is used so
12299          * that a pending state is not cleared when moving into wrap up (work) mode. 
12300          * Note that we don't add this as a pending state, since changes while in wrap up
12301          * occur immediately (and we don't want any "pending state" to flash on screen.
12302          * 
12303          * @see finesse.restservices.User.States
12304          * @returns {Boolean} True if user is in wrap-up mode.
12305          */
12306         isWrapUp: function () {
12307             var state = this.getState();
12308             return state && ((state === User.States.WORK) || (state === User.States.WORK_READY));
12309         },
12310     
12311         /**
12312          * @private
12313          * Parses a uriString to retrieve the id portion
12314          * @param {String} uriString
12315          * @return {String} id
12316          */
12317         _parseIdFromUriString : function (uriString) {
12318             return Utilities.getId(uriString);
12319         },
12320 
12321         /**
12322          * Gets the user's Reason Code label.
12323          * Works for both Not Ready and Logout reason codes
12324          * @return {String} the reason code label, or empty string if none
12325          */
12326         getReasonCodeLabel : function () {
12327             this.isLoaded();
12328 
12329             if (this.getData().reasonCode) {
12330                 return this.getData().reasonCode.label;
12331             } else {
12332                 return "";
12333             }
12334         },
12335     
12336         /**
12337          * Gets the user's Not Ready reason code.
12338          * @return {String} Reason Code Id, or undefined if not set or indeterminate
12339          */
12340         getNotReadyReasonCodeId : function () {
12341             this.isLoaded();
12342     
12343             var reasoncodeIdResult, finesseServerReasonCodeId;
12344             finesseServerReasonCodeId = this.getData().reasonCodeId;
12345     
12346             //FinesseServer will give "-l" => we will set to undefined (for convenience)
12347             if (finesseServerReasonCodeId !== "-1") {
12348                 reasoncodeIdResult = finesseServerReasonCodeId;
12349             }
12350     
12351             return reasoncodeIdResult;
12352         },
12353     
12354         /**
12355          * Performs a GET against the Finesse server looking up the reasonCodeId specified.
12356          * Note that there is no return value; use the success handler to process a
12357          * valid return.
12358          * @param {finesse.interfaces.RequestHandlers} handlers
12359          *     An object containing the handlers for the request
12360          * @param {String} reasonCodeId The id for the reason code to lookup
12361          * 
12362          */
12363         getReasonCodeById : function (handlers, reasonCodeId)
12364         {
12365             var self = this, contentBody, reasonCode, url;
12366             contentBody = {};
12367     
12368             url = this.getRestUrl() + "/ReasonCode/" + reasonCodeId;
12369             this.restRequest(url, {
12370                 method: 'GET',
12371                 success: function (rsp) {
12372                     reasonCode = {
12373                         uri: rsp.object.ReasonCode.uri,
12374                         label: rsp.object.ReasonCode.label,
12375                         id: self._parseIdFromUriString(rsp.object.ReasonCode.uri)
12376                     };
12377                     handlers.success(reasonCode);
12378                 },
12379                 error: function (rsp) {
12380                     handlers.error(rsp);
12381                 },
12382                 content: contentBody
12383             });
12384         },
12385     
12386         /**
12387          * Performs a GET against Finesse server retrieving all the specified type of reason codes.
12388          * @param {String} type (LOGOUT or NOT_READY)
12389          * @param {finesse.interfaces.RequestHandlers} handlers
12390          *     An object containing the handlers for the request
12391          */
12392         _getReasonCodesByType : function (type, handlers)
12393         {
12394             var self = this, contentBody = {}, url, reasonCodes, i, reasonCodeArray;
12395     
12396             url = this.getRestUrl() + "/ReasonCodes?category=" + type;
12397             this.restRequest(url, {
12398                 method: 'GET',
12399                 success: function (rsp) {
12400                     reasonCodes = [];
12401     
12402                     reasonCodeArray = rsp.object.ReasonCodes.ReasonCode;
12403                     if (reasonCodeArray === undefined) {
12404                         reasonCodes = undefined;
12405                     } else if (reasonCodeArray[0] !== undefined) {
12406                         for (i = 0; i < reasonCodeArray.length; i = i + 1) {
12407                             reasonCodes[i] = {
12408                                 label: rsp.object.ReasonCodes.ReasonCode[i].label,
12409                                 id: self._parseIdFromUriString(rsp.object.ReasonCodes.ReasonCode[i].uri)
12410                             };
12411                         }
12412                     } else {
12413                         reasonCodes[0] = {
12414                             label: rsp.object.ReasonCodes.ReasonCode.label,
12415                             id: self._parseIdFromUriString(rsp.object.ReasonCodes.ReasonCode.uri)
12416                         };
12417                     }
12418                     handlers.success(reasonCodes);
12419                 },
12420                 error: function (rsp) {
12421                     handlers.error(rsp);
12422                 },
12423                 content: contentBody
12424             });
12425         },
12426     
12427         /**
12428          * Performs a GET against Finesse server retrieving all the Signout reason codes.
12429          * Note that there is no return value; use the success handler to process a
12430          * valid return.
12431          * @param {finesse.interfaces.RequestHandlers} handlers
12432          *     An object containing the handlers for the request
12433          */
12434         getSignoutReasonCodes : function (handlers)
12435         {
12436             this._getReasonCodesByType("LOGOUT", handlers);
12437         },
12438     
12439         /**
12440          * Performs a GET against Finesse server retrieving all the Not Ready reason codes.
12441          * Note that there is no return value; use the success handler to process a
12442          * valid return.
12443          * @param {finesse.interfaces.RequestHandlers} handlers
12444          *     An object containing the handlers for the request
12445          */
12446         getNotReadyReasonCodes : function (handlers)
12447         {
12448             this._getReasonCodesByType("NOT_READY", handlers);
12449         }
12450     });
12451     
12452     User.States = /** @lends finesse.restservices.User.States.prototype */ {
12453             /**
12454              * User Login.  Note that while this is an action, is not technically a state, since a 
12455              * logged-in User will always be in a specific state (READY, NOT_READY, TALKING, etc.).
12456              */
12457             LOGIN: "LOGIN",
12458             /**
12459              * User is logged out.
12460              */
12461             LOGOUT: "LOGOUT",
12462             /**
12463              * User is not ready. Note that in UCCX implementations, the user is in this state while on a non-routed call.
12464              */
12465             NOT_READY: "NOT_READY",
12466             /**
12467              * User is ready for calls.
12468              */
12469             READY: "READY",
12470             /**
12471              * User has a call coming in, but has not answered it.
12472              */
12473             RESERVED: "RESERVED",
12474             /**
12475              * User has an outbound call being made, but has not been connected to it.
12476              */
12477             RESERVED_OUTBOUND: "RESERVED_OUTBOUND",
12478             /**
12479              * User has an outbound call's preview information being displayed, but has not acted on it.
12480              */
12481             RESERVED_OUTBOUND_PREVIEW: "RESERVED_OUTBOUND_PREVIEW",
12482             /**
12483              * User is on a call.  Note that in UCCX implementations, this is for routed calls only.
12484              */
12485             TALKING: "TALKING",
12486             /**
12487              * User is on hold.  Note that in UCCX implementations, the user remains in TALKING state while on hold.
12488              */
12489             HOLD: "HOLD",
12490             /**
12491              * User is wrap-up/work mode.  This mode is typically configured to time out, after which the user becomes NOT_READY.
12492              */
12493             WORK: "WORK",
12494             /**
12495              * This is the same as WORK, except that after time out user becomes READY.
12496              */
12497             WORK_READY: "WORK_READY",
12498             /**
12499              * @class Possible User state values.
12500              * @constructs
12501              */
12502             _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
12503           
12504         };
12505     
12506     User.WorkMode = { /** @lends finesse.restservices.User.WorkMode.prototype */
12507         /**
12508          * Mobile agent is connected (dialed) for each incoming call received.
12509          */
12510         CALL_BY_CALL: "CALL_BY_CALL",
12511         /**
12512          * Mobile agent is connected (dialed) at login.
12513          */
12514         NAILED_CONNECTION: "NAILED_CONNECTION",
12515         /**
12516          * @class Possible Mobile Agent Work Mode Types.
12517          * @constructs
12518          */
12519         _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
12520         
12521     };
12522 
12523     User.WrapUpMode = { /** @lends finesse.restservices.User.WrapUpMode.prototype */
12524         /**
12525          * Agent must go into wrap-up when call ends
12526          */
12527         REQUIRED: "REQUIRED",
12528         /**
12529          * Agent must go into wrap-up when call ends and must enter wrap-up data
12530          */
12531         REQUIRED_WITH_WRAP_UP_DATA: "REQUIRED_WITH_WRAP_UP_DATA",
12532         /**
12533          * Agent can choose to go into wrap-up on a call-by-call basis when the call ends
12534          */
12535         OPTIONAL: "OPTIONAL",
12536         /**
12537          * Agent is not allowed to go into wrap-up when call ends.
12538          */
12539         NOT_ALLOWED: "NOT_ALLOWED",
12540         /**
12541          * @class Possible Wrap-up Mode Types.
12542          * @constructs
12543          */
12544         _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
12545         
12546     };
12547 
12548     window.finesse = window.finesse || {};
12549     window.finesse.restservices = window.finesse.restservices || {};
12550     window.finesse.restservices.User = User;
12551         
12552     return User;
12553 });
12554 
12555 /**
12556  * JavaScript representation of the Finesse Users collection
12557  * object which contains a list of Users objects.
12558  *
12559  * @requires finesse.clientservices.ClientServices
12560  * @requires Class
12561  * @requires finesse.FinesseBase
12562  * @requires finesse.restservices.RestBase
12563  * @requires finesse.restservices.RestCollectionBase
12564  * @requires finesse.restservices.User
12565  */
12566 
12567 /** @private */
12568 define('restservices/Users',[
12569     'restservices/RestCollectionBase',
12570     'restservices/RestBase',
12571     'restservices/User'
12572 ],
12573 function (RestCollectionBase, RestBase, User) {
12574 
12575     var Users = RestCollectionBase.extend(/** @lends finesse.restservices.Users.prototype */{
12576 
12577     /**
12578      * @class
12579      * JavaScript representation of a Users collection object. 
12580      * While there is no method provided to retrieve all Users, this collection is
12581      * used to return the Users in a supervised Team.
12582      * @augments finesse.restservices.RestCollectionBase
12583      * @constructs
12584      * @see finesse.restservices.Team
12585      * @see finesse.restservices.User
12586      * @see finesse.restservices.User#getSupervisedTeams
12587      * @example
12588      *  // Note: The following method gets an Array of Teams, not a Collection.
12589      *  _teams = _user.getSupervisedTeams();
12590      *  if (_teams.length > 0) {
12591      *      _team0Users = _teams[0].getUsers();
12592      *  }
12593      */
12594     _fakeConstuctor: function () {
12595         /* This is here to hide the real init constructor from the public docs */
12596     },
12597         
12598     /**
12599      * @private
12600      * JavaScript representation of the Finesse Users collection
12601      * object which contains a list of Users objects.
12602      *
12603 	 * @param {Object} options
12604 	 *     An object with the following properties:<ul>
12605      *         <li><b>id:</b> The id of the object being constructed</li>
12606      *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
12607      *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
12608      *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
12609      *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
12610      *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
12611      *             <li><b>status:</b> {Number} The HTTP status code returned</li>
12612      *             <li><b>content:</b> {String} Raw string of response</li>
12613      *             <li><b>object:</b> {Object} Parsed object of response</li>
12614      *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
12615      *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
12616      *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
12617      *             </ul></li>
12618      *         </ul></li>
12619      *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
12620      **/
12621 	init: function (options) {
12622 		this._super(options);
12623 	},
12624 
12625 	/**
12626      * @private
12627 	 * Gets the REST class for the current object - this is the Users class.
12628 	 */
12629 	getRestClass: function () {
12630 	    return Users;
12631 	},
12632 
12633 	/**
12634      * @private
12635 	 * Gets the REST class for the objects that make up the collection. - this
12636 	 * is the User class.
12637 	 */
12638 	getRestItemClass: function () {
12639 		return User;
12640 	},
12641 
12642 	/**
12643      * @private
12644 	 * Gets the REST type for the current object - this is a "Users".
12645 	 */
12646 	getRestType: function () {
12647 	    return "Users";
12648 	},
12649 
12650 	/**
12651      * @private
12652 	 * Gets the REST type for the objects that make up the collection - this is "User".
12653 	 */
12654 	getRestItemType: function () {
12655 	    return "User";
12656 	},
12657 
12658 	/**
12659      * @private
12660      * Gets the node path for the current object - this is the team Users node
12661      * @returns {String} The node path
12662      */
12663     getXMPPNodePath: function () {
12664 		return this.getRestUrl();
12665     },
12666 
12667     /**
12668      * @private
12669      * Overloading _doGET to reroute the GET to /Team/id from /Team/id/Users
12670      * This needs to be done because the GET /Team/id/Users API is missing
12671      * @returns {Users} This Users (collection) object to allow cascading
12672      */
12673     _doGET: function (handlers) {
12674         var _this = this;
12675         handlers = handlers || {};
12676         // Only do this for /Team/id/Users
12677         if (this._restObj && this._restObj.getRestType() === "Team") {
12678             this._restObj._doGET({
12679                 success: function (rspObj) {
12680                     // Making sure the response was a valid Team
12681                     if (_this._restObj._validate(rspObj.object)) {
12682                         // Shimmying the response to look like a Users collection by extracting it from the Team response
12683                         rspObj.object[_this.getRestType()] = rspObj.object[_this._restObj.getRestType()][_this.getRestType().toLowerCase()];
12684                         handlers.success(rspObj);
12685                     } else {
12686                         handlers.error(rspObj);
12687                     }
12688                 },
12689                 error: handlers.error
12690             });
12691             return this; // Allow cascading
12692         } else {
12693             return this._super(handlers);
12694         }
12695     },
12696 
12697 	/**
12698      * @private
12699      * Override default to indicates that the collection doesn't support making
12700 	 * requests.
12701 	 */
12702 	supportsRequests: false,
12703 
12704     /**
12705      * @private
12706      * Indicates that this collection handles the subscription for its items
12707      */
12708     handlesItemSubscription: true,
12709 	
12710     /**
12711      * @private
12712      * Override default to indicate that we need to subscribe explicitly
12713      */
12714     explicitSubscription: true
12715     
12716 	});
12717 
12718     window.finesse = window.finesse || {};
12719     window.finesse.restservices = window.finesse.restservices || {};
12720     window.finesse.restservices.Users = Users;
12721 
12722     return Users;
12723 });
12724 
12725 /**
12726  * JavaScript representation of the Finesse Team Not Ready Reason Code Assignment object.
12727  *
12728  * @requires finesse.clientservices.ClientServices
12729  * @requires Class
12730  * @requires finesse.FinesseBase
12731  * @requires finesse.restservices.RestBase
12732  */
12733 
12734 /** @private */
12735 define('restservices/TeamNotReadyReasonCode',['restservices/RestBase'], function (RestBase) {
12736     
12737     var TeamNotReadyReasonCode = RestBase.extend( {
12738 
12739         /**
12740          * @class
12741          * JavaScript representation of a Team Not Ready ReasonCode object. Also exposes
12742          * methods to operate on the object against the server.
12743          *
12744          * @param {Object} options
12745          *     An object with the following properties:<ul>
12746          *         <li><b>id:</b> The id of the object being constructed</li>
12747          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
12748          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
12749          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
12750          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
12751          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
12752          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
12753          *             <li><b>content:</b> {String} Raw string of response</li>
12754          *             <li><b>object:</b> {Object} Parsed object of response</li>
12755          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
12756          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
12757          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
12758          *             </ul></li>
12759          *         </ul></li>
12760          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
12761          * @constructs
12762          **/
12763         init: function (options) {
12764             this._super(options);
12765         },
12766     
12767         /**
12768          * @private
12769          * Gets the REST class for the current object - this is the TeamNotReadyReasonCode class.
12770          * @returns {Object} The TeamNotReadyReasonCode class.
12771          */
12772         getRestClass: function () {
12773             return TeamNotReadyReasonCode;
12774         },
12775     
12776         /**
12777          * @private
12778          * Gets the REST type for the current object - this is a "ReasonCode".
12779          * @returns {String} The ReasonCode string.
12780          */
12781         getRestType: function () {
12782             return "ReasonCode";
12783         },
12784     
12785         /**
12786          * @private
12787          * Override default to indicate that this object doesn't support making
12788          * requests.
12789          */
12790         supportsRequests: false,
12791     
12792         /**
12793          * @private
12794          * Override default to indicate that this object doesn't support subscriptions.
12795          */
12796         supportsSubscriptions: false,
12797     
12798         /**
12799          * Getter for the category.
12800          * @returns {String} The category.
12801          */
12802         getCategory: function () {
12803             this.isLoaded();
12804             return this.getData().category;
12805         },
12806     
12807         /**
12808          * Getter for the code.
12809          * @returns {String} The code.
12810          */
12811         getCode: function () {
12812             this.isLoaded();
12813             return this.getData().code;
12814         },
12815     
12816         /**
12817          * Getter for the label.
12818          * @returns {String} The label.
12819          */
12820         getLabel: function () {
12821             this.isLoaded();
12822             return this.getData().label;
12823         },
12824     
12825         /**
12826          * Getter for the forAll value.
12827          * @returns {String} The forAll.
12828          */
12829         getForAll: function () {
12830             this.isLoaded();
12831             return this.getData().forAll;
12832         },
12833     
12834         /**
12835          * Getter for the Uri value.
12836          * @returns {String} The Uri.
12837          */
12838         getUri: function () {
12839             this.isLoaded();
12840             return this.getData().uri;
12841         }
12842 
12843     });
12844     
12845     window.finesse = window.finesse || {};
12846     window.finesse.restservices = window.finesse.restservices || {};
12847     window.finesse.restservices.TeamNotReadyReasonCode = TeamNotReadyReasonCode;
12848         
12849     return TeamNotReadyReasonCode;
12850 });
12851 
12852 /**
12853 * JavaScript representation of the Finesse TeamNotReadyReasonCodes collection
12854 * object which contains a list of TeamNotReadyReasonCode objects.
12855  *
12856  * @requires finesse.clientservices.ClientServices
12857  * @requires Class
12858  * @requires finesse.FinesseBase
12859  * @requires finesse.restservices.RestBase
12860  * @requires finesse.restservices.Dialog
12861  * @requires finesse.restservices.RestCollectionBase
12862  */
12863 
12864 /** @private */
12865 define('restservices/TeamNotReadyReasonCodes',[
12866     'restservices/RestCollectionBase',
12867     'restservices/RestBase',
12868     'restservices/TeamNotReadyReasonCode'
12869 ],
12870 function (RestCollectionBase, RestBase, TeamNotReadyReasonCode) {
12871 
12872     var TeamNotReadyReasonCodes = RestCollectionBase.extend( {
12873 
12874       /**
12875        * @class
12876        * JavaScript representation of a TeamNotReadyReasonCodes collection object. Also exposes
12877        * methods to operate on the object against the server.
12878        *
12879        * @param {Object} options
12880        *     An object with the following properties:<ul>
12881        *         <li><b>id:</b> The id of the object being constructed</li>
12882        *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
12883        *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
12884        *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
12885        *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
12886        *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
12887        *             <li><b>status:</b> {Number} The HTTP status code returned</li>
12888        *             <li><b>content:</b> {String} Raw string of response</li>
12889        *             <li><b>object:</b> {Object} Parsed object of response</li>
12890        *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
12891        *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
12892        *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
12893        *             </ul></li>
12894        *         </ul></li>
12895        *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
12896        * @augments finesse.restservices.RestCollectionBase
12897        * @constructs
12898        **/
12899       init: function (options) {
12900           this._super(options);
12901       },
12902     
12903       /**
12904        * @private
12905        * Gets the REST class for the current object - this is the TeamNotReadyReasonCodes class.
12906        */
12907       getRestClass: function () {
12908           return TeamNotReadyReasonCodes;
12909       },
12910     
12911       /**
12912        * @private
12913        * Gets the REST class for the objects that make up the collection. - this
12914        * is the TeamNotReadyReasonCode class.
12915        */
12916       getRestItemClass: function () {
12917           return TeamNotReadyReasonCode;
12918       },
12919     
12920       /**
12921        * @private
12922        * Gets the REST type for the current object - this is a "ReasonCodes".
12923        */
12924       getRestType: function () {
12925           return "ReasonCodes";
12926       },
12927     
12928       /**
12929        * @private
12930        * Overrides the parent class.  Returns the url for the NotReadyReasonCodes resource
12931        */
12932       getRestUrl: function () {
12933           // return ("/finesse/api/" + this.getRestType() + "?category=NOT_READY");
12934           var restObj = this._restObj,
12935               restUrl = "";
12936           //Prepend the base REST object if one was provided.
12937           //Otherwise prepend with the default webapp name.
12938           if (restObj instanceof RestBase) {
12939               restUrl += restObj.getRestUrl();
12940           }
12941           else {
12942               restUrl += "/finesse/api";
12943           }
12944           //Append the REST type.
12945           restUrl += "/ReasonCodes?category=NOT_READY";
12946           //Append ID if it is not undefined, null, or empty.
12947           if (this._id) {
12948               restUrl += "/" + this._id;
12949           }
12950           return restUrl;
12951       },
12952     
12953       /**
12954        * @private
12955        * Gets the REST type for the objects that make up the collection - this is "ReasonCode".
12956        */
12957       getRestItemType: function () {
12958           return "ReasonCode";
12959       },
12960     
12961       /**
12962        * @private
12963        * Override default to indicates that the collection supports making
12964        * requests.
12965        */
12966       supportsRequests: true,
12967     
12968       /**
12969        * @private
12970        * Override default to indicate that this object doesn't support subscriptions.
12971        */
12972       supportsRestItemSubscriptions: false,
12973     
12974       /**
12975        * @private
12976        * Retrieve the Not Ready Reason Codes.
12977        *
12978        * @returns {TeamNotReadyReasonCodes}
12979        *     This TeamNotReadyReasonCodes object to allow cascading.
12980        */
12981       get: function () {
12982           // set loaded to false so it will rebuild the collection after the get
12983           this._loaded = false;
12984           // reset collection
12985           this._collection = {};
12986           // perform get
12987           this._synchronize();
12988           return this;
12989       },
12990     
12991       /**
12992        * @private
12993        * Set up the PutSuccessHandler for TeamNotReadyReasonCodes
12994        * @param {Object} reasonCodes
12995        * @param {String} contentBody
12996        * @param successHandler    
12997        * @return {function}
12998        */
12999       createPutSuccessHandler: function (reasonCodes, contentBody, successHandler) {
13000           return function (rsp) {
13001               // Update internal structure based on response. Here we
13002               // inject the contentBody from the PUT request into the
13003               // rsp.object element to mimic a GET as a way to take
13004               // advantage of the existing _processResponse method.
13005               rsp.object = contentBody;
13006               reasonCodes._processResponse(rsp);
13007     
13008               //Remove the injected contentBody object before cascading response
13009               rsp.object = {};
13010     
13011               //cascade response back to consumer's response handler
13012               successHandler(rsp);
13013           };
13014       },
13015     
13016       /**
13017        * @private
13018        * Perform the REST API PUT call to update the reason code assignments for the team
13019        * @param {string[]} newValues
13020        * @param handlers     
13021        */
13022       update: function (newValues, handlers) {
13023           this.isLoaded();
13024           var contentBody = {}, contentBodyInner = [], i, innerObject = {};
13025     
13026           contentBody[this.getRestType()] = {
13027           };
13028     
13029           for (i in newValues) {
13030               if (newValues.hasOwnProperty(i)) {
13031                   innerObject = {
13032                       "uri": newValues[i]
13033                   };
13034                   contentBodyInner.push(innerObject);
13035               }
13036           }
13037     
13038           contentBody[this.getRestType()] = {
13039               "ReasonCode" : contentBodyInner
13040           };
13041     
13042           // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
13043           handlers = handlers || {};
13044     
13045           this.restRequest(this.getRestUrl(), {
13046               method: 'PUT',
13047               success: this.createPutSuccessHandler(this, contentBody, handlers.success),
13048               error: handlers.error,
13049               content: contentBody
13050           });
13051     
13052           return this; // Allow cascading
13053       }
13054   });
13055   
13056     window.finesse = window.finesse || {};
13057     window.finesse.restservices = window.finesse.restservices || {};
13058     window.finesse.restservices.TeamNotReadyReasonCodes = TeamNotReadyReasonCodes;
13059     
13060   return TeamNotReadyReasonCodes;
13061 });
13062 
13063 /**
13064  * JavaScript representation of the Finesse Team Wrap Up Reason object.
13065  *
13066  * @requires finesse.clientservices.ClientServices
13067  * @requires Class
13068  * @requires finesse.FinesseBase
13069  * @requires finesse.restservices.RestBase
13070  */
13071 /** @private */
13072 define('restservices/TeamWrapUpReason',['restservices/RestBase'], function (RestBase) {
13073 
13074     var TeamWrapUpReason = RestBase.extend({
13075 
13076     /**
13077      * @class
13078      * JavaScript representation of a TeamWrapUpReason object. Also exposes
13079      * methods to operate on the object against the server.
13080      *
13081      * @param {Object} options
13082      *     An object with the following properties:<ul>
13083      *         <li><b>id:</b> The id of the object being constructed</li>
13084      *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
13085      *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
13086      *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
13087      *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
13088      *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
13089      *             <li><b>status:</b> {Number} The HTTP status code returned</li>
13090      *             <li><b>content:</b> {String} Raw string of response</li>
13091      *             <li><b>object:</b> {Object} Parsed object of response</li>
13092      *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
13093      *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
13094      *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
13095      *             </ul></li>
13096      *         </ul></li>
13097      *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
13098      * @constructs
13099      **/
13100     init: function (options) {
13101         this._super(options);
13102     },
13103 
13104     /**
13105      * @private
13106      * Gets the REST class for the current object - this is the TeamWrapUpReason class.
13107      * @returns {Object} The TeamWrapUpReason class.
13108      */
13109     getRestClass: function () {
13110         return TeamWrapUpReason;
13111     },
13112 
13113     /**
13114      * @private
13115      * Gets the REST type for the current object - this is a "WrapUpReason".
13116      * @returns {String} The WrapUpReason string.
13117      */
13118     getRestType: function () {
13119         return "WrapUpReason";
13120     },
13121 
13122     /**
13123      * @private
13124      * Override default to indicate that this object doesn't support making
13125      * requests.
13126      */
13127     supportsRequests: false,
13128 
13129     /**
13130      * @private
13131      * Override default to indicate that this object doesn't support subscriptions.
13132      */
13133     supportsSubscriptions: false,
13134 
13135     /**
13136      * Getter for the label.
13137      * @returns {String} The label.
13138      */
13139     getLabel: function () {
13140         this.isLoaded();
13141         return this.getData().label;
13142     },
13143 
13144     /**
13145      * @private
13146      * Getter for the forAll value.
13147      * @returns {Boolean} True if global
13148      */
13149     getForAll: function () {
13150         this.isLoaded();
13151         return this.getData().forAll;
13152     },
13153 
13154     /**
13155      * @private
13156      * Getter for the Uri value.
13157      * @returns {String} The Uri.
13158      */
13159     getUri: function () {
13160         this.isLoaded();
13161         return this.getData().uri;
13162     }
13163 	});
13164 
13165     window.finesse = window.finesse || {};
13166     window.finesse.restservices = window.finesse.restservices || {};
13167     window.finesse.restservices.TeamWrapUpReason = TeamWrapUpReason;
13168 
13169     return TeamWrapUpReason;
13170 });
13171 
13172 /**
13173 * JavaScript representation of the Finesse Team Wrap-Up Reasons collection
13174 * object which contains a list of Wrap-Up Reasons objects.
13175  *
13176  * @requires finesse.clientservices.ClientServices
13177  * @requires Class
13178  * @requires finesse.FinesseBase
13179  * @requires finesse.restservices.RestBase
13180  * @requires finesse.restservices.Dialog
13181  * @requires finesse.restservices.RestCollectionBase
13182  */
13183 /** @private */
13184 define('restservices/TeamWrapUpReasons',[
13185     'restservices/RestCollectionBase',
13186     'restservices/RestBase',
13187     'restservices/TeamWrapUpReason'
13188 ],
13189 function (RestCollectionBase, RestBase, TeamWrapUpReason) {
13190 
13191     var TeamWrapUpReasons = RestCollectionBase.extend({
13192 
13193     /**
13194      * @class
13195      * JavaScript representation of a TeamWrapUpReasons collection object. Also exposes
13196      * methods to operate on the object against the server.
13197      *
13198      * @param {Object} options
13199      *     An object with the following properties:<ul>
13200      *         <li><b>id:</b> The id of the object being constructed</li>
13201      *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
13202      *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
13203      *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
13204      *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
13205      *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
13206      *             <li><b>status:</b> {Number} The HTTP status code returned</li>
13207      *             <li><b>content:</b> {String} Raw string of response</li>
13208      *             <li><b>object:</b> {Object} Parsed object of response</li>
13209      *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
13210      *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
13211      *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
13212      *             </ul></li>
13213      *         </ul></li>
13214      *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
13215      * @constructs
13216      **/
13217     init: function (options) {
13218         this._super(options);
13219     },
13220 
13221     /**
13222      * @private
13223      * Gets the REST class for the current object - this is the TeamWrapUpReasons class.
13224      */
13225     getRestClass: function () {
13226         return TeamWrapUpReasons;
13227     },
13228 
13229     /**
13230      * @private
13231      * Gets the REST class for the objects that make up the collection. - this
13232      * is the TeamWrapUpReason class.
13233      */
13234     getRestItemClass: function () {
13235         return TeamWrapUpReason;
13236     },
13237 
13238     /**
13239      * @private
13240      * Gets the REST type for the current object - this is a "WrapUpReasons".
13241      */
13242     getRestType: function () {
13243         return "WrapUpReasons";
13244     },
13245 
13246     /**
13247      * @private
13248      * Gets the REST type for the objects that make up the collection - this is "WrapUpReason".
13249      */
13250     getRestItemType: function () {
13251         return "WrapUpReason";
13252     },
13253 
13254     /**
13255      * @private
13256      * Override default to indicates that the collection supports making
13257      * requests.
13258      */
13259     supportsRequests: true,
13260 
13261     /**
13262      * @private
13263      * Override default to indicate that this object doesn't support subscriptions.
13264      */
13265     supportsRestItemSubscriptions: false,
13266 
13267     /**
13268      * Retrieve the Team Wrap Up Reasons.
13269      *
13270      * @returns {finesse.restservices.TeamWrapUpReasons}
13271      *     This TeamWrapUpReasons object to allow cascading.
13272      */
13273     get: function () {
13274         // set loaded to false so it will rebuild the collection after the get
13275         this._loaded = false;
13276         // reset collection
13277         this._collection = {};
13278         // perform get
13279         this._synchronize();
13280         return this;
13281     },
13282 
13283     /**
13284      * Set up the PutSuccessHandler for TeamWrapUpReasons
13285      * @param {Object} wrapUpReasons
13286      * @param {Object} contentBody
13287      * @param successHandler
13288      * @returns response
13289      */
13290     createPutSuccessHandler: function (wrapUpReasons, contentBody, successHandler) {
13291         return function (rsp) {
13292             // Update internal structure based on response. Here we
13293             // inject the contentBody from the PUT request into the
13294             // rsp.object element to mimic a GET as a way to take
13295             // advantage of the existing _processResponse method.
13296             rsp.object = contentBody;
13297             
13298             wrapUpReasons._processResponse(rsp);
13299 
13300             //Remove the injected contentBody object before cascading response
13301             rsp.object = {};
13302 
13303             //cascade response back to consumer's response handler
13304             successHandler(rsp);
13305         };
13306     },
13307 
13308     /**    
13309      * Perform the REST API PUT call to update the reason code assignments for the team
13310      * @param {String Array} newValues
13311      * @param handlers
13312      * @returns {Object} this
13313      */
13314     update: function (newValues, handlers) {
13315         this.isLoaded();
13316         var contentBody = {}, contentBodyInner = [], i, innerObject = {};
13317 
13318         contentBody[this.getRestType()] = {
13319         };
13320 
13321         for (i in newValues) {
13322             if (newValues.hasOwnProperty(i)) {
13323                 innerObject = {
13324                     "uri": newValues[i]
13325                 };
13326                 contentBodyInner.push(innerObject);
13327             }
13328         }
13329 
13330         contentBody[this.getRestType()] = {
13331             "WrapUpReason" : contentBodyInner
13332         };
13333 
13334         // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
13335         handlers = handlers || {};
13336 
13337         this.restRequest(this.getRestUrl(), {
13338             method: 'PUT',
13339             success: this.createPutSuccessHandler(this, contentBody, handlers.success),
13340             error: handlers.error,
13341             content: contentBody
13342         });
13343 
13344         return this; // Allow cascading
13345     }
13346 	});
13347 
13348     window.finesse = window.finesse || {};
13349     window.finesse.restservices = window.finesse.restservices || {};
13350     window.finesse.restservices.TeamWrapUpReasons = TeamWrapUpReasons;
13351 
13352     return TeamWrapUpReasons;
13353 });
13354 
13355 /**
13356  * JavaScript representation of a TeamSignOutReasonCode.
13357  *
13358  * @requires finesse.clientservices.ClientServices
13359  * @requires Class
13360  * @requires finesse.FinesseBase
13361  * @requires finesse.restservices.RestBase
13362  */
13363 
13364 /** @private */
13365 define('restservices/TeamSignOutReasonCode',['restservices/RestBase'], function (RestBase) {
13366     var TeamSignOutReasonCode = RestBase.extend({
13367 
13368         /**
13369          * @class
13370          * JavaScript representation of a TeamSignOutReasonCode object. Also exposes
13371          * methods to operate on the object against the server.
13372          *
13373          * @param {Object} options
13374          *     An object with the following properties:<ul>
13375          *         <li><b>id:</b> The id of the object being constructed</li>
13376          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
13377          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
13378          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
13379          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
13380          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
13381          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
13382          *             <li><b>content:</b> {String} Raw string of response</li>
13383          *             <li><b>object:</b> {Object} Parsed object of response</li>
13384          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
13385          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
13386          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
13387          *             </ul></li>
13388          *         </ul></li>
13389          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
13390          * @constructs
13391          * @ignore
13392          **/
13393         init: function (options) {
13394             this._super(options);
13395         },
13396 
13397         /**
13398          * @private
13399          * Gets the REST class for the current object - this is the TeamSignOutReasonCode class.
13400          * @returns {Object} The TeamSignOutReasonCode class.
13401          */
13402         getRestClass: function () {
13403             return TeamSignOutReasonCode;
13404         },
13405 
13406         /**
13407          * @private
13408          * Gets the REST type for the current object - this is a "ReasonCode".
13409          * @returns {String} The ReasonCode string.
13410          */
13411         getRestType: function () {
13412             return "ReasonCode";
13413         },
13414 
13415         /**
13416          * @private
13417          * Override default to indicate that this object doesn't support making
13418          * requests.
13419          */
13420         supportsRequests: false,
13421 
13422         /**
13423          * @private
13424          * Override default to indicate that this object doesn't support subscriptions.
13425          */
13426         supportsSubscriptions: false,
13427 
13428         /**
13429          * Getter for the category.
13430          * @returns {String} The category.
13431          */
13432         getCategory: function () {
13433             this.isLoaded();
13434             return this.getData().category;
13435         },
13436 
13437         /**
13438          * Getter for the code.
13439          * @returns {String} The code.
13440          */
13441         getCode: function () {
13442             this.isLoaded();
13443             return this.getData().code;
13444         },
13445 
13446         /**
13447          * Getter for the label.
13448          * @returns {String} The label.
13449          */
13450         getLabel: function () {
13451             this.isLoaded();
13452             return this.getData().label;
13453         },
13454 
13455         /**
13456          * Getter for the forAll value.
13457          * @returns {String} The forAll.
13458          */
13459         getForAll: function () {
13460             this.isLoaded();
13461             return this.getData().forAll;
13462         },
13463 
13464         /**
13465          * Getter for the Uri value.
13466          * @returns {String} The Uri.
13467          */
13468         getUri: function () {
13469             this.isLoaded();
13470             return this.getData().uri;
13471         }
13472 
13473     });
13474 
13475     window.finesse = window.finesse || {};
13476     window.finesse.restservices = window.finesse.restservices || {};
13477     window.finesse.restservices.TeamSignOutReasonCode = TeamSignOutReasonCode;
13478     
13479     return TeamSignOutReasonCode;
13480 });
13481 
13482 /**
13483 * JavaScript representation of the TeamSignOutReasonCodes collection
13484 * object which contains a list of TeamSignOutReasonCode objects.
13485  *
13486  * @requires finesse.clientservices.ClientServices
13487  * @requires Class
13488  * @requires finesse.FinesseBase
13489  * @requires finesse.restservices.RestBase
13490  * @requires finesse.restservices.Dialog
13491  * @requires finesse.restservices.RestCollectionBase
13492  */
13493 
13494 /** @private */
13495 define('restservices/TeamSignOutReasonCodes',[
13496     'restservices/RestCollectionBase',
13497     'restservices/RestBase',
13498     'restservices/TeamSignOutReasonCode'
13499 ],
13500 function (RestCollectionBase, RestBase, TeamSignOutReasonCode) {
13501     
13502     var TeamSignOutReasonCodes = RestCollectionBase.extend({
13503         /**
13504          * @class
13505          * JavaScript representation of a TeamSignOutReasonCodes collection object. Also exposes
13506          * methods to operate on the object against the server.
13507          *
13508          * @param {Object} options
13509          *     An object with the following properties:<ul>
13510          *         <li><b>id:</b> The id of the object being constructed</li>
13511          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
13512          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
13513          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
13514          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
13515          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
13516          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
13517          *             <li><b>content:</b> {String} Raw string of response</li>
13518          *             <li><b>object:</b> {Object} Parsed object of response</li>
13519          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
13520          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
13521          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
13522          *             </ul></li>
13523          *         </ul></li>
13524          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
13525          * @constructs
13526          **/
13527         init: function (options) {
13528             this._super(options);
13529         },
13530 
13531         /**
13532          * @private
13533          * Gets the REST class for the current object - this is the TeamSignOutReasonCodes class.
13534          */
13535         getRestClass: function () {
13536             return TeamSignOutReasonCodes;
13537         },
13538 
13539         /**
13540          * @private
13541          * Gets the REST class for the objects that make up the collection. - this
13542          * is the TeamSignOutReasonCode class.
13543          */
13544         getRestItemClass: function () {
13545             return TeamSignOutReasonCode;
13546         },
13547 
13548         /**
13549          * @private
13550          * Gets the REST type for the current object - this is a "ReasonCodes".
13551          */
13552         getRestType: function () {
13553             return "ReasonCodes";
13554         },
13555 
13556         /**
13557          * Overrides the parent class.  Returns the url for the SignOutReasonCodes resource
13558          */
13559         getRestUrl: function () {
13560             var restObj = this._restObj, restUrl = "";
13561 
13562             //Prepend the base REST object if one was provided.
13563             //Otherwise prepend with the default webapp name.
13564             if (restObj instanceof RestBase) {
13565                 restUrl += restObj.getRestUrl();
13566             } else {
13567                 restUrl += "/finesse/api";
13568             }
13569             //Append the REST type.
13570             restUrl += "/ReasonCodes?category=LOGOUT";
13571             //Append ID if it is not undefined, null, or empty.
13572             if (this._id) {
13573                 restUrl += "/" + this._id;
13574             }
13575             return restUrl;
13576         },
13577 
13578         /**
13579          * @private
13580          * Gets the REST type for the objects that make up the collection - this is "ReasonCode".
13581          */
13582         getRestItemType: function () {
13583             return "ReasonCode";
13584         },
13585 
13586         /**
13587          * @private
13588          * Override default to indicates that the collection supports making requests.
13589          */
13590         supportsRequests: true,
13591 
13592         /**
13593          * @private
13594          * Override default to indicates that the collection does not subscribe to its objects.
13595          */
13596         supportsRestItemSubscriptions: false,
13597 
13598         /**
13599          * Retrieve the Sign Out Reason Codes.
13600          *
13601          * @returns {finesse.restservices.TeamSignOutReasonCodes}
13602          *     This TeamSignOutReasonCodes object to allow cascading.
13603          */
13604         get: function () {
13605             // set loaded to false so it will rebuild the collection after the get
13606             this._loaded = false;
13607             // reset collection
13608             this._collection = {};
13609             // perform get
13610             this._synchronize();
13611             return this;
13612         },
13613 
13614         /* We only use PUT and GET on Reason Code team assignments
13615          * @param {Object} contact
13616          * @param {Object} contentBody
13617          * @param {Function} successHandler
13618          */
13619         createPutSuccessHandler: function (contact, 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.object = contentBody;
13626                 contact._processResponse(rsp);
13627 
13628                 //Remove the injected contentBody object before cascading response
13629                 rsp.object = {};
13630 
13631                 //cascade response back to consumer's response handler
13632                 successHandler(rsp);
13633             };
13634         },
13635 
13636         /**
13637          * Update - This should be all that is needed.
13638          * @param {Object} newValues
13639          * @param {Object} handlers
13640          * @returns {finesse.restservices.TeamSignOutReasonCodes}
13641          *     This TeamSignOutReasonCodes object to allow cascading.
13642          */
13643         update: function (newValues, handlers) {
13644             this.isLoaded();
13645             var contentBody = {}, contentBodyInner = [], i, innerObject = {};
13646 
13647             contentBody[this.getRestType()] = {
13648             };
13649 
13650             for (i in newValues) {
13651                 if (newValues.hasOwnProperty(i)) {
13652                     innerObject = {
13653                         "uri": newValues[i]
13654                     };
13655                     contentBodyInner.push(innerObject);
13656                 }
13657             }
13658 
13659             contentBody[this.getRestType()] = {
13660                 "ReasonCode" : contentBodyInner
13661             };
13662 
13663             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
13664             handlers = handlers || {};
13665 
13666             this.restRequest(this.getRestUrl(), {
13667                 method: 'PUT',
13668                 success: this.createPutSuccessHandler(this, contentBody, handlers.success),
13669                 error: handlers.error,
13670                 content: contentBody
13671             });
13672 
13673             return this; // Allow cascading
13674         }
13675 
13676     });
13677     
13678     window.finesse = window.finesse || {};
13679     window.finesse.restservices = window.finesse.restservices || {};
13680     window.finesse.restservices.TeamSignOutReasonCodes = TeamSignOutReasonCodes;
13681     
13682     return TeamSignOutReasonCodes;
13683 });
13684 
13685 /**
13686  * JavaScript representation of the Finesse PhoneBook Assignment object.
13687  *
13688  * @requires finesse.clientservices.ClientServices
13689  * @requires Class
13690  * @requires finesse.FinesseBase
13691  * @requires finesse.restservices.RestBase
13692  */
13693 
13694 /**
13695  * The following comment prevents JSLint errors concerning undefined global variables.
13696  * It tells JSLint that these identifiers are defined elsewhere.
13697  */
13698 /*jslint bitwise:true, browser:true, nomen:true, regexp:true, sloppy:true, white:true */
13699 
13700 /** The following comment is to prevent jslint errors about 
13701  * using variables before they are defined.
13702  */
13703 /*global $, jQuery, Handlebars, dojox, dojo, finesse */
13704 
13705 /** @private */
13706 define('restservices/TeamPhoneBook',['restservices/RestBase'], function (RestBase) {
13707     var TeamPhoneBook = RestBase.extend({
13708 
13709         /**
13710          * @class
13711          * JavaScript representation of a PhoneBook object. Also exposes
13712          * methods to operate on the object against the server.
13713          *
13714          * @param {Object} options
13715          *     An object with the following properties:<ul>
13716          *         <li><b>id:</b> The id of the object being constructed</li>
13717          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
13718          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
13719          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
13720          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
13721          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
13722          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
13723          *             <li><b>content:</b> {String} Raw string of response</li>
13724          *             <li><b>object:</b> {Object} Parsed object of response</li>
13725          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
13726          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
13727          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
13728          *             </ul></li>
13729          *         </ul></li>
13730          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
13731          * @constructs
13732          **/
13733         init: function (options) {
13734             this._super(options);
13735         },
13736 
13737         /**
13738          * @private
13739          * Gets the REST class for the current object - this is the PhoneBooks class.
13740          * @returns {Object} The PhoneBooks class.
13741          */
13742         getRestClass: function () {
13743             return TeamPhoneBook;
13744         },
13745 
13746         /**
13747          * @private
13748          * Gets the REST type for the current object - this is a "PhoneBook".
13749          * @returns {String} The PhoneBook string.
13750          */
13751         getRestType: function () {
13752             return "PhoneBook";
13753         },
13754 
13755         /**
13756          * @private
13757          * Override default to indicate that this object doesn't support making
13758          * requests.
13759          */
13760         supportsRequests: false,
13761 
13762         /**
13763          * @private
13764          * Override default to indicate that this object doesn't support subscriptions.
13765          */
13766         supportsSubscriptions: false,
13767 
13768         /**
13769          * Getter for the name.
13770          * @returns {String} The name.
13771          */
13772         getName: function () {
13773             this.isLoaded();
13774             return this.getData().name;
13775         },
13776 
13777         /**
13778          * Getter for the Uri value.
13779          * @returns {String} The Uri.
13780          */
13781         getUri: function () {
13782             this.isLoaded();
13783             return this.getData().uri;
13784         }
13785 
13786     });
13787 
13788     window.finesse = window.finesse || {};
13789     window.finesse.restservices = window.finesse.restservices || {};
13790     window.finesse.restservices.TeamPhoneBook = TeamPhoneBook;
13791     
13792     return TeamPhoneBook;
13793 });
13794 
13795 /**
13796 * JavaScript representation of the Finesse PhoneBook Assignments collection
13797 * object which contains a list of Not Ready Reason Codes objects.
13798  *
13799  * @requires finesse.clientservices.ClientServices
13800  * @requires Class
13801  * @requires finesse.FinesseBase
13802  * @requires finesse.restservices.RestBase
13803  * @requires finesse.restservices.Dialog
13804  * @requires finesse.restservices.RestCollectionBase
13805  */
13806 
13807 /**
13808  * The following comment prevents JSLint errors concerning undefined global variables.
13809  * It tells JSLint that these identifiers are defined elsewhere.
13810  */
13811 /*jslint bitwise:true, browser:true, nomen:true, regexp:true, sloppy:true, white:true */
13812 
13813 /** The following comment is to prevent jslint errors about 
13814  * using variables before they are defined.
13815  */
13816 /*global $, jQuery, Handlebars, dojox, dojo, finesse */
13817 
13818 /** @private */
13819 define('restservices/TeamPhoneBooks',[
13820     'restservices/RestCollectionBase',
13821     'restservices/RestBase',
13822     'restservices/TeamPhoneBook'
13823 ],
13824 function (RestCollectionBase, RestBase, TeamPhoneBook) {
13825     var TeamPhoneBooks = RestCollectionBase.extend({
13826         
13827         /**
13828          * @class
13829          * JavaScript representation of a TeamPhoneBooks collection object. Also exposes
13830          * methods to operate on the object against the server.
13831          *
13832          * @param {Object} options
13833          *     An object with the following properties:<ul>
13834          *         <li><b>id:</b> The id of the object being constructed</li>
13835          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
13836          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
13837          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
13838          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
13839          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
13840          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
13841          *             <li><b>content:</b> {String} Raw string of response</li>
13842          *             <li><b>object:</b> {Object} Parsed object of response</li>
13843          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
13844          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
13845          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
13846          *             </ul></li>
13847          *         </ul></li>
13848          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
13849          * @constructs
13850          **/
13851         init: function (options) {
13852             this._super(options);           
13853         },
13854 
13855         /**
13856          * @private
13857          * Gets the REST class for the current object - this is the TeamPhoneBooks class.
13858          */
13859         getRestClass: function () {
13860             return TeamPhoneBooks;
13861         },
13862 
13863         /**
13864          * @private
13865          * Gets the REST class for the objects that make up the collection. - this
13866          * is the TeamPhoneBooks class.
13867          */
13868         getRestItemClass: function () {
13869             return TeamPhoneBook;
13870         },
13871 
13872         /**
13873          * @private
13874          * Gets the REST type for the current object - this is a "ReasonCodes".
13875          */
13876         getRestType: function () {
13877             return "PhoneBooks";
13878         },
13879         
13880         /**
13881          * Overrides the parent class.  Returns the url for the PhoneBooks resource
13882          */
13883         getRestUrl: function () {
13884             // return ("/finesse/api/" + this.getRestType() + "?category=NOT_READY");
13885             var restObj = this._restObj,
13886             restUrl = "";
13887             //Prepend the base REST object if one was provided.
13888             if (restObj instanceof RestBase) {
13889                 restUrl += restObj.getRestUrl();
13890             }
13891             //Otherwise prepend with the default webapp name.
13892             else {
13893                 restUrl += "/finesse/api";
13894             }
13895             //Append the REST type.
13896             restUrl += "/PhoneBooks";
13897             //Append ID if it is not undefined, null, or empty.
13898             if (this._id) {
13899                 restUrl += "/" + this._id;
13900             }
13901             return restUrl;        
13902         },
13903         
13904         /**
13905          * @private
13906          * Gets the REST type for the objects that make up the collection - this is "ReasonCode".
13907          */
13908         getRestItemType: function () {
13909             return "PhoneBook";
13910         },
13911 
13912         /**
13913          * @private
13914          * Override default to indicates that the collection supports making
13915          * requests.
13916          */
13917         supportsRequests: true,
13918 
13919         /**
13920          * @private
13921          * Override default to indicates that the collection subscribes to its objects.
13922          */
13923         supportsRestItemSubscriptions: false,
13924         
13925         /**
13926          * Retrieve the Not Ready Reason Codes.
13927          *
13928          * @returns {finesse.restservices.TeamPhoneBooks}
13929          *     This TeamPhoneBooks object to allow cascading.
13930          */
13931         get: function () {
13932             // set loaded to false so it will rebuild the collection after the get
13933             /** @private */
13934             this._loaded = false;
13935             // reset collection
13936             /** @private */
13937             this._collection = {};
13938             // perform get
13939             this._synchronize();
13940             return this;
13941         },
13942 
13943         /* We only use PUT and GET on Reason Code team assignments 
13944          */
13945         createPutSuccessHandler: function(contact, contentBody, successHandler){
13946             return function (rsp) {
13947                 // Update internal structure based on response. Here we
13948                 // inject the contentBody from the PUT request into the
13949                 // rsp.object element to mimic a GET as a way to take
13950                 // advantage of the existing _processResponse method.
13951                 rsp.object = contentBody;
13952                 contact._processResponse(rsp);
13953 
13954                 //Remove the injected Contact object before cascading response
13955                 rsp.object = {};
13956                 
13957                 //cascade response back to consumer's response handler
13958                 successHandler(rsp);
13959             };
13960         },
13961 
13962         /**
13963          * Update - This should be all that is needed.
13964          */
13965         update: function (newValues, handlers) {
13966             this.isLoaded();
13967             var contentBody = {}, contentBodyInner = [], i, innerObject;
13968 
13969             contentBody[this.getRestType()] = {
13970             };
13971         
13972             for (i in newValues) {
13973                 if (newValues.hasOwnProperty(i)) {
13974                     innerObject = {};
13975                     innerObject = {
13976                         "uri": newValues[i]
13977                     };
13978                     contentBodyInner.push(innerObject);
13979                 }
13980             }
13981 
13982             contentBody[this.getRestType()] = {
13983                 "PhoneBook" : contentBodyInner
13984             };
13985 
13986             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
13987             handlers = handlers || {};
13988 
13989             this.restRequest(this.getRestUrl(), {
13990                 method: 'PUT',
13991                 success: this.createPutSuccessHandler(this, contentBody, handlers.success),
13992                 error: handlers.error,
13993                 content: contentBody
13994             });
13995 
13996             return this; // Allow cascading
13997         }       
13998         
13999     });
14000         
14001     window.finesse = window.finesse || {};
14002     window.finesse.restservices = window.finesse.restservices || {};
14003     window.finesse.restservices.TeamPhoneBooks = TeamPhoneBooks;
14004     
14005     return TeamPhoneBooks;
14006 });
14007 
14008 /**
14009  * JavaScript representation of the Finesse LayoutConfig object
14010  * @requires ClientServices
14011  * @requires finesse.FinesseBase
14012  * @requires finesse.restservices.RestBase
14013  */
14014 
14015 /** @private */
14016 define('restservices/LayoutConfig',['restservices/RestBase'], function (RestBase) {
14017     /** @private */
14018 	var LayoutConfig = RestBase.extend({
14019 
14020 		/**
14021 		 * @class
14022 		 * JavaScript representation of a LayoutConfig object. Also exposes methods to operate
14023 		 * on the object against the server.
14024 		 *
14025 		 * @param {String} id
14026 		 *     Not required...
14027 		 * @param {Object} callbacks
14028 		 *     An object containing callbacks for instantiation and runtime
14029 		 * @param {Function} callbacks.onLoad(this)
14030 		 *     Callback to invoke upon successful instantiation
14031 		 * @param {Function} callbacks.onLoadError(rsp)
14032 		 *     Callback to invoke on instantiation REST request error
14033 		 *     as passed by finesse.clientservices.ClientServices.ajax()
14034 		 *     {
14035 		 *         status: {Number} The HTTP status code returned
14036 		 *         content: {String} Raw string of response
14037 		 *         object: {Object} Parsed object of response
14038 		 *         error: {Object} Wrapped exception that was caught
14039 		 *         error.errorType: {String} Type of error that was caught
14040 		 *         error.errorMessage: {String} Message associated with error
14041 		 *     }
14042 		 * @param {Function} callbacks.onChange(this)
14043 		 *     Callback to invoke upon successful update
14044 		 * @param {Function} callbacks.onError(rsp)
14045 		 *     Callback to invoke on update error (refresh or event)
14046 		 *     as passed by finesse.clientservices.ClientServices.ajax()
14047 		 *     {
14048 		 *         status: {Number} The HTTP status code returned
14049 		 *         content: {String} Raw string of response
14050 		 *         object: {Object} Parsed object of response
14051 		 *         error: {Object} Wrapped exception that was caught
14052 		 *         error.errorType: {String} Type of error that was caught
14053 		 *         error.errorMessage: {String} Message associated with error
14054 		 *     }
14055 		 *  
14056 	     * @constructs
14057 		 */
14058 		init: function (callbacks) {
14059 			this._super("", callbacks);
14060 			//when post is performed and id is empty
14061 			/*if (id === "") {
14062 				this._loaded = true;
14063 			}*/
14064 	        this._layoutxml = {};
14065 		},
14066 	
14067 		/**
14068 		 * Returns REST class of LayoutConfig object
14069 		 */
14070 		getRestClass: function () {
14071 			return LayoutConfig;
14072 		},
14073 	
14074 		/**
14075 		 * The type of this REST object is LayoutConfig
14076 		 */
14077 		getRestType: function () {
14078 			return "LayoutConfig";
14079 		},
14080 
14081 		/**
14082 		 * Gets the REST URL of this object.
14083 		 * 
14084 		 * If the parent has an id, the id is appended.
14085 		 * On occasions of POST, it will not have an id.
14086 		 */
14087 		getRestUrl: function () {
14088 			var layoutUri = "/finesse/api/" + this.getRestType() + "/default";
14089 			/*if (this._id) {
14090 				layoutUri = layoutUri + "/" + this._id;
14091 			}*/
14092 			return layoutUri;
14093 		},
14094 	
14095 		/**
14096 		 * This API does not support subscription
14097 		 */
14098 		supportsSubscriptions: false,
14099 		
14100 		keepRestResponse: true,
14101 
14102 
14103 		/**
14104 		 * Gets finesselayout.xml retrieved from the API call
14105 		 */
14106 		getLayoutxml: function () {
14107 			this.isLoaded();
14108 			var layoutxml = this.getData().layoutxml;
14109 
14110             // We need to unescape everything that is unallowed in xml so consumers don't have to deal with it (used in tandem with update())
14111             layoutxml = layoutxml.replace(/&/g,"&");
14112 
14113             return layoutxml;
14114 		},
14115 	
14116 		/**
14117 		 * Gets the type of this LayoutConfig object
14118 		 */
14119 		/*
14120 		getType: function () {
14121 			this.isLoaded();
14122 			return this.getData().type;
14123 		},*/
14124 	
14125 		/**
14126 		 * Retrieve the LayoutConfig settings.
14127 		 * If the id is not provided the API call will fail.
14128 		 * @returns {LayoutConfig}
14129 		 *     This LayoutConfig object to allow cascading.
14130 		 */
14131 		get: function () {      
14132 			this._synchronize();
14133 			return this;
14134 		},
14135 
14136 		/**
14137 		 * Closure handle updating of the internal data for the LayoutConfig object
14138 		 * upon a successful update (PUT) request before calling the intended
14139 		 * success handler provided by the consumer
14140 		 * 
14141 		 * @param {Object}
14142 		 *            layoutconfig Reference to this LayoutConfig object
14143 		 * @param {Object}
14144 		 *            LayoutConfig Object that contains the  settings to be
14145 		 *            submitted in the api request
14146 		 * @param {Function}
14147 		 *            successHandler The success handler specified by the consumer
14148 		 *            of this object
14149 		 * @returns {LayoutConfig} This LayoutConfig object to allow cascading
14150 		 */
14151 	
14152 		createPutSuccessHandler: function (layoutconfig, contentBody, successHandler) {
14153 			return function (rsp) {			
14154 				// Update internal structure based on response. Here we
14155 				// inject the contentBody from the PUT request into the
14156 				// rsp.object element to mimic a GET as a way to take
14157 				// advantage of the existing _processResponse method.
14158 				rsp.content = contentBody;
14159 				rsp.object.LayoutConfig = {};
14160 				rsp.object.LayoutConfig.finesseLayout = contentBody;
14161 				layoutconfig._processResponse(rsp);
14162 	
14163 				//Remove the injected layoutConfig object before cascading response
14164 				rsp.object.LayoutConfig = {};
14165 	
14166 				//cascade response back to consumer's response handler
14167 				successHandler(rsp);
14168 			};
14169 		},
14170 	
14171 		/**
14172 		 *  Update LayoutConfig
14173 		 * @param {Object} finesselayout
14174 		 *     The XML for FinesseLayout being stored
14175 		 * 
14176 		 * @param {Object} handlers
14177 		 *     An object containing callback handlers for the request. Optional.
14178 		 * @param {Function} options.success(rsp)
14179 		 *     A callback function to be invoked for a successful request.
14180 		 *     {
14181 		 *         status: {Number} The HTTP status code returned
14182 		 *         content: {String} Raw string of response
14183 		 *         object: {Object} Parsed object of response
14184 		 *     }
14185 		 * @param {Function} options.error(rsp)
14186 		 *     A callback function to be invoked for an unsuccessful request.
14187 		 *     {
14188 		 *         status: {Number} The HTTP status code returned
14189 		 *         content: {String} Raw string of response
14190 		 *         object: {Object} Parsed object of response (HTTP errors)
14191 		 *         error: {Object} Wrapped exception that was caught
14192 		 *         error.errorType: {String} Type of error that was caught
14193 		 *         error.errorMessage: {String} Message associated with error
14194 		 *     }
14195 		 * @returns {finesse.restservices.LayoutConfig}
14196 		 *     This LayoutConfig object to allow cascading
14197 		 */
14198 	
14199 		update: function (layoutxml, handlers) {
14200 			this.isLoaded();
14201 
14202 			
14203 			var contentBody = {}, 
14204 			//Created a regex (re) to scoop out just the gadget URL (without before and after whitespace characters)
14205 			re = /<gadget>\s*(\S+)\s*<\/gadget>/g;
14206 
14207 			// We need to escape everything that is unallowed in xml so consumers don't have to deal with it (used in tandem with getLayoutxml())
14208 			layoutxml = layoutxml.replace(/&(?!amp;)/g, "&");
14209 
14210 			//used the regex (re) to the update and save the layoutxml with the improved gadget URL (without before/after whitespace)
14211 			layoutxml = layoutxml.replace(re, "<gadget>$1</gadget>");
14212 
14213 			contentBody[this.getRestType()] = {
14214 				"layoutxml": finesse.utilities.Utilities.translateHTMLEntities(layoutxml, true)
14215 			};
14216 
14217 			// Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
14218 			handlers = handlers || {};
14219 
14220 			this.restRequest(this.getRestUrl(), {
14221 				method: 'PUT',
14222 				success: this.createPutSuccessHandler(this, layoutxml, handlers.success),
14223 				error: handlers.error,
14224 				content: contentBody
14225 			});
14226 
14227 			return this; // Allow cascading
14228 		}
14229 	
14230 		/**
14231 		 *TODO createPostSuccessHandler needs to be debugged to make it working
14232 		 * Closure handle creating new  LayoutConfig object
14233 		 * upon a successful create (POST) request before calling the intended
14234 		 * success handler provided by the consumer
14235 		 * 
14236 		 * @param {Object}
14237 		 *            layoutconfig Reference to this LayoutConfig object
14238 		 * @param {Object}
14239 		 *            LayoutConfig Object that contains the  settings to be
14240 		 *            submitted in the api request
14241 		 * @param {Function}
14242 		 *            successHandler The success handler specified by the consumer
14243 		 *            of this object
14244 		 * @returns {finesse.restservices.LayoutConfig} This LayoutConfig object to allow cascading
14245 		 */
14246 	/*
14247 		createPostSuccessHandler: function (layoutconfig, contentBody, successHandler) {
14248 			return function (rsp) {
14249 	
14250 				rsp.object = contentBody;
14251 				layoutconfig._processResponse(rsp);
14252 	
14253 				//Remove the injected layoutConfig object before cascading response
14254 				rsp.object = {};
14255 	
14256 				//cascade response back to consumer's response handler
14257 				successHandler(rsp);
14258 			};
14259 		}, */
14260 	
14261 		/**
14262 		 * TODO Method needs to be debugged to make POST working
14263 		 *  Add LayoutConfig
14264 		 * @param {Object} finesselayout
14265 		 *     The XML for FinesseLayout being stored
14266 		 * 
14267 		 * @param {Object} handlers
14268 		 *     An object containing callback handlers for the request. Optional.
14269 		 * @param {Function} options.success(rsp)
14270 		 *     A callback function to be invoked for a successful request.
14271 		 *     {
14272 		 *         status: {Number} The HTTP status code returned
14273 		 *         content: {String} Raw string of response
14274 		 *         object: {Object} Parsed object of response
14275 		 *     }
14276 		 * @param {Function} options.error(rsp)
14277 		 *     A callback function to be invoked for an unsuccessful request.
14278 		 *     {
14279 		 *         status: {Number} The HTTP status code returned
14280 		 *         content: {String} Raw string of response
14281 		 *         object: {Object} Parsed object of response (HTTP errors)
14282 		 *         error: {Object} Wrapped exception that was caught
14283 		 *         error.errorType: {String} Type of error that was caught
14284 		 *         error.errorMessage: {String} Message associated with error
14285 		 *     }
14286 		 * @returns {finesse.restservices.LayoutConfig}
14287 		 *     This LayoutConfig object to allow cascading
14288 		 */
14289 	/*
14290 		add: function (layoutxml, handlers) {
14291 			this.isLoaded();
14292 			var contentBody = {};
14293 	
14294 	
14295 			contentBody[this.getRestType()] = {
14296 					"layoutxml": layoutxml,
14297 					"type": "current"
14298 			    };
14299 	
14300 			// Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
14301 			handlers = handlers || {};
14302 	
14303 			this.restRequest(this.getRestUrl(), {
14304 				method: 'POST',
14305 				success: this.createPostSuccessHandler(this, contentBody, handlers.success),
14306 				error: handlers.error,
14307 				content: contentBody
14308 			});
14309 	
14310 			return this; // Allow cascading
14311 		} */
14312 	});
14313 	
14314 	window.finesse = window.finesse || {};
14315     window.finesse.restservices = window.finesse.restservices || {};
14316     window.finesse.restservices.LayoutConfig = LayoutConfig;
14317     
14318 	return LayoutConfig;
14319 	
14320 });
14321 
14322 /**
14323  * JavaScript representation of the Finesse LayoutConfig object for a Team.
14324  *
14325  * @requires finesse.clientservices.ClientServices
14326  * @requires Class
14327  * @requires finesse.FinesseBase
14328  * @requires finesse.restservices.RestBase
14329  * @requires finesse.utilities.Utilities
14330  * @requires finesse.restservices.LayoutConfig
14331  */
14332 
14333 /** The following comment is to prevent jslint errors about 
14334  * using variables before they are defined.
14335  */
14336 /*global Exception */
14337 
14338 /** @private */
14339 define('restservices/TeamLayoutConfig',[
14340     'restservices/RestBase',
14341     'utilities/Utilities',
14342     'restservices/LayoutConfig'
14343 ],
14344 function (RestBase, Utilities, LayoutConfig) {
14345     
14346     var TeamLayoutConfig = RestBase.extend({
14347       // Keep the restresponse so we can parse the layoutxml out of it in getLayoutXML()
14348       keepRestResponse: true,
14349     
14350       /**
14351        * @class
14352        * JavaScript representation of a LayoutConfig object for a Team. Also exposes
14353        * methods to operate on the object against the server.
14354        *
14355        * @param {Object} options
14356        *     An object with the following properties:<ul>
14357        *         <li><b>id:</b> The id of the object being constructed</li>
14358        *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
14359        *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
14360        *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
14361        *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
14362        *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
14363        *             <li><b>status:</b> {Number} The HTTP status code returned</li>
14364        *             <li><b>content:</b> {String} Raw string of response</li>
14365        *             <li><b>object:</b> {Object} Parsed object of response</li>
14366        *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
14367        *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
14368        *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
14369        *             </ul></li>
14370        *         </ul></li>
14371        *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
14372        * @constructs
14373        **/
14374       init: function (options) {
14375           this._super(options);
14376       },
14377     
14378       /**
14379        * @private
14380        * Gets the REST class for the current object - this is the LayoutConfigs class.
14381        * @returns {Object} The LayoutConfigs class.
14382        */
14383       getRestClass: function () {
14384           return TeamLayoutConfig;
14385       },
14386     
14387       /**
14388        * @private
14389        * Gets the REST type for the current object - this is a "LayoutConfig".
14390        * @returns {String} The LayoutConfig string.
14391        */
14392       getRestType: function () {
14393           return "TeamLayoutConfig";
14394       },
14395     
14396       /**
14397        * @private
14398        * Override default to indicate that this object doesn't support making
14399        * requests.
14400        */
14401       supportsRequests: false,
14402     
14403       /**
14404        * @private
14405        * Override default to indicate that this object doesn't support subscriptions.
14406        */
14407       supportsSubscriptions: false,
14408     
14409       /**
14410        * Getter for the category.
14411        * @returns {String} The category.
14412        */
14413       getLayoutXML: function () {
14414           this.isLoaded();
14415           var layoutxml = this.getData().layoutxml;
14416 
14417           // We need to unescape everything that is unallowed in xml so consumers don't have to deal with it (used in tandem with put())
14418           layoutxml = layoutxml.replace(/&/g,"&");
14419 
14420           return layoutxml;
14421       },
14422     
14423       /**
14424        * Getter for the code.
14425        * @returns {String} The code.
14426        */
14427       getUseDefault: function () {
14428           this.isLoaded();
14429           return this.getData().useDefault;
14430       },
14431       
14432       /**
14433        * Retrieve the TeamLayoutConfig.
14434        *
14435        * @returns {finesse.restservices.TeamLayoutConfig}
14436        */
14437       get: function () {
14438           // this._id is needed, but is not used in this object.. we're overriding getRestUrl anyway
14439           this._id = "0";
14440           // set loaded to false so it will rebuild the collection after the get
14441           this._loaded = false;
14442           // reset collection
14443           this._collection = {};
14444           // perform get
14445           this._synchronize();
14446           return this;
14447       },
14448     
14449       createPutSuccessHandler: function(contact, contentBody, successHandler){
14450           return function (rsp) {
14451               // Update internal structure based on response. Here we
14452               // inject the contentBody from the PUT request into the
14453               // rsp.object element to mimic a GET as a way to take
14454               // advantage of the existing _processResponse method.
14455               rsp.object = contentBody;
14456               contact._processResponse(rsp);
14457     
14458               //Remove the injected Contact object before cascading response
14459               rsp.object = {};
14460               
14461               //cascade response back to consumer's response handler
14462               successHandler(rsp);
14463           };
14464       },
14465       
14466       put: function (newValues, handlers) {
14467           // this._id is needed, but is not used in this object.. we're overriding getRestUrl anyway
14468           this._id = "0";
14469           this.isLoaded();
14470 
14471           // We need to escape everything that is unallowed in xml so consumers don't have to deal with it (used in tandem with getLayoutxml())
14472           var layoutxml = newValues.layoutXML.replace(/&(?!amp;)/g, "&"),
14473               contentBody = {}, 
14474               //Created a regex (re) to scoop out just the gadget URL (without before and after whitespace characters)
14475               re = /<gadget>\s*(\S+)\s*<\/gadget>/g;
14476 
14477           //used the regex (re) to the update and save the layoutxml with the improved gadget URL (without before/after whitespace)
14478           layoutxml = layoutxml.replace(re, "<gadget>$1</gadget>");
14479           
14480           contentBody[this.getRestType()] = {
14481               "useDefault": newValues.useDefault,
14482               // The LayoutConfig restservice javascript class only translates ampersands, so we'll do that also
14483               "layoutxml": finesse.utilities.Utilities.translateHTMLEntities(layoutxml, true)
14484           };
14485     
14486           // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
14487           handlers = handlers || {};
14488     
14489           this.restRequest(this.getRestUrl(), {
14490               method: 'PUT',
14491               success: this.createPutSuccessHandler(this, contentBody, handlers.success),
14492               error: handlers.error,
14493               content: contentBody
14494           });
14495     
14496           return this; // Allow cascading
14497       },
14498     
14499       getRestUrl: function(){
14500           // return team's url + /LayoutConfig
14501           // eg: /api/Team/1/LayoutConfig
14502           if(this._restObj === undefined){
14503               throw new Exception("TeamLayoutConfig instances must have a parent team object.");
14504           }
14505           return this._restObj.getRestUrl() + '/LayoutConfig';
14506       }
14507     
14508       });
14509         
14510     window.finesse = window.finesse || {};
14511     window.finesse.restservices = window.finesse.restservices || {};
14512     window.finesse.restservices.TeamLayoutConfig = TeamLayoutConfig;
14513       
14514     return TeamLayoutConfig;
14515 });
14516 
14517 /**
14518  * JavaScript representation of a TeamWorkflow.
14519  *
14520  * @requires finesse.clientservices.ClientServices
14521  * @requires Class
14522  * @requires finesse.FinesseBase
14523  * @requires finesse.restservices.RestBase
14524  */
14525 /** @private */
14526 define('restservices/TeamWorkflow',['restservices/RestBase'], function (RestBase) {
14527 
14528     var TeamWorkflow = RestBase.extend({
14529 
14530         /**
14531          * @class
14532          * JavaScript representation of a TeamWorkflow object. Also exposes
14533          * methods to operate on the object against the server.
14534          *
14535          * @param {Object} options
14536          *     An object with the following properties:<ul>
14537          *         <li><b>id:</b> The id of the object being constructed</li>
14538          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
14539          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
14540          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
14541          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
14542          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
14543          *             <li><b>status:</b> {Number} The HTTP status description returned</li>
14544          *             <li><b>content:</b> {String} Raw string of response</li>
14545          *             <li><b>object:</b> {Object} Parsed object of response</li>
14546          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
14547          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
14548          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
14549          *             </ul></li>
14550          *         </ul></li>
14551          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
14552          * @constructs
14553          **/
14554         init: function (options) {
14555             this._super(options);
14556         },
14557 
14558         /**
14559          * @private
14560          * Gets the REST class for the current object - this is the TeamWorkflow class.
14561          * @returns {Object} The TeamWorkflow class.
14562          */
14563         getRestClass: function () {
14564             return TeamWorkflow;
14565         },
14566 
14567         /**
14568          * @private
14569          * Gets the REST type for the current object - this is a "Workflow".
14570          * @returns {String} The Workflow string.
14571          */
14572         getRestType: function () {
14573             return "Workflow";
14574         },
14575 
14576         /**
14577          * @private
14578          * Override default to indicate that this object doesn't support making
14579          * requests.
14580          */
14581         supportsRequests: false,
14582 
14583         /**
14584          * @private
14585          * Override default to indicate that this object doesn't support subscriptions.
14586          */
14587         supportsSubscriptions: false,
14588 
14589         /**
14590          * Getter for the name.
14591          * @returns {String} The name.
14592          */
14593         getName: function () {
14594             this.isLoaded();
14595             return this.getData().name;
14596         },
14597 
14598         /**
14599          * Getter for the description.
14600          * @returns {String} The description.
14601          */
14602         getDescription: function () {
14603             this.isLoaded();
14604             return this.getData().description;
14605         },
14606 
14607         /**
14608          * Getter for the Uri value.
14609          * @returns {String} The Uri.
14610          */
14611         getUri: function () {
14612             this.isLoaded();
14613             return this.getData().uri;
14614         }
14615 
14616     });
14617     
14618 	window.finesse = window.finesse || {};
14619     window.finesse.restservices = window.finesse.restservices || {};
14620     window.finesse.restservices.TeamWorkflow = TeamWorkflow;
14621 
14622     return TeamWorkflow;
14623 });
14624 
14625 /**
14626 * JavaScript representation of the TeamWorkflows collection
14627 * object which contains a list of TeamWorkflow objects.
14628  *
14629  * @requires finesse.clientservices.ClientServices
14630  * @requires Class
14631  * @requires finesse.FinesseBase
14632  * @requires finesse.restservices.RestBase
14633  * @requires finesse.restservices.Dialog
14634  * @requires finesse.restservices.RestCollectionBase
14635  */
14636 /** @private */
14637 define('restservices/TeamWorkflows',[
14638     'restservices/RestCollectionBase',
14639     'restservices/TeamWorkflow',
14640     'restservices/RestBase'
14641 ],
14642 function (RestCollectionBase, TeamWorkflow, RestBase) {
14643 
14644     var TeamWorkflows = RestCollectionBase.extend({
14645     
14646         /**
14647          * @class
14648          * JavaScript representation of a TeamWorkflows collection object. Also exposes
14649          * methods to operate on the object against the server.
14650          *
14651          * @param {Object} options
14652          *     An object with the following properties:<ul>
14653          *         <li><b>id:</b> The id of the object being constructed</li>
14654          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
14655          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
14656          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
14657          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
14658          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
14659          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
14660          *             <li><b>content:</b> {String} Raw string of response</li>
14661          *             <li><b>object:</b> {Object} Parsed object of response</li>
14662          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
14663          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
14664          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
14665          *             </ul></li>
14666          *         </ul></li>
14667          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
14668          * @constructs
14669          **/
14670         init: function (options) {
14671             this._super(options);
14672         },
14673 
14674         /**
14675          * @private
14676          * Gets the REST class for the current object - this is the TeamWorkflows class.
14677          */
14678         getRestClass: function () {
14679             return TeamWorkflows;
14680         },
14681 
14682         /**
14683          * @private
14684          * Gets the REST class for the objects that make up the collection. - this
14685          * is the TeamWorkflow class.
14686          */
14687         getRestItemClass: function () {
14688             return TeamWorkflow;
14689         },
14690 
14691         /**
14692          * @private
14693          * Gets the REST type for the current object - this is a "Workflows".
14694          */
14695         getRestType: function () {
14696             return "Workflows";
14697         },
14698 
14699         /**
14700          * Overrides the parent class.  Returns the url for the Workflows resource
14701          */
14702         getRestUrl: function () {
14703             var restObj = this._restObj, restUrl = "";
14704 
14705             //Prepend the base REST object if one was provided.
14706             //Otherwise prepend with the default webapp name.
14707             if (restObj instanceof RestBase) {
14708                 restUrl += restObj.getRestUrl();
14709             } else {
14710                 restUrl += "/finesse/api/Team";
14711             }
14712             //Append ID if it is not undefined, null, or empty.
14713             if (this._id) {
14714                 restUrl += "/" + this._id;
14715             }
14716             //Append the REST type.
14717             restUrl += "/Workflows";
14718             
14719             return restUrl;
14720         },
14721 
14722         /**
14723          * @private
14724          * Gets the REST type for the objects that make up the collection - this is "Workflow".
14725          */
14726         getRestItemType: function () {
14727             return "Workflow";
14728         },
14729 
14730         /**
14731          * @private
14732          * Override default to indicates that the collection supports making requests.
14733          */
14734         supportsRequests: true,
14735 
14736         /**
14737          * @private
14738          * Override default to indicates that the collection does not subscribe to its objects.
14739          */
14740         supportsRestItemSubscriptions: false,
14741 
14742         /**
14743          * Retrieve the Sign Out Reason Codes.
14744          *
14745          * @returns {finesse.restservices.TeamWorkflows}
14746          *     This TeamWorkflows object to allow cascading.
14747          */
14748         get: function () {
14749             // set loaded to false so it will rebuild the collection after the get
14750             this._loaded = false;
14751             // reset collection
14752             this._collection = {};
14753             // perform get
14754             this._synchronize();
14755             return this;
14756         },
14757 
14758         /* We only use PUT and GET on Reason Code team assignments
14759          * @param {Object} contact
14760          * @param {Object} contentBody
14761          * @param {Function} successHandler
14762          */
14763         createPutSuccessHandler: function (contact, contentBody, successHandler) {
14764             return function (rsp) {
14765                 // Update internal structure based on response. Here we
14766                 // inject the contentBody from the PUT request into the
14767                 // rsp.object element to mimic a GET as a way to take
14768                 // advantage of the existing _processResponse method.
14769                 rsp.object = contentBody;
14770                 contact._processResponse(rsp);
14771 
14772                 //Remove the injected contentBody object before cascading response
14773                 rsp.object = {};
14774 
14775                 //cascade response back to consumer's response handler
14776                 successHandler(rsp);
14777             };
14778         },
14779 
14780         /**
14781          * Update - This should be all that is needed.
14782          * @param {Object} newValues
14783          * @param {Object} handlers
14784          * @returns {finesse.restservices.TeamWorkflows}
14785          *     This TeamWorkflows object to allow cascading.
14786          */
14787         update: function (newValues, handlers) {
14788             this.isLoaded();
14789             var contentBody = {}, contentBodyInner = [], i, innerObject = {};
14790 
14791             contentBody[this.getRestType()] = {
14792             };
14793 
14794             for (i in newValues) {
14795                 if (newValues.hasOwnProperty(i)) {
14796                     innerObject = {
14797                         "uri": newValues[i]
14798                     };
14799                     contentBodyInner.push(innerObject);
14800                 }
14801             }
14802 
14803             contentBody[this.getRestType()] = {
14804                 "Workflow" : contentBodyInner
14805             };
14806 
14807             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
14808             handlers = handlers || {};
14809 
14810             this.restRequest(this.getRestUrl(), {
14811                 method: 'PUT',
14812                 success: this.createPutSuccessHandler(this, contentBody, handlers.success),
14813                 error: handlers.error,
14814                 content: contentBody
14815             });
14816 
14817             return this; // Allow cascading
14818         }
14819 
14820     });
14821     
14822 	window.finesse = window.finesse || {};
14823     window.finesse.restservices = window.finesse.restservices || {};
14824     window.finesse.restservices.TeamWorkflows = TeamWorkflows;
14825     
14826     return TeamWorkflows;
14827 });
14828 
14829 /**
14830  * JavaScript representation of the Finesse Team REST object.
14831  *
14832  * @requires finesse.clientservices.ClientServices
14833  * @requires Class
14834  * @requires finesse.FinesseBase
14835  * @requires finesse.restservices.RestBase
14836  * @requires finesse.restservices.RestCollectionBase
14837  * @requires finesse.restservices.User
14838  * @requires finesse.restservices.Users
14839  */
14840 
14841 /**
14842  * The following comment prevents JSLint errors concerning undefined global variables.
14843  * It tells JSLint that these identifiers are defined elsewhere.
14844  */
14845 /*jslint bitwise:true, browser:true, nomen:true, regexp:true, sloppy:true, white:true */
14846 
14847 /** The following comment is to prevent jslint errors about 
14848  * using variables before they are defined.
14849  */
14850 /*global $, jQuery, Handlebars, dojox, dojo, finesse */
14851 
14852 /** @private */
14853 define('restservices/Team',[
14854     'restservices/RestBase',
14855     'utilities/Utilities',
14856     'restservices/Users',
14857     'restservices/TeamNotReadyReasonCodes',
14858     'restservices/TeamWrapUpReasons',
14859     'restservices/TeamSignOutReasonCodes',
14860     'restservices/TeamPhoneBooks',
14861     'restservices/TeamLayoutConfig',
14862     'restservices/TeamWorkflows'
14863 ],
14864 function (RestBase, Utilities, Users, TeamNotReadyReasonCodes, TeamWrapUpReasons, TeamSignOutReasonCodes, TeamPhoneBooks, TeamLayoutConfig, TeamWorkflows) {
14865     var Team = RestBase.extend(/** @lends finesse.restservices.Team.prototype */{
14866         
14867         _teamLayoutConfig: null,
14868 
14869         /**
14870          * @class
14871          * A Team is a set of Agent Users, typically supervised by one or more Supervisor Users.
14872          *
14873          * @augments finesse.restservices.RestBase
14874          * @see finesse.restservices.User#getSupervisedTeams
14875          * @see finesse.restservices.Users
14876          * @constructs
14877          */
14878         _fakeConstuctor: function () {
14879             /* This is here to hide the real init constructor from the public docs */
14880         },
14881         
14882         /**
14883          * @private
14884          * @class
14885          * JavaScript representation of a Team object. Also exposes methods to operate
14886          * on the object against the server.
14887          *
14888          * @param {Object} options
14889          *     An object with the following properties:<ul>
14890          *         <li><b>id:</b> The id of the object being constructed</li>
14891          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
14892          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
14893          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
14894          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
14895          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
14896          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
14897          *             <li><b>content:</b> {String} Raw string of response</li>
14898          *             <li><b>object:</b> {Object} Parsed object of response</li>
14899          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
14900          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
14901          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
14902          *             </ul></li>
14903          *         </ul></li>
14904          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
14905          **/
14906         init: function (options) {
14907             this._super(options);
14908         },
14909     
14910         /**
14911          * @private
14912          * Gets the REST class for the current object - this is the Team class.
14913          * @returns {Object} The Team constructor.
14914          */
14915         getRestClass: function () {
14916             return finesse.restesrvices.Team;
14917         },
14918     
14919         /**
14920          * @private
14921          * Gets the REST type for the current object - this is a "Team".
14922          * @returns {String} The Team string.
14923          */
14924         getRestType: function () {
14925             return "Team";
14926         },
14927     
14928         /**
14929          * @private
14930          * Override default to indicate that this object doesn't support making
14931          * requests.
14932          */
14933         supportsSubscriptions: false,
14934     
14935         /**
14936          * Getter for the team id.
14937          * @returns {String} The team id.
14938          */
14939         getId: function () {
14940             this.isLoaded();
14941             return this.getData().id;
14942         },
14943     
14944         /**
14945          * Getter for the team name.
14946          * @returns {String} The team name
14947          */
14948         getName: function () {
14949             this.isLoaded();
14950             return this.getData().name;
14951         },
14952     
14953         /**
14954          * @private
14955          * Getter for the team uri.
14956          * @returns {String} The team uri
14957          */
14958         getUri: function () {
14959             this.isLoaded();
14960             return this.getData().uri;        
14961         },
14962     
14963         /**
14964          * Constructs and returns a collection of Users.
14965          * @param {finesse.interfaces.RestObjectHandlers} [handlers] Object that sets callback handlers.
14966          * @returns {finesse.restservices.Users} Users collection of User objects.
14967          */
14968         getUsers: function (options) {
14969             this.isLoaded();
14970             options = options || {};
14971     
14972             options.parentObj = this;
14973             // We are using getData() instead of getData.Users because the superclass (RestCollectionBase)
14974             // for Users needs the "Users" key to validate the provided payload matches the class type.
14975             options.data = this.getData();
14976     
14977             return new Users(options);
14978         },
14979     
14980         /**
14981          * @private
14982          * Getter for a teamNotReadyReasonCodes collection object that is associated with Team.
14983          * @param callbacks
14984          * @returns {teamNotReadyReasonCodes}
14985          *     A teamNotReadyReasonCodes collection object.
14986          */
14987         getTeamNotReadyReasonCodes: function (callbacks) {
14988             var options = callbacks || {};
14989             options.parentObj = this;
14990             this.isLoaded();
14991     
14992             if (!this._teamNotReadyReasonCodes) {
14993                 this._teamNotReadyReasonCodes = new TeamNotReadyReasonCodes(options);
14994             }
14995     
14996             return this._teamNotReadyReasonCodes;
14997         },
14998     
14999         /**
15000          * @private
15001          * Getter for a teamWrapUpReasons collection object that is associated with Team.
15002          * @param callbacks
15003          * @returns {teamWrapUpReasons}
15004          *     A teamWrapUpReasons collection object.
15005          */
15006         getTeamWrapUpReasons: function (callbacks) {
15007             var options = callbacks || {};
15008             options.parentObj = this;
15009             this.isLoaded();
15010     
15011             if (!this._teamWrapUpReasons) {
15012                 this._teamWrapUpReasons = new TeamWrapUpReasons(options);
15013             }
15014     
15015             return this._teamWrapUpReasons;
15016         },
15017     
15018         /**
15019          * @private
15020          * Getter for a teamSignOutReasonCodes collection object that is associated with Team.
15021          * @param callbacks
15022          * @returns {teamSignOutReasonCodes}
15023          *     A teamSignOutReasonCodes collection object.
15024          */
15025     
15026         getTeamSignOutReasonCodes: function (callbacks) {
15027             var options = callbacks || {};
15028             options.parentObj = this;
15029             this.isLoaded();
15030     
15031             if (!this._teamSignOutReasonCodes) {
15032                 this._teamSignOutReasonCodes = new TeamSignOutReasonCodes(options);
15033             }
15034     
15035             return this._teamSignOutReasonCodes;
15036         },
15037     
15038         /**
15039          * @private
15040          * Getter for a teamPhoneBooks collection object that is associated with Team.
15041          * @param callbacks
15042          * @returns {teamPhoneBooks}
15043          *     A teamPhoneBooks collection object.
15044          */
15045         getTeamPhoneBooks: function (callbacks) {
15046             var options = callbacks || {};
15047             options.parentObj = this;
15048             this.isLoaded();
15049     
15050             if (!this._phonebooks) {
15051                 this._phonebooks = new TeamPhoneBooks(options);
15052             }
15053     
15054             return this._phonebooks;
15055         },
15056     
15057         /**
15058          * @private
15059          * Getter for a teamWorkflows collection object that is associated with Team.
15060          * @param callbacks
15061          * @returns {teamWorkflows}
15062          *     A teamWorkflows collection object.
15063          */
15064         getTeamWorkflows: function (callbacks) {
15065             var options = callbacks || {};
15066             options.parentObj = this;
15067             this.isLoaded();
15068     
15069             if (!this._workflows) {
15070                 this._workflows = new TeamWorkflows(options);
15071             }
15072     
15073             return this._workflows;
15074         },
15075     
15076         /**
15077          * @private
15078          * Getter for a teamLayoutConfig object that is associated with Team.
15079          * @param callbacks
15080          * @returns {teamLayoutConfig}
15081          */
15082         getTeamLayoutConfig: function (callbacks) {
15083             var options = callbacks || {};
15084             options.parentObj = this;
15085             this.isLoaded();
15086     
15087             if (this._teamLayoutConfig === null) {
15088                 this._teamLayoutConfig = new TeamLayoutConfig(options);
15089             }
15090     
15091             return this._teamLayoutConfig;
15092         }
15093     
15094     });
15095     
15096     window.finesse = window.finesse || {};
15097     window.finesse.restservices = window.finesse.restservices || {};
15098     window.finesse.restservices.Team = Team;
15099     
15100     return Team;    
15101 });
15102 
15103 /**
15104  * JavaScript representation of the Finesse Teams collection.
15105  * object which contains a list of Team objects
15106  * @requires finesse.clientservices.ClientServices
15107  * @requires Class
15108  * @requires finesse.FinesseBase
15109  * @requires finesse.restservices.RestBase
15110  * @requires finesse.restservices.RestCollectionBase
15111  */
15112 
15113 /** @private */
15114 define('restservices/Teams',[
15115     'restservices/RestCollectionBase',
15116     'restservices/Team'
15117 ],
15118 function (RestCollectionBase, Team) {
15119     /** @private */
15120     var Teams = RestCollectionBase.extend({
15121 
15122         /**
15123          * @class
15124          * JavaScript representation of a Teams collection object. Also exposes methods to operate
15125          * on the object against the server.
15126          *
15127          * @param {Object} options
15128          *     An object with the following properties:<ul>
15129          *         <li><b>id:</b> The id of the object being constructed</li>
15130          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
15131          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
15132          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
15133          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
15134          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
15135          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
15136          *             <li><b>content:</b> {String} Raw string of response</li>
15137          *             <li><b>object:</b> {Object} Parsed object of response</li>
15138          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
15139          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
15140          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
15141          *             </ul></li>
15142          *         </ul></li>
15143          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
15144          * @constructs
15145          **/
15146         init: function (options) {
15147             this._super(options);
15148         },
15149 
15150         /**
15151          * @private
15152          * Gets the REST class for the current object - this is the Teams class.
15153          * @returns {Object} The Teams constructor.
15154          */
15155         getRestClass: function () {
15156             return Teams;
15157         },
15158 
15159         /**
15160          * @private
15161          * Gets the REST class for the objects that make up the collection. - this
15162          * is the Team class.
15163          */
15164         getRestItemClass: function () {
15165             return Team;
15166         },
15167 
15168         /**
15169          * @private
15170          * Gets the REST type for the current object - this is a "Teams".
15171          * @returns {String} The Teams string.
15172          */
15173         getRestType: function () {
15174             return "Teams";
15175         },
15176         
15177         /**
15178          * @private
15179          * Gets the REST type for the objects that make up the collection - this is "Team".
15180          */
15181         getRestItemType: function () {
15182             return "Team";
15183         },
15184 
15185         /**
15186          * @private
15187          * Override default to indicates that the collection supports making
15188          * requests.
15189          */
15190         supportsRequests: true,
15191 
15192         /**
15193          * @private
15194          * Override default to indicate that this object doesn't support subscriptions.
15195          */
15196         supportsRestItemSubscriptions: false,
15197         
15198         /**
15199          * @private
15200          * Retrieve the Teams.  This call will re-query the server and refresh the collection.
15201          *
15202          * @returns {finesse.restservices.Teams}
15203          *     This Teams object to allow cascading.
15204          */
15205         get: function () {
15206             // set loaded to false so it will rebuild the collection after the get
15207             this._loaded = false;
15208             // reset collection
15209             this._collection = {};
15210             // perform get
15211             this._synchronize();
15212             return this;
15213         }
15214 
15215     });
15216 
15217     window.finesse = window.finesse || {};
15218     window.finesse.restservices = window.finesse.restservices || {};
15219     window.finesse.restservices.Teams = Teams;
15220     
15221     return Teams;
15222 });
15223 
15224 /**
15225  * JavaScript representation of the Finesse SystemInfo object
15226  *
15227  * @requires finesse.clientservices.ClientServices
15228  * @requires Class
15229  * @requires finesse.FinesseBase
15230  * @requires finesse.restservices.RestBase
15231  */
15232 
15233 /** @private */
15234 define('restservices/SystemInfo',['restservices/RestBase'], function (RestBase) {
15235     
15236     var SystemInfo = RestBase.extend(/** @lends finesse.restservices.SystemInfo.prototype */{
15237         /**
15238          * @private
15239          * Returns whether this object supports subscriptions
15240          */
15241         supportsSubscriptions: false,
15242 
15243         doNotRefresh: true,
15244       
15245         /**
15246          * @class
15247          * JavaScript representation of a SystemInfo object.
15248          * 
15249          * @augments finesse.restservices.RestBase
15250          * @see finesse.restservices.SystemInfo.Statuses
15251          * @constructs
15252          */
15253         _fakeConstuctor: function () {
15254             /* This is here to hide the real init constructor from the public docs */
15255         },
15256         
15257          /**
15258          * @private
15259          * JavaScript representation of a SystemInfo object. Also exposes methods to operate
15260          * on the object against the server.
15261          *
15262          * @param {Object} options
15263          *     An object with the following properties:<ul>
15264          *         <li><b>id:</b> The id of the object being constructed</li>
15265          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
15266          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
15267          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
15268          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
15269          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
15270          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
15271          *             <li><b>content:</b> {String} Raw string of response</li>
15272          *             <li><b>object:</b> {Object} Parsed object of response</li>
15273          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
15274          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
15275          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
15276          *             </ul></li>
15277          *         </ul></li>
15278          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
15279          **/
15280         init: function (id, callbacks, restObj)
15281         {
15282             this._super(id, callbacks, restObj);
15283         },
15284 
15285         /**
15286          * @private
15287          * Gets the REST class for the current object - this is the SystemInfo object.
15288          */
15289         getRestClass: function () {
15290             return SystemInfo;
15291         },
15292 
15293         /**
15294          * @private
15295          * Gets the REST type for the current object - this is a "SystemInfo".
15296          */
15297         getRestType: function ()
15298         {
15299             return "SystemInfo";
15300         },
15301         
15302         _validate: function (obj)
15303         {
15304             return true;
15305         },
15306         
15307         /**
15308          * Returns the status of the Finesse system.
15309          *   IN_SERVICE if the Finesse API reports that it is in service,
15310          *   OUT_OF_SERVICE otherwise.
15311          * @returns {finesse.restservices.SystemInfo.Statuses} System Status
15312          */
15313         getStatus: function () {
15314             this.isLoaded();
15315             return this.getData().status;
15316         },
15317         
15318         /**
15319          * Returns the current timestamp from this SystemInfo object.
15320          *   This is used to calculate time drift delta between server and client.
15321          *  @returns {String} Time (GMT): yyyy-MM-dd'T'HH:mm:ss'Z'
15322          */
15323         getCurrentTimestamp: function () {
15324             this.isLoaded();
15325             return this.getData().currentTimestamp;
15326         },
15327         
15328         /**
15329          * Getter for the xmpp domain of the system.
15330          * @returns {String} The xmpp domain corresponding to this SystemInfo object.
15331          */
15332         getXmppDomain: function () {
15333             this.isLoaded();
15334             return this.getData().xmppDomain;
15335         },
15336         
15337         /**
15338          * Getter for the xmpp pubsub domain of the system.
15339          * @returns {String} The xmpp pubsub domain corresponding to this SystemInfo object.
15340          */
15341         getXmppPubSubDomain: function () {
15342             this.isLoaded();
15343             return this.getData().xmppPubSubDomain;
15344         },
15345 
15346         /**
15347          * Getter for the deployment type (UCCE or UCCX).
15348          * @returns {String} "UCCE" or "UCCX"
15349          */ 
15350         getDeploymentType: function () {
15351             this.isLoaded();
15352             return this.getData().deploymentType;
15353         },
15354 
15355         /**
15356          * Returns whether this is a single node deployment or not by checking for the existence of the secondary node in SystemInfo.
15357          * @returns {Boolean} True for single node deployments, false otherwise.
15358          */ 
15359         isSingleNode: function () {
15360             var secondary = this.getData().secondaryNode;
15361             if (secondary && secondary.host) {
15362                 return false;
15363             }
15364             return true;
15365         },
15366 
15367         /**
15368          * Checks all arguments against the primary and secondary hosts (FQDN) and returns the match.
15369          * This is useful for getting the FQDN of the current Finesse server.
15370          * @param {String} ...arguments[]... - any number of arguments to match against
15371          * @returns {String} FQDN (if properly configured) of the matched host of the primary or secondary node, or undefined if no match is found.
15372          */ 
15373         getThisHost: function () {
15374             var i,
15375             primary = this.getData().primaryNode,
15376             secondary = this.getData().secondaryNode;
15377 
15378             for (i = 0; (i < arguments.length); i = i + 1) {
15379                 if (primary && arguments[i] === primary.host) {
15380                     return primary.host;
15381                 } else if (secondary && arguments[i] === secondary.host) {
15382                     return secondary.host;
15383                 }
15384             }
15385         },
15386 
15387         /**
15388          * Checks all arguments against the primary and secondary hosts (FQDN) and returns the other node.
15389          * This is useful for getting the FQDN of the other Finesse server, i.e. for failover purposes.
15390          * @param {String} arguments - any number of arguments to match against
15391          * @returns {String} FQDN (if properly configured) of the alternate node, defaults to primary if no match is found, undefined for single node deployments.
15392          */ 
15393         getAlternateHost: function () {
15394             var i,
15395             isPrimary = false,
15396             primary = this.getData().primaryNode,
15397             secondary = this.getData().secondaryNode,
15398             xmppDomain = this.getData().xmppDomain,
15399             alternateHost;
15400 
15401             if (primary && primary.host) {
15402                     if (xmppDomain === primary.host) {
15403                         isPrimary = true;
15404                     }
15405                 if (secondary && secondary.host) {
15406                     if (isPrimary) {
15407                         return secondary.host;
15408                     }
15409                     return primary.host;
15410                 }
15411             }
15412         },
15413         
15414         /**
15415          * Gets the peripheral ID that Finesse is connected to. The peripheral
15416          * ID is the ID of the PG Routing Client (PIM).
15417          * 
15418          * @returns {String} The peripheral Id if UCCE, or empty string otherwise.
15419          */
15420         getPeripheralId : function () {
15421              this.isLoaded();
15422              
15423              var peripheralId = this.getData().peripheralId;
15424              if (peripheralId === null) {
15425                   return "";
15426              } else {
15427                   return this.getData().peripheralId;
15428              }
15429         },
15430 
15431          /**
15432          * Gets the license. Only apply to UCCX.
15433          * 
15434          * @returns {String} The license if UCCX, or empty string otherwise.
15435          */
15436         getlicense : function () {
15437              this.isLoaded();
15438              return this.getData().license || "";
15439         }
15440     });
15441     
15442     SystemInfo.Statuses = /** @lends finesse.restservices.SystemInfo.Statuses.prototype */ { 
15443         /** 
15444          * Finesse is in service. 
15445          */
15446         IN_SERVICE: "IN_SERVICE",
15447         /** 
15448          * Finesse is not in service. 
15449          */
15450         OUT_OF_SERVICE: "OUT_OF_SERVICE",
15451         /**
15452          * @class SystemInfo status values.
15453          * @constructs
15454          */
15455         _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
15456 
15457     };
15458     
15459     window.finesse = window.finesse || {};
15460     window.finesse.restservices = window.finesse.restservices || {};
15461     window.finesse.restservices.SystemInfo = SystemInfo;
15462     
15463     return SystemInfo;
15464 });
15465 
15466 /**
15467  * Provides standard way resolve message keys with substitution
15468  *
15469  * @requires finesse.container.I18n or gadgets.Prefs
15470  */
15471 
15472 // Add Utilities to the finesse.utilities namespace
15473 define('utilities/I18n',[], function () {
15474     var I18n = (function () {
15475 
15476         /**
15477          * Shortcut to finesse.container.I18n.getMsg or gadgets.Prefs.getMsg
15478          * @private
15479          */
15480         var _getMsg;
15481 
15482         return {
15483             /**
15484              * Provides a message resolver for this utility singleton.
15485              * @param {Function} getMsg
15486              *     A function that returns a string given a message key.
15487              *     If the key is not found, this function must return 
15488              *     something that tests false (i.e. undefined or "").
15489              */
15490             setGetter : function (getMsg) {
15491                 _getMsg = getMsg;
15492             },
15493 
15494             /**
15495              * Resolves the given message key, also performing substitution.
15496              * This generic utility will use a custom function to resolve the key
15497              * provided by finesse.utilities.I18n.setGetter. Otherwise, it will 
15498              * discover either finesse.container.I18n.getMsg or gadgets.Prefs.getMsg
15499              * upon the first invocation and store that reference for efficiency.
15500              * 
15501              * Since this will construct a new gadgets.Prefs object, it is recommended
15502              * for gadgets to explicitly provide the setter to prevent duplicate
15503              * gadgets.Prefs objects. This does not apply if your gadget does not need
15504              * access to gadgets.Prefs other than getMsg. 
15505              * 
15506              * @param {String} key
15507              *     The key to lookup
15508              * @param {String} arguments
15509              *     Arguments for substitution
15510              * @returns {String/Function}
15511              *     The resolved string if successful, otherwise a function that returns
15512              *     a '???' string that can also be casted into a string.
15513              */
15514             getString : function (key) {
15515                 var prefs, i, retStr, noMsg, getFailed = "";
15516                 if (!_getMsg) {
15517                     if (finesse.container && finesse.container.I18n) {
15518                         _getMsg = finesse.container.I18n.getMsg;
15519                     } else if (gadgets) {
15520                         prefs = new gadgets.Prefs();
15521                         _getMsg = prefs.getMsg;
15522                     }
15523                 }
15524                 
15525                 try {
15526                     retStr = _getMsg(key);
15527                 } catch (e) {
15528                     getFailed = "finesse.utilities.I18n.getString(): invalid _getMsg";
15529                 }
15530                 
15531                 if (retStr) { // Lookup was successful, perform substitution (if any)
15532                     for (i = 1; i < arguments.length; i += 1) {
15533                         retStr = retStr.replace(new RegExp("\\{" + (i - 1) + "\\}", "g"), arguments[i]);
15534                     }
15535                     //in order to fix French text with single quotes in it, we need to replace \' with '
15536                     return retStr.replace(/\\'/g, "'");
15537                 }
15538                 // We want a function because jQuery.html() and jQuery.text() is smart enough to invoke it.
15539                 /** @private */
15540                 noMsg = function () {
15541                     return "???" + key + "???" + getFailed;
15542                 };
15543                 // We overload the toString() of this "function" to allow JavaScript to cast it into a string
15544                 // For example, var myMsg = "something " + finesse.utilities.I18n.getMsg("unresolvable.key");
15545                 /** @private */
15546                 noMsg.toString = function () {
15547                     return "???" + key + "???" + getFailed;
15548                 };
15549                 return noMsg;
15550 
15551             }
15552         };
15553     }());
15554     
15555     window.finesse = window.finesse || {};
15556     window.finesse.utilities = window.finesse.utilities || {};
15557     window.finesse.utilities.I18n = I18n;
15558 
15559     return I18n;
15560 });
15561 
15562 /**
15563  * Logging.js: provides simple logging for clients to use and overrides synchronous native methods: alert(), confirm(), and prompt().
15564  * 
15565  * On Firefox, it will hook into console for logging.  On IE, it will log to the status bar. 
15566  */
15567 // Add Utilities to the finesse.utilities namespace
15568 define('utilities/Logger',[], function () {
15569     var Logger = (function () {
15570         
15571         var
15572         
15573         /** @private **/
15574         debugOn,
15575         
15576         /**
15577          * Pads a single digit number for display purposes (e.g. '4' shows as '04')
15578          * @param num is the number to pad to 2 digits
15579          * @returns a two digit padded string
15580          * @private
15581          */
15582         padTwoDigits = function (num) {        
15583             return (num < 10) ? '0' + num : num;  
15584         },
15585         
15586         /**
15587          * Checks to see if we have a console - this allows us to support Firefox or IE.
15588          * @returns {Boolean} True for Firefox, False for IE
15589          * @private
15590          */
15591         hasConsole = function () {
15592             var retval = false;
15593             try
15594             {
15595                 if (window.console !== undefined) 
15596                 {
15597                     retval = true;
15598                 }
15599             } 
15600             catch (err)
15601             {
15602                 retval = false;
15603             }
15604               
15605             return retval;
15606         },
15607         
15608         /**
15609          * Gets a timestamp.
15610          * @returns {String} is a timestamp in the following format: HH:MM:SS
15611          * @private
15612          */
15613         getTimeStamp = function () {
15614             var date = new Date(), timeStr;
15615             timeStr = padTwoDigits(date.getHours()) + ":" + padTwoDigits(date.getMinutes()) + ":" + padTwoDigits(date.getSeconds());
15616 
15617             return timeStr;
15618         };
15619         
15620         return {
15621             /**
15622              * Enable debug mode. Debug mode may impact performance on the UI.
15623              *
15624              * @param {Boolean} enable
15625              *      True to enable debug logging.
15626              * @private
15627              */
15628             setDebug : function (enable) {
15629                 debugOn = enable;
15630             },
15631             
15632             /**
15633              * Logs a string as DEBUG.
15634              * 
15635              * @param str is the string to log. 
15636              * @private
15637              */
15638             log : function (str) {
15639                 var timeStr = getTimeStamp();
15640                 
15641                 if (debugOn) {
15642                     if (hasConsole())
15643                     {
15644                         window.console.log(timeStr + ": " + "DEBUG" + " - " + str);
15645                     }
15646                 }
15647             },
15648             
15649             /**
15650              * Logs a string as INFO.
15651              * 
15652              * @param str is the string to log. 
15653              * @private
15654              */
15655             info : function (str) {
15656                 var timeStr = getTimeStamp();
15657                 
15658                 if (hasConsole())
15659                 {
15660                     window.console.info(timeStr + ": " + "INFO" + " - " + str);
15661                 }
15662             },
15663             
15664             /**
15665              * Logs a string as WARN.
15666              * 
15667              * @param str is the string to log. 
15668              * @private
15669              */
15670             warn : function (str) {
15671                 var timeStr = getTimeStamp();
15672                 
15673                 if (hasConsole())
15674                 {
15675                     window.console.warn(timeStr + ": " + "WARN" + " - " + str);
15676                 }
15677             },
15678             /**
15679              * Logs a string as ERROR.
15680              * 
15681              * @param str is the string to log. 
15682              * @private
15683              */
15684             error : function (str) {
15685                 var timeStr = getTimeStamp();
15686                 
15687                 if (hasConsole())
15688                 {
15689                     window.console.error(timeStr + ": " + "ERROR" + " - " + str);
15690                 }
15691             }
15692         };
15693     }());
15694     
15695     return Logger;
15696 });
15697 
15698 /**
15699  * Allows gadgets to call the log function to publish client logging messages over the hub.
15700  *
15701  * @requires OpenAjax
15702  */
15703 /** @private */
15704 define('cslogger/ClientLogger',[], function () {
15705 
15706     var ClientLogger = ( function () { /** @lends finesse.cslogger.ClientLogger.prototype */
15707         var _hub, _logTopic, _originId, _sessId, _host,
15708             MONTH = { 0 : "Jan", 1 : "Feb", 2 : "Mar", 3 : "Apr", 4 : "May", 5 : "Jun", 
15709                       6 : "Jul", 7 : "Aug", 8 : "Sep", 9 : "Oct", 10 : "Nov", 11 : "Dec"},
15710  
15711         /**
15712          * Gets timestamp drift stored in sessionStorage
15713          * @returns drift in seconds if it is set in sessionStorage otherwise returns undefined.
15714          * @private
15715         */
15716         getTsDrift = function() {
15717             if (window.sessionStorage.getItem('clientTimestampDrift') !== null) {
15718                 return parseInt(window.sessionStorage.getItem('clientTimestampDrift'), 10);
15719             }
15720             else { 
15721                 return undefined;
15722             }
15723         },
15724          
15725         /**
15726           * Sets timestamp drift in sessionStorage
15727           * @param delta is the timestamp drift between server.and client.
15728           * @private
15729          */
15730         setTsDrift = function(delta) {
15731              window.sessionStorage.setItem('clientTimestampDrift', delta.toString());
15732         },
15733           
15734         /**
15735          * Gets Finesse server timezone offset from GMT in seconds 
15736          * @returns offset in seconds if it is set in sessionStorage otherwise returns undefined.
15737          * @private
15738         */
15739         getServerOffset = function() {
15740             if (window.sessionStorage.getItem('serverTimezoneOffset') !== null) {
15741                 return parseInt(window.sessionStorage.getItem('serverTimezoneOffset'), 10);
15742             }
15743             else { 
15744                 return undefined;
15745             }
15746         },
15747          
15748         /**
15749           * Sets server timezone offset 
15750           * @param sec is the server timezone GMT offset in seconds.
15751           * @private
15752          */
15753         setServerOffset = function(sec) {
15754              window.sessionStorage.setItem('serverTimezoneOffset', sec.toString());
15755         },
15756  
15757         /**
15758          * Checks to see if we have a console.
15759          * @returns Whether the console object exists.
15760          * @private
15761          */
15762         hasConsole = function () {
15763             try {
15764                 if (window.console !== undefined) {
15765                     return true;
15766                 }
15767             } 
15768             catch (err) {
15769               // ignore and return false
15770             }
15771     
15772             return false;
15773         },
15774         
15775         /**
15776          * Gets a short form (6 character) session ID from sessionStorage
15777          * @private
15778         */
15779         getSessId = function() {
15780             if (!_sessId) {
15781                //when _sessId not defined yet, initiate it
15782                if (window.sessionStorage.getItem('enableLocalLog') === 'true') {
15783                   _sessId= " "+window.sessionStorage.getItem('finSessKey');
15784                }
15785                else {
15786                   _sessId=" ";
15787                }
15788             }
15789             return _sessId;
15790          },
15791 
15792         /**
15793          * Pads a single digit number for display purposes (e.g. '4' shows as '04')
15794          * @param num is the number to pad to 2 digits
15795          * @returns a two digit padded string
15796          * @private
15797          */
15798         padTwoDigits = function (num)
15799         {
15800             return (num < 10) ? '0' + num : num;
15801         },
15802         
15803         /**
15804          * Pads a single digit number for display purposes (e.g. '4' shows as '004')
15805          * @param num is the number to pad to 3 digits
15806          * @returns a three digit padded string
15807          * @private
15808          */
15809         padThreeDigits = function (num)
15810         {
15811             if (num < 10)
15812             {
15813               return '00'+num;
15814             }
15815             else if (num < 100)
15816             {
15817               return '0'+num;
15818             }
15819             else  
15820             {
15821                return num;
15822             }
15823         },
15824               
15825         /**
15826          * Compute the "hour"
15827          * 
15828          * @param s is time in seconds
15829          * @returns {String} which is the hour
15830          * @private
15831          */
15832         ho = function (s) {
15833              return ((s/60).toString()).split(".")[0];
15834         },
15835           
15836         /**
15837          * Gets local timezone offset string.
15838          * 
15839          * @param t is the time in seconds
15840          * @param s is the separator character between hours and minutes, e.g. ':'
15841          * @returns {String} is local timezone GMT offset in the following format: [+|-]hh[|:]MM
15842          * @private
15843          */
15844         getGmtOffString = function (min,s) {
15845             var t, sign;
15846             if (min<0) {
15847                t = -min;
15848                sign = "-";
15849             }
15850             else {
15851                t = min;
15852                sign = "+";
15853             }
15854             
15855             if (s===':') {
15856                 return sign+padTwoDigits(ho(t))+s+padTwoDigits(t%60);
15857             }
15858             else {
15859                 return sign+padTwoDigits(ho(t))+padTwoDigits(t%60);
15860             }    
15861         },
15862 
15863         /**
15864          * Gets short form of a month name in English 
15865          * 
15866          * @param monthNum is zero-based month number 
15867          * @returns {String} is short form of month name in English
15868          * @private
15869          */
15870         getMonthShortStr = function (monthNum) {
15871             var result;
15872             try {
15873                 result = MONTH[monthNum];
15874             } 
15875             catch (err) {
15876                 if (hasConsole()) {
15877                     window.console.log("Month must be between 0 and 11");
15878                 }
15879             }
15880             return result;
15881         },
15882           
15883         /**
15884           * Gets a timestamp.
15885           * @param aDate is a javascript Date object
15886           * @returns {String} is a timestamp in the following format: yyyy-mm-ddTHH:MM:ss.SSS [+|-]HH:MM
15887           * @private
15888           */
15889         getDateTimeStamp = function (aDate)
15890         {
15891             var date, off, timeStr;
15892             if (aDate === null) {
15893                 date = new Date();
15894             }
15895             else {
15896                 date = aDate;
15897             }
15898             off = -1*date.getTimezoneOffset();
15899             timeStr = date.getFullYear().toString() + "-" +
15900                       padTwoDigits(date.getMonth()+1) + "-" +
15901                       padTwoDigits (date.getDate()) + "T"+
15902                       padTwoDigits(date.getHours()) + ":" + 
15903                       padTwoDigits(date.getMinutes()) + ":" +
15904                       padTwoDigits(date.getSeconds())+"." + 
15905                       padThreeDigits(date.getMilliseconds()) + " "+
15906                       getGmtOffString(off, ':');
15907     
15908             return timeStr;
15909         },
15910         
15911         /**
15912          * Gets drift-adjusted timestamp.
15913          * @param aTimestamp is a timestamp in milliseconds
15914          * @param drift is a timestamp drift in milliseconds
15915          * @param serverOffset is a timezone GMT offset in minutes
15916          * @returns {String} is a timestamp in the Finesse server log format, e.g. Jan 07 2104 HH:MM:ss.SSS -0500
15917          * @private
15918          */
15919         getDriftedDateTimeStamp = function (aTimestamp, drift, serverOffset)
15920         {
15921            var date, timeStr, localOffset;
15922            if (aTimestamp === null) {
15923                return "--- -- ---- --:--:--.--- -----";
15924            }
15925            else if (drift === undefined || serverOffset === undefined) {
15926                if (hasConsole()) {
15927                    window.console.log("drift or serverOffset must be a number");
15928                }
15929                return "--- -- ---- --:--:--.--- -----";
15930            }
15931            else {
15932                //need to get a zone diff in minutes
15933                localOffset = (new Date()).getTimezoneOffset();
15934                date = new Date(aTimestamp+drift+(localOffset+serverOffset)*60000);
15935                timeStr = getMonthShortStr(date.getMonth()) + " "+
15936                          padTwoDigits (date.getDate())+ " "+
15937                          date.getFullYear().toString() + " "+
15938                          padTwoDigits(date.getHours()) + ":" + 
15939                          padTwoDigits(date.getMinutes()) + ":" +
15940                          padTwoDigits(date.getSeconds())+"." + 
15941                          padThreeDigits(date.getMilliseconds())+" "+
15942                          getGmtOffString(serverOffset, '');
15943                 return timeStr;
15944             }
15945         },
15946     
15947         /**
15948         * Logs a message to a hidden textarea element on the page
15949         *
15950         * @param msg is the string to log.
15951         * @private
15952         */
15953         writeToLogOutput = function (msg) {
15954             var logOutput = document.getElementById("finesseLogOutput");
15955     
15956             if (logOutput === null)
15957             {
15958                 logOutput = document.createElement("textarea");
15959                 logOutput.id = "finesseLogOutput";
15960                 logOutput.style.display = "none";
15961                 document.body.appendChild(logOutput);
15962             }
15963     
15964             if (logOutput.value === "")
15965             {
15966                 logOutput.value = msg;
15967             }
15968             else
15969             {
15970                 logOutput.value = logOutput.value + "\n" + msg;
15971             }
15972         },
15973 
15974         /*
15975          * Logs a message to console 
15976         * @param str is the string to log.         * @private
15977          */
15978         logToConsole = function (str)
15979         {
15980             var now, msg, timeStr, driftedTimeStr, sessKey=getSessId();
15981             now = new Date();
15982             timeStr = getDateTimeStamp(now);
15983             if (getTsDrift() !== undefined) {
15984                 driftedTimeStr = getDriftedDateTimeStamp(now.getTime(), getTsDrift(), getServerOffset());
15985             }
15986             else {
15987                driftedTimeStr = getDriftedDateTimeStamp(null, 0, 0);
15988             }
15989             msg = timeStr + ":"+sessKey+": "+ _host + ": "+driftedTimeStr+ ": " + str;
15990             // Log to console
15991             if (hasConsole()) {
15992                 window.console.log(msg);
15993             }
15994     
15995             //Uncomment to print logs to hidden textarea.
15996             //writeToLogOutput(msg);
15997     
15998             return msg;
15999         };
16000         return {
16001     
16002             /**
16003              * Publishes a Log Message over the hub.
16004              *
16005              * @param {String} message
16006              *     The string to log.
16007              * @example
16008              * _clientLogger.log("This is some important message for MyGadget");
16009              * 
16010              */
16011             log : function (message) {
16012                 if(_hub) {
16013                     _hub.publish(_logTopic, logToConsole(_originId + message));
16014                 }
16015             },
16016             
16017             /**
16018              * @class
16019              * Allows gadgets to call the log function to publish client logging messages over the hub.
16020              * 
16021              * @constructs
16022              */
16023             _fakeConstuctor: function () {
16024                 /* This is here so we can document init() as a method rather than as a constructor. */
16025             },
16026             
16027             /**
16028              * Initiates the client logger with a hub a gadgetId and gadget's config object.
16029              * @param {Object} hub
16030              *      The hub to communicate with.
16031              * @param {String} gadgetId
16032              *      A unique string to identify which gadget is doing the logging.
16033              * @param {finesse.gadget.Config} config
16034              *      The config object used to get host name for that thirdparty gadget
16035              * @example
16036              * var _clientLogger = finesse.cslogger.ClientLogger;
16037              * _clientLogger.init(gadgets.Hub, "MyGadgetId", config);
16038              * 
16039              */
16040             init: function (hub, gadgetId, config) {
16041                 _hub = hub;
16042                 _logTopic = "finesse.clientLogging." + gadgetId;
16043                 _originId = gadgetId + " : ";
16044                 if ((config === undefined) || (config === "undefined")) 
16045                 {
16046                      _host = ((finesse.container && finesse.container.Config && finesse.container.Config.host)?finesse.container.Config.host : "?.?.?.?");
16047                  } 
16048                 else 
16049                 {
16050                      _host = ((config && config.host)?config.host : "?.?.?.?");
16051                  }
16052             }
16053         };
16054     }());
16055     
16056     window.finesse = window.finesse || {};
16057     window.finesse.cslogger = window.finesse.cslogger || {};
16058     window.finesse.cslogger.ClientLogger = ClientLogger;
16059     
16060     finesse = finesse || {};
16061     /** @namespace Supports writing messages to a central log. */
16062     finesse.cslogger = finesse.cslogger || {};
16063 
16064     return ClientLogger;
16065 });
16066 
16067 /* using variables before they are defined.
16068  */
16069 /*global navigator,unescape,sessionStorage,localStorage,_initSessionList,_initSessionListComplete */
16070 
16071 /**
16072  * Allows each gadget to communicate with the server to send logs.
16073  */
16074 
16075 /**
16076  * @class
16077  * @private
16078  * Allows each product to initialize its method of storage
16079  */
16080 define('cslogger/FinesseLogger',["clientservices/ClientServices", "utilities/Utilities"], function (ClientServices, Utilities) {
16081     
16082     var FinesseLogger = (function () { 
16083 
16084         var
16085 
16086         /**
16087          * Array use to collect ongoing logs in memory
16088          * @private
16089          */
16090         _logArray = [],
16091 
16092         /**
16093          * The final data string sent to the server, =_logArray.join
16094          * @private
16095          */
16096         _logStr = "",
16097 
16098         /**
16099          * Keep track of size of log
16100          * @private
16101          */
16102         _logSize = 0,
16103 
16104         /**
16105          * Flag to keep track show/hide of send log link
16106          * @private
16107          */
16108         _sendLogShown = false,
16109 
16110         /**
16111          * Flag to keep track if local log initialized
16112          * @private
16113          */
16114         _loggingInitialized = false,
16115         
16116 
16117         /**
16118          * local log size limit
16119          * @private
16120          */
16121         _maxLocalStorageSize = 5000000,
16122 
16123         /**
16124          * half local log size limit
16125          * @private
16126          */
16127         _halfMaxLocalStorageSize = 0.5*_maxLocalStorageSize,
16128 
16129         
16130         /**
16131          * threshold for purge 
16132          * @private
16133          */
16134         _purgeStartPercent = 0.75,
16135         
16136         /**
16137          * log item prefix 
16138          * @private
16139          */
16140         _linePrefix = null,
16141         
16142         /**
16143          * locallog session 
16144          * @private
16145          */
16146         _session = null,
16147         
16148         /**
16149          * Flag to keep track show/hide of send log link
16150          * @private
16151          */
16152         _sessionKey = null,
16153         /**
16154          * Log session metadata 
16155          * @private
16156          */
16157         _logInfo = {},
16158         
16159         /**
16160          * Flag to find sessions 
16161          * @private
16162          */
16163         _findSessionsObj = null,
16164 
16165         /**
16166          * Wrap up console.log esp. for IE9 
16167          * @private
16168          */
16169         _myConsoleLog = function (str) {
16170             if (window.console !== undefined) {
16171               window.console.log(str);
16172             }
16173         },
16174         /**
16175          * Initialize the Local Logging
16176          * @private
16177          */
16178         _initLogging = function () {
16179             if (_loggingInitialized) {
16180                 return;
16181             }
16182             //Build a new store
16183             _session = sessionStorage.getItem("finSessKey");
16184             //if the _session is null or empty, skip the init
16185             if (!_session) {
16186               return;
16187             }
16188             _sessionKey = "Fi"+_session;
16189             _linePrefix = _sessionKey + "_";
16190             _logInfo = {};
16191             _logInfo.name = _session;
16192             _logInfo.size = 0;
16193             _logInfo.head = 0;
16194             _logInfo.tail = 0;
16195             _logInfo.startTime = new Date().getTime();
16196             _loggingInitialized = true;
16197             _initSessionList();
16198         },
16199         
16200         /**
16201          * get total data size 
16202          *
16203          * @return {Integer} which is the amount of data stored in local storage.
16204          * @private
16205          */
16206         _getTotalData = function ()
16207         {
16208             var sessName, sessLogInfoStr,sessLogInfoObj, sessionsInfoObj, totalData = 0,
16209             sessionsInfoStr = localStorage.getItem("FinesseSessionsInfo");
16210             if (!sessionsInfoStr) {
16211                  return 0;
16212             }
16213             sessionsInfoObj = JSON.parse(sessionsInfoStr);
16214 
16215             for (sessName in sessionsInfoObj.sessions)
16216             {
16217                 if (sessionsInfoObj.sessions.hasOwnProperty(sessName)) {
16218                     sessLogInfoStr = localStorage.getItem("Fi" + sessName);
16219                     if (!sessLogInfoStr) {
16220                         _myConsoleLog("_getTotalData failed to get log info for "+sessName);
16221                     }
16222                     else {
16223                        sessLogInfoObj = JSON.parse(sessLogInfoStr);
16224                        totalData = totalData + sessLogInfoObj.size;
16225                     }
16226                 }
16227             }
16228 
16229               return totalData;
16230         },
16231         
16232         /**
16233          * Remove lines from tail up until store size decreases to half of max size limit.
16234          *
16235          * @private
16236          */
16237         _purgeCurrentSession = function() {
16238             var curStoreSize, purgedSize=0, line, tailKey, secLogInfoStr, logInfoStr, theLogInfo;
16239             curStoreSize = _getTotalData();
16240             if (curStoreSize < _halfMaxLocalStorageSize) {
16241                return;
16242             }
16243             logInfoStr = localStorage.getItem(_sessionKey);
16244             if (!logInfoStr) {
16245                return;
16246             }
16247             theLogInfo = JSON.parse(logInfoStr);
16248             //_myConsoleLog("Starting _purgeCurrentSession() - currentStoreSize=" + curStoreSize);
16249             while(curStoreSize > _halfMaxLocalStorageSize) {
16250                try {
16251                    tailKey = _sessionKey+"_"+theLogInfo.tail;
16252                    line = localStorage.getItem(tailKey);
16253                    if (line) {
16254                        purgedSize = purgedSize +line.length;
16255                        localStorage.removeItem(tailKey);
16256                        curStoreSize = curStoreSize - line.length;
16257                        theLogInfo.size = theLogInfo.size - line.length;
16258                    }
16259                }
16260                catch (err) {
16261                    _myConsoleLog("purgeCurrentSession encountered err="+err);
16262                }
16263                if (theLogInfo.tail < theLogInfo.head) {
16264                    theLogInfo.tail = theLogInfo.tail  + 1;
16265                }
16266                else {
16267                    break;
16268                }
16269             }
16270             //purge stops here, we need to update session's meta data in storage
16271             secLogInfoStr = localStorage.getItem(_sessionKey);
16272             if (!secLogInfoStr) {
16273                 //somebody cleared the localStorage
16274                 return;
16275             }
16276             
16277             //_myConsoleLog("In _purgeCurrentSession() - after purging current session, currentStoreSize=" + curStoreSize);
16278             //_myConsoleLog("In _purgeCurrentSession() - after purging purgedSize=" + purgedSize);
16279             //_myConsoleLog("In _purgeCurrentSession() - after purging logInfo.size=" + theLogInfo.size);
16280             //_myConsoleLog("In _purgeCurrentSession() - after purging logInfo.tail=" + theLogInfo.tail);
16281             localStorage.setItem(_sessionKey, JSON.stringify(theLogInfo));
16282             _myConsoleLog("Done _purgeCurrentSession() - currentStoreSize=" + curStoreSize);
16283         },
16284        
16285         /**
16286          * Purge a session 
16287          *
16288          * @param sessionName is the name of the session
16289          * @return {Integer} which is the current amount of data purged
16290          * @private
16291          */
16292         _purgeSession = function (sessionName) {
16293               var theLogInfo, logInfoStr, sessionsInfoStr, sessionsInfoObj;
16294               //Get the session logInfo
16295               logInfoStr = localStorage.getItem("Fi" + sessionName);
16296               if (!logInfoStr) {
16297                  _myConsoleLog("_purgeSession failed to get logInfo for "+sessionName);
16298                  return 0;
16299               }
16300               theLogInfo = JSON.parse(logInfoStr);
16301               
16302               //Note: This assumes that we don't crash in the middle of purging
16303               //=> if we do then it should get deleted next time
16304               //Purge tail->head
16305               while (theLogInfo.tail <= theLogInfo.head)
16306               {
16307                   try {
16308                       localStorage.removeItem("Fi" + sessionName + "_" + theLogInfo.tail);
16309                       theLogInfo.tail = theLogInfo.tail + 1;
16310                   }
16311                   catch (err) {
16312                       _myConsoleLog("In _purgeSession err="+err);
16313                       break;
16314                   }
16315               }
16316 
16317               //Remove the entire session
16318               localStorage.removeItem("Fi" + sessionName);
16319 
16320               //Update FinesseSessionsInfo
16321               sessionsInfoStr = localStorage.getItem("FinesseSessionsInfo");
16322               if (!sessionsInfoStr) {
16323                  _myConsoleLog("_purgeSession could not get sessions Info, it was cleared?");
16324                  return 0;
16325               }
16326               sessionsInfoObj = JSON.parse(sessionsInfoStr);
16327               if (sessionsInfoObj.sessions !== null)
16328               {
16329                  delete sessionsInfoObj.sessions[sessionName];
16330               
16331                  sessionsInfoObj.total = sessionsInfoObj.total - 1;
16332                  sessionsInfoObj.lastWrittenBy = _session;
16333                  localStorage.setItem("FinesseSessionsInfo", JSON.stringify(sessionsInfoObj));
16334               }
16335               
16336               return theLogInfo.size;
16337         },
16338         
16339          /**
16340           * purge old sessions
16341           * 
16342           * @param storeSize
16343 	  * @return {Boolean} whether purging reaches its target
16344           * @private
16345          */
16346          _purgeOldSessions = function (storeSize) {
16347              var sessionsInfoStr, purgedSize = 0, sessName, sessions, curStoreSize, activeSession, sessionsInfoObj;
16348              sessionsInfoStr = localStorage.getItem("FinesseSessionsInfo");
16349              if (!sessionsInfoStr) {
16350                 _myConsoleLog("Could not get FinesseSessionsInfo");
16351                 return true;
16352              }
16353              sessionsInfoObj = JSON.parse(sessionsInfoStr);
16354              curStoreSize = _getTotalData();
16355              
16356              activeSession = _session;
16357              sessions = sessionsInfoObj.sessions;
16358              for (sessName in sessions) {
16359                 if (sessions.hasOwnProperty(sessName)) {
16360                     if (sessName !== activeSession) {
16361                         purgedSize = purgedSize + _purgeSession(sessName);
16362                         if ((curStoreSize-purgedSize) < _halfMaxLocalStorageSize) {
16363                             return true;
16364                         }
16365                     }
16366                 }
16367              }
16368             //purge is not done, so return false
16369             return false;
16370          },
16371          
16372        /**
16373         * handle insert error
16374         *
16375         * @param error
16376         * @private
16377         */
16378         _insertLineHandleError = function (error) {
16379             _myConsoleLog(error);
16380         },
16381 
16382         /**
16383          * check storage data size and if need purge
16384          * @private
16385          */
16386         _checkSizeAndPurge = function () {
16387             var purgeIsDone=false, totalSize = _getTotalData();
16388             if (totalSize > 0.75*_maxLocalStorageSize) {
16389                _myConsoleLog("in _checkSizeAndPurge, totalSize ("+totalSize+") exceeds limit");
16390                purgeIsDone = _purgeOldSessions(totalSize);
16391                if (purgeIsDone) {
16392                   _myConsoleLog("in _checkSizeAndPurge after purging old session, purge is done");
16393                }
16394                else {
16395                   //after all old sessions purged, still need purge
16396                   totalSize = _getTotalData();
16397                   if (totalSize > 0.75*_maxLocalStorageSize) {
16398                       _myConsoleLog("in _checkSizeAndPurge after purging old session,still needs purging, now storeSize ("+totalSize+")");
16399                      _purgeCurrentSession();
16400                      _myConsoleLog("in _checkSizeAndPurge done purging current session.");
16401                   }
16402                }
16403             }
16404         },
16405         
16406         /**
16407          * check if the session is already in meta data  
16408          * 
16409          * @param metaData
16410          * @param sessionName
16411          * @return {Boolean} true if session has metaData (false otherwise)
16412          * @private
16413          */
16414         _sessionsInfoContains = function (metaData, sessionName) {
16415            if (metaData && metaData.sessions && metaData.sessions.hasOwnProperty(sessionName)) {
16416               return true;
16417            }
16418            return false;
16419         },
16420         
16421         
16422         /**
16423          * setup sessions in local storage 
16424          * 
16425          * @param logInfo
16426          * @private
16427          */
16428         _getAndSetNumberOfSessions = function (logInfo) {
16429             var numOfSessionsPass1, numOfSessionsPass2, l;
16430             numOfSessionsPass1 = localStorage.getItem("FinesseSessionsInfo");
16431             if (numOfSessionsPass1 === null) {
16432                 //Init first time
16433                 numOfSessionsPass1 = {};
16434                 numOfSessionsPass1.total = 1;
16435                 numOfSessionsPass1.sessions = {};
16436                 numOfSessionsPass1.sessions[logInfo.name] = logInfo.startTime;
16437                 numOfSessionsPass1.lastWrittenBy = logInfo.name;
16438                 localStorage.setItem("FinesseSessionsInfo", JSON.stringify(numOfSessionsPass1));
16439             }
16440             else {
16441                 numOfSessionsPass1 = JSON.parse(numOfSessionsPass1);
16442                 //check if the session is already in the FinesseSessionSInfo
16443                 if (_sessionsInfoContains(numOfSessionsPass1, logInfo.name)) {
16444                     return;
16445                 }             
16446                 //Save numOfSessionsPass1
16447                 numOfSessionsPass1.total = parseInt(numOfSessionsPass1.total, 10) + 1;
16448                 numOfSessionsPass1.sessions[logInfo.name] = logInfo.startTime;
16449                 numOfSessionsPass1.lastWrittenBy = logInfo.name;
16450                 localStorage.setItem("FinesseSessionsInfo", JSON.stringify(numOfSessionsPass1));
16451                 numOfSessionsPass2 = localStorage.getItem("FinesseSessionsInfo");
16452                 if (!numOfSessionsPass2) {
16453                    _myConsoleLog("Could not get FinesseSessionsInfo");
16454                    return;
16455                 }
16456                 numOfSessionsPass2 = JSON.parse(numOfSessionsPass2);
16457                 //in future we need to confirm the numOfSessionsPass2 is the same as numOfSessionsPass1
16458                 ////if (numOfSessionsPass1.lastWrittenBy !== numOfSessionsPass2.lastWrittenBy) {
16459                 ////    _myConsoleLog("Rebuild sessions");
16460                 ////    _sessionTimerId = setTimeout(_initSessionList, 10000);
16461                 ////}
16462                 ////else {
16463                 ////    _sessionTimerId = null;
16464                 ////callback(numOfSessionsPass2.sessions);
16465                 ////}
16466             }
16467             if (!localStorage.getItem(_sessionKey)) {
16468                 localStorage.setItem(_sessionKey, JSON.stringify(_logInfo));
16469             }
16470         },
16471         
16472         
16473         /**
16474          * init session list 
16475          * @private
16476          */
16477         _initSessionList = function () {
16478             _getAndSetNumberOfSessions(_logInfo);
16479         },
16480         
16481        /**
16482         * do the real store of log line
16483         * 
16484         * @param line
16485         * @private
16486         */
16487         _persistLine = function (line) {
16488             var key, logInfoStr;
16489             logInfoStr = localStorage.getItem(_sessionKey);
16490             if (logInfoStr === null) {
16491                return;
16492             }
16493             _logInfo = JSON.parse(logInfoStr);
16494             _logInfo.head = _logInfo.head + 1;
16495             key = _linePrefix + _logInfo.head;
16496             localStorage.setItem(key, line);
16497             //Save the size
16498             _logInfo.size = _logInfo.size + line.length;
16499             if (_logInfo.tail === 0) {
16500                 _logInfo.tail = _logInfo.head;
16501             }
16502         
16503             localStorage.setItem(_sessionKey, JSON.stringify(_logInfo));
16504             _checkSizeAndPurge();
16505         },
16506         
16507         /**
16508          * Insert a line into the localStorage.
16509          *
16510          * @param line line to be inserted 
16511          * @private
16512         */
16513         _insertLine = function (line) {
16514             //_myConsoleLog("_insertLine: [" + line + "]");
16515             //Write the next line to localStorage
16516             try {
16517                //Persist the line 
16518                _persistLine(line);
16519             }
16520             catch (err) {
16521                _myConsoleLog("error in _insertLine(), err="+err);
16522                //_insertLineHandleError(err);
16523             }
16524         },
16525          
16526         
16527         /**
16528          * Clear the local storage
16529          * @private
16530          */
16531         _clearLocalStorage = function() {
16532             localStorage.clear();
16533 
16534         },
16535 
16536         /**
16537          * Collect logs when onCollect called
16538          *
16539          * @param data
16540          * @private
16541          */
16542         _collectMethod = function(data) {
16543           //Size of log should not exceed 1.5MB
16544           var info, maxLength = 1572864;
16545           
16546           //add size buffer equal to the size of info to be added when publish
16547           info = Utilities.getSanitizedUserAgentString() + "
";
16548           info = escape(info);
16549 
16550             //If log was empty previously, fade in buttons
16551             if (!_sendLogShown) {
16552                 //call the fadeInSendLog() in Footer
16553                 finesse.modules.Footer.sendLogAppear();
16554                 _sendLogShown = true;
16555                 _logSize = info.length;
16556             }
16557             
16558             //if local storage logging is enabled, then insert the log into local storage
16559             if (window.sessionStorage.getItem('enableLocalLog')==='true') {
16560                 if (data) {
16561                    if (data.length>0 && data.substring(0,1) === '\n') {
16562                       _insertLine(data.substring(1));
16563                    }
16564                    else {
16565                       _insertLine(data);
16566                    }
16567                 }
16568             }
16569               
16570             //escape all data to get accurate size (shindig will escape when it builds request)
16571             //escape 6 special chars for XML: &<>"'\n
16572             data = data.replace(/&/g, "&").replace(/"/g, """).replace(/'/g, "'").replace(/>/g, ">").replace(/</g, "<").replace(/\n/g, "
");
16573             data = escape(data+"\n");
16574 
16575             if (data.length < maxLength){
16576                 //make room for new data if log is exceeding max length
16577                 while (_logSize + data.length > maxLength) {
16578                     _logSize -= (_logArray.shift()).length;
16579                 }
16580             }
16581 
16582             //Else push the log into memory, increment the log size
16583             _logArray.push(data);
16584 
16585             //inc the size accordingly
16586             _logSize+=data.length;
16587 
16588         };
16589 
16590         return {
16591 
16592             /**
16593              * @private
16594              * Initiate FinesseLogger.
16595              */
16596             init: function () {
16597                 ClientServices.subscribe("finesse.clientLogging.*", _collectMethod);
16598                 _initLogging();
16599             },
16600 
16601             /**
16602              * @private
16603              * Clear all items stored in localStorage.
16604             */
16605             clear : function () {
16606                _clearLocalStorage();
16607             },
16608 
16609             /**
16610              * @private
16611              * Initialize the local storage logging.
16612             */
16613             initLocalLog: function () {
16614                _initLogging();
16615             },
16616 
16617             /**
16618              * @private
16619              * Inserts a line into the localStorage.
16620              * @param line to insert
16621             */
16622             localLog : function (line) {
16623                _insertLine(line);
16624             },
16625 
16626            /**
16627             * @ignore
16628             * Publish logs to server and clear the memory
16629             *
16630             * @param userObj
16631             * @param options
16632             * @param callBack
16633             */
16634             publish: function(userObj, options, callBack) {
16635                 // Avoid null references.
16636                 options = options || {};
16637                 callBack = callBack || {};
16638 
16639                 if (callBack.sending === "function") {
16640                     callBack.sending();
16641                 }
16642 
16643                 //logs the basic version and machine info and escaped new line
16644                 _logStr = Utilities.getSanitizedUserAgentString() + "
";
16645                 
16646                 //join the logs to correct string format
16647                 _logStr += unescape(_logArray.join(""));
16648 
16649                 //turning log string to JSON obj
16650                 var logObj = {
16651                         ClientLog: {
16652                         logData : _logStr //_logStr
16653                     }
16654                 },
16655                 tmpOnAdd = (options.onAdd && typeof options.onAdd === "function")? options.onAdd : function(){};
16656                 /** @private */
16657                 options.onAdd = function(){
16658                     tmpOnAdd();
16659                     _logArray.length = 0; _logSize =0;
16660                     _sendLogShown = false;
16661                     };
16662                 //adding onLoad to the callbacks, this is the subscribe success case for the first time user subscribe to the client log node
16663                 /** @private */
16664                 options.onLoad = function (clientLogObj) {
16665                     clientLogObj.sendLogs(logObj,{
16666                             error: callBack.error
16667                         });
16668                     };
16669 
16670                 userObj.getClientLog(options);
16671             }
16672         };
16673     }());
16674 
16675     window.finesse = window.finesse || {};
16676     window.finesse.cslogger = window.finesse.cslogger || {};
16677     /** @private */
16678     window.finesse.cslogger.FinesseLogger = FinesseLogger;
16679 
16680     return FinesseLogger;
16681 });
16682 
16683 /**
16684  *  Contains a list of topics used for containerservices pubsub.
16685  *
16686  */
16687 
16688 /**
16689  * @class
16690  * Contains a list of topics with some utility functions.
16691  */
16692 /** @private */
16693 define('containerservices/Topics',[], function () {
16694 
16695     var Topics = (function () {
16696 
16697     /**
16698      * The namespace prepended to all Finesse topics.
16699      */
16700     this.namespace = "finesse.containerservices";
16701 
16702     /**
16703      * @private
16704      * Gets the full topic name with the ContainerServices namespace prepended.
16705      * @param {String} topic
16706      *     The topic category.
16707      * @returns {String}
16708      *     The full topic name with prepended namespace.
16709      */
16710     var _getNSTopic = function (topic) {
16711         return this.namespace + "." + topic;
16712     };
16713 
16714 
16715 
16716     /** @scope finesse.containerservices.Topics */
16717     return {
16718         /** 
16719          * @private
16720          * request channel. */
16721         REQUESTS: _getNSTopic("requests"),
16722 
16723         /** 
16724          * @private
16725          * reload gadget channel. */
16726         RELOAD_GADGET: _getNSTopic("reloadGadget"),
16727 
16728         /**
16729          * @private
16730          * Convert a Finesse REST URI to a OpenAjax compatible topic name.
16731          */
16732         getTopic: function (restUri) {
16733             //The topic should not start with '/' else it will get replaced with
16734             //'.' which is invalid.
16735             //Thus, remove '/' if it is at the beginning of the string
16736             if (restUri.indexOf('/') === 0) {
16737                 restUri = restUri.substr(1);
16738             }
16739 
16740             //Replace every instance of "/" with ".". This is done to follow the
16741             //OpenAjaxHub topic name convention.
16742             return restUri.replace(/\//g, ".");
16743         }
16744     };
16745 	}());
16746 	
16747 	window.finesse = window.finesse || {};
16748     window.finesse.containerservices = window.finesse.containerservices || {};
16749     window.finesse.containerservices.Topics = Topics;
16750     
16751     /** @namespace JavaScript class objects and methods to handle gadget container services.*/
16752     finesse.containerservices = finesse.containerservices || {};
16753 
16754     return Topics;
16755  });
16756 
16757 /** The following comment is to prevent jslint errors about 
16758  * using variables before they are defined.
16759  */
16760 /*global finesse*/
16761 
16762 /**
16763  * Per containerservices request, publish to the OpenAjax gadget pubsub infrastructure.
16764  *
16765  * @requires OpenAjax, finesse.containerservices.Topics
16766  */
16767 
16768 /** @private */
16769 define('containerservices/MasterPublisher',[
16770     "utilities/Utilities",
16771     "containerservices/Topics"
16772 ],
16773 function (Utilities, Topics) {
16774 
16775     var MasterPublisher = function () {
16776 
16777     var
16778     
16779     /**
16780      * Reference to the gadget pubsub Hub instance.
16781      * @private
16782      */
16783     _hub = gadgets.Hub,
16784 
16785     /**
16786      * Reference to the Topics class.
16787      * @private
16788      */
16789     _topics = Topics,
16790     
16791     /**
16792      * Reference to conversion utilities class.
16793      * @private
16794      */
16795     _utils = Utilities,
16796     
16797     /**
16798      * References to ClientServices logger methods
16799      * @private
16800      */
16801     _logger = {
16802         log: finesse.clientservices.ClientServices.log
16803     },
16804     
16805    /**
16806      * The types of possible request types supported when listening to the
16807      * requests channel. Each request type could result in different operations.
16808      * @private
16809      */
16810     _REQTYPES = {
16811 		ACTIVETAB: "ActiveTabReq",
16812 		SET_ACTIVETAB: "SetActiveTabReq",
16813         RELOAD_GADGET: "ReloadGadgetReq"
16814     },
16815 
16816     /**
16817      * Handles client requests made to the request topic. The type of the
16818      * request is described in the "type" property within the data payload. Each
16819      * type can result in a different operation.
16820      * @param {String} topic
16821      *     The topic which data was published to.
16822      * @param {Object} data
16823      *     The data containing requests information published by clients.
16824      * @param {String} data.type
16825      *     The type of the request. Supported: "ActiveTabReq", "SetActiveTabReq", "ReloadGadgetReq"
16826      * @param {Object} data.data
16827      *     May contain data relevant for the particular requests.
16828      * @param {String} [data.invokeID]
16829      *     The ID used to identify the request with the response. The invoke ID
16830      *     will be included in the data in the publish to the topic. It is the
16831      *     responsibility of the client to correlate the published data to the
16832      *     request made by using the invoke ID.
16833      * @private
16834      */
16835     _clientRequestHandler = function (topic, data) {
16836     
16837         //Ensure a valid data object with "type" and "data" properties.
16838         if (typeof data === "object" &&
16839                 typeof data.type === "string" &&
16840                 typeof data.data === "object") {
16841 			switch (data.type) {
16842 			case _REQTYPES.ACTIVETAB:
16843                 _hub.publish("finesse.containerservices.activeTab", finesse.container.Tabs.getActiveTab());
16844                 break;
16845             case _REQTYPES.SET_ACTIVETAB:
16846                 if (typeof data.data.id === "string") {
16847                     _logger.log("Handling request to activate tab: " + data.data.id);
16848                     if (!finesse.container.Tabs.activateTab(data.data.id)) {
16849                         _logger.log("No tab found with id: " + data.data.id);
16850                     }
16851                 }
16852                 break;
16853             case _REQTYPES.RELOAD_GADGET:
16854                 _hub.publish("finesse.containerservices.reloadGadget", data.data);
16855                 break;
16856 			default:
16857 				break;
16858 			}
16859         }
16860     };
16861 
16862     (function () {
16863 
16864         //Listen to a request channel to respond to any requests made by other
16865         //clients because the Master may have access to useful information.
16866         _hub.subscribe(_topics.REQUESTS, _clientRequestHandler);
16867     }());
16868 
16869     //BEGIN TEST CODE//
16870     /**
16871      * Test code added to expose private functions that are used by unit test
16872      * framework. This section of code is removed during the build process
16873      * before packaging production code. The [begin|end]TestSection are used
16874      * by the build to identify the section to strip.
16875      * @ignore
16876      */
16877     this.beginTestSection = 0;
16878 
16879     /**
16880      * @ignore
16881      */
16882     this.getTestObject = function () {
16883         //Load mock dependencies.
16884         var _mock = new MockControl();
16885         _hub = _mock.createMock(gadgets.Hub);
16886 
16887         return {
16888             //Expose mock dependencies
16889             mock: _mock,
16890             hub: _hub,
16891 			
16892             //Expose internal private functions
16893             reqtypes: _REQTYPES,
16894             
16895             clientRequestHandler: _clientRequestHandler
16896 
16897         };
16898     };
16899 
16900 
16901     /**
16902      * @ignore
16903      */
16904     this.endTestSection = 0;
16905     //END TEST CODE//
16906 	};
16907 	
16908 	window.finesse = window.finesse || {};
16909     window.finesse.containerservices = window.finesse.containerservices || {};
16910     window.finesse.containerservices.MasterPublisher = MasterPublisher;
16911 	
16912     return MasterPublisher;
16913 });
16914 
16915 /**
16916  * JavaScript representation of the Finesse WorkflowActionEvent object.
16917  *
16918  * @requires finesse.FinesseBase
16919  */
16920 
16921 /** The following comment is to prevent jslint errors about 
16922  * using variables before they are defined.
16923  */
16924 /*global FinesseBase: true, publisher:true, define:true, finesse:true, window:true */
16925 /** @private */
16926 define('containerservices/WorkflowActionEvent', ["FinesseBase"], function (FinesseBase) {
16927     var WorkflowActionEvent = FinesseBase.extend(/** @lends finesse.containerservices.WorkflowActionEvent.prototype */{
16928         /**
16929          * Reference to the WorkflowActionEvent name
16930          * This will be set by setWorkflowActionEvent
16931          * @private
16932          */
16933         _name: null,
16934 
16935         /**
16936          * Reference to the WorkflowActionEvent type
16937          * This will be set by setWorkflowActionEvent
16938          * @private
16939          */
16940         _type: null,
16941 
16942         /**
16943          * Reference to the WorkflowActionEvent handledBy value
16944          * This will be set by setWorkflowActionEvent
16945          * @private
16946          */
16947         _handledBy: null,
16948 
16949         /**
16950          * Reference to the WorkflowActionEvent params array
16951          * This will be set by setWorkflowActionEvent
16952          * @private
16953          */
16954         _params: [],
16955 
16956         /**
16957          * Reference to the WorkflowActionEvent actionVariables array
16958          * This will be set by setWorkflowActionEvent
16959          * @private
16960          */            
16961         _actionVariables: [], 
16962         
16963         /**
16964          * @class
16965          * JavaScript representation of a WorkflowActionEvent object.
16966          * The WorkflowActionEvent object is delivered as the payload of
16967          * a WorkflowAction callback.  This can be subscribed to by using
16968          * {@link finesse.containerservices.ContainerServices#addHandler} with a 
16969          * topic of {@link finesse.containerservices.ContainerServices.Topics#WORKFLOW_ACTION_EVENT}. 
16970          * Gadgets should key on events with a handleBy value of "OTHER".
16971          * 
16972          * @constructs
16973          **/
16974         init: function () {
16975             this._super();
16976         },        
16977 
16978         /**
16979 	     * Validate that the passed in object is a WorkflowActionEvent object
16980 	     * and sets the variables if it is
16981 	     * @param maybeWorkflowActionEvent A possible WorkflowActionEvent object to be evaluated and set if 
16982 	     *                                 it validates successfully.
16983 	     * @returns {Boolean} Whether it is valid or not.
16984          * @private
16985 	     */
16986 	    setWorkflowActionEvent: function(maybeWorkflowActionEvent) {
16987 	        var returnValue;
16988 	
16989 	        if (maybeWorkflowActionEvent.hasOwnProperty("name") === true &&
16990 	                maybeWorkflowActionEvent.hasOwnProperty("type") === true &&
16991                     maybeWorkflowActionEvent.hasOwnProperty("handledBy") === true &&
16992 	                maybeWorkflowActionEvent.hasOwnProperty("params") === true &&
16993 	                maybeWorkflowActionEvent.hasOwnProperty("actionVariables") === true) {
16994 	            this._name = maybeWorkflowActionEvent.name;
16995 	            this._type = maybeWorkflowActionEvent.type;
16996                 this._handledBy = maybeWorkflowActionEvent.handledBy;
16997 	            this._params = maybeWorkflowActionEvent.params;
16998 	            this._actionVariables = maybeWorkflowActionEvent.actionVariables;
16999 	            returnValue = true;
17000 	        } else {
17001 	            returnValue = false;
17002 	        }
17003 	
17004 	        return returnValue;
17005 	    },
17006 	
17007 	    /**
17008 	     * Getter for the WorkflowActionEvent name.
17009 	     * @returns {String} The name of the WorkflowAction.
17010 	     */
17011 	    getName: function () {
17012 	        // escape nulls to empty string
17013 	        return this._name || "";
17014 	    },
17015 	
17016 	    /**
17017 	     * Getter for the WorkflowActionEvent type.
17018 	     * @returns {String} The type of the WorkflowAction (BROWSER_POP, HTTP_REQUEST).
17019 	     */
17020 	    getType: function () {
17021 	        // escape nulls to empty string
17022 	        return this._type || "";
17023 	    },
17024 	
17025         /**
17026          * Getter for the WorkflowActionEvent handledBy value. Gadgets should look for
17027          * events with a handleBy of "OTHER".
17028          * @see finesse.containerservices.WorkflowActionEvent.HandledBy
17029          * @returns {String} The handledBy value of the WorkflowAction that is a value of {@link finesse.containerservices.WorkflowActionEvent.HandledBy}.
17030          */
17031         getHandledBy: function () {
17032             // escape nulls to empty string
17033             return this._handledBy || "";
17034         },
17035 
17036 
17037 	    /**
17038 	     * Getter for the WorkflowActionEvent Params map.
17039 	     * @returns {Object} key = param name, value = Object{name, value, expandedValue}
17040 	     * BROWSER_POP<ul>
17041 	     * <li>windowName : Name of window to pop into, or blank to always open new window.
17042 	     * <li>path : URL to open.</ul>
17043 	     * HTTP_REQUEST<ul>
17044 	     * <li>method : "PUT" or "POST".
17045 	     * <li>location : "FINESSE" or "OTHER".
17046 	     * <li>contentType : MIME type of request body, if applicable, e.g. "text/plain".
17047 	     * <li>path : Request URL.
17048 	     * <li>body : Request content for POST requests.</ul>
17049 	     */
17050 	    getParams: function () {
17051 	        var map = {},
17052 	            params = this._params,
17053 	            i,
17054 	            param;
17055 	
17056 	        if (params === null || params.length === 0) {
17057 	            return map;
17058 	        }
17059 	
17060 	        for (i = 0; i < params.length; i += 1) {
17061 	            param = params[i];
17062 	            // escape nulls to empty string
17063 	            param.name = param.name || "";
17064 	            param.value = param.value || "";
17065 	            param.expandedValue = param.expandedValue || "";
17066 	            map[param.name] = param;
17067 	        }
17068 	
17069 	        return map;
17070 	    },
17071 	    
17072 	    /**
17073 	     * Getter for the WorkflowActionEvent ActionVariables map
17074 	     * @returns {Object} key = action variable name, value = Object{name, type, node, testValue, actualValue}
17075 	     */
17076 	    getActionVariables: function() {
17077 	        var map = {},
17078 	            actionVariables = this._actionVariables,
17079 	            i,
17080 	            actionVariable;
17081 	
17082 	        if (actionVariables === null || actionVariables.length === 0) {
17083 	            return map;
17084 	        }
17085 	
17086 	        for (i = 0; i < actionVariables.length; i += 1) {
17087 	            actionVariable = actionVariables[i];
17088 	            // escape nulls to empty string
17089 	            actionVariable.name = actionVariable.name || "";
17090 	            actionVariable.type = actionVariable.type || "";
17091 	            actionVariable.node = actionVariable.node || "";
17092 	            actionVariable.testValue = actionVariable.testValue || "";
17093 	            actionVariable.actualValue = actionVariable.actualValue || "";
17094 	            map[actionVariable.name] = actionVariable;
17095 	        }
17096 	
17097 	        return map;
17098 	    }
17099     }); 
17100     
17101     
17102     WorkflowActionEvent.HandledBy = /** @lends finesse.containerservices.WorkflowActionEvent.HandledBy.prototype */ {
17103         /**
17104          * This specifies that Finesse will handle this WorkflowActionEvent.  A 3rd Party can do additional processing
17105          * with the action, but first and foremost Finesse will handle this WorkflowAction.
17106          */
17107         FINESSE: "FINESSE",
17108 
17109         /**
17110          * This specifies that a 3rd Party will handle this WorkflowActionEvent.  Finesse's Workflow Engine Executor will 
17111          * ignore this action and expects Gadget Developers to take action.
17112          */
17113         OTHER: "OTHER",
17114         
17115         /**
17116          * @class This is the set of possible HandledBy values used for WorkflowActionEvent from ContainerServices.  This
17117          * is provided from the {@link finesse.containerservices.WorkflowActionEvent#getHandledBy} method.
17118          * @constructs
17119          */
17120         _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
17121     };    
17122     
17123     window.finesse = window.finesse || {};
17124     window.finesse.containerservices = window.finesse.containerservices || {};
17125     window.finesse.containerservices.WorkflowActionEvent = WorkflowActionEvent;
17126     
17127     return WorkflowActionEvent;
17128 });
17129 
17130 /**
17131  * JavaScript representation of the Finesse TimerTickEvent
17132  *
17133  * @requires finesse.FinesseBase
17134  */
17135 
17136 /** The following comment is to prevent jslint errors about 
17137  * using variables before they are defined.
17138  */
17139 /*global FinesseBase: true, publisher:true, define:true, finesse:true, window:true */
17140 /** @private */
17141 define('containerservices/TimerTickEvent',[
17142     "FinesseBase"
17143 ],
17144 function (FinesseBase) {
17145     var TimerTickEvent = FinesseBase.extend(/** @lends finesse.containerservices.TimerTickEvent.prototype */{
17146         /**
17147          * date the TimerTickEvent was queued 
17148          * @private
17149          */
17150         _dateQueued: null,
17151 
17152         /**
17153          * the frequency of the timer tick (in miiliseconds)
17154          * @private
17155          */
17156         _tickFrequency: 1000,
17157 
17158         /**
17159          * @class
17160          * JavaScript representation of a TimerTickEvent object.
17161          * The TimerTickEvent object is delivered as the payload of
17162          * a TimerTickEvent callback.  This can be subscribed to by using
17163          * {@link finesse.containerservices.ContainerServices#addHandler} with a 
17164          * topic of {@link finesse.containerservices.ContainerServices.Topics#TIMER_TICK_EVENT}. 
17165          * 
17166          * @constructs
17167          **/
17168         init: function (tickFrequency, dateQueued) {
17169             this._super();
17170             
17171             this._tickFrequency = tickFrequency;
17172             this._dateQueued = dateQueued;
17173         },
17174 
17175        /**
17176          * Get the "tickFrequency" field
17177          * @param {int} which is the "TickFrequency" field
17178          * @private
17179          */
17180         getTickFrequency: function () {
17181             return this._tickFrequency;
17182         },
17183 
17184         /**
17185          * Getter for the TimerTickEvent "DateQueued" field. 
17186          * @returns {Date} which is a Date object when the TimerTickEvent was queued
17187          */
17188         getDateQueued: function () {
17189             return this._dateQueued;
17190         }
17191 
17192     });
17193     
17194     window.finesse = window.finesse || {};
17195     window.finesse.containerservices = window.finesse.containerservices || {};
17196     window.finesse.containerservices.TimerTickEvent = TimerTickEvent;
17197     
17198     return TimerTickEvent;
17199 });
17200 
17201 /**
17202  * JavaScript representation of the Finesse GadgetViewChangedEvent object.
17203  *
17204  * @requires finesse.FinesseBase
17205  */
17206 
17207 /** The following comment is to prevent jslint errors about 
17208  * using variables before they are defined.
17209  */
17210 /*global FinesseBase: true, publisher:true, define:true, finesse:true, window:true */
17211 /** @private */
17212 define('containerservices/GadgetViewChangedEvent',[
17213     "FinesseBase"
17214 ],
17215 function (FinesseBase) {
17216     var GadgetViewChangedEvent = FinesseBase.extend(/** @lends finesse.containerservices.GadgetViewChangedEvent.prototype */{
17217         /**
17218          * Reference to the gadget id
17219          * @private
17220          */
17221         _gadgetId: null,
17222 
17223         /**
17224          * Reference to the tab id
17225          * @private
17226          */
17227         _tabId: null,
17228 
17229         /**
17230          * Reference to the maxAvailableHeight
17231          * @private
17232          */
17233         _maxAvailableHeight: null,
17234 
17235         /**
17236          * Reference to the view
17237          * E.g. 'default' or 'canvas'
17238          * @private
17239          */
17240         _view: null,
17241         
17242         /**
17243          * @class
17244          * JavaScript representation of a GadgetViewChangedEvent object.
17245          * The GadgetViewChangedEvent object is delivered as the payload of
17246          * a GadgetViewChangedEvent callback.  This can be subscribed to by using
17247          * {@link finesse.containerservices.ContainerServices#addHandler} with a 
17248          * topic of {@link finesse.containerservices.ContainerServices.Topics#GADGET_VIEW_CHANGED_EVENT}. 
17249          * 
17250          * @constructs
17251          **/
17252         init: function (gadgetId, tabId, maxAvailableHeight, view) {
17253             this._super();
17254 
17255             this._gadgetId = gadgetId;
17256             this._tabId = tabId;
17257             this._maxAvailableHeight = maxAvailableHeight;
17258             this._view = view;
17259         },
17260     
17261         /**
17262          * Getter for the gadget id.
17263          * @returns {String} The identifier for the gadget changing view.
17264          */
17265         getGadgetId: function () {
17266             // escape nulls to empty string
17267             return this._gadgetId || "";
17268         },
17269     
17270         /**
17271          * Getter for the maximum available height.
17272          * @returns {String} The maximum available height for the gadget's view.
17273          */
17274         getMaxAvailableHeight: function () {
17275             // escape nulls to empty string
17276             return this._maxAvailableHeight || "";
17277         },
17278 
17279         /**
17280          * Getter for the tab id.
17281          * @returns {String} The identifier for the tab where the gadget changing view resides.
17282          */
17283         getTabId: function () {
17284             // escape nulls to empty string
17285             return this._tabId || "";
17286         },
17287 
17288         /**
17289          * Getter for the view.
17290          * @returns {String} The view type the gadget is changing to.
17291          */
17292         getView: function () {
17293             // escape nulls to empty string
17294             return this._view || "";
17295         }
17296     });
17297     
17298     window.finesse = window.finesse || {};
17299     window.finesse.containerservices = window.finesse.containerservices || {};
17300     window.finesse.containerservices.GadgetViewChangedEvent = GadgetViewChangedEvent;
17301     
17302     return GadgetViewChangedEvent;
17303 });
17304 
17305 /**
17306  * JavaScript representation of the Finesse MaxAvailableHeightChangedEvent object.
17307  *
17308  * @requires finesse.FinesseBase
17309  */
17310 
17311 /** The following comment is to prevent jslint errors about 
17312  * using variables before they are defined.
17313  */
17314 /*global FinesseBase: true, publisher:true, define:true, finesse:true, window:true */
17315 /** @private */
17316 define('containerservices/MaxAvailableHeightChangedEvent',[
17317     "FinesseBase"
17318 ],
17319 function (FinesseBase) {
17320     var MaxAvailableHeightChangedEvent = FinesseBase.extend(/** @lends finesse.containerservices.MaxAvailableHeightChangedEvent.prototype */{
17321 
17322         /**
17323          * Reference to the maxAvailableHeight
17324          * @private
17325          */
17326         _maxAvailableHeight: null,
17327         
17328         /**
17329          * @class
17330          * JavaScript representation of a MaxAvailableHeightChangedEvent object.
17331          * The MaxAvailableHeightChangedEvent object is delivered as the payload of
17332          * a MaxAvailableHeightChangedEvent callback.  This can be subscribed to by using
17333          * {@link finesse.containerservices.ContainerServices#addHandler} with a 
17334          * topic of {@link finesse.containerservices.ContainerServices.Topics#MAX_AVAILABLE_HEIGHT_CHANGED_EVENT}. 
17335          * 
17336          * @constructs
17337          **/
17338         init: function (maxAvailableHeight) {
17339             this._super();
17340 
17341             this._maxAvailableHeight = maxAvailableHeight;
17342         },
17343     
17344         /**
17345          * Getter for the maximum available height.
17346          * @returns {String} The maximum available height for a gadget in canvas view
17347          */
17348         getMaxAvailableHeight: function () {
17349             // escape nulls to empty string
17350             return this._maxAvailableHeight || "";
17351         }
17352     });
17353     
17354     window.finesse = window.finesse || {};
17355     window.finesse.containerservices = window.finesse.containerservices || {};
17356     window.finesse.containerservices.MaxAvailableHeightChangedEvent = MaxAvailableHeightChangedEvent;
17357     
17358     return MaxAvailableHeightChangedEvent;
17359 });
17360 
17361 /**
17362  * Exposes a set of API wrappers that will hide the dirty work of
17363  *     constructing Finesse API requests and consuming Finesse events.
17364  *
17365  * @requires OpenAjax, jQuery 1.5, finesse.utilities.Utilities
17366  */
17367 
17368 /** The following comment is to prevent jslint errors about using variables before they are defined. */
17369 /*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 */
17370 /*jslint nomen: true, unparam: true, sloppy: true, white: true */
17371 /** @private */
17372 define('containerservices/ContainerServices',[
17373     "utilities/Utilities",
17374     "restservices/Notifier",
17375     "containerservices/Topics",
17376     "containerservices/MasterPublisher",
17377     "containerservices/WorkflowActionEvent",
17378     "containerservices/TimerTickEvent",
17379     "containerservices/GadgetViewChangedEvent",
17380     "containerservices/MaxAvailableHeightChangedEvent"
17381 ],
17382 function (Utilities, Notifier, Topics, MasterPublisher, WorkflowActionEvent) {
17383 
17384     var ContainerServices = ( function () { /** @lends finesse.containerservices.ContainerServices.prototype */
17385 
17386     var
17387 
17388     /**
17389      * Shortcut reference to the Utilities singleton
17390      * This will be set by init()
17391      * @private
17392      */
17393     _util,
17394 
17395     /**
17396      * Shortcut reference to the gadget pubsub Hub instance.
17397      * This will be set by init()
17398      * @private
17399      */
17400     _hub,
17401 
17402     /**
17403      * Boolean whether this instance is master or not
17404      * @private
17405      */
17406     _master = false,
17407 
17408     /**
17409      * Whether the Client Services have been initiated yet.
17410      * @private
17411      */
17412     _inited = false,
17413     
17414     /**
17415      * References to ClientServices logger methods
17416      * @private
17417      */
17418     _logger = {
17419         log: finesse.clientservices.ClientServices.log
17420     },
17421     
17422      /**
17423      * Stores the list of subscription IDs for all subscriptions so that it
17424      * could be retrieve for unsubscriptions.
17425      * @private
17426      */
17427     _subscriptionID = {},
17428     
17429     /**
17430      * Reference to the gadget's parent container
17431      * @private
17432      */
17433     _container,
17434 
17435     /**
17436      * Reference to the MasterPublisher
17437      * @private
17438      */
17439     _publisher,
17440     
17441     /**
17442      * Object that will contain the Notifiers
17443      * @private
17444      */
17445     _notifiers = {},
17446 
17447     /**
17448      * Reference to the tabId that is associated with the gadget
17449      * @private
17450      */
17451     _myTab = null,
17452     
17453     /**
17454      * Reference to the visibility of current gadget
17455      * @private
17456      */
17457     _visible = false,
17458     
17459     /**
17460      * Shortcut reference to the Topics class.
17461      * This will be set by init()
17462      * @private
17463      */
17464     _topics,
17465 
17466     /**
17467      * Associates a topic name with the private handler function.
17468      * Adding a new topic requires that you add this association here 
17469      *  in to keep addHandler generic.
17470      * @param {String} topic : Specifies the callback to retrieve
17471      * @return {Function} The callback function associated with the topic param.
17472      * @private
17473      */
17474     _topicCallback = function (topic) {
17475         var callback, notifier;
17476         switch (topic)
17477         {
17478             case finesse.containerservices.ContainerServices.Topics.ACTIVE_TAB:
17479                 callback = _tabTracker;
17480                 break;
17481             case finesse.containerservices.ContainerServices.Topics.WORKFLOW_ACTION_EVENT:
17482                 callback = _workflowActionEventTracker;
17483                 break;
17484             case finesse.containerservices.ContainerServices.Topics.RELOAD_GADGET_EVENT:
17485                 callback = _masterReloader;
17486                 break;
17487             case finesse.containerservices.ContainerServices.Topics.GADGET_VIEW_CHANGED_EVENT:
17488                 callback = _gadgetViewChanged;
17489                 break;
17490             case finesse.containerservices.ContainerServices.Topics.MAX_AVAILABLE_HEIGHT_CHANGED_EVENT:
17491                 callback = _maxAvailableHeightChanged;
17492                 break;
17493             default:
17494                 callback = function (param) {
17495                      var data = null;
17496                      
17497                      notifier = _getNotifierReference(topic);
17498                      
17499                      if (arguments.length === 1) {
17500                         data = param;
17501                      } else {
17502                         data = arguments;
17503                      }
17504                      notifier.notifyListeners(data);
17505                 };
17506         }
17507         return callback;
17508     },
17509 
17510     /**
17511      * Ensure that ClientServices have been inited.
17512      * @private
17513      */
17514     _isInited = function () {
17515         if (!_inited) {
17516             throw new Error("ContainerServices needs to be inited.");
17517         }
17518     },
17519 
17520     /**
17521      * Retrieves a Notifier reference to a particular topic, and creates one if it doesn't exist.
17522      * @param {String} topic : Specifies the notifier to retrieve
17523      * @return {Notifier} The notifier object.
17524      * @private
17525      */
17526     _getNotifierReference = function (topic) {
17527         if (!_notifiers.hasOwnProperty(topic))
17528         {
17529             _notifiers[topic] = new Notifier();
17530         }
17531 
17532         return _notifiers[topic];
17533     },
17534 
17535     /**
17536      * Utility function to make a subscription to a particular topic. Only one
17537      * callback function is registered to a particular topic at any time.
17538      * @param {String} topic
17539      *     The full topic name. The topic name should follow the OpenAjax
17540      *     convention using dot notation (ex: finesse.api.User.1000).
17541      * @param {Function} callback
17542      *     The function that should be invoked with the data when an event
17543      *     is delivered to the specific topic.
17544      * @returns {Boolean}
17545      *     True if the subscription was made successfully and the callback was
17546      *     been registered. False if the subscription already exist, the
17547      *     callback was not overwritten.
17548      * @private
17549      */
17550     _subscribe = function (topic, callback) {
17551         _isInited();
17552 
17553         //Ensure that the same subscription isn't made twice.
17554         if (!_subscriptionID[topic]) {
17555             //Store the subscription ID using the topic name as the key.
17556             _subscriptionID[topic] = _hub.subscribe(topic,
17557                 //Invoke the callback just with the data object.
17558                 function (topic, data) {
17559                     callback(data);
17560                 });
17561             return true;
17562         }
17563         return false;
17564     },
17565 
17566     /**
17567      * Unsubscribe from a particular topic.
17568      * @param {String} topic : The full topic name.
17569      * @private
17570      */
17571     _unsubscribe = function (topic) {
17572         _isInited();
17573 
17574         //Unsubscribe from the topic using the subscription ID recorded when
17575         //the subscription was made, then delete the ID from data structure.
17576         _hub.unsubscribe(_subscriptionID[topic]);
17577         delete _subscriptionID[topic];
17578     },
17579 
17580     /**
17581      * Get my tab id.
17582      * @returns {String} tabid : The tabid of this container/gadget.
17583      * @private
17584      */
17585     _getMyTab = function () {
17586         if (_myTab === null)
17587         {
17588             try {
17589             _myTab = $(frameElement).closest("div.tab-panel").attr("id").replace("panel_", "");
17590             } catch (err) {
17591                 _logger.log("Error accessing current tab: " + err.message);
17592                _myTab = null;
17593             }
17594         }
17595         return _myTab;
17596     },
17597     
17598     /**
17599      * Callback function that is called when an activeTab message is posted to the Hub.
17600      * Notifies listener functions if this tab is the one that was just made active.
17601      * @param {String} tabId : The tabId which was just made visible.
17602      * @private
17603      */
17604     _tabTracker = function(tabId) {
17605         if (tabId === _getMyTab()) {
17606             if(!_visible) {
17607                 _visible = true;
17608                 _notifiers[finesse.containerservices.ContainerServices.Topics.ACTIVE_TAB].notifyListeners(this);
17609             }
17610         } else {
17611             _visible = false;
17612         }
17613     },
17614     
17615     /**
17616      * Make a request to set a particular tab active. This
17617      * method should be called after {@link finesse.containerservices.ContainerServices#addHandler}
17618      * to ensure the gadget gets properly initialized.
17619      * @param {String} tabId
17620      *    The tabId (not the label text) of the tab to make active.  If the id is invalid, no action will occur.
17621      * @private
17622      */
17623     _activateTab = function ( tabId ) {
17624         _logger.log("Sending request to activate tab: " + tabId);
17625         if(_hub){
17626             var data = {
17627                 type: "SetActiveTabReq",
17628                 data: { id: tabId },
17629                 invokeID: (new Date()).getTime()          
17630             };
17631             _hub.publish(_topics.REQUESTS, data);
17632         } else {
17633             throw new Error("Hub is not defined.");
17634         }
17635         
17636     },
17637 
17638     /**
17639      * Callback function that is called when a gadget view changed message is posted to the Hub.
17640      * @private
17641      */
17642     _gadgetViewChanged = function (data) {
17643         if (data) {
17644             var gadgetViewChangedEvent = new finesse.containerservices.GadgetViewChangedEvent(
17645                 data.gadgetId,
17646                 data.tabId,
17647                 data.maxAvailableHeight,
17648                 data.view);
17649 
17650             _notifiers[finesse.containerservices.ContainerServices.Topics.GADGET_VIEW_CHANGED_EVENT].notifyListeners(gadgetViewChangedEvent);
17651         }
17652     },
17653 
17654     /**
17655      * Callback function that is called when a max available height changed message is posted to the Hub.
17656      * @private
17657      */
17658     _maxAvailableHeightChanged = function (data) {
17659         if (data) {
17660             var maxAvailableHeightChangedEvent = new finesse.containerservices.MaxAvailableHeightChangedEvent(
17661                 data.maxAvailableHeight);
17662 
17663             _notifiers[finesse.containerservices.ContainerServices.Topics.MAX_AVAILABLE_HEIGHT_CHANGED_EVENT].notifyListeners(maxAvailableHeightChangedEvent);
17664         }
17665     },
17666 
17667     /**
17668      * Callback function that is called when a workflowActionEvent message is posted to the Hub.
17669      * Notifies listener functions if the posted object can be converted to a proper WorkflowActionEvent object.
17670      * @param {String} workflowActionEvent : The workflowActionEvent that was posted to the Hub
17671      * @private
17672      */
17673     _workflowActionEventTracker = function(workflowActionEvent) {
17674         var vWorkflowActionEvent = new finesse.containerservices.WorkflowActionEvent();
17675                 
17676         if (vWorkflowActionEvent.setWorkflowActionEvent(workflowActionEvent)) {
17677             _notifiers[finesse.containerservices.ContainerServices.Topics.WORKFLOW_ACTION_EVENT].notifyListeners(vWorkflowActionEvent);
17678         }
17679         // else
17680         // {
17681             //?console.log("Error in ContainerServices : _workflowActionEventTracker - could not map published HUB object to WorkflowActionEvent");
17682         // }
17683 
17684     },
17685 
17686     /**
17687      * Callback function that is called when a reloadGadget event message is posted to the Hub.
17688      *
17689      * Grabs the id of the gadget we want to reload from the data and reload it!
17690      *
17691      * @param {String} topic
17692      *      which topic the event came on (unused)
17693      * @param {Object} data
17694      *      the data published with the event
17695      * @private
17696      */
17697     _masterReloader = function (topic, data) {
17698         var gadgetId = data.gadgetId;
17699         if (gadgetId) {
17700             _container.reloadGadget(gadgetId);
17701         }
17702     },
17703     
17704     /**
17705      * Pulls the gadget id from the url parameters
17706      * @return {String} id of the gadget
17707      * @private
17708      */
17709     _findMyGadgetId = function () {
17710         if (gadgets && gadgets.util && gadgets.util.getUrlParameters()) {
17711             return gadgets.util.getUrlParameters().mid;
17712         }
17713     };
17714 
17715     return {
17716         /**
17717          * @class
17718          * This class provides container-level services for gadget developers, exposing container events by
17719          * calling a set of exposed functions. Gadgets can utilize the container dialogs and 
17720          * event handling (add/remove).
17721          * @example
17722          *    containerServices = finesse.containerservices.ContainerServices.init();
17723          *    containerServices.addHandler(
17724          *      finesse.containerservices.ContainerServices.Topics.ACTIVE_TAB, 
17725          *      function() {
17726          *          clientLogs.log("Gadget is now visible");  // log to Finesse logger
17727          *          // automatically adjust the height of the gadget to show the html
17728          *          gadgets.window.adjustHeight();
17729          *      });
17730          *    containerServices.makeActiveTabReq();
17731          *    
17732          * @constructs
17733          */
17734         _fakeConstuctor: function () {
17735             /* This is here so we can document init() as a method rather than as a constructor. */
17736         },
17737         
17738         /**
17739          * Initialize ContainerServices for use in gadget.
17740          * @param {Boolean} [master=false] Do not use this parameter from your gadget.
17741          * @returns ContainerServices instance.
17742          */
17743         init: function (master) {
17744             if (!_inited) {
17745                 _inited = true;
17746                 // Set shortcuts
17747                 _util = Utilities;
17748 
17749                 //init the hub only when it's available
17750                 if(gadgets.Hub) {
17751                     _hub = gadgets.Hub;
17752                 }
17753 
17754                 if(Topics) {
17755                     _topics = Topics;
17756                 }
17757 
17758                 if (master) {
17759                     _master = true;
17760                     _container = finesse.container.Container;
17761                     _publisher = new MasterPublisher();
17762 
17763                     // subscribe for reloading gadget events
17764                     // we only want the master ContainerServices handling these events
17765                     _hub.subscribe(_topics.RELOAD_GADGET, _topicCallback(_topics.RELOAD_GADGET));
17766                 } else {
17767                     _container = parent.finesse.container.Container;
17768                 }
17769             }
17770             
17771             this.makeActiveTabReq();
17772 
17773             //Return the CS object for object chaining.
17774             return this;
17775         },
17776 
17777         /**
17778          * Shows the jQuery UI Dialog with the specified parameters. The following are the
17779          * default parameters: <ul>
17780          *     <li> Title of "Cisco Finesse".</li>
17781          *     <li>Message of "A generic error has occured".</li>
17782          *     <li>The only button, "Ok", closes the dialog.</li>
17783          *     <li>Modal (blocks other dialogs).</li>
17784          *     <li>Not draggable.</li>
17785          *     <li>Fixed size.</li></ul>
17786          * @param {Object} options
17787          *  An object containing additional options for the dialog.
17788          * @param {String/Boolean} options.title
17789          *  Title to use. undefined defaults to "Cisco Finesse". false to hide
17790          * @param {Function} options.close
17791          *  A function to invoke when the dialog is closed.
17792          * @param {String} options.message
17793          *  The message to display in the dialog.
17794          *  Defaults to "A generic error has occurred."
17795          * @param {Boolean} options.isBlocking
17796          *  Flag indicating whether this dialog will block other dialogs from being shown (Modal).
17797          * @returns {jQuery} JQuery wrapped object of the dialog DOM element.
17798          * @see finesse.containerservices.ContainerServices#hideDialog
17799          */
17800         showDialog: function(options) {
17801             if ((_container.showDialog !== undefined) && (_container.showDialog !== this.showDialog)) {
17802                 return _container.showDialog(options);
17803             }
17804         },
17805         
17806         /**
17807          * Hides the jQuery UI Dialog.
17808          * @returns {jQuery} jQuery wrapped object of the dialog DOM element
17809          * @see finesse.containerservices.ContainerServices#showDialog
17810          */
17811         hideDialog: function() {
17812             if ((_container.hideDialog !== undefined) && (_container.hideDialog !== this.hideDialog)) {
17813                 return _container.hideDialog();
17814             }
17815         },
17816 
17817         /**
17818          *  Reloads the current gadget. 
17819          *  For use from within a gadget only.
17820          */
17821         reloadMyGadget: function () {
17822             var topic, gadgetId, data;
17823 
17824             if (!_master) {
17825                 // first unsubscribe this gadget from all topics on the hub
17826                 for (topic in _notifiers) {
17827                     if (_notifiers.hasOwnProperty(topic)) {
17828                         _unsubscribe(topic);
17829                         delete _notifiers[topic];
17830                     }
17831                 }
17832 
17833                 // send an asynch request to the hub to tell the master container
17834                 // services that we want to refresh this gadget
17835                 gadgetId = _findMyGadgetId();
17836                 data = {
17837                     type: "ReloadGadgetReq",
17838                     data: {gadgetId: gadgetId},
17839                     invokeID: (new Date()).getTime()          
17840                 };
17841                 _hub.publish(_topics.REQUESTS, data);
17842             }            
17843         },
17844 
17845         /**
17846          * Updates the url for this gadget and then reload it.
17847          * 
17848          * This allows the gadget to be reloaded from a different location
17849          * than what is uploaded to the current server. For example, this
17850          * would be useful for 3rd party gadgets to implement their own failover
17851          * mechanisms.
17852          *
17853          * For use from within a gadget only.
17854          *
17855          * @param {String} url
17856          *      url from which to reload gadget
17857          */
17858         reloadMyGadgetFromUrl: function (url) {
17859             if (!_master) {
17860                 var gadgetId = _findMyGadgetId();
17861 
17862                 // update the url in the container
17863                 _container.modifyGadgetUrl(gadgetId, url);
17864 
17865                 // reload it
17866                 this.reloadMyGadget();
17867             }
17868         },
17869         
17870         /**
17871          * Adds a handler for one of the supported topics provided by ContainerServices.  The callbacks provided
17872          * will be invoked when that topic is notified.  
17873          * @param {String} topic
17874          *  The Hub topic to which we are listening.
17875          * @param {Function} callback
17876          *  The callback function to invoke.
17877          * @see finesse.containerservices.ContainerServices.Topics
17878          * @see finesse.containerservices.ContainerServices#removeHandler
17879          */
17880         addHandler: function (topic, callback) {
17881             _isInited();
17882             var notifier = null;
17883             
17884             try {    
17885                 // For backwards compatibility...
17886                 if (topic === "tabVisible") {
17887                     if (window.console && typeof window.console.log === "function") {
17888                         window.console.log("WARNING - Using tabVisible as topic.  This is deprecated.  Use finesse.containerservices.ContainerServices.Topics.ACTIVE_TAB now!");
17889                     }
17890                     
17891                     topic = finesse.containerservices.ContainerServices.Topics.ACTIVE_TAB;
17892                 }
17893                 
17894                 // Add the callback to the notifier.
17895                 _util.validateHandler(callback);
17896             
17897                 notifier = _getNotifierReference(topic);
17898             
17899                 notifier.addListener(callback);
17900             
17901                 // Subscribe to the topic. _subscribe ensures that a topic is only subscribed to once,
17902                 // so attempt to subscribe each time a handler is added. This ensures that a topic is subscribed
17903                 // to only when necessary.
17904                 _subscribe(topic, _topicCallback(topic));
17905             
17906             } catch (err) {
17907                 throw new Error("addHandler(): " + err);
17908             }
17909         }, 
17910         
17911         /**
17912          * Removes a previously-added handler for one of the supported topics.
17913          * @param {String} topic
17914          *  The Hub topic from which we are removing the callback.
17915          * @param {Function} callback
17916          *  The name of the callback function to remove.
17917          * @see finesse.containerservices.ContainerServices.Topics
17918          * @see finesse.containerservices.ContainerServices#addHandler
17919          */
17920         removeHandler: function(topic, callback) {
17921             var notifier = null;
17922             
17923             try {
17924                 _util.validateHandler(callback);
17925     
17926                 notifier = _getNotifierReference(topic);
17927     
17928                 notifier.removeListener(callback);
17929             } catch (err) {
17930                 throw new Error("removeHandler(): " + err);
17931             }
17932         },
17933 
17934         /**
17935          * Returns the visibility of current gadget.  Note that this 
17936          * will not be set until after the initialization of the gadget.
17937          * @return {Boolean} The visibility of current gadget.
17938          */
17939         tabVisible: function(){
17940             return _visible;
17941         },
17942         
17943         /**
17944          * Make a request to check the current tab.  The 
17945          * activeTab event will be invoked if on the active tab.  This
17946          * method should be called after {@link finesse.containerservices.ContainerServices#addHandler}
17947          * to ensure the gadget gets properly initialized.
17948          */
17949         makeActiveTabReq : function () {
17950             if(_hub){
17951                 var data = {
17952                     type: "ActiveTabReq",
17953                     data: {},
17954                     invokeID: (new Date()).getTime()          
17955                 };
17956                 _hub.publish(_topics.REQUESTS, data);
17957             } else {
17958                 throw new Error("Hub is not defined.");
17959             }
17960             
17961         },
17962 
17963         /**
17964          * Make a request to set a particular tab active. This
17965          * method should be called after {@link finesse.containerservices.ContainerServices#addHandler}
17966          * to ensure the gadget gets properly initialized.
17967          * @param {String} tabId
17968          *    The tabId (not the label text) of the tab to make active.  If the id is invalid, no action will occur.
17969          */
17970         activateTab : function (tabId) {
17971             _activateTab(tabId);
17972         },
17973         
17974         /**
17975          * Make a request to set this container's tab active. This
17976          * method should be called after {@link finesse.containerservices.ContainerServices#addHandler}
17977          * to ensure the gadget gets properly initialized.
17978          */
17979         activateMyTab : function () {
17980             _activateTab( _getMyTab() );
17981         },
17982         
17983         /**
17984          * Get the tabId of my container/gadget.
17985          * @returns {String} tabid : The tabid of this container/gadget.
17986          */
17987         getMyTabId : function () {
17988             return _getMyTab();
17989         },
17990 
17991         /**
17992          * Gets the id of the gadget.
17993          * @returns {number} the id of the gadget
17994          */
17995         getMyGadgetId : function () {
17996             return _findMyGadgetId();
17997         },
17998 
17999         //BEGIN TEST CODE//
18000         /**
18001          * Test code added to expose private functions that are used by unit test
18002          * framework. This section of code is removed during the build process
18003          * before packaging production code. The [begin|end]TestSection are used
18004          * by the build to identify the section to strip.
18005          * @ignore
18006          */
18007         beginTestSection : 0,
18008 
18009         /**
18010          * @ignore
18011          */
18012         getTestObject: function () {
18013             //Load mock dependencies.
18014             var _mock = new MockControl();
18015             _util = _mock.createMock(Utilities);
18016             _hub = _mock.createMock(gadgets.Hub);
18017             _inited = true;
18018             return {
18019                 //Expose mock dependencies
18020                 mock: _mock,
18021                 hub: _hub,
18022                 util: _util,
18023                 addHandler: this.addHandler,
18024                 removeHandler: this.removeHandler
18025             };
18026         },
18027 
18028         /**
18029          * @ignore
18030          */
18031        endTestSection: 0
18032         //END TEST CODE//
18033     };
18034     }());
18035     
18036     ContainerServices.Topics = /** @lends finesse.containerservices.ContainerServices.Topics.prototype */ {
18037         /**
18038          * Topic for subscribing to be notified when the active tab changes.
18039          * The provided callback will be invoked when the tab that the gadget 
18040          * that subscribes with this becomes active.  To ensure code is called
18041          * when the gadget is already on the active tab use the 
18042          * {@link finesse.containerservices.ContainerServices#makeActiveTabReq}
18043          * method.
18044          */
18045         ACTIVE_TAB: "finesse.containerservices.activeTab",
18046 
18047         /**
18048          * Topic for WorkflowAction events traffic.
18049          * The provided callback will be invoked when a WorkflowAction needs
18050          * to be handled.  The callback will be passed a {@link finesse.containerservices.WorkflowActionEvent}
18051          * that can be used to interrogate the WorkflowAction and determine to use or not.
18052          */
18053         WORKFLOW_ACTION_EVENT: "finesse.containerservices.workflowActionEvent",
18054         
18055         /**
18056          * Topic for Timer Tick event.
18057          * The provided callback will be invoked when this event is fired.
18058          * The callback will be passed a {@link finesse.containerservices.TimerTickEvent}.
18059          */
18060         TIMER_TICK_EVENT : "finesse.containerservices.timerTickEvent",
18061 
18062         /**
18063          * Topic for Reload Gadget events traffic.
18064          * Only the master ContainerServices instance will handle this event.
18065          */
18066         RELOAD_GADGET_EVENT: "finesse.containerservices.reloadGadget",
18067         
18068         /**
18069          * Topic for listening to gadget view changed events.
18070          * The provided callback will be invoked when a gadget changes view.
18071          * The callback will be passed a {@link finesse.containerservices.GadgetViewChangedEvent}.
18072          */
18073         GADGET_VIEW_CHANGED_EVENT: "finesse.containerservices.gadgetViewChangedEvent",
18074 
18075         /**
18076          * Topic for listening to max available height changed events.
18077          * The provided callback will be invoked when the maximum height available to a maximized gadget changes.
18078          * This event is only meant for maximized gadgets and will not be published unless a maximized gadget exists.
18079          * The callback will be passed a {@link finesse.containerservices.MaxAvailableHeightChangedEvent}.
18080          */
18081         MAX_AVAILABLE_HEIGHT_CHANGED_EVENT: "finesse.containerservices.maxAvailableHeightChangedEvent",
18082 
18083         /**
18084          * @class This is the set of Topics used for subscribing for events from ContainerServices.
18085          * Use {@link finesse.containerservices.ContainerServices#addHandler} to subscribe to the topic.
18086          * 
18087          * @constructs
18088          */
18089         _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
18090     };
18091     
18092     window.finesse = window.finesse || {};
18093     window.finesse.containerservices = window.finesse.containerservices || {};
18094     window.finesse.containerservices.ContainerServices = ContainerServices;
18095 
18096     return ContainerServices;
18097  });
18098 
18099 /**
18100  * This "interface" is just a way to easily jsdoc the Object callback handlers.
18101  *
18102  * @requires finesse.clientservices.ClientServices
18103  * @requires Class
18104  */
18105 /** @private */
18106 define('interfaces/RestObjectHandlers',[
18107     "FinesseBase",
18108      "utilities/Utilities",
18109      "restservices/Notifier",
18110      "clientservices/ClientServices",
18111      "clientservices/Topics"
18112 ],
18113 function () {
18114 
18115     var RestObjectHandlers = ( function () { /** @lends finesse.interfaces.RestObjectHandlers.prototype */
18116         
18117         return {
18118 
18119             /**
18120              * @class
18121              * This "interface" defines REST Object callback handlers, passed as an argument to
18122              * Object getter methods in cases where the Object is going to be created.
18123              * 
18124              * @param {Object} [handlers]
18125              *     An object containing callback handlers for instantiation and runtime
18126              *     Callback to invoke upon successful instantiation, passes in REST object.
18127              * @param {Function} [handlers.onLoad(this)]
18128              *     Callback to invoke upon loading the data for the first time.
18129              * @param {Function} [handlers.onChange(this)]
18130              *     Callback to invoke upon successful update object (PUT)
18131              * @param {Function} [handlers.onAdd(this)]
18132              *     Callback to invoke upon successful update to add object (POST)
18133              * @param {Function} [handlers.onDelete(this)]
18134              *     Callback to invoke upon successful update to delete object (DELETE)
18135              * @param {Function} [handlers.onError(rsp)]
18136              *     Callback to invoke on update error (refresh or event)
18137              *     as passed by finesse.restservices.RestBase.restRequest()<br>
18138              *     {<br>
18139              *         status: {Number} The HTTP status code returned<br>
18140              *         content: {String} Raw string of response<br>
18141              *         object: {Object} Parsed object of response<br>
18142              *         error: {Object} Wrapped exception that was caught<br>
18143              *         error.errorType: {String} Type of error that was caught<br>
18144              *         error.errorMessage: {String} Message associated with error<br>
18145              *     }<br>
18146              *     <br>
18147              * Note that RestCollections have two additional callback handlers:<br>
18148              * <br>
18149              * @param {Function} [handlers.onCollectionAdd(this)]: when an object is added to this collection
18150              * @param {Function} [handlers.onCollectionDelete(this)]: when an object is removed from this collection
18151 
18152              * @constructs
18153              */
18154             _fakeConstuctor: function () {
18155                 /* This is here to enable jsdoc to document this as a class. */
18156             }
18157         };
18158     }());
18159 
18160 window.finesse = window.finesse || {};
18161 window.finesse.interfaces = window.finesse.interfaces || {};
18162 window.finesse.interfaces.RestObjectHandlers = RestObjectHandlers;
18163 
18164 return RestObjectHandlers;
18165 
18166 });
18167 
18168 
18169 /**
18170  * This "interface" is just a way to easily jsdoc the REST request handlers.
18171  *
18172  * @requires finesse.clientservices.ClientServices
18173  * @requires Class
18174  */
18175 /** @private */
18176 define('interfaces/RequestHandlers',[
18177     "FinesseBase",
18178      "utilities/Utilities",
18179      "restservices/Notifier",
18180      "clientservices/ClientServices",
18181      "clientservices/Topics"
18182 ],
18183 function () {
18184 
18185     var RequestHandlers = ( function () { /** @lends finesse.interfaces.RequestHandlers.prototype */
18186         
18187         return {
18188 
18189             /**
18190              * @class
18191              * This "interface" defines REST Object callback handlers, passed as an argument to
18192              * Object getter methods in cases where the Object is going to be created.
18193              * 
18194              * @param {Object} handlers
18195              *     An object containing the following (optional) handlers for the request:<ul>
18196              *         <li><b>success(rsp):</b> A callback function for a successful request to be invoked with the following
18197              *         response object as its only parameter:<ul>
18198              *             <li><b>status:</b> {Number} The HTTP status code returned</li>
18199              *             <li><b>content:</b> {String} Raw string of response</li>
18200              *             <li><b>object:</b> {Object} Parsed object of response</li></ul>
18201              *         <li><b>error(rsp):</b> An error callback function for an unsuccessful request to be invoked with the
18202              *         error response object as its only parameter:<ul>
18203              *             <li><b>status:</b> {Number} The HTTP status code returned</li>
18204              *             <li><b>content:</b> {String} Raw string of response</li>
18205              *             <li><b>object:</b> {Object} Parsed object of response (HTTP errors)</li>
18206              *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
18207              *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
18208              *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
18209              *             </ul></li>
18210              *         </ul>
18211 
18212              * @constructs 
18213              */
18214             _fakeConstuctor: function () {
18215                 /* This is here to enable jsdoc to document this as a class. */
18216             }
18217         };
18218     }());
18219 
18220 window.finesse = window.finesse || {};
18221 window.finesse.interfaces = window.finesse.interfaces || {};
18222 window.finesse.interfaces.RequestHandlers = RequestHandlers;
18223 
18224 finesse = finesse || {};
18225 /** @namespace These interfaces are just a convenience for documenting common parameter structures. */
18226 finesse.interfaces = finesse.interfaces || {};
18227 
18228 return RequestHandlers;
18229 
18230 });
18231 
18232 
18233 
18234 define('gadget/Config',[
18235 	"utilities/Utilities"
18236 ], function (Utilities) {  
18237 	var Config = (function () { /** @lends finesse.gadget.Config.prototype */
18238 		
18239 		if (gadgets && gadgets.Prefs) {
18240 		
18241 			var _prefs = new gadgets.Prefs();
18242 		
18243 			return {
18244 				/**
18245 				 * The base64 encoded "id:password" string used for authentication.
18246 				 */
18247 				authorization: Utilities.getUserAuthString(),
18248 				
18249 				/**
18250 				 * The country code of the client (derived from locale).
18251 				 */
18252 				country: _prefs.getString("country"),
18253 				
18254 				/**
18255 				 * The language code of the client (derived from locale).
18256 				 */
18257 				language: _prefs.getString("language"),
18258 				
18259 				/**
18260 				 * The locale of the client.
18261 				 */
18262 				locale: _prefs.getString("locale"),
18263 				
18264 				/**
18265 				 * The Finesse server IP/host as reachable from the browser.
18266 				 */
18267 				host: _prefs.getString("host"),
18268 				
18269 				/**
18270 				 * The Finesse server host's port reachable from the browser.
18271 				 */
18272 				hostPort: _prefs.getString("hostPort"),
18273 				
18274 				/**
18275 				 * The extension of the user.
18276 				 */
18277 				extension: _prefs.getString("extension"),
18278 				
18279 				/**
18280 				 * One of the work modes found in {@link finesse.restservices.User.WorkMode}, or something false (undefined) for a normal login.
18281 				 */
18282 				mobileAgentMode: _prefs.getString("mobileAgentMode"),
18283 				
18284 				/**
18285 				 * The dial number to use for mobile agent, or something false (undefined) for a normal login.
18286 				 */
18287 				mobileAgentDialNumber: _prefs.getString("mobileAgentDialNumber"),
18288 				
18289 				/**
18290 				 * The domain of the XMPP server.
18291 				 */
18292 				xmppDomain: _prefs.getString("xmppDomain"),
18293 				
18294 				/**
18295 				 * The pub sub domain where the pub sub service is running.
18296 				 */
18297 				pubsubDomain: _prefs.getString("pubsubDomain"),
18298 				
18299 				/**
18300 				 * The Finesse API IP/host as reachable from the gadget container.
18301 				 */
18302 				restHost: _prefs.getString("restHost"),
18303 				
18304 				/**
18305 				 * The type of HTTP protocol (http or https).
18306 				 */
18307 				scheme: _prefs.getString("scheme"),
18308 				
18309 				/**
18310 				 * The localhost fully qualified domain name.
18311 				 */
18312 				localhostFQDN: _prefs.getString("localhostFQDN"),
18313 				
18314 				/**
18315 				 * The localhost port.
18316 				 */
18317 				localhostPort: _prefs.getString("localhostPort"),
18318 				
18319 				/**
18320 				 * The id of the team the user belongs to.
18321 				 */
18322 				teamId: _prefs.getString("teamId"),
18323 				
18324 				/**
18325 				 * The name of the team the user belongs to.
18326 				 */
18327 				teamName: _prefs.getString("teamName"),
18328 				
18329 				/**
18330 				 * The drift time between the client and the server in milliseconds.
18331 				 */
18332 				clientDriftInMillis: _prefs.getInt("clientDriftInMillis"),
18333 				
18334 				/**
18335 				 * The client compatibility mode configuration (true if it is or false otherwise).
18336 				 */
18337 				compatibilityMode: _prefs.getString("compatibilityMode"),
18338 				
18339 				/**
18340 				 * The peripheral Id that Finesse is connected to.
18341 				 */
18342 				peripheralId: _prefs.getString("peripheralId"),
18343 				
18344 				/**
18345 				* @class
18346 				* The Config object for gadgets within the Finesse desktop container which
18347 				* contains configuration data provided by the container page.
18348 				* @constructs
18349 				*/
18350 				_fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
18351 				
18352 			};
18353 		} else {
18354 			return {};
18355 		}
18356 	}());
18357 	
18358 	/** Assign to container and gadget namespace to have config available in both  */
18359 	window.finesse = window.finesse || {};
18360 	window.finesse.container = window.finesse.container || {};
18361 	window.finesse.container.Config = window.finesse.container.Config || Config;
18362 
18363 	window.finesse.gadget = window.finesse.gadget || {};
18364 	window.finesse.gadget.Config = Config;
18365 
18366 	return Config;
18367 });
18368 define('finesse',[
18369     'restservices/Users',
18370     'restservices/Teams',
18371     'restservices/SystemInfo',
18372     'utilities/I18n',
18373     'utilities/Logger',
18374     'utilities/SaxParser',
18375     'cslogger/ClientLogger',
18376     'cslogger/FinesseLogger',
18377     'containerservices/ContainerServices',
18378     'interfaces/RestObjectHandlers',
18379     'interfaces/RequestHandlers',
18380     'gadget/Config'
18381 ],
18382 function () {
18383     return window.finesse;
18384 });
18385 
18386 require(["finesse"]);
18387 return require('finesse'); }));
18388 
18389 // Prevent other JS files from wiping out window.finesse from the namespace
18390 var finesse = window.finesse;