[ajax, IE] rewrite some code to avoid duplicated evaluation with IE
authorSylvain Thénault <sylvain.thenault@logilab.fr>
Tue, 12 Jul 2011 10:36:22 +0200
changeset 7649 ede740bd7077
parent 7648 5d5d98930247
child 7650 278fe9c1f3ad
[ajax, IE] rewrite some code to avoid duplicated evaluation with IE
utils.py
web/data/cubicweb.ajax.js
web/data/cubicweb.js
--- a/utils.py	Mon Jul 11 19:48:37 2011 +0200
+++ b/utils.py	Tue Jul 12 10:36:22 2011 +0200
@@ -362,13 +362,33 @@
         # 4/ js files
         jsfiles = ((x, None) for x in self.jsfiles)
         for jsfile, media in self.group_urls(jsfiles) if self.datadir_url else jsfiles:
-            w(u'<script type="text/javascript" src="%s"></script>\n' %
-              xml_escape(jsfile))
+            if skiphead:
+                # Don't insert <script> tags directly as they would be
+                # interpreted directly by some browsers (e.g. IE).
+                # Use <pre class="script"> tags instead and let
+                # `loadAjaxHtmlHead` handle the script insertion / execution.
+                w(u'<pre class="script" src="%s"></pre>\n' %
+                  xml_escape(jsfile))
+                # FIXME: a probably better implementation might be to add
+                #        JS or CSS urls in a JS list that loadAjaxHtmlHead
+                #        would iterate on and postprocess:
+                #            cw._ajax_js_scripts.push('myscript.js')
+                #        Then, in loadAjaxHtmlHead, do something like:
+                #            jQuery.each(cw._ajax_js_script, jQuery.getScript)
+            else:
+                w(u'<script type="text/javascript" src="%s"></script>\n' %
+                  xml_escape(jsfile))
         # 5/ post inlined scripts (i.e. scripts depending on other JS files)
         if self.post_inlined_scripts:
-            w(self.xhtml_safe_script_opening)
-            w(u'\n\n'.join(self.post_inlined_scripts))
-            w(self.xhtml_safe_script_closing)
+            if skiphead:
+                for script in self.post_inlined_scripts:
+                    w(u'<pre class="script">')
+                    w(script)
+                    w(u'</pre>')
+            else:
+                w(self.xhtml_safe_script_opening)
+                w(u'\n\n'.join(self.post_inlined_scripts))
+                w(self.xhtml_safe_script_closing)
         header = super(HTMLHead, self).getvalue()
         if skiphead:
             return header
--- a/web/data/cubicweb.ajax.js	Mon Jul 11 19:48:37 2011 +0200
+++ b/web/data/cubicweb.ajax.js	Tue Jul 12 10:36:22 2011 +0200
@@ -93,9 +93,10 @@
 jQuery.extend(cw.ajax, {
     /* variant of jquery evalScript with cache: true in ajax call */
     _evalscript: function ( i, elem ) {
-       if ( elem.src ) {
+       var src = elem.getAttribute('src');
+       if (src) {
            jQuery.ajax({
-               url: elem.src,
+               url: src,
                async: false,
                cache: true,
                dataType: "script"
@@ -145,62 +146,76 @@
             );
         }
         return resources;
+    },
+
+    _buildMissingResourcesUrl: function(url, loadedResources) {
+        var resources = cw.ajax._listResources(url);
+        var missingResources = $.grep(resources, function(resource) {
+            return $.inArray(resource, loadedResources) == -1;
+        });
+        cw.utils.extend(loadedResources, missingResources);
+        var missingResourceUrl = null;
+        if (missingResources.length == 1) {
+            // only one resource missing: build a node with a single resource url
+            // (maybe the browser has it in cache already)
+            missingResourceUrl = missingResources[0];
+        } else if (missingResources.length > 1) {
+            // several resources missing: build a node with a concatenated
+            // resources url
+            var dataurl = cw.ajax._modconcatLikeUrl(url)[1];
+            var missing_path = $.map(missingResources, function(resource) {
+                return resource.substring(dataurl.length);
+            });
+            missingResourceUrl = dataurl + '??' + missing_path.join(',');
+        }
+        return missingResourceUrl;
+    },
+
+    _loadAjaxStylesheets: function($responseHead, $head) {
+        $responseHead.find('link[href]').each(function(i) {
+            var $srcnode = $(this);
+            var url = $srcnode.attr('href');
+            if (url) {
+                var missingStylesheetsUrl = cw.ajax._buildMissingResourcesUrl(url, cw.loaded_links);
+                // compute concat-like url for missing resources and append <link>
+                // element to $head
+                if (missingStylesheetsUrl) {
+                    $srcnode.attr('href', missingStylesheetsUrl);
+                    $srcnode.appendTo($head);
+                }
+            }
+        });
+        $responseHead.find('link[href]').remove();
+    },
+
+    _loadAjaxScripts: function($responseHead, $head) {
+        $responseHead.find('pre.script').each(function(i) {
+            var $srcnode = $(this);
+            var url = $srcnode.attr('src');
+            if (url) {
+                var missingScriptsUrl = cw.ajax._buildMissingResourcesUrl(url, cw.loaded_scripts);
+                if (missingScriptsUrl) {
+                    $srcnode.attr('src', missingScriptsUrl);
+                    /* special handling of <script> tags: script nodes appended by jquery
+                     * use uncached ajax calls and do not appear in the DOM
+                     * (See comments in response to Syt on // http://api.jquery.com/append/),
+                     * which cause undesired duplicated load in our case. We now handle
+                     * a list of already loaded resources, since bare DOM api gives bugs with the
+                     * server-response event, and we lose control on when the
+                     * script is loaded (jQuery loads it immediately). */
+                    cw.ajax.evalscripts($srcnode);
+                }
+            } else {
+                // <script> contains inlined javascript code, node content
+                // must be evaluated
+    	        jQuery.globalEval($srcnode.text());
+    	    }
+        });
+        $responseHead.find('pre.script').remove();
     }
 });
 
 //============= utility function handling remote calls responses. ==============//
-function _loadAjaxHtmlHead($node, $head, tag, srcattr) {
-    var jqtagfilter = tag + '[' + srcattr + ']';
-    if (cw['loaded_'+srcattr] === undefined) {
-        cw['loaded_'+srcattr] = [];
-        var loaded = cw['loaded_'+srcattr];
-        jQuery('head ' + jqtagfilter).each(function(i) {
-            // tab1.push.apply(tab1, tab2) <=> tab1 += tab2 (python-wise)
-            loaded.push.apply(loaded, cw.ajax._listResources(this.getAttribute(srcattr)));
-        });
-    } else {
-        var loaded = cw['loaded_'+srcattr];
-    }
-    $node.find(tag).each(function(i) {
-        var $srcnode = jQuery(this);
-        var url = $srcnode.attr(srcattr);
-        if (url) {
-            /* special handling of <script> tags: script nodes appended by jquery
-             * use uncached ajax calls and do not appear in the DOM
-             * (See comments in response to Syt on // http://api.jquery.com/append/),
-             * which cause undesired duplicated load in our case. We now handle
-             * a list of already loaded resources, since bare DOM api gives bugs with the
-             * server-response event, and we lose control on when the
-             * script is loaded (jQuery loads it immediately). */
-            var resources = cw.ajax._listResources(url);
-            var missingResources = $.grep(resources, function(resource) {
-                return $.inArray(resource, loaded) == -1;
-            });
-            loaded.push.apply(loaded, missingResources);
-            if (missingResources.length == 1) {
-                // only one resource missing: build a node with a single resource url
-                // (maybe the browser has it in cache already)
-                $srcnode.attr(srcattr, missingResources[0]);
-            } else if (missingResources.length > 1) {
-                // several resources missing: build a node with a concatenated
-                // resources url
-                var dataurl = cw.ajax._modconcatLikeUrl(url)[1];
-                var missing_path = $.map(missingResources, function(resource) {
-                    return resource.substring(dataurl.length);
-                });
-                $srcnode.attr(srcattr, dataurl + '??' + missing_path.join(','));
-            } else { return ; }
-            // === will work if both arguments are of the same type
-            if ( $srcnode.attr('type') === 'text/javascript' ) {
-                cw.ajax.evalscripts($srcnode);
-            } else {
-                $srcnode.appendTo($head);
-            }
-        }
-    });
-    $node.find(jqtagfilter).remove();
-}
-
 /**
  * .. function:: function loadAjaxHtmlHead(response)
  *
@@ -216,8 +231,8 @@
     if (!$responseHead.length) {
         return response;
     }
-    _loadAjaxHtmlHead($responseHead, $head, 'script', 'src');
-    _loadAjaxHtmlHead($responseHead, $head, 'link', 'href');
+    cw.ajax._loadAjaxStylesheets($responseHead, $head);
+    cw.ajax._loadAjaxScripts($responseHead, $head);
     // add any remaining children (e.g. meta)
     $responseHead.children().appendTo($head);
     // remove original container, which is now empty
@@ -487,11 +502,6 @@
         $fragment.loadxhtml('json', ajaxFuncArgs('view', extraparams));
     }
 }
-
-jQuery(document).ready(function() {
-    _loadDynamicFragments();
-});
-
 function unloadPageData() {
     // NOTE: do not make async calls on unload if you want to avoid
     //       strange bugs
@@ -817,3 +827,16 @@
     deferred = deferred.addCallback(resetCursor);
     return deferred;
 }
+
+jQuery(document).ready(function() {
+    _loadDynamicFragments();
+    // build loaded_scripts / loaded_links lists
+    cw.loaded_scripts = [];
+    jQuery('head script[src]').each(function(i) {
+        cw.utils.extend(cw.loaded_scripts, cw.ajax._listResources(this.getAttribute('src')));
+    });
+    cw.loaded_links = [];
+    jQuery('head link[href]').each(function(i) {
+        cw.utils.extend(cw.loaded_links, cw.ajax._listResources(this.getAttribute('href')));
+    });
+});
--- a/web/data/cubicweb.js	Mon Jul 11 19:48:37 2011 +0200
+++ b/web/data/cubicweb.js	Tue Jul 12 10:36:22 2011 +0200
@@ -318,6 +318,17 @@
         }
     },
 
+
+    /**
+     * .. function:: extend(array1, array2)
+     *
+     * equivalent of python ``+=`` statement on lists (array1 += array2)
+     */
+    extend: function(array1, array2) {
+        array1.push.apply(array1, array2);
+        return array1; // return array1 for convenience
+    },
+
     /**
      * .. function:: difference(lst1, lst2)
      *