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