/[webpac2]/trunk/web/iwf/iwfajax.js
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Contents of /trunk/web/iwf/iwfajax.js

Parent Directory Parent Directory | Revision Log Revision Log


Revision 52 - (show annotations)
Mon Nov 14 16:16:20 2005 UTC (18 years, 5 months ago) by dpavlin
File MIME type: text/cpp
File size: 19636 byte(s)
 r8861@llin:  dpavlin | 2005-11-14 16:36:22 +0100
 fix checkbox

1 // -----------------------------------------------------------------------------
2 // IWF - Interactive Website Framework. Javascript library for creating
3 // responsive thin client interfaces.
4 //
5 // Copyright (C) 2005 Brock Weaver brockweaver@gmail.com
6 //
7 // This library is free software; you can redistribute it and/or modify
8 // it under the terms of the GNU Lesser General Public License as published
9 // by the Free Software Foundation; either version 2.1 of the License, or
10 // (at your option) any later version.
11 //
12 // This library is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 // or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
15 // License for more details.
16 //
17 // You should have received a copy of the GNU Lesser General Public License
18 // along with this library; if not, write to the Free Software Foundation,
19 // Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 //
21 // Brock Weaver
22 // brockweaver@gmail.com
23 // 1605 NW Maple Pl
24 // Ankeny, IA 50021
25 // -----------------------------------------------------------------------------
26
27 // --------------------------------------------------------------------------
28 // iwfajax.js
29 //
30 // Thread-safe background xml request via XmlHttpRequest object
31 //
32 // Dependencies:
33 // iwfxml.js
34 // iwfcore.js
35 // iwfgui.js (optional -- pretty progress bar)
36 //
37 // Brock Weaver - brockweaver@sourceforge.net - iwf.sourceforge.net
38 // v 0.1 - 2005-06-05
39 // Initial release.
40 // --------------------------------------------------------------------------
41 // Known Issues:
42 // AddToHistory does not work
43 // Iframe not implemented
44 // --------------------------------------------------------------------------
45
46 if (!window.iwfGetById || !window.iwfXmlDoc){
47 iwfLog("IWF Dependency Error: iwfajax.js is dependent upon both iwfcore.js and iwfxml.js, so you *must* reference those files first.\n\nExample:\n\n<script type='text/javascript' src='iwfcore.js'></script>\n<script type='text/javascript' src='iwfxml.js'></script>\n<script type='text/javascript' src='iwfajax.js'></script>", true);
48 }
49
50 // -----------------------------------
51 // Begin: AJAX Request and Response
52 // -----------------------------------
53
54 function iwfRequest(urlOrForm, targetElementOnResponse, addToHistory, callback){
55
56 // we use a javascript feature here called "inner functions"
57 // using these means the local variables retain their values after the outer function
58 // has returned. this is useful for thread safety, so
59 // reassigning the onreadystatechange function doesn't stomp over earlier requests.
60
61 function iwfBindCallback(){
62 if (req.readyState == 4) {
63 _iwfOnRequestEnd();
64 if (req.status == 200 || req.status == 0) {
65 iwfLog('exact response from server:\n\n' + req.responseText);
66 _iwfResponseHandler(req.responseText, localCallback, localTarget);
67 } else {
68 _iwfOnRequestError(req.status, req.statusText, req.responseText);
69 }
70 return;
71 }
72 }
73
74 // determine how to hit the server...
75 var url = null;
76 var method = 'GET';
77 var postdata = null;
78 var isFromForm = true;
79 var contentType = 'application/x-www-form-urlencoded';
80
81 if (iwfIsString(urlOrForm)){
82
83 // if we get here, they either specified the url or the name of the form.
84 // either way, flag so we return nothing.
85 isFromForm = false;
86
87 var frm = iwfGetForm(urlOrForm);
88 if (!frm){
89 // is a url.
90 url = urlOrForm;
91 method = 'GET';
92 postdata = null;
93 } else {
94 // is name of a form.
95 // fill with the form object.
96 urlOrForm = frm;
97 }
98
99 }
100
101 // use a local variable to hold our callback and target until the inner function is called...
102 var localCallback = callback;
103 var localTarget = targetElementOnResponse;
104
105 if (!iwfIsString(urlOrForm)){
106
107 var ctl = null;
108
109
110 // is a form or a control in the form.
111 if (iwfExists(urlOrForm.form)){
112 // is a control in the form. jump up to the form.
113 ctl = urlOrForm;
114 urlOrForm = urlOrForm.form;
115 }
116
117
118 // if they passed a form and no local target, lookup the form.iwfTarget attribute and use it if possible
119 if (!localTarget){
120 localTarget = iwfAttribute(urlOrForm, 'iwfTarget');
121 }
122
123 if (localTarget){
124 var elTgt = iwfGetOrCreateWithinForm(urlOrForm, 'iwfTarget', 'input', 'hidden');
125 if (elTgt){
126 iwfAttribute(elTgt, 'value', localTarget);
127 iwfRemoveAttribute(elTgt, 'disabled');
128 }
129 }
130
131
132 url = urlOrForm.action;
133 method = urlOrForm.method.toUpperCase();
134 switch(method){
135 case "POST":
136 postdata = _iwfGetFormData(urlOrForm, url, ctl);
137
138 // we also need to properly set the content-type header...
139 var frm = iwfGetForm(urlOrForm);
140 if (frm){
141 var enc = iwfAttribute(frm, 'encoding');
142 if (!enc){
143 enc = iwfAttribute(frm, 'enctype');
144 }
145 if (enc){
146 contentType = enc;
147 }
148 }
149
150 break;
151 case "GET":
152 default:
153 url = _iwfGetFormData(urlOrForm, url, ctl);
154 break;
155 }
156 }
157
158 // prevent any browser caching of our url by requesting a unique url everytime...
159 url += ((url.indexOf('?') > -1) ? '&' : '?') + 'iwfRequestId=' + new Date().valueOf();
160
161 iwfLog("url = " + url);
162 iwfLog("method = " + method);
163 iwfLog("postdata = " + postdata);
164 iwfLog("contenttype = " + contentType);
165
166
167 var req = null;
168 if (!addToHistory){
169 iwfLog("using XHR to perform request...");
170 // use XHR to perform the request, as this will
171 // prevent the browser from adding it to history.
172 req = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP");
173
174 // bind our callback
175 req.onreadystatechange = iwfBindCallback;
176
177 // show progress if it's not already visible...
178 _iwfOnRequestStart();
179
180 // hit the server
181 req.open(method, url, true);
182 req.setRequestHeader('Content-Type', contentType);
183 req.send(postdata);
184 } else {
185 // use an IFRAME element to perform the request,
186 // as this will cause the browser to add it to history.
187 // TODO: make this work!!!
188 iwfLog("using IFRAME to perform request...");
189 iwfLog('request and add to history not implemented yet!!!', true);
190 return;
191
192 var el = iwfGetById("iwfHistoryFrame");
193 if (!el){
194 iwfLog("To enable history tracking in IWF, you must add an invisible IFRAME element to your page:\n<IFRAME id='iwfHistoryFrame' style='display:none'></IFRAME>", true);
195 }
196
197 el.src = url;
198
199 // show progress if it's not already visible...
200 _iwfOnRequestStart();
201
202
203 }
204
205 // if this is called from the form.onsubmit event, make sure the form doesn't submit...
206
207 if (isFromForm){
208 return false;
209 } else {
210 // return absolutely nothing so anchor tags don't hork the current page
211 }
212
213 }
214
215 function _iwfGetFormData(form, url, ctl){
216
217 var method = form.method;
218 if (!method) method = "get";
219
220 var output = null;
221
222 if (method == 'get'){
223 output = url + ((url.indexOf('?') > -1) ? '&' : '?');
224 } else {
225 output = '';
226 }
227
228
229 // if there is a target specified in the <form> tag,
230 // copy its contents to a hidden <input type='hidden'> tag.
231
232 iwfLog("total elements in form named '" + form.name + "': " + form.elements.length);
233 for(var i=0;i<form.elements.length;i++){
234 var el = form.elements[i];
235 var nm = iwfAttribute(el, 'name');
236 var val = null;
237 if (!iwfAttribute(el, 'disabled') && nm){
238 switch (el.tagName.toLowerCase()){
239 case 'input':
240 switch(iwfAttribute(el, 'type')){
241 case 'checkbox':
242 case 'radio':
243 if (iwfAttribute(el, 'checked') || el.checked){
244 val = iwfAttribute(el, 'value') || el.value;
245 }
246 break;
247 case 'button':
248 if (el == ctl){
249 val = iwfAttribute(el, 'value');
250 }
251 break;
252 case 'submit':
253 if (el == ctl){
254 val = iwfAttribute(el, 'value');
255 }
256 break;
257 case 'text':
258 default:
259 val = iwfAttribute(el, 'value');
260 break;
261 case 'file':
262 iwfLog('TODO: implement <input type="file"> in _iwfGetFormData', true);
263 val = iwfAttribute(el, 'value');
264 break;
265 case 'image':
266 iwfLog('TODO: implement <input type="image"> in _iwfGetFormData', true);
267 val = iwfAttribute(el, 'value');
268 break;
269 case 'reset':
270 iwfLog('TODO: implement <input type="reset"> in _iwfGetFormData', true);
271 break;
272 case 'hidden':
273 val = iwfAttribute(el, 'value');
274 break;
275 }
276 break;
277 case 'textarea':
278 val = iwfAttribute(el, 'innerText') || el.value;
279 break;
280 case 'button':
281 if (el == ctl){
282 val = iwfAttribute(el, 'innerText') || el.value;
283 }
284 break;
285 case 'select':
286 for(var j=0;j<el.options.length;j++){
287 if (iwfAttribute(el.options[j], 'selected') == 'true'){
288 if (!val){
289 val = iwfAttribute(el.options[j], 'value');
290 } else {
291 val += '+' + iwfAttribute(el.options[j], 'value');
292 }
293 }
294 }
295 }
296
297 if (val){
298 if (output.length > 0){
299 output += '&';
300 }
301 output += escape(nm) + '=' + escape(val);
302 }
303 }
304 }
305 if (output.length == 0){
306 return null;
307 } else {
308 return output;
309 }
310 }
311
312 function _iwfResponseReceived(doc){
313 iwfLog('iframeloaded');
314 var xmlDoc = new iwfXmlDoc(doc.innerHTML);
315 }
316
317 function _iwfResponseHandler(origText, callback, tgt){
318 var doc = new iwfXmlDoc(origText);
319 if (!doc.response){
320 // not in our default xml format.
321 // just throw out the xml to the callback, if any
322 if (callback){
323 callback(doc);
324 } else {
325 iwfLog("IWF Ajax Error: No callback defined for non-standard response:\n" + origText, true);
326 }
327 } else {
328 if (doc.response.debugging == 'true'){
329 iwfLoggingEnabled = true;
330 iwfLog("IWF Ajax Debugging:\nParsed response:\n\n" + doc.response.outerXml(true), true);
331 iwfShowLog();
332 }
333 for(var i=0; i< doc.response.childNodes.length; i++){
334 var node = doc.response.childNodes[i];
335 if (node.nodeName.indexOf("#") != 0){
336 iwfLog('node.target=' + node.target + '\ntgt=' + tgt);
337 if (!tgt) {
338 // server target is ignored if a client target exists.
339 tgt = node.target;
340 }
341 if (node.errorCode && iwfToInt(node.errorCode, true) != 0){
342 // an error occurred.
343 _iwfOnRequestError(node.errorCode, node.errorMessage);
344 } else {
345 if (!node.type){
346 node.type = "";
347 }
348 switch(node.type.toLowerCase()){
349 case "html":
350 case "xhtml":
351 var innerHtml = node.innerHtml();
352 //iwfLog('parsed html response:\n\n' + innerHtml);
353 _iwfInsertHtml(innerHtml, tgt);
354 break;
355 case "javascript":
356 case "js":
357 var bomb = true;
358 if (node.childNodes.length == 1){
359 var js = node.childNodes[0];
360 if (js.nodeName == '#cdata' || js.nodeName == '#comment'){
361 bomb = false;
362 var code = js.getText();
363 eval(code);
364 }
365 }
366 if (bomb){
367 iwfLog("IWF Ajax Error: When an <action> is defined of type javascript, it content must be contained by either a Comment (<!-- -->) or CDATA (<![CDATA[ ]]>).\nCDATA is the more appropriate manner, as this is the xml-compliant one.\nHowever, COMMENT is also allowed as this is html-compliant, such as within a <script> element.\n\n<action> xml returned:\n\n" + node.outerXml(), true);
368 }
369 break;
370 case "xml":
371 if (callback){
372 callback(node);
373 }
374 break;
375 case "debug":
376 iwfLog("IWF Debug: <action> type identified as 'debug'.\nXml received for current action:\n\n" + node.outerXml(), true);
377 break;
378 default:
379 iwfLog('IWF Ajax Error: <action> type of "' + node.type + '" is not a valid option.\n\nValid options:\n\'html\' or \'xhtml\' = parse as html and inject into element with the id specified by the target attribute\n\'javascript\' or \'js\' = parse as javascript and execute\n\'xml\' = parse as xml and call the callback specified when iwfRequest() was called\n\'debug\' = parse as xml and log/alert the result\n\n<action> xml returned:\n\n' + node.outerXml(), true);
380 break;
381 }
382 }
383 }
384 }
385 }
386
387 }
388
389 function _iwfInsertHtml(html, parentNodeId){
390 if(!parentNodeId){
391 parentNodeId = 'iwfContent';
392 iwfLog("IWF Ajax Warning: <action> with a type of 'html' does not have its target attribute specified, so using the default of 'iwfContent'.");
393 }
394
395 if(!parentNodeId){
396 iwfLog("IWF Ajax Error: <action> node with a type of 'html' does not have its target attribute specified to a valid element.\nPlease specify the id of the element into which the contents of the <action> node should be placed.\nTo fill the entire page, simply specify 'body'.", true);
397 } else {
398 var el = iwfGetById(parentNodeId);
399 if (!el){
400 if (parentNodeId == 'body'){
401 el = iwfGetByTagName('body');
402 if (!el || el.length == 0){
403 iwfLog("IWF Ajax Error: Could not locate the tag named 'body'", true);
404 return;
405 } else {
406 el = el[0];
407 }
408 } else {
409 iwfLog('IWF Ajax Error: Could not locate element with id of ' + parentNodeId + ' into which the following html should be placed:\n\n' + html, true);
410 return;
411 }
412 }
413
414 //iwfLog(iwfElementToString(el));
415 //iwfLog(html);
416
417 // trying to shove a <form> node inside another <form> node is a bad thing.
418 // make sure we don't do that here.
419 var re = /<form/i;
420 var match = re.exec(html);
421 if (match && document.forms.length > 0){
422 // our html to inject contains a form node.
423 // bubble up the chain until we find a <form> node, or we have no parents
424 var elParent = el;
425 while (elParent && elParent.tagName.toLowerCase() != 'form'){
426 elParent = iwfGetParent(elParent);
427 }
428
429 if (elParent && elParent.tagName.toLowerCase() == 'form'){
430 iwfLog('IWF Ajax Error: Attempting to inject html which contains a <form> node into a target element which is itself a <form> node, or is already contained by a <form> node.\nThis is bad html, and will not work appropriately on some major browsers.', true);
431 }
432 }
433
434
435 el.innerHTML = html;
436
437
438 // if there is a script element, we have to explicitly add it to the body element,
439 // as IE and firefox just don't like it when you try to add it as part of the innerHTML
440 // property. Go figure.
441
442 var i = 0;
443 // don't stomp on any existing scripts...
444 while (iwfGetById('iwfScript' + i)){
445 i++;
446 }
447
448 var scriptStart = html.indexOf("<script");
449 while(scriptStart > -1){
450 scriptStart = html.indexOf(">", scriptStart) + 1;
451
452 // copy contents of script into a default holder
453 var scriptEnd = html.indexOf("</script>", scriptStart);
454 var scriptHtml = html.substr(scriptStart, scriptEnd - scriptStart);
455
456 var re = /^\s*<!--/;
457 var match = re.exec(scriptHtml);
458 if (!match){
459 iwfLog("IWF Ajax Error: Developer, you *must* put the <!-- and //--> 'safety net' around your script within all <script> tags.\nThe offending <script> tag contains the following code:\n\n" + scriptHtml + "\n\nThis requirement is due to how IWF injects the html so the browser is able to actually parse the script and make its contents available for execution.", true);
460 }
461
462 // this code is the worst hack in this entire framework.
463 // the code in the try portion should work anywhere -- but
464 // ie barfs when you try to set the innerHTML on a script element.
465 // not only that, but ie won't parse script unless a visible element
466 // is contained in the innerHTML when it is set, so we need the <code>ie hack here</code>
467 // and it cannot be removed.
468 // I don't understand why creating a new node and setting its innerHTML causes the browsers
469 // to parse and execute the script, but it does.
470 // Note there is a major leak here, as we do not try to reuse existing iwfScriptXXXX elements
471 // in case they are in use by other targets. To clean these up, simply call iwfCleanScripts
472 // periodically. This is so app-dependent, I didn't want to try to keep track of everything
473 // and possibly miss a case, causing really weird results that only show up occassionally.
474 //
475 // Plus I'm getting lazy. :)
476 //
477 try {
478 // moz (DOM)
479 var elScript = iwfGetOrCreateById('iwfScript' + i, 'script', 'body');
480 elScript.type = 'text/javascript';
481 elScript.defer = 'true';
482 elScript.innerHTML = scriptHtml;
483
484 } catch(e){
485 // ie hack -- need a visible tag within a non-script element to have scripting apply... Don't ask me why, ask the IE team why...
486 var elDiv = iwfGetOrCreateById('iwfScript' + i, '<div style="display:none"></div>', 'body');
487 elDiv.innerHTML = '<code>ie hack here</code><script id="iwfScript' + i + '" defer="true">' + scriptHtml + '</script' + '>';
488 }
489
490 i++;
491
492 scriptStart = html.indexOf("<script", scriptEnd+8);
493 }
494 }
495 }
496
497 function iwfCleanScripts(){
498 var i = 0;
499 while((el = iwfGetById('iwfScript' + i))){
500 iwfRemoveNode(el);
501 i++;
502 }
503 }
504
505
506
507 var _iwfTotalRequests = 0;
508 var _iwfPendingRequests = 0;
509 var _iwfRequestTicker = null;
510 var _iwfRequestTickCount = 0;
511 var _iwfRequestTickDuration = 100;
512 function _iwfOnRequestStart(){
513 _iwfPendingRequests++;
514 _iwfTotalRequests++;
515
516 _iwfRequestTickDuration = 100;
517
518 if (!_iwfRequestTicker){
519 _iwfRequestTickCount = 0;
520 if (window.iwfOnRequestStart){
521 _iwfRequestTickDuration = iwfOnRequestStart();
522 } else {
523 // use default busy implementation...
524 // TODO: make this better!
525 window.status = 'busy.';
526 }
527 if (!_iwfRequestTickDuration){
528 _iwfRequestTickDuration = 100;
529 }
530 _iwfRequestTicker = setInterval(_iwfOnRequestTick, _iwfRequestTickDuration);
531 }
532 }
533
534 function _iwfOnRequestTick(){
535 _iwfRequestTickCount++;
536 if (window.iwfOnRequestTick){
537 iwfOnRequestTick(_iwfRequestTickCount, _iwfRequestTickDuration, _iwfPendingRequests);
538 } else if (!window.iwfOnRequestStart) {
539 // use default busy implementation...
540 // TODO: make this better!
541 window.status = 'busy...............................................'.substr(0, (_iwfRequestTickCount % 45) + 5);
542 } else {
543 // they didn't define a tick function,
544 // but they did define a start one, so do nothing.
545 }
546 }
547
548 function _iwfOnRequestEnd(){
549 _iwfPendingRequests--;
550 if (_iwfPendingRequests < 1){
551 _iwfPendingRequests = 0;
552 _iwfTotalRequests = 0;
553 clearInterval(_iwfRequestTicker);
554 _iwfRequestTicker = null;
555 if (window.iwfOnRequestEnd){
556 iwfOnRequestEnd();
557 } else if (!window.iwfOnRequestStart) {
558 // use default busy implementation...
559 // TODO: make this better!
560 window.status = 'done.';
561 } else {
562 // they didn't define an end function,
563 // but they did define a start one, so do nothing.
564 }
565
566 } else {
567 if (window.iwfOnRequestProgress){
568 iwfOnRequestProgress(_iwfPendingRequests, _iwfTotalRequests);
569 } else if (!window.iwfOnRequestStart) {
570 // use default busy implementation...
571 // TODO: make this better!
572 window.status = 'Remaining: ' + _iwfPendingRequests;
573 } else {
574 // they didn't define an end function,
575 // but they did define a start one, so do nothing.
576 }
577 }
578 }
579
580 function _iwfOnRequestError(code, msg, text){
581 iwfLog("Error " + code + ": " + msg + ":\n\n" + text);
582 if (window.iwfOnRequestError){
583 iwfOnRequestError(code, msg, text);
584 } else {
585 alert("Error " + code + ": " + msg + ":\n\n" + text);
586 }
587 }
588
589 // -----------------------------------
590 // End: AJAX Request and Response
591 // -----------------------------------

Properties

Name Value
svn:mime-type text/cpp

  ViewVC Help
Powered by ViewVC 1.1.26