Cable-Desktop/js-deps/backbone.localStorage.js
lilia b852e68290 Backbone message storage and views
Adds Backbone-based Whisper.Messages model/collection with local storage
extension. Saves sent and received messages in Whisper.Messages instead
of message map. This will assign a unique id to the message and save it
to localStorage.

Adds Backbone-based view to popup.html
  Automatically updates itself when new messages are saved to
  Whisper.Messages db from the background page.

Added some shiny new styles, and started splitting up css into multiple
files for sanity's sake.
2014-05-17 20:26:50 -07:00

247 lines
7 KiB
JavaScript

/**
* Backbone localStorage Adapter
* Version 1.1.7
*
* https://github.com/jeromegn/Backbone.localStorage
*/
(function (root, factory) {
if (typeof exports === 'object' && typeof require === 'function') {
module.exports = factory(require("backbone"));
} else if (typeof define === "function" && define.amd) {
// AMD. Register as an anonymous module.
define(["backbone"], function(Backbone) {
// Use global variables if the locals are undefined.
return factory(Backbone || root.Backbone);
});
} else {
factory(Backbone);
}
}(this, function(Backbone) {
// A simple module to replace `Backbone.sync` with *localStorage*-based
// persistence. Models are given GUIDS, and saved into a JSON object. Simple
// as that.
// 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
// Generate four random hex digits.
function S4() {
return (((1+Math.random())*0x10000)|0).toString(16).substring(1);
};
// Generate a pseudo-GUID by concatenating random hexadecimal.
function guid() {
return (S4()+S4()+"-"+S4()+"-"+S4()+"-"+S4()+"-"+S4()+S4()+S4());
};
function contains(array, item) {
var i = array.length;
while (i--) if (array[i] === item) return true;
return false;
}
function extend(obj, props) {
for (var key in props) obj[key] = props[key]
return obj;
}
// Our Store is represented by a single JS object in *localStorage*. Create it
// with a meaningful name, like the name you'd give a table.
// window.Store is deprectated, use Backbone.LocalStorage instead
Backbone.LocalStorage = window.Store = function(name, serializer) {
if( !this.localStorage ) {
throw "Backbone.localStorage: Environment does not support localStorage."
}
this.name = name;
this.serializer = serializer || {
serialize: function(item) {
return _.isObject(item) ? JSON.stringify(item) : item;
},
// fix for "illegal access" error on Android when JSON.parse is passed null
deserialize: function (data) {
return data && JSON.parse(data);
}
};
var store = this.localStorage().getItem(this.name);
this.records = (store && store.split(",")) || [];
};
extend(Backbone.LocalStorage.prototype, {
// Save the current state of the **Store** to *localStorage*.
save: function() {
this.localStorage().setItem(this.name, this.records.join(","));
},
// Add a model, giving it a (hopefully)-unique GUID, if it doesn't already
// have an id of it's own.
create: function(model) {
if (!model.id) {
model.id = guid();
model.set(model.idAttribute, model.id);
}
this.localStorage().setItem(this.name+"-"+model.id, this.serializer.serialize(model));
this.records.push(model.id.toString());
this.save();
return this.find(model) !== false;
},
// Update a model by replacing its copy in `this.data`.
update: function(model) {
this.localStorage().setItem(this.name+"-"+model.id, this.serializer.serialize(model));
var modelId = model.id.toString();
if (!contains(this.records, modelId)) {
this.records.push(modelId);
this.save();
}
return this.find(model) !== false;
},
// Retrieve a model from `this.data` by id.
find: function(model) {
return this.serializer.deserialize(this.localStorage().getItem(this.name+"-"+model.id));
},
// Return the array of all models currently in storage.
findAll: function() {
var result = [];
for (var i = 0, id, data; i < this.records.length; i++) {
id = this.records[i];
data = this.serializer.deserialize(this.localStorage().getItem(this.name+"-"+id));
if (data != null) result.push(data);
}
return result;
},
// Delete a model from `this.data`, returning it.
destroy: function(model) {
if (model.isNew())
return false
this.localStorage().removeItem(this.name+"-"+model.id);
var modelId = model.id.toString();
for (var i = 0, id; i < this.records.length; i++) {
if (this.records[i] === modelId) {
this.records.splice(i, 1);
}
}
this.save();
return model;
},
localStorage: function() {
return localStorage;
},
// Clear localStorage for specific collection.
_clear: function() {
var local = this.localStorage(),
itemRe = new RegExp("^" + this.name + "-");
// Remove id-tracking item (e.g., "foo").
local.removeItem(this.name);
// Match all data items (e.g., "foo-ID") and remove.
for (var k in local) {
if (itemRe.test(k)) {
local.removeItem(k);
}
}
this.records.length = 0;
},
// Size of localStorage.
_storageSize: function() {
return this.localStorage().length;
}
});
// localSync delegate to the model or collection's
// *localStorage* property, which should be an instance of `Store`.
// window.Store.sync and Backbone.localSync is deprecated, use Backbone.LocalStorage.sync instead
Backbone.LocalStorage.sync = window.Store.sync = Backbone.localSync = function(method, model, options) {
var store = model.localStorage || model.collection.localStorage;
var resp, errorMessage;
//If $ is having Deferred - use it.
var syncDfd = Backbone.$ ?
(Backbone.$.Deferred && Backbone.$.Deferred()) :
(Backbone.Deferred && Backbone.Deferred());
try {
switch (method) {
case "read":
resp = model.id != undefined ? store.find(model) : store.findAll();
break;
case "create":
resp = store.create(model);
break;
case "update":
resp = store.update(model);
break;
case "delete":
resp = store.destroy(model);
break;
}
} catch(error) {
if (error.code === 22 && store._storageSize() === 0)
errorMessage = "Private browsing is unsupported";
else
errorMessage = error.message;
}
if (resp) {
if (options && options.success) {
if (Backbone.VERSION === "0.9.10") {
options.success(model, resp, options);
} else {
options.success(resp);
}
}
if (syncDfd) {
syncDfd.resolve(resp);
}
} else {
errorMessage = errorMessage ? errorMessage
: "Record Not Found";
if (options && options.error)
if (Backbone.VERSION === "0.9.10") {
options.error(model, errorMessage, options);
} else {
options.error(errorMessage);
}
if (syncDfd)
syncDfd.reject(errorMessage);
}
// add compatibility with $.ajax
// always execute callback for success and error
if (options && options.complete) options.complete(resp);
return syncDfd && syncDfd.promise();
};
Backbone.ajaxSync = Backbone.sync;
Backbone.getSyncMethod = function(model) {
if(model.localStorage || (model.collection && model.collection.localStorage)) {
return Backbone.localSync;
}
return Backbone.ajaxSync;
};
// Override 'Backbone.sync' to default to localSync,
// the original 'Backbone.sync' is still available in 'Backbone.ajaxSync'
Backbone.sync = function(method, model, options) {
return Backbone.getSyncMethod(model).apply(this, [method, model, options]);
};
return Backbone.LocalStorage;
}));