]> dev.renevier.net Git - syj.git/blob - public/js/utils.js
d59a112736ce89d77e79b00c78b9de699c3f7e4c
[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 Deck = Class.create({
34     initialize: function(elt, options) {
35         this.element = $(elt);
36         this.index = null;
37         this.setIndex(parseInt(this.element.readAttribute("selectedindex") || 0, 10));
38     },
39     setIndex: function(idx) {
40         if (idx === this.index) {
41             return;
42         }
43
44         var childs = this.element.childElements();
45         if (childs.length === 0) {
46             this.index = -1;
47             return;
48         }
49         idx = Math.max(0, idx);
50         idx = Math.min(childs.length - 1, idx);
51
52         childs.each(function(item, i) {
53             if (idx === i) {
54                 item.show();
55             } else {
56                 item.hide();
57             }
58         });
59         this.index = idx;
60     },
61     getIndex: function() {
62         return this.index;
63     }
64 });
65
66 Element.addMethods({
67     highlight: function(element, color, timeout) {
68         var current;
69         if (typeof timeout === "undefined") {
70             timeout = 0.3;
71         }
72         current = element.getStyle('backgroundColor');
73         Element.setStyle(element, {'backgroundColor': color});
74         Element.setStyle.delay(timeout, element, {'backgroundColor': current});
75         return element;
76     }
77 });
78
79 Ajax.TimedRequest = Class.create(Ajax.Request, {
80     timeout: null,
81     delay: null,
82
83     abort: function() {
84         // see http://blog.pothoven.net/2007/12/aborting-ajax-requests-for-prototypejs.html
85         this.transport.onreadystatechange = Prototype.emptyFunction;
86         this.transport.abort();
87         Ajax.activeRequestCount--;
88     },
89
90     initialize: function($super, url, delay, options) {
91         this.delay = delay;
92         if (!options) {
93             options = {};
94         }
95
96         options.onSuccess = options.onSuccess &&
97             options.onSuccess.wrap(function(proceed, transport, json) {
98             if (this.timeout) {
99                 window.clearTimeout(this.timeout);
100                 this.timeout = null;
101             }
102             if (transport.getStatus() === 0) {
103                 this.options.onFailure(transport, json);
104             } else {
105                 proceed(transport, json);
106             }
107         }).bind(this);
108
109         options.onFailure = options.onFailure &&
110             options.onFailure.wrap(function(proceed, transport, json) {
111             if (this.timeout) {
112                 window.clearTimeout(this.timeout);
113                 this.timeout = null;
114             }
115             proceed(transport, json);
116         }).bind(this);
117
118         $super(url, options);
119     },
120
121     request: function($super, url) {
122         this.timeout = function() {
123             if (this.options.onFailure) {
124                 this.options.onFailure(null);
125             }
126             this.abort();
127         }.bind(this).delay(this.delay);
128         $super(url);
129     }
130 });
131
132 Ajax.Responders.register({
133     // needed for Ajax.TimedRequest.abort to work: see
134     // http://blog.pothoven.net/2007/12/aborting-ajax-requests-for-prototypejs.html
135     // again
136     onComplete: function() {
137         Ajax.activeRequestCount--;
138         if (Ajax.activeRequestCount < 0) {
139             Ajax.activeRequestCount = 0;
140         }
141     }
142 });
143
144 // wrapper around Form.request that sets up the submit listener, stops the
145 // submit event, calls presubmit function, calls Form.request and calls a
146 // postsubmit function
147 Element.addMethods('form', {
148     ajaxize : function(form, options) {
149         var reqoptions;
150
151         options = Object.clone(options || {});
152
153         $(form).observe('submit', function(evt) {
154             evt.stop(); // cancel form submission
155
156             reqoptions = Object.clone(options);
157             delete(reqoptions.presubmit);
158             delete(reqoptions.postsubmit);
159             delete(reqoptions.delay);
160
161             if (Object.isFunction(options.presubmit)) {
162                 if (options.presubmit(this) === false) {
163                     return;
164                 }
165             }
166
167             var params = reqoptions.parameters, action = this.readAttribute('action') || '';
168
169             if (action.blank()) {
170                 action = window.location.href;
171             }
172             reqoptions.parameters = this.serialize(true);
173
174             if (params) {
175                 if (Object.isString(params)) {
176                     params = params.toQueryParams();
177                 }
178                 Object.extend(reqoptions.parameters, params);
179             }
180
181             if (this.hasAttribute('method') && !reqoptions.method) {
182                 reqoptions.method = this.method;
183             }
184
185             new Ajax.TimedRequest(action, options.delay || 20, reqoptions);
186
187             if (Object.isFunction(options.postsubmit)) {
188                 options.postsubmit(this);
189             }
190         });
191     },
192
193     setfocus: function(form) {
194         var tofocus, error;
195
196         tofocus = null;
197         error = form.down('.error');
198         if (error) {
199             tofocus = error.previous('input,textarea');
200         } else {
201             tofocus = form.down('input:not([readonly],[disabled]),textarea:not([readonly][disabled])');
202         }
203         if (tofocus) {
204             if (error && (typeof tofocus.highlight === "function")) {
205                 tofocus.highlight('#F08080');
206             }
207             tofocus.activate();
208         }
209     },
210
211     checkEmptyElements: function(form, errorMessage) {
212         var results = [];
213         form.select('.required').each(function(elt) {
214             var id = elt.getAttribute('for'), control = $(id);
215             if (!control) {
216                 return;
217             }
218             if (!control.check(function() {
219                     return !this.value.strip().empty();
220                 }, errorMessage)) {
221                 results.push(control);
222             }
223         });
224         return results;
225     }
226 });
227
228 Element.addMethods(['input', 'textarea'], {
229     check: function(control, callback, errorMessage) {
230         if (callback.call(control)) {
231             return true;
232         }
233         control.insert({
234             after: new Element("div", {className: 'error'}).update(errorMessage)
235         });
236         return false;
237     },
238
239     observe : Element.Methods.observe.wrap(function(proceed, element, eventName, handler) {
240         if (eventName === "contentchange") {
241             proceed(element, 'keyup', function(evt) {
242                 if (evt.keyCode === 13) {
243                     return;
244                 }
245                 handler.apply(null, arguments);
246             });
247             proceed(element, 'paste', handler);
248             return proceed(element, 'change', handler);
249         }
250         return proceed(element, eventName, handler);
251     }),
252
253     timedobserve: function(element, callback, delay) {
254         var timeout = null, initialvalue = element.value;
255
256         if (typeof delay !== "number") {
257             delay = 0.5;
258         }
259         delay = delay * 1000;
260
261         var canceltimer = function() {
262             if (timeout) {
263                 clearTimeout(timeout);
264                 timeout = null;
265             }
266         };
267         var resettimer = function() {
268             canceltimer();
269             timeout = setTimeout(triggercallback, delay);
270         };
271         var triggercallback = function() {
272             canceltimer();
273             if (initialvalue !== element.value) {
274                 initialvalue = element.value;
275                 callback.call(element);
276             }
277         };
278
279         element.observe('blur', triggercallback).
280              observe('keyup', resettimer).
281              observe('paste', resettimer);
282         return element;
283     }
284 });
285
286 Element.addMethods('div', {
287     setMessage: function(div, message, status) {
288         div.clearMessages();
289         if (status) {
290             div.setMessageStatus(status);
291         }
292         if (message) {
293             div.addMessage(message);
294         }
295         return div;
296     },
297
298     clearMessages: function(div) {
299         var node = div.firstChild, nextNode;
300
301         while (node) {
302             nextNode = node.nextSibling;
303             if (node.nodeType === 3 || node.tagName.toLowerCase() === 'br') {
304                 div.removeChild(node);
305             }
306                 node = nextNode;
307         }
308
309         return div;
310     },
311
312     addMessage: function(div, message) {
313         var node = (div.ownerDocument || document).createTextNode(message);
314
315         if ($A(div.childNodes).filter(function(node) {
316                 return (node.nodeType === 3 || node.tagName.toLowerCase() === 'br');
317              }).length) {
318             div.insert(new Element('br'));
319         }
320
321         div.appendChild(node);
322         return div.show();
323     },
324
325     setMessageStatus: function(div, status) {
326         return div.removeClassName('error').
327                 removeClassName('warn').
328                 removeClassName('info').
329                 removeClassName('success').
330                 addClassName(status);
331     }
332 });