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