1. #1
    Sencha User
    Join Date
    Jan 2013
    Posts
    1
    Vote Rating
    1
    lpinto is on a distinguished road

      1  

    Default Element update with load scripts improved

    Element update with load scripts improved


    During the development of our applications it became necessary to load the contents of pages into a Ext.panel or Ext.window.
    The page content that we want to add, has:
    1. HTML
    2. CSS files to import
    3. JS files to import
    4. JavaScript code blocks that contain:
      1. code executions
      2. functions declarations
    Using Ext.dom.Element.update(...) method almost did the trick, but it didn't work when one of the javascript code block has a js file (to be imported) dependency.
    Importing script files, made by the browser, is asynchronous action and after checking the Update source code we notice that when the javascript block is execute, nothing guarantee's that all script files are already imported.

    Our searches leads us to posts saying something like this:
    1. Simple HTML fragments -> use autoLoad/update
    2. Ext components -> use RemoteComponent (or code it yourself, it's really simple)
    3. Entire HTML pages with CSS and/or javascript -> use ManagedIframe
    Link to post

    But we were so close that the decision was: try to resolve the issue.

    Our solution is override the Ext.dom.Element update method with the following code
    We are using Ext JS 4.1.1
    -------------------

    Code:
    
    /* ------------------------------ */
    /**
     * Import JS files and the process JS blocks
     * 
     * @param {String}
     *            Element id
     * @param {Function}
     *            [loadScripts] True to look for and process scripts (defaults to
     *            false)
     * @param {String}
     *            [callback] For async script loading you can be notified when the
     *            update completes
     * @return {Ext.dom.Element} this
     */
    function customFuncLoadScripts(id, interval, html) {
    
    
        /* Constructor */
        function ScriptLoadHandler(position) {
            this.position = position;
            this.eventHandler = function() {
                scriptImported[position] = true;
            };
        }
    
    
        // #1 - BEGIN
        /*
         * Variables copied from the source http://
         * docs.sencha.com/ext-js/4-1/source/Element2.html
         */
        var scriptTagRe = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
        var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
        var typeRe = /\stype=([\'\"])(.*?)\1/i;
        /* #1 - END */
    
    
        var hd, match, attrs, srcMatch, typeMatch, el, s;
    
    
        var bufferedjs = '';
        var scriptImported = new Array();
        var pos = 0;
    
    
        if (!(el = document.getElementById(id))) {
            return false;
        }
        clearInterval(interval);
        Ext.removeNode(el);
        hd = Ext.getHead().dom;
    
    
        while ((match = scriptTagRe.exec(html))) {
            attrs = match[1];
            srcMatch = attrs ? attrs.match(srcRe) : false;
            if (srcMatch && srcMatch[2]) {
                s = document.createElement("script");
                s.src = srcMatch[2];
                typeMatch = attrs.match(typeRe);
                if (typeMatch && typeMatch[2]) {
                    s.type = typeMatch[2];
                }
    
    
                /*
                 * Creates a new Script Load Handler to control when the script is
                 * imported
                 */
                scriptImported[pos] = false;
                handler = new ScriptLoadHandler(pos);
    
    
                if (s.addEventListener) {
                    s.addEventListener("load", handler.eventHandler, false);
                } else if (s.readyState) {
                    s.onreadystatechange = handler.eventHandler;
                }
                pos++;
    
    
                hd.appendChild(s);
            } else if (match[2] && match[2].length > 0) {
                bufferedjs += '\n' + match[2];
            }
        }
    
    
        /*
         * The JS execution block (code below) cannot be executed immediately after
         * the script import block (code above), since if there's a dependency from
         * a JS file and this file hasn't already been imported, undefined errors
         * occur. The solution is to call the JS execution block only when all the
         * scripts have already been imported. This can be accomplished by checking
         * if all the elements in the scriptImported array are true. The loop has a
         * 10 milliseconds gap and runs a maximum of 1000 times, after that give up
         * (time out) we let the errors occur;
         */
        if (bufferedjs != '') {
            var execScriptTries = 1000;
            var intervalExecScript = setInterval(function() {
                // Wait until all scripts are loaded or break after 1000 tries
                var allScriptsLoaded = true;
                for ( var i = 0; i < scriptImported.length; i++) {
                    allScriptsLoaded = allScriptsLoaded && scriptImported[i];
                }
    
    
                if (allScriptsLoaded) {
                    if (window.execScript) {
                        window.execScript(bufferedjs);
                    } else {
                        window.eval(bufferedjs);
                    }
                    clearInterval(intervalExecScript);
                } else if (execScriptTries == 0) {
                    clearInterval(intervalExecScript);
                }
                execScriptTries--;
    
    
            }, 10);
        }
    }
    
    
    /**
     * Updates the innerHTML of this element, optionally searching for and
     * processing scripts.
     * 
     * @param {String}
     *            html The new HTML
     * @param {Boolean}
     *            [loadScripts] True to look for and process scripts (defaults to
     *            false)
     * @param {Function}
     *            [callback] For async script loading you can be notified when the
     *            update completes
     * @return {Ext.dom.Element} this
     */
    Ext.override(Ext.dom.Element.prototype, {
        update : function(html, loadScripts, callback) {
            var me = this, id, dom, interval;
    
    
            if (!me.dom) {
                return me;
            }
            html = html || '';
            dom = me.dom;
    
    
            if (loadScripts !== true) {
                dom.innerHTML = html;
                Ext.callback(callback, me);
                return me;
            }
    
    
            id = Ext.id();
            html += '<span id="' + id + '"></span>';
    
    
            /* #1 - New code replacing the original javascripts loading system */
            /*
             * Variable copied from the source
             * http://docs.sencha.com/ext-js/4-1/source/Element2.html
             */
            var replaceScriptTagRe = /(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig;
    
    
            interval = setInterval(function() {
                customFuncLoadScripts(id, interval, html);
            }, 20);
    
    
            // #1 - END
    
    
            dom.innerHTML = html.replace(replaceScriptTagRe, '');
            return me;
        }
    });

  2. #2
    Sencha - Senior Forum Manager mitchellsimoens's Avatar
    Join Date
    Mar 2007
    Location
    Gainesville, FL
    Posts
    37,647
    Vote Rating
    899
    mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute

      0  

    Default


    Thanks for the contribution!
    Mitchell Simoens @SenchaMitch
    Sencha Inc, Senior Forum Manager
    ________________
    Check out my GitHub, lots of nice things for Ext JS 4 and Sencha Touch 2
    https://github.com/mitchellsimoens

    Think my support is good? Get more personalized support via a support subscription. https://www.sencha.com/store/

    Need more help with your app? Hire Sencha Services services@sencha.com

    Want to learn Sencha Touch 2? Check out Sencha Touch in Action that is in print!

    When posting code, please use BBCode's CODE tags.

Thread Participants: 1

Tags for this Thread