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