1 /**
  2  * Copyright 2008-Present actioncenters.org
  3  *
  4  * This file is part of actioncenters.org.
  5  *
  6  * Actioncenters.org is free software: you can redistribute it and/or modify
  7  * it under the terms of the BSD License found in the LICENSE.txt file.
  8 */
  9 
 10 /**
 11  * @fileOverview Provides ActionCenters utility functions.
 12  */
 13 
 14 /*global Ext, dojox, AC, workspaceUser, currentPSSWorkspaceId, workspaceUserId, acPSSUserId, acGlobalDelimiter,
 15     actioncenter, actionCenterAPI, actionCenterJSON, ActionCenterListener, ActionCenterWorkspace */
 16 
 17 Ext.namespace('AC.util');
 18 Ext.namespace('AC.data');
 19 Ext.namespace('AC.server');
 20 Ext.namespace('AC.tree.grid');
 21 
 22 /**
 23  * Default constructor.
 24  *
 25  * @class Defines standard icons and functions.
 26  */
 27 AC.util.StandardIcons = {
 28 
 29     /**
 30      * Project icon.
 31      *
 32      * @constant
 33      * @type String
 34      * @default 'actioncenters/resources/images/icn-project_color_16x16.png'
 35      */
 36     AC_Project_Definition: 'actioncenters/resources/images/icn-project_color_16x16.png',
 37 
 38     /**
 39      * Activity icon.
 40      *
 41      * @constant
 42      * @type String
 43      * @default 'actioncenters/resources/images/icn-activities_color_16x16.png'
 44      */
 45     AC_Activity: 'actioncenters/resources/images/icn-activities_color_16x16.png',
 46 
 47     /**
 48      * Phase icon
 49      *
 50      * @constant
 51      * @type String
 52      * @default 'actioncenters/resources/images/icn_phase.gif'
 53      */
 54     AC_Phase: 'actioncenters/resources/images/icn_phase.gif',
 55 
 56     /**
 57      * Role icon
 58      *
 59      * @constant
 60      * @type String
 61      * @default 'actioncenters/resources/images/icn_role.gif'
 62      */
 63     AC_Role: 'actioncenters/resources/images/icn_role.gif',
 64 
 65     /**
 66      * Screen icon.
 67      *
 68      * @constant
 69      * @type String
 70      * @default 'actioncenters/resources/images/icn-Screen_color_16x16.png'
 71      */
 72     AC_Screen: 'actioncenters/resources/images/icn-Screen_color_16x16.png',
 73 
 74     /**
 75      * Thinklet icon.
 76      *
 77      * @constant
 78      * @type String
 79      * @default 'actioncenters/resources/images/icn-ThinkLets_color_16x16.png'
 80      */
 81     AC_Thinklet: 'actioncenters/resources/images/icn-ThinkLets_color_16x16.png',
 82 
 83     /**
 84      * Agenda category icon.
 85      *
 86      * @constant
 87      * @type String
 88      * @default 'actioncenters/resources/images/icn-category_gray_16x16.png'
 89      */
 90     AC_Agenda_Category: 'actioncenters/resources/images/icn-category_gray_16x16.png',
 91 
 92     /**
 93      * Thinklet category icon.
 94      *
 95      * @constant
 96      * @type String
 97      * @default 'actioncenters/resources/images/icn-category_gray_16x16.png'
 98      */
 99     AC_Thinklet_Category: 'actioncenters/resources/images/icn-category_gray_16x16.png',
100 
101     /**
102      * Tool category icon.
103      *
104      * @constant
105      * @type String
106      * @default 'actioncenters/resources/images/icn-category_gray_16x16.png'
107      */
108     AC_Tool_Category: 'actioncenters/resources/images/icn-category_gray_16x16.png',
109 
110     /**
111      * Screen category icon.
112      *
113      * @constant
114      * @type String
115      * @default 'actioncenters/resources/images/icn-category_gray_16x16.png'
116      */
117     AC_Screen_Category: 'actioncenters/resources/images/icn-category_gray_16x16.png',
118 
119     /**
120      * Control category icon.
121      *
122      * @constant
123      * @type String
124      * @default 'actioncenters/resources/images/icn-category_gray_16x16.png'
125      */
126     AC_Control_Category: 'actioncenters/resources/images/icn-category_gray_16x16.png',
127 
128     /**
129      * Library icon.
130      *
131      * @constant
132      * @type String
133      * @default 'actioncenters/resources/images/icn-library_16x16.png'
134      */
135     AC_Library_Type: 'actioncenters/resources/images/icn-library_16x16.png',
136 
137     /**
138      * Workspace icon.
139      *
140      * @constant
141      * @type String
142      * @default 'actioncenters/resources/images/icn_ac-workspace_16x16.png'
143      */
144     AC_Workspace: 'actioncenters/resources/images/icn_ac-workspace_16x16.png',
145 
146     /**
147      * Info icon.
148      *
149      * @constant
150      * @type String
151      * @default 'actioncenters/resources/images/icn-info_16x16.png'
152      */
153     AC_Info: 'actioncenters/resources/images/icn-info_16x16.png',
154 
155     /**
156      * Orange info icon.
157      *
158      * @constant
159      * @type String
160      * @default 'actioncenters/resources/images/icn-info-orange_24x24.png'
161      */
162     AC_Info_Orange: 'actioncenters/resources/images/icn-info-orange_24x24.png',
163 
164     /**
165      * Edit icon.
166      *
167      * @constant
168      * @type String
169      * @default 'actioncenters/resources/images/pencil16x16.gif'
170      */
171     AC_Edit: 'actioncenters/resources/images/pencil16x16.gif',
172 
173     /**
174      * No-icon icon.
175      *
176      * @constant
177      * @type String
178      * @default 'actioncenters/resources/images/icn_no-icon_color_24x24.png'
179      */
180     AC_No_Icon: 'actioncenters/resources/images/icn_no-icon_color_24x24.png',
181 
182     /**
183      * Component icon.
184      *
185      * @constant
186      * @type String
187      * @default 'actioncenters/resources/images/icn-tools_color_16x16.png'
188      */
189     AC_Component: 'actioncenters/resources/images/icn-tools_color_16x16.png',
190 
191     /**
192      * Image icon.
193      *
194      * @constant
195      * @type String
196      * @default 'actioncenters/resources/images/image.png'
197      */
198     AC_Image: 'actioncenters/resources/images/image.png',
199 
200     /**
201      * Control icon.
202      *
203      * @constant
204      * @type String
205      * @default 'actioncenters/resources/images/image.png'
206      */
207      AC_Control: 'actioncenters/resources/images/icn-settings_gray_16x16.png',
208 
209      /**
210      *  icon.
211      *
212      * @constant
213      * @type String
214      * @default 'actioncenters/resources/images/image.png'
215      */
216      AC_Tool: 'actioncenters/resources/images/icn-settings_gray_16x16.png',
217 
218     /**
219      * Gets the path to the icon specified by the argument. If no icon is found, an empty icon is returned.
220      *
221      * @param type
222      *          the name of the icon
223      * @return the path to the icon or base64 encoding of an empty icon
224      */
225     getIconSrc: function (type) {
226         try {
227             return eval('AC.util.StandardIcons.' + type);
228         } catch (err) {
229             return 'data:image/gif;base64,R0lGODlhAQABAID/AMDAwAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==';
230         }
231     },
232 
233     /**
234      * Gets the path to the image servlet.
235      *
236      * @return the path to the image servlet
237      */
238     getImageServlet : function () {
239         return 'servlet/DisplayImageServlet?id=';
240     },
241 
242     /**
243      * Gets the path to the 1x1 px spacer image.
244      *
245      * @return the path to the 1x1 px spacer image
246      */
247     getSpacerImage : function () {
248         return 'ext-3.3.1/resources/images/default/s.gif';
249     },
250 
251     /**
252      * Gets the URL to the image specified by the argument id.
253      *
254      * @param id
255      *          the id of the image
256      * @param returnBlankIcon
257      *          the boolean flag to indicate whether to return a blank icon when the image id is null or empty
258      * @param scaleTo
259      *          the scale of the returned image
260      * @return the URL path to the image
261      */
262     getImageURL : function (id, returnBlankIcon, scaleTo) {
263         var imageURL = null;
264         if (id && id.length > 0) {
265             imageURL = this.getImageServlet() + id;
266             if (scaleTo) {
267                 imageURL = imageURL + '&scale=' + scaleTo;
268             }
269         } else if (returnBlankIcon) {
270             imageURL = Ext.BLANK_IMAGE_URL;
271         }
272         return imageURL;
273     }
274 };
275 /******************************* AC.util.FormatManager *************************************************/
276 /** 
277  * A hashmap that keeps track of the hierachical relationships between Component and Population Rule contributions
278  * along with their FIBU contributions.
279  * Hashmap key: the contributionId of the contribution
280  * Hashmap object includes: {contributionId: the contributionId of the contribution,
281  *                           type: the contribution type (e.g. AC_Component, AC_Population_Rule),
282  *                           superiorId: the contributionId of the higher level contribution (e.g. the higher level of AC_Population_Rule is AC_Component)
283  *                           FIBUs: a hashmap that keep track of the FIBU contributions that belong to this contribution
284  *                                  (the key of the hashmap is fibuState(normal, selected, etc.), and the object is the contributionId)
285  *                          } 
286  */
287 AC.util.FormatManager = new Ext.util.MixedCollection();
288 /******************************************************************************************************/
289 // TODO: move the utility into AC namespace
290 /**
291  * Default constructor.
292  *
293  * @class A utility class
294  */
295 var actionCenterUtility = {
296     /**
297      * Launches the FIBU editor.
298      *
299      * @param contributionId
300      *          the contribution id
301      * @param states
302      *          array of states for FIBU editing     
303      * @return void
304      */
305     FIBUEditor: function (contributionId, states) {
306         var stateEditors = [];
307         var editor = null;
308         var windowId = 'FFFIBUUDEditor' + acGlobalDelimiter + contributionId;
309         
310         var FIBUEditorWindow = new actioncenter.FIBUEditorWindow({
311             y : 25,
312             contributionId : contributionId,
313             states : states,            
314             id: windowId,
315             layout: 'form',
316             width: 700,
317             autoScroll: true,
318             title: 'FIBU Editor',
319             autoHeight: true,
320             bodyStyle: 'padding:5px',
321             hideLabels: true,
322             frame: true,
323             modal: true,
324             constrainHeader: true,
325             buttons: [{
326                 text: 'DONE',
327                 handler: function () {
328                     Ext.getCmp('FFFIBUUDEditor' + acGlobalDelimiter + contributionId).close();
329                 }
330             }],
331             buttonAlign: 'center'
332         });
333         actionCenterAPI.getSystemElementConfiguration("AC_Fibu", "Main", FIBUEditorWindow, FIBUEditorWindow.buildEditors);
334      },
335 
336     /**
337      * Launches the drop-event editor.
338      *
339      * @param popRuleContributionId
340      *          the population rule contribution id
341      * @param componentContributionId
342      *          the component contribution id
343      * @param states
344      *          the array of states
345      * @return void
346      */
347     DropEventsEditor: function (popRuleContributionId, componentContributionId, states) {
348         this.columns = states.length;
349         var editor = null;
350         var form = null;
351         var tree = null;
352         for (var i = 0; i < this.columns; i++) {
353             form = new actioncenter.DropEventEditorForm({
354                 superiorId: popRuleContributionId,
355                 dropType: states[i],
356                 layout: 'form',
357                 width: 280,
358                 border: false,
359                 style: 'padding-top: 6px;'
360             });
361             tree = new actioncenter.populationRuleSelector({
362                 superiorId: popRuleContributionId,
363                 componentId: componentContributionId,
364                 dropType: states[i],
365                 layout: 'fit',
366                 columnWidth: 1,
367                 height: 250
368             });
369             editor = {
370                 xtype: 'panel',
371                 layout: 'column',
372                 id: 'editorPanel' + states[i] + acGlobalDelimiter + popRuleContributionId,
373                 items: [form, tree],
374                 border: false,
375                 width: 500,
376                 height: 250,
377                 treePanel: tree
378             };
379         }
380 
381         var dropEventEditorWindow = new Ext.Window({
382             y : 25,
383             id: 'DropEventEditor' + acGlobalDelimiter + popRuleContributionId,
384             layout: 'fit',
385             title: 'Drop Event Editor',
386             bodyStyle: 'padding:5px',
387             hideLabels: true,
388             frame: true,
389             modal: true,
390             constrainHeader: true,
391             items: [editor],
392             buttons: [{
393                 text: 'Close',
394                 handler: function () {
395                     Ext.getCmp('DropEventEditor' + acGlobalDelimiter + popRuleContributionId).close();
396                 }
397             }],
398             buttonAlign: 'center',
399             toolbars: []
400         });
401         tree.containingWindow = dropEventEditorWindow;
402         dropEventEditorWindow.show();
403     },
404 
405     /**
406      * Starts the listeners for client messages.
407      *
408      * @return void
409      */
410     startClientMessageListener : function () {
411         actionCenterAPI.startBatch();
412         dojox.cometd.subscribe("/ActionCenters/message/error",   actionCenterUtility, 'displayClientMessage');
413         dojox.cometd.subscribe("/ActionCenters/message/info",    actionCenterUtility, 'displayClientMessage');
414         dojox.cometd.subscribe("/ActionCenters/message/debug",   actionCenterUtility, 'displayClientMessage');
415         dojox.cometd.subscribe("/ActionCenters/message/warning", actionCenterUtility, 'displayClientMessage');
416         actionCenterAPI.endBatch();
417     },
418 
419     /**
420      * Displays the specified message to the client.
421      *
422      * @param msg
423      *          the message
424      * @return void
425      */
426     displayClientMessage : function (msg) {
427         var messageLevel = actionCenterJSON.getMsgProperty(msg, 'data.userMessage.severity');
428         var messageText  = actionCenterJSON.getMsgProperty(msg, 'data.userMessage.text');
429         var msgIconCls = null;
430         switch (messageLevel) {
431             case 'DEBUG':   msgIconCls = Ext.MessageBox.QUESTION;
432                             break;
433             case 'WARNING': msgIconCls = Ext.MessageBox.WARNING;
434                             break;
435             case 'ERROR':   msgIconCls = Ext.MessageBox.ERROR;
436                             break;
437             case 'INFO':    msgIconCls = Ext.MessageBox.INFO;
438                             break;
439             default:        msgIconCls = Ext.MessageBox.WARNING;
440                             break;
441         }
442         Ext.MessageBox.show({
443             title: 'Client Message',
444             maxWidth : 700,
445             msg: messageText,
446             buttons: Ext.MessageBox.OK,
447             icon: msgIconCls
448         });
449     },
450 
451     /**
452      * Re-orders the specified new node as a child of the specified root node.
453      *
454      * @param rootNode
455      *          the root node
456      * @param newNode
457      *          the new node
458      * @param parentNode
459      *          the parent node
460      * @return void
461      */
462     reorder : function (rootNode, newNode, parentNode) {
463         if (!parentNode) {
464             parentNode = rootNode;
465         }
466         var desiredOrder = null;
467         if (parentNode.attributes && parentNode.attributes.order) {
468             desiredOrder = parentNode.attributes.order.split(',');
469         }
470         
471 //        var sm = rootNode.getOwnerTree().getSelectionModel();
472 //        //remember all the continuous selection pairs if this editOrder is not called rigth after a new node is created
473 //        if(sm.markContinuousSelectionPairs){
474 //          sm.markContinuousSelectionPairs();
475 //        }
476         
477         // Tracks whether a new node still needs to be placed
478         if (newNode) {
479             rootNode.appendChild(newNode);
480         }
481         //check whether the tree is in focus
482         var treeInFocus = false; 
483         rootNode.cascade(function(n){
484             if(n.ui.anchor ===  document.activeElement){
485                 treeInFocus = true;
486             }
487         });
488 
489         // sort when loadMonitor is not active and if there exists a desired order
490         if ((!parentNode.getOwnerTree().loadMonitor ||
491             !parentNode.getOwnerTree().loadMonitor.isActive) && desiredOrder) {
492             rootNode.sort(
493                 function (node1, node2) {
494                     var contributionId1 = node1.contributionId;
495                     var contributionId2 = node2.contributionId;
496                     var index1 = desiredOrder.length + 1;
497                     var i;
498                     for (i = 0; i < desiredOrder.length; i++) {
499                         if (contributionId1 === desiredOrder[i]) {
500                             index1 = i;
501                             break;
502                         }
503                     }
504                     var index2 = desiredOrder.length + 1;
505                     for (i = 0; i < desiredOrder.length; i++) {
506                         if (contributionId2 === desiredOrder[i]) {
507                             index2 = i;
508                             break;
509                         }
510                     }
511                     if (index1 < index2) {
512                         return -1;
513                     } else if (index1 > index2) {
514                         return 1;
515                     } else {
516                         return 0;
517                     }
518                 }
519             );
520         }
521         
522 //        //if there are visible nodes existing between the original continuous selected pairs, select them all
523 //        if(sm.selectNodesBetweenPairs){
524 //            sm.selectNodesBetweenPairs();
525 //        }
526         
527         if(treeInFocus){
528             if (rootNode.firstChild && rootNode.ui && rootNode.firstChild.ui.anchor){
529               rootNode.firstChild.ui.anchor.focus();
530             }
531         }
532         
533         if (rootNode.getOwnerTree().enableLineNumbering) {
534             this.reorderCounter(rootNode);
535         }
536     },
537 
538     /**
539      * Utility function to re-order line number counters.
540      *
541      * @param rootNode
542      *          the root node whose child nodes need to be re-ordered
543      * @param isRootCounterChanged
544      *          indicates whether the parent node's counter changed
545      * @return void
546      */
547     reorderCounter: function (rootNode, isParentCounterChanged) {
548         for (var i = 0; i < rootNode.childNodes.length; i++) {
549             var currentNode = rootNode.childNodes[i];
550             var uiCounter = null;
551             if (!currentNode.counter || currentNode.counter !== (i + 1) || isParentCounterChanged) {
552                 // set the counters
553                 currentNode.counter = i + 1;
554                 if (rootNode.ui.counter) {
555                     uiCounter = rootNode.ui.counter + currentNode.counter + '.';
556                 } else {
557                     uiCounter = currentNode.counter + '.';
558                 }
559                 currentNode.ui.counter = uiCounter;
560                 if (!currentNode.ui.rendered) {
561                     currentNode.ui.render(true);
562                 }
563 
564                 if (currentNode.ui.counterNode) {
565                     currentNode.ui.counterNode.firstChild.nodeValue = uiCounter;
566                     currentNode.fireEvent('counterchange');
567                 }
568                 // re-order children's counters as well
569                 this.reorderCounter(currentNode, true);
570             }
571         }
572     },
573 
574     /**
575      * Re-orders the specified new node as a child of the specified root node.
576      *
577      * @param rootNode
578      *          the root node
579      * @param newNode
580      *          the new node
581      * @param parentNode
582      *          the parent node
583      * @return void
584      */
585     reorderGrid : function (rootNode, newNode, parentNode) {
586         if (!parentNode) {
587             parentNode = rootNode;
588         }
589         var desiredOrder = {};
590         if (parentNode.order) {
591             desiredOrder = parentNode.order.split(',');
592         }
593         // Tracks whether a new node still needs to be placed
594         var newNodeNeedsPlaced = (newNode !== undefined && newNode !== null);
595         // Keep track of where we are in the rootNode's children
596         var childIndex = 0;
597         var nodeAtPoint = null;
598         // Iterate through the desired order list
599         for (var i = 0; i < desiredOrder.length; i++) {
600             // The id of the location we are in the parent's orderList
601             var currentId = desiredOrder[i];
602             console.log("currentId is " + currentId);
603             // If we are past the end of the child node array
604             if (childIndex >= rootNode.childNodes.length) {
605                 break;
606                 // else if the current node matches the one in the order list
607             } else if (currentId === rootNode.item(childIndex).contributionId) {
608                 childIndex++;
609                 // else if the new node needs to be placed here
610             } else if (newNodeNeedsPlaced && (currentId === newNode.contributionId)) {
611                 nodeAtPoint = rootNode.item(childIndex);
612                 console.log("nodeAtPoint item(" + childIndex + ") " + nodeAtPoint.name + " and " + newNode.name +
613                     "will be placed before it.");
614                 //**********************************************************
615                 rootNode.insertBefore(newNode, nodeAtPoint);
616 
617                 //**********************************************************
618                 newNodeNeedsPlaced = false;
619                 childIndex++;
620             } else {
621                 var node = rootNode.findChild("contributionId", currentId);
622                 // If the node that should be in this location is somewhere else in the child array
623                 if (node) {
624                     //************************************************************
625                     rootNode.removeChild(node);
626                     console.log("removing node " + node.name);
627                     nodeAtPoint = rootNode.item(childIndex);
628                     node.rendered = false;
629                     rootNode.insertBefore(node, nodeAtPoint);
630                     console.log("nodeAtPoint item(" + childIndex + ") " + nodeAtPoint.name + " and " + node.name +
631                         " will be placed before it.");
632 
633                     //***********************************************************
634                     childIndex++;
635                 }
636             }
637         }
638         // If we have gone through the complete list of children and still not found a place for the new node
639         if (newNodeNeedsPlaced) {
640             console.log("no place found so appending " + newNode.name);
641             rootNode.appendChild(newNode);
642         }
643         rootNode.renderChildren();
644     },
645 
646     /**
647      * Checks if the specified candidate is contained in the specified list.
648      *
649      * @param candidate
650      *          the candidate
651      * @param list
652      *          the list
653      * @return true if the candidate is in the list.
654      */
655     isInList : function (candidate, list) {
656         var retVal = false;
657         for (var i = 0; i < list.length; i++) {
658             // The id of the location we are in the parent's orderList
659             var currentThing = list[i];
660             if (candidate === currentThing) {
661                 retVal = true;
662             }
663         }
664         return retVal;
665     },
666 
667     /**
668      * Re-orderes the specified new node in the list of child nodes of the specified parent node.
669      *
670      * @param parentNode
671      *          the parent node
672      * @param newNode
673      *          the new node
674      * @return void
675      */
676     reorderInList : function (parentNode, newNode) {
677         var component;
678         var desiredOrder = {};
679         if (parentNode.order) {
680             desiredOrder = parentNode.order.split(',');
681         }
682         // Tracks whether a new node still needs to be placed
683         var newNodeNeedsPlaced = (newNode !== undefined && newNode !== null);
684         // Keep track of where we are in the rootNode's children
685         var childIndex = 0;
686         if (parentNode.items) {
687             // Iterate through the desired order list
688             for (var i = 0; i < desiredOrder.length; i++) {
689                 // The id of the location we are in the parent's orderList
690                 var currentId = desiredOrder[i];
691                 // If we are past the end of the child node array
692                 if (childIndex >= parentNode.items.length) {
693                     break;
694                     // else if the current node matches the one in the order list
695                 } else if (currentId === parentNode.items.items[childIndex].contributionId) {
696                     childIndex++;
697                     // else if the new node needs to be placed here
698                 } else if (newNodeNeedsPlaced && (currentId === newNode.contributionId)) {
699                     var nodeAtPoint = parentNode.items.items[childIndex];
700                     component = parentNode.insert(childIndex, newNode);
701                     newNodeNeedsPlaced = false;
702                     childIndex++;
703                 } else {
704                     // The node that should be in this location is not here, look for it someplace else in the array
705                     var object = null;
706                     parentNode.items.each(function (o) {
707                         if (currentId === o.contributionId) {
708                             object = o;
709                         }
710                     });
711                     // if it is found someplace else in the array, remove it and put it here
712                     if (object) {
713                         this.remove(object);
714                         component = parentNode.insert(childIndex, newNode);
715                         childIndex++;
716                     }
717                 }
718             }
719         }
720         // If we have gone through the complete list of children and still not found a place for the new node
721         if (newNodeNeedsPlaced) {
722             component = parentNode.add(newNode);
723         }
724         parentNode.doLayout();
725 
726         return component;
727     },
728 
729     /**
730      * Converts a string array to a delimited string.
731      *
732      * @param stringArray
733      *          an array of strings
734      * @return a delimitedString
735      */
736     convertStringArrayToDelimitedString: function (stringArray) {
737         var returnValue = '';
738         if (Ext.isArray(stringArray)) {
739             if (stringArray && stringArray.length > 0) {
740                 returnValue = returnValue + stringArray[0];
741             }
742             for (var index = 1; index < stringArray.length; index++) {
743                 returnValue = returnValue + acGlobalDelimiter + stringArray[index];
744             }
745         } else {
746             returnValue = stringArray;
747         }
748         return returnValue;
749     },
750 
751     /**
752      * Finds the node of the specified target type in the specified array of nodes to inspect.
753      *
754      * @param targetType
755      *          the target node type
756      * @param nodesToInspect
757      *          the array of nodes to inspect
758      * @return the found node or null otherwise
759      */
760     findNodeType: function (targetType, nodesToInspect) {
761         var foundNode = null;
762         if (nodesToInspect.length) {
763             var index = 0;
764             while (index < nodesToInspect.length && !foundNode) {
765                 // from the node find a node of 'targetType'
766                 if (nodesToInspect[index].type === targetType) {
767                     foundNode = nodesToInspect[index];
768                 } else {
769                     foundNode = nodesToInspect[index].find('type', targetType);
770                     if (foundNode && foundNode.length > 0) {
771                         foundNode = foundNode[0]; //simply get the first node that pops up.
772                     } else {
773                         foundNode = null; //in case foundNode is an empty array, reset to null
774                     }
775                 }
776                 index++;
777             }
778         }
779         return foundNode;
780     },
781     performStandardTreeModeMove: function (e, typecast) {
782         for (var c = 0; c < e.dropNode.length; c++) {
783             var relationshipToSuperior = "childof";
784             if (typecast && typecast.indexOf(acKeyValueSeperator) != -1) {
785                 var temp = typecast.split(acKeyValueSeperator);
786                 relationshipToSuperior = temp[1];
787                 typecast = temp[0];
788             }
789             var moveMap = actionCenterUtility.getContributionTreeDropMap(e, c, typecast);
790             actionCenterUtility.executeStandardTreeNodeMove(moveMap, relationshipToSuperior);
791         }
792     },
793     /**
794      * Gets a map specifying contribution info for tree node drops.
795      *
796      * @param e
797      *          the drop event
798      * @param c
799      *          the counter within drop nodes
800      * @param t
801      *          the type to cast the contribution to
802      *
803      * @return the tree drop node map
804      */
805     getContributionTreeDropMap: function (e, c, t) {
806         var parentNd = e.dropNode[c].parentNode;
807         var childNd = e.dropNode[c];
808         switch (parentNd.type) {
809             case 'AC_Standard_Activity_Palette':
810             case 'AC_Agenda_Palette':
811             case 'AC_Thinklet_Palette':
812             case 'AC_Screen_Palette':
813             case 'AC_Tool_Palette':
814             case 'AC_Control_Palette':
815             case 'AC_Image_Palette':
816                 parentNd = parentNd.parentNode;
817                 break;
818             case 'AC_Activity':
819                 if (childNd.type !== 'AC_User') {
820                     while (parentNd.type !== 'AC_Project_Definition') {
821                         parentNd = parentNd.parentNode;
822                     }
823                 }
824                 break;
825             default:break;
826         }
827 
828         var afterContributionId = null;
829         var beforeContributionId = null;
830 
831         var ultimateParentNode = null;
832         switch (e.point) {
833             case "append"         : ultimateParentNode = e.target;
834                                   break;
835             case "above"          : ultimateParentNode = e.target.parentNode;
836                                   beforeContributionId = e.target.contributionId;
837                                   break;
838             case "below"        : ultimateParentNode = e.target.parentNode;
839                                   afterContributionId = e.target.contributionId;
840                                   break;
841             case "parentAppend" : ultimateParentNode = e.target.parentNode;
842                                   break;
843             default    : break;
844         }
845         switch (ultimateParentNode.type) {
846             case 'AC_Standard_Activity_Palette':
847             case 'AC_Agenda_Palette':
848             case 'AC_Thinklet_Palette':
849             case 'AC_Screen_Palette':
850             case 'AC_Tool_Palette':
851             case 'AC_Control_Palette':
852             case 'AC_Image_Palette':
853                 ultimateParentNode = ultimateParentNode.parentNode;
854                 break;
855             case 'AC_Activity':
856                 if (childNd.type !== 'AC_User') {
857                     while (ultimateParentNode.type !== 'AC_Project_Definition') {
858                         ultimateParentNode = ultimateParentNode.parentNode;
859                     }
860                 }
861             default: break;
862         }
863 
864         var treeDropMap = {
865             currentParentNode: parentNd,
866             ultimateParentNode: ultimateParentNode,
867             nodeBeingMoved: childNd,
868             afterContributionId: afterContributionId,
869             beforeContributionId: beforeContributionId,
870             typecast: t
871         };
872         return treeDropMap;
873     },
874 
875     /**
876      * Executes a standard tree node move.
877      *
878      * @param moveMap
879      *          the map specifying the node move info
880      * @param relationshipType
881      *          the relationship type of the node after the move
882      * @return void
883      */
884     executeStandardTreeNodeMove: function (moveMap, relationshipType) {
885         var currentParentNode = moveMap.currentParentNode;
886         var ultimateParentNode = moveMap.ultimateParentNode;
887         var nodeBeingMoved = moveMap.nodeBeingMoved;
888         var afterContributionId = moveMap.afterContributionId;
889         var beforeContributionId = moveMap.beforeContributionId;
890         var typecast = moveMap.typecast;
891 
892         var currentParentNodeId = currentParentNode.contributionId;
893         var ultimateParentNodeId = ultimateParentNode.contributionId;
894 
895         if (ultimateParentNode.type === 'AC_Role') {
896             // see if it is a move to a new Activity node if so, get the activity node id and concatenate it
897             var currentActivityNodeId = currentParentNode.parentNode.contributionId;
898             var ultimateActivityNodeId = ultimateParentNode.parentNode.contributionId;
899             if (currentActivityNodeId !== ultimateActivityNodeId) {
900                 currentParentNodeId += acGlobalDelimiter + currentActivityNodeId;
901                 ultimateParentNodeId += acGlobalDelimiter + ultimateActivityNodeId;
902             }
903             if (ultimateParentNode.hasChildNodes()) {
904                 var otherScreenNode = ultimateParentNode.childNodes[0];
905                 var msg = 'A screen already exists here, do you wish to replace it?';
906                 var icon =  Ext.MessageBox.QUESTION;
907                 Ext.Msg.show({
908                     thisNode: this,
909                     title: 'Add Screen',
910                     msg: msg,
911                     buttons: Ext.Msg.YESNO,
912                     icon: icon,
913                     fn: function (btn, text) {
914                         if (btn === 'yes') {
915                             actionCenterAPI.moveContributionToRecycleBin(otherScreenNode.contributionId);
916                             actionCenterAPI.moveContribution2(
917                                 nodeBeingMoved.contributionId,
918                                 currentParentNodeId,
919                                 ultimateParentNodeId,
920                                 relationshipType,
921                                 afterContributionId,
922                                 beforeContributionId,
923                                 typecast,
924                                 nodeBeingMoved.addChannelRelationshipType);
925                         }
926                     }
927                 });
928             } else {
929                 ultimateParentNode.attributes.dropTarget = true;
930                 actionCenterAPI.moveContribution2(
931                     nodeBeingMoved.contributionId,
932                     currentParentNodeId,
933                     ultimateParentNodeId,
934                     relationshipType,
935                     afterContributionId,
936                     beforeContributionId,
937                     typecast,
938                     nodeBeingMoved.addChannelRelationshipType);
939             }
940         } else {
941             if (currentParentNodeId === ultimateParentNodeId) {
942                 actionCenterAPI.moveContribution(
943                     currentParentNodeId,
944                     nodeBeingMoved.contributionId,
945                     afterContributionId,
946                     beforeContributionId);
947             } else {
948                 if (!ultimateParentNode.hasChildNodes()) {
949                     ultimateParentNode.attributes.dropTarget = true;
950                 }
951                 actionCenterAPI.moveContribution2(
952                     nodeBeingMoved.contributionId,
953                     currentParentNodeId,
954                     ultimateParentNodeId,
955                     relationshipType,
956                     afterContributionId,
957                     beforeContributionId,
958                     typecast,
959                     nodeBeingMoved.addChannelRelationshipType);
960             }
961         }
962     },
963 
964     /**
965      * Displays a choice dialog.
966      *
967      * @param choices
968      *          the array of choices to be presented
969      * @param text
970      *          the title of the dialog
971      * @param target
972      *          the target id of the container of the dialog; if null, the dialog is a pop-up window
973      * @param passAlongData
974      *          the data to be passed along in a callback
975      * @param callBackObject
976      *          callback object
977      * @param callBackMethod
978      *          callback method
979      * @return void
980      */
981     choiceDialog: function (choices, text, target, passAlongData, callBackObject, callBackMethod) {
982         if (!target) {
983             target = Ext.id();
984         }
985         var selectItemDialog = Ext.getCmp(target);
986         var choice = null;
987         var itemsList = [];
988         for (var i = 0; i < choices.length; i++) {
989             if (typeof choices[i] === "string") {
990                 if (choices[i].indexOf(acKeyValueSeperator) != -1) {
991                     var tempChoice = choices[i].split(acKeyValueSeperator);
992                     choice = {label: tempChoice[0], value: choices[i]};
993                 } else {
994                     choice = choices[i];
995                 }
996              } else {
997                 choice = choices[i];
998             }
999             var itemsListItem = {
1000                 boxLabel: choice.label ? choice.label : choice,
1001                 inputValue: choice.value ? choice.value : choice,
1002                 name: 'chosenItem'
1003             };
1004             itemsList.push(itemsListItem);
1005         }
1006         if (!selectItemDialog) {
1007             selectItemDialog = new Ext.Window({
1008                 id: target,
1009                 layout: 'form',
1010                 title: text,
1011                 width: 370,
1012                 autoHeight: true,
1013                 bodyStyle: 'padding:5px',
1014                 resizable: false,
1015                 frame: true,
1016                 modal: true,
1017                 constrainHeader: true,
1018                 items: [
1019                     {
1020                         xtype: 'label',
1021                         text: text,
1022                         id: 'selectText',
1023                         hideLabel: true,
1024                         //cls: 'error-text-style',
1025                         anchor: '100%'
1026                     }, {
1027                         xtype: 'radiogroup',
1028                         columns: 1,
1029                         items: itemsList,
1030                         id: 'chosenItem',
1031                         hideLabel: true
1032                     }
1033                 ],
1034                 buttons: [{
1035                     text: 'Submit',
1036                     callbackobject: callBackObject,
1037                     callbackmethod: callBackMethod,
1038                     target: target,
1039                     handler: function () {
1040                         var chosenItemObj = Ext.getCmp(target).findById('chosenItem');
1041                         var chosenItem = chosenItemObj.getValue().inputValue;
1042                         this.callbackmethod.call(this.callbackobject, passAlongData, chosenItem);
1043                         Ext.getCmp(target).close();
1044                     }
1045                 }, {
1046                     text: 'Cancel',
1047                     handler: function () {
1048                         Ext.getCmp(target).close();
1049                     }
1050                 }],
1051                 buttonAlign: 'center'
1052             });
1053         }
1054         selectItemDialog.show();
1055     },
1056     getBrowserDimensions: function () {
1057         var myWidth = 0, myHeight = 0;
1058         if (typeof(window.innerWidth) === 'number') {
1059             // Non-IE
1060             myWidth  = window.innerWidth;
1061             myHeight = window.innerHeight;
1062         } else if (document.documentElement && (document.documentElement.clientWidth || document.documentElement.clientHeight)) {
1063             // IE 6+ in 'standards compliant mode'
1064             myWidth  = document.documentElement.clientWidth;
1065             myHeight = document.documentElement.clientHeight;
1066         } else if (document.body && (document.body.clientWidth || document.body.clientHeight)) {
1067             // IE 4 compatible
1068             myWidth  = document.body.clientWidth;
1069             myHeight = document.body.clientHeight;
1070         }
1071         return {
1072             width: myWidth,
1073             height: myHeight
1074         }
1075     }
1076 };
1077 /**
1078  * Default constructor for ShallowSelectionNode
1079  *
1080  *
1081  * @class Data object for keeping track of what contributions need to be copied with a shallow copy
1082  */
1083 
1084 AC.data.ShallowSelectionNode = function(i, c) {
1085       this.contiguousIds = c || [i];
1086       this.addContiguousId = function(i) {
1087           this.contiguousIds.push(i);
1088       };
1089 };
1090 /**
1091  * Default constructor for ShallowSelectionToken
1092  *
1093  * @class Data object used for processing a tree and determining what contributions will be copied
1094  *        when a shallow copy is required.
1095  */
1096 
1097 AC.data.ShallowSelectionToken = function(a, c, i) {
1098       this.selectionArray = a || [];
1099       this.contiguousSelection = c;
1100       if (i) {
1101           this.index = i;
1102       } else {
1103           this.index = 0;
1104       }
1105  };
1106 /**
1107  * Default constructor.
1108  *
1109  * @class Manages the clipboard.
1110  */
1111 AC.data.Clipboard = {
1112 
1113     /**
1114      * A property to store the current paste GUID
1115      * @field
1116      * @default null
1117      */
1118     clientGUID : false,
1119     /**
1120      * A flag to indicate a copy/cut is in process
1121      * @field
1122      * @default false
1123      */
1124     pendingCopy : false,
1125     /**
1126      * A list of items deep copied to the clipboard.
1127      *
1128      * @field
1129      * @default null
1130      */
1131     copiedItemList : null,
1132     /**
1133      *
1134      */
1135     /**
1136      * A list of items shallow copied to the clipboard.
1137      *
1138      * @field
1139      * @default null
1140      */
1141     shallowCopiedItemList : null,
1142     /**
1143      * The id of the source of the data in the clipboard
1144      * @field
1145      * @default null
1146      */
1147     sourceId : null,
1148     /**
1149      * ActionCenter listener.
1150      *
1151      * @field
1152      * @default null
1153      */
1154     actionCenterListener : null,
1155 
1156     /**
1157      * Gets a clipboard contribution and invokes subscribeToClipboard as a callback method.
1158      *
1159      * @return void
1160      */
1161     initiateClipboard : function () {
1162         actionCenterAPI.getPersonalWorkspaceContribution(
1163             "AC_Clipboard", AC.data.Clipboard, AC.data.Clipboard.subscribeToClipboard);
1164     },
1165 
1166     /**
1167      * Copies clipboard items from the specified clipboard contribution and subscribes to the clipboard update channel.
1168      *
1169      * @param clipboard
1170      *          the clipboard contribution
1171      * @return void
1172      */
1173     subscribeToClipboard : function (clipboard) {
1174         if (clipboard && clipboard.getProperty('clipboardTypes')) {
1175             this.copiedItemList = clipboard.getProperty('clipboardTypes').getValue();
1176         }
1177         if (clipboard && clipboard.getProperty('clipboardShallowTypes')) {
1178             this.shallowCopiedItemList = clipboard.getProperty('clipboardShallowTypes').getValue();
1179         }
1180         if (clipboard && clipboard.getProperty('sourceId')) {
1181             this.sourceId = clipboard.getProperty('sourceId').getValue();
1182         }
1183         if (!this.actionCenterListener) {
1184             this.actionCenterListener = new ActionCenterListener();
1185         }
1186         this.actionCenterListener.getContributionUpdates(clipboard.getId(), AC.data.Clipboard, 'replaceClipboard');
1187     },
1188 
1189     /**
1190      * Replaces contents of the clipboard with the contents of the clipboard specified in the message.
1191      *
1192      * @param msg
1193      *          the message with the new clipboard contents
1194      * @return void
1195      */
1196     replaceClipboard : function (msg) {
1197         AC.data.Clipboard.copiedItemList = msg.data.contribution.contributionProperties.clipboardTypes.value;
1198         AC.data.Clipboard.shallowCopiedItemList = msg.data.contribution.contributionProperties.clipboardShallowTypes.value;
1199         AC.data.Clipboard.sourceId = msg.data.contribution.contributionProperties.sourceId.value;
1200         AC.data.Clipboard.pendingCopy = false;
1201     },
1202     /**
1203      * Sets the value of the pendingCopy flag
1204      *
1205      */
1206     setPendingCopy : function (p) {
1207         AC.data.Clipboard.pendingCopy = p;
1208     },
1209 
1210     /**
1211      * Gets the value of the pendingCopy flag
1212      * @return boolean value
1213      */
1214     isPendingCopy : function () {
1215         return AC.data.Clipboard.pendingCopy;
1216     },
1217     /**
1218      * Displays a waiting message while pendingCopy flag is true
1219      *
1220      */
1221     pasteToNode : function() {
1222         if (AC.data.Clipboard.node) {
1223             if (!AC.data.Clipboard.node.busyEl && AC.data.Clipboard.node.ui.elNode) {
1224                 AC.data.Clipboard.node.busyEl = Ext.fly(AC.data.Clipboard.node.ui.elNode).createChild({
1225                     html: 'Waiting for previous clipboard copy...'
1226                 });
1227                 AC.data.Clipboard.node.busyEl.addClass('loading-indicator');
1228                 AC.data.Clipboard.node.busyEl.setStyle('z-index', '15000');
1229                 AC.data.Clipboard.node.busyEl.setVisible(true);
1230             }
1231             AC.data.Clipboard.node.pasteToThisNode(AC.data.Clipboard.pasteInfo.position, AC.data.Clipboard.pasteInfo.relationshipType);
1232         }
1233     },
1234     clearPendingInfo : function() {
1235         if (AC.data.Clipboard.node && AC.data.Clipboard.node.busyEl) {
1236             AC.data.Clipboard.node.busyEl.remove();
1237         }
1238         AC.data.Clipboard.node = null;
1239         AC.data.Clipboard.pasteInfo = null;
1240 
1241     },
1242     /**
1243      * Gets the deep contents of the clipboard.
1244      *
1245      * @return the list of deep copied item types
1246      */
1247     getClipboardTypes : function () {
1248         return AC.data.Clipboard.copiedItemList;
1249     },
1250     /**
1251      * Gets the shallow contents of the clipboard.
1252      *
1253      * @return the list of shallow copied item types
1254      */
1255     getShallowClipboardTypes : function () {
1256         return AC.data.Clipboard.shallowCopiedItemList;
1257     },
1258 
1259     /**
1260      * Gets the contents of the clipboard.
1261      *
1262      * @return the source component id of the clipboard contents
1263      */
1264     getSourceId : function () {
1265         return AC.data.Clipboard.sourceId;
1266     },
1267 
1268     /**
1269      * Sets the clientGUID
1270      */
1271     setClientGUID: function (clientGUID) {
1272         AC.data.Clipboard.clientGUID = clientGUID;
1273     },
1274 
1275     /**
1276      * gets the clientGUID
1277      *
1278      * @return (String) The clientGUID
1279      */
1280     getClientGUID: function () {
1281         return AC.data.Clipboard.clientGUID;
1282     }
1283 };
1284 
1285 
1286 // TODO: move the JSON utility into AC namespace
1287 /**
1288  * Default constructor.
1289  *
1290  * @class A utility class for manipulation of JSON messages.
1291  */
1292 var actionCenterJSON = {
1293 
1294     /**
1295      * A boolean constant denoting whether to log messages to the console.
1296      *
1297      * @field
1298      * @type bool
1299      * @default false
1300      */
1301     logmsgs: false,
1302 
1303     /**
1304      * Queries the specified JSON message using the specified path and returns message contents on the path or null
1305      * if the path is invalid.
1306      *
1307      * @param             msg     a JSON message
1308      * @param {String}    path    a dot-separated path to a message attribute
1309      * @returns null, if the message is null; or null, if the path is invalid; or the evaluated message on the path
1310      *            contents.  If the evaluated message has a value attribute, it's value is returned
1311      */
1312     getMsgPropertyValue: function (msg, path) {
1313         var property = this.getMsgProperty(msg, path);
1314         if (property && property.value) {
1315             return property.value;
1316         } else {
1317             return property;
1318         }
1319     },
1320 
1321     /**
1322      * Queries the specified JSON message using the specified path and returns message contents on the path or null
1323      * if the path is invalid.
1324      *
1325      * @param             msg     a JSON message
1326      * @param {String}    path    a dot-separated path to a message attribute
1327      * @returns null, if the message is null; or null, if the path is invalid; or the evaluated message on the path
1328      *            contents
1329      */
1330     getMsgProperty: function (msg, path) {
1331         if (!msg) {
1332             return null;
1333         }
1334         var pathelements = path.split('.');
1335         var builtpath = "msg.";
1336         var evaluatedProperty = null;
1337         for (var i = 0; i < pathelements.length; i++) {
1338             if (i === 0) {
1339                 builtpath += pathelements[i];
1340             } else {
1341                 builtpath = builtpath + "." + pathelements[i];
1342             }
1343             try {
1344                 evaluatedProperty = eval(builtpath);
1345             } catch (error) {
1346                 if (this.logmsgs) {
1347                     console.log(path + " does not have a valid path in this message.");
1348                 }
1349             }
1350             if (evaluatedProperty === null) {
1351                 if (this.logmsgs) {
1352                     console.log(path + " does not have a valid path in this message.");
1353                 }
1354                 return null;
1355             }
1356         }
1357         return evaluatedProperty;
1358     },
1359 
1360     /**
1361      * Sanitizes the specified string by replacing special characters by their string-safe equivalents.
1362      *
1363      * @param {String} inputString the string to make safe
1364      * @returns a string-safe equivalent
1365      */
1366     makeStringSafe : function (inputString) {
1367         inputString = inputString.replace(/\n/g, " ");
1368         inputString = inputString.replace(/\r/g, " ");
1369         inputString = inputString.replace(/\\/g, "\\\\");
1370         //inputString = inputString.replace(/\b/g, " ");
1371         //inputString = inputString.replace(/\t/g, " ");
1372         inputString = inputString.replace(/'/g, "\\'");
1373         inputString = inputString.replace(/"/g, "\\\"");
1374         return inputString;
1375     }
1376 };
1377 
1378 /**
1379  * Default constructor.
1380  *
1381  * @class Defines standard ActionCenters constants
1382  */
1383 AC.util.Constants = {
1384 
1385     /**
1386      * The key to access the id attribute.
1387      *
1388      * @constant
1389      * @type String
1390      * @default "id"
1391      */
1392     actionCenterIdKey: "id",
1393 
1394     /**
1395      * The key to access the type attribute.
1396      *
1397      * @constant
1398      * @type String
1399      * @default "type"
1400      */
1401     actionCenterTypeKey: "type",
1402 
1403     /**
1404      * The key to access the user attribute.
1405      *
1406      * @constant
1407      * @type String
1408      * @default "user"
1409      */
1410     actionCenterUserKey: "user",
1411 
1412     /**
1413      * The key to access the workspace attribute.
1414      *
1415      * @constant
1416      * @type String
1417      * @default "workspace"
1418      */
1419     actionCenterWorkspaceKey: "workspace",
1420 
1421     /**
1422      * The key to access the fromDate attribute.
1423      *
1424      * @constant
1425      * @type String
1426      * @default "fromDate"
1427      */
1428     actionCenterFromDateKey: "fromDate",
1429 
1430     /**
1431      * The key to access the toDate attribute.
1432      *
1433      * @constant
1434      * @type String
1435      * @default "toDate"
1436      */
1437     actionCenterToDateKey: "toDate",
1438 
1439     /**
1440      * The key to access the relationships attribute.
1441      *
1442      * @constant
1443      * @type String
1444      * @default "relationships"
1445      */
1446     actionCenterRelationshipsKey: "relationships",
1447 
1448     /**
1449      * The key to access the contribution properties attribute.
1450      *
1451      * @constant
1452      * @type String
1453      * @default "contributionProperties"
1454      */
1455     actionCenterContributionPropertiesKey: "contributionProperties",
1456 
1457     /**
1458      * The key to access the add contribution properties attribute.
1459      *
1460      * @constant
1461      * @type String
1462      * @default "addContributionProperties"
1463      */
1464     actionCenterAddContributionPropertiesKey: "addContributionProperties",
1465 
1466     /**
1467      * The key to access the remove contribution properties attribute.
1468      *
1469      * @constant
1470      * @type String
1471      * @default "removeContributionProperties"
1472      */
1473     actionCenterRemoveContributionPropertiesKey: "removeContributionProperties",
1474 
1475     /**
1476      * The key to access the update contribution properties attribute.
1477      *
1478      * @constant
1479      * @type String
1480      * @default "updateContributionProperties"
1481      */
1482     actionCenterUpdateContributionPropertiesKey: "updateContributionProperties",
1483 
1484     /**
1485      * The key to access the value attribute.
1486      *
1487      * @constant
1488      * @type String
1489      * @default "value"
1490      */
1491     actionCenterValueKey: "value",
1492 
1493     /**
1494      * The key to access the locked by attribute.
1495      *
1496      * @constant
1497      * @type String
1498      * @default "lockedBy"
1499      */
1500     actionCenterLockedByKey: "lockedBy",
1501 
1502     /**
1503      * The key to access the contribution attribute.
1504      *
1505      * @constant
1506      * @type String
1507      * @default "Contribution"
1508      */
1509     actionCenterContributionKey: "Contribution",
1510 
1511     /**
1512      * The key to access the thumb prints attribute.
1513      *
1514      * @constant
1515      * @type String
1516      * @default "thumbprints"
1517      */
1518     actionCenterThumbprintsKey: "thumbprints",
1519 
1520     /**
1521      * The key to access the disabled flag attribute.
1522      *
1523      * @constant
1524      * @type String
1525      * @default "disabledFlag"
1526      */
1527     actionCenterDisabledFlagKey: "disabledFlag",
1528 
1529     /**
1530      * The key to access the email attribute.
1531      *
1532      * @constant
1533      * @type String
1534      * @default "email"
1535      */
1536     actionCenterEmailKey: "email",
1537 
1538     /**
1539      * The key to access the pending approval flag attribute.
1540      *
1541      * @constant
1542      * @type String
1543      * @default "pendingApprovalFlag"
1544      */
1545     actionCenterPendingApprovalFlagKey: "pendingApprovalFlag",
1546 
1547     /**
1548      * The key to access the first name attribute.
1549      *
1550      * @constant
1551      * @type String
1552      * @default "firstName"
1553      */
1554     actionCenterFirstNameKey: "firstName",
1555 
1556     /**
1557      * The key to access the last name attribute.
1558      *
1559      * @constant
1560      * @type String
1561      * @default "lastName"
1562      */
1563     actionCenterLastNameKey: "lastName",
1564 
1565     /**
1566      * The key to access the system roles attribute.
1567      *
1568      * @constant
1569      * @type String
1570      * @default "systemRoles"
1571      */
1572     actionCenterSystemRolesKey: "systemRoles",
1573 
1574     /**
1575      * The key to access the username attribute.
1576      *
1577      * @constant
1578      * @type String
1579      * @default "username"
1580      */
1581     actionCenterUsernameKey: "username",
1582 
1583     /**
1584      * The key to access the workspace roles attribute.
1585      *
1586      * @constant
1587      * @type String
1588      * @default "workspaceRoles"
1589      */
1590     actionCenterWorkspaceRolesKey: "workspaceRoles",
1591 
1592     /**
1593      * The key to access the response type attribute in a message.
1594      *
1595      * @constant
1596      * @type String
1597      * @default 'ResponseType'
1598      */
1599     actionCenterResponseTypeKey: 'ResponseType',
1600 
1601     /**
1602      * The key to access the contribution attribute in a response message from the server.
1603      *
1604      * @constant
1605      * @type String
1606      * @default 'Contribution'
1607      */
1608     actionCenterResponseTypeContributionKey: 'Contribution',
1609 
1610     /**
1611      * The key to access the user attribute in a response message from the server.
1612      *
1613      * @constant
1614      * @type String
1615      * @default 'User'
1616      */
1617     actionCenterResponseTypeUserKey: 'User',
1618 
1619     /**
1620      * The key to access the user list attribute in a response message from the server.
1621      *
1622      * @constant
1623      * @type String
1624      * @default 'UserList'
1625      */
1626     actionCenterResponseTypeUserListKey: 'UserList'
1627 };
1628 
1629 /**
1630  * Creates a new contribution from the specified message.
1631  *
1632  * @class Represents a contribution.
1633  * @param msg a JSON message
1634  */
1635 AC.data.Contribution = function (msg) {
1636 
1637     /**
1638      * The contents of the JSON message.
1639      */
1640     this._msg = msg;
1641 
1642     /**
1643      * Gets an attribute's value from the message based on the specified attribute key.
1644      *
1645      * @param {String} key the key of the message attribute.
1646      * @return the value of the message attribute
1647      */
1648     this.getAttribute = function (key) {
1649         return actionCenterJSON.getMsgProperty(this._msg, key);
1650     };
1651 
1652     /**
1653      * Gets the contribution id.
1654      *
1655      * @return the contribution id
1656      */
1657     this.getId = function () {
1658         return this.getAttribute(AC.util.Constants.actionCenterIdKey);
1659     };
1660 
1661     /**
1662      * Gets the contribution type.
1663      *
1664      * @return the contribution type
1665      */
1666     this.getType = function () {
1667         return this.getAttribute(AC.util.Constants.actionCenterTypeKey);
1668     };
1669 
1670     /**
1671      * Gets the contribution's user contents from the message and instantiates the user.
1672      *
1673      * @return the contribution's user
1674      */
1675     this.getUser = function () {
1676         return new AC.data.User(this.getAttribute(AC.util.Constants.actionCenterUserKey));
1677     };
1678 
1679     /**
1680      * Gets the contribution's workspace contents from the message and instantiates the workspace.
1681      *
1682      * @return the contribution's workspace
1683      */
1684     this.getWorkspace = function () {
1685         // TODO: where is the definition of ActionCenterWorkspace?
1686         return new ActionCenterWorkspace(this.getAttribute(AC.util.Constants.actionCenterWorkspaceKey));
1687     };
1688 
1689     /**
1690      * Gets the contribution's time stamp.
1691      *
1692      * @return the contribution's time stamp
1693      */
1694     this.getFromDate = function () {
1695         return this.getAttribute(AC.util.Constants.actionCenterFromDateKey);
1696     };
1697 
1698     /**
1699      * Gets the contribution's relationships.
1700      *
1701      * @return the contribution's relationships
1702      */
1703     this.getRelationships = function () {
1704         return this.getAttribute(AC.util.Constants.actionCenterRelationshipsKey);
1705     };
1706 
1707     /**
1708      * Gets the contribution's properties.
1709      *
1710      * @return the contribution's properties
1711      */
1712     this.getProperties = function () {
1713         return this.getAttribute(AC.util.Constants.actionCenterContributionPropertiesKey);
1714     };
1715 
1716     /**
1717      * Gets an String with properties in format "{propertyKey:value, propertyKey:value}"
1718      *
1719      * @return a string that represents an object with all of the properties as elements
1720      */
1721     this.getPropertiesObject = function () {
1722         var mixedCollection = new Ext.util.MixedCollection();
1723         mixedCollection.addAll(this.getProperties());
1724         mixedCollection.propertiestring = '{';
1725         var buildPropertyString = function (item, index, length) {
1726             // this is a temporary fix to avoid a crash, need to figure out a better way to fix, trying to decode
1727             // the activityReportTemplate property causes an error
1728             if (item.propertyKey === 'activityReportTemplate' || item.propertyKey === 'undefined' || !item.propertyKey) {
1729                 return true;
1730             }
1731             mixedCollection.propertiestring += '"' + item.propertyKey + '":"' + item.value + '"';
1732             if (index + 1 < length) {
1733                 mixedCollection.propertiestring += ",";
1734             }
1735             return true;
1736         };
1737 
1738         mixedCollection.each(buildPropertyString);
1739         mixedCollection.propertiestring += "}";
1740         return mixedCollection.propertiestring;
1741     };
1742 
1743     /**
1744      * Gets a property object, where a property's value is indexed by the property's key.
1745      *
1746      * @return this contribution's properties wrapped as an object
1747      */
1748     this.getParsedProperties = function () {
1749         var mixedCollection = new Ext.util.MixedCollection();
1750         mixedCollection.addAll(this.getProperties());
1751         mixedCollection.propertiestring = '{';
1752         var buildPropertyString = function (item, index, length) {
1753             // this is a temporary fix to avoid a crash, need to figure out a better way to fix, trying to decode
1754             // the activityReportTemplate property causes an error
1755             if (item.propertyKey === 'activityReportTemplate' || item.propertyKey === 'undefined' || !item.propertyKey) {
1756                 return true;
1757             }
1758             mixedCollection.propertiestring += '"' + item.propertyKey + '":"' + item.value + '"';
1759             if (index + 1 < length) {
1760                 mixedCollection.propertiestring += ",";
1761             }
1762             return true;
1763         };
1764 
1765         mixedCollection.each(buildPropertyString);
1766         mixedCollection.propertiestring += '}';
1767         var parsedProperties = Ext.decode(mixedCollection.propertiestring);
1768         return parsedProperties;
1769     };
1770 
1771     /**
1772      * Gets the contribution's add-properties.
1773      *
1774      * @return the contribution's add-properties
1775      */
1776     this.getAddedProperties = function () {
1777         return this.getAttribute(AC.util.Constants.actionCenterAddContributionPropertiesKey);
1778     };
1779 
1780     /**
1781      * Gets the contribution's remove-properties.
1782      *
1783      * @return the contribution's remove-properties
1784      */
1785     this.getRemovedProperties = function () {
1786         return this.getAttribute(AC.util.Constants.actionCenterRemoveContributionPropertiesKey);
1787     };
1788 
1789     /**
1790      * Gets the contribution's update-properties.
1791      *
1792      * @return the contribution's update-properties
1793      */
1794     this.getUpdatedProperties = function () {
1795         return this.getAttribute(AC.util.Constants.actionCenterUpdateContributionPropertiesKey);
1796     };
1797 
1798     /**
1799      * Gets the contribution's property object based on the specified property key.
1800      *
1801      * @param {String} propertyName the name of the property
1802      * @return the property object of the specified property
1803      */
1804     this.getProperty = function (propertyName) {
1805         var returnValue = null;
1806         var property = actionCenterJSON.getMsgProperty(this.getProperties(), propertyName);
1807         if (property) {
1808             returnValue = new AC.data.ContributionProperty(property);
1809         }
1810         return returnValue;
1811     };
1812 
1813     /**
1814      * Gets the contribution's property value based on the specified property key.
1815      *
1816      * @param {String} propertyName the name of the property
1817      * @return the value of the specified property
1818      */
1819     this.getPropertyValue = function (propertyName) {
1820         var returnValue = null;
1821         var property = actionCenterJSON.getMsgProperty(this.getProperties(), propertyName);
1822         if (property) {
1823             returnValue = new AC.data.ContributionProperty(property);
1824             returnValue = returnValue.getValue();
1825         }
1826         return returnValue;
1827     };
1828 }
1829 
1830 /**
1831  * Creates a new contribution property from the specified message.
1832  *
1833  * @class Represents a contribution property.
1834  * @param msg a JSON message
1835  */
1836 AC.data.ContributionProperty = function (msg) {
1837 
1838     /**
1839      * The contents of the JSON message.
1840      */
1841     this._msg = msg;
1842 
1843     /**
1844      * Gets an attribute's value from the message based on the specified attribute key.
1845      *
1846      * @param {String} key the key of the message attribute.
1847      * @return the value of the message attribute
1848      */
1849     this.getAttribute = function (key) {
1850         return actionCenterJSON.getMsgProperty(this._msg, key);
1851     };
1852 
1853     /**
1854      * Gets the property's value.
1855      *
1856      * @return the property's value
1857      */
1858     this.getValue = function () {
1859         return this.getAttribute(AC.util.Constants.actionCenterValueKey);
1860     };
1861 
1862     /**
1863      * Gets the property's locking user.
1864      *
1865      * @return the property's locking user
1866      */
1867     this.getLockedBy = function () {
1868         return new AC.data.User(this.getAttribute(AC.util.Constants.actionCenterLockedByKey));
1869     };
1870 
1871     /**
1872      * Gets the property's thumbprints.
1873      *
1874      * @return the property's thumbprints
1875      */
1876     this.getThumbprints = function () {
1877         return this.getAttribute(AC.util.Constants.actionCenterThumbprintsKey);
1878     };
1879 
1880     /**
1881      * Gets the property's user.
1882      *
1883      * @return the property's user
1884      */
1885     this.getUser = function () {
1886         return new AC.data.User(this.getAttribute(AC.util.Constants.actionCenterUserKey));
1887     };
1888 }
1889 
1890 /**
1891  * Creates a new user from the specified message.
1892  *
1893  * @class Represents a user.
1894  * @param msg a JSON message
1895  */
1896 AC.data.User = function (msg) {
1897 
1898     /**
1899      * The contents of the JSON message.
1900      */
1901     this._msg = msg;
1902 
1903     /**
1904      * Gets an attribute's value from the message based on the specified attribute key.
1905      *
1906      * @param {String} key the key of the message attribute.
1907      * @return the value of the message attribute
1908      */
1909     this.getAttribute = function (key) {
1910         return actionCenterJSON.getMsgProperty(this._msg, key);
1911     };
1912 
1913     /**
1914      * Gets the user's id.
1915      *
1916      * @return the user's id
1917      */
1918     this.getId = function () {
1919         return this.getAttribute(AC.util.Constants.actionCenterIdKey);
1920     };
1921 
1922     /**
1923      * Gets the user's disabled flag.
1924      *
1925      * @return the user's disabled flag
1926      */
1927     this.getDisabledFlag = function () {
1928         return this.getAttribute(AC.util.Constants.actionCenterDisabledFlagKey);
1929     };
1930 
1931     /**
1932      * Gets the user's email.
1933      *
1934      * @return the user's email
1935      */
1936     this.getEmail = function () {
1937         return this.getAttribute(AC.util.Constants.actionCenterEmailKey);
1938     };
1939 
1940     /**
1941      * Gets the user's fromDate.
1942      *
1943      * @return the user's fromDate
1944      */
1945     this.getFromDate = function () {
1946         return this.getAttribute(AC.util.Constants.actionCenterFromDateKey);
1947     };
1948 
1949     /**
1950      * Gets the user's pending approval flag.
1951      *
1952      * @return the user's pending approval flag
1953      */
1954     this.getPendingApprovalFlag = function () {
1955         return this.getAttribute(AC.util.Constants.actionCenterPendingApprovalFlagKey);
1956     };
1957 
1958     /**
1959      * Gets the user's first name.
1960      *
1961      * @return the user's first name
1962      */
1963     this.getFirstName = function () {
1964         return this.getAttribute(AC.util.Constants.actionCenterFirstNameKey);
1965     };
1966 
1967     /**
1968      * Gets the user's last name.
1969      *
1970      * @return the user's last name
1971      */
1972     this.getLastName = function () {
1973         return this.getAttribute(AC.util.Constants.actionCenterLastNameKey);
1974     };
1975 
1976     /**
1977      * Gets the user's system roles.
1978      *
1979      * @return the user's system roles
1980      */
1981     this.getSystemRoles = function () {
1982         return this.getAttribute(AC.util.Constants.actionCenterSystemRolesKey);
1983     };
1984 
1985     /**
1986      * Gets the user's toDate.
1987      *
1988      * @return the user's toDate
1989      */
1990     this.getToDate = function () {
1991         return this.getAttribute(AC.util.Constants.actionCenterToDateKey);
1992     };
1993 
1994     /**
1995      * Gets the user's user name.
1996      *
1997      * @return the user's user name
1998      */
1999     this.getUsername = function () {
2000         return this.getAttribute(AC.util.Constants.actionCenterUsernameKey);
2001     };
2002 
2003     /**
2004      * Gets the user's workspace roles.
2005      *
2006      * @return the user's workspace roles
2007      */
2008     this.getWorkspaceRoles = function () {
2009         return this.getAttribute(AC.util.Constants.actionCenterWorkspaceRolesKey);
2010     };
2011 }
2012 
2013 /**
2014  * Creates a user list from the specified message.
2015  *
2016  * @class Represents a user list.
2017  * @param msg a JSON message
2018  */
2019 AC.data.UserList = function (msg) {
2020 
2021     /**
2022      * The contents of the JSON message.
2023      */
2024     this._msg = msg;
2025 
2026     /**
2027      * Gets the length of the user list.
2028      *
2029      * @return the length of the user list
2030      */
2031     this.getLength = function () {
2032         return this._msg.length;
2033     };
2034 
2035     /**
2036      * Gets the user from the user list based on the specified index.
2037      *
2038      * @param {int} index the index to use in getting the user
2039      * @return the user at the specified index
2040      */
2041     this.getUser = function (index) {
2042         return new AC.data.User(this._msg[index]);
2043     };
2044 }
2045 
2046 /**
2047  * A counter used to create unique response channels.
2048  *
2049  * @type int
2050  * @default 0
2051  */
2052 AC.server.callCounter = 0;
2053 
2054 /**
2055  * Default constructor.
2056  *
2057  * @class Maintains callback information
2058  * @param callbackObject
2059  *          the callback object
2060  * @param callbackmethod
2061  *          the callback function
2062  */
2063 AC.server.CallbackInfo = function (callbackObject, callbackmethod, passdata) {
2064 
2065     /**
2066      * The object on which a callback should be invoked.
2067      */
2068     this._callbackobject = callbackObject;
2069 
2070     /**
2071      * The callback function.
2072      */
2073     this._callbackmethod = callbackmethod;
2074 
2075       /**
2076      * The object to pass along to the callback method.
2077      */
2078     this._passdata = passdata;
2079 
2080  }
2081 
2082 /**
2083  * Default constructor.
2084  *
2085  * @class Defines functions for invoking server methods.
2086  */
2087 AC.server.Callback = {
2088 
2089     /**
2090      * Maps AC.server.callCounter to instances of AC.server.CallbackInfo for invocations of callbacks.
2091      */
2092     _hashmap : new Ext.util.MixedCollection(),
2093 
2094     /**
2095      * The channel for reply messages coming from the server.
2096      *
2097      * @default null
2098      */
2099     _subscription : null,
2100 
2101     /**
2102      * Invokes the specified method on the server in the read-only mode.
2103      *
2104      * @param message           the message to pass to the server
2105      * @param methodName        the name of the method to invoke on the server
2106      * @param callbackobject    the callback object
2107      * @param callbackobject    the callback method
2108      */
2109     callServerReadOnly : function (message, methodName, callbackobject, callbackmethod, passdata) {
2110         this.callServer(message, methodName, "/service/requests", callbackobject, callbackmethod, passdata);
2111     },
2112 
2113     /**
2114      * Invokes the specified method on the server in the update mode.
2115      *
2116      * @param message           the message to pass to the server
2117      * @param methodName        the name of the method to invoke on the server
2118      * @param callbackobject    the callback object
2119      * @param callbackobject    the callback method
2120      */
2121     callServerUpdate : function (message, methodName, callbackobject, callbackmethod, passdata) {
2122         this.callServer(message, methodName, "/service/requests", callbackobject, callbackmethod, passdata);
2123     },
2124 
2125     /**
2126      * Invokes the specified method on the server.
2127      *
2128      * @param message           the message to pass to the server
2129      * @param methodName        the name of the method to invoke on the server
2130      * @param channel           the name of the channel on which to publish the message
2131      * @param callbackobject    the callback object
2132      * @param callbackobject    the callback method
2133      */
2134     callServer : function (message, methodName, channel, callbackobject, callbackmethod, passdata) {
2135         actionCenterAPI.startBatch();
2136         if (!message) {
2137             message = {};
2138         }
2139 
2140         // Only subscribe if there is a callback object and callback method provided
2141         if (callbackobject && callbackmethod) {
2142             var callCounter = AC.server.callCounter++;
2143             message.receivingchannel = "/response/service";
2144             message.acMessageNumber = callCounter;
2145             this._hashmap.add(callCounter, new AC.server.CallbackInfo(callbackobject, callbackmethod, passdata));
2146             if (!this._subscription) {
2147                 this._subscription = dojox.cometd.subscribe(message.receivingchannel, this, 'setReturnMsg');
2148             }
2149         }
2150 
2151         message.action = methodName;
2152         message.user = workspaceUser;
2153 
2154         dojox.cometd.publish(channel, message);
2155         actionCenterAPI.endBatch();
2156     },
2157 
2158     /**
2159      * Handles server response messages by invoking callbacks passing server message as the argument.
2160      *
2161      * @param msg the server's reply
2162      */
2163     setReturnMsg : function (msg) {
2164         var callbackInfo = this._hashmap.removeKey(msg.data.acMessageNumber);
2165         callbackInfo._callbackmethod.call(callbackInfo._callbackobject, this.translate(msg), callbackInfo._passdata);
2166     },
2167 
2168     /**
2169      * Translates the specified message into objects if the message's response type fits certain patterns:<br>
2170      * (Contribution -> AC.data.Contribution);<br>
2171      * (UserList -> AC.data.UserList);<br>
2172      * (anyOther -> the specified message).
2173      *
2174      * @param msg the message
2175      * @return the original message; or the objects as noted above
2176      */
2177     translate : function (msg) {
2178         var responseType = actionCenterJSON.getMsgProperty(
2179             msg, "data." + AC.util.Constants.actionCenterResponseTypeKey);
2180         if (AC.util.Constants.actionCenterResponseTypeContributionKey === responseType) {
2181             return new AC.data.Contribution(actionCenterJSON.getMsgProperty(
2182                 msg, "data." + AC.util.Constants.actionCenterResponseTypeContributionKey));
2183         } else if (AC.util.Constants.actionCenterResponseTypeUserListKey === responseType) {
2184             return new AC.data.UserList(actionCenterJSON.getMsgProperty(
2185                 msg, "data." + AC.util.Constants.actionCenterResponseTypeUserListKey));
2186         } else {
2187             return msg;
2188         }
2189     }
2190 };
2191 
2192 // TODO: move the API into AC namespace
2193 /**
2194  * Default constructor.
2195  *
2196  * @class Defines the standard API of the ActionCenters.
2197  */
2198 var actionCenterAPI = {
2199 
2200     /**
2201      * The batch counter tracks the number of inner batches so that we can
2202      * end the batch when the counter gets back to 0.  This way we can effectively have
2203      * nested batches.
2204      * @private
2205      * @default 0
2206      */
2207     batchCounter : 0,
2208 
2209     /**
2210      * Holds contribution id's for batched, as opposed to one-by-one, thumbprinting.
2211      */
2212     thumbprints: "",
2213     /**
2214      * Holds the systemElementConfiguration
2215      */
2216     systemElementConfiguration: null,
2217     /**
2218      * Initiates a batch connection to the server so that the multiple messages can be sent inside one http
2219      * request.  There must be a matching call to endBatch.
2220      *
2221      * @return void
2222      */
2223     startBatch : function () {
2224         if (this.batchCounter === 0) {
2225             dojox.cometd.startBatch();
2226         } else {
2227             //console.log("batch depth = " + this.batchCounter);
2228         }
2229         this.batchCounter++;
2230     },
2231 
2232     /**
2233      * Terminates a batch connection to the server.
2234      *
2235      * @return void
2236      */
2237     endBatch : function () {
2238         this.batchCounter--;
2239         if (this.batchCounter === 0) {
2240             dojox.cometd.endBatch();
2241         }
2242     },
2243 
2244     /**
2245      * Creates a new project.
2246      * TODO: This function should be moved to a class for CACE functions because it is not intended for use by component developers.
2247      *
2248      * @param {Object} contributionProperties
2249      *          the contribution properties
2250      *          TODO: Remove this parameter because it is not used.
2251      * @param (String} projectType
2252      *          the type of the project to create.
2253      * @return void
2254      */
2255     createProject : function (contributionProperties, projectType) {
2256         AC.server.Callback.callServerUpdate(
2257             {contributionType: projectType,
2258              contributionProperties: contributionProperties
2259             },
2260             "createProject", null, null);
2261     },
2262 
2263    /**
2264      * Adds a relationship of the specified type between the specified contributions.
2265      *
2266      * @param {String} superiorContributionId
2267      *          The id of the superior contribution.
2268      * @param {String} subordinateContributionId
2269      *          The id of the subordinate contribution.
2270      * @param {String} relationshipType
2271      *          The type of the relationship.
2272      * @param {String} [afterContributionId]
2273      *          The id of the preceding contribution.
2274      * @param {String} [beforeContributionId]
2275      *          The id of the succeeding contribution.
2276      * @param {String} subordinateContributionType
2277      *          The type of the subordinate contribution.
2278      * @param {Object} [contributionProperties]
2279      *          The contribution properties.
2280      * @param {Object} [callbackObject]
2281      *          The object to call back after the relationship is added.
2282      * @param {function} [callbackMethod]
2283      *          The method to call back after the relationship is added.
2284      * @param {Object} [contributionBinObjProperties]
2285      *          The binary contribution properties.
2286      * @return void
2287      */
2288     addRelationship: function (superiorContributionId, subordinateContributionId, relationshipType, afterContributionId,
2289             beforeContributionId, subordinateContributionType, contributionProperties, callbackObject, callbackMethod,
2290             contributionBinObjProperties) {
2291         AC.server.Callback.callServerUpdate(
2292             {superiorContributionId: superiorContributionId,
2293              subordinateContributionId: subordinateContributionId,
2294              relationshipType: relationshipType,
2295              afterContributionId: afterContributionId,
2296              beforeContributionId: beforeContributionId,
2297              subordinateContributionType: subordinateContributionType,
2298              contributionProperties: contributionProperties,
2299              contributionBinObjProperties: contributionBinObjProperties},
2300             "addRelationship", callbackObject, callbackMethod);
2301     },
2302 
2303     /**
2304      * Deletes a relationship of the specified type between the specified contributions.
2305      *
2306      * @param {String} superiorContributionId
2307      *          The id of the superior contribution.
2308      * @param {String} subordinateContributionId
2309      *          The id of the subordinate contribution.
2310      * @param {String} relationshipType
2311      *          The type of the relationship.
2312      * @param {Boolean} [isDeleteAll]
2313      *          Boolean flag to indicate whether all relationships to the superior should be deleted.  Defalts to false.
2314      *          TODO: remove this parameter because it is not used.
2315      * @return void
2316      */
2317     deleteRelationship : function (superiorContributionId, subordinateContributionId, relationshipType, isDeleteAll) {
2318         AC.server.Callback.callServerUpdate(
2319             {superiorContributionId: superiorContributionId,
2320              subordinateContributionId: subordinateContributionId,
2321              relationshipType: relationshipType,
2322              isDeleteAll: isDeleteAll},
2323             "deleteRelationship");
2324     },
2325 
2326     /**
2327      * Deletes a contribution (remove from recycle bin).
2328      *
2329      * @param {String} contributionId
2330      *          The id of the contribution to be deleted.
2331      * @return void
2332      */
2333     deleteContribution : function (contributionId) {
2334         AC.server.Callback.callServerUpdate(
2335             {contributionId: contributionId},
2336             "deleteContribution");
2337     },
2338 
2339     /**
2340      * Moves the specified contribution, within the same superior, to be among subordinates
2341      * of the specified superior contribution between the 'after-' and 'before-' contributions
2342      * in the ordering of subordinates.
2343      *
2344      * @param {String} superiorContributionId
2345      *          The id of the superior contribution.
2346      * @param {String} contributionToMoveId
2347      *          The id of the contribution to move.
2348      * @param {String} [afterContributionId]
2349      *          The id of the preceding contribution.  If this parameter is not provided, the beforeContributionId must be provided.
2350      * @param {String} [beforeContributionId]
2351      *          The id of the succeeding contribution.  If this parameter is not provided, the afterContributionId must be provided.
2352      * @return void
2353      */
2354     moveContribution : function (superiorContributionId, contributionToMoveId,
2355             afterContributionId, beforeContributionId) {
2356         AC.server.Callback.callServerUpdate(
2357             {superiorContributionId: superiorContributionId,
2358              contributionToMoveId: contributionToMoveId,
2359              afterContributionId: afterContributionId,
2360              beforeContributionId: beforeContributionId},
2361             "moveContribution");
2362     },
2363 
2364     /**
2365      * Moves the specified contribution, to a different superior, to be among subordinates
2366      * of the specified superior contribution between the 'after-' and 'before-' contributions
2367      * in the ordering of subordinates.
2368      *
2369      * @param {String} contributionToMoveId
2370      *          The id of the contribution to move.
2371      * @param {String} oldSuperiorId
2372      *          The id of the old superior contribution.
2373      * @param {String} newSuperiorId
2374      *          The id of the new superior contribution.
2375      * @param {String} relationshipType
2376      *          The type of the new relationship to the new superior.
2377      * @param {String} [afterContributionId]
2378      *          The id of the preceding contribution.  If this parameter is not provided, the beforeContributionId must be provided.
2379      * @param {String} [beforeContributionId]
2380      *          The id of the succeeding contribution.  If this parameter is not provided, the afterContributionId must be provided.
2381      * @param {String} [castToType]
2382      *          The type of the contribution after the move.
2383      * @return void
2384      */
2385     moveContribution2 : function (contributionToMoveId, oldSuperiorId, newSuperiorId,
2386            relationshipType, afterContributionId, beforeContributionId, castToType, oldRelationshipType) {
2387         AC.server.Callback.callServerUpdate(
2388             {contributionToMoveId: contributionToMoveId,
2389              oldSuperiorId: oldSuperiorId,
2390              newSuperiorId: newSuperiorId,
2391              relationshipType: relationshipType,
2392              afterContributionId: afterContributionId,
2393              beforeContributionId: beforeContributionId,
2394              type: castToType,
2395              oldRelationshipType: oldRelationshipType},
2396             "moveContribution2");
2397     },
2398 
2399     /**
2400      * Moves a project to the user's recyle bin.
2401      *
2402      * @param {String} projectId
2403      *          the id of the project to put in the user's recycle bin
2404      * @return void
2405      */
2406     moveProjectToRecycleBin : function (projectId) {
2407         AC.server.Callback.callServerUpdate(
2408             {projectId: projectId},
2409             "moveProjectToRecycleBin");
2410     },
2411 
2412     /**
2413      * Empties the user's project recyle bin.
2414      *
2415      * @param {String} contributionType
2416      *          The type of project to remove from the user's project recycle bin.
2417      * @return void
2418      */
2419     emptyProjectRecycleBin : function (contributionType) {
2420         AC.server.Callback.callServerUpdate(
2421             {contributionType: contributionType},
2422             "emptyProjectRecycleBin");
2423     },
2424 
2425     /**
2426      * Edits the properties of a contribution.
2427      *
2428      * @param {String} contributionId
2429      *          The id of the contribution to be edited.
2430      * @param {Object} [properties]
2431      *          A map of properties to modify.
2432      * @param {Object} [binaryObjectProperties]
2433      *          A map of binary properties to modify.
2434      * @param {Object} [callbackObject]
2435      *          The callback object.
2436      * @param {function} [callbackMethod]
2437      *          The callback method.
2438      * @param {Boolean} [unlock]
2439      *          Boolean flag indicating whether the contribution should be unlocked after the edit.
2440      * @param {String} [type]
2441      *          The new contribution type if this is a cast.
2442      * @return void
2443      */
2444     editContribution : function (contributionId, properties, binaryObjectProperties, callbackObject, callbackMethod,
2445             unlock, type) {
2446         AC.server.Callback.callServerUpdate(
2447             {id: contributionId,
2448              unlock: unlock,
2449              type: type,
2450              contributionProperties: properties,
2451              contributionBinObjProperties: binaryObjectProperties},
2452             "editContribution", callbackObject, callbackMethod);
2453     },
2454 
2455     /**
2456      * Gets system demo dataset.  This is only used for our COA demonstration.
2457      *
2458      * @param {Object} callbackObject
2459      *          the callback object
2460      * @param {function} callbackMethod
2461      *          the callback method
2462      * @return void
2463      */
2464     getSystemDemoDataset : function (callbackObject, callbackMethod) {
2465         AC.server.Callback.callServerReadOnly(
2466             null,
2467             "getSystemDemoDataset", callbackObject, callbackMethod);
2468     },
2469 
2470     /**
2471      * Gets element configuration for a item in the ActionCenters system configuration.
2472      *
2473      * @param {String} elementConfigScope
2474      *          The element configuration scope.  Usually the contribution type for which the configuration is retrieved.
2475      * @param {String} [elementCategoryName]
2476      *          The element category name.
2477      * @param {Object} callbackObject
2478      *          The callback object.
2479      * @param {function} callbackMethod
2480      *          The callback method.
2481      * @return void
2482      */
2483     getSystemElementConfiguration : function (elementConfigScope, elementCategoryName, callbackObject, callbackMethod) {
2484           var passdata = {
2485                elementConfigScope: elementConfigScope,
2486                elementCategoryName: elementCategoryName,
2487                callbackObject: callbackObject,
2488                callbackMethod: callbackMethod};
2489         if (!this.systemElementConfiguration) {
2490              AC.server.Callback.callServerReadOnly({},"getSystemElementConfiguration", this, this.getRequestedSystemElementConfig, passdata);
2491            } else {
2492                this.getRequestedSystemElementConfig(null, passdata);
2493            }
2494     },    
2495     /**
2496      *
2497      */
2498     getRequestedSystemElementConfig : function (msg, passdata) {
2499         if (msg) {
2500             this.systemElementConfiguration = actionCenterJSON.getMsgProperty(msg, 'data.elementConfiguration');
2501         }
2502         var returnVal = this.systemElementConfiguration;
2503         if (passdata.elementConfigScope) {
2504             returnVal = actionCenterJSON.getMsgProperty(returnVal, passdata.elementConfigScope);
2505             if (passdata.elementCategoryName) {
2506                 var category = null;
2507                 for (var i=0; i<returnVal.categories.length && !category; i++) {
2508                     if (returnVal.categories[i].name === passdata.elementCategoryName) {
2509                         category = returnVal.categories[i];
2510                     }
2511                 }
2512                 returnVal = category;
2513             }
2514         }
2515         passdata.callbackMethod.call(passdata.callbackObject, returnVal);
2516     },
2517     
2518     /**
2519      * Update new system element configuration to the server.
2520      *
2521      * @param {Object} systemElementConfiguration
2522      *          The new system element configuration.
2523      * @param {Object} callbackObject
2524      *          The callback object.
2525      * @param {function} callbackMethod
2526      *          The callback method.
2527      * @return void
2528      */
2529     updateSystemElementConfiguration: function(systemElementConfiguration, callbackObject, callbackMethod){      
2530         AC.server.Callback.callServerUpdate(
2531             systemElementConfiguration,
2532             "updateSystemElementConfiguration", callbackObject, callbackMethod);
2533     },
2534     
2535     /**
2536      * Gets a contribution grouping of the specified type from the personal workspace of the user.
2537      *
2538      * @param {String} contributionGroupingType
2539      *          The contribution grouping type to lookup in the user's personal workspace.
2540      * @param {Object} callbackObject
2541      *          The callback object.
2542      * @param {function} callbackMethod
2543      *          The callback method.
2544      * @return void
2545      */
2546     getPersonalWorkspaceContribution : function (contributionGroupingType, callbackObject, callbackMethod) {
2547         AC.server.Callback.callServerReadOnly(
2548             {contributionType: contributionGroupingType},
2549             "getPersonalWorkspaceContribution", callbackObject, callbackMethod);
2550     },
2551 
2552     /**
2553      * Gets the properties of the specified contribution.
2554      *
2555      * @param {String} contributionId
2556      *          The contribution id.
2557      * @param {Object} callbackObject
2558      *          The callback object.
2559      * @param {function} callbackMethod
2560      *          The callback method.
2561      * @return void
2562      */
2563     getContributionProperties : function (contributionId, callbackObject, callbackMethod) {
2564         AC.server.Callback.callServerReadOnly(
2565             {contributionId: contributionId},
2566             "getContributionProperties", callbackObject, callbackMethod);
2567     },
2568 
2569     /**
2570      * Tells the server to publish all the current contributions for a given channel.  The server will parse the
2571      * channel name and retrive all existing contributions for that channel and publish them to this client only.
2572      * The server does not re-publish the matching contributions to all clients.
2573      *
2574      * @param {String} addChannel
2575      *          The channel on which to publish all the existing matching contributions
2576      * @param {Boolean} createOneIfNoneExists
2577      *          boolean flag indicating whether to create a subordinate contribution if none has been found
2578      * @param {Object} callbackObject
2579      *          The callback object.
2580      * @param {function} callbackMethod
2581      *          The callback method.
2582      * @return void
2583      */
2584     publishRelatedContributions : function (addChannel, createOneIfNoneExists, createOneMode, miniMessage, callbackObject, callbackMethod) {
2585         AC.server.Callback.callServerUpdate(
2586             {AC_requestedchannel: addChannel,
2587              miniMessage: miniMessage,
2588              createOneIfNoneExists: createOneIfNoneExists,
2589              createOneMode: createOneMode},
2590             "publishRelatedContributions", callbackObject, callbackMethod);
2591     },
2592 
2593     /**
2594      * Publishes a specified contribution if it has a specified relationship to the specified superiorid
2595      *
2596      * @param {String} addChannel
2597      *          The channel that specifies the superiorId/relationshipType/subordinateId and for which to publish
2598      *          the existing contribution if the matching relationship exists
2599       * @return void
2600      */
2601     publishRelationship : function (addChannel) {
2602         AC.server.Callback.callServerReadOnly(
2603             {AC_requestedchannel: addChannel},
2604             "publishRelationship");
2605     },
2606 
2607     /**
2608      * Gets contributions that have a set of relationships and are of a specified type.  The matching contributions
2609      * will be sent back to the callback object/method
2610      * in an array.  The message key to the array in the message is "matchingContributions".
2611      *
2612      * @param {String[]} superiorIds
2613      *          an array of superior ids
2614      * @param {String[]} relationshipTypes
2615      *          an array of relationship types.  The index of the relationship matches the index of the superior id in
2616      *          the superiorIds array to which it is related.
2617      * @param {String} contributionType
2618      *          the type of subordinate contributions
2619      * @param {Boolean} [createOneIfNoneExists]
2620      *          boolean flag indicating whether to create a subordinate contribution if none is found
2621      * @param {Object} callbackObject
2622      *          The callback object.
2623      * @param {function} callbackMethod
2624      *          The callback method.
2625      * @return void
2626      */
2627     getContributionsMatchingRelationships : function (superiorIds, relationshipTypes, contributionType,
2628             createOneIfNoneExists, callbackObject, callbackMethod) {
2629         AC.server.Callback.callServerUpdate(
2630             {superiorIds: actionCenterUtility.convertStringArrayToDelimitedString(superiorIds),
2631              relationshipTypes: actionCenterUtility.convertStringArrayToDelimitedString(relationshipTypes),
2632              contributionType: contributionType,
2633              createOneIfNoneExists: createOneIfNoneExists},
2634             "getContributionsMatchingRelationships", callbackObject, callbackMethod);
2635     },
2636 
2637     /**
2638      * Gets a contribution that has the specified property and is related related to the specified superior contribution
2639      * via the specified relationship type and of the specified contribution type. If no such contribution is found, one
2640      * will be created if the createOneIfNoneExists boolean flag is set.
2641      *
2642      * @param {String} superiorId
2643      *          The superior id
2644      * @param {String} [relationshipType]
2645      *          The relationship type
2646      * @param {String} [subordinateType]
2647      *          The subordinate type
2648      * @param {String} propertyKey
2649      *          The property key
2650      * @param {String} propertyValue
2651      *          The property value
2652      * @param {Boolean} [createOneIfNoneExists]
2653      *          Boolean flag to indicate whether a new contribution should be created if no match is found.  Default is false.
2654      * @param {Object} callbackObject
2655      *          The callback object.
2656      * @param {function} callbackMethod
2657      *          The callback method.
2658      * @return void
2659      */
2660     getContributionByProperty : function (superiorId, relationshipType, subordinateType, propertyKey, propertyValue,
2661             createOneIfNoneExists, callbackObject, callbackMethod) {
2662         AC.server.Callback.callServerUpdate(
2663             {superiorId: superiorId,
2664              relationshipType: relationshipType,
2665              subordinateType: subordinateType,
2666              propertyKey: propertyKey,
2667              propertyValue: propertyValue,
2668              createOneIfNoneExists: createOneIfNoneExists},
2669             "getContributionByProperty", callbackObject, callbackMethod);
2670     },
2671 
2672     /**
2673      * Gets users of the specified project.
2674      *
2675      * @param {String} projectID
2676      *          The id of the project contribution.
2677      * @param {Object} callbackObject
2678      *          The callback object.
2679      * @param {function} callbackMethod
2680      *          The callback method.
2681      * @return void
2682      */
2683     getUsersInProject : function (projectID, callbackObject, callbackMethod) {
2684         AC.server.Callback.callServerReadOnly({projectID:  projectID}, "getUsersInProject",
2685             callbackObject, callbackMethod);
2686     },
2687 
2688     /**
2689      * Gets users having the specified systemrole(s).
2690      *
2691      * @param {String[]} systemRoles
2692      *          An array of systemroles.
2693      * @param {Object} callbackObject
2694      *          The callback object.
2695      * @param {function} callbackMethod
2696      *          The callback method.
2697      * @return void
2698      */
2699     getUsers : function (systemRoles, callbackObject, callbackMethod) {
2700         var systemRoleDelimitedString = actionCenterUtility.convertStringArrayToDelimitedString(systemRoles);
2701         AC.server.Callback.callServerReadOnly({systemRoles: systemRoleDelimitedString}, "getUsers",
2702             callbackObject, callbackMethod);
2703     },
2704 
2705     /**
2706      * Gets users having specified systemrole(s) and no role in the specified project.
2707      *
2708      * @param {String} workspaceProjectDefId
2709      *          The id of the project contribution.
2710      * @param {String[]} systemRoles
2711      *          an array of systemroles
2712      * @param {Object} callbackObject
2713      *          The callback object.
2714      * @param {function} callbackMethod
2715      *          The callback method.
2716      * @return void
2717      */
2718     getUnassignedUsers : function (workspaceProjectDefId, systemRoles, callbackObject, callbackMethod) {
2719         var systemRoleDelimitedString = actionCenterUtility.convertStringArrayToDelimitedString(systemRoles);
2720         if (!workspaceProjectDefId) {
2721             workspaceProjectDefId = currentPSSWorkspaceId;
2722         }
2723         AC.server.Callback.callServerReadOnly(
2724             {projectID: workspaceProjectDefId, systemRoles: systemRoleDelimitedString},
2725             "getUnassignedUsers",
2726             callbackObject, callbackMethod);
2727     },
2728 
2729     /**
2730      * Remove the specified user(s) from the specified project.
2731      *
2732      * @param {String[]} usernames
2733      *          The array of user names to remove
2734      * @param {String} projectID
2735      *          The id of the project contribution.
2736      * @return void
2737      */
2738     removeUserFromProject : function (usernames, projectID) {
2739         this.startBatch();
2740         for (var index = 0; index < usernames.length; index++) {
2741             AC.server.Callback.callServerReadOnly(
2742                 {removeUser: usernames[index], projectID: projectID},
2743                 "removeUserFromProject");
2744         }
2745         this.endBatch();
2746     },
2747 
2748     /**
2749      * Grants the specified user(s) access to the specified project.
2750      *
2751      * @param {String[]} usernames
2752      *          The array of user names.
2753      * @param {String} projectID
2754      *          The id of the project.
2755      * @return void
2756      */
2757     grantUserAccessToProject: function (usernames, projectID) {
2758         this.startBatch();
2759         for (var index = 0; index < usernames.length; index++) {
2760             AC.server.Callback.callServerReadOnly(
2761                 {grantUser: usernames[index], projectID: projectID}, "grantUserAccessToProject");
2762         }
2763         this.endBatch();
2764     },
2765 
2766     /**
2767      * Copies contributions and all the subordinates to the clipboard
2768      *
2769      * @param {String[]} contributionIds
2770      *          Array of contributions which should be copied to the clipboard.
2771      * @return void
2772      */
2773     copyToClipboard : function (componentId, deepCopyIds, shallowCopyIds) {
2774         AC.data.Clipboard.setPendingCopy(true);
2775         AC.server.Callback.callServerUpdate(
2776                 {componentId: componentId,
2777                  deepCopyIds: deepCopyIds,
2778                  shallowCopyIds: shallowCopyIds},
2779                 "copyToClipboard", null, null);
2780     },
2781 
2782     /**
2783      * Cuts contributions and all the subordinates to the clipboard.
2784      *
2785      * @param {String[]} contributionIds
2786      *          Array of contributions which should be cut to the clipboard.
2787      * @return void
2788      */
2789     cutToClipboard : function (componentId, deepCopyIds) {
2790          AC.data.Clipboard.setPendingCopy(true);
2791         AC.server.Callback.callServerUpdate(
2792                 {componentId: componentId,
2793                  deepCopyIds: deepCopyIds},
2794                 "cutToClipboard", null, null);
2795     },
2796 
2797     /**
2798      * Pastes contributions from the clipboard in the selected superior, having this new relationship,
2799      * after a certain contribution.
2800      *
2801      * @param {String} superiorContributionId
2802      *          The superior contribution for the contributions being pasted.
2803      * @param {String} relationshipType
2804      *          The pasted contributions relationship to the new superior.
2805      * @param {String} afterContributionId
2806      *          The pasted contributions should be after this contribution.
2807      * @return void
2808      */
2809     pasteFromClipboard : function (superiorContributionId, relationshipType, afterContributionId, beforeContributionId, targetId, type) {
2810         var clientGUID = dojox.uuid.generateTimeBasedUuid();
2811         AC.data.Clipboard.setClientGUID(clientGUID);
2812         AC.server.Callback.callServerUpdate(
2813                 {superiorContributionId: superiorContributionId,
2814                 relationshipType: relationshipType,
2815                 afterContributionId: afterContributionId,
2816                 beforeContributionId: beforeContributionId,
2817                 targetId: targetId,
2818                 type: type,
2819                 clientGUID: clientGUID},
2820                 "pasteFromClipboard", null, null);
2821     },
2822 
2823     /**
2824      * Gets the recycle bin contributions for a project.
2825      *
2826      * @param {String} projectId
2827      *          The project contribution id for which we are retrieving recycle bin contributions.
2828      * @param {Object} callbackObject
2829      *          The callback object.
2830      * @param {function} callbackMethod
2831      *          The callback method.
2832      * @return void
2833      */
2834     getRecycleBinContributions : function (projectId, callbackObject, callbackMethod) {
2835         AC.server.Callback.callServerReadOnly(
2836                 {projectId: projectId},
2837                 "getRecycleBinContributions", callbackObject, callbackMethod);
2838     },
2839 
2840     /**
2841      * Gets the recycle bin projects.
2842      * TODO: Move this to a CACE API.
2843      *
2844      * @param {String} [projectType]
2845      *          The type of projects.  Defaults to "AC_Project_Definition".
2846      * @param {Object} callbackObject
2847      *          The callback object.
2848      * @param {function} callbackMethod
2849      *          The callback method.
2850      * @return void
2851      */
2852     getRecycleBinProjects : function (projectType, callbackObject, callbackMethod) {
2853         AC.server.Callback.callServerReadOnly(
2854                 {contributionType: projectType},
2855                 "getRecycleBinProjects", callbackObject, callbackMethod);
2856     },
2857 
2858     /**
2859      * Gets the open CACE tool projects for a particular user.
2860      *
2861      * @param {Object} callbackObject
2862      *          The callback object.
2863      * @param {function} callbackMethod
2864      *          The callback method.
2865      * @return void
2866      */
2867     getAvailableProjects : function (callbackObject, callbackMethod) {
2868         AC.server.Callback.callServerReadOnly(
2869                 null,
2870                 "getAvailableProjects", callbackObject, callbackMethod);
2871     },
2872 
2873     /**
2874      * Gets the available system widgets.
2875      * TODO: Move this to a CACE API.
2876      *
2877      * @param {String} widgetType
2878      *          the widget type.  'AC_Control' or 'AC_Tool'.
2879      * @param {Object} callbackObject
2880      *          The callback object.
2881      * @param {function} callbackMethod
2882      *          The callback method.
2883      * @return void
2884      */
2885     getAvailableWidgets: function (widgetType, callbackObject, callbackMethod) {
2886         AC.server.Callback.callServerReadOnly(
2887                 {widgetType: widgetType},
2888                 "getAvailableWidgets", callbackObject, callbackMethod);
2889     },
2890 
2891     /**
2892      * Moves a contribution or collection of contributions to the recycle bin.
2893      *
2894      * @param {String|Object} contribution
2895      *          The id of the contribution to move to the recycle bin
2896      *                 - or -
2897      *             a collection of objects with property contributionId, each of the contributions will be moved to the recycle bin.
2898      * @return void
2899      */
2900     moveContributionToRecycleBin : function (contribution) {
2901         if (Ext.isArray(contribution)) {
2902             var contributionIds = new Array();
2903             for (var i=0; i<contribution.length; i++) {
2904          //for each contribution, check to see if it is a child of one already moved to contribution recycle bin
2905                  var currentContribution = contribution[i];
2906                  var ancestorAlreadyMoved = false;
2907                  for (var x=0; x<i && !ancestorAlreadyMoved; x++) {
2908                      ancestorAlreadyMoved = currentContribution.isAncestor(contribution[x]);
2909                  }
2910                  if (!ancestorAlreadyMoved) {
2911                      contributionIds.push(currentContribution.contributionId);
2912                  }
2913             }
2914             AC.server.Callback.callServerUpdate(
2915                 {contributionId: contributionIds},
2916                 "moveContributionToRecycleBin", null, null);
2917         } else {
2918             AC.server.Callback.callServerUpdate(
2919                     {contributionId: [contribution]},
2920                     "moveContributionToRecycleBin", null, null);
2921         }
2922     },
2923 
2924     /**
2925      * Restores contributions from the recycle bin.
2926      *
2927      * @param {String[]} contributionIds
2928      *          Array of contribution ids which should be restored.
2929      * @return void
2930      */
2931     restoreContributionsFromRecycleBin : function (contributionIds) {
2932         AC.server.Callback.callServerUpdate(
2933                 {contributionIds: contributionIds},
2934                 "restoreContributionsFromRecycleBin", null, null);
2935     },
2936 
2937     /**
2938      * Restores projects from a user's recycle bin.
2939      * TODO: Move this to a CACE API.
2940      *
2941      * @param {String} projectId
2942      *          The id of the project contribution to restore.
2943      * @return void
2944      */
2945     restoreProjectFromRecycleBin : function (projectId) {
2946         AC.server.Callback.callServerUpdate(
2947                 {projectId: projectId},
2948                 "restoreProjectFromRecycleBin", null, null);
2949     },
2950 
2951     /**
2952      * Publishes an application to the "App Store".
2953      * TODO: Move this to a CACE API.
2954      *
2955      * @param {String} projectId
2956      *          The id of the project contribution to publish.
2957      * @return void
2958      */
2959     publishApplication : function (projectId) {
2960         Ext.MessageBox.show({
2961             title: 'Publish Initiated',
2962             maxWidth : 700,
2963             msg: "Your request to Publish a Project is being processed.",
2964             buttons: Ext.MessageBox.OK,
2965             icon: Ext.MessageBox.INFO
2966         });
2967 
2968         AC.server.Callback.callServerUpdate(
2969             {projectId: projectId},
2970             "publishApplication");
2971     },
2972 
2973     /**
2974      * unpublishes an application to the "App Store".
2975      * TODO: Move this to a CACE API.
2976      *
2977      * @param {String} projectId
2978      *          The id of the project contribution to publish.
2979      * @return void
2980      */
2981     unPublishApplication : function (projectId) {
2982         Ext.MessageBox.show({
2983             title: 'Unpublish Initiated',
2984             maxWidth : 700,
2985             msg: "Your request to Unpublish a Project is being processed.",
2986             buttons: Ext.MessageBox.OK,
2987             icon: Ext.MessageBox.INFO
2988         });
2989 
2990         AC.server.Callback.callServerUpdate(
2991             {projectId: projectId},
2992             "unPublishApplication");
2993     },
2994 
2995     /**
2996      * Creates a workspace from an application.
2997      * TODO: Move this to a CACE API.
2998      *
2999      * @param {String} projectId
3000      *          The id of the project contribution to publish.
3001      * @param {Object} callbackObject
3002      *          The callback object.
3003      * @param {function} callbackMethod
3004      *          The callback method.
3005      * @return void
3006      */
3007     createWorkspace : function (projectId, callbackObject, callbackMethod) {
3008         AC.server.Callback.callServerUpdate(
3009             {projectId: projectId},
3010             "createWorkspace",
3011             callbackObject,
3012             callbackMethod);
3013     },
3014 
3015     /**
3016      * Get System Configuration for this user and for a particular configuration type.
3017      * TODO: Move this to a CACE API.
3018      *
3019      * @param {String}configurationType
3020      *          A type of the system configuration you are looking for.
3021      * @param {Object} callbackObject
3022      *          The callback object.
3023      * @param {function} callbackMethod
3024      *          The callback method.
3025      * @return void
3026      */
3027     getSystemConfiguration : function (configurationType, callbackObject, callbackMethod) {
3028         AC.server.Callback.callServerUpdate(
3029             {configurationType: configurationType},
3030             "getSystemConfiguration",
3031             callbackObject,
3032             callbackMethod);
3033     },
3034 
3035     /**
3036      * Copies the specified contribution to the specified palette.
3037      * TODO: Move this to a CACE API.
3038      *
3039      * @param {String[]} contributionIds
3040      *          The array of id's of the contributions to copy.
3041      * @param {String}paletteCategoryId
3042      *          The id of the palette category where the contribution will be copied.
3043      * @param {String} [leaderRoleId]
3044      *          The leader role id.  This should be provided when copying an agenda item to the palette.
3045      *          TODO: move this functionality to the back end.
3046      * @param {String} [participantRoleId]
3047      *          The participant role id.  This should be provided when copying an agenda item to the palette.
3048      *          TODO: move this functionality to the back end.
3049      * @return void
3050      */
3051     copyToPalette : function (contributionIds, paletteCategoryId, leaderRoleId, participantRoleId) {
3052         AC.server.Callback.callServerUpdate(
3053             {contributionIds: contributionIds,
3054              categoryId: paletteCategoryId,
3055              leaderRoleId: leaderRoleId,
3056              participantRoleId: participantRoleId},
3057             "copyToPalette");
3058     },
3059 
3060     /**
3061      * Pastes contributions from palette.
3062      * TODO: Move this to a CACE API.
3063      *
3064      * @param {String} superiorContributionId
3065      *          The superior contribution for the contributions being pasted.
3066      * @param {String} contributionId
3067      *          The id of the palette item being pasted/dropped.
3068      * @param {String} relationshipType
3069      *          The pasted contributions relationship to the new superior.
3070      * @param {String} [afterContributionId]
3071      *          The pasted contributions should be after this contribution.
3072      * @param {String} beforeContributionId.
3073      *          The pasted contributions should be before this contribution.
3074      * @return void
3075      */
3076     pasteFromPalette: function (superiorContributionId,
3077                                 contributionId,
3078                                 relationshipType,
3079                                 afterContributionId,
3080                                 beforeContributionId) {
3081         AC.server.Callback.callServerUpdate(
3082                 {superiorContributionId: superiorContributionId,
3083                 contributionId: contributionId,
3084                 relationshipType: relationshipType,
3085                 afterContributionId: afterContributionId,
3086                 beforeContributionId: beforeContributionId},
3087                 "pasteFromPalette");
3088     },
3089 
3090     /**
3091      * Performs a (shallow) copy of the contribution and relates it to the superior with the given relationship name.
3092      *
3093      * @param {String} contributionId
3094      *            The contribution value object.
3095      * @param {String} superiorId
3096      *            The superior contribution value object.
3097      * @param {String} relationshipType
3098      *            The relationship to new superior.
3099      * @param {String} [beforeContributionId]
3100      *            Place this new copy before the contribution with this id.
3101      * @param {String} [afterContributionId]
3102      *            Place this new copy after the contribution with this id.
3103      * @return void
3104      */
3105     copyContribution: function (contributionId, superiorId, relationshipType, afterContributionId, beforeContributionId, castToType) {
3106         AC.server.Callback.callServerUpdate(
3107                 {contributionId: contributionId,
3108                 superiorId: superiorId,
3109                 relationshipType: relationshipType,
3110                 afterContributionId: afterContributionId,
3111                 beforeContributionId: beforeContributionId,
3112                 type: castToType},
3113                 "copyContribution");
3114     },
3115 
3116     /**
3117      * Performs a deep copy of the contribution and relates it to the superior with the given relationship name.
3118      *
3119      * @param {String} contributionId
3120      *            The contribution value object.
3121      * @param {String} superiorId
3122      *            The superior contribution value object.
3123      * @param {String} relationshipType
3124      *            The relationship to new superior.
3125      * @param {String} [beforeContributionId]
3126      *            Place this new copy before the contribution with this id.
3127      * @param {String} [afterContributionId]
3128      *            Place this new copy after the contribution with this id.
3129      * @return void
3130      */
3131     deepCopy: function (contributionId, superiorId, relationshipType, afterContributionId, beforeContributionId, castToType) {
3132         AC.server.Callback.callServerUpdate(
3133                 {contributionId: contributionId,
3134                 superiorId: superiorId,
3135                 relationshipType: relationshipType,
3136                 afterContributionId: afterContributionId,
3137                 beforeContributionId: beforeContributionId,
3138                 type: castToType,
3139                 deepCopy: "true"},
3140                 "copyContribution");
3141     },
3142     /**
3143      * Copies a screen and adds superior relationship to activity and roled ids specified.
3144      *
3145      * @param {String} screenId
3146      *            The screen id to be copied.
3147      * @param {String} activityId
3148      *            The superior activity id.
3149      * @param {String} roleId
3150      *            The superior role id.
3151      * @return void
3152      */
3153     copyScreenToActivityRole: function (screenId, activityId, roleId) {
3154         AC.server.Callback.callServerUpdate(
3155                 {screenId: screenId,
3156                 activityId: activityId,
3157                 roleId: roleId},
3158                 "copyScreenToActivityRole");
3159     },
3160 
3161     /**
3162      * Copies a screen and replaces the specified screen
3163      *
3164      * @param {String} copyScreenId
3165      *            The id of the screen to copy.
3166      * @param {String} replaceScreenId
3167      *            The id of the screen to replace.
3168      * @return void
3169      */
3170     copyReplaceScreen: function (copyScreenId, replaceScreenId) {
3171         AC.server.Callback.callServerUpdate(
3172                 {screenId: copyScreenId,
3173                  replaceId: replaceScreenId},
3174                 "copyScreenToActivityRole");
3175     },
3176 
3177     /**
3178      * Moves a screen and adds superior relationship to activity and roled ids specified.
3179      *
3180      * @param {String} screenId
3181      *            The screen id to be copied.
3182      * @param {String} activityId
3183      *            The superior activity id.
3184      * @param {String} roleId
3185      *            The superior role id.
3186      * @return void
3187      */
3188     moveScreenToActivityRole: function (screenId, activityId, roleId) {
3189         AC.server.Callback.callServerUpdate(
3190                 {screenId: screenId,
3191                 activityId: activityId,
3192                 roleId: roleId},
3193                 "moveScreenToActivityRole");
3194     },
3195 
3196     /**
3197      * Move a user or group of users to a screen.
3198      *
3199      * @param {String} moveToId
3200      *             This will indicate where to move the user(s).  If it is the text "next" or "previous", the
3201      *             contributon service will determine what that means.  If the value is an id of a Role, then all users
3202      *             specified will be moved to that role, if the id is an activity, all users specified will be moved to
3203      *             that activity
3204      * @param (boolean) followTheLeader
3205      *             Boolean indicator to tell whether all workspace users will get moved with the initiating user.
3206      * @param {String} workspaceProjectDefId
3207      *             The id of the project definition contribution for the workspace the user is currently in
3208      * @param {Sting|String[]} contributionId
3209      *             This is a single id or an Array of ids.  The id(s) could be for contribution(s) of type AC_User,
3210      *             so that specific users would be moved, it could be of type AC_Role, in which case all users having
3211      *             that role would be moved, or of type AC_Activity so that all users on that activity would be moved.
3212      * @return void
3213      */
3214     moveUser: function (moveToId, followTheLeader, workspaceProjectDefId, contributionId) {
3215         if (!workspaceProjectDefId) {
3216             workspaceProjectDefId = currentPSSWorkspaceId;
3217         }
3218         var contributionIds = null;
3219         if (contributionId) {
3220             contributionIds = actionCenterUtility.convertStringArrayToDelimitedString(contributionId);
3221         }
3222         AC.server.Callback.callServerUpdate(
3223             {contributionIds: contributionIds,
3224              followTheLeader: followTheLeader,
3225              moveToId: moveToId,
3226              workspaceProjectDefId: workspaceProjectDefId},
3227             "moveUser");
3228     },
3229 
3230     /**
3231      * Add a PSS user to a workspace.
3232      *
3233      * @param {String} roleId
3234      *             The id of the role the user is being assigned.
3235      * @param {String|String[]} userid
3236      *             This is a single userId or an Array of userIds.  The userIds represent users in the user table
3237      * @param {String} workspaceProjectDefId
3238      *             The id of the project definition contribution for the workspace to which the user is to be added.
3239      * @return void
3240      */
3241     addUser: function (roleId, userId, workspaceProjectDefId) {
3242         if (!workspaceProjectDefId) {
3243             workspaceProjectDefId = currentPSSWorkspaceId;
3244         }
3245         var userIds = null;
3246         if (userId) {
3247             userIds = actionCenterUtility.convertStringArrayToDelimitedString(userId);
3248         }
3249         AC.server.Callback.callServerUpdate(
3250             {userIds: userIds,
3251              roleToId: roleId,
3252              workspaceProjectDefId: workspaceProjectDefId},
3253             "addUser");
3254     },
3255 
3256     /**
3257      * Remove a PSS user from a workspace
3258      *
3259      * @param {String|String[]} userid
3260      *             This is a single contribution id or an Array of contribution id.  The contribution ids represent contribution ids for user
3261      *             contributions in contribution table.
3262      * @return void
3263      */
3264     removeUserFromWorkspace: function (userId) {
3265         var userIds = null;
3266         if (userId) {
3267             userIds = actionCenterUtility.convertStringArrayToDelimitedString(userId);
3268         }
3269         AC.server.Callback.callServerUpdate(
3270             {userIds: userIds},
3271             "removeUserFromWorkspace");
3272     },
3273 
3274     /**
3275      * Get the workspace dataset based on the datasetName and workspaceId
3276      *
3277      * @param {String} datasetName
3278      *             This is the datasetName
3279      * @param {Object} callbackObject
3280      *          The callback object.
3281      * @param {function} callbackMethod
3282      *          The callback method.
3283      * @param {String} workspaceId
3284      *             The workspace ID.
3285      * @return void
3286      */
3287     getWorkspaceDataset: function (datasetName, callbackObject, callbackMethod, workspaceId) {
3288         if (!workspaceId) {
3289             workspaceId = currentPSSWorkspaceId;
3290         }
3291         AC.server.Callback.callServerUpdate(
3292             {datasetName: datasetName,
3293              workspaceId: workspaceId},
3294             "getWorkspaceDataset",
3295             callbackObject,
3296             callbackMethod);
3297     },
3298      /**
3299      * Add an activity to a workspace, copying or linking datasets as specified,
3300      *
3301      * @param {String} activityId
3302      *             This id of the activity to copy
3303      * @param {String} workspaceId
3304      *          The id of the workspace project to add to
3305      * @param (String) parentId
3306      *          The id of the superior contribution to the new activity
3307      * @param (String) afterId
3308      *          The id of the sibling contribution to place the new activity after
3309      * @param (String) initialData
3310      *          A value indicating whether to copy or link data
3311      * @param (String) dataSourceIncrementer
3312      *          The incrementer of the activity being copied or linked to
3313      * @param (String) name
3314      *          The name of the new Activity
3315      * @param (String) description
3316      *          The description of the new Activity
3317      * @param (Object) callbackObject
3318      *          The callback object
3319      * @param {function} callbackMethod
3320      *          The callback method.
3321      * @return void
3322      */
3323     addWorkspaceActivity: function (activityId, workspaceId, parentId, afterId, initialData, dataSourceIncrementer, name, description, callbackObject, callbackMethod) {
3324         AC.server.Callback.callServerUpdate(
3325            {workspaceId: workspaceId,
3326            parentId: parentId,
3327            afterId: afterId,
3328            activityId: activityId,
3329            initialData: initialData,
3330            dataSourceIncrementer: dataSourceIncrementer,
3331            name: name,
3332            description: description},
3333            "addActivityToWorkspace",
3334            callbackObject,
3335            callbackMethod);
3336     },
3337     /**
3338      * Gets the user id.
3339      *
3340      * @return CACE or PSS user id
3341      */
3342     getUserId: function () {
3343         try {
3344             if (acPSSUserId) {
3345                 return acPSSUserId;
3346             }
3347         } catch (err) {
3348             return workspaceUserId;
3349         }
3350     },
3351 
3352     /**
3353      * Thumb-prints the specified contribution with the current user's id.
3354      *
3355      * @param {String} contributionId
3356      *          Contribution id.
3357      * @param {Boolean} [isCommit]
3358      *          Boolean flag to indicate whether thumbprints should be submitted to the server.  Defalut is false.
3359      * @return void
3360      */
3361     thumbprint: function (contributionId, isCommit) {
3362         if (contributionId !== null) {
3363             if (this.thumbprints === "") {
3364                 this.thumbprints += contributionId;
3365             } else {
3366                 this.thumbprints += acGlobalDelimiter + contributionId;
3367             }
3368         }
3369         if (this.thumbprints !== "" && isCommit) {
3370             AC.server.Callback.callServerUpdate(
3371                 {contributionId: this.thumbprints},
3372                 "thumbprint");
3373             this.thumbprints = "";
3374         }
3375     },
3376 
3377     /**
3378      * Thumb-prints the specified contribution when the window is closed.
3379      *
3380      * @param {String} contributionId
3381      *          Contribution id.
3382      * @return void
3383      */
3384     thumbprintContributionOnClose: function (contributionId) {
3385         if (typeof currentScreenId  !== "undefined") {
3386             if (contributionId && currentScreenId && actionCenterAPI.getUserId()) {
3387                 AC.server.Callback.callServerUpdate(
3388                     {contributionId: contributionId,
3389                      screenId: currentScreenId,
3390                      userId: actionCenterAPI.getUserId()},
3391                     "thumbprintContributionOnClose");
3392             }
3393         }
3394     },
3395 
3396     /**
3397      * Locks the specified contribution.
3398      *
3399      * @param {String} contributionId
3400      *          The id of the contribution to lock.
3401      * @param {Object} callbackObject
3402      *          The callback object.
3403      * @param {function} callbackMethod
3404      *          The callback method.
3405      * @return void
3406      */
3407     lockContribution: function (contributionId, callbackObject, callbackMethod) {
3408         AC.server.Callback.callServerUpdate(
3409             {contributionId: contributionId},
3410             "lockContribution",
3411             callbackObject,
3412             callbackMethod);
3413     },
3414 
3415     /**
3416      * Unlocks the specified contribution.
3417      *
3418      * @param {String} contributionId
3419      *          The id of the contribution to unlock.
3420      * @param {Object} callbackObject
3421      *          The callback object.
3422      * @param {function} callbackMethod
3423      *          The callback method.
3424      * @return void
3425      */
3426     unlockContribution: function (contributionId, callbackObject, callbackMethod) {
3427         AC.server.Callback.callServerUpdate(
3428             {contributionId: contributionId},
3429             "unlockContribution",
3430             callbackObject,
3431             callbackMethod);
3432     },
3433 
3434     /**
3435      * Edits the specified contribution property.
3436      *
3437      * @param {String} contributionId    The id of the contribution, whose property to edit.
3438      * @param {String} key               The property key.
3439      * @param {String} value             The property value.
3440      * @param {Object} callbackObject    The callback object.
3441      * @param {function} callbackMethod  The callback method.
3442      * @return void
3443      */
3444     editContributionProperty : function (contributionId, key, value, callbackObject, callbackMethod) {
3445         AC.server.Callback.callServerUpdate(
3446             {contributionId: contributionId,
3447              key: key,
3448              value: value},
3449             "editContributionProperty", callbackObject, callbackMethod);
3450     },
3451 
3452     /**
3453      * Locks the specified contribution property.
3454      *
3455      * @param {String} contributionId    The id of the contribution whose property to lock.
3456      * @param {String} propertyKey       The property key.
3457      * @param {Object} callbackObject    The callback object.
3458      * @param {function} callbackMethod  The callback method.
3459      * @return void
3460      */
3461     lockContributionProperty: function (contributionId, propertyKey, callbackObject, callbackMethod) {
3462         AC.server.Callback.callServerUpdate(
3463             {contributionId: contributionId,
3464              propertyKey: propertyKey},
3465             "lockContributionProperty",
3466             callbackObject,
3467             callbackMethod);
3468     },
3469 
3470     /**
3471      * Unlocks the specified contribution property.
3472      *
3473      * @param {String} contributionId    The id of the contribution whose property to unlock.
3474      * @param {String} propertyKey       the property key
3475      * @param {Object} callbackObject    The callback object.
3476      * @param {function} callbackMethod  The callback method.
3477      * @return void
3478      */
3479     unlockContributionProperty: function (contributionId, propertyKey, callbackObject, callbackMethod) {
3480         AC.server.Callback.callServerUpdate(
3481             {contributionId: contributionId,
3482              propertyKey: propertyKey},
3483             "unlockContributionProperty",
3484             callbackObject,
3485             callbackMethod);
3486     },
3487 
3488     /**
3489      * Gets population rules for a component.
3490      *
3491      * @param {String} contributionId    The component contribution id.
3492      * @param {Object} callbackObject    The callback object.
3493      * @param {function} callbackMethod  The callback method.
3494      * @return void
3495      */
3496     getPopulationRules : function (contributionId, callbackObject, callbackMethod) {
3497         AC.server.Callback.callServerReadOnly(
3498                 {contributionId: contributionId},
3499                 "getPopulationRules", callbackObject, callbackMethod);
3500     },
3501 
3502     /**
3503      * Gets sub-component configurations for the component.
3504      *
3505      * @param {String} superiorContributionId  The superiorContributionId.
3506      * @param {String} componentId             The component contribution id.
3507      * @param {Object} callbackObject          The callback object.
3508      * @param {function} callbackMethod        The callback method.
3509      * @return void
3510      */
3511     getComponentConfiguration : function (superiorContributionId, componentId, callbackObject, callbackMethod) {
3512         if (superiorContributionId) {
3513             AC.server.Callback.callServerReadOnly(
3514                     {superiorContributionId: superiorContributionId,
3515                      componentId: componentId},
3516                     "getComponentConfiguration", callbackObject, callbackMethod);
3517         } else {
3518             AC.server.Callback.callServerReadOnly(
3519                     {componentId: componentId},
3520                     "getComponentConfiguration", callbackObject, callbackMethod);
3521         }
3522     },
3523 
3524     /**
3525      * Gets a new swapper parent.
3526      *
3527      * @param {String} superiorRootContributionId  The root contribution of the superior contribution.
3528      * @param {String} subordinateComponentId      The subordinate component for which swapping is enabled.
3529      * @param {String} currentContributionId       The current contribution id.
3530      * @param {String} shellSubcomponentId         The shell subcomponent id.
3531      * @param {Object} callbackObject              The callback object.
3532      * @param {function} callbackMethod            The callback method.
3533      * @return void
3534      */
3535     getSwapperParent : function (superiorRootContributionId, subordinateComponentId, currentContributionId,
3536             shellSubcomponentId, callbackObject, callbackMethod) {
3537         AC.server.Callback.callServerReadOnly(
3538             {superiorRootContributionId: superiorRootContributionId,
3539              subordinateComponentId: subordinateComponentId,
3540              currentContributionId: currentContributionId,
3541              shellSubcomponentId: shellSubcomponentId},
3542             "getSwapperParent", callbackObject, callbackMethod);
3543     },
3544 
3545     /**
3546      * Gets the specified contribution.
3547      *
3548      * @param {String} contributionId    The id of the contribution to retrieve.
3549      * @param {Object} callbackObject    The callback object.
3550      * @param {function} callbackMethod  The callback method.
3551      * @return void
3552      */
3553     getContribution : function (contributionId, callbackObject, callbackMethod) {
3554         AC.server.Callback.callServerReadOnly(
3555                 {contributionId: contributionId},
3556                 "getContribution", callbackObject, callbackMethod);
3557     },
3558 
3559     /**
3560      * Gets the Standard Activity Palette
3561      *
3562      * @return void
3563      */
3564     getStandardActivityPalette : function (callbackObject, callbackMethod) {
3565         AC.server.Callback.callServerReadOnly(
3566                 {},
3567                 "getStandardActivityPalette", callbackObject, callbackMethod);
3568     },
3569 
3570     /**
3571      * Deletes a widget from the system.
3572      *
3573      * @return void
3574      */
3575     deleteWidget : function (contributionIds, isReadOnly, callbackObject, callbackMethod) {
3576         AC.server.Callback.callServerUpdate(
3577             {contributionIds: contributionIds,
3578              isReadOnly: isReadOnly},
3579             "deleteWidget", callbackObject, callbackMethod);
3580     }
3581 };
3582 
3583 /**
3584  * Default constructor.
3585  *
3586  * @class Defines functions for manipulation of population rules.
3587  * @param {Object[]} populationRules
3588  *          The array of population rules.
3589  */
3590 AC.data.PopulationRulesParser = function (populationRules) {
3591     Ext.QuickTips.init();
3592     /**
3593      * @private
3594      */
3595     this.popRulesArray = populationRules;
3596 
3597     /**
3598      * Gets the specified property matching superior and subordinate types starting at the specified index in the
3599      * population rules array.
3600      *
3601      * @param {String} subordinateType
3602      *          The subordinate type.
3603      * @param {String} superiorType
3604      *          The superior type.
3605      * @param {String} key
3606      *          The key.
3607      * @param {String} index
3608      *          The starting index into the population rules array
3609      * @return {String}
3610      *          The matching property or null if no match is found.
3611      */
3612     this.getRuleProperty = function (subordinateType, superiorType, key, index) {
3613         if (!index) {
3614             index = 0;
3615         }
3616         var property = null;
3617         for (var i = index; i < this.popRulesArray.length; i++) {
3618             if (subordinateType === this.popRulesArray[i].subordinate_type) {
3619                 if (superiorType === this.popRulesArray[i].superior_type) {
3620                     property = eval('this.popRulesArray[' + i + '].' + key);
3621                 } else if (!property) {
3622                     // in case of a node that is in an invalid location, match the first one matching
3623                     // the subordinate type
3624                     property = eval('this.popRulesArray[' + i + '].' + key);
3625                 }
3626             }
3627         }
3628         return property;
3629     };
3630 
3631     /**
3632      * Gets the array of subordinates of the specified superior type starting at the specified index.
3633      *
3634      * @param {String} superiorType
3635      *          The superior type.
3636      * @param {Integer} index
3637      *          The starting index into the population rules array.
3638      * @return {Object[]}
3639      *          The array of matching subordinates.
3640      */
3641     this.getSubordinatesOfType = function (superiorType, index) {
3642         if (!index) {
3643             index = 0;
3644         }
3645         var subordinateTypes = [];
3646         for (var i = index; i < this.popRulesArray.length; i++) {
3647             if (superiorType === this.popRulesArray[i].superior_type) {
3648                 subordinateTypes.push(this.popRulesArray[i]);
3649             }
3650         }
3651         return subordinateTypes;
3652     };
3653 
3654     /**
3655      * Checks if the specified superior has any subordinates.
3656      *
3657      * @param {String} superiorType
3658      *          The superior type.
3659      * @param {Integer} index
3660      *          The starting index into the population rules array.
3661      * @return {Boolean}
3662      *          True if the superior has at least one subordinate or false otherwise.
3663      */
3664     this.hasSubordinatesOfType = function (superiorType, index) {
3665         return (this.getSubordinatesOfType(superiorType, index).length > 0);
3666     };
3667 
3668     /**
3669      * Gets the relationship type for the specified superior and subordinate types.
3670      *
3671      * @param {String} subordinateType
3672      *          The subordinate type.
3673      * @param {String} superiorType
3674      *          The superior type.
3675      * @return {String}
3676      *          The matching relationship type if one exists or null otherwise.
3677      */
3678     this.getRelationshipTypeFor = function (subordinateType, superiorType) {
3679         var relationshipType = null;
3680         for (var i = 0; i < this.popRulesArray.length; i++) {
3681             if (subordinateType === this.popRulesArray[i].subordinate_type &&
3682                     superiorType === this.popRulesArray[i].superior_type) {
3683                 relationshipType =  this.popRulesArray[i].relationship_to_superior;
3684                 break;
3685             }
3686         }
3687         return relationshipType;
3688     };
3689 
3690     /**
3691      * Gets all population rule types.
3692      *
3693      * @param {Integer} index
3694      *          The starting index into the population rules array.
3695      * @return {String[]}
3696      *          An array of rule types.
3697      */
3698     this.getAllTypesList = function (index) {
3699         if (!index) {
3700             index = 0;
3701         }
3702         var subordinateTypes = [];
3703         for (var i = index; i < this.popRulesArray.length; i++) {
3704             subordinateTypes.push(this.popRulesArray[i].subordinate_type);
3705         }
3706         return subordinateTypes;
3707 
3708     };
3709 
3710     /**
3711      * Gets a list of all child types.
3712      *
3713      * @param {Integer} index
3714      *          The starting index into the population rules array.
3715      * @return {String[]}
3716      *          An array of child types.
3717      */
3718     this.getAllChildTypeList = function (index) {
3719         var childTypeList = [];
3720         if (this.popRulesArray) {
3721             for (var x = 0; x < this.popRulesArray.length; x++) {
3722                 var childType = {
3723                     type:           this.popRulesArray[x].subordinate_type,
3724                     relationship:   this.popRulesArray[x].relationship_to_superior,
3725                     createOne:      false
3726                 };
3727                 if (childTypeList.indexOf(childType < 0)) {
3728                     childTypeList.push(childType);
3729                 }
3730             }
3731         }
3732         return childTypeList;
3733     };
3734 
3735     /**
3736      * Gets a list of all valid child types of the specified superior.
3737      *
3738      * @param {String} superiorType
3739      *          The superior type.
3740      * @param {Integer} index
3741      *          The starting index into the population rules array.
3742      * @return {String[]}
3743      *          An array of valid child types.
3744      */
3745     this.getValidChildTypeList = function (superiorType, index) {
3746         var supType = superiorType;
3747         var children = this.getSubordinatesOfType(supType, index);
3748         var childTypeList = [];
3749         if (children) {
3750             for (var x = 0; x < children.length; x++) {
3751                 var childType = {
3752                     type:           children[x].subordinate_type,
3753                     relationship:   children[x].relationship_to_superior,
3754                     createOne:      false
3755                 };
3756                 childTypeList.push(childType);
3757             }
3758         }
3759         return childTypeList;
3760     };
3761 
3762     /**
3763      * Checks if the specified child is a valid child for the specified superior.
3764      *
3765      * @param {String} superiorType
3766      *          The superior type.
3767      * @param {String} childType
3768      *          The child type.
3769      * @return {Boolean}
3770      *          True if the child is a valid child or false otherwise
3771      */
3772     this.isValidChildType = function (superiorType, childType) {
3773         var validChildTypes = this.getSubordinatesOfType(superiorType);
3774         for (var i = 0; i < validChildTypes.length; i++) {
3775             if (validChildTypes[i].subordinate_type === childType) {
3776                 return true;
3777             }
3778         }
3779         return false;
3780     };
3781 
3782     /**
3783      * Checks if the array of population rules has more than one occurrence of any given type.
3784      *
3785      * @return {Boolean}
3786      *           True if there exists more than one rule with any given type or false otherwise.
3787      */
3788     this.hasAmbiguities = function () {
3789         var isAmbiguous = false;
3790         var workingList = [];
3791         for (var x = 0; x < this.popRulesArray.length && !isAmbiguous; x++) {
3792             if (workingList.indexOf(this.popRulesArray[x].superior_type) === -1) {
3793                 workingList.push(this.popRulesArray[x].superior_type);
3794             } else {
3795                 isAmbiguous = true;
3796             }
3797         }
3798         return isAmbiguous;
3799     };
3800 
3801     /**
3802      * Gets valid cast types on trigger-drop by iterating through the population rule array and collecting all
3803      * population rule types that are valid casts.
3804      *
3805      * @return {Boolean}
3806      *           The array of valid casts.
3807      */
3808     this.getValidCastTypesOnTriggerDrop = function () {
3809         var validCastTypes = [];
3810         for (var x = 0; this.popRulesArray && x < this.popRulesArray.length; x++) {
3811             if (this.popRulesArray[x].isValidCastOnTriggerDrop &&
3812                     this.popRulesArray[x].isValidCastOnTriggerDrop === 'Y') {
3813                 validCastTypes.push(this.popRulesArray[x].subordinate_type);
3814             }
3815         }
3816         if (validCastTypes.length > 0) {
3817             return validCastTypes;
3818         } else {
3819             return null;
3820         }
3821     };
3822 
3823     /**
3824      * Gets the drop rules properties for the specfied subordinate type and drop type
3825      *
3826      * @param {String} subordinateType
3827      *         The contribution type of the drop target.
3828      * @param {String} dropType
3829      *         A string representing the component drop type, expected values are: 'ctrlDropFromWithin', 'dropFromWithin', 'ctrlDropFromWithout' or 'dropFromWithout'.
3830      * @return {Object}
3831      *         An object containing the properties of the matching drop event contribution
3832      */
3833     this.getDropRules = function(subordinateType, dropType) {
3834         var popRule = null;
3835         for (var i = 0; i < this.popRulesArray.length; i++) {
3836             var rule = this.popRulesArray[i];
3837             if (subordinateType === rule.subordinate_type) {
3838                 popRule = rule;
3839                 break;
3840             }
3841         }
3842         if (popRule && popRule[dropType]) {
3843             var dropRules = new AC.data.Contribution(popRule[dropType]).getParsedProperties();
3844             dropRules.dropDisabled  = dropRules.enabled      !== 'Y';
3845             dropRules.doMove        = dropRules.action       === 'MOVE';
3846             dropRules.doCopyShallow = dropRules.action       === 'COPY SHALLOW';
3847             dropRules.doCopyDeep    = dropRules.action       === 'COPY DEEP';
3848             dropRules.toSub         = dropRules.location     === 'SUB';
3849             dropRules.toBefore      = dropRules.location     === 'BEFORE';
3850             dropRules.toAfter       = dropRules.location     === 'AFTER';
3851             dropRules.toAppend      = dropRules.location     === 'APPEND';
3852             dropRules.toStandard    = dropRules.location     === 'STANDARD';
3853             return dropRules;
3854         }
3855     };
3856 };
3857 
3858 /**
3859  * Default constructor.
3860  *
3861  * @class Defines the contribution counter nodes.
3862  * @param contributionChildCounter
3863  *          the child counter
3864  * @param contributionId
3865  *          the contribution id
3866  * @param thumbprints
3867  *          the thumbprints
3868  * @param superiorNode
3869  *          the superior of this node
3870  */
3871 AC.data.ContributionChildCounterNode = function (contributionChildCounter, contributionId, thumbprints, superiorNode) {
3872     /**
3873      * Indicates if this is a new contribution
3874      *
3875      * @default false
3876      */
3877     this._isNew = false;
3878 
3879     /** The child counter. */
3880     this._contributionChildCounter = contributionChildCounter;
3881 
3882     /** The contribution id. */
3883     this._contributionId = contributionId;
3884 
3885     /** The superior node. */
3886     this._superiorNode = superiorNode;
3887 
3888     /** The array of subordinates. */
3889     this._subordinates = [];
3890 
3891     /** The AC listener. */
3892     this._actionCenterListener = new ActionCenterListener();
3893 
3894     /** The array of all child types. */
3895     this._childTypeList = this._contributionChildCounter._populationRulesUtil.getAllChildTypeList();
3896 
3897     /**
3898      * Looks up a child node with the specified id in the list of subordinates.
3899      *
3900      * @param id
3901      *          the contributon id
3902      * @return the subordinate node with the specified id or null if no such node is found
3903      */
3904     this.findChildById = function (id) {
3905         for (var x = 0; x < this._subordinates.length; x++) {
3906             if (id === this._subordinates[x]._contributionId) {
3907                 return this._subordinates[x];
3908             }
3909         }
3910         return null;
3911     };
3912 
3913     /**
3914      * Creates a child contribution counter node with the configuration of the specified message.
3915      *
3916      * @param msg
3917      *          the message
3918      * @return void
3919      */
3920     this.createChild = function (msg) {
3921         var id = msg.data.contribution.id;
3922         if (id) {
3923             if (!this.findChildById(id)) {
3924                 this._subordinates.push(
3925                     new AC.data.ContributionChildCounterNode(
3926                         this._contributionChildCounter,
3927                         msg.data.contribution.id,
3928                         msg.data.contribution.thumbprints,
3929                         this
3930                     )
3931                 );
3932             }
3933         }
3934     };
3935 
3936     /**
3937      * Deletes the child node specified in the message by invoking the destroy function on the node.
3938      *
3939      * @param msg
3940      *          the message
3941      * @return void
3942      */
3943     this.deleteChild = function (msg) {
3944         if (!this.isMove(msg)) {
3945             var childNode = this.findChildById(msg.data.contribution.id);
3946             if (childNode) {
3947                 childNode.destroy();
3948             }
3949         }
3950     };
3951 
3952     /**
3953      * Destroys this node.
3954      *
3955      * @param msg
3956      *          the message
3957      * @return void
3958      */
3959     this.destroy = function (msg) {
3960         // destroy all subordinate nodes
3961         while (this._subordinates.length > 0) {
3962             this._subordinates[0].destroy();
3963         }
3964         // unsubscribe
3965         this._actionCenterListener.destroy();
3966         if (this._superiorNode) {
3967             // decrement counters
3968             if (this._isNew) {
3969                 this._contributionChildCounter.decrementNewCount();
3970             }
3971             this._contributionChildCounter.decrementCount();
3972             // Remove this node from it's superior's collection of subordinates
3973             this._superiorNode._subordinates.remove(this);
3974         }
3975     };
3976 
3977     /**
3978      * Checks if the action specified by the message is a move, instead of a delete, message.
3979      *
3980      * @param msg
3981      *          the message
3982      * @return true if the delete is part of a move or false otherwise
3983      */
3984     this.isMove = function (msg) {
3985         return false;
3986     };
3987 
3988     /**
3989      * Thumbprints this node upon receipt of a thumbprint message.
3990      *
3991      * @param msg
3992      *          the message
3993      * @return void
3994      */
3995     this.thumbprinted = function (msg) {
3996         this._actionCenterListener.unsubscribeThumbprints();
3997         this._contributionChildCounter.decrementNewCount();
3998         this._isNew = false;
3999     };
4000 
4001     if (this._superiorNode) {
4002         //  if this is not the top level node, increment appropriate counters
4003         this._contributionChildCounter.incrementCount();
4004 
4005         if (!thumbprints || thumbprints.indexOf(actionCenterAPI.getUserId()) === -1) {
4006             // if this user's thumbprint is not on this contribution,
4007             // mark as new
4008             this._isNew = true;
4009             // increment the new counter
4010             this._contributionChildCounter.incrementNewCount();
4011             //  and listen for thumbprint
4012             this._actionCenterListener.getThumbprints(this._contributionId, this, 'thumbprinted');
4013         }
4014     }
4015 
4016     for (var x = 0; x < this._childTypeList.length; x++) {
4017         this._actionCenterListener.getSubordinates(
4018                 this._contributionId,
4019                 this._childTypeList[x].relationship,
4020                 this._childTypeList[x].type,
4021                 this, 'createChild',
4022                 false,
4023                 this, 'deleteChild',
4024                 null, true);
4025     }
4026 }
4027 
4028 /**
4029  * Default constructor.
4030  *
4031  * @class Contribution counter.
4032  * @param superiorId
4033  *          the superior counter node's contribution id
4034  * @param populationRules
4035  *          the population rules
4036  * @param callbackobject
4037  *          the callback object
4038  * @param callbackmethod
4039  *          the callback method
4040  */
4041 AC.data.ContributionChildCounter = function (superiorId, populationRules, callbackobject, callbackmethod) {
4042 
4043     /** The population rule utility: i.e. an instance of AC.data.PopulationRulesParser. */
4044     this._populationRulesUtil = populationRules;
4045 
4046     this._callbackobject = callbackobject;
4047 
4048     /** The callback method. */
4049     this._callbackmethod = callbackmethod;
4050 
4051     /** The count of all (both new and old) contributions. */
4052     this.count = 0;
4053 
4054     /** The count of new contributions. */
4055     this.newCount = 0;
4056 
4057     /**
4058      * Increments the count of all contributions.
4059      *
4060      * @return void
4061      */
4062     this.incrementCount = function () {
4063         this.count++;
4064         this.doCallback();
4065     };
4066 
4067     /**
4068      * Decrements the count of all contributions.
4069      *
4070      * @return void
4071      */
4072     this.decrementCount = function () {
4073         this.count--;
4074         this.doCallback();
4075     };
4076 
4077     /**
4078      * Increments the count of new contributions.
4079      *
4080      * @return void
4081      */
4082     this.incrementNewCount = function () {
4083         this.newCount++;
4084         this.doCallback();
4085     };
4086 
4087     /**
4088      * Decrements the count of new contributions.
4089      *
4090      * @return void
4091      */
4092     this.decrementNewCount = function () {
4093         this.newCount--;
4094         this.doCallback();
4095     };
4096 
4097     /**
4098      * Invokes the callback method on the callback object.
4099      *
4100      * @return void
4101      */
4102     this.doCallback = function () {
4103         this._callbackmethod.call(this._callbackobject, this);
4104     };
4105 
4106     /** Contribution counter node. */
4107     this._superiorContribution = new AC.data.ContributionChildCounterNode(this, superiorId);
4108 
4109     /**
4110      * Destroys the contribution counter node.
4111      *
4112      * @return void
4113      */
4114     this.destroy = function () {
4115         this._superiorContribution.destroy();
4116     };
4117 }
4118 
4119 // TODO: move the listener into AC namespace
4120 /**
4121  * Default constructor.
4122  *
4123  * @class ActionCenters listener.
4124  */
4125 function ActionCenterListener() {
4126 
4127     /**
4128      * An array of channels on which this listener is registered.
4129      *
4130      * @private
4131      * @field
4132      * @type Array
4133      */
4134     this.subscribedChannels = [];
4135 
4136     /**
4137      * The subscription to listen for a thumbprint.
4138      *
4139      * @private
4140      * @field
4141      * @type Subscription
4142      */
4143     this.thumbprintSubscription = null;
4144 
4145     /**
4146      * Registers a callback function which will be invoked once for each contribution which matches the type and relationship to the superior contribution.  The
4147      * callback function will also be called for each contribution which is added that matches the type and relationship to the superior contribution.  If a remove
4148      * object and method is provided, a callback to that function will be invoked whenever a contribution matching the type and relationship to the superior contribution
4149      * is deleted.
4150      *
4151      * @param {String} contributionId
4152      *          The id of the superior contribution.
4153      * @param {String} relationshipType
4154      *          The type of the relationship.
4155      * @param {String} subordinateType
4156      *          The type of the subordinate contribution.
4157      * @param {Object} addObjectToCall
4158      *          Callback object to invoke after addition of the relationship.
4159      * @param {String} addMethodToCall
4160      *          Callback method to invoke after addition of the relationship.
4161      * @param {Boolean} [createOneIfNoneExists]
4162      *          Boolean flag to indicate whether to create a subordinate contribution if none has been found.
4163      * @param {Object} [removeObjectToCall]
4164      *          Callback object to invoke after removal of the relationship.
4165      * @param {String} [removeMethodToCall]
4166      *          Callback method to invoke after removal of the relationship.
4167      * @param {AC.data.LoadMonitor} [loadMonitor]
4168      *          Load monitor to monitor the initial loading of a tree.
4169      * @param {Boolean} [miniMessage]
4170      *          Exclude properties, dates, and locked by information from the callback message.  This is useful for contribution counters.  Default value is false.
4171      * @param {String} [createOneMode]
4172      *          If this is 'project', then setting createOneIfNoneExists to true will create a subordinate project.
4173      * @return void
4174      */
4175     this.getSubordinates = function (contributionId, relationshipType, subordinateType,
4176             addObjectToCall, addMethodToCall,
4177             createOneIfNoneExists, removeObjectToCall, removeMethodToCall, loadMonitor, miniMessage, createOneMode) {
4178         actionCenterAPI.startBatch();
4179         var addChannel =
4180             "/contributions/relationship/" + contributionId + "/" + relationshipType + "/" + subordinateType + "/add";
4181         this.subscribedChannels.push(dojox.cometd.subscribe(addChannel, addObjectToCall, addMethodToCall));
4182         if (loadMonitor) {
4183             loadMonitor.increment();
4184         }
4185         actionCenterAPI.publishRelatedContributions(addChannel, createOneIfNoneExists, createOneMode, miniMessage, addObjectToCall,
4186             function (msg) {
4187                 var subordinates = msg.data.relatedContributions;
4188                 this.childNodesLength = subordinates.length;
4189                 for (var i = 0; i < subordinates.length; i++) {
4190                     var subMsg = {data: {contribution: subordinates[i]}, channel: msg.data.AC_requestedchannel};
4191                     if (this.type && this.type === "AC_ComponentSuperior" && !this.childNodes) {
4192                         // in this case processCreateChild throws exception
4193                     } else {
4194                         eval("this." + addMethodToCall + "(subMsg)"); // invoke the add method
4195                     }
4196                 }
4197                 if (loadMonitor) {
4198                     loadMonitor.decrement();
4199                 }
4200             }
4201         );
4202         if (removeObjectToCall && removeMethodToCall) {
4203             var deleteChannel = "/contributions/relationship/" + contributionId + "/" + relationshipType + "/" +
4204                 subordinateType + "/delete";
4205             this.subscribedChannels.push(dojox.cometd.subscribe(deleteChannel, removeObjectToCall, removeMethodToCall));
4206         }
4207         actionCenterAPI.endBatch();
4208     };
4209 
4210     /**
4211      * Gets contributions of all types, which are subordinate to the specified contribution with the
4212      * specified relationship type.  Registers a callback function which will be invoked once for each contribution of any type and the provided relationship
4213      * to the superior contribution.  The  callback function will also be called for each contribution which is added that matches the type and relationship
4214      * to the superior contribution.
4215      *
4216      * @param {String} contributionId
4217      *          The id of the superior contribution.
4218      * @param {String} relationshipType
4219      *          The type of the relationship.
4220      * @param {Object} objectToCall
4221      *          Callback object to invoke after addition of the relationship.
4222      * @param {String} methodToCall
4223      *          Callback method to invoke after addition of the relationship.
4224      * @param {AC.data.LoadMonitor} [loadMonitor]
4225      *          Load monitor to monitor the initial loading of a tree.
4226      * @param {Boolean} [miniMessage]
4227      *          Exclude properties, dates, and locked by information from the callback message.  This is useful for contribution counters.  Default value is false.
4228      * @return void
4229      */
4230     this.getAllSubordinates = function (contributionId, relationshipType, objectToCall, methodToCall, loadMonitor, miniMessage) {
4231         actionCenterAPI.startBatch();
4232         var actionChannel = "/contributions/relationship/" + contributionId + "/" + relationshipType + "/**";
4233         this.subscribedChannels.push(dojox.cometd.subscribe(actionChannel, objectToCall, methodToCall));
4234         if (loadMonitor) {
4235             loadMonitor.increment();
4236         }
4237         actionCenterAPI.publishRelatedContributions(actionChannel, false, null, miniMessage, objectToCall,
4238             function (msg) {
4239                 var subordinates = msg.data.relatedContributions;
4240                 this.childNodesLength = subordinates.length;
4241                 for (var i = 0; i < subordinates.length; i++) {
4242                     var subMsg = {data: {contribution: subordinates[i]}, channel: msg.data.AC_requestedchannel};
4243                     if (this.type && this.type === "AC_ComponentSuperior" && !this.childNodes) {
4244                         // in this case processCreateChild throws exception
4245                     } else {
4246                         eval("this." + methodToCall + "(subMsg)"); // invoke the add method
4247                     }
4248                 }
4249                 if (loadMonitor) {
4250                     loadMonitor.decrement();
4251                 }
4252             }
4253         );
4254         actionCenterAPI.endBatch();
4255     };
4256 
4257     /**
4258      * Monitors a relationship with the specified criteria.
4259      *
4260      * @param {String} superiorId
4261      *          The id of the superior contribution.
4262      * @param {String} relationshipType
4263      *          The type of the relationship.
4264      * @param {String} subordinateId
4265      *          The subordinate id.
4266      * @param {Object} addObjectToCall
4267      *          Callback object to invoke after addition of the relationship.
4268      * @param {String} addMethodToCall
4269      *          Callback method to invoke after addition of the relationship.
4270      * @param {Object} [removeObjectToCall]
4271      *          Callback object to invoke after removal of the relationship.
4272      * @param {String} [removeMethodToCall]
4273      *          Callback method to invoke after removal of the relationship.
4274      * @return void
4275      */
4276     this.monitorRelationship = function (superiorId, relationshipType, subordinateId, addObjectToCall, addMethodToCall,
4277             removeObjectToCall, removeMethodToCall) {
4278         actionCenterAPI.startBatch();
4279         if (addObjectToCall && addMethodToCall) {
4280             var addChannel =
4281                 "/contributions/relationship/" + superiorId + "/" + relationshipType + "/" + subordinateId + "/add";
4282             this.subscribedChannels.push(dojox.cometd.subscribe(addChannel, addObjectToCall, addMethodToCall));
4283         }
4284         if (removeObjectToCall && removeMethodToCall) {
4285             var deleteChannel =
4286                 "/contributions/relationship/" + superiorId + "/" + relationshipType + "/" + subordinateId + "/delete";
4287             this.subscribedChannels.push(dojox.cometd.subscribe(deleteChannel, removeObjectToCall, removeMethodToCall));
4288         }
4289         if (addChannel) {
4290            actionCenterAPI.publishRelationship(addChannel);
4291         }
4292         actionCenterAPI.endBatch();
4293     };
4294 
4295     /**
4296      * Subscribes to the channel of contribution updates for the specified contribution.
4297      *
4298      * @param {String} contributionId
4299      *          The id of the contribution.
4300      * @param {Object} objectToCall
4301      *          Callback object to invoke for each update to the contribution.
4302      * @param {String} methodToCall
4303      *          Callback method to invoke for each update to the contribution.
4304      * @return void
4305      */
4306     this.getContributionUpdates = function (contributionId, objectToCall, methodToCall) {
4307         var updateChannel = "/contributions/contribution/" + contributionId + "/update";
4308         this.subscribedChannels.push(dojox.cometd.subscribe(updateChannel, objectToCall, methodToCall));
4309     };   
4310     
4311     /**
4312      * Subscribes to the channel of contribution property updates for the specified contribution and the specified
4313      * property.
4314      *
4315      * @param {String} contributionId
4316      *          The id of the contribution.
4317      * @param {String} propertyName
4318      *          the name of the property
4319      * @param {Object} objectToCall
4320      *          Callback object to invoke for each update to the contribution.
4321      * @param {String} methodToCall
4322      *          Callback method to invoke for each update to the contribution.
4323      * @return void
4324      */
4325     this.getContributionPropertyUpdates = function (contributionId, propertyName, objectToCall, methodToCall) {
4326         var updateChannel = "/contributions/contribution/" + contributionId + "/property/" +  propertyName + "/update";
4327         this.subscribedChannels.push(dojox.cometd.subscribe(updateChannel, objectToCall, methodToCall));
4328     };
4329     
4330     /**
4331      * Subscribes to the channel of contribution property deletion for the specified contribution and the specified
4332      * property.
4333      *
4334      * @param {String} contributionId
4335      *          The id of the contribution.
4336      * @param {String} propertyName
4337      *          the name of the property
4338      * @param {Object} objectToCall
4339      *          Callback object to invoke for each update to the contribution.
4340      * @param {String} methodToCall
4341      *          Callback method to invoke for each update to the contribution.
4342      * @return void
4343      */
4344     this.getContributionPropertyDelete = function (contributionId, propertyName, objectToCall, methodToCall) {
4345         var deleteChannel = "/contributions/contribution/" + contributionId + "/property/" +  propertyName + "/delete";
4346         this.subscribedChannels.push(dojox.cometd.subscribe(deleteChannel, objectToCall, methodToCall));
4347     };
4348 
4349     /**
4350      * Subscribes to the channel to listen for the delete of the specified contribution.
4351      *
4352      * @param {String} contributionId
4353      *          The id of the contribution.
4354      * @param {Object} objectToCall
4355      *          Callback object to invoke for each update to the contribution.
4356      * @param {String} methodToCall
4357      *          Callback method to invoke for each update to the contribution.
4358      * @return void
4359      */
4360     this.getContributionDelete = function (contributionId, objectToCall, methodToCall) {
4361         var deleteChannel = "/contributions/contribution/" + contributionId + "/delete";
4362         this.subscribedChannels.push(dojox.cometd.subscribe(deleteChannel, objectToCall, methodToCall));
4363     };
4364 
4365     /**
4366      * Subscribes to listen for users in a given system role.
4367      *
4368      * @param {String[]} systemRoles
4369      *          The array of system roles.
4370      * @param {Object} objectToCall
4371      *          Callback object to invoke for each update to the contribution.
4372      * @param {String} methodToCall
4373      *          Callback method to invoke for each update to the contribution.
4374      * @return void
4375      */
4376     this.getUserUpdates = function (systemRoles,
4377                                         addObjectToCall, addMethodToCall, removeObjectToCall, removeMethodToCall) {
4378         actionCenterAPI.startBatch();
4379         for (var index = 0; index < systemRoles.length; index++) {
4380             if (addObjectToCall && addMethodToCall) {
4381                 this.subscribedChannels.push(dojox.cometd.subscribe(
4382                     "/contributions/relationship/systemuser/childof/" + systemRoles[index] + "/add",
4383                     addObjectToCall, addMethodToCall
4384                 ));
4385             }
4386             if (removeObjectToCall && removeMethodToCall) {
4387                 this.subscribedChannels.push(dojox.cometd.subscribe(
4388                     "/contributions/relationship/systemuser/childof/" + systemRoles[index] + "/delete",
4389                     removeObjectToCall, removeMethodToCall
4390                 ));
4391             }
4392         }
4393         actionCenterAPI.endBatch();
4394     };
4395 
4396     /**
4397      * Subscribes to the channel to listen for thumbprints on the specified contribution.
4398      *
4399      * @param {String} contributionId
4400      *          The id of the contribution.
4401      * @param {Object} objectToCall
4402      *          Callback object to invoke for each update to the contribution.
4403      * @param {String} methodToCall
4404      *          Callback method to invoke for each update to the contribution.
4405      * @return void
4406      */
4407     this.getThumbprints = function (contributionId, objectToCall, methodToCall) {
4408         var userId = actionCenterAPI.getUserId();
4409         var channel = "/contributions/contribution/" + contributionId + "/" + userId + "/thumbprints";
4410         this.thumbprintSubscription = dojox.cometd.subscribe(channel, objectToCall, methodToCall);
4411     };
4412 
4413     /**
4414      * Unsubscribes from the thumbprint channel.
4415      *
4416      * @return void
4417      */
4418     this.unsubscribeThumbprints = function () {
4419         if (this.thumbprintSubscription) {
4420             dojox.cometd.unsubscribe(this.thumbprintSubscription);
4421             this.thumbprintSubscription = null;
4422         }
4423     };
4424 
4425     /**
4426      * Unsubscribes from all channels on which this listener has been registered.  This function should be called by
4427      * this ActionCenterListener's owning object destroy function.
4428      *
4429      * @return void
4430      */
4431     this.destroy = function () {
4432         dojox.cometd.startBatch();
4433         while (this.subscribedChannels.length > 0) {
4434             dojox.cometd.unsubscribe(this.subscribedChannels.pop());
4435         }
4436         this.unsubscribeThumbprints();
4437         dojox.cometd.endBatch();
4438     };
4439 }
4440 
4441 /**
4442  * Default constructor.
4443  *
4444  * @class AC load monitor.
4445  * @extends Ext.util.Observable
4446  * @param owner
4447  *          the owner of this load monitor
4448  */
4449 AC.data.LoadMonitor = function (owner) {
4450     this.owner = owner;
4451     this.counter = 0;
4452     this.isActive = true;
4453 };
4454 
4455 Ext.extend(AC.data.LoadMonitor, Ext.util.Observable,
4456 /**
4457  * @lends AC.data.LoadMonitor
4458  */
4459 {
4460     /**
4461      * Counter indicating the status of data loading. Zero means loading is done.
4462      *
4463      * @default 0
4464      */
4465     counter: 0,
4466 
4467     /**
4468      * Indicates if this monitor is active.
4469      *
4470      * @default false
4471      */
4472     isActive: false,
4473 
4474     /**
4475      * Owner of this monitor.
4476      *
4477      * @default null
4478      */
4479     owner: null,
4480 
4481     /**
4482      * Increments the load counter.
4483      *
4484      * @return void
4485      */
4486     increment: function () {
4487         this.counter++;
4488     },
4489 
4490     /**
4491      * Decrements the load counter.
4492      *
4493      * @return void
4494      */
4495     decrement: function () {
4496         this.counter--;
4497         if (this.isActive && this.counter === 0) {
4498             this.afterLoad();
4499         }
4500     },
4501 
4502     /**
4503      * Invoked after the load counter reaches zero.
4504      *
4505      * @return void
4506      */
4507     afterLoad : function () {
4508         //this.owner.fireEvent('afterload');
4509     }
4510 });
4511 
4512 /**
4513  * Default constructor.
4514  *
4515  * @class AC tree load monitor.
4516  * @extends AC.data.LoadMonitor
4517  * @param owner
4518  *          the owner of this monitor
4519  */
4520 AC.data.TreeLoadMonitor = function (owner) {
4521     AC.data.TreeLoadMonitor.superclass.constructor.apply(this, arguments);
4522 };
4523 
4524 Ext.extend(AC.data.TreeLoadMonitor, AC.data.LoadMonitor,
4525 /** @lends AC.data.TreeLoadMonitor */
4526 {
4527     /**
4528      * Fires the 'aftertreeload' event and disables the load monitor.
4529      *
4530      * @return void
4531      */
4532     afterLoad : function () {
4533         AC.data.TreeLoadMonitor.superclass.afterLoad.apply(this, arguments);
4534         this.isActive = false;
4535         this.owner.fireEvent('aftertreeload');
4536     }
4537 });
4538 
4539 /**
4540  * Default contructor.
4541  *
4542  * @class Defines the superior of a tree-grid cell.
4543  * @param superiorContributionType the superior contribution type.
4544  * @param superiorContributionType the superior contribution id.
4545  */
4546 AC.tree.grid.Superior = function (superiorContributionType, superiorContributionId) {
4547 
4548     /**
4549      * Superior contribution type.
4550      *
4551      * @field
4552      * @type String
4553      */
4554     this._superiorContributionType = superiorContributionType;
4555 
4556     /**
4557      * Superior contribution id.
4558      *
4559      * @field
4560      * @type String
4561      */
4562     this._superiorContributionId = superiorContributionId;
4563 
4564     /**
4565      * Gets the superior contribution type.
4566      *
4567      * @return the superior contribution type
4568      */
4569     this.getSuperiorContributionType = function () {
4570         return this._superiorContributionType;
4571     };
4572 
4573     /**
4574      * Gets the superior contribution id.
4575      *
4576      * @return the superior contribution id
4577      */
4578     this.getSuperiorContributionId = function () {
4579         return this._superiorContributionId;
4580     };
4581 }
4582 
4583 /**
4584  * Default constructor.
4585  *
4586  * @class A hash table for persisting widgets which have been instantiated (aka treegrid.objectManager).
4587  * @param factory the factory that will be used to create the widget if one does not already exist.
4588  *        The factory must have a method with the signature getWidget(AC.tree.grid.Superior[]).
4589  */
4590 AC.tree.grid.WidgetManager = function (factory) {
4591     /**
4592      * A factory that will be used to create the widget if one does not already exist.
4593      * The factory must have a method that with the signature getWidget(AC.tree.grid.Superior[]).
4594      *
4595      * @field
4596      * @type Factory
4597      */
4598     this._factory = factory;
4599 
4600     /**
4601      * A hashmap that will be used to hold the widgets that have been created.
4602      *
4603      * @field
4604      * @type Ext.util.MixedCollection
4605      */
4606     this.hashMap = new Ext.util.MixedCollection(false);
4607 
4608     /**
4609      * Get a Widget for the given relationship/s.
4610      *
4611      * @param arrayOfSuperiors
4612      *          an array of AC.tree.grid.Superior objects which are the superiors
4613      *          for the contribution which is represented by the widget that we are looking for.
4614      * @return the widget for the given superior combination.
4615      */
4616     this.getWidgetForRelationship = function (arrayOfSuperiors, treegrid, config) {
4617         var idKey = this.getIdKey(arrayOfSuperiors);
4618         if (this.hashMap.containsKey(idKey)) {
4619             return this.hashMap.get(idKey);
4620         } else {
4621             var widget = this._factory.getWidget(arrayOfSuperiors, treegrid, config);
4622             return this.hashMap.add(idKey, widget);
4623         }
4624     };
4625 
4626     /**
4627      * Get a unique id based on the given superiors for storing and retrieving the widget in a hashtable.
4628      *
4629      * @param arrayOfSuperiors
4630      *          an array of AC.tree.grid.Superior objects which are the superiors
4631      *          for the contribution which is represented by the widget that we are looking for.
4632      * @return a unique string id
4633      */
4634     this.getIdKey = function (arrayOfSuperiors) {
4635         var returnValue = '';
4636         if (arrayOfSuperiors.length > 0) {
4637             returnValue += arrayOfSuperiors[0].getSuperiorContributionId();
4638         }
4639         for (var index = 1; index < arrayOfSuperiors.length; index++) {
4640             returnValue += acGlobalDelimiter + arrayOfSuperiors[index].getSuperiorContributionId();
4641         }
4642         return returnValue;
4643     };
4644 
4645     /**
4646      * Destroys the widget. This method is called for every widget in the hashtable upon destroy of this
4647      * hash table.
4648      *
4649      * @param widget
4650      *          the widget to be destroyed
4651      * @return void
4652      */
4653     this.destroyEach = function (widget) {
4654         widget.destroy();
4655     };
4656 
4657     /**
4658      * Destroys this class and destroys every widget contained by this class.
4659      *
4660      * @return void
4661      */
4662     this.destroy = function () {
4663         var hashdestroy;
4664         var items = [].concat(this.hashMap.items); // each safe for removal
4665         for(var i = 0, len = items.length; i < len; i++){
4666              var widget = items[i];
4667              if(widget != null){
4668                  this.destroyEach(widget);
4669              }
4670          }
4671         //this.hashMap.each(this.destroyEach);
4672         this.hashMap.clear();
4673     };
4674 }
4675 
4676 /**
4677  * Default constructor.
4678  *
4679  * @class Factory of widgets corresponding to a given column and row.
4680  */
4681 AC.tree.grid.CellObjectFactory = function () {
4682 
4683     /** The hash map of widgets. */
4684     this.hashMap = new Ext.util.MixedCollection(false);
4685 
4686     /**
4687      * Gets a widget corresponding to the specified array of superiors.
4688      *
4689      * @param arrayOfSuperiors
4690      *          the array of superiors
4691      * @param treegrid
4692      *          the treegrid
4693      * @param config
4694      *          the configuration of the widget
4695      * @return the corresponding widget
4696      */
4697     this.getWidget = function (arrayOfSuperiors, treegrid, config) {
4698         var keyForObject = '';
4699         var colid = null;
4700         var rowid = null;
4701         if (!config) {
4702             config = {};
4703         }
4704         for (var i = 0; i < arrayOfSuperiors.length; i++) {
4705             var thisobj = arrayOfSuperiors[i];
4706             if (i === 0) {
4707                 colid = thisobj.getSuperiorContributionId();
4708             }
4709             if (i === 1) {
4710                 rowid = thisobj.getSuperiorContributionId();
4711             }
4712             keyForObject += thisobj.getSuperiorContributionType();
4713         }
4714         var objtype = null;
4715         if (this.hashMap.containsKey(keyForObject)) {
4716             objtype = this.hashMap.get(keyForObject);
4717             if (!objtype) {
4718                 return null;
4719             }
4720             var cell = actioncenter.tgrCellCreate(objtype, rowid, colid, treegrid, config);
4721             return cell;
4722         }
4723     };
4724 
4725     /**
4726      * Adds the specified widget into the factory's hash map.
4727      *
4728      * @param arrayOfTypes
4729      *          the array of types corresponding to the widget
4730      * @param objtype
4731      *          the type of the widget
4732      * @param config
4733      *          the configuration of the widget
4734      * @return void
4735      */
4736     this.addWidget = function (arrayOfTypes, objtype, config) {
4737         var keyForObject = '';
4738         for (var i = 0; i < arrayOfTypes.length; i++) {
4739             keyForObject += arrayOfTypes[i];
4740         }
4741         this.hashMap.add(keyForObject, objtype);
4742     };
4743 
4744     this.destroy = function() {
4745         this.hashMap.clear();
4746         this.hashMap = null;
4747     };
4748 }