]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - jssource/src_files/include/javascript/sugarwidgets/SugarYUIWidgets.js
Release 6.5.0
[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: false,
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         if (config.forceMulti){
212         this.subscribe("rowClickEvent", function(o){
213             o.event.preventDefault();
214             this.clearTextSelection();
215             o.event = SUGAR.util.clone(o.event);
216             o.event.ctrlKey = o.event.metaKey = true;
217             this.onEventSelectRow(o);
218         });
219     } else {
220         this.subscribe("rowClickEvent", this.onEventSelectRow);
221     }
222
223         // Programmatically select the first row
224         this.selectRow(this.getTrEl(0));
225         // Programmatically bring focus to the instance so arrow selection works immediately
226         this.focus();
227 }
228
229 YAHOO.extend(sw.SelectionGrid, YAHOO.widget.ScrollingDataTable, {
230         //Bugfix, the default getColumn will fail if a th element is passed in. http://yuilibrary.com/projects/yui2/ticket/2528034
231         getColumn : function(column) {
232             var oColumn = this._oColumnSet.getColumn(column);
233
234             if(!oColumn) {
235                 // Validate TD element
236                 var elCell = this.getTdEl(column);
237             if(elCell && (!column.tagName || column.tagName.toUpperCase() != "TH")) {
238                         oColumn = this._oColumnSet.getColumn(elCell.cellIndex);
239                 }
240                 // Validate TH element
241                 else {
242                     elCell = this.getThEl(column);
243
244                     if(elCell) {
245                         // Find by TH el ID
246                         var allColumns = this._oColumnSet.flat;
247                         for(var i=0, len=allColumns.length; i<len; i++) {
248                             if(allColumns[i].getThEl().id === elCell.id) {
249                                 oColumn = allColumns[i];
250                             }
251                         }
252                     }
253                 }
254             }
255             if(!oColumn) {
256                 YAHOO.log("Could not get Column for column at " + column, "info", this.toString());
257             }
258             return oColumn;
259         }
260 });
261
262
263 /**
264  * DragDropTable is a YUI Data Table with support for drag/drop row re-ordering.
265  */
266 sw.DragDropTable = function(containerEl, columns, dataSource, config){
267         var DDT = sw.DragDropTable;
268         DDT.superclass.constructor.call(this, containerEl, columns, dataSource, config);
269         this.DDGroup = config.group ? config.group : "defGroup";
270         //Add table to the dragdrop table groups
271         if (typeof DDT.groups[this.DDGroup] == "undefined")
272                 DDT.groups[this.DDGroup] = [];
273
274         DDT.groups[this.DDGroup][DDT.groups[this.DDGroup].length] = this;
275         this.tabledd = new YAHOO.util.DDTarget(containerEl);
276 }
277 sw.DragDropTable.groups = {
278                 defGroup: []
279 }
280
281 YAHOO.extend(sw.DragDropTable, YAHOO.widget.ScrollingDataTable, {
282         _addTrEl : function (oRecord) {
283                 var elTr = sw.DragDropTable.superclass._addTrEl.call(this, oRecord);
284                 if (!this.disableEmptyRows || (
285                   oRecord.getData()[this.getColumnSet().keys[0].key] != false
286                   && oRecord.getData()[this.getColumnSet().keys[0].key] != "")
287                 ) {
288                     var _rowDD = new sw.RowDD(this, oRecord, elTr);
289                 }
290             return elTr;
291         },
292         getGroup : function () {
293                 return sw.DragDropTable.groups[this.DDGroup];
294         }
295 });
296
297 /**
298  * subclass of DragDrop to allow rows to be picked up and dropped between other rows.
299  */
300 sw.RowDD = function(oDataTable, oRecord, elTr) {
301         if(oDataTable && oRecord && elTr) {
302                 //sw.RowDD.superclass.constructor.call(this, elTr);
303                 this.ddtable = oDataTable;
304         this.table = oDataTable.getTableEl();
305         this.row = oRecord;
306         this.rowEl = elTr;
307         this.newIndex = null;
308         this.init(elTr);
309         this.initFrame(); // Needed for DDProxy
310         this.invalidHandleTypes = {};
311     }
312 };
313
314
315 YAHOO.extend(sw.RowDD, YAHOO.util.DDProxy, {
316 //    _removeIdRegex : /(<.[^\/<]*)id\s*=\s*['|"]?\w*['|"]?([^>]*>)/gim,
317     _removeIdRegex : new RegExp("(<.[^\\/<]*)id\\s*=\\s*['|\"]?\w*['|\"]?([^>]*>)", "gim"),
318
319         _resizeProxy: function() {
320         this.constructor.superclass._resizeProxy.apply(this, arguments);
321         var dragEl = this.getDragEl(),
322             el = this.getEl();
323
324         Dom.setStyle(this.pointer, 'height', (this.rowEl.offsetHeight + 5) + 'px');
325         Dom.setStyle(this.pointer, 'display', 'block');
326         var xy = Dom.getXY(el);
327         Dom.setXY(this.pointer, [xy[0], (xy[1] - 5)]);
328
329         Dom.setStyle(dragEl, 'height', this.rowEl.offsetHeight + "px");
330         Dom.setStyle(dragEl, 'width', (parseInt(Dom.getStyle(dragEl, 'width'),10) + 4) + 'px');
331         Dom.setXY(this.dragEl, xy);
332     },
333
334     startDrag: function(x, y) {
335             var dragEl = this.getDragEl();
336         var clickEl = this.getEl();
337         Dom.setStyle(clickEl, "opacity", "0.25");
338         var tableWrap = false;
339                 if (clickEl.tagName.toUpperCase() == "TR")
340             tableWrap = true;
341                 dragEl.innerHTML = "<table>" + clickEl.innerHTML.replace(this._removeIdRegex, "$1$2") + "</table>";
342         //Dom.setStyle(dragEl, "color", Dom.getStyle(clickEl, "color"));
343         Dom.addClass(dragEl, "yui-dt-liner");
344         Dom.setStyle(dragEl, "height", (clickEl.clientHeight - 2) + "px");
345         Dom.setStyle(dragEl, "backgroundColor", Dom.getStyle(clickEl, "backgroundColor"));
346             Dom.setStyle(dragEl, "border", "2px solid gray");
347                 Dom.setStyle(dragEl, "display", "");
348
349             this.newTable = this.ddtable;
350     },
351
352          clickValidator: function(e) {
353         if (this.row.getData()[0] == " ")
354                 return false;
355         var target = Event.getTarget(e);
356         return ( this.isValidHandleChild(target) &&
357                         (this.id == this.handleElId || this.DDM.handleWasClicked(target, this.id)) );
358     },
359     /**
360      * This function checks that the target of the drag is a table row in this
361      * DDGroup and simply moves the sourceEL to that location as a preview.
362      */
363     onDragOver: function(ev, id) {
364         var groupTables = this.ddtable.getGroup();
365         for(i in groupTables) {
366                 var targetTable = groupTables[i];
367                 if (!targetTable.getContainerEl)
368                         continue;
369                 //We got a table id
370                 if (targetTable.getContainerEl().id == id) {
371                         if (targetTable != this.newTable) { //  Moved from one table to another
372                                 this.newIndex = targetTable.getRecordSet().getLength() - 1;
373                                 var destEl = Dom.get(targetTable.getLastTrEl());
374                                 destEl.parentNode.insertBefore(this.getEl(), destEl);
375                         }
376
377                         this.newTable = targetTable
378                         return true;
379                 }
380         }
381
382         if (this.newTable && this.newTable.getRecord(id)) {  // Found the target row
383                 var targetRow = this.newTable.getRecord(id);
384                 var destEl = Dom.get(id);
385                 destEl.parentNode.insertBefore(this.getEl(), destEl);
386                 this.newIndex = this.newTable.getRecordIndex(targetRow);
387                 }
388     },
389
390     endDrag: function() {
391         //Ensure the element is back on the home table to be cleaned up.
392         if (this.newTable != null && this.newIndex != null) {
393                 this.getEl().style.display = "none";
394                 this.table.appendChild(this.getEl());
395                 this.newTable.addRow(this.row.getData(), this.newIndex);
396                 try{
397                 //This method works, but throws an error under IE8
398                 this.ddtable.deleteRow(this.row);
399             }catch(e){
400                 if(typeof(console) != "undefined" && console.log)
401                 {
402                     console.log(e);
403                 }
404             }
405                 this.ddtable.render();
406         }
407         this.newTable = this.newIndex = null
408
409                 var clickEl = this.getEl();
410         Dom.setStyle(clickEl, "opacity", "");
411     }
412 });
413 /**
414  * A YUI panel that supports loading and re-loading it's contents from an Asynch request.
415  */
416
417 sw.AsyncPanel = function (el, params) {
418         if (params)
419                 sw.AsyncPanel.superclass.constructor.call(this, el, params);
420         else
421                 sw.AsyncPanel.superclass.constructor.call(this, el);
422 }
423
424 YAHOO.extend(sw.AsyncPanel, YAHOO.widget.Panel, {
425         loadingText : "Loading...",
426         failureText : "Error loading content.",
427
428         load : function(url, method, callback, postdata) {
429                 method = method ? method : "GET";
430                 this.setBody(this.loadingText);
431                 if (Connect.url) url = Connect.url + "&" +  url;
432                 this.callback = callback;
433                 Connect.asyncRequest(method, url, {success:this._updateContent, failure:this._loadFailed, scope:this}, postdata);
434         },
435
436         _updateContent : function (o) {
437                 //Under safari, the width of the panel may expand for no apparent reason, and under FF it will contract
438                 var w = this.cfg.config.width.value + "px";
439                 this.setBody(o.responseText);
440                 if (!SUGAR.isIE)
441                         this.body.style.width = w
442                 if (this.callback != null)
443                         this.callback(o);
444         },
445
446         _loadFailed : function(o) {
447                 this.setBody(this.failureText);
448         }
449 });
450
451 sw.ClosableTab = function(el, parent, conf) {
452         this.closeEvent = new YAHOO.util.CustomEvent("close", this);
453         if (conf)
454                 sw.ClosableTab.superclass.constructor.call(this, el, conf);
455         else
456                 sw.ClosableTab.superclass.constructor.call(this, el);
457
458         this.setAttributeConfig("TabView", {
459         value: parent
460     });
461         this.get("labelEl").parentNode.href = "javascript:void(0);";
462 }
463
464 YAHOO.extend(sw.ClosableTab, YAHOO.widget.Tab, {
465         close : function () {
466                 this.closeEvent.fire();
467                 var parent = this.get("TabView");
468                 parent.removeTab(this);
469         },
470
471         initAttributes: function(attr) {
472                 sw.ClosableTab.superclass.initAttributes.call(this, attr);
473
474                 /**
475                  * The message to display when closing the tab
476          * @attribute closeMsg
477          * @type String
478                  */
479                 this.setAttributeConfig("closeMsg", {
480                         value: attr.closeMsg || ""
481                 });
482
483                 /**
484          * The tab's label text (or innerHTML).
485          * @attribute label
486          * @type String
487          */
488         this.setAttributeConfig("label", {
489             value: attr.label || this._getLabel(),
490             method: function(value) {
491                 var labelEl = this.get("labelEl");
492                 if (!labelEl) { // create if needed
493                     this.set(LABEL_EL, this._createLabelEl());
494                 }
495
496                 labelEl.innerHTML = value;
497
498                 var closeButton = document.createElement('a');
499                 closeButton.href = "javascript:void(0);";
500                 Dom.addClass(closeButton, "sugar-tab-close");
501                 Event.addListener(closeButton, "click", function(e, tab){
502                                 if (tab.get("closeMsg") != "")
503                                 {
504                                         if (confirm(tab.get("closeMsg"))) {
505                                                         tab.close();
506                                                 }
507                                 }
508                                 else {
509                                                 tab.close();
510                                         }
511                         },
512                         this
513                 );
514                 labelEl.appendChild(closeButton);
515             }
516         });
517         }
518 });
519
520
521
522 /**
523  * The sugar Tree is a YUI tree with node construction based on AJAX data built in.
524  */
525 sw.Tree = function (parentEl, baseRequestParams, rootParams) {
526         this.baseRequestParams = baseRequestParams;
527         sw.Tree.superclass.constructor.call(this, parentEl);
528         if (rootParams) {
529                 if (typeof rootParams == "string")
530                         this.sendTreeNodeDataRequest(this.getRoot(), rootParams);
531                 else
532                         this.sendTreeNodeDataRequest(this.getRoot(), "");
533         }
534 }
535
536 YAHOO.extend(sw.Tree, YAHOO.widget.TreeView, {
537         sendTreeNodeDataRequest: function(parentNode, params){
538                 YAHOO.util.Connect.asyncRequest('POST', 'index.php', {
539                         success: this.handleTreeNodeDataRequest,
540                         argument: {
541                                 parentNode: parentNode
542                         },
543                         scope: this
544                 }, this.baseRequestParams + params);
545         },
546         handleTreeNodeDataRequest : function(o) {
547                 var parentNode = o.argument.parentNode;
548                 //parent.tree.removeChildren(parentNode);
549                 var resp = YAHOO.lang.JSON.parse(o.responseText);
550                 if (resp.tree_data.nodes) {
551                         for (var i = 0; i < resp.tree_data.nodes.length; i++) {
552                                 var newChild = this.buildTreeNodeRecursive(resp.tree_data.nodes[i], parentNode);
553                         }
554                 }
555                 parentNode.tree.draw();
556         },
557
558         buildTreeNodeRecursive : function(nodeData, parentNode) {
559                 nodeData.label = nodeData.text;
560                 var node = new YAHOO.widget.TextNode(nodeData, parentNode, nodeData.expanded);
561                 if (typeof(nodeData.children) == 'object') {
562                         for (var i = 0; i < nodeData.children.length; i++) {
563                                 this.buildTreeNodeRecursive(nodeData.children[i], node);
564                         }
565                 }
566                 return node;
567         }
568 });
569
570
571 /**
572  * A 1/2 second fade-in animation.
573  * @class TVSlideIn
574  * @constructor
575  * @param el {HTMLElement} the element to animate
576  * @param callback {function} function to invoke when the animation is finished
577  */
578 YAHOO.widget.TVSlideIn = function(el, callback) {
579     /**
580      * The element to animate
581      * @property el
582      * @type HTMLElement
583      */
584     this.el = el;
585
586     /**
587      * the callback to invoke when the animation is complete
588      * @property callback
589      * @type function
590      */
591     this.callback = callback;
592
593     this.logger = new YAHOO.widget.LogWriter(this.toString());
594 };
595
596 YAHOO.widget.TVSlideIn.prototype = {
597     /**
598      * Performs the animation
599      * @method animate
600      */
601     animate: function() {
602         var tvanim = this;
603
604         var s = this.el.style;
605         s.height = "";
606         s.display = "";
607         s.overflow = "hidden";
608
609         var th = this.el.clientHeight;
610         s.height = "0px";
611
612         var dur = 0.4;
613         var a = new YAHOO.util.Anim(this.el, {height: {from: 0, to: th, unit:"px"}}, dur);
614         a.onComplete.subscribe( function() { tvanim.onComplete(); } );
615         a.animate();
616     },
617
618     /**
619      * Clean up and invoke callback
620      * @method onComplete
621      */
622     onComplete: function() {
623         this.el.style.overflow = "";
624         this.el.style.height = "";
625         this.callback();
626     },
627
628     /**
629      * toString
630      * @method toString
631      * @return {string} the string representation of the instance
632      */
633     toString: function() {
634         return "TVSlideIn";
635     }
636 };
637
638 /**
639  * A 1/2 second fade out animation.
640  * @class TVSlideOut
641  * @constructor
642  * @param el {HTMLElement} the element to animate
643  * @param callback {Function} function to invoke when the animation is finished
644  */
645 YAHOO.widget.TVSlideOut = function(el, callback) {
646     /**
647      * The element to animate
648      * @property el
649      * @type HTMLElement
650      */
651     this.el = el;
652
653     /**
654      * the callback to invoke when the animation is complete
655      * @property callback
656      * @type function
657      */
658     this.callback = callback;
659
660     this.logger = new YAHOO.widget.LogWriter(this.toString());
661 };
662
663 YAHOO.widget.TVSlideOut.prototype = {
664     /**
665      * Performs the animation
666      * @method animate
667      */
668     animate: function() {
669         var tvanim = this;
670         var dur = 0.4;
671         var th = this.el.clientHeight;
672         this.el.style.overflow = "hidden";
673         var a = new YAHOO.util.Anim(this.el, {height: {from: th, to: 0, unit:"px"}}, dur);
674         a.onComplete.subscribe( function() { tvanim.onComplete(); } );
675         a.animate();
676     },
677
678     /**
679      * Clean up and invoke callback
680      * @method onComplete
681      */
682     onComplete: function() {
683         var s = this.el.style;
684         s.display = "none";
685         this.el.style.overflow = "";
686         this.el.style.height = "";
687         this.callback();
688     },
689
690     /**
691      * toString
692      * @method toString
693      * @return {string} the string representation of the instance
694      */
695     toString: function() {
696         return "TVSlideOut";
697     }
698 };
699
700 })();
701
702
703 /**
704  * Problem: SODA tests rely on grabbing report field names by IDs. However,
705  * these ID's don't always seem to be consistent as they are auto-generated by YUI.
706  * Here I have replaced the constructor to set the id of the row element to the modulename
707  * concatenated with the fieldname if the report flag has been set, otherwise proceed as normal.
708  *
709  * Peter D.
710  */
711 (function() {
712     var temp = YAHOO.widget.Record.prototype;
713     YAHOO.widget.Record = function(oLiteral) {
714         this._nCount = YAHOO.widget.Record._nCount;
715
716         YAHOO.widget.Record._nCount++;
717         this._oData = {};
718         if (YAHOO.lang.isObject(oLiteral)) {
719             for (var sKey in oLiteral) {
720                 if (YAHOO.lang.hasOwnProperty(oLiteral, sKey)) {
721                     this._oData[sKey] = oLiteral[sKey];
722                 }
723             }
724         }
725
726         if (SUGAR.reports && SUGAR.reports.overrideRecord) {
727             this._sId = this._oData.module_name + "_" + this._oData.field_name;
728         } else {
729             this._sId = Dom.generateId(null, "yui-rec"); //"yui-rec" + this._nCount;
730         }
731     };
732     YAHOO.widget.Record._nCount = 0;
733     YAHOO.widget.Record.prototype = temp;
734 })();