1 // (c) BenoƮt PIN 2006-2014
12 FormManager = function(form
, responseTextDest
, lazy
, noHistory
) {
13 if (form
.elements
.namedItem("noAjax")) {return;}
16 this.responseTextDest
= responseTextDest
;
18 this.noHistory
= noHistory
;
19 var thisManager
= this;
20 this.form
.onsubmit = function(evt
) { thisManager
.submit(evt
); };
21 this.form
.onclick = function(evt
) { thisManager
.click(evt
); };
23 /* raised on form submit */
24 this.onBeforeSubmit
= null;
25 /* raised after xmlhttp response */
26 this.onResponseLoad
= null;
27 /* raised when the responseText is added inside the main element.
28 * (onResponseLoad may have the default value) */
29 this.onAfterPopulate
= null;
30 this.submitButton
= null;
33 this.form
.onclick = function(evt
){
34 thisManager
.replaceElementByField(evt
);
35 thisManager
.click(evt
);
37 if (browser
.isDOM2Event
) {
38 this.form
.onfocus
= this.form
.onclick
;
40 else if (browser
.isIE6up
) {
41 this.form
.onfocusin
= this.form
.onclick
;
43 this.onResponseLoad = function(req
){ thisManager
.restoreField(req
); };
44 this.lazyListeners
= [];
48 FormManager
.prototype.submit = function(evt
) {
50 var thisManager
= this;
52 var bsMessage
; // before submit message
53 if (!this.onBeforeSubmit
) {
54 var onBeforeSubmit
= form
.elements
.namedItem("onBeforeSubmit");
56 if (onBeforeSubmit
.length
) {
57 onBeforeSubmit
= onBeforeSubmit
[0];
59 /*jslint evil: true */
60 this.onBeforeSubmit
= eval(onBeforeSubmit
.value
);
61 bsMessage
= this.onBeforeSubmit(thisManager
, evt
);
65 bsMessage
= this.onBeforeSubmit(thisManager
, evt
);
68 if (bsMessage
=== 'cancelSubmit') {
69 try {disableDefault(evt
);}
74 if (!this.onResponseLoad
) {
75 var onResponseLoad
= form
.elements
.namedItem("onResponseLoad");
77 this.onResponseLoad
= eval(onResponseLoad
.value
);
80 this.onResponseLoad
= this.loadResponse
;
84 var submitButton
= this.submitButton
;
85 var queryInfo
= this.formData2QueryString();
86 var query
= queryInfo
.query
;
87 this.hasFile
= queryInfo
.hasFile
;
90 if (!this.onAfterPopulate
) {
91 var onAfterPopulate
= form
.elements
.namedItem("onAfterPopulate");
92 if (onAfterPopulate
) {
93 this.onAfterPopulate
= onAfterPopulate
.value
;
96 this.onAfterPopulate = function() {};
101 query
+= submitButton
.name
+ '=' + submitButton
.value
+ '&';
104 if (form
.method
.toLowerCase() === 'post') {
111 try {disableDefault(evt
);}
115 FormManager
.prototype._post = function(query
) {
116 // send form by XmlHttpRequest
119 var req
= new XMLHttpRequest();
120 var thisManager
= this;
121 req
.onreadystatechange = function() {
122 switch (req
.readyState
) {
128 if (req
.status
=== 200 || req
.status
=== 204) {
129 thisManager
.onResponseLoad(req
);
132 alert('Error: ' + req
.status
);
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");
143 FormManager
.prototype._get = function(query
) {
144 var url
= this.form
.action
;
146 AjaxLinkHandler
.prototype.loadUrl(url
);
150 FormManager
.prototype.click = function(evt
) {
151 var target
= getTargetedObject(evt
);
152 if(target
.type
=== "submit" || target
.type
=== "image") {
153 this.submitButton
= target
;
154 disablePropagation(evt
);
158 FormManager
.prototype.replaceElementByField = function(evt
) {
159 evt
= getEventObject(evt
);
160 var ob
= getTargetedObject(evt
);
161 var eventType
= evt
.type
;
162 if (eventType
=== 'focus' || eventType
=== 'focusin') {
163 if (this.liveFormField
&& ob
.tagName
!== 'INPUT') {
164 this.pendingEvent
= [ob
, 'click'];
168 var fieldName
= ob
.getAttribute('id');
170 this.fieldTagName
= ob
.tagName
;
171 var tabIndex
= ob
.tabIndex
;
173 if (ob
.firstChild
&& ob
.firstChild
.className
=== 'hidden_value') {
174 text
= ob
.firstChild
.innerHTML
;
179 disablePropagation(evt
);
182 switch (ob
.tagName
) {
184 // create input element
185 var inputText
= document
.createElement("input");
186 inputText
.setAttribute("type", "text");
187 text
= text
.replace(/\n/g, ' ');
188 text
= text
.replace(/\s+/g, ' ');
189 text
= text
.replace(/^ /, '');
190 text
= text
.replace(/ $/, '');
191 inputText
.setAttribute("value", text
);
192 var inputWidth
= text
.length
/ 1.9;
193 inputWidth
= (inputWidth
> 5) ? inputWidth
: 5;
194 inputText
.style
.width
= inputWidth
+ 'em';
195 //inputText.setAttribute("size", text.length);
198 parent
= ob
.parentNode
;
199 parent
.replaceChild(inputText
, ob
);
203 inputText
.setAttribute('name', fieldName
);
204 inputText
.tabIndex
= tabIndex
;
205 inputText
.className
= 'live_field';
206 this.liveFormField
= inputText
;
207 this.lazyListeners
.push({'element': inputText
, 'eventName' : 'blur', 'handler': function(){ thisManager
.submit();}});
208 this.lazyListeners
.push({'element': inputText
, 'eventName' : 'keypress', 'handler': function(evt
){ thisManager
._fitField(evt
);}});
209 this._addLazyListeners();
215 var ta
= document
.createElement('textarea');
216 ta
.style
.display
= 'block';
217 ta
.className
= 'live_field';
218 text
= text
.replace(/^\s*/, '');
219 text
= text
.replace(/\s*$/, '');
223 parent
= ob
.parentNode
;
224 parent
.replaceChild(ta
, ob
);
228 ta
.setAttribute('name', fieldName
);
229 ta
.tabIndex
= tabIndex
;
230 this.liveFormField
= ta
;
231 this.lazyListeners
.push({'element': ta
, 'eventName' : 'blur', 'handler': function(){ thisManager
.submit();}});
232 this._addLazyListeners();
238 FormManager
.prototype._addLazyListeners = function() {
240 for (i
=0 ; i
<this.lazyListeners
.length
; i
++) {
241 handlerInfo
= this.lazyListeners
[i
];
242 addListener(handlerInfo
.element
, handlerInfo
.eventName
, handlerInfo
.handler
);
246 FormManager
.prototype._removeLazyListeners = function() {
248 for (i
=0 ; i
<this.lazyListeners
.length
; i
++) {
249 handlerInfo
= this.lazyListeners
[i
];
250 removeListener(handlerInfo
.element
, handlerInfo
.eventName
, handlerInfo
.handler
);
255 FormManager
.prototype.restoreField = function(req
) {
257 var input
= this.liveFormField
;
258 if (req
.status
=== 200) {
259 if (req
.getResponseHeader('Content-Type').indexOf('text/xml') !== -1) {
260 var out
= '..........';
261 if (req
.responseXML
.documentElement
.firstChild
) {
262 out
= req
.responseXML
.documentElement
.firstChild
.nodeValue
;
265 switch (req
.responseXML
.documentElement
.nodeName
) {
266 case 'computedField':
270 this._removeLazyListeners();
272 this.pendingEvent
= null;
274 this._addLazyListeners();
279 text
= req
.responseText
;
286 if (!text
.match(/\w/)) {
290 var field
= document
.createElement(this.fieldTagName
);
291 field
.innerHTML
= text
;
292 field
.setAttribute('id', input
.getAttribute('name'));
293 field
.className
= 'editable';
294 field
.tabIndex
= input
.tabIndex
;
296 var parent
= input
.parentNode
;
297 parent
.replaceChild(field
, input
);
298 this.liveFormField
= null;
300 if (this.pendingEvent
) {
301 raiseMouseEvent(this.pendingEvent
[0], this.pendingEvent
[1]);
307 FormManager
.prototype.formData2QueryString = function() {
308 // http://www.onlamp.com/pub/a/onlamp/2005/05/19/xmlhttprequest.html
309 var form
= this.form
;
310 var strSubmit
= '', formElem
, elements
;
315 elements
= form
.elements
;
319 var formElements
= form
.elements
;
320 for (i
= 0; i
< formElements
.length
; i
++) {
321 formElem
= formElements
[i
];
322 switch (formElem
.type
) {
324 elements
.push(formElem
);
327 if (formElem
=== this.liveFormField
) {
328 elements
.push(formElem
);
334 for (i
= 0; i
< elements
.length
; i
++) {
335 formElem
= elements
[i
];
336 switch (formElem
.type
) {
337 // text, select, hidden, password, textarea elements
343 strSubmit
+= formElem
.name
+ '=' + encodeURIComponent(formElem
.value
) + '&';
347 if (formElem
.checked
) {
348 strSubmit
+= formElem
.name
+ '=' + encodeURIComponent(formElem
.value
) + '&';
351 case 'select-multiple':
352 var options
= formElem
.getElementsByTagName("OPTION"), option
;
354 for (j
= 0 ; j
< options
.length
; j
++) {
356 if (option
.selected
) {
357 strSubmit
+= formElem
.name
+ '=' + encodeURIComponent(option
.value
) + '&';
362 if (formElem
.value
) {
368 return {'query' : strSubmit
, 'hasFile' : hasFile
};
371 FormManager
.prototype.loadResponse = function(req
) {
373 if (req
.getResponseHeader('Content-Type').indexOf('text/xml') !== -1) {
374 switch(req
.responseXML
.documentElement
.nodeName
) {
377 var sb
= this.submitButton
;
379 var h
= document
.createElement('input');
383 this.form
.appendChild(h
);
389 var fragments
= req
.responseXML
.documentElement
.childNodes
;
390 var element
, dest
, i
, j
;
391 for (i
=0 ; i
< fragments
.length
; i
++) {
392 element
= fragments
[i
];
393 switch (element
.nodeName
) {
395 dest
= document
.getElementById(element
.getAttribute('id'));
397 dest
.innerHTML
= element
.firstChild
.nodeValue
;
398 scripts
= dest
.getElementsByTagName('script');
399 for (j
=0 ; j
< scripts
.length
; j
++) {
400 globalScriptRegistry
.loadScript(scripts
[j
]); }
404 var headBase
= document
.getElementsByTagName('base');
405 if (headBase
.length
> 0) {
406 headBase
[0].setAttribute('href', element
.getAttribute('href'));
409 headBase
= document
.createElement('base');
410 headBase
.setAttribute('href', element
.getAttribute('href'));
411 document
.head
.appendChild(headBase
);
418 alert(req
.responseXML
.documentElement
.firstChild
.nodeValue
);
423 this.responseTextDest
.innerHTML
= req
.responseText
;
424 scripts
= this.responseTextDest
.getElementsByTagName('script');
426 for (k
=0 ; k
< scripts
.length
; k
++) {
427 globalScriptRegistry
.loadScript(scripts
[k
]);
431 var onAfterPopulate
= this.onAfterPopulate
;
433 this.scrollToPortalMessage();
434 var url
= this.form
.action
;
435 if (!this.noHistory
){ history
.pushState(url
, document
.title
, url
); }
438 FormManager
.prototype.scrollToPortalMessage = function() {
439 var psm
= document
.getElementById('DesktopStatusBar');
441 var msgOffset
= psm
.offsetTop
;
442 smoothScroll(window
.scrollY
, msgOffset
);
443 shake(psm
, 10, 1000);
447 FormManager
.prototype._fitField = function(evt
) {
448 var ob
= getTargetedObject(evt
);
449 var inputWidth
= ob
.value
.length
/ 1.9;
450 inputWidth
= (inputWidth
> 5) ? inputWidth
: 5;
451 ob
.style
.width
= inputWidth
+ 'em';
454 function initForms(baseElement
, lazy
) {
456 baseElement
= document
;
458 var dest
= document
.getElementById("mainCell");
459 var forms
= baseElement
.getElementsByTagName("form");
461 for (i
= 0 ; i
< forms
.length
; i
++ ) {
462 f
= new FormManager(forms
[i
], dest
, lazy
);
466 function smoothScroll(from, to
) {
478 var jump = function() {
479 window
.scroll(0, pos
);
480 pos
= pos
+ step
* dir
;
481 if ((dir
=== 1 && pos
>= to
) ||
482 (dir
=== -1 && pos
<= to
)) {
483 window
.clearInterval(intervalId
);
484 window
.scroll(0, to
);
487 intervalId
= window
.setInterval(jump
, 10);
490 /* adapted from http://xahlee.info/js/js_shake_box.html */
491 shake = function(e, distance, time) {
493 if (!time) { time = 500; }
494 if (!distance) { distance = 5; }
496 // Save the original style of e, Make e relatively positioned, Note the animation start time, Start the animation
497 var originalStyle = e.style.cssText;
498 e.style.position = "relative";
499 var start = (new Date()).getTime();
501 // This function checks the elapsed time and updates the position of e.
502 // If the animation is complete, it restores e to its original state.
503 // Otherwise, it updates e's position and schedules itself to run again.
505 var now = (new Date()).getTime();
507 var elapsed = now-start;
508 // How long since we started
509 var fraction = elapsed/time;
510 // What fraction of total time?
512 // If the animation is not yet complete
513 // Compute the x position of e as a function of animation
514 // completion fraction. We use a sinusoidal function, and multiply
515 // the completion fraction by 4pi, so that it shakes back and
517 var x = distance * Math.sin(fraction*8*Math.PI);
518 e.style.left = x + "px";
519 // Try to run again in 25ms or at the end of the total time.
520 // We're aiming for a smooth 40 frames/second animation.
521 setTimeout(animate, Math.min(25, time-elapsed));
524 // Otherwise, the animation is complete
525 e.style.cssText = originalStyle; // Restore the original style