initial import from onelab svn codebase
[plewww.git] / misc / drupal.js
1 // $Id: drupal.js 144 2007-03-28 07:52:20Z thierry $
2
3 /**
4  * Only enable Javascript functionality if all required features are supported.
5  */
6 function isJsEnabled() {
7   if (typeof document.jsEnabled == 'undefined') {
8     // Note: ! casts to boolean implicitly.
9     document.jsEnabled = !(
10      !document.getElementsByTagName ||
11      !document.createElement        ||
12      !document.createTextNode       ||
13      !document.documentElement      ||
14      !document.getElementById);
15   }
16   return document.jsEnabled;
17 }
18
19 // Global Killswitch on the <html> element
20 if (isJsEnabled()) {
21   document.documentElement.className = 'js';
22 }
23
24 /**
25  * Make IE's XMLHTTP object accessible through XMLHttpRequest()
26  */
27 if (typeof XMLHttpRequest == 'undefined') {
28   XMLHttpRequest = function () {
29     var msxmls = ['MSXML3', 'MSXML2', 'Microsoft']
30     for (var i=0; i < msxmls.length; i++) {
31       try {
32         return new ActiveXObject(msxmls[i]+'.XMLHTTP')
33       }
34       catch (e) { }
35     }
36     throw new Error("No XML component installed!");
37   }
38 }
39
40 /**
41  * Creates an HTTP GET request and sends the response to the callback function.
42  *
43  * Note that dynamic arguments in the URI should be escaped with encodeURIComponent().
44  */
45 function HTTPGet(uri, callbackFunction, callbackParameter) {
46   var xmlHttp = new XMLHttpRequest();
47   var bAsync = true;
48   if (!callbackFunction) {
49     bAsync = false;
50   }
51   xmlHttp.open('GET', uri, bAsync);
52   xmlHttp.send(null);
53
54   if (bAsync) {
55     if (callbackFunction) {
56       xmlHttp.onreadystatechange = function() {
57         if (xmlHttp.readyState == 4) {
58           callbackFunction(xmlHttp.responseText, xmlHttp, callbackParameter);
59         }
60       }
61     }
62     return xmlHttp;
63   }
64   else {
65     return xmlHttp.responseText;
66   }
67 }
68
69 /**
70  * Creates an HTTP POST request and sends the response to the callback function
71  *
72  * Note: passing null or undefined for 'object' makes the request fail in Opera 8.
73  *       Pass an empty string instead.
74  */
75 function HTTPPost(uri, callbackFunction, callbackParameter, object) {
76   var xmlHttp = new XMLHttpRequest();
77   var bAsync = true;
78   if (!callbackFunction) {
79     bAsync = false;
80   }
81   xmlHttp.open('POST', uri, bAsync);
82
83   var toSend = '';
84   if (typeof object == 'object') {
85     xmlHttp.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
86     for (var i in object) {
87       toSend += (toSend ? '&' : '') + i + '=' + encodeURIComponent(object[i]);
88     }
89   }
90   else {
91     toSend = object;
92   }
93   xmlHttp.send(toSend);
94
95   if (bAsync) {
96     if (callbackFunction) {
97       xmlHttp.onreadystatechange = function() {
98         if (xmlHttp.readyState == 4) {
99           callbackFunction(xmlHttp.responseText, xmlHttp, callbackParameter);
100         }
101       }
102     }
103     return xmlHttp;
104   }
105   else {
106     return xmlHttp.responseText;
107   }
108 }
109
110 /**
111  * Redirects a button's form submission to a hidden iframe and displays the result
112  * in a given wrapper. The iframe should contain a call to
113  * window.parent.iframeHandler() after submission.
114  */
115 function redirectFormButton(uri, button, handler) {
116   // (Re)create an iframe to target.
117   createIframe();
118
119   // Trap the button
120   button.onmouseover = button.onfocus = function() {
121     button.onclick = function() {
122       // Prepare variables for use in anonymous function.
123       var button = this;
124       var action = button.form.action;
125       var target = button.form.target;
126
127       // Redirect form submission
128       this.form.action = uri;
129       this.form.target = 'redirect-target';
130
131       handler.onsubmit();
132
133       // Set iframe handler for later
134       window.iframeHandler = function () {
135         var iframe = $('redirect-target');
136         // Restore form submission
137         button.form.action = action;
138         button.form.target = target;
139
140         // Get response from iframe body
141         try {
142           response = (iframe.contentWindow || iframe.contentDocument || iframe).document.body.innerHTML;
143           // Firefox 1.0.x hack: Remove (corrupted) control characters
144           response = response.replace(/[\f\n\r\t]/g, ' ');
145           if (window.opera) {
146             // Opera-hack: it returns innerHTML sanitized.
147             response = response.replace(/&quot;/g, '"');
148           }
149         }
150         catch (e) {
151           response = null;
152         }
153
154         $('redirect-target').onload = null;
155         $('redirect-target').src = 'about:blank';
156
157         response = parseJson(response);
158         // Check response code
159         if (response.status == 0) {
160           handler.onerror(response.data);
161           return;
162         }
163         handler.oncomplete(response.data);
164       }
165
166       return true;
167     }
168   }
169   button.onmouseout = button.onblur = function() {
170     button.onclick = null;
171   }
172 }
173
174 /**
175  * Adds a function to the window onload event
176  */
177 function addLoadEvent(func) {
178   var oldOnload = window.onload;
179   if (typeof window.onload != 'function') {
180     window.onload = func;
181   }
182   else {
183     window.onload = function() {
184       oldOnload();
185       func();
186     }
187   }
188 }
189
190 /**
191  * Adds a function to a given form's submit event
192  */
193 function addSubmitEvent(form, func) {
194   var oldSubmit = form.onsubmit;
195   if (typeof oldSubmit != 'function') {
196     form.onsubmit = func;
197   }
198   else {
199     form.onsubmit = function() {
200       return oldSubmit() && func();
201     }
202   }
203 }
204
205 /**
206  * Retrieves the absolute position of an element on the screen
207  */
208 function absolutePosition(el) {
209   var sLeft = 0, sTop = 0;
210   var isDiv = /^div$/i.test(el.tagName);
211   if (isDiv && el.scrollLeft) {
212     sLeft = el.scrollLeft;
213   }
214   if (isDiv && el.scrollTop) {
215     sTop = el.scrollTop;
216   }
217   var r = { x: el.offsetLeft - sLeft, y: el.offsetTop - sTop };
218   if (el.offsetParent) {
219     var tmp = absolutePosition(el.offsetParent);
220     r.x += tmp.x;
221     r.y += tmp.y;
222   }
223   return r;
224 };
225
226 function dimensions(el) {
227   return { width: el.offsetWidth, height: el.offsetHeight };
228 }
229
230 /**
231  * Returns true if an element has a specified class name
232  */
233 function hasClass(node, className) {
234   if (node.className == className) {
235     return true;
236   }
237   var reg = new RegExp('(^| )'+ className +'($| )')
238   if (reg.test(node.className)) {
239     return true;
240   }
241   return false;
242 }
243
244 /**
245  * Adds a class name to an element
246  */
247 function addClass(node, className) {
248   if (hasClass(node, className)) {
249     return false;
250   }
251   node.className += ' '+ className;
252   return true;
253 }
254
255 /**
256  * Removes a class name from an element
257  */
258 function removeClass(node, className) {
259   if (!hasClass(node, className)) {
260     return false;
261   }
262   // Replaces words surrounded with whitespace or at a string border with a space. Prevents multiple class names from being glued together.
263   node.className = eregReplace('(^|\\s+)'+ className +'($|\\s+)', ' ', node.className);
264   return true;
265 }
266
267 /**
268  * Toggles a class name on or off for an element
269  */
270 function toggleClass(node, className) {
271   if (!removeClass(node, className) && !addClass(node, className)) {
272     return false;
273   }
274   return true;
275 }
276
277 /**
278  * Emulate PHP's ereg_replace function in javascript
279  */
280 function eregReplace(search, replace, subject) {
281   return subject.replace(new RegExp(search,'g'), replace);
282 }
283
284 /**
285  * Removes an element from the page
286  */
287 function removeNode(node) {
288   if (typeof node == 'string') {
289     node = $(node);
290   }
291   if (node && node.parentNode) {
292     return node.parentNode.removeChild(node);
293   }
294   else {
295     return false;
296   }
297 }
298
299 /**
300  * Prevents an event from propagating.
301  */
302 function stopEvent(event) {
303   if (event.preventDefault) {
304     event.preventDefault();
305     event.stopPropagation();
306   }
307   else {
308     event.returnValue = false;
309     event.cancelBubble = true;
310   }
311 }
312
313 /**
314  * Parse a JSON response.
315  *
316  * The result is either the JSON object, or an object with 'status' 0 and 'data' an error message.
317  */
318 function parseJson(data) {
319   if (data.substring(0,1) != '{') {
320     return { status: 0, data: data.length ? data : 'Unspecified error' };
321   }
322   return eval('(' + data + ');');
323 }
324
325 /**
326  * Create an invisible iframe for form submissions.
327  */
328 function createIframe() {
329   // Delete any previous iframe
330   deleteIframe();
331   // Note: some browsers require the literal name/id attributes on the tag,
332   // some want them set through JS. We do both.
333   window.iframeHandler = function () {};
334   var div = document.createElement('div');
335   div.id = 'redirect-holder';
336   div.innerHTML = '<iframe name="redirect-target" id="redirect-target" class="redirect" onload="window.iframeHandler();"></iframe>';
337   var iframe = div.firstChild;
338   with (iframe) {
339     name = 'redirect-target';
340     setAttribute('name', 'redirect-target');
341     id = 'redirect-target';
342   }
343   with (iframe.style) {
344     position = 'absolute';
345     height = '1px';
346     width = '1px';
347     visibility = 'hidden';
348   }
349   document.body.appendChild(div);
350 }
351
352 /**
353  * Delete the invisible iframe for form submissions.
354  */
355 function deleteIframe() {
356   var holder = $('redirect-holder');
357   if (holder != null) {
358     removeNode(holder);
359   }
360 }
361
362 /**
363  * Wrapper around document.getElementById().
364  */
365 function $(id) {
366   return document.getElementById(id);
367 }