1 /**
  2  * An object for editing toolsets.
  3  * @extends Monk.component.Component
  4  * @author Andrew
  5  */
  6 
  7 Monk.component.ToolsetEditor = function(args) {
  8 
  9     this.projectToolsets = args.projectToolsets;
 10 
 11     // the tools
 12     this.tools = feature.components;
 13 
 14     var toolsHTML = '<div id="toolsParent" style="float: left; width: 100%; height: 100%;">';
 15 
 16     var toolsTemplate = new Ext.Template(
 17         '<div id="{id}" class="draggable-tool">',
 18             '<img src="{icon}" title="{summary}" onerror="this.src=\'resources/images/tools/default_icon.gif\'"/>',
 19             '<span style="float: left; clear: left;">{label}</span>',
 20         '</div>'
 21     );
 22 
 23     this.tools.each(
 24         function(tool) {
 25             if (!tool.clone) { // don't add clones to the tool panel
 26                 toolsHTML += toolsTemplate.apply({id: tool.id, icon: tool.icon, label: tool.label, summary: tool.label + (tool.description ? ": " + tool.description : "")});
 27             }
 28         },
 29     this);
 30 
 31     toolsHTML += '</div>';
 32 
 33     this.toolsPanel = new Ext.Panel({
 34         id: 'tools-panel',
 35         collapsible: true,
 36         titleCollapse: true,
 37         autoScroll: true,
 38         region: 'south',
 39         title: 'tools',
 40         height: 130,
 41         html: toolsHTML
 42     });
 43 
 44     this.initialToolset = null;
 45 
 46     // keeps track of the tools for each "step" in the workflow
 47     this.toolsteps = new Ext.util.MixedCollection();
 48     // has an existing toolset been modified?
 49     this.modifiedToolset = false;
 50 
 51     this.toolsetEditorPanel = new Ext.Panel({
 52         id: 'toolsetEditorPanel',
 53         border: false,
 54         region: 'center',
 55         margins: '0 10 0 0',
 56         html:
 57         //'<span id="toolsetLabel"></span>'+
 58         '<div id="toolsetEditor" style="width: 100%; height: 97%; overflow: auto; float: left; clear: both;"></div>'
 59     });
 60 
 61     this.panel = new Ext.Panel({
 62         id: 'editorParentPanel',
 63         layout: 'border',
 64         defaults: {
 65             border: false
 66         },
 67         items: [
 68             {
 69                 title: '',
 70                 height: 70,
 71                 region: 'north',
 72                 html: '<h2 id="toolsetLabel"></h2>'+
 73                 '<div id="toolsetMenuBar">'+
 74                 '</div>'+
 75                 '<div style="clear: both;"></div>'
 76             },
 77             this.toolsetEditorPanel,
 78             this.toolsPanel
 79         ]
 80     });
 81 
 82     this.saveDialog = null;
 83 
 84     this.toolsetSelected = function(action) {
 85         var actionName = action || 'perform an action on';
 86         if (this.projectToolsets.selectedToolset == null) {
 87             Monk.component.messenger.alert('Monk Workbench', 'You must select a toolset before you can '+actionName+' it.');
 88             return false;
 89         } else {
 90             return true;
 91         }
 92     }
 93 
 94     this.toolsetCreated = function(action) {
 95         var actionName = action || 'perform an action on';
 96         if (this.toolsteps.getCount() == 0) {
 97             Monk.component.messenger.alert('Monk Workbench', 'You must create a toolset before you can '+actionName+' it.');
 98             return false;
 99         } else {
100             return true;
101         }
102     }
103 
104     this.fadeInToolstep = function(toolstepIndex) {
105         // TODO fix in Opera
106         var toolstep = this.toolsteps.get(toolstepIndex);
107         if (toolstep) {
108             var toolstepEl = Ext.get(toolstep.id);
109 
110             toolstepEl.shift({
111                 opacity: 1,
112                 duration: 0.3,
113                 callback: function(){
114                     toolstepEl.removeClass('invisible');
115                 }
116             });
117 
118             toolstepIndex++;
119 
120             this.fadeInToolstep.defer(150, this, [toolstepIndex]);
121         }
122     }
123 
124     this.afterRenderFirstTime = function() {
125         var buttonParent = Ext.get('toolsetMenuBar');
126 
127         var newButton = new Ext.Button({
128             id: 'newToolsetButton',
129             text: 'new',
130             renderTo: buttonParent,
131             handler: function(){
132                 Monk.component.messenger.confirm('Monk Workbench', 'This will create a new, empty, toolset.  Do you wish to continue?', function(btn){
133                     if (btn == 'yes') this.reset();
134                 }, this, this.window);
135             },
136             scope: this
137         });
138 
139         var saveButton = new Ext.Button({
140             id: 'saveToolsetButton',
141             text: 'save',
142             renderTo: buttonParent,
143             handler: function(){
144                 if (this.toolsetCreated('save')) {
145                     if (this.projectToolsets.selectedToolset == null) {
146                         if (this.saveDialog == null) {
147                             this.initializeSaveDialog();
148                         } else {
149                             this.saveDialog.show();
150                         }
151                     } else {
152                         if (this.projectToolsets.isToolsetDefault(this.projectToolsets.selectedToolset)) {
153                             if (this.saveDialog == null) {
154                                 this.initializeSaveDialog();
155                             } else {
156                                 this.saveDialog.show();
157                             }
158                         } else {
159                             this.saveToolset({toolsetId: this.projectToolsets.selectedToolset});
160                         }
161                     }
162                 }
163             },
164             scope: this
165         });
166 
167         var saveAsButton = new Ext.Button({
168             id: 'saveAsToolsetButton',
169             text: 'save as',
170             renderTo: buttonParent,
171             handler: function(){
172                 if (this.toolsetSelected('save a copy of')) {
173                     if (this.saveDialog == null) {
174                         this.initializeSaveDialog();
175                     } else {
176                         this.saveDialog.show();
177                     }
178                 }
179             },
180             scope: this
181         });
182 
183         var deleteButton = new Ext.Button({
184             id: 'deleteToolsetButton',
185             text: 'delete',
186             renderTo: buttonParent,
187             handler: function(){
188                 if (this.toolsetSelected('delete')) {
189                     if (feature.toolsets.get(this.projectToolsets.selectedToolset) != null) {
190                         Monk.component.messenger.alert('Monk Workbench', 'You cannot delete a default toolset.');
191                     } else {
192                         Monk.component.messenger.confirm('Monk Workbench', 'Are you sure you want to delete this toolset?', function(btn){
193                             if (btn == 'yes') {
194                                 Monk.data.toolset.deleteToolset(this.projectToolsets.selectedToolset);
195                             }
196                         }, this, this.window);
197                     }
198                 }
199             },
200             scope: this
201         });
202 
203         var exportButton = new Ext.Button({
204             id: 'exportToolsetButton',
205             text: 'export',
206             renderTo: buttonParent,
207             disabled: true,
208             handler: function(){
209                 if (this.toolsetSelected('export')) {
210                     Monk.component.messenger.alert('Monk Workbench', 'Not yet implemented.');
211                 }
212             },
213             scope: this
214         });
215 
216         var moveButton = new Ext.Button({
217             id: 'moveToolsetButton',
218             text: 'move',
219             renderTo: buttonParent,
220             disabled: true,
221             handler: function(){
222                 if (this.toolsetSelected('move')) {
223                     Monk.component.messenger.alert('Monk Workbench', 'Not yet implemented.');
224                 }
225             },
226             scope: this
227         });
228 
229         // set the toolset label
230 //        var labelField = new Ext.form.InlineTextField({
231 //            id: 'toolsetLabelField',
232 //            saveOnlyOnChange: true,
233 //            value: this.label,
234 //            grow: true,
235 //            cls: 'header'
236 //        });
237 //
238 //        labelField.saveMethod = function(toolsetEditor) {
239 //            var label = this.getValue();
240 //            toolsetEditor.label = label;
241 //        }.createDelegate(labelField, [this]);
242 //
243 //        // update the toolbar button when the toolset name changes
244 //        labelField.on('change', function(field, newVal, oldVal){
245 //            //feature.flowManager.setButtonLabel(this.id, newVal);
246 //        }, this);
247 //
248 //        labelField.render(Ext.get('toolsetLabel'));
249 
250         this.initializeDragDrop();
251     }
252 
253     this.initializeToolsteps = function() {
254         if (this.initialToolset.tools.length > 0) {
255             for (var i = 0; i < this.initialToolset.tools.length; i++) {
256                 var initialToolstep = this.initialToolset.tools[i];
257                 var components = initialToolstep.components;
258 
259                 var newToolstep = null;
260                 if (i == 0) {
261                     newToolstep = this.createToolstep(null, initialToolstep, true);
262                 } else {
263                     var siblingEl = Ext.get(this.toolsteps.itemAt(i - 1).id)
264                     newToolstep = this.createToolstep(siblingEl, initialToolstep, true);
265                 }
266 
267                 for (var j = 0; j < components.length; j++) {
268                     var component = components[j];
269                     // remove the clone append if it exists
270                     component = component.split('-clone')[0];
271                     var componentEl = Ext.get(component);
272                     if (componentEl == null) {
273                         Monk.component.messenger.alert(
274                             'Monk Workbench',
275                             'This toolset contains an unknown tool ('+component+') which cannot be loaded.'
276                         );
277                     } else {
278                         this.handleToolTransfer(newToolstep, componentEl);
279                     }
280                 }
281             }
282 
283             this.updateToolstepNumbers();
284 
285             this.fadeInToolstep(0);
286         }
287     }
288 
289     this.afterRenderEveryTime = function() {
290         var continueButton = Ext.getCmp('universal-continue-button');
291         continueButton.setHandler(
292             this.loadToolset,
293             this
294         );
295 
296         this.saveDialog = null;
297 
298         if (this.toolsteps.getCount() == 0) {
299             this.setToolsetLabel('new toolset');
300 
301             var toolset = this.projectToolsets.getToolset(this.projectToolsets.selectedToolset);
302             if (toolset != null) {
303                 this.initialToolset = toolset;
304                 this.setToolsetLabel(toolset.label);
305                 this.initializeToolsteps();
306             }
307         }
308     }
309 
310     Monk.component.ToolsetEditor.superclass.constructor.call(this, args);
311 }
312 
313 Workbench.extend(Monk.component.ToolsetEditor, Monk.component.Component, {
314 
315     id: 'toolset-editor',
316     label: 'Toolset Editor',
317     "window": this.window,
318 
319     handle : function(monkEvent, data) {
320         if (monkEvent.instanceOf(Monk.event.toolset.ToolsetSelected)) {
321             if (this.isVisible()) {
322                 this.reset();
323 
324                 if (data.selected) {
325                     this.initialToolset = data.toolset;
326                     this.setToolsetLabel(data.toolset.label);
327                     this.initializeToolsteps();
328 
329                     Ext.getCmp('saveAsToolsetButton').enable();
330                 } else {
331                     Ext.getCmp('saveAsToolsetButton').disable();
332                 }
333             }
334         } else if (monkEvent.instanceOf(Monk.event.toolset.ToolsetDeleted)) {
335             this.reset();
336         } else if (monkEvent.instanceOf(Monk.event.toolset.ToolsetCreated)) {
337             if (this.isVisible()) {
338                 var toolset = data.toolsetJson;
339 
340                 toolset.id = data.toolsetId;
341                 toolset.label = toolset.toolSetLabel;
342 
343                 delete toolset.toolSetLabel;
344                 delete toolset.toolSetConfig;
345 
346                 Monk.component.dataManager.getProjectData().toolsets.add(toolset.id, toolset);
347 
348                 this.setToolsetLabel(toolset.label);
349             }
350         } else if (monkEvent.instanceOf(Monk.event.toolset.ToolsetSaved)) {
351             if (this.isVisible()) {
352                 var toolset = data.toolsetJson;
353 
354                 toolset.label = toolset.toolSetLabel;
355 
356                 delete toolset.toolSetLabel;
357                 delete toolset.toolSetConfig;
358 
359                 Monk.component.dataManager.getProjectData().toolsets.replace(data.toolsetId, toolset);
360 
361                 this.setToolsetLabel(toolset.label);
362             }
363         } else if (monkEvent.instanceOf(Monk.event.toolset.LoadToolset)) {
364             this.loadToolset();
365         }
366     },
367 
368 //    setProjectLabel : function(label) {
369 //        this.label = label;
370 //        feature.flowManager.setButtonLabel(this.id, label);
371 //    },
372 
373     /**
374      * Save/create a toolset
375      * @param {Object} config An object containing toolset properties
376      * @config {String} label The toolset label
377      * @config {String} worksetId A number representing the workset ID
378      * @config {String} toolsetId A number representing the toolset ID
379      * @config {Boolean} shareable Where to share this toolset with other users
380      * @config {String} description A description of the toolset
381      * If this is present, the toolset will be updated.  Otherwise, a new toolset will be created.
382      */
383     saveToolset : function(config) {
384         var toolsetConfig = {};
385 
386         if (config.toolsetId != null) {
387             toolsetConfig = this.projectToolsets.getToolset(config.toolsetId);
388             toolsetConfig.toolSetLabel = this.label;
389         } else {
390             toolsetConfig.worksetId = config.worksetId;
391             toolsetConfig.toolSetLabel = config.label;
392             toolsetConfig.shareable = config.shareable != null ? config.shareable : true;
393             toolsetConfig.pathToIcon = config.pathToIcon || 'resources/images/toolsets/Toolset-default.gif';
394             toolsetConfig.description = config.description;
395         }
396 
397         var tempTools = this.createToolsetConfig();
398         var tools = [];
399         tempTools.tools.each(function(item, index, length){
400             tools.push(item);
401         });
402         toolsetConfig.tools = tools;
403 
404         if (config.toolsetId != null) {
405             Monk.data.toolset.saveToolset(config.toolsetId, toolsetConfig);
406         } else {
407             Monk.data.toolset.createToolset(toolsetConfig);
408         }
409     },
410 
411     initializeSaveDialog : function() {
412         var projectId = Monk.component.dataManager.getProjectId();
413 
414 
415         var xmlReader = new Ext.data.XmlReader({
416                 record: 'workset',
417                 id: '@id'
418             }, [
419                 {name: 'id', mapping: '@id'},
420                 'label'
421             ]
422         );
423 
424         var worksetsStore = new Ext.data.Store({
425             reader: xmlReader,
426             autoLoad: true,
427             proxy: new Ext.data.HttpProxy({
428                 url: Monk.data.PROXY_URL + 'get/ProjectManager.getWorksets',
429                 method: 'GET'
430             }),
431             baseParams: {projectId: projectId}
432         });
433 
434         var worksetsCombo = new Ext.form.ComboBox({
435             fieldLabel: 'Workset',
436             name: 'workset',
437             width: 175,
438             store: worksetsStore,
439             triggerAction: 'all',
440             mode: 'local',
441             allowBlank: false,
442             editable: false,
443             forceSelection: true,
444             displayField: 'label',
445             hiddenName: 'worksetId',
446             valueField: 'id'
447         });
448 
449         var labelField = new Ext.form.TextField({
450             fieldLabel: 'Label',
451             name: 'label',
452             width: 175,
453             value: this.label,
454             allowBlank: false
455         });
456 
457         var sharedRadioGroup = new Ext.ux.RadioGroup({
458             fieldLabel: 'Privacy',
459             name: 'shareable',
460             horizontal: true,
461             width: 175,
462             radios: [{
463                     value: 1,
464                     boxLabel: 'Public',
465                     checked: true
466                 },{
467                     value: 0,
468                     boxLabel: 'Private'
469                 }
470             ]
471         });
472 
473         var imageUrl = new Ext.form.TextField({
474             fieldLabel: 'Icon (URL)',
475             name: 'pathToIcon',
476             width: 175,
477             disabled: true,
478             // regex for image match
479             regex: /(http:\/\/)([a-zA-Z0-9\-_\.]+(\.(com|edu|gov|net|org|biz|info|name|museum|co\.uk)))(\/(?!\/))(([a-zA-Z0-9\-_\/]*)?)([a-zA-Z0-9])+\.((jpg|jpeg|gif|png)(?!(\w|\W)))/,
480             regexText: "This doesn't look like a valid image URL. Make sure it starts with 'http://'."
481         });
482         // ANDREW: I commented the value -causes exception when creating
483         // new toolset
484         var description = new Ext.form.TextArea({
485             fieldLabel: 'Toolset Description',
486             name: 'description',
487             width: 175,
488             height: 80,
489             maxLength: 500
490           // value: this.initialToolset.description
491         });
492 
493         var saveForm = new Ext.form.FormPanel({
494             labelAlign: 'right',
495             labelWidth: 70,
496             border: false
497         });
498 
499         saveForm.add({
500             xtype: 'panel',
501             border: false,
502             html: '<div style="padding: 5px;">Select a workset to associate your toolset with.</div>'
503         });
504         saveForm.add(worksetsCombo);
505         saveForm.add(labelField);
506         saveForm.add(sharedRadioGroup);
507         saveForm.add(description);
508         //saveForm.add(imageUrl);
509 
510         this.saveDialog = new Ext.Window({
511             title: 'Save Toolset',
512             height: 250,
513             width: 300,
514             modal: true,
515             shadow: true,
516             plain: true,
517             border: false,
518             collapsible: false,
519             items: saveForm,
520             buttons: [
521                 {
522                     text: 'Save',
523                     handler: function(){
524                         if (saveForm.getForm().isValid()) {
525                             var values = saveForm.getForm().getValues();
526                             // convert to boolean
527                             values.shareable = values.shareable == '1' ? true : false;
528                             this.saveToolset(values);
529 
530                             saveForm.getForm().reset();
531                             this.saveDialog.hide();
532                         }
533                     },
534                     scope: this
535                 },
536                 {
537                     text: 'Cancel',
538                     handler: function(){
539                         this.saveDialog.hide();
540                     },
541                     scope: this
542                 }
543             ]
544         });
545 
546         this.saveDialog.show();
547     },
548 
549     /**
550      * Create a config for Monk.component.Toolset based on a toolset object
551      * @param {String} [toolsetId] If included, create a config for a saved toolset, otherwise, create a config for a new toolset
552      */
553     createToolsetConfig : function(toolsetId) {
554         var config = {};
555         if (toolsetId != null) {
556             config = Monk.component.dataManager.createToolsetConfig(toolsetId);
557         } else {
558             config.tools = new Ext.util.MixedCollection();
559             this.toolsteps.each(
560                 function(tool) {
561                     var tools = tool.tools;
562                     var toolNames = [];
563                     for (var toolId in tools) {
564                         // get rid of the appended underscore & number
565                         toolId = toolId.split('_')[0];
566                         // check for clones
567                         if (toolNames.indexOf(toolId) != -1) {
568                             toolId = toolId + '-clone';
569                         }
570                         toolNames.push(toolId);
571                     }
572                     var processedTool = {
573                         id: tool.id,
574                         label: tool.label,
575                         components: toolNames,
576                         helpUrl: tool.helpUrl
577                     };
578                     config.tools.add(tool.id, processedTool);
579                 },
580             this);
581             config.label = this.label;
582         }
583 
584         return config;
585     },
586 
587     setToolsetLabel : function(label) {
588         this.label = label;
589         Ext.getDom('toolsetLabel').innerHTML = label;
590         //feature.flowManager.setButtonLabel(this.id, label);
591     },
592 
593     /**
594      * Creates a new toolstep.
595      * @param {Ext.Element} siblingToolstep The sibling of this toolstep
596      * @param {Object} config A config object containing label and help url
597      * @param {String} config.label The label for this toolstep
598      * @param {String} config.helpUrl The help url for this toolstep
599      * @param {Boolean} invisible Whether the toolstep should be invisible initially
600      */
601     createToolstep : function(siblingToolstep, config, invisible) {
602         var thisConfig = config == null ? {label: 'Tool Label', helpUrl: null} : config;
603         this.modifiedToolset = true;
604 
605         // give the toolset a default name if it's new
606         if (this.label == '') {// && !Ext.getCmp('toolsetLabelField').isDirty()) {
607             this.setToolsetLabel('new toolset');
608         }
609 
610         var toolstep = null;
611         var index = null;
612         var toolLabel = thisConfig.label;
613         var helpUrl = thisConfig.helpUrl;
614 
615         var toolstepClass = 'toolstep-row';
616         if (invisible) toolstepClass = 'toolstep-row invisible';
617 
618         if (!siblingToolstep) {
619             // then it's the first toolstep
620             index = 0;
621             var parent = Ext.get('toolsetEditor');
622             toolstep = parent.createChild({cls:toolstepClass});
623         } else {
624             var siblingId = siblingToolstep.id;
625             index = this.toolsteps.indexOfKey(siblingId) + 1;
626             toolstep = siblingToolstep.next().insertSibling({cls:toolstepClass}, 'after');
627         }
628 
629         // remove the ext generated id and create a more useable one
630         toolstep.dom.removeAttribute('id');
631         toolstep.id = Ext.id(toolstep, 'toolstep-');
632         //Ext.DomHelper.insertHtml('afterBegin', toolstep.dom, '<span>'+toolstep.id+'</span>');
633 
634         var fieldParent = toolstep.createChild({style:'float: left; width: 100px;'});
635 
636         var labelField = new Ext.form.InlineTextArea({
637             saveOnlyOnChange: true,
638             value: toolLabel,
639             width: 95,
640             height: 'auto',
641             preventScrollbars: true,
642             style: "margin:5px;padding:0;float:left;",
643             grow: true,
644             growMin: 0,
645             growAppend: ''
646         });
647 
648         labelField.saveMethod = function(toolsetEditor) {
649             // assign the label to the appropriate toolstep entry
650             var label = this.getValue();
651             var toolstepId = this.getEl().findParent('div.toolstep-row', 3, true).id;
652             toolsetEditor.toolsteps.get(toolstepId).label = label;
653         }.createDelegate(labelField, [this]);
654 
655         labelField.render(fieldParent);
656         labelField.autoSize();
657 
658         var helpUrlField = new Ext.form.InlineTextField({
659             saveOnlyOnChange: true,
660             value: helpUrl,
661             emptyText: 'URL for step help',
662             regex: /^(http|https):\/\/[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}(([0-9]{1,5})?\/.*)?$/,
663             regexText: "This doesn't look like a valid URL. Make sure it starts with 'http://'.",
664             width: 95,
665             height: 'auto',
666             preventScrollbars: true,
667             style: "margin:5px;padding:0;float:left;clear:left;",
668             grow: false,
669             growMin: 0,
670             growAppend: ''
671         });
672 
673         helpUrlField.saveMethod = function(toolsetEditor) {
674             // assign the url to the appropriate toolstep entry
675             if (this.isValid()) {
676                 var url = this.getValue();
677                 var toolstepId = this.getEl().findParent('div.toolstep-row', 3, true).id;
678                 toolsetEditor.toolsteps.get(toolstepId).helpUrl = url;
679             }
680         }.createDelegate(helpUrlField, [this]);
681 
682         helpUrlField.render(fieldParent);
683         helpUrlField.autoSize();
684 
685         var closeButton = toolstep.createChild({cls: 'x-tool x-tool-close', title: 'Remove Toolstep'});
686         closeButton.on('click', function(){
687             this.deleteToolstep(toolstep);
688         }, this);
689 
690         var number = toolstep.createChild({id:toolstep.id+'-number', cls:'toolstep-number'});
691 
692         var gap = toolstep.insertSibling({cls:'toolstep-gap'}, 'after');
693         gap.dom.removeAttribute('id');
694         gap.id = Ext.id(gap, 'gap-');
695 
696         var columnDropzone = new Ext.dd.DropZone(toolstep.id, {ddGroup:'toolsDD'});
697         var gapDropzone = new Ext.dd.DropZone(gap.id, {ddGroup:'toolsDD'});
698 
699         this.toolsteps.insert(index, toolstep.id, {id: toolstep.id, tools:{}, label: toolLabel, helpUrl: helpUrl});
700 
701         this.updateToolstepNumbers();
702 
703         return toolstep;
704     },
705 
706     deleteToolstep : function(toolstep) {
707         this.modifiedToolset = true;
708 
709         try {
710             this.toolsteps.removeKey(toolstep.id);
711 
712             var toolstepEl = Ext.get(toolstep.id);
713             var gapEl = toolstepEl.next();
714             toolstepEl.remove();
715             gapEl.remove();
716 
717             this.updateToolstepNumbers();
718         } catch (e) {
719 
720         }
721     },
722 
723     /**
724      * Handle transfer of tools between toolsteps and tool parent, etc.
725      * @param {Object} target The receiving target Element
726      * @param {Ext.Element} toolEl The tool to transfer
727      */
728     handleToolTransfer : function(target, toolEl) {
729         // TODO: check if previous toolstep is empty
730         this.modifiedToolset = true;
731 
732         var flagForDeletion = false; // set if we need to delete the toolstep at the end of this method
733         var newToolEl = toolEl;
734 
735         var oldLocation = toolEl.parent();
736         var oldLocationId = oldLocation.id;
737         if (oldLocationId.split('-')[0] == 'toolstep') {
738             // delete tool from toolstep
739             delete this.toolsteps.get(oldLocationId).tools[toolEl.id];
740 
741             // check to see if any tools remain in this toolstep, if not, then delete it
742             var toolsCounter = 0;
743             for (var key in this.toolsteps.get(oldLocationId).tools) {
744                 toolsCounter++;
745                 break;
746             }
747             if (toolsCounter == 0) {
748                 // delete the toolstep after we've moved the tool
749                 flagForDeletion = true;
750             }
751         } else if (oldLocationId == 'toolsParent' && target.id != 'toolsParent') {
752             // copy the tool
753             newToolEl = toolEl.dom.cloneNode(true);
754             // generate a unique ID for the clone
755             var oldId = toolEl.id;
756             newToolEl.removeAttribute('id');
757             var cloneId = Ext.id(newToolEl, oldId+'_');
758             newToolEl.setAttribute('id', cloneId);
759         } else {
760             // we're moving the tool, don't make any changes
761         }
762 
763         if (target.id != 'toolsParent') {
764             target.setStyle({height: 'auto'});
765             target.appendChild(newToolEl);
766             this.toolsteps.get(target.id).tools[newToolEl.id] = true;
767 
768             // make it drag n droppable
769 			newToolEl = Ext.get(newToolEl);
770             newToolEl.dd = new Ext.ux.EditorDDProxy(cloneId, 'toolsDD', {isTarget: false});
771 			newToolEl.dd.constrain(newToolEl);
772             newToolEl.applyStyles({position:'', width:''});
773 
774             // add delete context menu
775             newToolEl.on('contextmenu', function(event, el){
776                 event.preventDefault();
777                 var menu = new Ext.menu.Menu([{
778                     text: 'Remove Tool',
779                     iconCls: 'delete-tool',
780                     handler : function(){
781                         var toolsParent = Ext.get('toolsParent');
782                         this.handleToolTransfer(toolsParent, newToolEl);
783                     },
784                     scope: this
785                 }]);
786                 menu.showAt(event.getPoint());
787             }, this);
788         } else if (oldLocationId != 'toolsParent') {
789             // if no toolstepIndex, then we're moving the tool back to the tool list
790             newToolEl.remove();
791         }
792 
793         if (flagForDeletion) {
794             this.deleteToolstep(oldLocation);
795         }
796     },
797 
798     /**
799      * Set the number for each step, based on their index
800      */
801     updateToolstepNumbers : function() {
802         this.toolsteps.each(function(item, index, length) {
803             var toolstep = Ext.get(item.id);
804             Ext.get(toolstep.id+'-number').update(index + 1);
805         }, this);
806     },
807 
808     // called after tools have been rendered
809     initializeDragDrop : function() {
810         var editorDropzone = new Ext.dd.DropZone('toolsetEditor', {ddGroup:'toolsDD'});
811         var toolDropzone = new Ext.dd.DropZone('toolsParent', {ddGroup:'toolsDD'});
812 
813         // custom drag n drop proxy
814         Ext.ux.EditorDDProxy = Ext.extend(Ext.dd.DDProxy, {
815             parent : this,
816 
817             // custom methods
818             showPreview: function(target) {
819                 if (!this.previewEl) {
820                     this.previewEl = target.createChild({cls:'dd-preview'});
821                 }
822                 if (target.dom.className == 'toolstep-gap') {
823                     // increase the width of the gap (fix for opera)
824                     target.setStyle({width:'50%'});
825                 }
826                 target.setStyle({'background-color':'#eeeeee'});
827             },
828 
829             hidePreview: function() {
830                 if (this.previewEl) {
831                     this.previewEl.parent().setStyle({'background-color':'#fff'});
832                     this.previewEl.remove();
833                     delete this.previewEl;
834                 }
835             },
836 
837 			// constrain dragging to the barriers of the screen
838 			constrain: function(element) {
839 				var windowWidth = Ext.lib.Dom.getViewWidth();
840                 var windowHeight = Ext.lib.Dom.getViewHeight();
841                 var leftConstraint = element.getX();
842                 var rightConstraint = windowWidth  - leftConstraint - element.getWidth();
843                 var topConstraint = element.getY();
844                 var bottomConstraint = windowHeight - topConstraint - element.getHeight();
845                 this.setXConstraint(leftConstraint, rightConstraint);
846                 this.setYConstraint(topConstraint, bottomConstraint);
847 			},
848 
849             // overrides of DDProxy methods
850             startDrag: function(x, y) {
851                 var dragEl = Ext.get(this.getDragEl());
852                 var el = Ext.get(this.getEl());
853 
854                 dragEl.applyStyles({border:'','z-index':2000});
855                 dragEl.update(el.dom.innerHTML);
856                 dragEl.addClass(el.dom.className + ' dd-proxy');
857             },
858 
859             onDragOver: function(e, targetId) {
860                 if (this.id != targetId) {
861                     if ('toolsetEditor' === targetId){
862                         var index = this.parent.toolsteps.getCount() - 1;
863                         if (index == -1) {
864                             // no toolsteps yet, use toolsetEditor as target
865                             var target = Ext.get(targetId);
866                             this.lastTarget = target;
867                             this.showPreview(target);
868                         } else {
869                             // otherwise use the last toolstep gap as target
870                             targetId = this.parent.toolsteps.get(index).id
871                             var target = Ext.get(targetId).next();
872                             target.setStyle({height:'auto'});
873                             this.lastTarget = target;
874                             this.hidePreview();
875                             this.showPreview(target);
876                         }
877                     } else if (targetId.split('-')[0] == 'gap') {
878                         var target = Ext.get(targetId);
879                         if (target != this.lastTarget) {
880                             target.setStyle({height:'auto'});
881                             this.lastTarget = target;
882                             this.hidePreview();
883                             this.showPreview(target);
884                         }
885                     } else {
886                         // we're in a toolstep or the tool list (toolParent)
887                         var target = Ext.get(targetId);
888                         // test if we're in the tool's current toolstep
889                         if (target.contains(this.getEl())) {
890                             this.lastTarget = target;
891                             this.hidePreview();
892                         } else {
893                             if (target != this.lastTarget) {
894                                 this.lastTarget = target;
895                                 this.hidePreview();
896                                 this.showPreview(target);
897                             }
898                         }
899                     }
900                 }
901             },
902             onDragOut: function(e, targetId) {
903                 if (this.lastTarget) {
904                     if (this.lastTarget.id.split('-')[0] == 'gap') {
905                         this.lastTarget.setStyle({width:'50%'});
906                         this.lastTarget.setHeight(20);
907                     }
908                 }
909                 if('toolsetEditor' === targetId || 'toolsParent' === targetId) {
910                     var target = Ext.get(targetId);
911                     this.lastTarget = null;
912                     this.hidePreview();
913                 } else {
914                     var target = Ext.get(targetId);
915                     this.lastTarget = null;
916                     this.hidePreview();
917                 }
918             },
919 
920             endDrag: function() {
921                 this.hidePreview();
922 
923                 var dragEl = Ext.get(this.getDragEl());
924                 var el = Ext.get(this.getEl());
925 
926                 if (this.lastTarget) {
927                     // tool dropped on the editor
928                     if (this.lastTarget.id == 'toolsetEditor'){
929                         var index = this.parent.toolsteps.getCount() - 1;
930                         var newToolstep = this.parent.createToolstep();
931                         this.parent.handleToolTransfer(newToolstep, el);
932 
933                     // tool dropped on a gap in between toolsteps
934                     } else if (this.lastTarget.id.split('-')[0] == 'gap') {
935                         var gap = Ext.get(this.lastTarget);
936                         var target = gap.prev(); // get toolstep before this gap
937                         var newToolstep = this.parent.createToolstep(target);
938                         var newGap = newToolstep.prev();
939                         newGap.setHeight(20);
940                         this.parent.handleToolTransfer(newToolstep, el);
941 
942                     // tool dropped on the tool list
943                     } else if (this.lastTarget.id == 'toolsParent') {
944                         var toolsParent = Ext.get(this.lastTarget);
945                         this.parent.handleToolTransfer(toolsParent, el);
946 
947                     // tool dropped in an existing toolstep
948                     } else {
949                         var toolstep = Ext.get(this.lastTarget);
950                         // test if the toolstep contained the tool initially
951                         if (!toolstep.contains(el.dom)) {
952                             this.parent.handleToolTransfer(toolstep, el);
953                         }
954                     }
955                 } else {
956                     // no lastTarget
957                 }
958             }
959         });
960 
961         this.tools.each(
962             function(tool) {
963                 if (!tool.clone) {
964                     var toolDD = Ext.get(tool.id);
965                     toolDD.dd = new Ext.ux.EditorDDProxy(tool.id, 'toolsDD', {isTarget: false});
966     				toolDD.dd.constrain(toolDD);
967                 }
968             }, this);
969 
970     },
971 
972     /**
973      * Load a toolset.
974      * @param {Boolean} savedToolset Is the toolset one of the previously saved ones?  Or is it a new (unsaved) toolset.
975      */
976     loadToolset : function() {
977         var config = null;
978         if (this.projectToolsets.selectedToolset != null && !this.modifiedToolset) {
979             config = this.createToolsetConfig(this.projectToolsets.selectedToolset);
980             Ext.get('toolset-'+this.projectToolsets.selectedToolset).removeClass('selected');
981         } else {
982             // see if there's anything in the toolset editor
983             config = this.createToolsetConfig();
984         }
985         if (config.tools.getCount() == 0) {
986             Monk.component.messenger.alert('Monk Workbench', 'You must select or create a toolset before you can continue.');
987         } else {
988             var toolsetObject = new Monk.component.Toolset(config);
989             Monk.component.dataManager.setToolsetId(toolsetObject.id);
990             feature.toolFlowManager.setToolset(toolsetObject);
991             feature.flowManager.addStep(feature.toolFlowManager, false);
992             feature.flowManager.goToStep(feature.toolFlowManager.id);
993         }
994     },
995 
996     /**
997      * Resets everything back to the factory state.
998      */
999     reset : function() {
1000         this.setToolsetLabel('new toolset');
1001 
1002         this.initialToolset = null;
1003 
1004         this.toolsteps.each(function(toolstep) {
1005             this.deleteToolstep(toolstep);
1006         }, this);
1007 
1008         this.toolsteps.clear();
1009 
1010         this.modifiedToolset = false;
1011 
1012         //Ext.getCmp('toolsetLabelField').destroy();
1013 
1014         //this.afterRenderFirstTimeCalled = false;
1015     }
1016 
1017 });