]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - jssource/src_files/include/javascript/sugarwidgets/SugarYUIWidgets.js
Release 6.4.1
[Github/sugarcrm.git] / jssource / src_files / include / javascript / sugarwidgets / SugarYUIWidgets.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 YAHOO.namespace("SUGAR");
38 (function() {
39         var sw = YAHOO.SUGAR,
40                 Event = YAHOO.util.Event,
41                 Connect = YAHOO.util.Connect,
42             Dom = YAHOO.util.Dom;
43             
44 /**
45  * Message Box is a singleton widget designed to replace the browsers 'alert'
46  * function, as well as provide capabilities for pop-over loading bars and
47  * other small non-interactive pop-overs.
48  * TODO:Still needs configurable buttons in the footer as well as
49  * auto building of a loading bar. 
50  */
51 sw.MessageBox = {
52         progressTemplate : "{body}<br><div class='sugar-progress-wrap'><div class='sugar-progress-bar'/></div>",
53         promptTemplate : "{body}:<input id='sugar-message-prompt' class='sugar-message-prompt' name='sugar-message-prompt'></input>",
54         show: function(config) {
55                 var myConf = sw.MessageBox.config = {
56                         type:'message',
57                         modal:true,
58                         width: 240,
59                         id:'sugarMsgWindow',
60                         close:true,
61                         title:"Alert",
62                         msg: " ",
63                         buttons: [ ]
64                 };
65                 
66                 if (config['type'] && config['type'] == "prompt") {
67                         myConf['buttons'] = [{
68                                 text: SUGAR.language.get("app_strings", "LBL_EMAIL_CANCEL"), handler:YAHOO.SUGAR.MessageBox.hide
69                         },{
70                                 text: SUGAR.language.get("app_strings", "LBL_EMAIL_OK"), handler:config['fn'] ? 
71                                                                 function(){
72                                                                         var returnValue = config['fn'](YAHOO.util.Dom.get("sugar-message-prompt").value);
73                                                                         if (typeof(returnValue) == "undefined" || returnValue) {
74                                                                                 YAHOO.SUGAR.MessageBox.hide();
75                                                                         } // if
76                                                                 } 
77                                                                 : YAHOO.SUGAR.MessageBox.hide, isDefault:true
78                         }];
79                 } else if ((config['type'] && config['type'] == "alert")) {
80                         myConf['buttons'] = [{
81                                 text:           SUGAR.language.get("app_strings", "LBL_EMAIL_OK"), 
82                                 handler:        config['fn'] ? 
83                                                                 function(){YAHOO.SUGAR.MessageBox.hide(); config['fn']();} 
84                                                         :
85                                                                 YAHOO.SUGAR.MessageBox.hide, 
86                                 isDefault:true
87                         }]      
88                 } else if((config['type'] && config['type'] == "confirm")) {
89                         myConf['buttons'] = [{
90                                 text: SUGAR.language.get("app_strings", "LBL_EMAIL_YES"), handler:config['fn'] ? 
91                                                                 function(){config['fn']('yes');YAHOO.SUGAR.MessageBox.hide();} 
92                                                                 : YAHOO.SUGAR.MessageBox.hide, isDefault:true
93                         },{
94                                 text: SUGAR.language.get("app_strings", "LBL_EMAIL_NO"), handler:config['fn'] ? 
95                                                                 function(){config['fn']('no');YAHOO.SUGAR.MessageBox.hide();} 
96                                                                 : YAHOO.SUGAR.MessageBox.hide
97                         }];
98                 }
99                 else if((config['type'] && config['type'] == "plain")) {
100                         myConf['buttons'] = [];
101                 } // else if 
102         
103                 for (var i in config) {
104                         myConf[i] = config[i];
105                 }
106                 if (sw.MessageBox.panel) {
107                         sw.MessageBox.panel.destroy();
108                 }
109                 sw.MessageBox.panel = new YAHOO.widget.SimpleDialog(myConf.id, {
110                         width: myConf.width + 'px',
111                         close: myConf.close,
112                         modal: myConf.modal,
113                         visible: true,
114                         fixedcenter: true,
115                 constraintoviewport: true,
116                 draggable: true,
117                 buttons: myConf.buttons
118                 });
119                 if (myConf.type == "progress") {
120                         sw.MessageBox.panel.setBody(sw.MessageBox.progressTemplate.replace(/\{body\}/gi, myConf.msg));
121                 } else if  (myConf.type == "prompt") {
122                         sw.MessageBox.panel.setBody(sw.MessageBox.promptTemplate.replace(/\{body\}/gi, myConf.msg));
123                 } else if (myConf.type == "confirm") {
124                         sw.MessageBox.panel.setBody(myConf.msg);
125                 } else {
126                         sw.MessageBox.panel.setBody(myConf.msg);
127                 }
128                 sw.MessageBox.panel.setHeader(myConf.title);
129                 
130                 if (myConf.beforeShow) {
131                         sw.MessageBox.panel.beforeShowEvent.subscribe(function() {myConf.beforeShow();});
132                 } // if 
133                 if (myConf.beforeHide) {
134                         sw.MessageBox.panel.beforeHideEvent.subscribe(function() {myConf.beforeHide();});
135                 } // if 
136                 sw.MessageBox.panel.render(document.body);
137                 sw.MessageBox.panel.show();
138         },
139         
140         updateProgress: function(percent, message) {
141                 if (!sw.MessageBox.config.type == "progress") return;
142                 
143                 if (typeof message == "string") {
144                         sw.MessageBox.panel.setBody(sw.MessageBox.progressTemplate.replace(/\{body\}/gi, message));
145                 }
146                 
147                 var barEl = Dom.getElementsByClassName("sugar-progress-bar", null, YAHOO.SUGAR.MessageBox.panel.element)[0];
148                 if (percent > 100)
149                         percent = 100;
150                 else if (percent < 0)
151                         percent = 0;
152                 
153                 barEl.style.width = percent + "%";
154         },
155          
156         hide: function() {
157                 if (sw.MessageBox.panel)
158                         sw.MessageBox.panel.hide();
159         }
160 };
161
162 sw.Template = function(content) {
163         this._setContent(content);
164 };
165
166 sw.Template.prototype = {
167         regex : /\{([\w\.]*)\}/gim,
168         
169         append: function (target, args) {
170                 var tEl = Dom.get(target);
171                 if (tEl) tEl.innerHTML += this.exec(args);
172                 else if (typeof(console) != "undefined" && typeof(console.log) == "function")
173             console.log("Warning, unable to find target:" + target);
174         },
175         exec : function (args) {
176                 var out = this.content;
177                 for (var i in this.vars) {
178                         var val = this._getValue(i, args);
179                         var reg = new RegExp("\\{" + i + "\\}", "g");
180                         out = out.replace(reg, val);
181                 }
182                 return out;
183         },
184         
185         _setContent : function(content) {
186                 this.content = content;
187                 var lastIndex = -1;
188                 var result =  this.regex.exec(content);
189                 this.vars = { };
190                 while(result && result.index > lastIndex){
191                 lastIndex = result.index;
192                 this.vars[result[1]] = true;
193                 result =  this.regex.exec(content);
194         }       
195         }, 
196         
197         _getValue: function(v, scope) {
198                 return function(e) {return eval("this." + e);}.call(scope, v);
199         }
200 };
201
202
203 /**
204  * SelectionGrid is simply a YUI Data Table with row selection already enabled.
205  */
206 sw.SelectionGrid = function(containerEl, columns, dataSource, config){
207         sw.SelectionGrid.superclass.constructor.call(this, containerEl, columns, dataSource, config);
208         // Subscribe to events for row selection  
209         this.subscribe("rowMouseoverEvent", this.onEventHighlightRow); 
210         this.subscribe("rowMouseoutEvent", this.onEventUnhighlightRow); 
211         this.subscribe("rowClickEvent", this.onEventSelectRow);
212         // Programmatically select the first row 
213         this.selectRow(this.getTrEl(0)); 
214         // Programmatically bring focus to the instance so arrow selection works immediately 
215         this.focus();
216 }
217
218 YAHOO.extend(sw.SelectionGrid, YAHOO.widget.ScrollingDataTable, {
219         //Bugfix, the default getColumn will fail if a th element is passed in. http://yuilibrary.com/projects/yui2/ticket/2528034
220         getColumn : function(column) {
221             var oColumn = this._oColumnSet.getColumn(column);
222             
223             if(!oColumn) {
224                 // Validate TD element
225                 var elCell = this.getTdEl(column);
226             if(elCell && (!column.tagName || column.tagName.toUpperCase() != "TH")) {
227                         oColumn = this._oColumnSet.getColumn(elCell.cellIndex);
228                 }
229                 // Validate TH element
230                 else {
231                     elCell = this.getThEl(column);
232                     
233                     if(elCell) {
234                         // Find by TH el ID
235                         var allColumns = this._oColumnSet.flat;
236                         for(var i=0, len=allColumns.length; i<len; i++) {
237                             if(allColumns[i].getThEl().id === elCell.id) {
238                                 oColumn = allColumns[i];
239                             } 
240                         }
241                     }
242                 }
243             }
244             if(!oColumn) {
245                 YAHOO.log("Could not get Column for column at " + column, "info", this.toString());
246             }
247             return oColumn;
248         }
249 });
250
251
252 /**
253  * DragDropTable is a YUI Data Table with support for drag/drop row re-ordering.
254  */
255 sw.DragDropTable = function(containerEl, columns, dataSource, config){
256         var DDT = sw.DragDropTable;
257         DDT.superclass.constructor.call(this, containerEl, columns, dataSource, config);
258         this.DDGroup = config.group ? config.group : "defGroup";
259         //Add table to the dragdrop table groups
260         if (typeof DDT.groups[this.DDGroup] == "undefined")
261                 DDT.groups[this.DDGroup] = [];
262         
263         DDT.groups[this.DDGroup][DDT.groups[this.DDGroup].length] = this;
264         this.tabledd = new YAHOO.util.DDTarget(containerEl);
265 }
266 sw.DragDropTable.groups = {
267                 defGroup: []
268 }
269
270 YAHOO.extend(sw.DragDropTable, YAHOO.widget.ScrollingDataTable, {
271         _addTrEl : function (oRecord) {
272                 var elTr = sw.DragDropTable.superclass._addTrEl.call(this, oRecord);
273                 if (!this.disableEmptyRows || (
274                   oRecord.getData()[this.getColumnSet().keys[0].key] != false 
275                   && oRecord.getData()[this.getColumnSet().keys[0].key] != "")
276                 ) {
277                     var _rowDD = new sw.RowDD(this, oRecord, elTr);
278                 }
279             return elTr;
280         },
281         getGroup : function () {
282                 return sw.DragDropTable.groups[this.DDGroup];
283         }
284 });
285
286 /**
287  * subclass of DragDrop to allow rows to be picked up and dropped between other rows.
288  */
289 sw.RowDD = function(oDataTable, oRecord, elTr) {
290         if(oDataTable && oRecord && elTr) {
291                 //sw.RowDD.superclass.constructor.call(this, elTr);
292                 this.ddtable = oDataTable;
293         this.table = oDataTable.getTableEl();
294         this.row = oRecord;
295         this.rowEl = elTr;
296         this.newIndex = null;
297         this.init(elTr);
298         this.initFrame(); // Needed for DDProxy
299         this.invalidHandleTypes = {};
300     }
301 };
302
303         
304 YAHOO.extend(sw.RowDD, YAHOO.util.DDProxy, {    
305 //    _removeIdRegex : /(<.[^\/<]*)id\s*=\s*['|"]?\w*['|"]?([^>]*>)/gim,
306     _removeIdRegex : new RegExp("(<.[^\\/<]*)id\\s*=\\s*['|\"]?\w*['|\"]?([^>]*>)", "gim"),
307     
308         _resizeProxy: function() {
309         this.constructor.superclass._resizeProxy.apply(this, arguments);
310         var dragEl = this.getDragEl(),
311             el = this.getEl();
312
313         Dom.setStyle(this.pointer, 'height', (this.rowEl.offsetHeight + 5) + 'px');
314         Dom.setStyle(this.pointer, 'display', 'block');
315         var xy = Dom.getXY(el);
316         Dom.setXY(this.pointer, [xy[0], (xy[1] - 5)]);
317         
318         Dom.setStyle(dragEl, 'height', this.rowEl.offsetHeight + "px");
319         Dom.setStyle(dragEl, 'width', (parseInt(Dom.getStyle(dragEl, 'width'),10) + 4) + 'px');
320         Dom.setXY(this.dragEl, xy);
321     },
322     
323     startDrag: function(x, y) { 
324             var dragEl = this.getDragEl(); 
325         var clickEl = this.getEl(); 
326         Dom.setStyle(clickEl, "opacity", "0.25"); 
327         var tableWrap = false;
328                 if (clickEl.tagName.toUpperCase() == "TR")
329             tableWrap = true;
330                 dragEl.innerHTML = "<table>" + clickEl.innerHTML.replace(this._removeIdRegex, "$1$2") + "</table>";
331         //Dom.setStyle(dragEl, "color", Dom.getStyle(clickEl, "color")); 
332         Dom.addClass(dragEl, "yui-dt-liner");
333         Dom.setStyle(dragEl, "height", (clickEl.clientHeight - 2) + "px");
334         Dom.setStyle(dragEl, "backgroundColor", Dom.getStyle(clickEl, "backgroundColor")); 
335             Dom.setStyle(dragEl, "border", "2px solid gray"); 
336                 Dom.setStyle(dragEl, "display", "");
337                 
338             this.newTable = this.ddtable;
339     },
340         
341          clickValidator: function(e) {
342         if (this.row.getData()[0] == " ")
343                 return false;
344         var target = Event.getTarget(e);
345         return ( this.isValidHandleChild(target) && 
346                         (this.id == this.handleElId || this.DDM.handleWasClicked(target, this.id)) );
347     },
348     /**
349      * This funciton checks that the target of the drag is a table row in this
350      * DDGroup and simply moves the sourceEL to that location as a preview.
351      */
352     onDragOver: function(ev, id) {
353         var groupTables = this.ddtable.getGroup();
354         for(i in groupTables) {
355                 var targetTable = groupTables[i];
356                 if (!targetTable.getContainerEl)
357                         continue;
358                 //We got a table id
359                 if (targetTable.getContainerEl().id == id) {
360                         if (targetTable != this.newTable) { //  Moved from one table to another
361                                 this.newIndex = targetTable.getRecordSet().getLength() - 1;
362                                 var destEl = Dom.get(targetTable.getLastTrEl());
363                                 destEl.parentNode.insertBefore(this.getEl(), destEl);
364                         }
365                         
366                         this.newTable = targetTable
367                         return true;
368                 }
369         }
370         
371         if (this.newTable && this.newTable.getRecord(id)) {  // Found the target row
372                 var targetRow = this.newTable.getRecord(id);
373                 var destEl = Dom.get(id);
374                 destEl.parentNode.insertBefore(this.getEl(), destEl);
375                 this.newIndex = this.newTable.getRecordIndex(targetRow);
376                 }
377     },
378     
379     endDrag: function() {
380         //Ensure the element is back on the home table to be cleaned up.
381         if (this.newTable != null && this.newIndex != null) {
382                 this.getEl().style.display = "none";
383                 this.table.appendChild(this.getEl());
384                 this.newTable.addRow(this.row.getData(), this.newIndex);
385                 try{
386                 //This method works, but throws an error under IE8
387                 this.ddtable.deleteRow(this.row);
388             }catch(e){
389                 if(typeof(console) != "undefined" && console.log)
390                 {
391                     console.log(e);
392                 }
393             }
394                 this.ddtable.render();
395         }
396         this.newTable = this.newIndex = null
397
398                 var clickEl = this.getEl();
399         Dom.setStyle(clickEl, "opacity", "");
400     }
401 });
402 /**
403  * A YUI panel that supports loading and re-loading it's contents from an Asynch request.
404  */
405
406 sw.AsyncPanel = function (el, params) {
407         if (params)
408                 sw.AsyncPanel.superclass.constructor.call(this, el, params);
409         else 
410                 sw.AsyncPanel.superclass.constructor.call(this, el);
411 }
412
413 YAHOO.extend(sw.AsyncPanel, YAHOO.widget.Panel, {
414         loadingText : "Loading...",
415         failureText : "Error loading content.",
416         
417         load : function(url, method, callback) {
418                 method = method ? method : "GET";
419                 this.setBody(this.loadingText);
420                 if (Connect.url) url = Connect.url + "&" +  url;
421                 this.callback = callback;
422                 Connect.asyncRequest(method, url, {success:this._updateContent, failure:this._loadFailed, scope:this});
423         },
424         
425         _updateContent : function (o) {
426                 //Under safari, the width of the panel may expand for no apparent reason, and under FF it will contract
427                 var w = this.cfg.config.width.value + "px"; 
428                 this.setBody(o.responseText);
429                 if (!SUGAR.isIE)
430                         this.body.style.width = w
431                 if (this.callback != null)
432                         this.callback(o);
433         },
434         
435         _loadFailed : function(o) {
436                 this.setBody(this.failureText);
437         }
438 });
439
440 sw.ClosableTab = function(el, parent, conf) {
441         this.closeEvent = new YAHOO.util.CustomEvent("close", this);
442         if (conf)
443                 sw.ClosableTab.superclass.constructor.call(this, el, conf);
444         else 
445                 sw.ClosableTab.superclass.constructor.call(this, el);
446         
447         this.setAttributeConfig("TabView", {
448         value: parent
449     });
450         this.get("labelEl").parentNode.href = "javascript:void(0);";
451 }
452
453 YAHOO.extend(sw.ClosableTab, YAHOO.widget.Tab, {
454         close : function () {
455                 this.closeEvent.fire();
456                 var parent = this.get("TabView");
457                 parent.removeTab(this);
458         },
459         
460         initAttributes: function(attr) {
461                 sw.ClosableTab.superclass.initAttributes.call(this, attr);
462                 
463                 /**
464                  * The message to display when closing the tab
465          * @attribute closeMsg
466          * @type String
467                  */
468                 this.setAttributeConfig("closeMsg", {
469                         value: attr.closeMsg || ""
470                 });
471                 
472                 /**
473          * The tab's label text (or innerHTML).
474          * @attribute label
475          * @type String
476          */
477         this.setAttributeConfig("label", {
478             value: attr.label || this._getLabel(),
479             method: function(value) {
480                 var labelEl = this.get("labelEl");
481                 if (!labelEl) { // create if needed
482                     this.set(LABEL_EL, this._createLabelEl());
483                 }
484                 
485                 labelEl.innerHTML = value;
486                 
487                 var closeButton = document.createElement('a');
488                 closeButton.href = "javascript:void(0);";
489                 Dom.addClass(closeButton, "sugar-tab-close");
490                 Event.addListener(closeButton, "click", function(e, tab){
491                                 if (tab.get("closeMsg") != "")
492                                 {
493                                         if (confirm(tab.get("closeMsg"))) {
494                                                         tab.close();
495                                                 }
496                                 }
497                                 else {
498                                                 tab.close();
499                                         }
500                         },
501                         this
502                 );
503                 labelEl.appendChild(closeButton);
504             }
505         });
506         }
507 });
508
509
510
511 /**
512  * The sugar Tree is a YUI tree with node construction based on AJAX data built in.
513  */
514 sw.Tree = function (parentEl, baseRequestParams, rootParams) {
515         this.baseRequestParams = baseRequestParams;
516         sw.Tree.superclass.constructor.call(this, parentEl);
517         if (rootParams) {
518                 if (typeof rootParams == "string")
519                         this.sendTreeNodeDataRequest(this.getRoot(), rootParams);
520                 else
521                         this.sendTreeNodeDataRequest(this.getRoot(), "");
522         }
523 }
524
525 YAHOO.extend(sw.Tree, YAHOO.widget.TreeView, {
526         sendTreeNodeDataRequest: function(parentNode, params){
527                 YAHOO.util.Connect.asyncRequest('POST', 'index.php', {
528                         success: this.handleTreeNodeDataRequest,
529                         argument: {
530                                 parentNode: parentNode
531                         },
532                         scope: this
533                 }, this.baseRequestParams + params);
534         },
535         handleTreeNodeDataRequest : function(o) {
536                 var parentNode = o.argument.parentNode;
537                 //parent.tree.removeChildren(parentNode);
538                 var resp = YAHOO.lang.JSON.parse(o.responseText);
539                 if (resp.tree_data.nodes) {
540                         for (var i = 0; i < resp.tree_data.nodes.length; i++) {
541                                 var newChild = this.buildTreeNodeRecursive(resp.tree_data.nodes[i], parentNode);
542                         }
543                 }
544                 parentNode.tree.draw();
545         },
546
547         buildTreeNodeRecursive : function(nodeData, parentNode) {
548                 nodeData.label = nodeData.text;
549                 var node = new YAHOO.widget.TextNode(nodeData, parentNode, nodeData.expanded);
550                 if (typeof(nodeData.children) == 'object') {
551                         for (var i = 0; i < nodeData.children.length; i++) {
552                                 this.buildTreeNodeRecursive(nodeData.children[i], node);
553                         }
554                 }
555                 return node;
556         }
557 });
558
559
560 /**
561  * A 1/2 second fade-in animation.
562  * @class TVSlideIn
563  * @constructor
564  * @param el {HTMLElement} the element to animate
565  * @param callback {function} function to invoke when the animation is finished
566  */
567 YAHOO.widget.TVSlideIn = function(el, callback) {
568     /**
569      * The element to animate
570      * @property el
571      * @type HTMLElement
572      */
573     this.el = el;
574
575     /**
576      * the callback to invoke when the animation is complete
577      * @property callback
578      * @type function
579      */
580     this.callback = callback;
581
582     this.logger = new YAHOO.widget.LogWriter(this.toString());
583 };
584
585 YAHOO.widget.TVSlideIn.prototype = {
586     /**
587      * Performs the animation
588      * @method animate
589      */
590     animate: function() {
591         var tvanim = this;
592
593         var s = this.el.style;
594         s.height = "";
595         s.display = "";
596         s.overflow = "hidden";
597         
598         var th = this.el.clientHeight;
599         s.height = "0px";
600
601         var dur = 0.4; 
602         var a = new YAHOO.util.Anim(this.el, {height: {from: 0, to: th, unit:"px"}}, dur);
603         a.onComplete.subscribe( function() { tvanim.onComplete(); } );
604         a.animate();
605     },
606
607     /**
608      * Clean up and invoke callback
609      * @method onComplete
610      */
611     onComplete: function() {
612         this.el.style.overflow = "";
613         this.el.style.height = "";
614         this.callback();
615     },
616
617     /**
618      * toString
619      * @method toString
620      * @return {string} the string representation of the instance
621      */
622     toString: function() {
623         return "TVSlideIn";
624     }
625 };
626
627 /**
628  * A 1/2 second fade out animation.
629  * @class TVSlideOut
630  * @constructor
631  * @param el {HTMLElement} the element to animate
632  * @param callback {Function} function to invoke when the animation is finished
633  */
634 YAHOO.widget.TVSlideOut = function(el, callback) {
635     /**
636      * The element to animate
637      * @property el
638      * @type HTMLElement
639      */
640     this.el = el;
641
642     /**
643      * the callback to invoke when the animation is complete
644      * @property callback
645      * @type function
646      */
647     this.callback = callback;
648
649     this.logger = new YAHOO.widget.LogWriter(this.toString());
650 };
651
652 YAHOO.widget.TVSlideOut.prototype = {
653     /**
654      * Performs the animation
655      * @method animate
656      */
657     animate: function() {
658         var tvanim = this;
659         var dur = 0.4;
660         var th = this.el.clientHeight;
661         this.el.style.overflow = "hidden";
662         var a = new YAHOO.util.Anim(this.el, {height: {from: th, to: 0, unit:"px"}}, dur);
663         a.onComplete.subscribe( function() { tvanim.onComplete(); } );
664         a.animate();
665     },
666
667     /**
668      * Clean up and invoke callback
669      * @method onComplete
670      */
671     onComplete: function() {
672         var s = this.el.style;
673         s.display = "none";
674         this.el.style.overflow = "";
675         this.el.style.height = "";
676         this.callback();
677     },
678
679     /**
680      * toString
681      * @method toString
682      * @return {string} the string representation of the instance
683      */
684     toString: function() {
685         return "TVSlideOut";
686     }
687 };
688
689 })();
690
691
692 /**
693  * Problem: SODA tests rely on grabbing report field names by IDs. However,
694  * these ID's don't always seem to be consistent as they are auto-generated by YUI.
695  * Here I have replaced the constructor to set the id of the row element to the modulename
696  * concatenated with the fieldname if the report flag has been set, otherwise proceed as normal.
697  *
698  * Peter D.
699  */
700 (function() {
701     var temp = YAHOO.widget.Record.prototype;
702     YAHOO.widget.Record = function(oLiteral) {
703         this._nCount = YAHOO.widget.Record._nCount;
704
705         YAHOO.widget.Record._nCount++;
706         this._oData = {};
707         if (YAHOO.lang.isObject(oLiteral)) {
708             for (var sKey in oLiteral) {
709                 if (YAHOO.lang.hasOwnProperty(oLiteral, sKey)) {
710                     this._oData[sKey] = oLiteral[sKey];
711                 }
712             }
713         }
714
715         if (SUGAR.reports && SUGAR.reports.overrideRecord) {
716             this._sId = this._oData.module_name + "_" + this._oData.field_name;
717         } else {
718             this._sId = Dom.generateId(null, "yui-rec"); //"yui-rec" + this._nCount;
719         }
720     };
721     YAHOO.widget.Record._nCount = 0;
722     YAHOO.widget.Record.prototype = temp;
723 })();