]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - modules/ModuleBuilder/javascript/studio2.js
Release 6.5.0
[Github/sugarcrm.git] / modules / ModuleBuilder / javascript / studio2.js
1 /*********************************************************************************
2  * SugarCRM Community Edition is a customer relationship management program developed by
3  * SugarCRM, Inc. Copyright (C) 2004-2012 SugarCRM Inc.
4  * 
5  * This program is free software; you can redistribute it and/or modify it under
6  * the terms of the GNU Affero General Public License version 3 as published by the
7  * Free Software Foundation with the addition of the following permission added
8  * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
9  * IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
10  * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
11  * 
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more
15  * details.
16  * 
17  * You should have received a copy of the GNU Affero General Public License along with
18  * this program; if not, see http://www.gnu.org/licenses or write to the Free
19  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20  * 02110-1301 USA.
21  * 
22  * You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
23  * SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
24  * 
25  * The interactive user interfaces in modified source and object code versions
26  * of this program must display Appropriate Legal Notices, as required under
27  * Section 5 of the GNU Affero General Public License version 3.
28  * 
29  * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
30  * these Appropriate Legal Notices must retain the display of the "Powered by
31  * SugarCRM" logo. If the display of the logo is not reasonably feasible for
32  * technical reasons, the Appropriate Legal Notices must display the words
33  * "Powered by SugarCRM".
34  ********************************************************************************/
35
36
37 if(typeof('console') == 'undefined'){
38         console = {
39                 log: function(message) {
40         
41                 }
42         }
43 }
44 var _debug_ = false;
45 var _write_ = function( message ){ if(_debug_){ console.log(message);} }
46
47
48
49 Studio2 = {
50
51         init: function() {
52                 var Dom = YAHOO.util.Dom,
53                         DDTarget = YAHOO.util.DDTarget,
54                         DDM = YAHOO.utilDragDropMgr;
55                 
56                 Studio2.maxColumns = parseInt(document.getElementById('maxColumns').value);
57                 Studio2.setStartId(parseInt(document.getElementById('idCount').value));
58                 Studio2.setStartId(1000);
59                 Studio2.fieldwidth = parseInt(document.getElementById('fieldwidth').value);
60                 Studio2.panelNumber = parseInt(document.getElementById('nextPanelId').value);
61                 Studio2.isIE = SUGAR.isIE;
62                 Studio2.expandableFields = [];
63         Studio2.scrollZones = {};
64
65                 //      Activate the layout panels
66                 var panels = document.getElementById('panels');
67                 Dom.setStyle(panels, "visibility", "hidden");
68                 //new Studio2.PanelDD(panels.id,'studio2.panels'); // add the main layout area into PANELS to allow for panel drags to empty areas
69                 new DDTarget(panels.id, 'studio2.panels');
70                 for (var i=0;i<panels.childNodes.length;i++) {
71                         var panel = panels.childNodes[i];
72                         if (panel.nodeName == 'DIV') { // a valid panel
73
74                                 // add the panel into the ROWS drag-and-drop group to allow for drags to empty areas of the panels
75                                 new DDTarget(panel.id,'studio2.rows');
76
77                                 for (var j=0;j<panel.childNodes.length;j++) {
78                                         var row = panel.childNodes[j];
79                                         if(typeof(row.id)!='undefined' && row.id!=null) 
80
81                                         if (row.nodeName == 'DIV' && row.className == 'le_row' ) { // a valid row
82
83                                                 // Convert all (empty) fields into variable width fields
84                                                 var leftSibling = null;
85                                                 var foundEmpty = 0;
86
87                                                 for (var k=0;k<row.childNodes.length;k++) {
88                                                         var field = row.childNodes[k];
89                                                         var ranOnce = false;
90                                                         if (field.nodeName == 'DIV') { // field
91
92                                                                 for (var l=0;l<field.childNodes.length;l++) {
93                                                                         var property = field.childNodes[l];
94
95                                                                         if (property.className && (property.className.indexOf('field_name') != -1) && property.firstChild) {
96                                                                                 if (property.firstChild.nodeValue.indexOf('(empty)') != -1) {
97                                                                                         //register field to be expandable
98                                                                                         _write_("(empty) found");
99                                                                                         Studio2.setSpecial(field);
100                                                                                         Studio2.registerExpandableField( Studio2.prevField(field) || Studio2.nextField( field ) );
101                                                                                         break;
102                                                                                 }else if (property.firstChild.nodeValue.indexOf('(filler)') != -1){
103                                                                                         var sibling = Studio2.prevField( field ) || Studio2.nextField( field );
104                                                                                         Studio2.setSpecial( field );
105                                                                                         var swapFields = Studio2.nextField( field ) != null;
106                                                                                         Studio2.registerExpandableField( sibling);
107                                                                                         this.toggleFieldWidth (sibling );
108                                                                                         ranOnce = true;
109                                                                                         if( swapFields ){
110                                                                                                 //swap (filler) with the sibling
111                                                                                                 field = Studio2.nextField (sibling ) || Studio2.prevField( sibling );
112
113                                                                                                 this.swapElements( sibling , field)
114
115                                                                                         }
116                                                                                         break;
117                                                                                 }
118
119                                                                         }
120                                                                 }
121                                                         }
122                                                         if( ranOnce ){break;}
123                                                 }
124
125                                                 Studio2.activateElement(row);
126                                         }
127                                 }
128                         }
129                 }
130                 Studio2.activateElement(panels);
131         Dom.setStyle(panels, "visibility", '');
132
133                 // Activate the fields in the availablefields list
134                 var availablefields = document.getElementById('availablefields');
135                 Studio2.activateElement(availablefields);
136
137                 // Setup Delete area in the toolbox (the Trash icon)
138                 var d = document.getElementById('delete');
139                 Studio2.setSpecial(d);
140                 var delDD = new DDTarget('delete', 'studio2.panels');
141                 delDD.addToGroup('studio2.rows');
142                 delDD.addToGroup('studio2.fields');
143                 
144                 var fillerProxy = Studio2.newField();
145                 Studio2.setSpecial(fillerProxy);
146                 var hanger = document.getElementById('fillerproxy');
147                 hanger.parentNode.insertBefore(fillerProxy,hanger.nextSibling);
148                 Studio2.activateElement(fillerProxy);
149
150                 rowProxy = Studio2.newRow(true);
151                 Studio2.setSpecial(rowProxy);
152                 var hanger = document.getElementById('rowproxy');
153                 hanger.parentNode.insertBefore(rowProxy,hanger.nextSibling);
154                 Studio2.activateElement(rowProxy);
155
156                 var hanger = document.getElementById('panelproxy');
157                 if (hanger != null) // if no panelproxy then don't display a new panel option: needed for portal layouts which have only one panel
158                 {
159                         panelProxy = Studio2.newPanel();
160                         Studio2.setSpecial(panelProxy);
161                         hanger.parentNode.insertBefore(panelProxy,hanger.nextSibling);
162                         Studio2.activateElement(panelProxy);
163                 }
164                 
165                 Studio2.resizeDivs();
166
167                 ModuleBuilder.helpRegisterByID('layoutEditor','div');
168                 ModuleBuilder.helpRegisterByID('layoutEditorButtons','input');
169                 ModuleBuilder.helpSetup('layoutEditor','default');
170
171         },
172         
173         resizeDivs : function () {
174                 var Dom = YAHOO.util.Dom;
175                 if (!Dom.get('panels'))
176             return;
177         var body = document.getElementById('mbtabs');
178         var targetHeight =  body.clientHeight - (Dom.getY('panels') - Dom.getY(body)) - 32;
179                 if (Studio2.isIE) targetHeight -= 10;
180                 Dom.setStyle('panels', "height", targetHeight + "px");
181                 Dom.setStyle('panels', "width" , ((Studio2.fieldwidth * 2) + 112) + "px");
182                 Dom.setStyle('toolbox', "height", targetHeight + "px");
183         Studio2.scrollZones = {
184             panels: Studio2.getScrollZones('panels'),
185             toolbox: Studio2.getScrollZones('toolbox')
186         }
187         },
188
189         /**
190         * SIGNATURE
191         *       array = Studio2.expandableFields
192         *       element = id of the element to unregister.
193         * RETURN
194         *       element is removed from Studio2.expandableFields if found.
195         */
196
197         unregisterExpandableField:function( field ){
198                 //_write_("received for unregister: "+field.id);
199                 if(field==null || typeof(field) == 'undefined'){ return; }
200                 if ( this.isExpandable(field) ) {
201                         if (this.getColumnWidth( field ) > 1) { this.reduceFieldWidth( field ); }
202                          _write_("Unregistered:"+field.id);
203                          field.removeChild( field.childNodes[1] );
204                          field.removeAttribute("expandable");
205                          field.removeAttribute("state");
206                 }
207         },
208         isExpandable:function( field ){
209                 return field.getAttribute("expandable")!=null && !this.isSpecial(field);//&& field.getAttribute("expandable") == "true";
210         },
211         swapStates:function( src, dest ){
212                 var old_src= {state:src.getAttribute("state"), img: src.childNodes[1].src};
213                 src.setAttribute("state", dest.getAttribute("state"));
214                 src.childNodes[1].src = dest.childNodes[1].src;
215                 dest.childNodes[1].src = old_src.img;
216                 dest.setAttribute("state", old_src.state);
217         },
218
219         getImageElement:function(default_toggle){
220                 var img = document.createElement('img');
221                 if(!default_toggle)
222                         img.src = 'index.php?entryPoint=getImage&themeName='+SUGAR.themes.theme_name+'&imageName=minus_inline.gif';
223                 else
224                         img.src = 'index.php?entryPoint=getImage&themeName='+SUGAR.themes.theme_name+'&imageName=plus_inline.gif';
225                 img.className = 'le_edit';
226                 img.style.paddingRight = 2;
227                 img.style.cssFloat = 'left';
228                 img.name = 'expandable_field_icon';
229                 return img;
230         },
231
232
233         toggleFieldWidth:function(id){
234
235                 var field = YAHOO.util.Dom.get(id);
236                 if ( typeof(field) == 'undefined' || field === null ) return; 
237                 var img = field.childNodes[1];
238
239                 if( field.getAttribute("state") && field.getAttribute("state")=='reduced' ){
240                         field.parentNode.removeChild( Studio2.nextField(field) || Studio2.prevField(field) );
241                         Studio2.setColumnWidth(id,2);
242                         img.src = 'index.php?entryPoint=getImage&themeName='+SUGAR.themes.theme_name+'&imageName=minus_inline.gif';
243                         this.setExpanded( field );
244
245
246                 }else if( field.getAttribute("state") && field.getAttribute("state")=='expanded' ){
247                         Studio2.setColumnWidth(id,1);
248                         var newField = Studio2.newField();
249                         Studio2.setSpecial(newField);
250                         Studio2.activateElement(newField);
251                         field.parentNode.appendChild(newField);
252                         this.setReduced( field );
253                         img.src='index.php?entryPoint=getImage&themeName='+SUGAR.themes.theme_name+'&imageName=plus_inline.gif';
254
255                 }
256         },
257         setExpanded: function( field ){
258                 field.setAttribute("expandable","true");
259                 field.setAttribute("state","expanded");
260                 _write_("Expanded: "+field.id);
261         },
262         setReduced: function( field ){
263                 field.setAttribute("expandable","true");
264                 field.setAttribute("state","reduced");
265                 _write_("Recued: "+field.id);
266         },
267         isExpanded: function (field ){
268                 return field.getAttribute('state') == 'expanded';
269         },
270         isReduced:function (field ){
271                 return field.getAttribute('state') == 'reduced';
272         },
273         registerExpandableField: function( field ) {//field = HTML element
274                 if( Studio2.maxColumns < 2 || field == null || typeof(field) == 'undefined' || this.isSpecial (field) ) { return; }
275                 if( !this.isExpandable( field ) )  {
276                         var next = this.nextField ( field ) ;
277                         var prev = this.prevField ( field ) ;
278                         var removeMe = next || prev ;
279                         if( this.isSpecial( next) || this.isSpecial( prev ) || this.isEmpty( next ) || this.isEmpty( prev ) || removeMe == null ){ //Always Expanded
280                                 _write_("remove me is :"+removeMe);
281                                 if (removeMe != null) { field.parentNode.removeChild(removeMe); }
282                                 var img = this.getImageElement ( false );
283                                 img.onclick = function () { Studio2.toggleFieldWidth ( field.id ) };
284                                 field.insertBefore ( img, field.childNodes[1] );
285                                 this.setColumnWidth( field.id, 2 );
286                                 this.setExpanded( field );
287                                 _write_("registered field");
288                         }
289                 }else{ _write_("Could not Register field:"+field.id); }
290         },
291         setStartId: function(id) {
292                 Studio2.idStack = [id];
293         },
294
295         nextId: function() {
296                 if (Studio2.idStack.length == 1) { // if down to our last id, allocate another
297                         Studio2.idStack[0]++;
298                         Studio2.idStack.push(Studio2.idStack[0]);
299                 }
300                 return Studio2.idStack.pop();
301         },
302
303         setNextId: function(id) {
304                 Studio2.idStack.push(id);
305         },
306
307         isSpecial: function(element) {
308                 if(element==null || typeof(element) == 'undefined'){return false;};
309                 return YAHOO.util.Dom.hasClass(element,'special');
310         },
311
312         setSpecial: function(el) {
313                 YAHOO.util.Dom.addClass(el, 'special');
314         },
315
316         unsetSpecial: function(el) {
317                 YAHOO.util.Dom.removeClass(el, 'special');
318         },
319         isEmpty: function( element ){
320                 if (element == null || typeof(element) == 'undefined') {return false;};
321                 return YAHOO.util.Dom.hasClass(element, 'le_field special');
322         },
323         count: function(element) {
324                 var count = 0;
325                 for (var j=0;j<element.childNodes.length;j++) {
326                         var child = element.childNodes[j];
327                         if (child.nodeName == 'DIV') { // a valid child
328                                 count++;
329                         }
330                 }
331                 return count;
332         },
333
334         newField: function(){ // TODO: use properties to set field contents
335                 //This object must exists on the page
336                 var newField = document.createElement('div');
337                 newField.className ='le_field';
338                 newField.id = Studio2.nextId();
339                 newField.innerHTML = '<span>'+SUGAR.language.get('ModuleBuilder', 'LBL_FILLER')+'</span>' + // the displayed label
340                                                          '<span class=\'field_name\'>(filler)</span>'; // the hidden field that identifies this as something to be saved by prepareForSave()
341                 return newField;
342         },
343
344         newRow: function(titleRequired) {
345                 var newRow = document.createElement('div');
346                 if (titleRequired) {
347                         var child = document.createElement('span');
348                 child.className = 'panel_name';
349                 child.appendChild(document.createTextNode(SUGAR.language.get('ModuleBuilder', 'LBL_NEW_ROW') ) );
350                 newRow.appendChild(child);
351                 }
352                 newRow.className='le_row';
353                 newRow.id = Studio2.nextId();
354                 for(var i=0;i<Studio2.maxColumns;i++) {
355                         var newField = Studio2.newField();
356                         Studio2.setSpecial(newField);
357                         newRow.appendChild(newField);
358                 }
359                 return newRow;
360         },
361
362         newPanel: function() {
363                 var newPanel = document.createElement('div');
364                 newPanel.className='le_panel';
365                 newPanel.id = Studio2.nextId();
366                 // get the panelid for this panel - must be unique in the layout, even across saves
367                 // our dynamically assigned DOM ids won't work as any panel given one of these DOM ids could find itself in conflict with a panel created in a later session
368                 //var panelIdentifier = 'lbl_panel'+(Studio2.panelNumber ++) ;
369                 var panelNumber = (Studio2.panelNumber ++) ;
370                 var view = document.getElementById('prepareForSave').view.value;
371                 var panelLabel = 'lbl_' + view +  '_panel' + panelNumber;
372
373                 var div = document.createElement('div');
374                 div.id = 'le_panellabel_' + newPanel.id;
375
376         var child = document.createElement('span');
377         child.id = 'le_panelname_' + newPanel.id;
378         child.className = 'panel_name';
379         child.appendChild(document.createTextNode(SUGAR.language.get('ModuleBuilder', 'LBL_NEW_PANEL') ) );
380         div.appendChild(child);
381         var child = document.createElement('span');
382         child.id = 'le_panelid_' + newPanel.id;
383         child.className = 'panel_id';
384         child.appendChild(document.createTextNode(panelLabel));
385                 div.appendChild(child);
386                 newPanel.appendChild(div);
387
388                 var img = document.createElement('img');
389                 img.src='index.php?entryPoint=getImage&themeName='+SUGAR.themes.theme_name+'&imageName=edit_inline.gif';
390                 img.className = 'le_edit';
391                 img.style.cursor="pointer;";
392                 var editModule = document.getElementById('prepareForSave').view_module.value;
393                 var editString = 'module=ModuleBuilder&action=editProperty&view_module='+editModule+'&view='+view+'&id_label=le_panelname_'+newPanel.id+'&name_label=label_'+panelLabel+'&title_label='+SUGAR.language.get('ModuleBuilder', 'LBL_LABEL_TITLE') ;
394                 if (document.getElementById('prepareForSave').view_package)
395                       editString += '&view_package='+document.getElementById('prepareForSave').view_package.value ;
396                 var view = document.prepareForSave.view.value;
397                 img.onclick = function() { var value_label = document.getElementById('le_panelname_'+newPanel.id).innerHTML;ModuleBuilder.asyncRequest( editString + '&value_label=' + value_label, ModuleBuilder.updateContent ); }
398                 newPanel.appendChild(img);
399                 return newPanel;
400         },
401
402         establishLocation: function(element) {
403                 var location = null;
404                 while(element.parentNode != 'body') {
405                         location = element.id;
406                         if ((location == 'panels') || (location == 'toolbox') || (location == 'delete')){
407                                 break;
408                         }
409                         element = element.parentNode;
410                 }
411                 if (location == null) {
412                         alert("Studio2:establishLocation: badly formed document");
413                         die();
414                 }
415                 return location;
416         },
417
418         reclaimIds: function(element) {
419                 // return the ids in this element to the available pool
420                 // do not reclaim field IDs as they never really disappear - they just move between toolbox and panel
421                 if (element.className.indexOf('le_field') == -1) {
422                         Studio2.setNextId(element.id);
423                         for (var i=0;i<element.childNodes.length;i++) {
424                                 var child = element.childNodes[i];
425                                 if (child.nodeName == 'DIV') { // a subelement
426                                         Studio2.reclaimIds(child);
427                                 }
428                         }
429                 }
430         },
431
432         reclaimFields: function(element) {
433                 if (element.className.indexOf('le_field') != -1) {
434                         if (! Studio2.isSpecial(element)) {
435                                 var destination = document.getElementById('availablefields');
436 //                              destination.appendChild(element.parentNode.removeChild(element));
437                                 destination.appendChild(element);
438                                 Studio2.resetFieldWidth(element);
439                         } else {
440                                 element.parentNode.removeChild(element);
441                         }
442                 } else {
443                         for (var i=0;i<element.childNodes.length;i++) {
444                                 var child = element.childNodes[i];
445                                 if (child.nodeName == 'DIV') { // a subelement
446                                         Studio2.reclaimFields(child);
447                                 }
448                         }
449                 }
450         },
451
452         highlightElement: function(field) {
453                 YAHOO.util.Dom.setStyle(field,'visibility','hidden');
454         },
455
456         /* FIELD WIDTH FUNCTIONS */
457
458         getSpacing: function(field) {
459                 var Field = new YAHOO.util.Element(field);
460                 var leftMargin = parseInt(Field.getStyle('margin-left'));
461                 var rightMargin = parseInt(Field.getStyle('margin-right'));
462                 var leftPadding = parseInt(Field.getStyle('padding-left'));
463                 var rightPadding = parseInt(Field.getStyle('padding-right'));
464                 if (Studio2.isIE) {
465                         return (leftMargin + rightMargin);
466                 } else {
467                         return (leftMargin + rightMargin + leftPadding + rightPadding + 2);
468                 }
469         },
470
471         resetFieldWidth: function(field) {
472                 YAHOO.util.Dom.setStyle(field,'width',Studio2.fieldwidth + 'px');
473                 //Dom.setStyle(field,'width',Studio2.fieldwidth + 'px' );
474         },
475
476         /* a hack function, purely because Firefox has a problem with field widths during the init function */
477         /* so rather than relying on the style values we just set the width directly to the final value */
478         adjustWidth: function(field,columns) {
479                 var newWidth = columns * (Studio2.fieldwidth + Studio2.getSpacing(field));
480                 YAHOO.util.Dom.setStyle(field,'width',newWidth + 'px' );
481         },
482
483         increaseFieldWidth: function(field) {
484                 var newWidth;
485 //              var currentWidth = parseInt(field.clientWidth);
486                 var currentWidth = Studio2.getFieldWidth(field);
487                 newWidth = currentWidth + Studio2.fieldwidth + Studio2.getSpacing(field);
488 //              field.style.width = newWidth+'px';
489                 YAHOO.util.Dom.setStyle(field,'width',newWidth + 'px' );
490         },
491
492         reduceFieldWidth: function(field) {
493                 var newWidth;
494                 var currentWidth = Studio2.getFieldWidth(field);
495                 newWidth = currentWidth - Studio2.fieldwidth - Studio2.getSpacing(field);
496                 YAHOO.util.Dom.setStyle(field,'width',newWidth + 'px' );
497         },
498
499         getFieldWidth: function(field) {
500                 var width = parseInt(YAHOO.util.Dom.getStyle(field, 'width')); // computed style value of the field width (or currentStyle in IE - same result)
501                 if (isNaN(width)) {
502                         width = Studio2.fieldwidth; // if field width is set to something like 'auto' we need to take a better guess
503                 }
504                 return width;
505         },
506
507         setFieldWidth: function(field,width) {
508                 YAHOO.util.Dom.setStyle(field,'width',width);
509         },
510
511         getColumnWidth: function(field) {
512                 return Math.floor(Studio2.getFieldWidth(field)/Studio2.fieldwidth);
513         },
514
515         setColumnWidth: function(field,columns) {
516                 var spacing = Studio2.getSpacing(field);
517                 var newWidth = columns * (Studio2.fieldwidth + spacing) - spacing;
518                 YAHOO.util.Dom.setStyle(field,'width',newWidth + 'px' );
519         },
520
521         firstField: function(row) {
522                 var firstfield = row.firstChild;
523                 while (firstfield.nodeName != 'DIV') {
524                         firstfield = firstfield.nextSibling;
525                 }
526                 return firstfield;
527         },
528
529         getColumn: function(field) {
530                 var firstfield = Studio2.firstField(field.parentNode);
531                 return Math.ceil((YAHOO.util.Dom.getX(field) - YAHOO.util.Dom.getX(firstfield) / Studio2.fieldwidth));  
532         },
533
534         getRow: function(field) {
535                 // find our parent row
536                 // find how many previous siblings we have that are also rows
537                 // our row is that + 1
538                 var row = field.parentNode;
539                 var count = 1;
540                 while ((row = row.previousSibling) !== null) {
541                         if (row.nodeName == 'DIV') {
542                                 count++;
543                         }
544                 }
545                 return count;
546         },
547
548         prevField: function(field){
549                 var prev = field.previousSibling;
550                 while( (null !== prev) && (prev.nodeName != 'DIV')){
551                         prev = prev.previousSibling;
552                 }
553                 return prev;
554         },
555         nextField: function(field) {
556                 var next = field.nextSibling;
557                 while (typeof(next)!='undefined' && (next !== null) && (next.nodeName != 'DIV')) {
558                         next = next.nextSibling;
559                 }
560                 return next;
561         },
562
563
564         /* ELEMENT FUNCTIONS */
565
566         // TODO: rewrite tidyPanels, tidyRows and tidyFields as a recursive tidy
567         tidyPanels: function() {
568                 var panels = document.getElementById('panels');
569                 if (Studio2.count(panels) <= 0) {
570                         var newPanel = Studio2.newPanel();
571                         newPanel.appendChild(Studio2.newRow(false));
572                         panels.appendChild(newPanel);
573                         Studio2.activateElement(newPanel);
574                 }
575         },
576
577         tidyRows: function(panel) {
578                 if (Studio2.count(panel) <= 0) { // no rows left
579                         if (Studio2.count(panel.parentNode)>1) {
580                                 Studio2.removeElement(panel);
581                                 Studio2.tidyPanels();
582                         } else {
583                                 // add a blank row back in
584                                 var newRow = Studio2.newRow(false);
585                                 panel.appendChild(newRow);
586                                 Studio2.activateElement(newRow);
587 //                              debugger;
588                         }
589                 }
590         },
591
592         tidyFields: function(row) {
593                 if (Studio2.count(row) <= 0) { // no fields left
594                         var panel = row.parentNode;
595                         Studio2.removeElement(row);
596                         Studio2.tidyRows(panel);
597                 }
598         },
599
600         removeElement: function(element) {
601                 Studio2.reclaimIds(element);
602                 Studio2.reclaimFields(element);
603                 if (element.className.indexOf('le_field') == -1) {
604                         // all fields have been moved to availablefields in Studio2.reclaimFields
605                         element.parentNode.removeChild(element);
606                 }
607         },
608
609         swapElements: function(el1,el2) {
610                 // TODO: record this swap in TRANSACTION
611                 var el1Width = Studio2.getFieldWidth(el1);
612                 var el2Width = Studio2.getFieldWidth(el2);
613                 YAHOO.util.DragDropMgr.swapNode(el1, el2);
614                 Studio2.setFieldWidth(el1,el2Width);
615                 Studio2.setFieldWidth(el2,el1Width);
616         },
617
618         activateElement: function(element) {
619                 if (!document.getElementById(element.id)) {
620                         document.body.appendChild(element);
621                 }
622                 if (element.className.indexOf('le_panel') != -1) {
623                         new Studio2.PanelDD(element.id,'studio2.panels');
624                         new YAHOO.util.DDTarget(element.id,'studio2.rows'); // add so a background for row moves
625                 }
626                 if (element.className.indexOf('le_row') != -1) {
627                         new Studio2.RowDD(element.id,'studio2.rows');
628                 }
629                 if (element.className.indexOf('le_field') != -1) {
630                         new Studio2.FieldDD(element,'studio2.fields');
631                 }
632                 for (var i=0;i<element.childNodes.length;i++) {
633                         var child = element.childNodes[i];
634                         if (child.nodeName == 'DIV') { // a valid child
635                                 Studio2.activateElement(child);
636                         }
637                 }
638
639         },
640
641         /**
642          * A substitute for cloneNode that is Yahoo Drag-and-Drop compatible.
643          * Using document.cloneNode causes Yahoo DnD to fail as the ID is also cloned, leading to duplicate IDs in the document
644          * This substitute doesn't copy the ID
645          */
646
647         copyElement: function(element) {
648                 var copy = document.createElement(element.tagName);
649                 if (element.attributes.length > 0) {
650                         var attrs = element.attributes;
651                         for(var i=0;i<attrs.length;i++) {
652                                 if (attrs[i].name != 'id') {
653                                         // check to see if this attribute is actually set in the document, or just a default - IE's attributes array contains both, and we only want the specified attributes
654                                         var a = element.getAttributeNode(attrs[i].name);
655                                         if (a && a.specified) {
656                                                 if (attrs[i].name == 'class') { // Needed for IE
657                                                         copy.className = attrs[i].value;
658                                                 }
659                                                 copy.setAttribute(attrs[i].name,attrs[i].value);
660                                         }
661                                 }
662                         }
663                 }
664
665                 Studio2.copyChildren(element, copy);
666                 copy.id = Studio2.nextId();
667                 return copy;
668         },
669         
670         copyChildren : function(original, clone)
671         {
672                 var children = original.childNodes
673                 for (var i in children)
674                 {
675                         if (children[i])
676                         {
677                                 if (children[i].tagName && children[i].tagName.substr(0,1) != "#"){
678                                         clone.appendChild(Studio2.copyElement(children[i]));
679                                 }
680                                 else if (children[i].nodeName && children[i].nodeName == "#text")
681                                 {
682                                         clone.innerHTML += children[i].data;
683                                 }
684                         }
685                 }
686         },
687
688         setCopy: function(copy) {
689                 Studio2.copyId = copy.id;
690         },
691
692         copy: function() {
693                 if (Studio2.copyId != null) {
694                         return document.getElementById(Studio2.copyId);
695                 } else {
696                         return false;
697                 }
698         },
699
700         activateCopy:   function() {
701                 Studio2.activateElement(document.getElementById(Studio2.copyId));
702         },
703
704         removeCopy:      function() {
705                 if (Studio2.copyId != null) {
706                         Studio2.removeElement(Studio2.copy());
707                 }
708                 Studio2.copyId = null;
709         },
710
711         // Copy all the slot content across to a temporary form table for submitting to the backend for the save
712         // We could have made all the slots be hidden fields within a form, (ala the original Portal editor), but then we'd have to do a lot of work during
713         // editing that really only needs to be done once per save
714
715         prepareForSave: function() {
716                 // create a new saveForm
717                 var panels = document.getElementById('panels');
718                 var saveForm = document.getElementById('prepareForSave');
719                 if (! saveForm) {
720                         saveForm = document.createElement('form');
721                         saveForm.id = 'prepareForSave';
722                         YAHOO.util.Dom.setStyle(saveForm,"visibility", "hidden");
723                         panels.parentNode.appendChild(saveForm);
724                 }
725                 // remove any existing slot information, but importantly, preserve any non-slot stuff needed for form submittal
726                 var length = saveForm.childNodes.length;
727                 var index = 0;
728                 for( var i=0; i<length; i++) {
729                         if (saveForm.childNodes[index].nodeName != 'INPUT') {
730                                 index++;
731                         } else {
732                                 if (saveForm.childNodes[index].getAttribute('name').substr(0,4) == "slot") {
733                                         saveForm.removeChild(saveForm.childNodes[index]);
734                                 } else {
735                                         index++;
736                                 }
737                         }
738                 }
739
740                 //      convert to input name='slot-{panel}-{slot}-{type}' value={value}
741                 var panelid = 0;
742                 var panels = document.getElementById('panels');
743
744                 for( var i=0;i<panels.childNodes.length;i++) {
745                         var panel = panels.childNodes[i];
746
747                         if (panel.nodeName == 'DIV') { // a panel
748                                 panelid++;
749                                 var fieldid = -1;
750
751                                 for (var j=0;j<panel.childNodes.length;j++) {
752                                         var row = panel.childNodes[j];
753
754                                         // save the panel name and label
755                                         if (row.id && row.id.indexOf('le_panellabel') != -1) {
756                                                 // a panel label
757                                                 var inputField = document.createElement('input');
758                                                 inputField.setAttribute('type','hidden');
759                                                 inputField.setAttribute('name','panel-'+panelid+'-name');
760                         inputField.setAttribute('value',document.getElementById('le_panelname_'+row.id.substr(14,row.id.length)).innerHTML);
761                                                 saveForm.appendChild(inputField);
762                                                 var inputField = document.createElement('input');
763                                                 inputField.setAttribute('type','hidden');
764                         inputField.setAttribute('name','panel-'+panelid+'-label');
765                         inputField.setAttribute('value',document.getElementById('le_panelid_'+row.id.substr(14,row.id.length)).innerHTML);
766                         saveForm.appendChild(inputField);
767                                         }
768
769                                         // now for the rows
770                                         if (row.nodeName == 'DIV') { // a row
771                                                 for (var k=0;k<row.childNodes.length;k++) {
772                                                         var field = row.childNodes[k];
773
774
775                                                         if (field.nodeName == 'DIV') { // a field
776                                                                 fieldid++;
777                                                                 for ( var l=0; l < field.childNodes.length; l++ ) {
778                                                                         var property = field.childNodes[l];
779
780                                                                         if (property.nodeName == 'SPAN') { // a property of a field
781                                                                                 if (property.attributes.length > 0) {
782 //                                                                              if (property.hasAttributes) {
783                                                                                         var type = property.className;
784                                                                                         if ((type.length>5) && (type.substr(0,5) == 'field') && (property.childNodes.length != 0)) {
785                                                                                                 var value = property.firstChild.nodeValue;
786                                                                                                 var inputField = document.createElement('input');
787                                                                                                 inputField.setAttribute('type','hidden');
788                                                                                                 inputField.setAttribute('name','slot-'+panelid+'-'+fieldid+'-'+type.substr(6,type.length));
789                                                                                                 inputField.setAttribute('value',value);
790                                                                                                 saveForm.appendChild(inputField);
791                                                                                         }
792                                                                                 }
793                                                                         }
794                                                                 }
795                                                                 // check fieldwidth in the layout; if more than one, then add an (empty) for each (so the parser can keep track of the slots)
796                                                                 var endId = fieldid+Studio2.getColumnWidth(field)-1;
797                                                                 while (fieldid<endId) {
798                                                                         fieldid++;
799                                                                         var inputField = document.createElement('input');
800                                                                         inputField.setAttribute('type','hidden');
801                                                                         inputField.setAttribute('name','slot-'+panelid+'-'+fieldid+'-name');
802                                                                         inputField.setAttribute('value','(empty)');
803                                                                         saveForm.appendChild(inputField);
804
805                                                                 }
806
807                                                         }
808                                                 }
809                                         }
810                                 }
811                         }
812                 }
813         },
814
815         handleSave: function() {
816                 ajaxStatus.showStatus(SUGAR.language.get('app_strings', 'LBL_SAVING'));
817                 ModuleBuilder.state.isDirty=false;
818                 this.prepareForSave();
819                 // set <input type='hidden' name='action' value='saveLayout'>
820                 var saveForm = document.forms['prepareForSave'];
821                 var inputField = document.createElement('input');
822                 inputField.setAttribute('type','hidden');
823                 inputField.setAttribute('name','action');
824                 inputField.setAttribute('value','saveLayout');
825                 saveForm.appendChild(inputField);
826                 ModuleBuilder.submitForm('prepareForSave');
827                 ajaxStatus.flashStatus('Save complete',5000);
828         },
829
830         handlePublish: function() {
831                 ajaxStatus.showStatus(SUGAR.language.get('app_strings', 'LBL_SAVING'));
832                 ModuleBuilder.state.isDirty=false;
833                 this.prepareForSave();
834                 // set <input type='hidden' name='action' value='saveAndPublishLayout'>
835                 var saveForm = document.forms['prepareForSave'];
836                 var inputField = document.createElement('input');
837                 inputField.setAttribute('type','hidden');
838                 inputField.setAttribute('name','action');
839                 inputField.setAttribute('value','saveAndPublishLayout');
840                 saveForm.appendChild(inputField);
841                 ModuleBuilder.submitForm('prepareForSave');
842                 ajaxStatus.flashStatus(SUGAR.language.get('ModuleBuilder','LBL_DEPLOYE_COMPLETE'),5000);
843         },
844         
845         checkGridLayout : function(view)
846         {
847             if (Studio2.countGridFields() == 0) {
848                    ModuleBuilder.layoutValidation.popup() ;
849                    return false;
850                 }
851                 if (view == "detailview")       
852                         return true;  
853                 
854             return Studio2.checkRequiredFields();
855         },
856
857         countGridFields : function() {
858             var count = 0;
859             var divs = document.getElementById( 'panels' ).getElementsByTagName( 'div' ) ;
860             for ( var j=0;j<divs.length;j++) {
861                 if (divs[j].className == 'le_field')
862                             count++;
863             }
864             return count;
865         },  
866
867     checkRequiredFields : function(){
868                 var Dom = YAHOO.util.Dom;
869                 var availablefields = Dom.get('availablefields');
870                 var fields = Dom.getElementsByClassName('field_name', '', 'availablefields');
871                 var missing = [ ];
872                 for(field in fields){
873                     if (Studio2.requiredFields.indexOf(fields[field].innerHTML) != -1) {
874                                 missing[missing.length] = fields[field].innerHTML;
875                         }
876                 }
877                 if (missing.length > 0) 
878                 {
879                     var msg = SUGAR.language.get("ModuleBuilder", "ERROR_REQUIRED_FIELDS");
880                         for (var i = 0; i < missing.length; i++) {
881                           msg += '"' + missing[i] + '"';
882                           if (i != missing.length - 1)
883                               msg += ",";
884                         }
885                 return window.confirm(msg);
886                 }
887                                 
888             return true;
889         },
890         
891         checkCalcFields: function(view, error) {
892                 if (view == "DetailView")
893            return true;
894                 
895                 var Dom = YAHOO.util.Dom;
896             var panels = Dom.get('panels');
897             var fields = Dom.getElementsByClassName('field_name', 'span', 'panels');
898             var cfs = [ ];
899             for(i in fields){
900                 if (Studio2.calcFieldList.indexOf(fields[i].innerHTML) != -1) {
901                     cfs.push(fields[i].innerHTML);
902                 }
903             }
904             if (cfs.length > 0) 
905             {
906                 var msg = SUGAR.language.get("ModuleBuilder", error) + "\n";
907                 for (var i = 0; i < cfs.length; i++) {
908                   msg += '"' + cfs[i] + '"';
909                   if (i != cfs.length - 1)
910                       msg += ",";
911                 }
912                 return window.confirm(msg);
913             }
914             return true;
915                 
916         },
917
918     getScrollZones: function(parent){
919         var Dom = YAHOO.util.Dom, TL, TR, BL, BR;
920         //Height of the scroll zones
921         var h = 20, el = Dom.get(parent);
922         //Calculate the top area
923         TL = Dom.getXY(el);
924         BR = [TL[0] + el.clientWidth, TL[1] + h];
925         var scrollUpBox = [TL, BR];
926         //Calculate the bottom area.
927         BR = [BR[0], TL[1] + el.clientHeight];
928         TL = [TL[0], BR[1] - h];
929
930         var scrollDownBox = [TL, BR];
931         return  {
932             up: scrollUpBox,
933             down: scrollDownBox
934         }
935     },
936
937     isWithinBox: function(xy, box)
938     {
939         return xy[0] > box[0][0] && xy[0] < box[1][0] && xy[1] > box[0][1] && xy[1] < box[1][1];
940     },
941
942     setScrollObj: function(o){
943         Studio2.scrollObj = o;
944         Studio2.scrollCheck = setInterval(function(){
945             var o = Studio2.scrollObj;
946             for(var i in Studio2.scrollZones)
947             {
948                 var zone = Studio2.scrollZones[i];
949                 if (Studio2.isWithinBox([o.lastX, o.lastY], zone.up))
950                 {
951                     document.getElementById(i).scrollTop -= 5;
952                     YAHOO.util.DragDropMgr.refreshCache();
953                     return;
954                 }
955                 else if (Studio2.isWithinBox([o.lastX, o.lastY], zone.down))
956                 {
957                     document.getElementById(i).scrollTop += 5;
958                     YAHOO.util.DragDropMgr.refreshCache();
959                     return;
960                 }
961             }
962         }, 25);
963     },
964
965     clearScrollObj: function() {
966         Studio2.scrollObj = false;
967         if (Studio2.scrollCheck)
968             clearInterval(Studio2.scrollCheck);
969         Studio2.scrollCheck = false;
970     },
971
972     onDrag: function(e) {
973        // Keep track of the direction of the drag for use during onDragOver
974         var y = e.pageY;
975
976         if (y < this.lastY) {
977             this.goingUp = true;
978         } else if (y > this.lastY) {
979             this.goingUp = false;
980         }
981
982         this.lastY = e.pageY || e.clientY;
983         this.lastX = e.pageX || e.clientX;
984     }
985 };
986
987