]> dev.renevier.net Git - syj.git/blob - public/js/utils.js
general class for accout info toggler
[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             new Ajax.TimedRequest(action, options.delay || 20, reqoptions);
216
217             if (Object.isFunction(options.postsubmit)) {
218                 options.postsubmit(this);
219             }
220         });
221     },
222
223     setfocus: function(form) {
224         var tofocus, error;
225
226         tofocus = null;
227         error = form.down('.error');
228         if (error) {
229             tofocus = error.previous('input,textarea');
230         } else {
231             tofocus = form.down('input:not([readonly],[disabled]),textarea:not([readonly][disabled])');
232         }
233         if (tofocus) {
234             if (error && (typeof tofocus.highlight === "function")) {
235                 tofocus.highlight('#F08080');
236             }
237             tofocus.activate();
238         }
239     },
240
241     checkEmptyElements: function(form, errorMessage) {
242         var results = [];
243         form.select('.required').each(function(elt) {
244             var id = elt.getAttribute('for'), control = $(id);
245             if (!control) {
246                 return;
247             }
248             if (!control.check(function() {
249                     return !this.value.strip().empty();
250                 }, errorMessage)) {
251                 results.push(control);
252             }
253         });
254         return results;
255     }
256 });
257
258 Element.addMethods(['input', 'textarea'], {
259     check: function(control, callback, errorMessage) {
260         if (callback.call(control)) {
261             return true;
262         }
263         control.insert({
264             after: new Element("div", {className: 'error'}).update(errorMessage)
265         });
266         return false;
267     },
268
269     observe : Element.Methods.observe.wrap(function(proceed, element, eventName, handler) {
270         if (eventName === "contentchange") {
271             proceed(element, 'keyup', function(evt) {
272                 if (evt.keyCode === 13) {
273                     return;
274                 }
275                 handler.apply(null, arguments);
276             });
277             proceed(element, 'paste', handler);
278             return proceed(element, 'change', handler);
279         }
280         return proceed(element, eventName, handler);
281     }),
282
283     timedobserve: function(element, callback, delay) {
284         var timeout = null, initialvalue = element.value;
285
286         if (typeof delay !== "number") {
287             delay = 0.5;
288         }
289         delay = delay * 1000;
290
291         var canceltimer = function() {
292             if (timeout) {
293                 clearTimeout(timeout);
294                 timeout = null;
295             }
296         };
297         var resettimer = function() {
298             canceltimer();
299             timeout = setTimeout(triggercallback, delay);
300         };
301         var triggercallback = function() {
302             canceltimer();
303             if (initialvalue !== element.value) {
304                 initialvalue = element.value;
305                 callback.call(element);
306             }
307         };
308
309         element.observe('blur', triggercallback).
310              observe('keyup', resettimer).
311              observe('paste', resettimer);
312         return element;
313     }
314 });
315
316 Element.addMethods('div', {
317     setMessage: function(div, message, status) {
318         div.clearMessages();
319         if (status) {
320             div.setMessageStatus(status);
321         }
322         if (message) {
323             div.addMessage(message);
324         }
325         return div;
326     },
327
328     clearMessages: function(div) {
329         var node = div.firstChild, nextNode;
330
331         while (node) {
332             nextNode = node.nextSibling;
333             if (node.nodeType === 3 || node.tagName.toLowerCase() === 'br') {
334                 div.removeChild(node);
335             }
336                 node = nextNode;
337         }
338
339         return div;
340     },
341
342     addMessage: function(div, message) {
343         var node = (div.ownerDocument || document).createTextNode(message);
344
345         if ($A(div.childNodes).filter(function(node) {
346                 return (node.nodeType === 3 || node.tagName.toLowerCase() === 'br');
347              }).length) {
348             div.insert(new Element('br'));
349         }
350
351         div.appendChild(node);
352         return div.show();
353     },
354
355     setMessageStatus: function(div, status) {
356         return div.removeClassName('error').
357                 removeClassName('warn').
358                 removeClassName('info').
359                 removeClassName('success').
360                 addClassName(status);
361     }
362 });