1 /**
  2  * @class A manager for workflow.  Handles transitions between steps in the flow.
  3  * Contains the initial workbench screen.
  4  * @extends Monk.component.Component
  5  * @author Andrew
  6  * 
  7  * @property {Ext.util.MixedCollection} steps A list containing all the steps in the workflow.
  8  * @property {String} currentStep The ID of the step currently being viewed.
  9  * @property {Ext.Panel} panel This is the container for all the steps in the flow, the toolbar for the steps, and the universal continue button.
 10  * An {@link Ext.ux.AnimatedCardLayout} is used to switch between steps.
 11  */
 12 Monk.component.FlowManager = function(args) {
 13     
 14     this.steps = new Ext.util.MixedCollection();
 15     this.currentStep = '';
 16     
 17     var home = new Monk.component.ProjectSelector();
 18     
 19     this.panel = new Ext.Panel({
 20         id: 'main-panel',
 21         collapsible: false,
 22         layout: 'animatedcard',
 23         activeItem: 0,
 24         border: false,
 25         tbar: [{
 26             xtype:'tbcustomfill',
 27             width: '110px'
 28         }],
 29         bbar: [{
 30             id: 'universal-previous-button',
 31             text: '« previous',
 32             scope: this
 33         },{
 34             xtype: 'tbfill'
 35         },{
 36             id: 'universal-continue-button',
 37             text: 'continue »',
 38             scope: this
 39         }],
 40         items: home.panel
 41     });
 42     
 43     this.panel.on('render', function(panel){
 44         panel.getBottomToolbar().setHeight(40);
 45         
 46         /**
 47          * Removes an item from the toolbar
 48          * @param {String|Integer} key The ID/index of the item.
 49          */
 50         panel.getTopToolbar().remove = function(item) {
 51             if(this.rendered && item){
 52                 if(item.destroy)item.destroy();
 53                 if(item.removeListeners)item.removeListeners();
 54             
 55                 //all items are contained by a <TD> tag, so might need:
 56                 if(item.dom && item.dom.parentNode){
 57                     var td = item.dom.parentNode;
 58                     td.removeChild(item.dom);
 59                     td.parentNode.removeChild(td);
 60                 }
 61             
 62                 this.items.remove(item);
 63             }
 64         };
 65         
 66         // add the panel to the list of steps
 67         this.addStep(home);
 68     }, this);
 69     
 70     Monk.component.FlowManager.superclass.constructor.call(this, args);
 71 }
 72 
 73 Workbench.extend(Monk.component.FlowManager, Monk.component.Component, {
 74 /** @lends Monk.component.FlowManager.prototype */
 75     
 76     id: 'flow-manager',
 77     label : "Flow Manager",
 78     
 79     /**
 80      * For moving between steps in the workflow.
 81      * @param {String|Integer} key The key or index of the step.
 82      * @param {Boolean} [noFade] True to cancel default fade in animation (defaults to false). 
 83      */
 84     goToStep : function(key, noFade) {
 85         var step = this.steps.get(key);
 86         
 87         this.toolbarButtonToggle(step.id);
 88         
 89         // show the panel for the item
 90         this.panel.layout.setActiveItem(this.steps.indexOfKey(step.id), noFade);
 91         
 92         var oldStep = this.steps.get(this.currentStep);
 93         if (oldStep != null) {
 94             // call any function that needs to get called before we leave the old step
 95             if (oldStep.beforeExit) oldStep.beforeExit.call(oldStep);
 96             // set the previous button to get back to the old step
 97             Ext.getCmp('universal-previous-button').setHandler(
 98                 function() {
 99                     this.goToStep(oldStep.id);
100                 },
101                 this
102             );
103         }
104         
105         this.currentStep = step.id;
106         this.notify(new Monk.event.flowmanager.StepSelected({
107            label: 'StepSelected: "'+this.currentStep+'"',
108             "component" : this
109            }), this.currentStep);
110     },
111     
112     /**
113      * Set a specific button to appear pressed.
114      * @param {String} buttonId The button ID to use (corresponds to step ID).
115      */
116     toolbarButtonToggle : function(buttonId) {
117         // set all toolbar items to not pressed
118         var tbar = this.panel.getTopToolbar();
119         tbar.items.each(function(item, index, length){
120             if (item.pressed) item.toggle(false);
121             return true;
122         });
123         
124         // press the item that we're moving to
125         var button = tbar.items.get(buttonId);
126         if (button) button.toggle(true);
127     },
128     
129     /**
130      * Check if the user's currently running a toolset, and confirm that they want to exit it.
131      * @param {String|Integer} key The key or index of the step to move to
132      */
133     withinToolsetCheck : function(key) {
134         if (this.currentStep != key) {
135             if (this.currentStep == 'tool-flow-manager') {
136                 if (Monk.component.dataManager.isWorksetUnsaved()) {
137                     Monk.component.messenger.confirm(
138                         'Monk Workbench',
139                         'Navigating away from your toolset will cause you to lose unsaved data.  Do you wish to continue?',
140                         function(buttonId) {
141                             if (buttonId == 'yes') {
142                                 this.goToStep(key)
143                                 // hide the search box
144                                 feature.searchComponent.hide();
145                             } else {
146                                 // turn off the button that was pressed
147                                 this.panel.getTopToolbar().items.get(key).toggle(false);
148                             };
149                         }, this);
150                 } else {
151                     this.goToStep(key)
152                     // hide the search box
153                     feature.searchComponent.hide();
154                 }
155             } else {
156                 this.goToStep(key);
157             }
158         }
159     },
160     
161     /**
162      * Adds a step to the flow
163      * @param {Object} nextStep
164      * @param {String} nextStep.id The ID of the step
165      * @param {String} nextStep.label The label for the step
166      * @param {Ext.Panel} nextStep.panel The panel to render
167      * @param {Boolean} [addTBarButton] Whether to add a toolbar button (defaults to true)
168      */
169     addStep : function(nextStep, addTBarButton) {
170         if (!this.steps.containsKey(nextStep.id)) {
171             // call any function that needs to get called after the panel is rendered
172             nextStep.panel.on('show', function() {
173                 if (nextStep.afterRenderFirstTime) {
174                     if (nextStep.afterRenderFirstTimeCalled == false) {
175                         nextStep.afterRenderFirstTime.call(nextStep);
176                         nextStep.afterRenderFirstTimeCalled = true;
177                     }
178                 }
179                 if (nextStep.afterRenderEveryTime) nextStep.afterRenderEveryTime.call(nextStep);
180             });
181             
182             this.steps.add(nextStep.id, nextStep);
183             
184             this.panel.add(nextStep.panel);
185             
186             if (addTBarButton !== false) {
187                 var tbar = this.panel.getTopToolbar();
188                 if (this.steps.getCount() > 1) tbar.addSeparator();
189                 tbar.addButton({
190                    id: nextStep.id,
191                    text: nextStep.label,
192                    handler: this.withinToolsetCheck.createDelegate(this, [nextStep.id]),
193                    enableToggle: true,
194                    pressed: false
195                 });
196             }
197         }
198     },
199     
200     /**
201      * Removes a step from the flow
202      * @param {String} stepId The ID of the step
203      */
204     removeStep : function(stepId) {
205         // remove from toolbar
206         var tbar = this.panel.getTopToolbar();
207         var step = tbar.items.get(stepId);
208         var separator = tbar.items.get(tbar.items.indexOfKey(stepId)-1);
209         if (step) tbar.remove(step);
210         if (separator) tbar.remove(separator);
211         // remove from steps
212         this.steps.removeKey(stepId);
213     },
214     
215     /**
216      * Removes the specified step, and all steps after it
217      * @param {String} stepId The ID of the step
218      */
219     trimSteps : function(stepId) {
220         var steps = this.steps.getRange(this.steps.indexOfKey(stepId));
221         for (var i = 0, len = steps.length; i < len; i++) {
222             this.removeStep(steps[i].id);
223         }
224     },
225     
226     /**
227      * Gets a particular step in the flow. Returns the step, or null if none is found.
228      * @param {String} key The ID of the step to get.
229      * @returns {Object} The step (or null)
230      */
231     getStep : function(key) {
232         return this.steps.get(key);
233     },
234     
235     /**
236      * Sets the button label for a particular step.
237      * @param {String} stepId The ID of the step
238      * @param {String} label The label to use for the step
239      */
240     setButtonLabel : function(stepId, label) {
241         var tbar = this.panel.getTopToolbar();
242         var button = tbar.items.get(stepId);
243         button.setText(label);
244     }    
245 });
246