<!DOCTYPE html> <html> <head> <title>backbone.localstorage.js</title> <meta http-equiv="content-type" content="text/html; charset=UTF-8"> <meta name="viewport" content="width=device-width, target-densitydpi=160dpi, initial-scale=1.0; maximum-scale=1.0; user-scalable=0;"> <link rel="stylesheet" media="all" href="docco.css" /> </head> <body> <div id="container"> <div id="background"></div> <ul id="jump_to"> <li> <a class="large" href="javascript:void(0);">Jump To …</a> <a class="small" href="javascript:void(0);">+</a> <div id="jump_wrapper"> <div id="jump_page"> <a class="source" href="backbone.localstorage.html"> backbone.localstorage.js </a> <a class="source" href="todos.html"> todos.js </a> </div> </li> </ul> <ul class="sections"> <li id="title"> <div class="annotation"> <h1>backbone.localstorage.js</h1> </div> </li> <li id="section-1"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-1">¶</a> </div> </div> <div class="content"><div class='highlight'><pre><span class="comment">/** * Backbone localStorage Adapter * Version 1.1.0 * * https://github.com/jeromegn/Backbone.localStorage */</span> (<span class="function"><span class="keyword">function</span> <span class="params">(root, factory)</span> {</span> <span class="keyword">if</span> (<span class="keyword">typeof</span> define === <span class="string">"function"</span> && define.amd) {</pre></div></div> </li> <li id="section-2"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-2">¶</a> </div> <p>AMD. Register as an anonymous module.</p> </div> <div class="content"><div class='highlight'><pre> define([<span class="string">"underscore"</span>,<span class="string">"backbone"</span>], <span class="keyword">function</span>(_, Backbone) {</pre></div></div> </li> <li id="section-3"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-3">¶</a> </div> <p>Use global variables if the locals are undefined.</p> </div> <div class="content"><div class='highlight'><pre> <span class="keyword">return</span> factory(_ || root._, Backbone || root.Backbone); }); } <span class="keyword">else</span> {</pre></div></div> </li> <li id="section-4"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-4">¶</a> </div> <p>RequireJS isn't being used. Assume underscore and backbone are loaded in <script> tags</p> </div> <div class="content"><div class='highlight'><pre> factory(_, Backbone); } }(<span class="keyword">this</span>, <span class="keyword">function</span>(_, Backbone) {</pre></div></div> </li> <li id="section-5"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-5">¶</a> </div> <p>A simple module to replace <code>Backbone.sync</code> with <em>localStorage</em>-based persistence. Models are given GUIDS, and saved into a JSON object. Simple as that.</p> <p>Hold reference to Underscore.js and Backbone.js in the closure in order to make things work even if they are removed from the global namespace</p> <p>Generate four random hex digits.</p> </div> <div class="content"><div class='highlight'><pre><span class="function"><span class="keyword">function</span> <span class="title">S4</span><span class="params">()</span> {</span> <span class="keyword">return</span> (((<span class="number">1</span>+Math.random())*<span class="number">0x10000</span>)|<span class="number">0</span>).toString(<span class="number">16</span>).substring(<span class="number">1</span>); };</pre></div></div> </li> <li id="section-6"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-6">¶</a> </div> <p>Generate a pseudo-GUID by concatenating random hexadecimal.</p> </div> <div class="content"><div class='highlight'><pre><span class="function"><span class="keyword">function</span> <span class="title">guid</span><span class="params">()</span> {</span> <span class="keyword">return</span> (S4()+S4()+<span class="string">"-"</span>+S4()+<span class="string">"-"</span>+S4()+<span class="string">"-"</span>+S4()+<span class="string">"-"</span>+S4()+S4()+S4()); };</pre></div></div> </li> <li id="section-7"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-7">¶</a> </div> <p>Our Store is represented by a single JS object in <em>localStorage</em>. Create it with a meaningful name, like the name you'd give a table. window.Store is deprectated, use Backbone.LocalStorage instead</p> </div> <div class="content"><div class='highlight'><pre>Backbone.LocalStorage = window.Store = <span class="keyword">function</span>(name) { <span class="keyword">this</span>.name = name; <span class="keyword">var</span> store = <span class="keyword">this</span>.localStorage().getItem(<span class="keyword">this</span>.name); <span class="keyword">this</span>.records = (store && store.split(<span class="string">","</span>)) || []; }; _.extend(Backbone.LocalStorage.prototype, {</pre></div></div> </li> <li id="section-8"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-8">¶</a> </div> <p>Save the current state of the <strong>Store</strong> to <em>localStorage</em>.</p> </div> <div class="content"><div class='highlight'><pre> save: <span class="keyword">function</span>() { <span class="keyword">this</span>.localStorage().setItem(<span class="keyword">this</span>.name, <span class="keyword">this</span>.records.join(<span class="string">","</span>)); },</pre></div></div> </li> <li id="section-9"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-9">¶</a> </div> <p>Add a model, giving it a (hopefully)-unique GUID, if it doesn't already have an id of it's own.</p> </div> <div class="content"><div class='highlight'><pre> create: <span class="keyword">function</span>(model) { <span class="keyword">if</span> (!model.id) { model.id = guid(); model.set(model.idAttribute, model.id); } <span class="keyword">this</span>.localStorage().setItem(<span class="keyword">this</span>.name+<span class="string">"-"</span>+model.id, JSON.stringify(model)); <span class="keyword">this</span>.records.push(model.id.toString()); <span class="keyword">this</span>.save(); <span class="keyword">return</span> <span class="keyword">this</span>.find(model); },</pre></div></div> </li> <li id="section-10"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-10">¶</a> </div> <p>Update a model by replacing its copy in <code>this.data</code>.</p> </div> <div class="content"><div class='highlight'><pre> update: <span class="keyword">function</span>(model) { <span class="keyword">this</span>.localStorage().setItem(<span class="keyword">this</span>.name+<span class="string">"-"</span>+model.id, JSON.stringify(model)); <span class="keyword">if</span> (!_.include(<span class="keyword">this</span>.records, model.id.toString())) <span class="keyword">this</span>.records.push(model.id.toString()); <span class="keyword">this</span>.save(); <span class="keyword">return</span> <span class="keyword">this</span>.find(model); },</pre></div></div> </li> <li id="section-11"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-11">¶</a> </div> <p>Retrieve a model from <code>this.data</code> by id.</p> </div> <div class="content"><div class='highlight'><pre> find: <span class="keyword">function</span>(model) { <span class="keyword">return</span> <span class="keyword">this</span>.jsonData(<span class="keyword">this</span>.localStorage().getItem(<span class="keyword">this</span>.name+<span class="string">"-"</span>+model.id)); },</pre></div></div> </li> <li id="section-12"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-12">¶</a> </div> <p>Return the array of all models currently in storage.</p> </div> <div class="content"><div class='highlight'><pre> findAll: <span class="keyword">function</span>() { <span class="keyword">return</span> _(<span class="keyword">this</span>.records).chain() .map(<span class="keyword">function</span>(id){ <span class="keyword">return</span> <span class="keyword">this</span>.jsonData(<span class="keyword">this</span>.localStorage().getItem(<span class="keyword">this</span>.name+<span class="string">"-"</span>+id)); }, <span class="keyword">this</span>) .compact() .value(); },</pre></div></div> </li> <li id="section-13"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-13">¶</a> </div> <p>Delete a model from <code>this.data</code>, returning it.</p> </div> <div class="content"><div class='highlight'><pre> destroy: <span class="keyword">function</span>(model) { <span class="keyword">if</span> (model.isNew()) <span class="keyword">return</span> <span class="literal">false</span> <span class="keyword">this</span>.localStorage().removeItem(<span class="keyword">this</span>.name+<span class="string">"-"</span>+model.id); <span class="keyword">this</span>.records = _.reject(<span class="keyword">this</span>.records, <span class="keyword">function</span>(id){ <span class="keyword">return</span> id === model.id.toString(); }); <span class="keyword">this</span>.save(); <span class="keyword">return</span> model; }, localStorage: <span class="keyword">function</span>() { <span class="keyword">return</span> localStorage; },</pre></div></div> </li> <li id="section-14"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-14">¶</a> </div> <p>fix for "illegal access" error on Android when JSON.parse is passed null</p> </div> <div class="content"><div class='highlight'><pre> jsonData: <span class="function"><span class="keyword">function</span> <span class="params">(data)</span> {</span> <span class="keyword">return</span> data && JSON.parse(data); } });</pre></div></div> </li> <li id="section-15"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-15">¶</a> </div> <p>localSync delegate to the model or collection's <em>localStorage</em> property, which should be an instance of <code>Store</code>. window.Store.sync and Backbone.localSync is deprectated, use Backbone.LocalStorage.sync instead</p> </div> <div class="content"><div class='highlight'><pre>Backbone.LocalStorage.sync = window.Store.sync = Backbone.localSync = <span class="keyword">function</span>(method, model, options) { <span class="keyword">var</span> store = model.localStorage || model.collection.localStorage; <span class="keyword">var</span> resp, errorMessage, syncDfd = $.Deferred && $.Deferred(); <span class="comment">//If $ is having Deferred - use it.</span> <span class="keyword">try</span> { <span class="keyword">switch</span> (method) { <span class="keyword">case</span> <span class="string">"read"</span>: resp = model.id != <span class="literal">undefined</span> ? store.find(model) : store.findAll(); <span class="keyword">break</span>; <span class="keyword">case</span> <span class="string">"create"</span>: resp = store.create(model); <span class="keyword">break</span>; <span class="keyword">case</span> <span class="string">"update"</span>: resp = store.update(model); <span class="keyword">break</span>; <span class="keyword">case</span> <span class="string">"delete"</span>: resp = store.destroy(model); <span class="keyword">break</span>; } } <span class="keyword">catch</span>(error) { <span class="keyword">if</span> (error.code === DOMException.QUOTA_EXCEEDED_ERR && window.localStorage.length === <span class="number">0</span>) errorMessage = <span class="string">"Private browsing is unsupported"</span>; <span class="keyword">else</span> errorMessage = error.message; } <span class="keyword">if</span> (resp) { model.trigger(<span class="string">"sync"</span>, model, resp, options); <span class="keyword">if</span> (options && options.success) options.success(resp); <span class="keyword">if</span> (syncDfd) syncDfd.resolve(resp); } <span class="keyword">else</span> { errorMessage = errorMessage ? errorMessage : <span class="string">"Record Not Found"</span>; <span class="keyword">if</span> (options && options.error) options.error(errorMessage); <span class="keyword">if</span> (syncDfd) syncDfd.reject(errorMessage); }</pre></div></div> </li> <li id="section-16"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-16">¶</a> </div> <p>add compatibility with $.ajax always execute callback for success and error</p> </div> <div class="content"><div class='highlight'><pre> <span class="keyword">if</span> (options && options.complete) options.complete(resp); <span class="keyword">return</span> syncDfd && syncDfd.promise(); }; Backbone.ajaxSync = Backbone.sync; Backbone.getSyncMethod = <span class="keyword">function</span>(model) { <span class="keyword">if</span>(model.localStorage || (model.collection && model.collection.localStorage)) { <span class="keyword">return</span> Backbone.localSync; } <span class="keyword">return</span> Backbone.ajaxSync; };</pre></div></div> </li> <li id="section-17"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-17">¶</a> </div> <p>Override 'Backbone.sync' to default to localSync, the original 'Backbone.sync' is still available in 'Backbone.ajaxSync'</p> </div> <div class="content"><div class='highlight'><pre>Backbone.sync = <span class="keyword">function</span>(method, model, options) { <span class="keyword">return</span> Backbone.getSyncMethod(model).apply(<span class="keyword">this</span>, [method, model, options]); }; <span class="keyword">return</span> Backbone.LocalStorage; }));</pre></div></div> </li> </ul> </div> </body> </html>