<commit_before><html>
	<head>
		<title>Basket.js</title>
	</head>
	<link rel="stylesheet" type="text/css" href="resources/base.css">
	<body>

	<div id="container">
		<h1>basket.js</h1>
		<h2>A (proof-of-concept) loader for caching scripts in localStorage </h2>
		  <div id="message" class="content">If you're seeing this message, something has gone wrong.</div>
		  <div class="content">
		  <p>
		  	Basket.js was created as a response to a tweet by <NAME> asking that the jQuery project start caching jQuery in localStorage for performance reasons. Intrigued by just how well this might work, I began exploring a minimalist solution to it.
		  </p>

		  <p>
		  	The results of these efforts is a script loader that treats localStorage as a cache: if script(s) have previously been saved locally, they will simply be loaded and injected into the current document. If not, they will be XHR'd in for use right away, but cached so that no additional loads are required in the future.
		  </p>
		  <p>
		  	Before we get into the really interesting part of the experiment (performance benchmarking), let's briefly review the basket.js API:
		  </p>
		  <p>
		  <ul>
		  	<li><strong>basket.add()</strong></li>
		  	<li><strong>basket.wait()</strong></li>
		  </ul>

		  <p><strong>basket.add( uri )</strong></p>

<p>uri: A relative URI to a local script to load.</p>

<p><code>.add()</code> allows us to construct loader chains, each of which instruct basket.js to fetch (either externally or from a localStorage cached-copy) a script defined by a supplied uri. The basic loading of a single uri could be achieved as follows:</p>

<pre><code>basket.add('jquery.js');
</code></pre>

<p>This will check to see if an item with the key <code>basket-jquery.js</code> exists in localStorage (i.e a cached copy of the script). If present, the script contents are read from storage and injected into the current page. If not present, the script will be fetched, cached in storage and then similarly injected into the document for use.</p>

<p>basket.js also supports chaining and so requesting multiple scripts is also straight-forward:</p>

<pre><code>basket
   .add('jquery.js')
   .add('underscore.js')
   .add('backbone.js');
</code></pre>

<p><em>Note that at present, JSONP/server-side middleware is necessary for the support of remote scripts. This is down to access of script content being limited under cross-origin policies.</em></p>

<p><strong>basket.wait( [,callback] )</strong></p>

<p>callback: An optional function to be executed once a script has loaded</p>

<p>When <code>wait()</code> is inserted into a chain, it informs the chain to ensure previous scripts inserted into the chain complete their loading prior to allowing the next item to load and execute. At it&rsquo;s most basic, <code>wait()</code> can be used as follows:</p>

<pre><code>basket
    .add('jquery.js').wait()
    .add('jquery.plugin.js');
</code></pre>

<p>You can also specify an (optional) callback (typically an anonymous function) that will be executed in order following the previous scripts executing and before subsequent scripts in the chain are executed.</p>

<pre><code>basket
    .add('jquery.js')
    .add('underscore.js')
    .add('app.js').wait(function(){
        $('#message').html('Live long and prosper, bro');
    });
</code></pre>

<h2>Performance testing</h2>
<p>
Now onto the juicy part of the experiment. What we're interested in asserting is whether there are any significant performance benefits in locally caching a script in localStorage vs. caching in the browser's standard data cache.</p>
<p>In order to simplfy this, let's reduce some of the code in basket.js down to loading the contents of a script from localStorage and then injecting this into the current document. The following snippet should hopefully suffice:</p>

<pre>
// Test for localStorage support
function hasLocalStorage(a, b) {
    try {
        return (a = localStorage).setItem(b, a), a.removeItem(b), !0
    } catch (c) {

    }
};

if (hasLocalStorage()) {

    // where scriptContent is the predefined content of
    // a script we want to benchmark loading
    // (see the jsPerf test for complete code)

    localStorage.setItem('myscript.js', scriptContent);
    var text = localStorage.getItem('myscript.js');
    var script = d.createElement("script"),
        fragment = d.createDocumentFragment();
    script.defer = true;
    head = d.head || d.getElementsByTagName("head")[0];
    script.appendChild(d.createTextNode(text));
    fragment.appendChild(script);
    head.appendChild(fragment);

    // Cleanup for jsPerf test
    localStorage.clear();

}
</pre>

<p>We then need a snippet that will emulate loading a script in from the browser cache.</p>

</p>



		  </p>

		  </div>
	</div>

	<script type="text/template" id="intro">
		<p><em>This section was rendered using Underscore templates - just one of a number of scripts that were added to this document using basket.js when you loaded it and should now be <strong>cached</strong> in localStorage (if supported).
		    Backbone.js was also loaded and we created a new model with the name: <%= name %> (just for funsies).</em></p>
	</script>


	  <script src="basket.js"></script>
	  <script>
	     var message, str;

     	 basket
	       .add("test/jquery-1.7.1.min.js").wait()
	       .add("test/underscore-min.js")
      	   .add("test/backbone-min.js").wait(function() {


			var testModel = Backbone.Model.extend({
			  idAttribute: "_id"
			});

			var myModel = new testModel({ _id: 4, name: "<NAME>" });
			

  	   		message = $('#message');
  	   		var templateContent = $('#intro').html();
  	   		var compiled = _.template(templateContent);
  	   		message.html( compiled({name: myModel.get('name')}));



     	    });

	  </script>
	</body>
</html>

<commit_msg>updating tutorial/info<commit_after><html>
	<head>
		<title>Basket.js</title>
	</head>
	<link rel="stylesheet" type="text/css" href="resources/base.css">
	<body>

	<div id="container">
		<h1>basket.js</h1>
		<h2>A simple loader for caching scripts in localStorage </h2>
		  <div id="message" class="content">If you're seeing this message, something has gone wrong.</div>
		  <div class="content">
		  <p>
		  	Basket.js was created as a response to a tweet by <NAME> asking that the jQuery project start caching jQuery in localStorage for performance reasons. Intrigued by just how well this might work, I began exploring a minimalist solution to it.
		  </p>

		  <p>
		  	You might remember that Souders previously did a write-up on search engines making use of localStorage caching back in 2011. His conclusions were:
		  	<blockquote>Bing and Google Search make extensive use of localStorage for stashing SCRIPT blocks that are used on subsequent page views. None of the other top sites from my previous post use localStorage in this way. Are Bing and Google Search onto something? Yes, definitely.</blockquote>
		  </p>
		  <p>
		  	 To help provide a more general solution for this problem, I put together a script loader that treats localStorage as a cache: if script(s) have previously been saved locally, they will simply be loaded and injected into the current document. If not, they will be XHR'd in for use right away, but cached so that no additional loads are required in the future.
		  </p>
		  <p>
		  	basket.js is extremely simple, but it gets the job done. Let's briefly review its API:
		  </p>
		  <p>
		  <ul>
		  	<li><strong>basket.add()</strong></li>
		  	<li><strong>basket.wait()</strong></li>
		  </ul>

		  <p><strong>basket.add( uri )</strong></p>

<p>uri: A relative URI to a local script to load.</p>

<p><code>.add()</code> allows us to construct loader chains, each of which instruct basket.js to fetch (either externally or from a localStorage cached-copy) a script defined by a supplied uri. The basic loading of a single uri could be achieved as follows:</p>

<pre><code>basket.add('jquery.js');
</code></pre>

<p>This will check to see if an item with the key <code>basket-jquery.js</code> exists in localStorage (i.e a cached copy of the script). If present, the script contents are read from storage and injected into the current page. If not present, the script will be fetched, cached in storage and then similarly injected into the document for use.</p>

<p>basket.js also supports chaining and so requesting multiple scripts is also straight-forward:</p>

<pre><code>basket
   .add('jquery.js')
   .add('underscore.js')
   .add('backbone.js');
</code></pre>

<p><em>Note that at present, JSONP/server-side middleware is necessary for the support of remote scripts. This is down to access of script content being limited under cross-origin policies.</em></p>

<p><strong>basket.wait( [,callback] )</strong></p>

<p>callback: An optional function to be executed once a script has loaded</p>

<p>When <code>wait()</code> is inserted into a chain, it informs the chain to ensure previous scripts inserted into the chain complete their loading prior to allowing the next item to load and execute. At it&rsquo;s most basic, <code>wait()</code> can be used as follows:</p>

<pre><code>basket
    .add('jquery.js').wait()
    .add('jquery.plugin.js');
</code></pre>

<p>You can also specify an (optional) callback (typically an anonymous function) that will be executed in order following the previous scripts executing and before subsequent scripts in the chain are executed.</p>

<pre><code>basket
    .add('jquery.js')
    .add('underscore.js')
    .add('app.js').wait(function(){
        $('#message').html('Live long and prosper, bro');
    });
</code></pre>


<h2>Implementation</h2>
<pre>
/* Basket.js
 * A script-loader that handles caching scripts in localStorage
 * where supported.
 * http://addyosmani.com/
 * Credits: <NAME>, <NAME>, Ironsjp.
 * Copyright (c) 2012 <NAME>; 
 * Licensed MIT, GPL 
 */

 ;(function (w, d) {

    function basketLoader() {

        var
        _storagePrefix = "basket-",
            _localStorage = function (a, b) {
                try {
                    return (
                    a = localStorage).setItem(b, a), 
                    a.removeItem(b), !0
                } catch (c) {}
            }(),
            scripts = [],
            scriptsExecuted = 0,
            waitCount = 0,
            waitCallbacks = [],

            // Minimalist Cross-browser XHR
            // from https://gist.github.com/991713
            getXMLObj = function (s, a) {
                a = [a = "Msxml2.XMLHTTP", a + ".3.0", a + ".6.0"];
                do
                try {
                    s = a.pop();
                    return new(s ? ActiveXObject : XMLHttpRequest)(s)
                } catch (e) {;

                }
                while (s)
            },

            getUrl = function (url, callback) {
                var xhr = getXMLObj();
                xhr.open("GET", url, true);
                xhr.onreadystatechange = function (e) {
                    if (xhr.readyState === 4) {
                        callback(xhr.responseText);
                    }
                };
                xhr.send();
            },

            injectScript = function (text) {
                var script = d.createElement("script"),
                    fragment = d.createDocumentFragment();
                script.defer = true;
                head = d.head || d.getElementsByTagName("head")[0];
                script.appendChild(d.createTextNode(text));
                fragment.appendChild(script);
                head.appendChild(fragment);
            },

            queueExec = function (waitCount) {
                var script, i, j, callback;

                if (scriptsExecuted >= waitCount) {
                    for (i = 0; i < scripts.length; i++) {
                        script = scripts[i];

                        if (!script) {
                            // loading/executed
                            continue;
                        }
                        scripts[i] = null;
                        injectScript(script);
                        scriptsExecuted++;

                        for (j = i; j < scriptsExecuted; j++) {
                            if (callback = waitCallbacks[j]) {
                                console.log('in callbacks');
                                waitCallbacks[j] = null;
                                callback();
                            }
                        }
                    }
                }
            };

        return {

            add: function (path) {

                var key = _storagePrefix + path,
                    scriptIndex = scripts.length,
                    _waitCount = waitCount;


                scripts[scriptIndex] = null;

                if (_localStorage && localStorage.getItem(key)) {
                    scripts[scriptIndex] = localStorage.getItem(key);
                    queueExec(_waitCount);
                } else {
                    getUrl(path, function (text) {
                        (_localStorage) && localStorage.setItem(key, text);
                        scripts[scriptIndex] = text;
                        queueExec(_waitCount);
                    });
                }
                return this;
            },

            wait: function (callback) {
                waitCount = scripts.length;


                if (callback) {
                    (scriptsExecuted >= waitCount - 1) ? 
                    callback() : waitCallbacks[waitCount - 1] = callback;
                }

                return this;
            }
        };

    }


    w['basket'] = basketLoader();

})(this, document);
</pre>
<!--
<h2>Performance testing</h2>
<p>
Now onto the juicy part of the experiment. What we're interested in asserting is whether there are any significant performance benefits in locally caching a script in localStorage vs. caching in the browser's standard data cache.</p>
<p>In order to simplfy this, let's reduce some of the code in basket.js down to loading the contents of a script from localStorage and then injecting this into the current document. The following snippet should hopefully suffice:</p>

<pre>
// Test for localStorage support
function hasLocalStorage(a, b) {
    try {
        return (a = localStorage).setItem(b, a), a.removeItem(b), !0
    } catch (c) {

    }
};

if (hasLocalStorage()) {
    var text = localStorage.getItem('myscript.js');
    var script = d.createElement("script"),
        fragment = d.createDocumentFragment();
    script.defer = true;
    head = d.head || d.getElementsByTagName("head")[0];
    script.appendChild(d.createTextNode(text));
    fragment.appendChild(script);
    head.appendChild(fragment);

}
</pre>

<p>We then need a snippet that will emulate loading a script in from the browser cache.</p>
-->
</p>



		  </p>

		  </div>
	</div>

	<script type="text/template" id="intro">
		<p><em>This section was rendered using Underscore templates - just one of a number of scripts that were added to this document using basket.js when you loaded it and should now be <strong>cached</strong> in localStorage (if supported).
		    Backbone.js was also loaded and we created a new model with the name: <%= name %> (just for funsies).</em></p>
	</script>


	  <script src="basket.js"></script>
	  <script>
	     var message, str;

     	 basket
	       .add("test/jquery-1.7.1.min.js").wait()
	       .add("test/underscore-min.js")
      	   .add("test/backbone-min.js").wait(function() {


			var testModel = Backbone.Model.extend({
			  idAttribute: "_id"
			});

			var myModel = new testModel({ _id: 4, name: "<NAME>" });
			

  	   		message = $('#message');
  	   		var templateContent = $('#intro').html();
  	   		var compiled = _.template(templateContent);
  	   		message.html( compiled({name: myModel.get('name')}));



     	    });

	  </script>
	</body>
</html>


{'fraction_non_alphanumeric': 0.11795420231504782, 'fraction_numerical': 0.003334172118772018, 'mean_word_length': 2.442399307059333, 'pattern_counts': {'":': 0, '<': 243, '<?xml version=': 0, '>': 243, 'https://': 1, 'lorem ipsum': 0, 'www.': 0, 'xml': 1}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2514627', 'n_tokens_mistral': 4731, 'n_tokens_neox': 4355, 'n_words': 1595}