My domain is quite simple. I have a Message class, and a MessagesManager class.
Message has 3 attributes:
author
message (body)
timestamp
MessagesManager is basically just an interface that keeps the history of messages, adds new messages etc.
I want to store the messages in localStorage so that they can be retrieved at later points, yet I'm not sure how to do it.
class MessagesManager {
constructor() {
this.msgs = []; //retrieve from localStorage
}
add(author, message) {
var msg = new Message(author, message)
this.msgs.push(msg);
}
save() {
// save to localStorage
}
delete() {
this.msgs = [];
}
}
And my Message:
class Message {
constructor(author, message) {
this.timestamp = Date.now();
this.author = author;
this.message = message;
}
toString() {
var date = new Date(this.timestamp);
var hours = date.getHours();
var minutes = date.getMinutes();
var seconds = date.getSeconds();
if (hours < 10) hours = "0" + hours;
if (minutes < 10) minutes = "0" + minutes;
if (seconds < 10) seconds = "0" + seconds;
var time = "[" + [hours, minutes, seconds].join(":") + "]";
return time + " " + this.author + ": " + this.message;
}
}
What is the nicest/best way to store the messages (as objects, kind of) in the localStorage, while also being able to retrieve them easily (ie. in MessagesManager contructor)?
Try this:
class MessagesManager {
constructor() {
this.msgs = (localStorage.getItem("messages"))?JSON.parse(localStorage.getItem("messages")):[];
}
add(author, message) {
var msg = new Message(author, message)
this.msgs.push(msg);
}
save() {
localStorage.setItem("messages", JSON.stringify(this.msgs));
}
delete() {
this.msgs = [];
localStorage.removeItem("messages");
}
}
Docs: http://www.w3schools.com/html/html5_webstorage.asp
Related
I have the following code:
class Currency {
private counter = document.getElementById('counter');
private dust = 0;
private books = 0;
private bookCounter = document.getElementById("books");
constructor() {
console.log("test")
document.getElementById("bookbuy").addEventListener("click", this.buyBook)
}
public count() {
this.dust ++;
this.counter.innerHTML = "You have " + this.dust + " dust";
console.log(this.dust)
}
public buyBook() {
if (this.dust >= 10) {
console.log("if works");
this.dust - 10;
this.books ++;
this.counter.innerHTML = "You have " + this.dust + " dust";
this.bookCounter.innerHTML = "You have " + this.books + " books";
} else {
console.log(this.dust)
}
}
}
window.addEventListener("load", init);
function init(): void {
const currency = new Currency();
setInterval(() => {currency.count();}, 1000);
}
the console.log in count() is defined and working fine, but when i try to use this.dust in buyBook() it returns as undefined.
Why is this and how do I fix it?
You want to bind the this context for buyBook, or the event handler will redefine the this context.
Edit: Also, I think you meant to decrement 10 from this.dust not just subtract 10
class Currency {
private counter = document.getElementById('counter');
private dust = 0;
private books = 0;
private bookCounter = document.getElementById("books");
constructor() {
console.log("test")
document.getElementById("bookbuy").addEventListener("click", this.buyBook.bind(this))
}
public count() {
this.dust ++;
this.counter.innerHTML = "You have " + this.dust + " dust";
console.log(this.dust)
}
public buyBook() {
if (this.dust >= 10) {
console.log("if works");
this.dust -= 10;
this.books ++;
this.counter.innerHTML = "You have " + this.dust + " dust";
this.bookCounter.innerHTML = "You have " + this.books + " books";
} else {
console.log(this.dust)
}
}
}
window.addEventListener("load", init);
function init(): void {
const currency = new Currency();
setInterval(() => {currency.count();}, 1000);
}
I'm working on setting cookies for a website and I've been having trouble getting it right. When I check DevTools on my browser (Chrome), I'm always getting messages about the SameSite attribute being unspecified. Would anyone be able to help out?
const dropCookie = true;
const cookieDays = 14;
const cookieName = "Compliance";
const cookieState = "on";
const banner = document.getElementById("cookie-banner");
const displayBanner = () => {
const main = document.getElementsByTagName("main")[0];
banner.style.display = "flex";
main.className += " cookie-banner";
createCookie(window.cookieName, window.cookieState, window.cookieDays);
}
const createCookie = (name, value, days) => {
let cookie = name + "=" + encodeURIComponent(value);
if (typeof days === "number") {
const date = new Date();
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
const expires = "expires=" + date.toUTCString();
}
if (window.dropCookie) {
cookie = `${name}=${value}; ${expires}; path=/; secure; samesite=none`
document.cookie = cookie;
}
}
function checkCookie(name) {
const nameEq = `${name}=`;
const cookieArr = document.cookie.split(";");
for (let i = 0; i < cookieArr.length; i++) {
let c = cookieArr[i];
while (c.charAt(0) == " ") c = c.substring(1, c.length);
if (c.indexOf(nameEq) == 0) return c.substring(nameEq.length, c.length);
}
return null;
}
function eraseCookie(name) {
createCookie(name, "", -1);
}
window.onload = () => {
if (checkCookie(window.cookieName) != window.cookieState) {
displayBanner();
}
}
I found that there were several reference errors being thrown from my code. I had to correct the instances where I referenced variables as properties of window, and also fix the Unix to UTC time conversion in createCookie.
const dropCookie = true;
const cookieDays = 14;
const cookieName = "compliance";
const cookieState = "on";
const banner = document.getElementById("cookie-banner");
const displayBanner = () => {
const main = document.getElementsByTagName("main")[0];
banner.style.display = "flex";
main.className += " cookie-banner";
createCookie(cookieName, cookieState, cookieDays);
}
const closeBanner = () => {
banner.style.display = "none";
}
const createCookie = (name, value, days) => {
let cookie = name + "=" + encodeURIComponent(value);
if (typeof days === "number") {
let date = new Date();
let unixDate = date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
date = new Date(unixDate);
const expires = "expires=" + date.toUTCString();
if (dropCookie) {
document.cookie = `${name}=${value}; ${expires}; path=/; secure; samesite=lax`
}
}
}
Lastly, I fixed the window.onload statement by saving the function to a variable windowInit, then calling it in window.onload.
const windowInit = () => {
if (checkCookie(cookieName) != cookieState) {
displayBanner();
}
}
window.onload = windowInit();
Currently I am trying to learn how to use es6 classes for an application I am working on.
The goal is to increase the age of an animal based on the current date.
The Animal Class
Animal.js
class Animal {
constructor(name, age, species, gender, activity, birthday, hunger = 100, stamina = 100, happy = 100) {
this.name = name;
this.age = age;
this.species = species;
this.gender = gender;
this.activity = activity;
this.hunger = hunger;
this.stamina = stamina;
this.happy = happy;
this.birthday = birthday
}
birthday() {
this.age++
console.log(`Happy birthday to ${this.name}, ${this.name} is now ${this.age}`)
}
}
module.exports = Animal
The Time Class
Time.js
class Time {
constructor() {
this.seconds = 0;
this.minutes = 0;
this.days = 1;
this.season = 1;
this.year = 0;
this.clock = `${this.minutes}:${this.seconds}`;
this.monthDate = `${this.season}/${this.days}`
this.yearDate = `${this.season}/${this.days}/${this.year}`
this.timeStamp = `${this.season}/${this.days}/${this.year}-${this.minutes}:${this.seconds}`
// Start timer when
this.countTime = setInterval(this.increaseTime.bind(this), 1000)
}
increaseTime() {
this.clock = `${this.minutes}:${this.seconds}`
this.monthDate = `${this.season}/${this.days}`
this.yearDate = `${this.season}/${this.days}/${this.year}`
this.timeStamp = `${this.season}/${this.days}/${this.year}-${this.minutes}:${this.seconds}`
console.log(this.clock)
if (this.seconds === 60) {
this.seconds = 0;
this.minutes++
if (this.minutes === 1) {
this.minutes = 0;
this.days++
console.log(this.timeStamp)
if (this.days === 15) {
this.days = 0;
this.season++
if (this.season > 4) {
this.season = 1;
this.year++
}
}
}
} else {
this.seconds++
}
}
}
module.exports = Time;
Here is where I try to utilize them both:
Zoo.js
const Animal = require("./Animal");
const Time = require("./Time");
let time1 = new Time();
let gary = new Animal("Gary", 10, "Gorilla", "Male", "Sleeping", '1/2')
if (time1.monthDate === gary.birthday) {
gary.birthday()
}
So based off of the value of time1.monthDate (which is always changing because I have a setInterval function within the Time class), I will execute a function within the Animal Class to increase Gary's age.
I realize that in this current state, that I need to constantly run this check, so I have also tried this:
while (time1) {
if (time1.monthDate === gary.birthday) {
gary.birthday();
}
}
This also does not work although I am constantly running this check.
Any help would be appreciated, thanks in advance!
This also does not work although I am constantly running this check.
That's because no other code can run while that loop runs. That includes the setInterval callback that increases the time.
Instead, why don't you provide a way to schedule a callback to the timer tick:
class Time {
constructor() {
this.seconds = 0;
this.minutes = 0;
this.days = 1;
this.season = 1;
this.year = 0;
this.handlers = []; // < collect handlers
// Start timer when
this.countTime = setInterval(this.increaseTime.bind(this), 1000)
}
get clock() { // if this is a getter, it is always up to date
return `${this.minutes}:${this.seconds}`;
}
get monthDate() {
return `${this.season}/${this.days}`;
}
get yearDate() {
return `${this.season}/${this.days}/${this.year}`;
}
get timeStamp () {
return `${this.season}/${this.days}/${this.year}-${this.minutes}:${this.seconds}`;
}
increaseTime() {
this.seconds++
if (this.seconds === 60) {
this.seconds = 0;
this.minutes++;
}
if (this.minutes === 1) {
this.minutes = 0;
this.days++;
}
if (this.days === 15) {
this.days = 0;
this.season++
}
if (this.season > 4) {
this.season = 1;
this.year++
}
this.handlers.forEach(h => h(this)); // < trigger handlers
}
onTick(h) { this.handlers.push(h); }
}
Then you can do:
time1.onTick(() => {
if (time1.monthDate === gary.birthday) {
gary.birthday();
}
});
I'm trying to figure out the best fallback method for storing data when someone visits our site. We use this to store search filter preferences in particular, that need to move across the site as they move around. Ideally it would work with:
localStorage first
fallback to sessionStorage
then fallback to cookies
My current setup looks a bit like:
var localStorageAvail = supports_html5_storage();
var storage = "local";
if (localStorageAvail == undefined || localStorageAvail == false || localStorageAvail == null) {
storage = "cookie";
}
function supports_html5_storage() {
var testKey = 'test', storage = window.localStorage;
try {
storage.setItem(testKey, '1');
storage.removeItem(testKey);
return true;
} catch (error) {
return false;
}
}
function getValue(what) {
if (storage == "cookie") {
// get from cookie
return readCookie(what);
} else {
// localstorage
return localStorage.getItem(what)
}
}
function setValue(what,value) {
if (storage == "cookie") {
// get from cookie
writeCookie(what,value,365);
} else {
// localstorage
localStorage.setItem(what,value)
}
}
function readCookie(name) {
var nameEQ = name + "=";
var ca = document.cookie.split(';');
for(var i=0;i < ca.length;i++) {
var c = ca[i];
while (c.charAt(0)==' ') c = c.substring(1,c.length);
if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
}
return null;
}
function writeCookie(name,value,days) {
var expires;
if (days) {
var date = new Date();
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
expires = "; expires=" + date.toGMTString();
}
else {
expires = "";
}
document.cookie = name + "=" + value + expires + "; path=/";
}
FWIW, I'm not using jQuery, and I don't want to have to include extra libraries to do this. It needs to all be in Vanilla JS :)
My question: What is the best way to do this? Should I just tweak my current method, so it includes sessionStorage as well? Or are there better methods to do this?
var yModule = require('youtube-node'),
nodeYoutube = new yModule();
nodeYoutube.setKey("key");
module.exports.getVideoLength = function (vData){
youTube.getById(vData, function (result) {
return convertTime(result['items'][0]['contentDetails']['duration']);
})
};
var convertTime = function (time){
var reptms = /(?:(\d+)DT)?(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?$/;
var days = "00", hours = "00", minutes = "00", seconds = "00", formattedTime;
//if (reptms.test(time)) {
var matches = reptms.exec(time);
console.log(matches);
if (matches[1]) days = String(matches[1]);
if (matches[2]) hours = String(matches[2]);
if (matches[3]) minutes = String(matches[3]);
if (matches[4]) seconds = String(matches[4]);
formattedTime = "[" + days + ":" + hours + ":" + minutes + ":" + seconds + "]";
return formattedTime;
//}
};
I'm struggling to understand callbacks even after reading a few things about it.
nodeJs callbacks simple example this helped a little, but I'm still unclear about how it works. I've spent the past hour trying to figure out how to write this using callbacks.
This module is being called by this:
ytRetrieve.getVideoLength(youtube_parser(text))
youtube_parser's function:
function youtube_parser(url){
var regExp = /^.*((youtu.be\/)|(v\/)|(\/u\/\w\/)|(embed\/)|(watch\?))\??v?=?([^#\&\?]*).*/;
var match = url.match(regExp);
//console.log(match);
if (match&&match[7]){
return match[7].split(" ")[0];
}
}
You need to use callbacks. The issue with your code youtube_parser( is that you are calling the function. A callback is a function that is passed as an argument to be called later. If you call the function, a string is returned. getVideoLength is expecting a function as an argument, not a string.
Instead use getVideoLength(youtube_parser). This actually passes in the youtube_parser function itself to be called later (i.e. when getVideoLength completes). The arguments to youtube_parser may need to be (error, url) instead, though.
Here is a solution I came up with. Is there anything I can do to enhance this code?
Thank you for your help.
Main.js
var ytempRetrieve = require('./youtube'), ytRetrieve = new ytempRetrieve();
var ytRegex = /(?:https?:\/\/)?(?:www\.)?(?:youtu\.be\/|youtube\.com\/(?:embed\/|v\/|watch\?v=|watch\?.+&v=))((\w|-){11})(?:\S+)?/;
bot.addListener('message', function (from, to, text, message) {
if (text.match(ytRegex)) {
console.log(text);
youtube_parser(text, to, ytRetrieve.getVideoLength)
}
});
function youtube_parser(url, to, callback) {
var regExp = /^.*((youtu.be\/)|(v\/)|(\/u\/\w\/)|(embed\/)|(watch\?))\??v?=?([^#\&\?]*).*/;
var match = url.match(regExp);
//console.log(match);
if (match && match[7]) {
callback(match[7].split(" ")[0], function (res) {
setTimeout(function () {
bot.say(to, match[7].split(" ")[0] + " is " + res + " long.")
}, 1500)
});
}
}
youtube.js
var yModule = require('youtube-node'),
nodeYoutube = new yModule(),
apiKey = require('./config');
var youtube = function () {
var self = this;
self.time = null;
self.setAPIKey = function (key) {
nodeYoutube.setKey(key);
};
apiKey.getAPIKey(self.setAPIKey);
self.getVideoLength = function (vData, callback) {
nodeYoutube.getById(vData, function (result) {
callback(self.convertTime(result['items'][0]['contentDetails']['duration']));
})
};
self.convertTime = function (time) {
var reptms = /(?:(\d+)DT)?(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?$/;
var days = 0, hours = 0, minutes = 0, seconds = 0, formattedTime;
//if (reptms.test(time)) {
var matches = reptms.exec(time);
console.log(matches);
if (matches[1]) days = Number(matches[1]);
if (matches[2]) hours = Number(matches[2]);
hours += days * 24;
if (hours.toString().length === 1) {
hours = "0" + hours
}
if (matches[3]) minutes = String(matches[3]);
if (minutes.toString().length === 1) {
minutes = "0" + minutes
}
if (matches[4]) seconds = String(matches[4]);
if (seconds.toString().length === 1) {
seconds = "0" + seconds
}
formattedTime = "[" + hours + ":" + minutes + ":" + seconds - 1 + "]";
return (formattedTime);
//}
};
};
module.exports = youtube;