b44eab201481a934180e2f0df8ca99fbc49dad23
[plewww.git] / planetlab / minitabs / minitabs.js
1 /*
2  
3   Animated miniTabs by frequency decoder (http://www.frequency-decoder.com/)
4  
5   Based on an idea by Rob L Glazebrook (http://www.rootarcana.com/test/smartmini/) itself
6   derived from the original idea of Stephen Clark (http://www.sgclark.com/sandbox/minislide/)
7  
8   Adjusted by Thierry Parmentelat -- INRIA - uses only forms rather than <a> tags, for supporting http-POST
9   
10   $Id$
11
12 */
13
14 function plc_debug (txt,value) {
15   window.console.log (txt + ' => ' + value);
16 }  
17
18 function plc_element (txt,elem) {
19   var message=txt + ':';
20   message += ' type=' + elem.nodeName;
21   message += ' id=' + elem.id;
22   message += ' cls=' + elem.className;
23   window.console.log (message);
24 }  
25
26 /* lists all attributes - or the specified one - 
27  * verbose means the attribute value gets printed as well */
28 function plc_introspect (txt,obj,verbose,attribute) {
29   window.console.log ('=== beg intro ' + txt);
30   for (var prop in obj) {
31     if ( (attribute === undefined) || ( prop == attribute ) ) 
32       if (verbose) 
33         window.console.log (prop + '=' + obj[prop]);
34       else
35         window.console.log (prop);
36   }
37   window.console.log ('=== end intro ' + txt);
38 }
39
40 /* I'm done with this - write it ourselves - don't care about perfs so much anyway */
41 /* define getElementsByClassName on Element if missing */
42 function getElementsByClassName (elt,cls) {
43   try {
44     var retval= elt.getElementsByClassName(cls);
45     plc_debug ('getElementsByClassName','used native method');
46     return retval;
47     } catch (err) {
48     plc_debug ('getElementsByClassName','running custom method');
49     var retVal = new Array();
50     var elements = elt.getElementsByTagName("*");
51     for (var i = 0; i < elements.length; i++) {
52       var element = elements[i];
53       var classes = element.className.split(" ");
54       for (var c = 0; c < classes.length; c++) 
55         if (classes[c] == cls) 
56           retVal.push(elements[i]);
57     }
58     return retVal;
59   }
60 }
61
62 var miniTab = {
63  currentTab:     0,
64  activeTab:      0,
65  destX:          0,
66  destW:          0,
67  t:              0,
68  b:              0,
69  c:              0,
70  d:              20,
71  animInterval:   null,
72  sliderObj:      null,
73  aHeight:        0,
74  ul:             [],
75  liArr:          [],
76  inputArr:       [],
77         
78  init: function() {
79
80     miniTab.ul          = document.getElementById("minitabs-list");
81     miniTab.liArr       = miniTab.ul.getElementsByTagName("li");
82     // Thierry: the original impl. relied on <a> links rather than forms 
83     // we use <input>s and there might be hidden ones, so use a class marker instead
84     miniTab.inputArr        = getElementsByClassName(miniTab.ul,"minitabs-submit");
85  
86     for(var i = 0, li; li = miniTab.liArr[i]; i++) {
87       li.onmouseover = miniTab.inputArr[i].onfocus = function(e) {
88         var pos = 0;
89         var elem = this;
90         /* some browsers - firefox - somehow trigger this on <input> */
91         if (this.nodeName != "LI") return;
92         /* move up until we find the 'LI' tag */
93         while(elem.previousSibling) {
94           elem = elem.previousSibling;
95           if(elem.tagName && elem.tagName == "LI") pos++;
96  
97         }
98         miniTab.initSlide(pos);
99       }
100     }
101  
102     miniTab.ul.onmouseout = function(e) {
103       miniTab.initSlide(miniTab.currentTab);
104       miniTab.setActive (miniTab.activeTab,false);
105     };
106  
107     for(var i = 0, a; a = miniTab.inputArr[i]; i++) {
108       if(a.className.search("active") != -1) {
109         miniTab.activeTab = miniTab.currentTab = i;
110       }
111       a.style.borderBottom  = "0px";
112       /*a.style.paddingBottom = "6px";*/
113     }
114  
115     miniTab.slideObj                = miniTab.ul.parentNode.appendChild(document.createElement("div"));
116     miniTab.slideObj.appendChild(document.createTextNode(String.fromCharCode(160)));
117     miniTab.slideObj.id             = "minitabs-sliding";
118     miniTab.slideObj.style.top      = (miniTab.ul.offsetTop + miniTab.liArr[miniTab.activeTab].offsetTop + 
119                                        miniTab.inputArr[miniTab.activeTab].offsetTop) + "px";
120     miniTab.slideObj.style.left     = (miniTab.ul.offsetLeft + miniTab.liArr[miniTab.activeTab].offsetLeft + 
121                                        miniTab.inputArr[miniTab.activeTab].offsetLeft) + "px";
122     miniTab.slideObj.style.width    = miniTab.inputArr[miniTab.activeTab].offsetWidth + "px";
123     miniTab.aHeight                 = (miniTab.ul.offsetTop + miniTab.liArr[miniTab.activeTab].offsetTop + 
124                                        miniTab.inputArr[miniTab.activeTab].offsetTop);
125  
126     miniTab.initSlide(miniTab.activeTab, true);
127  
128     var intervalMethod = function() { miniTab.slideIt(); }
129     miniTab.animInterval = setInterval(intervalMethod,10);
130   },
131  
132  cleanUp: function() {
133     clearInterval(miniTab.animInterval);
134     miniTab.animInterval = null;
135   },
136  
137  initSlide: function(pos, force) {
138     if(!force && pos == miniTab.activeTab) return;
139     miniTab.setActive (miniTab.activeTab,false);
140     miniTab.activeTab = pos;
141     miniTab.setActive (miniTab.activeTab,true);
142     miniTab.initAnim();
143   },
144  
145  setActive: function (pos,active) {
146     var input=getElementsByClassName(miniTab.liArr[pos],"minitabs-submit")[0];
147     var cn=input.className;
148     cn=cn.replace(" active","");
149     if (active) cn += " active";
150     input.className=cn;
151   },
152  
153  initAnim: function() {
154     /* search for the input with type != hidden */
155     var input=getElementsByClassName(miniTab.liArr[miniTab.activeTab],"minitabs-submit")[0];
156     miniTab.destX = parseInt(miniTab.liArr[miniTab.activeTab].offsetLeft + input.offsetLeft + miniTab.ul.offsetLeft);
157     miniTab.destW = parseInt(input.offsetWidth);
158     miniTab.t = 0;
159     miniTab.b = miniTab.slideObj.offsetLeft;
160     miniTab.c = miniTab.destX - miniTab.b;
161  
162     miniTab.bW = miniTab.slideObj.offsetWidth;
163     miniTab.cW = miniTab.destW - miniTab.bW;
164  
165     miniTab.slideObj.style.top = (miniTab.ul.offsetTop + miniTab.liArr[miniTab.activeTab].offsetTop + miniTab.inputArr[miniTab.activeTab].offsetTop) + "px";
166   },
167  
168  slideIt:function() {
169  
170     // Has the browser text size changed?
171     if(miniTab.aHeight != miniTab.ul.offsetTop + miniTab.liArr[miniTab.activeTab].offsetTop + miniTab.inputArr[miniTab.activeTab].offsetTop) {
172       miniTab.initAnim();
173       miniTab.aHeight = miniTab.ul.offsetTop + miniTab.liArr[miniTab.activeTab].offsetTop + miniTab.inputArr[miniTab.activeTab].offsetTop
174     };
175  
176     if(miniTab.t++ < miniTab.d) {
177       var x = miniTab.animate(miniTab.t,miniTab.b,miniTab.c,miniTab.d);
178       var w = miniTab.animate(miniTab.t,miniTab.bW,miniTab.cW,miniTab.d);
179  
180       miniTab.slideObj.style.left = parseInt(x) + "px";
181       miniTab.slideObj.style.width = parseInt(w) + "px";
182     } else {
183       miniTab.slideObj.style.left = miniTab.destX + "px";
184       miniTab.slideObj.style.width = miniTab.destW +"px";
185     }
186   },
187  
188  animate: function(t,b,c,d) {
189     if ((t/=d/2) < 1) return c/2*t*t + b;
190     return -c/2 * ((--t)*(t-2) - 1) + b;
191   },
192
193  submit: function (message) {
194     /* save activeTab before confirmation; some browsers - firefox - send mouseout during confirm .. */
195     var submitTab = this.activeTab;
196     /* ask for confirmation if message is not empty */
197     if (message && ! confirm (message) ) return;
198     this.inputArr[submitTab].parentNode.parentNode.submit();
199   }
200 }
201  
202 window.onload = miniTab.init;
203 window.onunload = miniTab.cleanUp;
204