Published using Google Docs
jQuery Mobile JSON Local Storage 2
Updated automatically every 5 minutes

jQuery Mobile JSON Local Storage 2

Based on http://blog.jamesjacobs.me/2014/01/simple-todo-app-jquery-mobile-localstorage/ 

Continue from the previous exercise http://xdk-steps.blogspot.com/2015/02/jquery-mobile-json-local-storage-1.html 

1) Create LocalStorage Object

1.1) Find the code below.

If you uncomment the codes highlighted in green colour, you will get an error during emulation..

To fix this error, we have to define LocalStorageDB.

1.2) Create another JavaScript file local-storage.js

Enter the following codes

var LocalStorageDB = function(successCallback, errorCallback) {

}

1.3) Add the reference to the HEAD section of index.html

<head>

    <meta charset="utf-8">

    <meta name="viewport" content="width=device-width, initial-scale=1">

    <title>jQuery Mobile To Do</title>

    <link rel="stylesheet"  href="http://code.jquery.com/mobile/1.3.2/jquery.mobile-1.3.2.min.css">

    <script src="http://code.jquery.com/jquery-1.10.2.min.js"></script>

    <script src="http://code.jquery.com/mobile/1.3.2/jquery.mobile-1.3.2.min.js"></script>

<script src="./js/local-storage.js"></script>

<script src="./js/app.js"></script>

</head>

Info:

“<script src="./js/local-storage.js"></script>” must be entered before “<script src="./js/app.js"></script>” so that LocalStorageDB would be recognised by the codes in app.js

2) Insert Call Delay error capture code

Call Delay error capture sets the time-out limit during which operation must be completed or else terminated.

var LocalStorageDB = function(successCallback, errorCallback) {

        // Used to simulate async calls. This is done to provide a consistent interface with storage methods like WebSQL and serverside ajax calls

    var callDelay = function(callback, data) {

        if (callback) {

            setTimeout(function() {

                callback(data);

            }, 100);

        }

    }

/* other codes will be placed here */

    callDelay(successCallback);

}

3) Insert tasks codes to app.js and uncomment call to tasks

(add these codes on top of the function initialize in app.js file)

findAll: function() {...} ,

findById: function () {...} ,

markCompleted: function(id) {...} ,

markOutstanding: function(id) {...} ,

insert: function(json) {...} ,

update: function(json) {...} ,

delete: function(json) {...} ,

Don’t forget to put a comma between the function definitions

    findAll: function() {

        console.log('DEBUG - 2. findAll() triggered');

        this.store.findAll(function(todos) {

            var l = todos.length;

            var td;

            // Create new arrays so we can order them with outstanding first

            outstanding = [];

            completed = [];

            allTodos = [];

            // Loop through todos, build up lis and push to arrays

            for (var i=0; i<l; i++) {

                td = todos[i];

                // If not completed

                if (td.status == 0) {

                    outstanding.push('<li data-row-id="' + td.id + '" class="outstanding"><a href="#view" data-transition="slide" class="view" data-view-id="' + td.id +'"><h2>' + td.title+ '</h2><p>' + td.description + '</p></a><a href="#" data-icon="check" data-iconpos="notext" class="mark-completed" data-mark-id="' + td.id +'">Mark as completed</a></li>');

                }

                // If is completed

                else {

                    completed.push('<li data-row-id="' + td.id + '" class="completed"><a href="#view" data-transition="slide" class="view" data-view-id="' + td.id +'"><h2>' + td.title+ '</h2><p>' + td.description + '</p></a><a href="#" data-icon="delete" data-iconpos="notext" class="mark-outstanding" data-mark-id="' + td.id +'">Mark as outstanding</a></li>');

                }

            }

            // Join both arrays

            allTodos = outstanding.concat(completed);

            // Remove any previously appended

            $('.todo-listview li').remove();

            // Append built up arrays to ULs here.

            $('.todo-listview').append(allTodos);            

            // Refresh JQM listview

            $('.todo-listview').listview('refresh');

        });

    },

    findById: function(id) {

       

        this.store.findById(id, function(result) {

           

            $('#title').val(result.title);

            $('#title').attr('data-id', id);

            $('#description').val(result.description);

            $('#id').val(id);

       

        });

    },

    markCompleted: function(id) {

        // Passing json as any store will be able to handle it (even if we change to localStorage etc)

        this.store.markCompleted(id, function(result) {

            // DB updates successful

            if(result) {

                console.log("DEBUG - Success, db updated and marked as completed");

                // Find original row and grab details

                var originalRow =  $('#home *[data-row-id="'+id+'"]'),

                    title = originalRow.find("h2").text(),

                    desc = originalRow.find("p").text();

                // Remove from pending row

                originalRow.remove();

                // Re-build the li rather than clone as jqm generates a lot of fluff

                var newRow = '<li data-row-id="' + id + '" class="completed"><a href="#view" data-transition="slide" class="view" data-view-id="' + id +'"><h2>' + title + '</h2><p>' + desc + '</p></a><a href="#" data-icon="delete" data-iconpos="notext" class="mark-outstanding" data-mark-id="' + id +'">Mark as outstanding</a></li>';

                // Add to completed

                $('.todo-listview').append(newRow);

                // Refresh dom

                $('.todo-listview').listview('refresh');

                // Kept for debugging use

                //console.log("id length = " + $('[data-row-id='+id+']').length);

            } else {

                alert("Error - db did not update and NOT marked as completed");

            }

        });

    },

    markOutstanding: function(id) {

        // Passing json as any store will be able to handle it (even if we change to localStorage, indexedDB etc)

        this.store.markOutstanding(id, function(result) {

            // DB updates successful

            if(result) {

                console.log("DEBUG - Success, db updated and marked as outstanding");

                // Find original row and grab details

                var originalRow =  $('*[data-row-id="'+id+'"]'),

                    title = originalRow.find("h2").text(),

                    desc = originalRow.find("p").text();

                // Remove from pending row

                originalRow.remove();

                // Re-build the li rather than clone as jqm generates a lot of fluff

                var newRow = '<li data-row-id="' + id + '" class="outstanding"><a href="#view" data-transition="slide" class="view" data-view-id="' + id +'"><h2>' + title + '</h2><p>' + desc + '</p></a><a href="#" data-icon="check" data-iconpos="notext" class="mark-completed" data-mark-id="' + id +'">Mark as completed</a></li>';

                // Add to completed

                $('.todo-listview').prepend(newRow);

                // Refresh dom

                $('.todo-listview').listview('refresh');

                // Kept for debugging use

                //console.log("id length = " + $('[data-row-id='+id+']').length);

            } else {

                alert("Error - db did not update and NOT marked as outstanding");

            }

        });

    },

    insert: function(json) {

        // Passing json as any store will be able to handle it (even if we change to localStorage etc)

        this.store.insert(json, function(result) {

            // On successful db insert

            if(result) {

                console.log("DEBUG - Success,  add returned true");

                // Redirect back to #home page, add a transition andchange the hash

                $.mobile.changePage( $("#home"), {

                    transition: "slide",

                    reverse: true,

                    changeHash: true,

                });

            } else {

                alert("Error on insert!");

            }

        });

    },

    update: function(json) {

        // Passing json as any store will be able to handle it (even if we change to localStorage etc)

        this.store.update(json, function(result) {

            // On succuessful db update

            if(result) {

                console.log("DEBUG - Success, updated returned true");

            } else {

                alert("Error on update!");

            }

        });

    },

    delete: function(json) {

        // Passing json as any store will be able to handle it (even if we change to localStorage etc)

        this.store.delete(json, function(result) {

            // On successful db delete

            if(result) {

                console.log("DEBUG - Success, delete returned true");

                // Redirect back to #home page

                $.mobile.changePage( $("#home"), {

                    transition: "slide",

                    reverse: true,

                    changeHash: true

                });

            } else {

                alert("Error on delete!");

            }

        });

    },

4) Emulate

You will get error because your store object has yet to contain the required methods

5) Edit Local-Storage.js

5.1) Find the comment codes “/* other codes will be placed here */

5.2) Replace with the following codes

The green highlighted codes show the method binding for the store object

this.findAll

this.findById

this.markCompleted

this.markOutstanding

this.insert

this.update

this.delete

    // Allows us to sort an array of object - Use array.sort(sortByProperty('firstName'));

    var sortByProperty = function(property) {

        'use strict';

        return function (a, b) {

            var sortStatus = 0;

            if (a[property] < b[property]) {

                sortStatus = -1;

            } else if (a[property] > b[property]) {

                sortStatus = 1;

            }

     

            return sortStatus;

        };

    }

 

        // Sample Data (An array of objects)

    var todos = [

        {"id": 1, "title": "Go to the shop", "description": "Get milk and bread", "status": 0},

        {"id": 2, "title": "Post office", "description": "Collect mail", "status": 0},

        {"id": 3, "title": "Email Dad", "description": "About birthday", "status": 0},

        {"id": 4, "title": "Haircut", "description": "Well overdue", "status": 1}

    ];

    // Add the sample data to localStorage

    window.localStorage.setItem("todos", JSON.stringify(todos));

    this.findAll = function(callback) {

            // Parse a string as json

            var todos = JSON.parse(window.localStorage.getItem("todos"));

        callDelay(callback, todos);

    }

    this.findById = function(id, callback) {

        var todos = JSON.parse(window.localStorage.getItem("todos")),

                todo = null,

                len = todos.length,

            i = 0;

       

        for (; i < len; i++) {

            if (todos[i].id === id) {

                todo = todos[i];

                break;

            }

        }

        callDelay(callback, todo);

    }

    this.markCompleted = function(id, callback) {

        // Get all todos

        var todos = JSON.parse(window.localStorage.getItem("todos")),

            todo = null,

            len = todos.length,

            i = 0;

       

        // Loop through them and update the value

        $.each(todos, function(i, v) {

            if ( v.id === id ) {

                v.status = 1;

                return false;

            }

        });

        // Save the JSON back to localStorage

        if (window.localStorage.setItem("todos", JSON.stringify(todos))) {

            callDelay(callback, "true");

        } else {

            callDelay(callback, "false");

        }

    }

    this.markOutstanding = function(id, callback) {

        // Get all todos

        var todos = JSON.parse(window.localStorage.getItem("todos")),

            todo = null,

            len = todos.length,

            i = 0;

       

        // Loop through them and update the value

        $.each(todos, function(i, v) {

            if ( v.id === id ) {

                v.status = 0;

                return false;

            }

        });

        // Save the JSON back to localStorage

        if (window.localStorage.setItem("todos", JSON.stringify(todos))) {

            callDelay(callback, "true");

        } else {

            callDelay(callback, "false");

        }

    }

    this.insert = function(json, callback) {

        // Converts a JavaScript Object Notation (JSON) string into an object.

        var passedJson = JSON.parse(json),

            status = 0;

       

        // Get all todos

        var todos = JSON.parse(window.localStorage.getItem("todos")),

            todo = null,

            len = todos.length,

            i = 0;

       

        // Sort the json by ID (default is ASC)

        todos.sort(sortByProperty('id'));

        // Generate a new ID, pop the last obj in the array and grab the ID

        var lastTodo = todos.pop(),

            newID = lastTodo.id + 1;

        // Create the new Todo

        var newTodo = {"id": newID, "title": passedJson.title, "description": passedJson.description, "status": 0}

        // Add it to the existing todos        

        todos.push(lastTodo); // Add the popped one back in

        todos.push(newTodo);

        // Save the JSON back to localStorage

        if (window.localStorage.setItem("todos", JSON.stringify(todos))) {

            callDelay(callback, "true");

        } else {

            callDelay(callback, "false");

        }

    }

    this.update = function(json, callback) {

        // Converts a JavaScript Object Notation (JSON) string into an object.

        var passedJson = JSON.parse(json);

        // Get all todos

        var todos = JSON.parse(window.localStorage.getItem("todos")),

            todo = null,

            len = todos.length,

            i = 0;

       

        // Loop through them and update the value

        $.each(todos, function(i, v) {

            if ( v.id == passedJson.id ) {                

                v.title = passedJson.title;

                v.description = passedJson.description;

                return false;

            }

        });

        // Save the JSON back to localStorage

        if (window.localStorage.setItem("todos", JSON.stringify(todos))) {

            callDelay(callback, "true");

        } else {

            callDelay(callback, "false");

        }

    }

    this.delete = function(json, callback) {

        // Converts a JavaScript Object Notation (JSON) string into an object.

        var passedJson = JSON.parse(json);

        // Get all todos

        var todos = JSON.parse(window.localStorage.getItem("todos")),

            todo = null,

            len = todos.length,

            i = 0;

        // Loop through existing todos and remove one to be deleted

        for(var i=0; i<todos.length; i++){

            if(todos[i].id == passedJson.id){

                todos.splice(i, 1);  //removes 1 element at position i

                break;

            }

        }

        // Save the JSON back to localStorage

        if (window.localStorage.setItem("todos", JSON.stringify(todos))) {

            callDelay(callback, "true");

        } else {

            callDelay(callback, "false");

        }

    }

5.3) Emulate again.

6) Form Action Processing

6.1) There are two Form Actions; Add and Delete.

6.2) At the moment, if you click on the buttons, you will get error message. The method to convert form data into JSON object is missing.

6.3) Add a method “serializeObject” into app.js as shown below

// jQuery plugin - Encode a set of form elements as a JSON object for manipulation/submission.

$.fn.serializeObject = function()

{

    var o = {};

    var a = this.serializeArray();

    $.each(a, function() {

        if (o[this.name] !== undefined) {

            if (!o[this.name].push) {

                o[this.name] = [o[this.name]];

            }

            o[this.name].push(this.value || '');

        } else {

            o[this.name] = this.value || '';

        }

    });

    return o;

};

6.4) Test in emulator.

Click the test item shown above.

Click the Delete button.

The item will be deleted from the list.

7) Final Codes

7.1) index.html

<!DOCTYPE html>

<html>

<head>

    <meta charset="utf-8">

    <meta name="viewport" content="width=device-width, initial-scale=1">

    <title>jQuery Mobile To Do</title>

    <link rel="stylesheet"  href="http://code.jquery.com/mobile/1.3.2/jquery.mobile-1.3.2.min.css">

    <script src="http://code.jquery.com/jquery-1.10.2.min.js"></script>

    <script src="http://code.jquery.com/mobile/1.3.2/jquery.mobile-1.3.2.min.js"></script>

    <script src="./js/local-storage.js"></script>

    <script src="./js/apps.js"></script>

</head>

<body>

    <!-- Home -->

    <div data-role="page" id="home">

            <div data-role="header" data-position="fixed">

            <h1>JQM To Do</h1>

            <a href="#add" data-icon="plus" data-iconpos="notext" class="ui-btn-right" data-transition="slide" >New</a>

        </div>

        <div data-role="content">

            <ul data-role="listview" class="todo-listview">

            <li data-row-id="1" class="completed"><a href="#view" data-transition="slide" class="view" data-view-id="1"><h2>title</h2><p>desc</p></a><a href="#" data-icon="delete" data-iconpos="notext" class="mark-outstanding" data-mark-id="1">Mark as outstanding</a></li>

        </div>

    </div>

    <!-- /Home -->

  <!-- View -->        

    <div data-role="page" id="view" data-add-back-btn="true">

            <div data-role="header" data-position="fixed">

            <h1>View</h1>

        </div>

        <div data-role="content">

            <form id="edit">

                <input type="hidden" id="id" value="" name="id"/>

                <input type="text" id="title" value="" data-id="" class="target" name="title"/>

                <textarea id="description" data-id="" class="target" name="description"></textarea>

            </form>

            <a href="" data-role="button" class="delete">Delete</a>

        </div>

    </div>

    <!-- /View -->

    <!-- Add -->

    <div data-role="page" id="add" data-add-back-btn="true">

        <div data-role="header" data-position="fixed">

            <h1>Add</h1>

        </div>

        <div data-role="content">

            <form method="" action="" id="insert">

                <label for="title">Title:</label>

                <input type="text" name="title" id="text-basic" value="">

                <label for="description">Description:</label>

                <textarea cols="40" rows="8" name="description" id="text-basic"></textarea>

            </form>

            <a href="" data-role="button" class="add">Add</a>

        </div>

    </div>

    <!-- /Add -->          

</body>

</html>

7.2) app.js

/*

Main App file

*/

// jQuery plugin - Encode a set of form elements as a JSON object for manipulation/submission.

$.fn.serializeObject = function()

{

    var o = {};

    var a = this.serializeArray();

    $.each(a, function() {

        if (o[this.name] !== undefined) {

            if (!o[this.name].push) {

                o[this.name] = [o[this.name]];

            }

            o[this.name].push(this.value || '');

        } else {

            o[this.name] = this.value || '';

        }

    });

    return o;

};

// Define the app as an object to take out of global scope

var app = {

    findAll: function() {

        console.log('DEBUG - 2. findAll() triggered');

        this.store.findAll(function(todos) {

            var l = todos.length;

            var td;

            // Create new arrays so we can order them with outstanding first

            outstanding = [];

            completed = [];

            allTodos = [];

            // Loop through todos, build up lis and push to arrays

            for (var i=0; i<l; i++) {

                td = todos[i];

                // If not completed

                if (td.status == 0) {

                    outstanding.push('<li data-row-id="' + td.id + '" class="outstanding"><a href="#view" data-transition="slide" class="view" data-view-id="' + td.id +'"><h2>' + td.title+ '</h2><p>' + td.description + '</p></a><a href="#" data-icon="check" data-iconpos="notext" class="mark-completed" data-mark-id="' + td.id +'">Mark as completed</a></li>');

                }

                // If is completed

                else {

                    completed.push('<li data-row-id="' + td.id + '" class="completed"><a href="#view" data-transition="slide" class="view" data-view-id="' + td.id +'"><h2>' + td.title+ '</h2><p>' + td.description + '</p></a><a href="#" data-icon="delete" data-iconpos="notext" class="mark-outstanding" data-mark-id="' + td.id +'">Mark as outstanding</a></li>');

                }

            }

            // Join both arrays

            allTodos = outstanding.concat(completed);

            // Remove any previously appended

            $('.todo-listview li').remove();

            // Append built up arrays to ULs here.

            $('.todo-listview').append(allTodos);            

            // Refresh JQM listview

            $('.todo-listview').listview('refresh');

        });

    },

    findById: function(id) {

       

        this.store.findById(id, function(result) {

           

            $('#title').val(result.title);

            $('#title').attr('data-id', id);

            $('#description').val(result.description);

            $('#id').val(id);

       

        });

    },

    markCompleted: function(id) {

        // Passing json as any store will be able to handle it (even if we change to localStorage etc)

        this.store.markCompleted(id, function(result) {

            // DB updates successful

            if(result) {

                console.log("DEBUG - Success, db updated and marked as completed");

                // Find original row and grab details

                var originalRow =  $('#home *[data-row-id="'+id+'"]'),

                    title = originalRow.find("h2").text(),

                    desc = originalRow.find("p").text();

                // Remove from pending row

                originalRow.remove();

                // Re-build the li rather than clone as jqm generates a lot of fluff

                var newRow = '<li data-row-id="' + id + '" class="completed"><a href="#view" data-transition="slide" class="view" data-view-id="' + id +'"><h2>' + title + '</h2><p>' + desc + '</p></a><a href="#" data-icon="delete" data-iconpos="notext" class="mark-outstanding" data-mark-id="' + id +'">Mark as outstanding</a></li>';

                // Add to completed

                $('.todo-listview').append(newRow);

                // Refresh dom

                $('.todo-listview').listview('refresh');

                // Kept for debugging use

                //console.log("id length = " + $('[data-row-id='+id+']').length);

            } else {

                alert("Error - db did not update and NOT marked as completed");

            }

        });

    },

    markOutstanding: function(id) {

        // Passing json as any store will be able to handle it (even if we change to localStorage, indexedDB etc)

        this.store.markOutstanding(id, function(result) {

            // DB updates successful

            if(result) {

                console.log("DEBUG - Success, db updated and marked as outstanding");

                // Find original row and grab details

                var originalRow =  $('*[data-row-id="'+id+'"]'),

                    title = originalRow.find("h2").text(),

                    desc = originalRow.find("p").text();

                // Remove from pending row

                originalRow.remove();

                // Re-build the li rather than clone as jqm generates a lot of fluff

                var newRow = '<li data-row-id="' + id + '" class="outstanding"><a href="#view" data-transition="slide" class="view" data-view-id="' + id +'"><h2>' + title + '</h2><p>' + desc + '</p></a><a href="#" data-icon="check" data-iconpos="notext" class="mark-completed" data-mark-id="' + id +'">Mark as completed</a></li>';

                // Add to completed

                $('.todo-listview').prepend(newRow);

                // Refresh dom

                $('.todo-listview').listview('refresh');

                // Kept for debugging use

                //console.log("id length = " + $('[data-row-id='+id+']').length);

            } else {

                alert("Error - db did not update and NOT marked as outstanding");

            }

        });

    },

    insert: function(json) {

        // Passing json as any store will be able to handle it (even if we change to localStorage etc)

        this.store.insert(json, function(result) {

            // On successful db insert

            if(result) {

                console.log("DEBUG - Success,  add returned true");

                // Redirect back to #home page, add a transition andchange the hash

                $.mobile.changePage( $("#home"), {

                    transition: "slide",

                    reverse: true,

                    changeHash: true,

                });

            } else {

                alert("Error on insert!");

            }

        });

    },

    update: function(json) {

        // Passing json as any store will be able to handle it (even if we change to localStorage etc)

        this.store.update(json, function(result) {

            // On succuessful db update

            if(result) {

                console.log("DEBUG - Success, updated returned true");

            } else {

                alert("Error on update!");

            }

        });

    },

    delete: function(json) {

        // Passing json as any store will be able to handle it (even if we change to localStorage etc)

        this.store.delete(json, function(result) {

            // On successful db delete

            if(result) {

                console.log("DEBUG - Success, delete returned true");

                // Redirect back to #home page

                $.mobile.changePage( $("#home"), {

                    transition: "slide",

                    reverse: true,

                    changeHash: true

                });

            } else {

                alert("Error on delete!");

            }

        });

    },

    initialize: function() {

        // Create a new store

         this.store = new LocalStorageDB();

        // Bind all events here when the app initializes

        $(document).on('pagebeforeshow', '#home', function(event) {

            console.log("DEBUG - 1. Home pageinit bind");

         app.findAll();

        });

        $(document).on('click', '.view', function(event) {

            console.log("DEBUG - Trying to access view");

         app.findById($(this).data('view-id'))

        });

        $(document).on('click', '.add', function(event) {

            console.log("DEBUG - Trying to insert via the add method");

         var data = JSON.stringify($('#insert').serializeObject());

         app.insert(data);

        });

        $(document).on('change', '.target', function(event) {

            console.log("DEBUG - Trying to update on change");

         var data = JSON.stringify($('#edit').serializeObject());

         app.update(data);

        });

        $(document).on('click', '.delete', function(event) {

            console.log("DEBUG - Trying to delete after delete btn press");

         var data = JSON.stringify($('#edit').serializeObject());

         app.delete(data);

        });

        $(document).on('click', '.mark-completed', function(event) {

            console.log("DEBUG - Mark completed pressed");

         app.markCompleted($(this).data('mark-id'));

        });

        $(document).on('click', '.mark-outstanding', function(event) {

            console.log("DEBUG - Mark outstanding pressed");

         app.markOutstanding($(this).data('mark-id'));

        });

    }

};

app.initialize();

7.3) local-storage.js

var LocalStorageDB = function(successCallback, errorCallback) {

        // Used to simulate async calls. This is done to provide a consistent interface with storage methods like WebSQL and serverside ajax calls

    var callDelay = function(callback, data) {

        if (callback) {

            setTimeout(function() {

                callback(data);

            }, 100);

        }

    }

 // Allows us to sort an array of object - Use array.sort(sortByProperty('firstName'));

    var sortByProperty = function(property) {

        'use strict';

        return function (a, b) {

            var sortStatus = 0;

            if (a[property] < b[property]) {

                sortStatus = -1;

            } else if (a[property] > b[property]) {

                sortStatus = 1;

            }

     

            return sortStatus;

        };

    }

 

        // Sample Data (An array of objects)

    var todos = [

        {"id": 1, "title": "Go to the shop", "description": "Get milk and bread", "status": 0},

        {"id": 2, "title": "Post office", "description": "Collect mail", "status": 0},

        {"id": 3, "title": "Email Dad", "description": "About birthday", "status": 0},

        {"id": 4, "title": "Haircut", "description": "Well overdue", "status": 1}

    ];

    // Add the sample data to localStorage

    window.localStorage.setItem("todos", JSON.stringify(todos));

    this.findAll = function(callback) {

            // Parse a string as json

            var todos = JSON.parse(window.localStorage.getItem("todos"));

        callDelay(callback, todos);

    }

    this.findById = function(id, callback) {

        var todos = JSON.parse(window.localStorage.getItem("todos")),

                todo = null,

                len = todos.length,

            i = 0;

       

        for (; i < len; i++) {

            if (todos[i].id === id) {

                todo = todos[i];

                break;

            }

        }

        callDelay(callback, todo);

    }

    this.markCompleted = function(id, callback) {

        // Get all todos

        var todos = JSON.parse(window.localStorage.getItem("todos")),

            todo = null,

            len = todos.length,

            i = 0;

       

        // Loop through them and update the value

        $.each(todos, function(i, v) {

            if ( v.id === id ) {

                v.status = 1;

                return false;

            }

        });

        // Save the JSON back to localStorage

        if (window.localStorage.setItem("todos", JSON.stringify(todos))) {

            callDelay(callback, "true");

        } else {

            callDelay(callback, "false");

        }

    }

    this.markOutstanding = function(id, callback) {

        // Get all todos

        var todos = JSON.parse(window.localStorage.getItem("todos")),

            todo = null,

            len = todos.length,

            i = 0;

       

        // Loop through them and update the value

        $.each(todos, function(i, v) {

            if ( v.id === id ) {

                v.status = 0;

                return false;

            }

        });

        // Save the JSON back to localStorage

        if (window.localStorage.setItem("todos", JSON.stringify(todos))) {

            callDelay(callback, "true");

        } else {

            callDelay(callback, "false");

        }

    }

    this.insert = function(json, callback) {

        // Converts a JavaScript Object Notation (JSON) string into an object.

        var passedJson = JSON.parse(json),

            status = 0;

       

        // Get all todos

        var todos = JSON.parse(window.localStorage.getItem("todos")),

            todo = null,

            len = todos.length,

            i = 0;

       

        // Sort the json by ID (default is ASC)

        todos.sort(sortByProperty('id'));

        // Generate a new ID, pop the last obj in the array and grab the ID

        var lastTodo = todos.pop(),

            newID = lastTodo.id + 1;

        // Create the new Todo

        var newTodo = {"id": newID, "title": passedJson.title, "description": passedJson.description, "status": 0}

        // Add it to the existing todos        

        todos.push(lastTodo); // Add the popped one back in

        todos.push(newTodo);

        // Save the JSON back to localStorage

        if (window.localStorage.setItem("todos", JSON.stringify(todos))) {

            callDelay(callback, "true");

        } else {

            callDelay(callback, "false");

        }

    }

    this.update = function(json, callback) {

        // Converts a JavaScript Object Notation (JSON) string into an object.

        var passedJson = JSON.parse(json);

        // Get all todos

        var todos = JSON.parse(window.localStorage.getItem("todos")),

            todo = null,

            len = todos.length,

            i = 0;

       

        // Loop through them and update the value

        $.each(todos, function(i, v) {

            if ( v.id == passedJson.id ) {                

                v.title = passedJson.title;

                v.description = passedJson.description;

                return false;

            }

        });

        // Save the JSON back to localStorage

        if (window.localStorage.setItem("todos", JSON.stringify(todos))) {

            callDelay(callback, "true");

        } else {

            callDelay(callback, "false");

        }

    }

    this.delete = function(json, callback) {

        // Converts a JavaScript Object Notation (JSON) string into an object.

        var passedJson = JSON.parse(json);

        // Get all todos

        var todos = JSON.parse(window.localStorage.getItem("todos")),

            todo = null,

            len = todos.length,

            i = 0;

        // Loop through existing todos and remove one to be deleted

        for(var i=0; i<todos.length; i++){

            if(todos[i].id == passedJson.id){

                todos.splice(i, 1);  //removes 1 element at position i

                break;

            }

        }

        // Save the JSON back to localStorage

        if (window.localStorage.setItem("todos", JSON.stringify(todos))) {

            callDelay(callback, "true");

        } else {

            callDelay(callback, "false");

        }

    }    

    callDelay(successCallback);

}

8) Download

Download Source Files for this part:

https://drive.google.com/file/d/0B86b-ALn-1MGbG1qeDZLVy1LbjA/view?usp=sharing