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