1 // (c) Benoît PIN 2006-2007
11 FormManager = function(form
, responseTextDest
, lazy
) {
12 if (form
.elements
.namedItem("noAjax")) {return;}
15 this.responseTextDest
= responseTextDest
;
17 var thisManager
= this;
18 this.form
.onsubmit = function(evt
) { thisManager
.submit(evt
); };
19 this.form
.onclick = function(evt
) { thisManager
.click(evt
); };
20 this.submitButton
= null;
22 /* raised on form submit */
23 this.onBeforeSubmit
= null;
24 /* raised after xmlhttp response */
25 this.onResponseLoad
= null;
26 /* raised when the responseText is added inside the main element.
27 * (onResponseLoad may have the default value) */
28 this.onAfterPopulate
= null;
29 this.submitButton
= null;
32 this.form
.onclick = function(evt
){
33 thisManager
.replaceElementByField(evt
);
34 thisManager
.click(evt
);
36 if (browser
.isDOM2Event
) {
37 this.form
.onfocus
= this.form
.onclick
;
39 else if (browser
.isIE6up
) {
40 this.form
.onfocusin
= this.form
.onclick
;
42 this.onResponseLoad = function(req
){ thisManager
.restoreField(req
); };
43 this.lazyListeners
= [];
47 FormManager
.prototype.submit = function(evt
) {
49 var thisManager
= this;
51 var bsMessage
; // before submit message
52 if (!this.onBeforeSubmit
) {
53 var onBeforeSubmit
= form
.elements
.namedItem("onBeforeSubmit");
55 if (onBeforeSubmit
.length
) {
56 onBeforeSubmit
= onBeforeSubmit
[0];
58 this.onBeforeSubmit
= eval(onBeforeSubmit
.value
);
59 bsMessage
= this.onBeforeSubmit(thisManager
, evt
);
63 bsMessage
= this.onBeforeSubmit(thisManager
, evt
);
66 if (bsMessage
=== 'cancelSubmit') {
67 try {disableDefault(evt
);}
72 if (!this.onResponseLoad
) {
73 var onResponseLoad
= form
.elements
.namedItem("onResponseLoad");
75 this.onResponseLoad
= eval(onResponseLoad
.value
);
78 this.onResponseLoad
= this.loadResponse
;
82 var submitButton
= this.submitButton
;
83 var queryInfo
= this.formData2QueryString();
84 var query
= queryInfo
.query
;
85 this.hasFile
= queryInfo
.hasFile
;
88 if (!this.onAfterPopulate
) {
89 var onAfterPopulate
= form
.elements
.namedItem("onAfterPopulate");
90 if (onAfterPopulate
) {
91 this.onAfterPopulate
= onAfterPopulate
.value
;
94 this.onAfterPopulate = function() {};
99 query
+= submitButton
.name
+ '=' + submitButton
.value
+ '&';
102 if (window
.AJAX_CONFIG
&& (AJAX_CONFIG
& 1 === 1)) {
103 if (form
.method
.toLowerCase() === 'post') {
114 try {disableDefault(evt
);}
118 FormManager
.prototype._post = function(query
) {
119 // send form by XmlHttpRequest
122 var req
= new XMLHttpRequest();
123 var thisManager
= this;
124 req
.onreadystatechange = function() {
125 switch (req
.readyState
) {
131 if (req
.status
=== 200 || req
.status
=== 204) {
132 thisManager
.onResponseLoad(req
);
135 alert('Error: ' + req
.status
);
140 var url
= this.form
.action
;
141 req
.open("POST", url
, true);
142 req
.setRequestHeader("Content-Type", "application/x-www-form-urlencoded;charset=utf-8");
146 FormManager
.prototype._get = function(query
) {
147 // send form by browser location
148 var url
= this.form
.action
;
150 linkHandler
.loadUrl(url
);
154 FormManager
.prototype.click = function(evt
) {
155 var target
= getTargetedObject(evt
);
156 if(target
.type
=== "submit" || target
.type
=== "image") {
157 this.submitButton
= target
;
158 disablePropagation(evt
);
162 FormManager
.prototype.replaceElementByField = function(evt
) {
163 evt
= getEventObject(evt
);
164 var ob
= getTargetedObject(evt
);
165 var eventType
= evt
.type
;
166 if (eventType
=== 'focus' || eventType
=== 'focusin') {
167 if (this.liveFormField
&& ob
.tagName
!== 'INPUT') {
168 this.pendingEvent
= [ob
, 'click'];
172 var fieldName
= ob
.getAttribute('id');
174 this.fieldTagName
= ob
.tagName
;
175 var tabIndex
= ob
.tabIndex
;
177 if (ob
.firstChild
&& ob
.firstChild
.className
=== 'hidden_value') {
178 text
= ob
.firstChild
.innerHTML
;
183 disablePropagation(evt
);
186 switch (ob
.tagName
) {
188 // create input element
189 var inputText
= document
.createElement("input");
190 inputText
.setAttribute("type", "text");
191 text
= text
.replace(/\n/g, ' ');
192 text
= text
.replace(/\s+/g, ' ');
193 text
= text
.replace(/^ /, '');
194 text
= text
.replace(/ $/, '');
195 inputText
.setAttribute("value", text
);
196 var inputWidth
= text
.length
/ 1.9;
197 inputWidth
= (inputWidth
> 5) ? inputWidth
: 5;
198 inputText
.style
.width
= inputWidth
+ 'em';
199 //inputText.setAttribute("size", text.length);
202 parent
= ob
.parentNode
;
203 parent
.replaceChild(inputText
, ob
);
207 inputText
.setAttribute('name', fieldName
);
208 inputText
.tabIndex
= tabIndex
;
209 inputText
.className
= 'live_field';
210 this.liveFormField
= inputText
;
211 this.lazyListeners
.push({'element': inputText
, 'eventName' : 'blur', 'handler': function(){ thisManager
.submit();}});
212 this.lazyListeners
.push({'element': inputText
, 'eventName' : 'keypress', 'handler': function(evt
){ thisManager
._fitField(evt
);}});
213 this._addLazyListeners();
219 var ta
= document
.createElement('textarea');
220 ta
.style
.display
= 'block';
221 ta
.className
= 'live_field';
222 text
= text
.replace(/^\s*/, '');
223 text
= text
.replace(/\s*$/, '');
227 parent
= ob
.parentNode
;
228 parent
.replaceChild(ta
, ob
);
232 ta
.setAttribute('name', fieldName
);
233 ta
.tabIndex
= tabIndex
;
234 this.liveFormField
= ta
;
235 this.lazyListeners
.push({'element': ta
, 'eventName' : 'blur', 'handler': function(){ thisManager
.submit();}});
236 this._addLazyListeners();
242 FormManager
.prototype._addLazyListeners = function() {
244 for (i
=0 ; i
<this.lazyListeners
.length
; i
++) {
245 handlerInfo
= this.lazyListeners
[i
];
246 addListener(handlerInfo
.element
, handlerInfo
.eventName
, handlerInfo
.handler
);
250 FormManager
.prototype._removeLazyListeners = function() {
252 for (i
=0 ; i
<this.lazyListeners
.length
; i
++) {
253 handlerInfo
= this.lazyListeners
[i
];
254 removeListener(handlerInfo
.element
, handlerInfo
.eventName
, handlerInfo
.handler
);
259 FormManager
.prototype.restoreField = function(req
) {
261 var input
= this.liveFormField
;
262 if (req
.status
=== 200) {
263 if (req
.getResponseHeader('Content-Type').indexOf('text/xml') !== -1) {
264 var out
= '..........';
265 if (req
.responseXML
.documentElement
.firstChild
) {
266 out
= req
.responseXML
.documentElement
.firstChild
.nodeValue
;
269 switch (req
.responseXML
.documentElement
.nodeName
) {
270 case 'computedField':
274 this._removeLazyListeners();
276 this.pendingEvent
= null;
278 this._addLazyListeners();
283 text
= req
.responseText
;
290 if (!text
.match(/\w/)) {
294 var field
= document
.createElement(this.fieldTagName
);
295 field
.innerHTML
= text
;
296 field
.setAttribute('id', input
.getAttribute('name'));
297 field
.className
= 'editable';
298 field
.tabIndex
= input
.tabIndex
;
300 var parent
= input
.parentNode
;
301 parent
.replaceChild(field
, input
);
302 this.liveFormField
= null;
304 if (this.pendingEvent
) {
305 raiseMouseEvent(this.pendingEvent
[0], this.pendingEvent
[1]);
311 FormManager
.prototype.formData2QueryString = function() {
312 // http://www.onlamp.com/pub/a/onlamp/2005/05/19/xmlhttprequest.html
313 var form
= this.form
;
314 var strSubmit
= '', formElem
, elements
;
319 elements
= form
.elements
;
323 var formElements
= form
.elements
;
324 for (i
= 0; i
< formElements
.length
; i
++) {
325 formElem
= formElements
[i
];
326 switch (formElem
.type
) {
328 elements
.push(formElem
);
331 if (formElem
=== this.liveFormField
) {
332 elements
.push(formElem
);
338 for (i
= 0; i
< elements
.length
; i
++) {
339 formElem
= elements
[i
];
340 switch (formElem
.type
) {
341 // text, select, hidden, password, textarea elements
347 strSubmit
+= formElem
.name
+ '=' + encodeURIComponent(formElem
.value
) + '&';
351 if (formElem
.checked
) {
352 strSubmit
+= formElem
.name
+ '=' + encodeURIComponent(formElem
.value
) + '&';
355 case 'select-multiple':
356 var options
= formElem
.getElementsByTagName("OPTION"), option
;
358 for (j
= 0 ; j
< options
.length
; j
++) {
360 if (option
.selected
) {
361 strSubmit
+= formElem
.name
+ '=' + encodeURIComponent(option
.value
) + '&';
366 if (formElem
.value
) {
372 return {'query' : strSubmit
, 'hasFile' : hasFile
};
375 FormManager
.prototype.loadResponse = function(req
) {
377 if (req
.getResponseHeader('Content-Type').indexOf('text/xml') !== -1) {
378 switch(req
.responseXML
.documentElement
.nodeName
) {
381 var sb
= this.submitButton
;
383 var h
= document
.createElement('input');
387 this.form
.appendChild(h
);
393 var fragments
= req
.responseXML
.documentElement
.childNodes
;
396 for (i
=0 ; i
<fragments
.length
; i
++) {
397 fragment
= fragments
[i
];
398 if (fragment
.nodeName
=== 'fragment') {
399 dest
= document
.getElementById(fragment
.getAttribute('id'));
400 dest
.innerHTML
= fragment
.firstChild
.nodeValue
;
402 scripts
= dest
.getElementsByTagName('script');
404 for (j
=0 ; j
< scripts
.length
; j
++) {
405 globalScriptRegistry
.loadScript(scripts
[j
]);
411 alert(req
.responseXML
.documentElement
.firstChild
.nodeValue
);
416 this.responseTextDest
.innerHTML
= req
.responseText
;
417 scripts
= this.responseTextDest
.getElementsByTagName('script');
419 for (k
=0 ; k
< scripts
.length
; k
++) {
420 globalScriptRegistry
.loadScript(scripts
[k
]);
424 var onAfterPopulate
= this.onAfterPopulate
;
425 if (typeof(onAfterPopulate
) === "string") {
426 if (window
.console
) {
427 console
.warn('Deprecation WARNING onAfterPopulate: ' + onAfterPopulate
);
429 onAfterPopulate
= eval(onAfterPopulate
);
432 this.scrollToPortalMessage();
435 FormManager
.prototype.scrollToPortalMessage = function() {
436 var psm
= document
.getElementById('DesktopStatusBar');
437 var msgOffset
= psm
.offsetTop
;
438 smoothScroll(window
.scrollY
, msgOffset
);
439 shake(psm
, 10, 1000);
442 FormManager
.prototype._fitField = function(evt
) {
443 var ob
= getTargetedObject(evt
);
444 var inputWidth
= ob
.value
.length
/ 1.9;
445 inputWidth
= (inputWidth
> 5) ? inputWidth
: 5;
446 ob
.style
.width
= inputWidth
+ 'em';
449 function initForms(baseElement
, lazy
) {
451 baseElement
= document
;
453 var dest
= document
.getElementById("mainCell");
454 var forms
= baseElement
.getElementsByTagName("form");
456 for (i
= 0 ; i
< forms
.length
; i
++ ) {
457 f
= new FormManager(forms
[i
], dest
, lazy
);
461 function smoothScroll(from, to
) {
473 var jump = function() {
474 window
.scroll(0, pos
);
475 pos
= pos
+ step
* dir
;
476 if ((dir
=== 1 && pos
>= to
) ||
477 (dir
=== -1 && pos
<= to
)) {
478 window
.clearInterval(intervalId
);
479 window
.scroll(0, to
);
482 intervalId
= window
.setInterval(jump
, 10);
485 /* adapted from http://xahlee.info/js/js_shake_box.html */
486 function shake(e, distance, time) {
488 if (!time) time = 500;
489 if (!distance) distance = 5;
491 // Save the original style of e, Make e relatively positioned, Note the animation start time, Start the animation
492 var originalStyle = e.style.cssText;
493 e.style.position = "relative";
494 var start = (new Date()).getTime();
497 // This function checks the elapsed time and updates the position of e.
498 // If the animation is complete, it restores e to its original state.
499 // Otherwise, it updates e's position and schedules itself to run again.
501 var now = (new Date()).getTime();
503 var elapsed = now-start;
504 // How long since we started
505 var fraction = elapsed/time;
506 // What fraction of total time?
508 // If the animation is not yet complete
509 // Compute the x position of e as a function of animation
510 // completion fraction. We use a sinusoidal function, and multiply
511 // the completion fraction by 4pi, so that it shakes back and
513 var x = distance * Math.sin(fraction*8*Math.PI);
514 e.style.left = x + "px";
515 // Try to run again in 25ms or at the end of the total time.
516 // We're aiming for a smooth 40 frames/second animation.
517 setTimeout(animate, Math.min(25, time-elapsed));
520 // Otherwise, the animation is complete
521 e.style.cssText = originalStyle; // Restore the original style