]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - modules/ModuleBuilder/javascript/studio2.js
Release 6.5.3
[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         var elem = document.getElementById('prepareForSave').elements;
172         if (elem != null) {
173             var has_tab = false;
174
175             for (var i = 0; i < elem.length; i++) {
176                 if (elem[i].name.match(/^tabDefs_.*_newTab$/)) {
177                     if (elem[i].value == '1' && elem[i].name != 'tabDefs_' + Studio2.firstPanelId + '_newTab')
178                         has_tab = true;
179                 }
180             }
181             if (has_tab == true) {
182                 document.getElementById('le_paneltype_select_' + Studio2.firstPanelIdCount).disabled = true;
183             }
184         }
185         },
186         
187         resizeDivs : function () {
188                 var Dom = YAHOO.util.Dom;
189                 if (!Dom.get('panels'))
190             return;
191         var body = document.getElementById('mbtabs');
192         var targetHeight =  body.clientHeight - (Dom.getY('panels') - Dom.getY(body)) - 32;
193                 if (Studio2.isIE) targetHeight -= 10;
194                 Dom.setStyle('panels', "height", targetHeight + "px");
195                 Dom.setStyle('panels', "width" , ((Studio2.fieldwidth * 2) + 112) + "px");
196                 Dom.setStyle('toolbox', "height", targetHeight + "px");
197         Studio2.scrollZones = {
198             panels: Studio2.getScrollZones('panels'),
199             toolbox: Studio2.getScrollZones('toolbox')
200         }
201         },
202
203         /**
204         * SIGNATURE
205         *       array = Studio2.expandableFields
206         *       element = id of the element to unregister.
207         * RETURN
208         *       element is removed from Studio2.expandableFields if found.
209         */
210
211         unregisterExpandableField:function( field ){
212                 //_write_("received for unregister: "+field.id);
213                 if(field==null || typeof(field) == 'undefined'){ return; }
214                 if ( this.isExpandable(field) ) {
215                         if (this.getColumnWidth( field ) > 1) { this.reduceFieldWidth( field ); }
216                          _write_("Unregistered:"+field.id);
217                          field.removeChild( field.childNodes[1] );
218                          field.removeAttribute("expandable");
219                          field.removeAttribute("state");
220                 }
221         },
222         isExpandable:function( field ){
223                 return field.getAttribute("expandable")!=null && !this.isSpecial(field);//&& field.getAttribute("expandable") == "true";
224         },
225         swapStates:function( src, dest ){
226                 var old_src= {state:src.getAttribute("state"), img: src.childNodes[1].src};
227                 src.setAttribute("state", dest.getAttribute("state"));
228                 src.childNodes[1].src = dest.childNodes[1].src;
229                 dest.childNodes[1].src = old_src.img;
230                 dest.setAttribute("state", old_src.state);
231         },
232
233         getImageElement:function(default_toggle){
234                 var img = document.createElement('img');
235                 if(!default_toggle)
236                         img.src = 'index.php?entryPoint=getImage&themeName='+SUGAR.themes.theme_name+'&imageName=minus_inline.gif';
237                 else
238                         img.src = 'index.php?entryPoint=getImage&themeName='+SUGAR.themes.theme_name+'&imageName=plus_inline.gif';
239                 img.className = 'le_edit';
240                 img.style.paddingRight = 2;
241                 img.style.cssFloat = 'left';
242                 img.name = 'expandable_field_icon';
243                 return img;
244         },
245
246
247         toggleFieldWidth:function(id){
248
249                 var field = YAHOO.util.Dom.get(id);
250                 if ( typeof(field) == 'undefined' || field === null ) return; 
251                 var img = field.childNodes[1];
252
253                 if( field.getAttribute("state") && field.getAttribute("state")=='reduced' ){
254                         field.parentNode.removeChild( Studio2.nextField(field) || Studio2.prevField(field) );
255                         Studio2.setColumnWidth(id,2);
256                         img.src = 'index.php?entryPoint=getImage&themeName='+SUGAR.themes.theme_name+'&imageName=minus_inline.gif';
257                         this.setExpanded( field );
258
259
260                 }else if( field.getAttribute("state") && field.getAttribute("state")=='expanded' ){
261                         Studio2.setColumnWidth(id,1);
262                         var newField = Studio2.newField();
263                         Studio2.setSpecial(newField);
264                         Studio2.activateElement(newField);
265                         field.parentNode.appendChild(newField);
266                         this.setReduced( field );
267                         img.src='index.php?entryPoint=getImage&themeName='+SUGAR.themes.theme_name+'&imageName=plus_inline.gif';
268
269                 }
270         },
271         setExpanded: function( field ){
272                 field.setAttribute("expandable","true");
273                 field.setAttribute("state","expanded");
274                 _write_("Expanded: "+field.id);
275         },
276         setReduced: function( field ){
277                 field.setAttribute("expandable","true");
278                 field.setAttribute("state","reduced");
279                 _write_("Recued: "+field.id);
280         },
281         isExpanded: function (field ){
282                 return field.getAttribute('state') == 'expanded';
283         },
284         isReduced:function (field ){
285                 return field.getAttribute('state') == 'reduced';
286         },
287         registerExpandableField: function( field ) {//field = HTML element
288                 if( Studio2.maxColumns < 2 || field == null || typeof(field) == 'undefined' || this.isSpecial (field) ) { return; }
289                 if( !this.isExpandable( field ) )  {
290                         var next = this.nextField ( field ) ;
291                         var prev = this.prevField ( field ) ;
292                         var removeMe = next || prev ;
293                         if( this.isSpecial( next) || this.isSpecial( prev ) || this.isEmpty( next ) || this.isEmpty( prev ) || removeMe == null ){ //Always Expanded
294                                 _write_("remove me is :"+removeMe);
295                                 if (removeMe != null) { field.parentNode.removeChild(removeMe); }
296                                 var img = this.getImageElement ( false );
297                                 img.onclick = function () { Studio2.toggleFieldWidth ( field.id ) };
298                                 field.insertBefore ( img, field.childNodes[1] );
299                                 this.setColumnWidth( field.id, 2 );
300                                 this.setExpanded( field );
301                                 _write_("registered field");
302                         }
303                 }else{ _write_("Could not Register field:"+field.id); }
304         },
305         setStartId: function(id) {
306                 Studio2.idStack = [id];
307         },
308
309         nextId: function() {
310                 if (Studio2.idStack.length == 1) { // if down to our last id, allocate another
311                         Studio2.idStack[0]++;
312                         Studio2.idStack.push(Studio2.idStack[0]);
313                 }
314                 return Studio2.idStack.pop();
315         },
316
317         setNextId: function(id) {
318                 Studio2.idStack.push(id);
319         },
320
321         isSpecial: function(element) {
322                 if(element==null || typeof(element) == 'undefined'){return false;};
323                 return YAHOO.util.Dom.hasClass(element,'special');
324         },
325
326         setSpecial: function(el) {
327                 YAHOO.util.Dom.addClass(el, 'special');
328         },
329
330         unsetSpecial: function(el) {
331                 YAHOO.util.Dom.removeClass(el, 'special');
332         },
333         isEmpty: function( element ){
334                 if (element == null || typeof(element) == 'undefined') {return false;};
335                 return YAHOO.util.Dom.hasClass(element, 'le_field special');
336         },
337         count: function(element) {
338                 var count = 0;
339                 for (var j=0;j<element.childNodes.length;j++) {
340                         var child = element.childNodes[j];
341                         if (child.nodeName == 'DIV') { // a valid child
342                                 count++;
343                         }
344                 }
345                 return count;
346         },
347
348         newField: function(){ // TODO: use properties to set field contents
349                 //This object must exists on the page
350                 var newField = document.createElement('div');
351                 newField.className ='le_field';
352                 newField.id = Studio2.nextId();
353                 newField.innerHTML = '<span>'+SUGAR.language.get('ModuleBuilder', 'LBL_FILLER')+'</span>' + // the displayed label
354                                                          '<span class=\'field_name\'>(filler)</span>'; // the hidden field that identifies this as something to be saved by prepareForSave()
355                 return newField;
356         },
357
358         newRow: function(titleRequired) {
359                 var newRow = document.createElement('div');
360                 if (titleRequired) {
361                         var child = document.createElement('span');
362                 child.className = 'panel_name';
363                 child.appendChild(document.createTextNode(SUGAR.language.get('ModuleBuilder', 'LBL_NEW_ROW') ) );
364                 newRow.appendChild(child);
365                 }
366                 newRow.className='le_row';
367                 newRow.id = Studio2.nextId();
368                 for(var i=0;i<Studio2.maxColumns;i++) {
369                         var newField = Studio2.newField();
370                         Studio2.setSpecial(newField);
371                         newRow.appendChild(newField);
372                 }
373                 return newRow;
374         },
375
376         newPanel: function() {
377                 var newPanel = document.createElement('div');
378                 newPanel.className='le_panel';
379                 newPanel.id = Studio2.nextId();
380                 // get the panelid for this panel - must be unique in the layout, even across saves
381                 // 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
382                 //var panelIdentifier = 'lbl_panel'+(Studio2.panelNumber ++) ;
383                 var panelNumber = (Studio2.panelNumber ++) ;
384                 var view = document.getElementById('prepareForSave').view.value;
385                 var panelLabel = 'lbl_' + view +  '_panel' + panelNumber;
386
387                 var div = document.createElement('div');
388                 div.id = 'le_panellabel_' + newPanel.id;
389
390         var child = document.createElement('span');
391         child.id = 'le_panelname_' + newPanel.id;
392         child.className = 'panel_name';
393         child.appendChild(document.createTextNode(SUGAR.language.get('ModuleBuilder', 'LBL_NEW_PANEL') ) );
394         div.appendChild(child);
395         var child = document.createElement('span');
396         child.id = 'le_panelid_' + newPanel.id;
397         child.className = 'panel_id';
398         child.appendChild(document.createTextNode(panelLabel));
399                 div.appendChild(child);
400                 newPanel.appendChild(div);
401
402                 var img = document.createElement('img');
403                 img.src='index.php?entryPoint=getImage&themeName='+SUGAR.themes.theme_name+'&imageName=edit_inline.gif';
404                 img.className = 'le_edit';
405                 img.style.cursor="pointer;";
406                 var editModule = document.getElementById('prepareForSave').view_module.value;
407                 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') ;
408                 if (document.getElementById('prepareForSave').view_package)
409                       editString += '&view_package='+document.getElementById('prepareForSave').view_package.value ;
410                 var view = document.prepareForSave.view.value;
411                 img.onclick = function() { var value_label = document.getElementById('le_panelname_'+newPanel.id).innerHTML;ModuleBuilder.asyncRequest( editString + '&value_label=' + value_label, ModuleBuilder.updateContent ); }
412                 newPanel.appendChild(img);
413                 return newPanel;
414         },
415
416         establishLocation: function(element) {
417                 var location = null;
418                 while(element.parentNode != 'body') {
419                         location = element.id;
420                         if ((location == 'panels') || (location == 'toolbox') || (location == 'delete')){
421                                 break;
422                         }
423                         element = element.parentNode;
424                 }
425                 if (location == null) {
426                         alert("Studio2:establishLocation: badly formed document");
427                         die();
428                 }
429                 return location;
430         },
431
432         reclaimIds: function(element) {
433                 // return the ids in this element to the available pool
434                 // do not reclaim field IDs as they never really disappear - they just move between toolbox and panel
435                 if (element.className.indexOf('le_field') == -1) {
436                         Studio2.setNextId(element.id);
437                         for (var i=0;i<element.childNodes.length;i++) {
438                                 var child = element.childNodes[i];
439                                 if (child.nodeName == 'DIV') { // a subelement
440                                         Studio2.reclaimIds(child);
441                                 }
442                         }
443                 }
444         },
445
446         reclaimFields: function(element) {
447                 if (element.className.indexOf('le_field') != -1) {
448                         if (! Studio2.isSpecial(element)) {
449                                 var destination = document.getElementById('availablefields');
450 //                              destination.appendChild(element.parentNode.removeChild(element));
451                                 destination.appendChild(element);
452                                 Studio2.resetFieldWidth(element);
453                         } else {
454                                 element.parentNode.removeChild(element);
455                         }
456                 } else {
457                         for (var i=0;i<element.childNodes.length;i++) {
458                                 var child = element.childNodes[i];
459                                 if (child.nodeName == 'DIV') { // a subelement
460                                         Studio2.reclaimFields(child);
461                                 }
462                         }
463                 }
464         },
465
466         highlightElement: function(field) {
467                 YAHOO.util.Dom.setStyle(field,'visibility','hidden');
468         },
469
470         /* FIELD WIDTH FUNCTIONS */
471
472         getSpacing: function(field) {
473                 var Field = new YAHOO.util.Element(field);
474                 var leftMargin = parseInt(Field.getStyle('margin-left'));
475                 var rightMargin = parseInt(Field.getStyle('margin-right'));
476                 var leftPadding = parseInt(Field.getStyle('padding-left'));
477                 var rightPadding = parseInt(Field.getStyle('padding-right'));
478                 if (Studio2.isIE) {
479                         return (leftMargin + rightMargin);
480                 } else {
481                         return (leftMargin + rightMargin + leftPadding + rightPadding + 2);
482                 }
483         },
484
485         resetFieldWidth: function(field) {
486                 YAHOO.util.Dom.setStyle(field,'width',Studio2.fieldwidth + 'px');
487                 //Dom.setStyle(field,'width',Studio2.fieldwidth + 'px' );
488         },
489
490         /* a hack function, purely because Firefox has a problem with field widths during the init function */
491         /* so rather than relying on the style values we just set the width directly to the final value */
492         adjustWidth: function(field,columns) {
493                 var newWidth = columns * (Studio2.fieldwidth + Studio2.getSpacing(field));
494                 YAHOO.util.Dom.setStyle(field,'width',newWidth + 'px' );
495         },
496
497         increaseFieldWidth: function(field) {
498                 var newWidth;
499 //              var currentWidth = parseInt(field.clientWidth);
500                 var currentWidth = Studio2.getFieldWidth(field);
501                 newWidth = currentWidth + Studio2.fieldwidth + Studio2.getSpacing(field);
502 //              field.style.width = newWidth+'px';
503                 YAHOO.util.Dom.setStyle(field,'width',newWidth + 'px' );
504         },
505
506         reduceFieldWidth: function(field) {
507                 var newWidth;
508                 var currentWidth = Studio2.getFieldWidth(field);
509                 newWidth = currentWidth - Studio2.fieldwidth - Studio2.getSpacing(field);
510                 YAHOO.util.Dom.setStyle(field,'width',newWidth + 'px' );
511         },
512
513         getFieldWidth: function(field) {
514                 var width = parseInt(YAHOO.util.Dom.getStyle(field, 'width')); // computed style value of the field width (or currentStyle in IE - same result)
515                 if (isNaN(width)) {
516                         width = Studio2.fieldwidth; // if field width is set to something like 'auto' we need to take a better guess
517                 }
518                 return width;
519         },
520
521         setFieldWidth: function(field,width) {
522                 YAHOO.util.Dom.setStyle(field,'width',width);
523         },
524
525         getColumnWidth: function(field) {
526                 return Math.floor(Studio2.getFieldWidth(field)/Studio2.fieldwidth);
527         },
528
529         setColumnWidth: function(field,columns) {
530                 var spacing = Studio2.getSpacing(field);
531                 var newWidth = columns * (Studio2.fieldwidth + spacing) - spacing;
532                 YAHOO.util.Dom.setStyle(field,'width',newWidth + 'px' );
533         },
534
535         firstField: function(row) {
536                 var firstfield = row.firstChild;
537                 while (firstfield.nodeName != 'DIV') {
538                         firstfield = firstfield.nextSibling;
539                 }
540                 return firstfield;
541         },
542
543         getColumn: function(field) {
544                 var firstfield = Studio2.firstField(field.parentNode);
545                 return Math.ceil((YAHOO.util.Dom.getX(field) - YAHOO.util.Dom.getX(firstfield) / Studio2.fieldwidth));  
546         },
547
548         getRow: function(field) {
549                 // find our parent row
550                 // find how many previous siblings we have that are also rows
551                 // our row is that + 1
552                 var row = field.parentNode;
553                 var count = 1;
554                 while ((row = row.previousSibling) !== null) {
555                         if (row.nodeName == 'DIV') {
556                                 count++;
557                         }
558                 }
559                 return count;
560         },
561
562         prevField: function(field){
563                 var prev = field.previousSibling;
564                 while( (null !== prev) && (prev.nodeName != 'DIV')){
565                         prev = prev.previousSibling;
566                 }
567                 return prev;
568         },
569         nextField: function(field) {
570                 var next = field.nextSibling;
571                 while (typeof(next)!='undefined' && (next !== null) && (next.nodeName != 'DIV')) {
572                         next = next.nextSibling;
573                 }
574                 return next;
575         },
576
577
578         /* ELEMENT FUNCTIONS */
579
580         // TODO: rewrite tidyPanels, tidyRows and tidyFields as a recursive tidy
581         tidyPanels: function() {
582                 var panels = document.getElementById('panels');
583                 if (Studio2.count(panels) <= 0) {
584                         var newPanel = Studio2.newPanel();
585                         newPanel.appendChild(Studio2.newRow(false));
586                         panels.appendChild(newPanel);
587                         Studio2.activateElement(newPanel);
588                 }
589         },
590
591         tidyRows: function(panel) {
592                 if (Studio2.count(panel) <= 0) { // no rows left
593                         if (Studio2.count(panel.parentNode)>1) {
594                                 Studio2.removeElement(panel);
595                                 Studio2.tidyPanels();
596                         } else {
597                                 // add a blank row back in
598                                 var newRow = Studio2.newRow(false);
599                                 panel.appendChild(newRow);
600                                 Studio2.activateElement(newRow);
601 //                              debugger;
602                         }
603                 }
604         },
605
606         tidyFields: function(row) {
607                 if (Studio2.count(row) <= 0) { // no fields left
608                         var panel = row.parentNode;
609                         Studio2.removeElement(row);
610                         Studio2.tidyRows(panel);
611                 }
612         },
613
614         removeElement: function(element) {
615                 Studio2.reclaimIds(element);
616                 Studio2.reclaimFields(element);
617                 if (element.className.indexOf('le_field') == -1) {
618                         // all fields have been moved to availablefields in Studio2.reclaimFields
619                         element.parentNode.removeChild(element);
620                 }
621         },
622
623         swapElements: function(el1,el2) {
624                 // TODO: record this swap in TRANSACTION
625                 var el1Width = Studio2.getFieldWidth(el1);
626                 var el2Width = Studio2.getFieldWidth(el2);
627                 YAHOO.util.DragDropMgr.swapNode(el1, el2);
628                 Studio2.setFieldWidth(el1,el2Width);
629                 Studio2.setFieldWidth(el2,el1Width);
630         },
631
632         activateElement: function(element) {
633                 if (!document.getElementById(element.id)) {
634                         document.body.appendChild(element);
635                 }
636                 if (element.className.indexOf('le_panel') != -1) {
637                         new Studio2.PanelDD(element.id,'studio2.panels');
638                         new YAHOO.util.DDTarget(element.id,'studio2.rows'); // add so a background for row moves
639                 }
640                 if (element.className.indexOf('le_row') != -1) {
641                         new Studio2.RowDD(element.id,'studio2.rows');
642                 }
643                 if (element.className.indexOf('le_field') != -1) {
644                         new Studio2.FieldDD(element,'studio2.fields');
645                 }
646                 for (var i=0;i<element.childNodes.length;i++) {
647                         var child = element.childNodes[i];
648                         if (child.nodeName == 'DIV') { // a valid child
649                                 Studio2.activateElement(child);
650                         }
651                 }
652
653         },
654
655         /**
656          * A substitute for cloneNode that is Yahoo Drag-and-Drop compatible.
657          * Using document.cloneNode causes Yahoo DnD to fail as the ID is also cloned, leading to duplicate IDs in the document
658          * This substitute doesn't copy the ID
659          */
660
661         copyElement: function(element) {
662                 var copy = document.createElement(element.tagName);
663                 if (element.attributes.length > 0) {
664                         var attrs = element.attributes;
665                         for(var i=0;i<attrs.length;i++) {
666                                 if (attrs[i].name != 'id') {
667                                         // 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
668                                         var a = element.getAttributeNode(attrs[i].name);
669                                         if (a && a.specified) {
670                                                 if (attrs[i].name == 'class') { // Needed for IE
671                                                         copy.className = attrs[i].value;
672                                                 }
673                                                 copy.setAttribute(attrs[i].name,attrs[i].value);
674                                         }
675                                 }
676                         }
677                 }
678
679                 Studio2.copyChildren(element, copy);
680                 copy.id = Studio2.nextId();
681                 return copy;
682         },
683         
684         copyChildren : function(original, clone)
685         {
686                 var children = original.childNodes
687                 for (var i in children)
688                 {
689                         if (children[i])
690                         {
691                                 if (children[i].tagName && children[i].tagName.substr(0,1) != "#"){
692                                         clone.appendChild(Studio2.copyElement(children[i]));
693                                 }
694                                 else if (children[i].nodeName && children[i].nodeName == "#text")
695                                 {
696                                         clone.innerHTML += children[i].data;
697                                 }
698                         }
699                 }
700         },
701
702         setCopy: function(copy) {
703                 Studio2.copyId = copy.id;
704         },
705
706         copy: function() {
707                 if (Studio2.copyId != null) {
708                         return document.getElementById(Studio2.copyId);
709                 } else {
710                         return false;
711                 }
712         },
713
714         activateCopy:   function() {
715                 Studio2.activateElement(document.getElementById(Studio2.copyId));
716         },
717
718         removeCopy:      function() {
719                 if (Studio2.copyId != null) {
720                         Studio2.removeElement(Studio2.copy());
721                 }
722                 Studio2.copyId = null;
723         },
724
725         // Copy all the slot content across to a temporary form table for submitting to the backend for the save
726         // 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
727         // editing that really only needs to be done once per save
728
729         prepareForSave: function() {
730                 // create a new saveForm
731                 var panels = document.getElementById('panels');
732                 var saveForm = document.getElementById('prepareForSave');
733                 if (! saveForm) {
734                         saveForm = document.createElement('form');
735                         saveForm.id = 'prepareForSave';
736                         YAHOO.util.Dom.setStyle(saveForm,"visibility", "hidden");
737                         panels.parentNode.appendChild(saveForm);
738                 }
739                 // remove any existing slot information, but importantly, preserve any non-slot stuff needed for form submittal
740                 var length = saveForm.childNodes.length;
741                 var index = 0;
742                 for( var i=0; i<length; i++) {
743                         if (saveForm.childNodes[index].nodeName != 'INPUT') {
744                                 index++;
745                         } else {
746                                 if (saveForm.childNodes[index].getAttribute('name').substr(0,4) == "slot") {
747                                         saveForm.removeChild(saveForm.childNodes[index]);
748                                 } else {
749                                         index++;
750                                 }
751                         }
752                 }
753
754                 //      convert to input name='slot-{panel}-{slot}-{type}' value={value}
755                 var panelid = 0;
756                 var panels = document.getElementById('panels');
757
758                 for( var i=0;i<panels.childNodes.length;i++) {
759                         var panel = panels.childNodes[i];
760
761                         if (panel.nodeName == 'DIV') { // a panel
762                                 panelid++;
763                                 var fieldid = -1;
764
765                                 for (var j=0;j<panel.childNodes.length;j++) {
766                                         var row = panel.childNodes[j];
767
768                                         // save the panel name and label
769                                         if (row.id && row.id.indexOf('le_panellabel') != -1) {
770                                                 // a panel label
771                                                 var inputField = document.createElement('input');
772                                                 inputField.setAttribute('type','hidden');
773                                                 inputField.setAttribute('name','panel-'+panelid+'-name');
774                         inputField.setAttribute('value',document.getElementById('le_panelname_'+row.id.substr(14,row.id.length)).innerHTML);
775                                                 saveForm.appendChild(inputField);
776                                                 var inputField = document.createElement('input');
777                                                 inputField.setAttribute('type','hidden');
778                         inputField.setAttribute('name','panel-'+panelid+'-label');
779                         inputField.setAttribute('value',document.getElementById('le_panelid_'+row.id.substr(14,row.id.length)).innerHTML);
780                         saveForm.appendChild(inputField);
781                                         }
782
783                                         // now for the rows
784                                         if (row.nodeName == 'DIV') { // a row
785                                                 for (var k=0;k<row.childNodes.length;k++) {
786                                                         var field = row.childNodes[k];
787
788
789                                                         if (field.nodeName == 'DIV') { // a field
790                                                                 fieldid++;
791                                                                 for ( var l=0; l < field.childNodes.length; l++ ) {
792                                                                         var property = field.childNodes[l];
793
794                                                                         if (property.nodeName == 'SPAN') { // a property of a field
795                                                                                 if (property.attributes.length > 0) {
796 //                                                                              if (property.hasAttributes) {
797                                                                                         var type = property.className;
798                                                                                         if ((type.length>5) && (type.substr(0,5) == 'field') && (property.childNodes.length != 0)) {
799                                                                                                 var value = property.firstChild.nodeValue;
800                                                                                                 var inputField = document.createElement('input');
801                                                                                                 inputField.setAttribute('type','hidden');
802                                                                                                 inputField.setAttribute('name','slot-'+panelid+'-'+fieldid+'-'+type.substr(6,type.length));
803                                                                                                 inputField.setAttribute('value',value);
804                                                                                                 saveForm.appendChild(inputField);
805                                                                                         }
806                                                                                 }
807                                                                         }
808                                                                 }
809                                                                 // check fieldwidth in the layout; if more than one, then add an (empty) for each (so the parser can keep track of the slots)
810                                                                 var endId = fieldid+Studio2.getColumnWidth(field)-1;
811                                                                 while (fieldid<endId) {
812                                                                         fieldid++;
813                                                                         var inputField = document.createElement('input');
814                                                                         inputField.setAttribute('type','hidden');
815                                                                         inputField.setAttribute('name','slot-'+panelid+'-'+fieldid+'-name');
816                                                                         inputField.setAttribute('value','(empty)');
817                                                                         saveForm.appendChild(inputField);
818
819                                                                 }
820
821                                                         }
822                                                 }
823                                         }
824                                 }
825                         }
826                 }
827         },
828
829         handleSave: function() {
830                 ajaxStatus.showStatus(SUGAR.language.get('app_strings', 'LBL_SAVING'));
831                 ModuleBuilder.state.isDirty=false;
832                 this.prepareForSave();
833                 // set <input type='hidden' name='action' value='saveLayout'>
834                 var saveForm = document.forms['prepareForSave'];
835                 var inputField = document.createElement('input');
836                 inputField.setAttribute('type','hidden');
837                 inputField.setAttribute('name','action');
838                 inputField.setAttribute('value','saveLayout');
839                 saveForm.appendChild(inputField);
840                 ModuleBuilder.submitForm('prepareForSave');
841                 ajaxStatus.flashStatus('Save complete',5000);
842         },
843
844         handlePublish: function() {
845                 ajaxStatus.showStatus(SUGAR.language.get('app_strings', 'LBL_SAVING'));
846                 ModuleBuilder.state.isDirty=false;
847                 this.prepareForSave();
848                 // set <input type='hidden' name='action' value='saveAndPublishLayout'>
849                 var saveForm = document.forms['prepareForSave'];
850                 var inputField = document.createElement('input');
851                 inputField.setAttribute('type','hidden');
852                 inputField.setAttribute('name','action');
853                 inputField.setAttribute('value','saveAndPublishLayout');
854                 saveForm.appendChild(inputField);
855                 ModuleBuilder.submitForm('prepareForSave');
856                 ajaxStatus.flashStatus(SUGAR.language.get('ModuleBuilder','LBL_DEPLOYE_COMPLETE'),5000);
857         },
858         
859         checkGridLayout : function(view)
860         {
861             if (Studio2.countGridFields() == 0) {
862                    ModuleBuilder.layoutValidation.popup() ;
863                    return false;
864                 }
865                 if (view == "detailview")       
866                         return true;  
867                 
868             return Studio2.checkRequiredFields();
869         },
870
871         countGridFields : function() {
872             var count = 0;
873             var divs = document.getElementById( 'panels' ).getElementsByTagName( 'div' ) ;
874             for ( var j=0;j<divs.length;j++) {
875                 if (divs[j].className == 'le_field')
876                             count++;
877             }
878             return count;
879         },  
880
881     checkRequiredFields : function(){
882                 var Dom = YAHOO.util.Dom;
883                 var availablefields = Dom.get('availablefields');
884                 var fields = Dom.getElementsByClassName('field_name', '', 'availablefields');
885                 var missing = [ ];
886                 for(field in fields){
887                     if (Studio2.requiredFields.indexOf(fields[field].innerHTML) != -1) {
888                                 missing[missing.length] = fields[field].innerHTML;
889                         }
890                 }
891                 if (missing.length > 0) 
892                 {
893                     var msg = SUGAR.language.get("ModuleBuilder", "ERROR_REQUIRED_FIELDS");
894                         for (var i = 0; i < missing.length; i++) {
895                           msg += '"' + missing[i] + '"';
896                           if (i != missing.length - 1)
897                               msg += ",";
898                         }
899                 return window.confirm(msg);
900                 }
901                                 
902             return true;
903         },
904         
905         checkCalcFields: function(view, error) {
906                 if (view == "DetailView")
907            return true;
908                 
909                 var Dom = YAHOO.util.Dom;
910             var panels = Dom.get('panels');
911             var fields = Dom.getElementsByClassName('field_name', 'span', 'panels');
912             var cfs = [ ];
913             for(i in fields){
914                 if (Studio2.calcFieldList.indexOf(fields[i].innerHTML) != -1) {
915                     cfs.push(fields[i].innerHTML);
916                 }
917             }
918             if (cfs.length > 0) 
919             {
920                 var msg = SUGAR.language.get("ModuleBuilder", error) + "\n";
921                 for (var i = 0; i < cfs.length; i++) {
922                   msg += '"' + cfs[i] + '"';
923                   if (i != cfs.length - 1)
924                       msg += ",";
925                 }
926                 return window.confirm(msg);
927             }
928             return true;
929                 
930         },
931
932     getScrollZones: function(parent){
933         var Dom = YAHOO.util.Dom, TL, TR, BL, BR;
934         //Height of the scroll zones
935         var h = 20, el = Dom.get(parent);
936         //Calculate the top area
937         TL = Dom.getXY(el);
938         BR = [TL[0] + el.clientWidth, TL[1] + h];
939         var scrollUpBox = [TL, BR];
940         //Calculate the bottom area.
941         BR = [BR[0], TL[1] + el.clientHeight];
942         TL = [TL[0], BR[1] - h];
943
944         var scrollDownBox = [TL, BR];
945         return  {
946             up: scrollUpBox,
947             down: scrollDownBox
948         }
949     },
950
951     isWithinBox: function(xy, box)
952     {
953         return xy[0] > box[0][0] && xy[0] < box[1][0] && xy[1] > box[0][1] && xy[1] < box[1][1];
954     },
955
956     setScrollObj: function(o){
957         Studio2.scrollObj = o;
958         Studio2.scrollCheck = setInterval(function(){
959             var o = Studio2.scrollObj;
960             for(var i in Studio2.scrollZones)
961             {
962                 var zone = Studio2.scrollZones[i];
963                 if (Studio2.isWithinBox([o.lastX, o.lastY], zone.up))
964                 {
965                     document.getElementById(i).scrollTop -= 5;
966                     YAHOO.util.DragDropMgr.refreshCache();
967                     return;
968                 }
969                 else if (Studio2.isWithinBox([o.lastX, o.lastY], zone.down))
970                 {
971                     document.getElementById(i).scrollTop += 5;
972                     YAHOO.util.DragDropMgr.refreshCache();
973                     return;
974                 }
975             }
976         }, 25);
977     },
978
979     clearScrollObj: function() {
980         Studio2.scrollObj = false;
981         if (Studio2.scrollCheck)
982             clearInterval(Studio2.scrollCheck);
983         Studio2.scrollCheck = false;
984     },
985
986     onDrag: function(e) {
987        // Keep track of the direction of the drag for use during onDragOver
988         var y = e.pageY;
989
990         if (y < this.lastY) {
991             this.goingUp = true;
992         } else if (y > this.lastY) {
993             this.goingUp = false;
994         }
995
996         this.lastY = e.pageY || e.clientY;
997         this.lastX = e.pageX || e.clientX;
998     }
999 };
1000
1001 Studio2.firstPanelId = "";
1002 Studio2.firstPanelIdCount = 0;