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