Storing reference to setTimeout() dynamically - javascript

I'm creating a reusable function to monitor how frequently a button is clicked and wait until the user stops clicking it to continue and it seemed to work great until i tried creating 2 buttons (which will be how it will be in production)
I am using objects to store the data while the user is clicking.
If the timer is not reset and runs out, it will post the data.
If you try just spamming the lemon button a few times you will see how it works. The same if you spam the diamond button.
var arr = {};
var SC={};
function SpamControl(u, i) {
this.ui = u+i;
this.Sp = SC[ui];
if (!SC[ui]){
SC[ui] = arr;
SC[ui].timer= "";
SC[ui].count = 0;
}
clearTimeout(SC[ui].timer);
SC[ui].count = SC[ui].count + 1;
SC[ui].timer = setTimeout(function(){
$('#count').prepend(u+" gave "+ SC[ui].count +" "+i+"'s in a controlled fashion!<br>");
delete SC[ui];
}, 1000);
}
The problem comes when you spam between the two buttons. I had hoped it would handle the two users attached to the buttons separately but it seems not and im not sure why.
I realize this is a bit unclear but all i can say is try it out to understand what i mean
Here is a fiddle: https://jsfiddle.net/gbe7dv1r/1/

Don't use global variables, use local ones:
let ui = u + i;
let Sp = SC[ui];
Also all entries in Sp will reference the same arr object, when initualizing an entry you might want to create a new object for each:
SC[ui] = { };
Finally some wise words: Cryptic abbreviations like u, i, ui, SC, Sp, arr will really cause you headaches when maintaining this piece of code.
const controls = { /*[name]: { timer, count } */ };
function spamControl(user, item) {
const name = user + item;
const control = controls[name] || (controls[name] = { timer: 0, count: 0 });
control.count += 1;
clearTimeout(control.timer);
control.timer = setTimeout(function(){
$('#count').prepend(
`${user} gave ${control.count} ${item}"s in a controlled fashion!<br>`
);
delete controls[name];
}, 1000);
}

Related

Why does every other object I create in a loop turns out blank on old version of Chrome?

It's something that's really bothering me, and it's also kind of important since it's part of my job.
I made an object that basically parses a hostname and puts labels on the different parts of that name.
Sounds pretty straightforward, right? HOWEVER, when I create several instances of that object in a row, every other instance turns out blank, with nothing but _proto and a few functions. No data whatsoever.
It might be important to note that I'm using an old version of Chrome (which I have to use, since the network at work is closed-circuit and it's impossible to update the software beyond what's on the net). The same code works at home.
WHAT AM I DOING WRONG, THEN?
Thanks in advance.
var reg = /([A,B,C,D,E])(\d{3})(\d{2})([F,G,H])(\d{2})/i;
var hostParser = function(hostname) {
var parsed = reg.exec(hostname);
if (parsed) {
this.prefix = parsed[1];
this.arena = parsed[2];
this.waitingRoom = parsed[3];
this.adminStatus = parsed[4];
this.ID = parsed[5];
this.hostname = hostname.toUpperCase();
return this;
}
return false;
};
Array.prototype.eliminateDuplicates = function() {
var r = [];
this.forEach(function(n) {
if (r.indexOf(n) < 0)
r.push(n);
});
return r;
};
Array.prototype.trim = function() {
var r = [];
this.forEach(function (n) {
if (!/^\s?$/.test(n))
r.push(n);
});
return r;
};
var list = [
'A40800G01',
'A40800G02',
'A40800G03',
'A40800G04',
'A40800G05',
'A40800G06',
'A40800G07',
'A40800G08',
'A40800G09'
];
list.trim().eliminateDuplicates().forEach(function (item) {
var itemParser = new hostParser(item);
console.log(itemParser);
});
This was indeed a bug with that particular Chrome version. Not being able to update, I duplicated array items on purpose and it worked.

Optimization of DefineProperty in Angularjs

I am trying to optimized my code when using DefineProperty. Below is a link to a jsfiddle example of what I need to do in my app.
https://jsfiddle.net/ismohamed/moqbpoku/
var app = angular.module("TestApp", []);
console.log(app);
app.service("MyCalc", function() {
this.addCalcs = function(totals) {
var newTotals = [];
angular.forEach(totals, function(t, index) {
newTotals[index] = {};
Object.defineProperty(newTotals[index], 'Sum', {
get: function() {
console.log("Called!")
var a = parseInt(t.a);
var b = parseInt(t.b);
return a + b;
}
});
Object.defineProperty(newTotals[index], 'Sub', {
get: function() {
var a = parseInt(t.a);
var b = parseInt(t.b);
return a - b;
}
});
});
return newTotals;
}
});
app.controller("MyCtrl", function($scope, MyCalc) {
$scope.myObject = [];
$scope.savedObject = [];
var nums1 = {};
nums1.a = 3;
nums1.b = 2;
$scope.myObject[0] = nums1;
var nums2 = {};
nums2.a = 5;
nums2.b = 4
$scope.myObject[1] = nums2;
$scope.Calcs = MyCalc.addCalcs($scope.myObject);
// Copy the original Object - Copy Messes things up so use slice instead
// to get shallow coppy
//angular.copy($scope.savedObject, $scope.myObject);
$scope.savedObject = $scope.myObject.slice();
var nums3 = {};
nums3.a = 10;
nums3.b = 5;
$scope.savedObject[0] = nums3;
$scope.SavedCalcs = MyCalc.addCalcs($scope.savedObject);
});
Basically I need to calculate in real-time the output when a user is typing in some numbers in the input textboxes. The user has groups of text boxes so each group can have it own real-time outputs, hence the use of forEach in the Calculation service which I have made. This functionality needs to remain as is.
In the example I needed to test what happened when I copied an object array as the user can make some changes and then Cancel his/her changes. By using Angular.Copy it messes things up. Therefore I found out that using slice for a shallow copy is better. Anyone knows the reason for that?
My second and real question is that as you can see I have a "Called" log output in one of the "Get" functions. Every time I change a value in one of the textboxes the Get gets called 4 times in my example. How can I optimize this so that Get is called less? Is it even possible?
Any optimization examples for this code would be greatly appreciated.

How to alter HTML code with different combinations of button presses

I wrote some code consisting of three buttons and a paragraph.
The paragraph says, "Hello my name is Dolly." and the buttons give you the choice to do one of three things to Dolly. You can either say hello, hug, or kill them, and these buttons give a response respectively. However is it possible to press a combination of buttons for a different response? For example, if I pressed the button to kill Dolly, then the button to hug Dolly, could I make it say something about hugging a dead body? If so, how?
This can be achieved by keeping a state object for dolly.
var dolly = {
is_alive: true,
hugCount: 0,
helloCount: 0,
message: function() {
if(!this.is_alive) {
//whatever you want to print if dolly's dead.
}
if(this.hugCount) {
//whatever for a certain number of hug counts.
}
if(this.helloCount) {
//whatever for a certain number of hello counts.
}
},
kill: function(){
if(this.is_alive){
this.is_alive = false;
return this.message();
}
}
};
You can keep adding more functionality if this is a simulation game prototype. just add more functions to the object, if you need to add more people like tina or james, you can make a constructor too.
var Person = function() {
this.is_alive = true,
this.hugCount = 0,
this.helloCount = 0,
};
Person.prototype.message = function() {
if(!this.is_alive) {
//whatever you want to print if dolly's dead.
}
if(this.hugCount) {
//whatever for a certain number of hug counts.
}
if(this.helloCount) {
//whatever for a certain number of hello counts.
}
};
Person.prototype.kill = function(){
if(this.is_alive){
this.is_alive = false;
return this.message();
}
};
Person.prototype.hello = function() {
this.helloCount+= 1;
return this.message();
}
Now you can just spawn as many dollys you want with the same functionality!
var dolly = new Person();
dolly.kill(); //YOU DIED!
EDIT 1
As per the suggestion of Norman Bentley, you can also use array to keep a track of user's interaction with "dolly".
var Person = function() {
this.is_alive = true,
this.hugCount = 0,
this.helloCount = 0,
this.interaction = []
};
var ACTIONS = {"HUG":0x01,"KILL":0x02,"GREET":0x03};
// Using hexes in an attempt to save bytes not sure what's the best way to do this!
Person.prototype.interaction = function(action) {
// you can use an array of constants for your actions.
this.interaction.push(action);
}
Person.prototype.kill = function() {
this.interaction(ACTIONS.KILL);
this.is_alive = false;
return this.message();
}
EDIT 2
To embed this along with HTML, refer to this JS fiddle.
https://jsfiddle.net/3jvbqm9a/
Sure you could. Create a variable/array for dolly in Javascript. Each time you perform an action to dolly, add it to this array. Read the array each time and decide what your response should be.

Plain OOP Javascript: Treating localStorage as an Array doesn't work?

I am trying to implement localStorage with my simple OOP todo list.
The fiddle is here: https://jsfiddle.net/b81t2789/
I thought I could just treat the local storage like an array and copy the logic I used with my actual array but that doesn't work.
Here, right after pushing the task into the array, I added a line that stores the task in the local storage and stringifies it:
// function that adds new task to the array
function pushArray(){
var newtask = new Task(toDo.value, "No note yet");
taskItems.push(newtask);
var storedTask = localStorage.setItem(newtask, JSON.stringify(newtask));
displayStorage(result2, storedTask);
displayArray(result, newtask.Name);
appendNote(result, newtask);
}
Then right below the function that displays the new array element I added one that retrieves the item from local storage, parses it, then creates a DOM element with the new task and appends it to another container.
//function that displays array elements
function displayArray(parent,obj){
var task = make("div","class","taskitem",obj);
parent.appendChild(task);
fadeIn(task);
}
//function that displays storage elements
function displayStorage(parent,obj){
var retrieveObject = localStorage.getItem(obj);
var parseTask = JSON.parse(retrieveObject);
var newDiv = make("div", "class", "newdiv", parseTask);
parent.appendChild(newDiv);
fadeIn(newDiv);
}
This doesn't work at all, not sure why, and then if I were to be able to get this to work how would I continue to go about storing and updating notes like I did in the array with local Storage? I thought this would be easy as I figured out how to make a todo with objects and arrays pretty quickly (when I thought it would be super difficult, but it's been a week now and I've made no progress!)
I guess these are the pitfalls of learning to code by yourself, any help would be much appreciated thank you!
Here is the full javascript code:
//getElementById shortcut
function grab(id) {
return document.getElementById(id);
}
// add eventlistener shortcut
var when = function() {
return function(obj, event, func) {
obj.addEventListener(event, func, false);
};
}();
//Custom function to create DOM elements and set their contents
function make(el,type,name,content){
var theElement = document.createElement(el);
theElement.setAttribute(type, name);
theElement.innerHTML = content;
return theElement;
}
//compute style shortcut
function setStyle(theElement){
return window.getComputedStyle(theElement);
}
//fade in shortcut.
function fadeIn(theElement){
var compute = setStyle(theElement).opacity;
theElement.style.opacity = 1;
}
/*****************************************************/
var toDo = grab("todo");
var result = grab("demo");
var demolist = grab("demolist");
var button = grab("btn");
// submit input on enter which fires function that pushes task into the array.
when(toDo, "keypress", function(event){
if (event.key == "Enter" || event.keyCode == 13) {
pushArray();
toDo.value = "";
}
});
// "SHOW ARRAY" FUNCTION to verify that the array is being updated (I like this better than using the console);
when(button, "click", function(event){
demolist.innerHTML = "";
for(i=0; i< taskItems.length; i++){
demolist.innerHTML += taskItems[i].Name + " " + taskItems[i].Note + "<br>";
}
});
function showNotes(theNote){
var defaultNote = "No note yet";
if(theNote){
}
}
var taskItems = [];
/*********************************************************/
//create Task object
function Task(name, note){
this.Name = name;
this.Note = note;
this.completed = false;
}
// function that adds new task to the array
function pushArray(){
var newtask = new Task(toDo.value, "No note yet");
taskItems.push(newtask);
displayArray(result, newtask.Name);
appendNote(result, newtask);
}
//function that displays array elements
function displayArray(parent,obj){
var task = make("div","class","taskitem",obj);
parent.appendChild(task);
fadeIn(task);
}
//function that displays notes
function appendNote(theElement,obj){
var newClassItem = make("input","class","tasknote");
theElement.appendChild(newClassItem);
when(newClassItem, "keypress", submitNote.bind(null, obj, newClassItem));
}
//function for submitting notes
function submitNote(task,noteInput){
if (event.key == "Enter" || event.keyCode == 13) {
task.Note = noteInput.value;
var newNote = make("div", "class", "hasNote", task.Note);
noteInput.parentNode.replaceChild(newNote, noteInput);
fadeIn(newNote);
when(newNote,"dblclick", function(){
newNote.parentNode.replaceChild(noteInput, newNote);
});
}
}
Being localStorage a key-value storage, depending on your needs, you are better off serializing (stringifying, whatever) the array and saving in a single index.
var tasks = [
'post the question on SO',
'describe it carefully',
'get a nice reply',
'implement the suggested solution'
];
If you really need to split it for performance reasons, you have to index them by a arbitrary index. If you have reordering it gets tricky and you can either reflush the whole set of tasks every time someone adds/edits/deletes/reorder the tasks (memory-efficient, but very CPU intensive) or save the indexes in a different key so you can reconstruct the order later, like:
var tasks = {
'task1': 'implement the suggested solution',
'task2': 'describe it carefully',
'task4': 'get a nice reply',
'task9': 'post the question on SO'
};
var tasksOrder = [9, 2, 4, 1];
The first idea is very simple to implement, but will give you problems with arbitrarily long lists, the second one is much more easy on the CPU but much harder to implement (and uses more memory). It depends on the specifics of your case.

Apply Undo Redo on elements that can dragable (drag and drop)

I want to implement functionality on the svgElements that can be dragged with javascript how could I do this...
I have implemented this with mouse up
When mouse up occurs, I save the x and y position, object id, object type (circle, rect, etc.)
Can any one tell...is this good way to implement?
If you're asking how to implement undo/redo functionality in general, it's fairly simple: you have an array of actions and a counter. You push new elements onto the array when actions occur and step backwards when people hit undo.
Very basic implementation:
var history = {
stack : [],
counter : -1,
add : function(item){
this.stack[++this.counter] = item;
this.doSomethingWith(item);
// delete anything forward of the counter
this.stack.splice(this.counter+1);
},
undo : function(){
this.doSomethingWith(this.stack[--this.counter]);
},
redo : function(){
this.doSomethingWith(this.stack[++this.counter]);
},
doSomethingWith : function(item){
// show item
}
};
Note that there should be basic error checking to see that counter doesn't go beyond bounds and that you may want to pass 'undo' info into doSomethingWith in the case of an undo, but all that is app specific.
cwolves describes a good structure for the undo/redo functionality.
You are correct, that during mouse-up, you'll want to store the history, but you'll also want to store the original location of the object(s) being manipulated during mouse-down so you'll have have it when you undo the move.
If you end up taking it a step further, and allowing scaling, then you'll want to store the complete original transform e.g. "translate(10,10) scale(1.2,1.2) rotate(90)", for history, but also so that you'll have a baseline to apply the drag-scaling action to.
I found a better structure without counter, index shift or limit handling problems.
Simply 2 stack for "done" and "reverted" action that are balancing.
var history = function() {
this.done = this.reverted = [];
var self = this;
this.add = function(item) {
self.done.push(item);
// delete anything forward
self.reverted = [];
};
this.undo = function() {
var item = self.done.pop();
if (item) {
self.reverted.push(item);
}
return item;
};
this.redo = function() {
var item = self.reverted.pop();
if (item) {
self.done.push(item);
}
return item;
};
};
There are some issues with the above codes.
I attempted to use those and found out that I had to hit undo twice initially before it started going down the array.
So say I have done[0] done[1] done[2].
done[2] was just saved into the array. If I hit undo, it returns that. You dont want that. It is replacing what is already there. But hitting undo again, THEN you get your previous code.
Mine, as I have a drag and drop editor with different modes of edits. Drag and Drop Elements. Edit Elements HTML/Pictures. Sorting elements.
$("#neoContentContainer") contains all editor html.
And you can call editor_add on clicks, mousedowns ect... seeing it is a function you can easily call.
function editor_add(){
undo.push($("#neoContentContainer").html());
update_buttons();
}
function editor_undo(){
var item = undo.pop();
// prevent undo/redo from undoing to the same HTML currently shown.
if(item == $("#neoContentContainer").html()){
redo.push(item);
item = undo.pop();
}
if(item){
redo.push(item);
$("#neoContentContainer").html(item);
}
update_buttons();
}
function editor_redo(){
var item = redo.pop();
if(item == $("#neoContentContainer").html()){
undo.push(item);
item = redo.pop();
}
if(item){
undo.push(item);
$("#neoContentContainer").html(item);
}
update_buttons();
}
function update_buttons(){
if(undo.length == 0){
$('button[data-id="undo"]').attr('disabled',true);
} else {
$('button[data-id="undo"]').attr('disabled',false);
}
if(redo.length == 0){
$('button[data-id="redo"]').attr('disabled',true);
} else {
$('button[data-id="redo"]').attr('disabled',false);
}
}
https://jsfiddle.net/s1L6vv5y/
Not perfect, but get the jist of it. (Still wondering when we can get ONE LINE LINE BREAKS!!!! DEVS AT STACKOVERFLOW!! :)
So when my page loads, I run: editor_add();
Because when they do something, it needs to undo to something!
Now every time they drop something, sort things I run editor_add(); Took me all day, now realizing how simple it was this works very well for me.
So with this one, which is good....
var history = function() {
this.done = this.reverted = [];
var self = this;
this.add = function(item) {
self.done.push(item);
};
this.undo = function() {
if(done.length >= 3){
var undo_item = self.done.pop();
self.reverted.push(undo_item);
}
var item = self.done.pop();
if (item) {
self.reverted.push(item);
}
return item;
};
this.redo = function() {
if(reverted.length >= 3){
var revert_item = self.reverted.pop();
self.done.push(revert_item);
}
var item = self.reverted.pop();
if (item) {
self.done.push(item);
}
return item;
};
};
You do not want to clear the redos until you ran through the array.
(Guess logging in before editing helps!)

Categories