]> dev.renevier.net Git - syj.git/blob - public/js/utils.js
disable form during request in Ajaxize
[syj.git] / public / js / utils.js
1 /*  This file is part of Syj, Copyright (c) 2010 Arnaud Renevier,
2     and is published under the AGPL license. */
3
4 var CloseBtn = Class.create({
5     initialize: function(elt, options) {
6         var btn, imgsrc, style;
7
8         elt = $(elt);
9         if (!elt) {
10             return;
11         }
12
13         style = Object.extend({
14             float: "right",
15             margin: "2px",
16             fontWeight: "bold",
17             padding: "0px"
18         }, typeof options === "object" ? options.style: {});
19
20         imgsrc = (options && options.closeBtnSrc) || "icons/close.png";
21         btn = new Element("input", { type: "image", src: imgsrc, alt: "X"}).setStyle(style);
22         elt.insert({top: btn});
23         btn.observe("click", function(evt) {
24             evt.stop();
25             if (typeof options.callback === "function") {
26                 options.callback.call(elt);
27             }
28             elt.hide();
29         });
30     }
31 });
32
33 var Toggler = Class.create({
34     initialize: function(target, options) {
35         options = Object.extend({}, options);
36         target = $(target).hide();
37
38         var openIcn = options.openIcn || 'icons/bullet_arrow_right.png',
39             closeIcn = options.closeIcn || 'icons/bullet_arrow_down.png';
40
41         this.element = new Element("img", { src: openIcn })
42                               .setStyle({ border: 'none',  // in firefox, in image inside an anchor has a border
43                                         verticalAlign: "middle"});
44
45         this.element.observe('click', function(evt) {
46             if (target.visible()) {
47                 evt.target.src = openIcn;
48                 target.hide();
49             } else {
50                 evt.target.src = closeIcn;
51                 target.show();
52             }
53             evt.stop();
54         });
55
56         if (options.initialShow) {
57             target.show();
58             this.element.src = closeIcn;
59         }
60     }
61 });
62
63 var Deck = Class.create({
64     initialize: function(elt, options) {
65         this.element = $(elt);
66         this.index = null;
67         this.setIndex(parseInt(this.element.readAttribute("selectedindex") || 0, 10));
68     },
69     setIndex: function(idx) {
70         if (idx === this.index) {
71             return;
72         }
73
74         var childs = this.element.childElements();
75         if (childs.length === 0) {
76             this.index = -1;
77             return;
78         }
79         idx = Math.max(0, idx);
80         idx = Math.min(childs.length - 1, idx);
81
82         childs.each(function(item, i) {
83             if (idx === i) {
84                 item.show();
85             } else {
86                 item.hide();
87             }
88         });
89         this.index = idx;
90     },
91     getIndex: function() {
92         return this.index;
93     }
94 });
95
96 Element.addMethods({
97     highlight: function(element, color, timeout) {
98         var current;
99         if (typeof timeout === "undefined") {
100             timeout = 0.3;
101         }
102         current = element.getStyle('backgroundColor');
103         Element.setStyle(element, {'backgroundColor': color});
104         Element.setStyle.delay(timeout, element, {'backgroundColor': current});
105         return element;
106     }
107 });
108
109 Ajax.TimedRequest = Class.create(Ajax.Request, {
110     timeout: null,
111     delay: null,
112
113     abort: function() {
114         // see http://blog.pothoven.net/2007/12/aborting-ajax-requests-for-prototypejs.html
115         this.transport.onreadystatechange = Prototype.emptyFunction;
116         this.transport.abort();
117         Ajax.activeRequestCount--;
118     },
119
120     initialize: function($super, url, delay, options) {
121         this.delay = delay;
122         if (!options) {
123             options = {};
124         }
125
126         options.onSuccess = options.onSuccess &&
127             options.onSuccess.wrap(function(proceed, transport, json) {
128             if (this.timeout) {
129                 window.clearTimeout(this.timeout);
130                 this.timeout = null;
131             }
132             if (transport.getStatus() === 0) {
133                 this.options.onFailure(transport, json);
134             } else {
135                 proceed(transport, json);
136             }
137         }).bind(this);
138
139         options.onFailure = options.onFailure &&
140             options.onFailure.wrap(function(proceed, transport, json) {
141             if (this.timeout) {
142                 window.clearTimeout(this.timeout);
143                 this.timeout = null;
144             }
145             proceed(transport, json);
146         }).bind(this);
147
148         $super(url, options);
149     },
150
151     request: function($super, url) {
152         this.timeout = function() {
153             if (this.options.onFailure) {
154                 this.options.onFailure(null);
155             }
156             this.abort();
157         }.bind(this).delay(this.delay);
158         $super(url);
159     }
160 });
161
162 Ajax.Responders.register({
163     // needed for Ajax.TimedRequest.abort to work: see
164     // http://blog.pothoven.net/2007/12/aborting-ajax-requests-for-prototypejs.html
165     // again
166     onComplete: function() {
167         Ajax.activeRequestCount--;
168         if (Ajax.activeRequestCount < 0) {
169             Ajax.activeRequestCount = 0;
170         }
171     }
172 });
173
174 // wrapper around Form.request that sets up the submit listener, stops the
175 // submit event, calls presubmit function, calls Form.request and calls a
176 // postsubmit function
177 Element.addMethods('form', {
178     ajaxize : function(form, options) {
179         var reqoptions;
180
181         options = Object.clone(options || {});
182
183         $(form).observe('submit', function(evt) {
184             evt.stop(); // cancel form submission
185
186             reqoptions = Object.clone(options);
187             delete(reqoptions.presubmit);
188             delete(reqoptions.postsubmit);
189             delete(reqoptions.delay);
190
191             if (Object.isFunction(options.presubmit)) {
192                 if (options.presubmit(this) === false) {
193                     return;
194                 }
195             }
196
197             var params = reqoptions.parameters, action = this.readAttribute('action') || '';
198
199             if (action.blank()) {
200                 action = window.location.href;
201             }
202             reqoptions.parameters = this.serialize(true);
203
204             if (params) {
205                 if (Object.isString(params)) {
206                     params = params.toQueryParams();
207                 }
208                 Object.extend(reqoptions.parameters, params);
209             }
210
211             if (this.hasAttribute('method') && !reqoptions.method) {
212                 reqoptions.method = this.method;
213             }
214
215             if (reqoptions.onFailure) {
216                 reqoptions.onFailure = reqoptions.onFailure.wrap(function(proceed, transport, json) {
217                     form.enable();
218                     proceed(transport, json);
219                 });
220             } else {
221                 reqoptions.onFailure = function() {
222                     form.enable();
223                 };
224             }
225
226             if (reqoptions.onSuccess) {
227                 reqoptions.onSuccess = reqoptions.onSuccess.wrap(function(proceed, transport, json) {
228                     form.enable();
229                     proceed(transport, json);
230                 });
231             } else {
232                 reqoptions.onSuccess = function() {
233                     form.enable();
234                 };
235             }
236
237             new Ajax.TimedRequest(action, options.delay || 20, reqoptions);
238
239             if (Object.isFunction(options.postsubmit)) {
240                 options.postsubmit(this);
241             }
242             Form.getElements(form).each(function(elt) {
243                 elt.blur();
244                 elt.disable();
245             });
246         });
247     },
248
249     setfocus: function(form) {
250         var tofocus, error;
251
252         tofocus = null;
253         error = form.down('.error');
254         if (error) {
255             tofocus = error.previous('input,textarea');
256         } else {
257             tofocus = form.down('input:not([readonly],[disabled]),textarea:not([readonly][disabled])');
258         }
259         if (tofocus) {
260             if (error && (typeof tofocus.highlight === "function")) {
261                 tofocus.highlight('#F08080');
262             }
263             tofocus.activate();
264         }
265     },
266
267     checkEmptyElements: function(form, errorMessage) {
268         var results = [];
269         form.select('.required').each(function(elt) {
270             var id = elt.getAttribute('for'), control = $(id);
271             if (!control) {
272                 return;
273             }
274             if (!control.check(function() {
275                     return !this.value.strip().empty();
276                 }, errorMessage)) {
277                 results.push(control);
278             }
279         });
280         return results;
281     }
282 });
283
284 Element.addMethods(['input', 'textarea'], {
285     check: function(control, callback, errorMessage) {
286         if (callback.call(control)) {
287             return true;
288         }
289         control.insert({
290             after: new Element("div", {className: 'error'}).update(errorMessage)
291         });
292         return false;
293     },
294
295     observe : Element.Methods.observe.wrap(function(proceed, element, eventName, handler) {
296         if (eventName === "contentchange") {
297             proceed(element, 'keyup', function(evt) {
298                 if (evt.keyCode === 13) {
299                     return;
300                 }
301                 handler.apply(null, arguments);
302             });
303             proceed(element, 'paste', handler);
304             return proceed(element, 'change', handler);
305         }
306         return proceed(element, eventName, handler);
307     }),
308
309     timedobserve: function(element, callback, delay) {
310         var timeout = null, initialvalue = element.value;
311
312         if (typeof delay !== "number") {
313             delay = 0.5;
314         }
315         delay = delay * 1000;
316
317         var canceltimer = function() {
318             if (timeout) {
319                 clearTimeout(timeout);
320                 timeout = null;
321             }
322         };
323         var resettimer = function() {
324             canceltimer();
325             timeout = setTimeout(triggercallback, delay);
326         };
327         var triggercallback = function() {
328             canceltimer();
329             if (initialvalue !== element.value) {
330                 initialvalue = element.value;
331                 callback.call(element);
332             }
333         };
334
335         element.observe('blur', triggercallback).
336              observe('keyup', resettimer).
337              observe('paste', resettimer);
338         return element;
339     }
340 });
341
342 Element.addMethods('div', {
343     setMessage: function(div, message, status) {
344         div.clearMessages();
345         if (status) {
346             div.setMessageStatus(status);
347         }
348         if (message) {
349             div.addMessage(message);
350         }
351         return div;
352     },
353
354     clearMessages: function(div) {
355         var node = div.firstChild, nextNode;
356
357         while (node) {
358             nextNode = node.nextSibling;
359             if (node.nodeType === 3 || node.tagName.toLowerCase() === 'br') {
360                 div.removeChild(node);
361             }
362                 node = nextNode;
363         }
364
365         return div;
366     },
367
368     addMessage: function(div, message) {
369         var node = (div.ownerDocument || document).createTextNode(message);
370
371         if ($A(div.childNodes).filter(function(node) {
372                 return (node.nodeType === 3 || node.tagName.toLowerCase() === 'br');
373              }).length) {
374             div.insert(new Element('br'));
375         }
376
377         div.appendChild(node);
378         return div.show();
379     },
380
381     setMessageStatus: function(div, status) {
382         return div.removeClassName('error').
383                 removeClassName('warn').
384                 removeClassName('info').
385                 removeClassName('success').
386                 addClassName(status);
387     }
388 });