rewriting localStorage javascript for chrome.local.set - javascript

I have this code which is working with the localStorage html5 calls. However it has to be rewritten for a Chrome Desktop app and I can't figure out how to port it over.
window.fakeStorage = {
_data: {},
setItem: function (id, val) {
return this._data[id] = String(val);
},
getItem: function (id) {
return this._data.hasOwnProperty(id) ? this._data[id] : undefined;
},
removeItem: function (id) {
return delete this._data[id];
},
clear: function () {
return this._data = {};
}
};
function LocalScoreManager() {
this.key = "bestScore";
var supported = this.localStorageSupported();
this.storage = supported ? window.localStorage : window.fakeStorage;
}
LocalScoreManager.prototype.localStorageSupported = function () {
var testKey = "test";
var storage = window.localStorage;
try {
storage.setItem(testKey, "1");
storage.removeItem(testKey);
return true;
} catch (error) {
return false;
}
};
LocalScoreManager.prototype.get = function () {
return this.storage.getItem(this.key) || 0;
};
LocalScoreManager.prototype.set = function (score) {
this.storage.setItem(this.key, score);
};
The error I get says "window.localStorage is not available in packaged apps. Use chrome.storage.local instead."
My attempt to rewrite it was this so far.. but it is breaking somewhere along the way.
$(document).ready(function() {
$("body").bind('keyup', function() {
var number = $(".best-container").val();
if(number == 'undefined'){
var number = "0";
}
chrome.storage.local.set({'bestScore': number});
});
chrome.storage.local.get('bestScore', function (result) {
hello= result.bestScore || 0;
$(".best-container").val(hello);
});
});

Porting localStorage to chrome.storage has one important pitfall: chrome.storage methods are asynchronous whereas localStorage access is synchronous.
That means: If you try to get a value from chrome.storage before the callback of the set method has been called, the value will still be undefined
Wrong way:
chrome.storage.local.set({'key': value});
...
chrome.storage.local.get('key', function(items) {
if(items.key) // won't be able to find the key
alert(items.key);
});
Correct way:
chrome.storage.local.set({'key': value}, function() {
...
chrome.storage.local.get('key', function(items) {
if(items.key)
alert(items.key); // will be "value"
});
});
or rather:
chrome.storage.local.set({'key': value}, function() {
doFurtherStuff();
});
function doFurtherStuff() {
...
chrome.storage.local.get('key', function(items) {
if(items.key)
alert(items.key); // will be "value"
});
}

Related

How do I access object that is inside an event

I have this object that carries some functions. At some point, I would like to hold an event and call this object within that event, but only if you call this object using this, is there any way to call it without this?
I haven't tried anything yet, because I couldn't find anything to help.
const cantaVideoModal = {
click: null,
target: null,
urlVideo: null,
config: function (c) {
this.click = c.click;
this.target = c.target;
this.urlVideo = c.urlVideo;
this.init();
},
init: function () {
this.click = (this.click) ? document.querySelector(this.click) : null;
this.target = (this.target) ? document.querySelector(this.target) : null;
let btnCloseVideo = document.querySelector('[data-close-modal]');
if(btnCloseVideo){
btnCloseVideo.addEventListener('click', function(){
//call modalAction object here using this
})
}
},
modalAction: function (act) {
let elementClick = this.click;
let elementtarget = this.target;
if (elementClick) {
elementClick.addEventListener('click', function (e) {
e.preventDefault();
if (elementtarget) {
if(act === "toggle")
elementtarget.classList.toggle('in');
if(act === "show")
elementtarget.classList.add('in');
if(act === "hide")
elementtarget.classList.remove('in');
}
})
}
}
}
The reason you cannot use this is because you are in a function inside a addEventListener. I'm not sure why, but you can create a new object that refer to the current object, which can be used inside the annonymous functon. For more information on why you can't use this inside a addEventListener see this answer
const cantaVideoModal = {
click: null,
target: null,
urlVideo: null,
config: function (c) {
this.click = c.click;
this.target = c.target;
this.urlVideo = c.urlVideo;
this.init();
},
init: function () {
this.click = (this.click) ? document.querySelector(this.click) : null;
this.target = (this.target) ? document.querySelector(this.target) : null;
let btnCloseVideo = document.querySelector('[data-close-modal]');
if(btnCloseVideo){
// we create a new variable that refer to the current content.
var self = this
btnCloseVideo.addEventListener('click', function() {
//call modalAction object here using self.
self.modalAction(/* some parameters */);
})
}
},
modalAction: function (act) {
let elementClick = this.click;
let elementtarget = this.target;
if (elementClick) {
elementClick.addEventListener('click', function (e) {
e.preventDefault();
if (elementtarget) {
if(act === "toggle")
elementtarget.classList.toggle('in');
if(act === "show")
elementtarget.classList.add('in');
if(act === "hide")
elementtarget.classList.remove('in');
}
})
}
}
}
P.S. I tried using an arrow function but couldn't get the code to work properly.

Adding a method to an existing object

This is an evolution of a question I asked more than a year ago: How to create methods with a loop in jquery/javascript
I've a code that is shared with other co-workers so it's better if it changes not much. It goes like this:
var scriptList = {
components : [
'all'
],
modules : [
'one',
'two',
'three'
]
}
function core() {
var scope = this;
var promises = [];
jQuery.each(scriptList, function(key, value) {
jQuery.each(value, function (index, name) {
var hookValue = 'hook_'+name,
stringValue = 'string_'+name,
argsValue = 'args_'+name;
scope[name] = function(callback){
window[hookValue] = jQuery('.js-'+name),
window[stringValue] = 'js-'+name;
window[argsValue] = arguments;
loadAndUse(window[hookValue],key+'/'+name,callback);
}
if(key === 'modules'){
scope[name]();
}
});
});
jQuery.when.apply(jQuery, promises).then(function() {
window.executeReady = true;
});
}
ui = new core();
ui.exec = methodLoader;
ui.exec();
This code works fine, because I can use the various method I added with ui.one - ui.two and so on and is also logged in the console if I do console.log(ui).
Before this code gets fired tho, I have now another block of code inside the HTML page, which create a method (always of the ui object) called exec:
window.executeReady = false;
var ui = {},
scriptToBeLoaded = [];
var methodLoader = function(){
var scope = this;
this.exec = function(module, callback){
scriptToBeLoaded.push({
'module' : module,
'callback' : callback
});
if(module === undefined){
console.warn('This module does not exists. Please check the scriptList.');
} else {
function waitForList($context, $variable, $callback) {
if ($context[$variable]) {
$callback();
} else {
Object.defineProperty($context, $variable, {
configurable: true,
enumerable: true,
writeable: true,
get: function() {
return this['_' + $variable];
},
set: function(val) {
this['_' + $variable] = val;
$callback();
}
});
}
}
waitForList(window, 'executeReady', function(){
for (var i = 0; i < scriptToBeLoaded.length; i++) {
ui[scriptToBeLoaded[i].module](scriptToBeLoaded[i].callback);
}
scriptToBeLoaded = [];
});
}
};
};
ui = new methodLoader();
Because of this block of code, when I console.log(ui); I see only the exec method and all of the other methods are gone. Although, the method I create in the core() function are executed correctly, but not present in the ui object.
I would like to edit the code in the HTML Page to have the ui object with exec (which is create on the html side) and the other method (that are created in the js file) all inside the ui object.
How can I achieve that?
You can add new methods to existing object like this. Or you can use jQuery.extend() to merge two object.
var ui = ui || {},
scriptToBeLoaded = [];
ui.exec = function(module, callback){
scriptToBeLoaded.push({
'module' : module,
'callback' : callback
});
if(module === undefined){
console.warn('This module does not exists. Please check the scriptList.');
} else {
function waitForList($context, $variable, $callback) {
if ($context[$variable]) {
$callback();
} else {
Object.defineProperty($context, $variable, {
configurable: true,
enumerable: true,
writeable: true,
get: function() {
return this['_' + $variable];
},
set: function(val) {
this['_' + $variable] = val;
$callback();
}
});
}
}
waitForList(window, 'executeReady', function(){
for (var i = 0; i < scriptToBeLoaded.length; i++) {
ui[scriptToBeLoaded[i].module](scriptToBeLoaded[i].callback);
}
scriptToBeLoaded = [];
});
}
};

Integrating TextExt in SlickGrid custom editor

i want to implement a custom editor in SlickGrid using TextExt but I'm having trouble doing so.
I have two different lists args.column.names and `args.column.values.
I want the tags to show the selected names but to post the list of the corresponding ids.
Here is a first draft, I don't really see how to manage that.
Can someone help me figure out what to write in these functions to match what I'm trying to do ?
function AutoCompletedTextField(args) {
var $input;
var defaultValue;
var scope = this;
this.init = function () {
$input = $("<textarea class='textarea' rows='1'></textarea>")
.appendTo(args.container)
.bind("keydown.nav", function (e) {
if (e.keyCode === $.ui.keyCode.LEFT || e.keyCode === $.ui.keyCode.RIGHT) {
e.stopImmediatePropagation();
}
})
.focus()
.select();
$('.textarea').textext({
plugins: 'tags autocomplete arrow'
}).bind('getSuggestions', function (e, data) {
var list = args.column.names,
textext = $(e.target).textext()[0],
query = (data ? data.query : '') || '';
$(this).trigger('setSuggestions', { result: textext.itemManager().filter(list, query) });
});
};
this.destroy = function () {
$input.remove();
};
this.focus = function () {
$input.focus();
};
this.getValue = function () {
return $input.textext()[0].hiddenInput().val();
};
this.setValue = function (val) {
$input.textext()[0].hiddenInput().val(val)
};
this.loadValue = function (item) {
$input[0].defaultValue = item[args.column.field];
$input.select();
};
this.serializeValue = function () {
return $input[0].defaultValue;
};
this.applyValue = function (item, state) {
item[args.column.field] = state;
};
this.isValueChanged = function () {
return (!($input.textext()[0].hiddenInput().val() == "" && defaultValue == null)) && ($input.textext()[0].hiddenInput().val() != defaultValue);
};
this.validate = function () {
if (args.column.validator) {
var validationResults = args.column.validator($input.val());
if (!validationResults.valid) {
return validationResults;
}
}
return {
valid: true,
msg: null
};
};
this.init();
}
You may want to try my repo, it's updated for the latest jQuery and has a lot of fixes and enhancements.
One of those enhancements is a Select2 editor, which is very similar to what you're trying to do. I think if you check that out it will be clear what is needed.
There's an example here.
As a bonus, there also a mechanism to allow certain keycodes to bubble through to the grid rather than be captured by the editor. This can be useful for up-arrow, etc. This isn't in the MLeibman branch.

javascript OO how to update self parameters with some JSON variable

Lets say I have a javascript object with the the following
var Settings = function () {
this.timelimit = 0;
this.locked = false;
this.expires = null;
this.age = null;
};
And then I set some get/set functions like:
Settings.prototype = {
getAllAges: function () {
return self.age;
},
getTimeLimit: function () {
return self.timelimit;
},
load: function() {
data_from_local_storage = LoadLocalStorage();
}
}
In data_from_local_storage I have JSON variables that match the above variables (timelimit, locked etc .. )
Issue is, the object var settings_ref = Settings() have all these 4 variables - but also have these 3 functions assigned in settings_ref - due to this OO behavior I need to write inside the load() function:
this.timelimit = data_from_local_storage.timelimit
this.age = data_from_local_storage.age
this.locked = data_from_local_storage.locked
Because if I'll write
this = data_from_local_storage it will destroy my object.
So how can I avoid writing all these variables one-by-one ?
w/o a for loop inside a function
in this example are just 4 but there are much much more and I cannot write it everywhere everytime
I'm looking for some .update() function like in Python or something ..
Any quick shortcut that someone know ?
You can use Object.assign() in ES2015:
load: function() {
Object.assign(this, LoadLocalStorage());
}
It's apparently not supported yet in IE, but there's a polyfill on the MDN page:
if (typeof Object.assign != 'function') {
(function () {
Object.assign = function (target) {
'use strict';
// We must check against these specific cases.
if (target === undefined || target === null) {
throw new TypeError('Cannot convert undefined or null to object');
}
var output = Object(target);
for (var index = 1; index < arguments.length; index++) {
var source = arguments[index];
if (source !== undefined && source !== null) {
for (var nextKey in source) {
if (source.hasOwnProperty(nextKey)) {
output[nextKey] = source[nextKey];
}
}
}
}
return output;
};
})();
}
(Personally I would use Object.defineProperty() to add the method, but that's verbatim from MDN.)
(edit though I guess if you don't have Object.assign() you may not have Object.defineProperty() either :)
If you store the data inside another object literal, it makes persisting things to localstorage and back a lot easier.. Here is an example..
//pretend local storage loader
function LoadLocalStorage() {
return {
timelimit: 100,
locked: true,
expires: new Date(),
age:40
}
}
var Settings = function () {
this.data = {
timelimit: 0,
locked: false,
expires: null,
age:null
}
};
Settings.prototype = {
getAllAges: function () {
return this.data.age;
},
getTimeLimit: function () {
return this.data.timelimit;
},
load: function() {
this.data = LoadLocalStorage();
}
}
var settings = new Settings;
console.log('Age before our load');
console.log(settings.getAllAges());
settings.load();
console.log('Age after our load');
console.log(settings.getAllAges());

Writing an escaped/unescaped Observable in Knockout

I have an issue where I want to store everything as quoted strings and display everything unquoted while in the form. My first solution was to just create two extra bindingHandlers to do this one for value and one for text.
ko.bindingHandlers.escapedValue = {
init : function (element, valueAccessor, allBindingsAccessor) {
var $element = $(element),
contentObservable = valueAccessor(),
currentTxt = ko.utils.unwrapObservable(contentObservable);
if (currentTxt) {
$element.val(unescape(currentTxt));
}
$element.change(function (e) {
contentObservable(escape($element.val()));
});
}
};
ko.bindingHandlers.escapedText = {
init : function (element, valueAccessor, allBindingsAccessor) {
var $element = $(element),
contentObservable = valueAccessor(),
currentTxt = ko.utils.unwrapObservable(contentObservable);
if (currentTxt) {
$element.text(unescape(currentTxt));
}
contentObservable.subscribe(function (newValue) {
$element.text(unescape(newValue));
});
}
};
However that gave me two issues 1) I do not get live updating anymore after key down 2) When I do some validation on the values for example character length it checks the length of the quoted string.
How can I write something like a ko.escapedObservable() or ko.subscribable.fn.escaped()
I have gotten closer but cant seem to get the saving correct. So now it displays properly and does the comparison properly but when I goto save it the values are still unescped
ko.escapedObservable = function (initialValue) {
var observableVal = ko.observable(initialValue),
result = ko.computed({
read: function () {
return unescape(observableVal());
},
write: function (newValue) {
return observableVal(escape(newValue));
}
});
this.toJSON = function () {
return escape(observableVal());
};
return result;
};
====EDIT====
Solution using two observables
// Escape and Unescape a text value
ko.escapedObservable = function (initialValue) {
var observableVal = ko.observable(initialValue),
result = ko.computed({
read: function () {
return observableVal();
},
write: function (newValue) {
observableVal(newValue);
}
});
result.unescaped = ko.computed({
read: function () {
return unescape(observableVal());
},
write: function (newValue) {
observableVal(escape(newValue));
}
});
return result;
};
ko.escapedObservable = function (initialValue) {
var observableVal = ko.observable(initialValue),
result = ko.computed({
read: function () {
return observableVal();
},
write: function (newValue) {
observableVal(newValue);
}
});
result.unescaped = ko.computed({
read: function () {
return unescape(observableVal());
},
write: function (newValue) {
observableVal(escape(newValue));
}
});
return result;
};
I'm having trouble figuring out exactly what you are asking. However, I believe you really just need a computed observable: [Example]
function viewModel() {
this.test = ko.observable('\"Something\"');
this.escapedTest = ko.computed(function () {
return escape(this.test());
}, this);
this.unescapedTest = ko.computed(function () {
return unescape(this.escapedTest());
}, this);
}

Categories

Resources