]> dev.renevier.net Git - syj.git/blob - public/js/utils.js
rewrite form.ajaxize to create a separate Ajax.TimedRequest class
[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
89         options.onSuccess = options.onSuccess.wrap(function(proceed, transport, json) {
90             if (this.timeout) {
91                 window.clearTimeout(this.timeout);
92                 this.timeout = null;
93             }
94             if (transport.getStatus() === 0) {
95                 this.options.onFailure(transport, json);
96             } else {
97                 proceed(transport, json);
98             }
99         }).bind(this);
100
101         options.onFailure = options.onFailure.wrap(function(proceed, transport, json) {
102             if (this.timeout) {
103                 window.clearTimeout(this.timeout);
104                 this.timeout = null;
105             }
106             proceed(transport, json);
107         }).bind(this);
108
109         $super(url, options);
110     },
111
112     request: function($super, url) {
113         this.timeout = (function() {
114             this.options.onFailure(null);
115             this.abort();
116         }).bind(this).delay(this.delay);
117         $super(url);
118     }
119 });
120
121 Ajax.Responders.register({
122     // needed for Ajax.TimedRequest.abort to work: see
123     // http://blog.pothoven.net/2007/12/aborting-ajax-requests-for-prototypejs.html
124     // again
125     onComplete: function() {
126         Ajax.activeRequestCount--;
127         if (Ajax.activeRequestCount < 0) {
128             Ajax.activeRequestCount = 0;
129         }
130     }
131 });
132
133 // wrapper around Form.request that sets up the submit listener, stops the
134 // submit event, calls presubmit function, calls Form.request and calls a
135 // postsubmit function
136 Element.addMethods('form', {
137     ajaxize : function(form, options) {
138         var reqoptions;
139
140         options = Object.clone(options || {});
141
142         $(form).observe('submit', function(evt) {
143             evt.stop(); // cancel form submission
144
145             reqoptions = Object.clone(options);
146             delete(reqoptions.presubmit);
147             delete(reqoptions.postsubmit);
148             delete(reqoptions.delay);
149
150             if (Object.isFunction(options.presubmit)) {
151                 if (options.presubmit(this) === false) {
152                     return;
153                 }
154             }
155
156             var params = reqoptions.parameters, action = this.readAttribute('action') || '';
157
158             if (action.blank()) {
159                 action = window.location.href;
160             }
161             reqoptions.parameters = this.serialize(true);
162
163             if (params) {
164                 if (Object.isString(params)) {
165                     params = params.toQueryParams();
166                 }
167                 Object.extend(reqoptions.parameters, params);
168             }
169
170             if (this.hasAttribute('method') && !reqoptions.method) {
171                 reqoptions.method = this.method;
172             }
173
174             new Ajax.TimedRequest(action, options.delay || 20, reqoptions);
175
176             if (Object.isFunction(options.postsubmit)) {
177                 options.postsubmit(this);
178             }
179         });
180     },
181
182     focus: function(form) {
183         var tofocus, error;
184
185         tofocus = null;
186         error = form.down('.error');
187         if (error) {
188             tofocus = error.previous('input,textarea');
189         } else {
190             tofocus = form.down('input:not([readonly],[disabled]),textarea:not([readonly][disabled])');
191         }
192         if (tofocus) {
193             if (error && (typeof tofocus.highlight === "function")) {
194                 tofocus.highlight('#F08080');
195             }
196             tofocus.activate();
197         }
198     },
199
200     checkEmptyElements: function(form, errorMessage) {
201         var results = [];
202         form.select('.required').each(function(elt) {
203             var id = elt.getAttribute('for'), control = $(id);
204             if (!control) {
205                 return;
206             }
207             if (!control.check(function() {
208                     return !this.value.strip().empty();
209                 }, errorMessage)) {
210                 results.push(control);
211             }
212         });
213         return results;
214     }
215 });
216
217 Element.addMethods(['input', 'textarea'], {
218     check: function(control, callback, errorMessage) {
219         if (callback.call(control)) {
220             return true;
221         }
222         control.insert({
223             after: new Element("div", {className: 'error'}).update(errorMessage)
224         });
225         return false;
226     }
227 });
228
229 Element.addMethods('div', {
230     setMessage: function(div, message, status) {
231         div.clearMessages();
232         if (status) {
233             div.setMessageStatus(status);
234         }
235         if (message) {
236             div.addMessage(message);
237         }
238         return div;
239     },
240
241     clearMessages: function(div) {
242         var node = div.firstChild, nextNode;
243
244         while (node) {
245             nextNode = node.nextSibling;
246             if (node.nodeType === 3 || node.tagName.toLowerCase() === 'br') {
247                 div.removeChild(node);
248             }
249                 node = nextNode;
250         }
251
252         return div;
253     },
254
255     addMessage: function(div, message) {
256         var node = (div.ownerDocument || document).createTextNode(message);
257         if (!div.empty()) {
258             div.insert(new Element('br'));
259         }
260         div.appendChild(node);
261         return div.show();
262     },
263
264     setMessageStatus: function(div, status) {
265         return div.removeClassName('error').
266                 removeClassName('warn').
267                 removeClassName('info').
268                 removeClassName('success').
269                 addClassName(status);
270     }
271 });