bugfix : on vérifie que le parent implémente bien l'interface d'ordonancement.
[Plinn.git] / skins / ajax_scripts / ajax_form_manager.js
1 // (c) Benoît PIN 2006-2007
2 // http://plinn.org
3 // Licence GPL
4 //
5 //
6
7
8 function FormManager(form, responseTextDest, lazy) {
9 if (form.elements.namedItem("noAjax")) return;
10
11 this.form = form;
12 this.responseTextDest = responseTextDest;
13 this.lazy = lazy;
14 var thisManager = this;
15 this.form.onsubmit = function(evt) { thisManager.submit(evt); };
16 this.form.onclick = function(evt) { thisManager.click(evt); };
17 this.submitButton = null;
18
19 /* raised on form submit */
20 this.onBeforeSubmit = null;
21 /* raised after xmlhttp response */
22 this.onResponseLoad = null;
23 /* raised when the responseText is added inside the main element.
24 * (onResponseLoad may have the default value) */
25 this.onAfterPopulate = null;
26 this.submitButton = null;
27
28 if (this.lazy) {
29 this.fieldTagName;
30 this.liveFormField;
31 this.pendingEvent;
32 this.form.onclick = function(evt){
33 thisManager.replaceElementByField(evt);
34 thisManager.click(evt);
35 };
36 if (browser.isDOM2Event)
37 this.form.onfocus = this.form.onclick;
38 else if (browser.isIE6up)
39 this.form.onfocusin = this.form.onclick;
40 this.onResponseLoad = function(req){ thisManager.restoreField(req); };
41 this.lazyListeners = new Array();
42 }
43 }
44
45 FormManager.prototype.submit = function(evt) {
46 var form = this.form;
47 var thisManager = this;
48
49 var bsMessage; // before submit message
50 if (!this.onBeforeSubmit) {
51 var onBeforeSubmit = form.elements.namedItem("onBeforeSubmit");
52 if (onBeforeSubmit) {
53 if (onBeforeSubmit.length)
54 onBeforeSubmit = onBeforeSubmit[0];
55 this.onBeforeSubmit = eval(onBeforeSubmit.value);
56 bsMessage = this.onBeforeSubmit(thisManager, evt);
57 }
58 }
59 else
60 bsMessage = this.onBeforeSubmit(thisManager, evt);
61
62 if (bsMessage == 'cancelSubmit') {
63 try {disableDefault(evt);}
64 catch (e){}
65 return;
66 }
67
68 if (!this.onResponseLoad) {
69 var onResponseLoad = form.elements.namedItem("onResponseLoad");
70 if (onResponseLoad)
71 this.onResponseLoad = eval(onResponseLoad.value);
72 else
73 this.onResponseLoad = this.loadResponse;
74 }
75
76 var submitButton = this.submitButton;
77 var queryInfo = this.formData2QueryString();
78 var query = queryInfo['query'];
79 this.hasFile = queryInfo['hasFile'];
80
81
82 if (!this.onAfterPopulate) {
83 var onAfterPopulate = form.elements.namedItem("onAfterPopulate");
84 if (onAfterPopulate)
85 this.onAfterPopulate = onAfterPopulate.value;
86 else
87 this.onAfterPopulate = function() {};
88 }
89
90 if (submitButton) {
91 query += submitButton.name + '=' + submitButton.value + '&';
92 }
93
94 if (window.AJAX_CONFIG && (AJAX_CONFIG & 1 == 1)) {
95 if (form.method.toLowerCase() == 'post')
96 this._post(query);
97 else
98 this._get(query)
99 }
100 else
101 this._post(query);
102
103 try {disableDefault(evt);}
104 catch (e){}
105 };
106
107 FormManager.prototype._post = function(query) {
108 // send form by XmlHttpRequest
109 query += "ajax=1";
110
111 var req = new XMLHttpRequest();
112 var thisManager = this;
113 req.onreadystatechange = function() {
114 switch (req.readyState) {
115 case 1 :
116 showProgressImage();
117 break;
118 case 4 :
119 hideProgressImage();
120 if (req.status == 200 || req.status == 204)
121 thisManager.onResponseLoad(req);
122 else
123 alert('Error: ' + req.status);
124 };
125 };
126 var url = this.form.action;
127 req.open("POST", url, true);
128 req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded;charset=utf-8");
129 req.send(query);
130 };
131
132 FormManager.prototype._get = function(query) {
133 // send form by browser location
134 var url = this.form.action;
135 url += '?' + query
136 linkHandler.loadUrl(url);
137 };
138
139
140 FormManager.prototype.click = function(evt) {
141 var target = getTargetedObject(evt);
142 if(target.type == "submit" || target.type == "image") {
143 this.submitButton = target;
144 disablePropagation(evt);
145 }
146 };
147
148 FormManager.prototype.replaceElementByField = function(evt) {
149 evt = getEventObject(evt);
150 var ob = getTargetedObject(evt);
151 var eventType = evt.type;
152 if (eventType == 'focus' || eventType == 'focusin') {
153 if (this.liveFormField && ob.tagName != 'INPUT')
154 this.pendingEvent = [ob, 'click'];
155 return;
156 }
157 var fieldName = ob.getAttribute('id');
158 if (fieldName) {
159 this.fieldTagName = ob.tagName;
160 var tabIndex = ob.tabIndex;
161 var text;
162 if (ob.firstChild && ob.firstChild.className == 'hidden_value')
163 text = ob.firstChild.innerHTML;
164 else
165 text = ob.innerHTML;
166 disablePropagation(evt);
167 switch (ob.tagName) {
168 case 'SPAN' :
169 // create input element
170 var inputText = document.createElement("input");
171 inputText.setAttribute("type", "text");
172 text = text.replace(/\n/g, ' ');
173 text = text.replace(/\s+/g, ' ');
174 text = text.replace(/^ /, '');
175 text = text.replace(/ $/, '');
176 inputText.setAttribute("value", text);
177 var inputWidth = text.length / 1.9;
178 inputWidth = (inputWidth > 5) ? inputWidth : 5;
179 inputText.style.width = inputWidth + 'em';
180 //inputText.setAttribute("size", text.length);
181
182 // replacement
183 var parent = ob.parentNode;
184 parent.replaceChild(inputText, ob);
185
186 inputText.focus();
187 inputText.select();
188 inputText.setAttribute('name', fieldName);
189 inputText.tabIndex = tabIndex;
190 inputText.className = 'live_field';
191 this.liveFormField = inputText;
192 var thisManager = this;
193 this.lazyListeners.push({'element': inputText, 'eventName' : 'blur', 'handler': function(){ thisManager.submit();}});
194 this.lazyListeners.push({'element': inputText, 'eventName' : 'keypress', 'handler': function(evt){ thisManager._fitField(evt);}});
195 this._addLazyListeners();
196 break;
197
198 case 'DIV' :
199 case 'P' :
200 // create textarea
201 var ta = document.createElement('textarea');
202 ta.style.display = 'block';
203 ta.className = 'live_field';
204 text = text.replace(/^\s*/, '');
205 text = text.replace(/\s*$/, '');
206 ta.value = text;
207
208 // replacement
209 var parent = ob.parentNode;
210 parent.replaceChild(ta, ob);
211
212 ta.focus();
213 ta.select();
214 ta.setAttribute('name', fieldName);
215 ta.tabIndex = tabIndex;
216 this.liveFormField = ta;
217 var thisManager = this;
218 this.lazyListeners.push({'element': ta, 'eventName' : 'blur', 'handler': function(){ thisManager.submit();}});
219 this._addLazyListeners();
220 break;
221
222
223 };
224 }
225 };
226
227 FormManager.prototype._addLazyListeners = function() {
228 for (var i=0 ; i<this.lazyListeners.length ; i++) {
229 var handlerInfo = this.lazyListeners[i];
230 addListener(handlerInfo['element'], handlerInfo['eventName'], handlerInfo['handler']);
231 }
232 };
233
234 FormManager.prototype._removeLazyListeners = function() {
235 for (var i=0 ; i<this.lazyListeners.length ; i++) {
236 var handlerInfo = this.lazyListeners[i];
237 removeListener(handlerInfo['element'], handlerInfo['eventName'], handlerInfo['handler']);
238 }
239 };
240
241
242 FormManager.prototype.restoreField = function(req) {
243 var text;
244 var input = this.liveFormField;
245 if (req.status == 200) {
246 if (req.getResponseHeader('Content-Type').indexOf('text/xml') != -1) {
247 var out = '..........';
248 if (req.responseXML.documentElement.firstChild)
249 out = req.responseXML.documentElement.firstChild.nodeValue;
250
251 switch (req.responseXML.documentElement.nodeName) {
252 case 'computedField':
253 text = out;
254 break;
255 case 'error':
256 this._removeLazyListeners();
257 alert(out);
258 this.pendingEvent = null;
259 input.focus();
260 this._addLazyListeners();
261 return false;
262 break;
263 }
264 }
265 else {
266 text = req.responseText;
267 }
268 }
269 else
270 text = '';
271
272 if (!text.match(/\w/))
273 text = '..........';
274
275 var field = document.createElement(this.fieldTagName);
276 field.innerHTML = text;
277 field.setAttribute('id', input.getAttribute('name'));
278 field.className = 'editable';
279 field.tabIndex = input.tabIndex;
280
281 var parent = input.parentNode;
282 parent.replaceChild(field, input);
283 this.liveFormField = null;
284
285 if (this.pendingEvent)
286 raiseMouseEvent(this.pendingEvent[0], this.pendingEvent[1]);
287 return true;
288 };
289
290
291 FormManager.prototype.formData2QueryString = function() {
292 // http://www.onlamp.com/pub/a/onlamp/2005/05/19/xmlhttprequest.html
293 var form = this.form;
294 var strSubmit = '', formElem, elements;
295 var hasFile = false;
296
297 if (!this.lazy)
298 elements = form.elements;
299 else {
300 elements = new Array();
301 var formElements = form.elements;
302 for (var i = 0; i < formElements.length; i++) {
303 formElem = formElements[i];
304 switch (formElem.type) {
305 case 'hidden':
306 elements.push(formElem);
307 break;
308 default :
309 if (formElem == this.liveFormField)
310 elements.push(formElem);
311 };
312 }
313 }
314
315 for (var i = 0; i < elements.length; i++) {
316 formElem = elements[i];
317 switch (formElem.type) {
318 // text, select, hidden, password, textarea elements
319 case 'text':
320 case 'select-one':
321 case 'hidden':
322 case 'password':
323 case 'textarea':
324 strSubmit += formElem.name + '=' + encodeURIComponent(formElem.value) + '&';
325 break;
326 case 'radio':
327 case 'checkbox':
328 if (formElem.checked)
329 strSubmit += formElem.name + '=' + encodeURIComponent(formElem.value) + '&';
330 break;
331 case 'select-multiple':
332 var options = formElem.getElementsByTagName("OPTION"), option;
333 for (var j = 0 ; j < options.length ; j++) {
334 option = options[j];
335 if (option.selected)
336 strSubmit += formElem.name + '=' + encodeURIComponent(option.value) + '&';
337 }
338 break;
339 case 'file':
340 if (formElem.value)
341 hasFile = true;
342 break;
343 };
344 }
345 return {'query' : strSubmit, 'hasFile' : hasFile};
346 };
347
348 FormManager.prototype.loadResponse = function(req) {
349 if (req.getResponseHeader('Content-Type').indexOf('text/xml') != -1) {
350 switch(req.responseXML.documentElement.nodeName) {
351 case 'fragments' :
352 if (this.hasFile) {
353 var sb = this.submitButton;
354 if (sb) {
355 var h = document.createElement('input');
356 h.type = 'hidden';
357 h.name = sb.name;
358 h.value = sb.value;
359 this.form.appendChild(h);
360 }
361
362 this.form.submit();
363 return;
364 }
365 var fragments = req.responseXML.documentElement.childNodes;
366 var fragment, dest, scripts;
367 for (var i=0 ; i<fragments.length ; i++) {
368 fragment = fragments[i];
369 if (fragment.nodeName == 'fragment') {
370 dest = document.getElementById(fragment.getAttribute('id'));
371 dest.innerHTML = fragment.firstChild.nodeValue;
372
373 scripts = dest.getElementsByTagName('script');
374 for (var j=0 ; j < scripts.length ; j++)
375 globalScriptRegistry.loadScript(scripts[j]);
376 }
377 }
378 break;
379 case 'error':
380 alert(req.responseXML.documentElement.firstChild.nodeValue);
381 return;
382 }
383 }
384 else {
385 this.responseTextDest.innerHTML = req.responseText;
386 var scripts = this.responseTextDest.getElementsByTagName('script');
387 for (var j=0 ; j < scripts.length ; j++)
388 globalScriptRegistry.loadScript(scripts[j]);
389 }
390
391 var onAfterPopulate = this.onAfterPopulate;
392 if (typeof(onAfterPopulate) == "string") {
393 if (window.console)
394 console.warn('Deprecation WARNING onAfterPopulate: ' + onAfterPopulate);
395 onAfterPopulate = eval(onAfterPopulate);
396 }
397 onAfterPopulate();
398 };
399
400 FormManager.prototype._fitField = function(evt) {
401 var ob = getTargetedObject(evt);
402 var inputWidth = ob.value.length / 1.9;
403 inputWidth = (inputWidth > 5) ? inputWidth : 5;
404 ob.style.width = inputWidth + 'em';
405 };
406
407 function initForms(baseElement, lazy) {
408 if (!baseElement)
409 baseElement = document;
410 var dest = document.getElementById("mainCell");
411 var forms = baseElement.getElementsByTagName("form");
412 var f;
413 for (var i = 0 ; i < forms.length ; i++ )
414 f = new FormManager(forms[i], dest, lazy);
415 }
416
417
418 //registerStartupFunction(initForms);