I'm try to save data to localstorage. Created "class" through constructor and try to put get and set methods to them. But when I click my button(when button was clicked data must be saving) nothing happens(In developer tools "Resource" tab). When I tried simple save data through JSON.stringify and else all was worked.
(function() {
window.onload = function() {
document.getElementById('buttonCreate').onclick = function() {
var topicValue = document.getElementById("create-topic").value;
var statusValue = document.getElementById("create-status").value;
var descriptionValue = document.getElementById("create-description").value;
var storage = new Storage();
var ticket = {
topic: topicValue,
status: statusValue,
description: descriptionValue
};
storage.set("Item", item);
}
}
})();
"class" Storage:
function Storage() {
this._ITEMS_DESCRIPTOR = 'items';
}
Storage.prototype.get = function() {
var fromStorage = localStorage.getItem(this._ITEMS_DESCRIPTOR);
return fromStorage ? JSON.parse(fromStorage) : [];
};
Storage.prototype.set = function(key, items) {
localStorage.setItem(key, JSON.stringify(items));
};
The exact issue with your code is the storage key and also the item that you are trying to store which is not defined.
It stores it in to Item key and the get method is written to take it from the key, items.
by looking at the given code, you should suppose to store ticket object. storage.set(ticket);
A suggested better approach: To pass a key to instantiate Storage object and then use it accordingly.
Such as var storage = new Storage('Item');
(function() {
window.onload = function() {
document.getElementById('buttonCreate').onclick = function() {
var topicValue = document.getElementById("create-topic").value;
var statusValue = document.getElementById("create-status").value;
var descriptionValue = document.getElementById("create-description").value;
var storage = new Storage("ticket");
var ticket = {
topic: topicValue,
status: statusValue,
description: descriptionValue
};
storage.set(ticket);
}
}
})();
"class" Storage:
function Storage(key) {
this._ITEMS_DESCRIPTOR = key;
}
Storage.prototype.get = function() {
var fromStorage = localStorage.getItem(this._ITEMS_DESCRIPTOR);
return fromStorage ? JSON.parse(fromStorage) : {};
};
Storage.prototype.set = function(item) {
localStorage.setItem(this._ITEMS_DESCRIPTOR, JSON.stringify(item));
};
To get the ticket value stored in localstorage:
var storage = new Storage('ticket');
var ticket = storage.get();
Per Mike McCaughan's comment, you were referencing an undefined variable.
Using strict mode would have caught this.
You have another bug in the keys used to address items in storage:
'items' !== 'Item'
Also; you have no classes in your code.
Related
Isolated Question:
user[username] = {
cool: true,
stuff: false,
arrData: []
};
I am trying to globally initialize my arrData array in order to populate in different scope; the below does not work.
let user.arrData = [],
later I want to do the following, for context:
...........
inputs.forEach(function(input){
user = JSON.parse(localStorage.getItem(username)); // tried moving this here
user.arrData.push({ id: input.id, checked: input.checked });
// the above errors on .arrData cannot push undefined, I have tried also including the key here, how is this done?
});
localStorage.setItem(user, JSON.stringify(user.arrData));
console.log({ arrData: user.arrData }); // returns undefined
............
Sub question: can I set and retrieve a object.array with localStorage like this. Meaning my reference to (user.arrData)?
Context/actual code:
Below is the complete relevant code for context: I am trying to save a series of checkbox states per user with local storage, then reload them when a button is clicked.
let user = {};
var savebtnBM = document.getElementById("savebtnBM");
const username = document.querySelector("span.name").textContent;
user[username] = {
cool: true,
stuff: false,
arrData: []
};
var layersMod = document.querySelectorAll(".layers #savebtnBM")[0];
layersMod.addEventListener('click', ()=>{
setTimeout(
function() {
Saver();
}, 2000);
});
function Saver() {
var inputs = document.querySelectorAll('input[type="checkbox"]');
// var arrData = [];
inputs.forEach(function(input){
user[username].arrData.push({ id: input.id, checked: input.checked });
});
localStorage.setItem(user, JSON.stringify(user.arrData));
console.log(JSON.stringify(user.arrData));
}
var etHOME= document.getElementById("et-phone-home");
etHOME.addEventListener('click', ()=>{
user = JSON.parse(localStorage.getItem(username));
console.log(user);
document.getElementsByClassName("big-button")[0].click();
setTimeout(load_, 2000);
function load_() {
var inputs = JSON.parse(localStorage.getItem(user));
inputs.forEach(function(input){
document.getElementById(input.id).checked = input.checked;
load__();
});
function load__() {
var elems = document.querySelectorAll('input:checked');
for(var i = 0; i<elems.length; i++) {
elems[i].click();
}
}
}
});
Sub question response:
The localStorage and sessionStorage can only handle strings.
So first we must convert our object to string with JSON.stringify() as in the following example:
localStorage.setItem('user', JSON.stringify(user));
When it comes to obtaining the previously saved we do the opposite, using JSON.parse():
//...
const storedUser = JSON.parse(localStorage.getItem('user'));
console.log({ arrData: storedUser.arrData });
Hi there i try to make i clientside message exchange with the signal protocol (only for tests). But i have trouble to the proccessPreKey.
Here is the test code
<script src="javascripts/libsignal-protocol.js"></script>
<script src="javascripts/InMemorySignalProtocolStore.js"></script>
<script>
var KeyHelperUser1 = libsignal.KeyHelper;
var KeyHelperUser2 = libsignal.KeyHelper;
var registrationId_User1 = KeyHelperUser1.generateRegistrationId();
var registrationId_User2 = KeyHelperUser2.generateRegistrationId();
// Store registrationId somewhere durable and safe.
var identityKeyPair_User1, identityKeyPair_User2;
var SignedPreKey_User1, SignedPreKey_User2;
// Test Store
var store_User1 = new SignalProtocolStore();
var store_User2 = new SignalProtocolStore();
var PreKey_User1, PreKey_User2;
// Build the session
var address_User1 = new libsignal.SignalProtocolAddress(1002, 0);
var address_User2 = new libsignal.SignalProtocolAddress(1001, 0);
var sessionBuilder_User1 = new libsignal.SessionBuilder(store_User1, address_User1);
var sessionBuilder_User2 = new libsignal.SessionBuilder(store_User2, address_User2);
KeyHelperUser1.generateIdentityKeyPair().then(function(identityKeyPair) {
// keyPair -> { pubKey: ArrayBuffer, privKey: ArrayBuffer }
// Store identityKeyPair somewhere durable and safe.
identityKeyPair_User1 = identityKeyPair;
KeyHelperUser1.generatePreKey(1001).then(function(preKey) {
//store.storePreKey(preKey.keyId, preKey.keyPair);
PreKey_User1 = preKey;
KeyHelperUser2.generatePreKey(1002).then(function(preKey) {
//store.storePreKey(preKey.keyId, preKey.keyPair);
PreKey_User2 = preKey;
KeyHelperUser1.generateSignedPreKey(identityKeyPair_User1, 1001).then(function(signedPreKey) {
store_User1.storeSignedPreKey(signedPreKey.keyId, signedPreKey.keyPair);
SignedPreKey_User1 = signedPreKey;
KeyHelperUser2.generateIdentityKeyPair().then(function(identityKeyPair) {
// keyPair -> { pubKey: ArrayBuffer, privKey: ArrayBuffer }
// Store identityKeyPair somewhere durable and safe.
identityKeyPair_User2 = identityKeyPair;
KeyHelperUser2.generateSignedPreKey(identityKeyPair_User2, 1002).then(function(signedPreKey) {
store_User2.storeSignedPreKey(signedPreKey.keyId, signedPreKey.keyPair);
SignedPreKey_User2 = signedPreKey;
var promise_User1 = sessionBuilder_User1.processPreKey({
registrationId: registrationId_User2,
identityKey: identityKeyPair_User2.pubKey,
signedPreKey: {
keyId : 1002,
publicKey : SignedPreKey_User2.pubKey,
signature : SignedPreKey_User2.signature
},
preKey: {
keyId : 1002,
publicKey : PreKey_User1.pubKey
}
});
promise_User1.catch(function onerror(error) {
// handle identity key conflict
//console.log(error);
});
});
});
});
});
});
});
</script>
I don't really know which parameters the processPreKey wanted. Can someone help?
Hey so this is something i put together, hope it helps.
var KeyHelper = libsignal.KeyHelper;
function generateIdentity(store) {
return Promise.all([
KeyHelper.generateIdentityKeyPair(),
KeyHelper.generateRegistrationId(),
]).then(function(result) {
store.put('identityKey', result[0]);
store.put('registrationId', result[1]);
});
}
function generatePreKeyBundle(store, preKeyId, signedPreKeyId) {
return Promise.all([
store.getIdentityKeyPair(),
store.getLocalRegistrationId()
]).then(function(result) {
var identity = result[0];
var registrationId = result[1];
return Promise.all([
KeyHelper.generatePreKey(preKeyId),
KeyHelper.generateSignedPreKey(identity, signedPreKeyId),
]).then(function(keys) {
var preKey = keys[0]
var signedPreKey = keys[1];
store.storePreKey(preKeyId, preKey.keyPair);
store.storeSignedPreKey(signedPreKeyId, signedPreKey.keyPair);
return {
identityKey: identity.pubKey,
registrationId : registrationId,
preKey: {
keyId : preKeyId,
publicKey : preKey.keyPair.pubKey
},
signedPreKey: {
keyId : signedPreKeyId,
publicKey : signedPreKey.keyPair.pubKey,
signature : signedPreKey.signature
}
};
});
});
}
var ALICE_ADDRESS = new libsignal.SignalProtocolAddress("xxxxxxxxx", 1);
var BOB_ADDRESS = new libsignal.SignalProtocolAddress("yyyyyyyyyyyyy", 1);
var aliceStore = new libsignal.SignalProtocolStore();
var bobStore = new libsignal.SignalProtocolStore();
var bobPreKeyId = 1337;
var bobSignedKeyId = 1;
var Curve = libsignal.Curve;
Promise.all([
generateIdentity(aliceStore),
generateIdentity(bobStore),
]).then(function() {
return generatePreKeyBundle(bobStore, bobPreKeyId, bobSignedKeyId);
}).then(function(preKeyBundle) {
var builder = new libsignal.SessionBuilder(aliceStore, BOB_ADDRESS);
return builder.processPreKey(preKeyBundle).then(function() {
var originalMessage = util.toArrayBuffer("my message ......");
var aliceSessionCipher = new libsignal.SessionCipher(aliceStore, BOB_ADDRESS);
var bobSessionCipher = new libsignal.SessionCipher(bobStore, ALICE_ADDRESS);
aliceSessionCipher.encrypt(originalMessage).then(function(ciphertext) {
// check for ciphertext.type to be 3 which includes the PREKEY_BUNDLE
return bobSessionCipher.decryptPreKeyWhisperMessage(ciphertext.body, 'binary');
}).then(function(plaintext) {
alert(plaintext);
});
bobSessionCipher.encrypt(originalMessage).then(function(ciphertext) {
return aliceSessionCipher.decryptWhisperMessage(ciphertext.body, 'binary');
}).then(function(plaintext) {
assertEqualArrayBuffers(plaintext, originalMessage);
});
});
});
Not enough rep to comment; just addressing the comments below the answer.
#Niczem Olaske, the util.toArrayBuffer([message]) can be removed. The returned ciphertext is already an arrayBuffer, and converting the message to an arrayBuffer prior to encryption creates the need to convert it back to a string at the end anyway. However, a global 'util' object must be declared in order for libsignal to work (at least in JavaScript). So you can just put: const util = {}; somewhere outside function bodies, or alternatively: Object.assign(window, { util: {} }); before using libsignal.
#Hasib Mahmud, this error is from Bob trying to encrypt a message to Alice when a session has not yet been established. If you use async/await instead of .then(() => etc...), you will avoid this problem. Bob must decrypt Alice's message first in order to establish a session with her, and then encrypt messages to her. Note that he needs to use the 'decryptPreKeyWhisperMessage' method in order to use her preKey to establish the session whilst calling the method. After the session has been established on both sides, he can use the 'decryptWhisperMessage' method.
I'm using an object with information for a bookmark tagging system that needs to persist across Chrome sessions, so I'm trying to save it to local storage and update it whenever a new bookmark is created.
When I create a new bookmark, I fire a function to see if there are now any other bookmarks with the same tag as the new bookmark. This organizes bookmarks into "tag groups" that function kind of like dynamic folders.
When I set the storage object, the object being stored has all the data I'd expect. However, as soon as I get the same object out of storage, one of the nested objects mysteriously turns to null. See console output: the top object is just before the set call in function updateStorage. The bottom is what I get back when I "get" that object from storage. Notice the tagGroups bookmarks are now null. The bookmarks themselves are still there, it's only in the tag group object that they disappear. I've spent a full day and night messing around with this trying to get it to work.
Here is the model code. I included everything for context, but the most relevant pieces are the createNewBookmark, updatePrimaryTreeWithTagGroups, and updateStorage methods.
UPDATE: I've edited the code to make all the changes to the bookmarks tree before setting/getting anything from storage, then making a final call to update storage with the resulting object. I'm literally storing one thing, one time, and getting back another whenever I try to retrieve.
function PrimaryBookmarksTree(){
chrome.storage.sync.get(null, this.findOrCreate.bind(this));
}
PrimaryBookmarksTree.prototype.findOrCreate = function(result){
if (result.bookmarksTree != undefined){
this.bookmarks = result.bookmarksTree.bookmarks;
this.title = result.bookmarksTree.title;
this.tagGroups = result.bookmarksTree.tagGroups;
console.log(this);
} else {
this.bookmarks = [];
this.title = "Marinade Bookmarks";
this.tagGroups = [];
chrome.storage.sync.set({"bookmarksTree": this}, function(){console.log("New tree created!")});
console.log(this);
}
}
function Bookmark(name, tags, url){
this.name = name;
this.tags = tags;
this.url = url;
this.dateCreated = this.date();
}
function TagGroup(tag){
this.bookmarks = [];
this.tag = tag;
}
//called by controller when user tags a new bookmark via the extension
PrimaryBookmarksTree.prototype.createNewBookmark = function(name, tags, url){
var newBookmark = new Bookmark(name, tags, url);
this.bookmarks.push(newBookmark);
this.tagGroups = this.updatePrimaryTreeWithTagGroups();
this.updateStorage(this);
}
PrimaryBookmarksTree.prototype.updatePrimaryTreeWithTagGroups = function(){
var tagsForGrouping = this.getTagsWithMultipleBookmarks(this.bookmarks);
for(j=0;j<tagsForGrouping.length;j++){
this.tagGroups.push(this.buildTagGroup(tagsForGrouping[j]));
}
return this.tagGroups;
}
PrimaryBookmarksTree.prototype.getTagsWithMultipleBookmarks = function(bookmarks){
var tagsToCheck = this.pluck(bookmarks, "tags");
var tagCounts = tagsToCheck.reduce(function (obj, curr){
if (typeof obj[curr] == 'undefined') {
obj[curr] = 1;
} else {
obj[curr] += 1;
}
return obj;
}, {});
var tagGroups = this.filter(tagCounts, function(x){return x > 1});
return tagGroups;
}
PrimaryBookmarksTree.prototype.buildTagGroup = function(tag){
tagGroup = new TagGroup(tag);
for(i=0;i<this.bookmarks.length;i++){
if(this.bookmarks[i].tags[0] == tag){
tagGroup.bookmarks.push(this.bookmarks[i]);
}
}
if (tagGroup.bookmarks.length != 0){
return tagGroup;
}
}
PrimaryBookmarksTree.prototype.updateStorage = function(updatedTree){
console.log(JSON.stringify(updatedTree));
chrome.storage.sync.set({"bookmarksTree": updatedTree}, function(){console.log("final storage complete")});
}
You are always setting this.tagGroups to undefined when you retrieve your data from the sync storage:
PrimaryBookmarksTree.prototype.findOrCreate = function(result){
if (result.bookmarksTree != undefined){
this.bookmarks = result.bookmarksTree.bookmarks;
this.title = result.bookmarksTree.title;
this.tagGroups = result.tagGroups; // should be result.bookmarksTree.tagGroups
console.log(this);
}
}
I need to print only the column that has been created by me.
function retrieveFieldsOfListView(listTitle,viewName){
var context = new SP.ClientContext.get_current();
var web = context.get_web();
var list = web.get_lists().getByTitle(listTitle);
var listFields = list.get_fields();
context.load(listFields);
context.executeQueryAsync(printFieldNames,onError);
function printFieldNames() {
var e = listFields.getEnumerator();
while (e.moveNext()) {
var fieldName = e.get_title();
console.log(fieldName);
}
}
function onError(sender,args)
{
console.log(args.get_message());
}
}
But this code is printing all the pre-defined fields along with my field. I don't want the pre-defined fields like 'modified,created,etc'. I want only the coding changes. UI changes are not upto me.
How to determine whether field is system or user-defined
Probably the most reliable way to determine whether field is system or user-defined is to utilize SourceId property of a Field. For system fields it's value is set to http://schemas.microsoft.com/sharepoint/v3
Note: SP.Field object does not expose SourceId property, but it could be extracted from SP.Field.schemaXml property as demonstrated below:
function getListFields(listTitle,success,error){
var context = SP.ClientContext.get_current();
var web = context.get_web();
var list = web.get_lists().getByTitle(listTitle);
var fields = list.get_fields();
context.load(fields);
context.executeQueryAsync(
function(){
success(fields);
},
error);
}
//Usage
getListFields('Pages',
function(fields) {
//get only user defined fields
var userDefinedFields = fields.get_data().filter(function(f){
var schema = f.get_schemaXml();
if (schema.indexOf('SourceID="http://schemas.microsoft.com/sharepoint/v3"') === -1){
return f;
}
});
//print user defined fields title
userDefinedFields.forEach(function(f){
console.log(f.get_title());
});
},
function(sender,args)
{
console.log(args.get_message());
});
Like a said SourceId property is not available for Field object, the below example demonstrates a different approach for getting field properties
function getListFields(listTitle,success,error){
var context = SP.ClientContext.get_current();
var web = context.get_web();
var list = web.get_lists().getByTitle(listTitle);
var fields = list.get_fields();
context.load(fields,'Include(SchemaXml)');
context.executeQueryAsync(
function(){
var result = [];
fields.get_data().forEach(function(f){
var schema = f.get_schemaXml();
result.push(schemaXml2Json(schema));
});
success(result);
},
error);
}
function schemaXml2Json(schemaXml)
{
var jsonObject = {};
var schemaXmlDoc = $.parseXML(schemaXml);
$(schemaXmlDoc).find('Field').each(function() {
$.each(this.attributes, function(i, attr){
jsonObject[attr.name] = attr.value;
});
});
return jsonObject;
}
Then you could use SourceId property:
getListFields('Pages',
function(fields) {
//get only user defined fields
var userDefinedFields = fields.filter(function(f){
if (f.SourceID !== "http://schemas.microsoft.com/sharepoint/v3"){
return f;
}
});
//print user defined fields title
userDefinedFields.forEach(function(f){
console.log(f.DisplayName);
});
},
function(sender,args)
{
console.log(args.get_message());
});
I'm using a proxy class as the data I have is a reference to a Firebase location that stores my object but I want to act as if I have the object itself. I've got something that works fine but I would like to improve it, the key criteria being to reduce repetition. I suspect something is possible by inspecting the Map class and using apply() but I don't know quite how to do that (or if there is a better solution).
I think it would also be useful if the solution could be generalised to support any class, not just the Map class.
var Map = function() {
...
};
var MapProxy = function(mapRef) {
this.mapRef = mapRef;
};
Map.prototype.addToken = function(portrait, newLocation) {
...
};
Map.prototype.removeToken = function(token) {
...
};
Map.prototype.moveToken = function(token, newLocation) {
...
};
MapProxy.prototype.addToken = function(portrait, newLocation) {
var mapRef = this.mapRef;
mapRef.once('value', function(data) {
var map = new Map();
map.init(mapRef, data.val());
map.addToken(portrait, newLocation);
});
};
MapProxy.prototype.removeToken = function(token) {
var mapRef = this.mapRef;
mapRef.once('value', function(data) {
var map = new Map();
map.init(mapRef, data.val());
map.removeToken(token);
});
};
MapProxy.prototype.moveToken = function(token, newLocation) {
var mapRef = this.mapRef;
mapRef.once('value', function(data) {
var map = new Map();
map.init(mapRef, data.val());
map.moveToken(token, newLocation);
});
};
var mapProxy = new MapProxy(mapRef);
Think I solved it myself in the end.
var FirebaseProxy = function(classToProxy, firebaseRef) {
var key,
self = this;
self.proxy = classToProxy;
self.firebaseRef = firebaseRef;
for (key in self.proxy.prototype) {
if (typeof self.proxy.prototype[key] === 'function') {
(function(inner_key) {
self[inner_key] = function ()
{
var args = arguments;
self.firebaseRef.once('value', function(data) {
var proxiedInstance = new self.proxy();
if (typeof proxiedInstance.init === 'function') {
proxiedInstance.init(self.firebaseRef, data.val());
}
proxiedInstance[inner_key].apply(proxiedInstance, args);
});
}
})(key);
}
}
}
I don't think I completely follow what you're trying to accomplish. Could you forego the proxy and just use something like this?
var Map = function(mapRef) {
mapRef.on('value', function(snap) {
this.init(snap.val());
});
};
Map.prototype.init = function(data) {
// update internal state with new data from Firebase ...
};
...
Since 'value' will fire every time the data at mapRef changes, your map object will always have the latest data.
It's worth noting that if you're going to be needing the latest map data on a regular basis, you should probably use .on(), not .once(). .once() will go and retrieve the data from the servers every time you ask for it, while .on() will always have the latest data cached (since it subscribes to updates). So it'll be faster and use less bandwidth.