b852e68290
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.
247 lines
7 KiB
JavaScript
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;
|
|
}));
|