/[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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 52 - (hide annotations)
Mon Nov 14 16:16:20 2005 UTC (18 years, 6 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 dpavlin 46 // -----------------------------------------------------------------------------
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 dpavlin 52 if (iwfAttribute(el, 'checked') || el.checked){
244     val = iwfAttribute(el, 'value') || el.value;
245 dpavlin 46 }
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 dpavlin 51 val = iwfAttribute(el, 'innerText') || el.value;
279 dpavlin 46 break;
280     case 'button':
281     if (el == ctl){
282 dpavlin 51 val = iwfAttribute(el, 'innerText') || el.value;
283 dpavlin 46 }
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 dpavlin 48 window.status = 'Remaining: ' + _iwfPendingRequests;
573 dpavlin 46 } 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 dpavlin 48 // -----------------------------------

Properties

Name Value
svn:mime-type text/cpp

  ViewVC Help
Powered by ViewVC 1.1.26