I am trying to create a chrome extension for Twitch and I'm facing a small issue which I cannot figure out how to solve. I must say my javascript knowledge is not the best (I am a C# developer) and this time not even google could help me (maybe because I don't know the correct search terms).
So to the problem. The goal of this extension is to notify you when a streamer changes games during live stream. I would like to notify the user only once per game changed. I have an array which contains objects, this object is a class with some properties, one of the properties is game. This array is then parsed into a json string to be saved in the browser localstorage. Below is the code i'm using to check what game is the streamer playing and trying to set the property game to that value. Then parsing the array back to json string and set the localstorage.
function checkPlaying() {
var twitchUsers = JSON.parse(localStorage.twitchUsers);
if (twitchUsers.length > 0) {
var f = (function () {
var xhr = [];
for (i = 0; i < twitchUsers.length; i++) {
(function (i) {
if (twitchUsers[i].isOnline) {
xhr[i] = new XMLHttpRequest();
xhr[i].open('GET', REQUEST_BASE + REQUEST_CHANNELS + twitchUsers[i].id, true);
xhr[i].setRequestHeader('Accept', 'application/vnd.twitchtv.v5+json')
xhr[i].setRequestHeader('Client-ID', CLIENT_ID)
xhr[i].onreadystatechange = function () {
if (xhr[i].readyState == 4 && xhr[i].status == 200) {
var response = JSON.parse(xhr[i].responseText);
var currentGame = response.game;
if (twitchUsers[i].game != currentGame) {
twitchUsers[i].game = currentGame;
if (twitchUsers[i].notify) {
browser.notifications.create({
"type": "basic",
"iconUrl": twitchUsers[i].logo,
"title": "TwitchWatcher",
"message": twitchUsers[i].name + ' is currently playing ' + twitchUsers[i].game
});
}
}
}
};
xhr[i].send();
}
localStorage.twitchUsers = JSON.stringify(twitchUsers);
})(i);
}
})();
}
}
The issue is that localStorage.twitchUsers never gets the new values, and this causes the extension to keep notifying the user with the same game.
I have tried to change the position of localStorage.twitchUsers = JSON.stringify(twitchUsers); to inside of the for loop and to the outside of the f variable, but I had no success.
Does anyone know what am I doing wrong?
Thank you.
Related
I have a tricky requirement which im not able to fix.
I have a button and if i click on that button, Im getting below data from a REST web service ..
{
"results": [{
"Reqdate": "\/Date(1520899200000)\/",
"Tooltip": "13.03.2018 Blood Donation Approved ",
"Legendid": "GROUP_LEVEL1"
},{
"Reqdate": "\/Date(1523836800000)\/",
"Tooltip": "16.04.2018 Privilege Leave Sent ",
"Legendid": "BADVALUE_LIGHT",
},{
"Reqdate": "\/Date(1524528000000)\/",
"Tooltip": "24.04.2018 Privilege Leave Sent ",
"Legendid": "BADVALUE_LIGHT",
}]
}
If im getting the above data for the first time. Im going to display one section. Now again if i click on the same button, If i get the same data in the service then i have to hide the section. But im not able to check the same data for the second time.
Im trying to fix this issue by storing the data in a variable like below..
var firstTimeResults = data.results;
Now im not able to check if the same data is coming for the second time. Actually im not able to produce the sample code too. Im sorry for that
Can someone please help me to fix this issue.
I am not fully understand your problem but I think you can store your data as JSON and check if the same data is return from the second time onwards?
Code example
// Variable declaration
var firstTimeResults = null;
// When data return
var dataJson = JSON.stringify(data.results);
if (firstTimeResults === null) {
firstTimeResults = dataJson;
}
else {
// check to see if the data is the same as the firstTimeResults
var isSame = firstTimeResults === dataJson;
if (isSame) {
// Same
} else {
// Not the same
}
}
You can simply try with Lodash library.
It makes JavaScript easier to work with arrays, numbers, objects, strings, etc.
https://lodash.com/
var firstResult = null;
var secondResult;
var showSection;
function getData() {
// here goes your API request to get the data
};
function onButtonClick() {
this.getData().then(function(value) {
if !(firstResult) {
firstResult = value;
showSection = true;
return;
}
secondResult = value;
if (_.isEqual(firstResult, secondResult)) {
showSection = false;
}
})
};
Hope this will be helpful to you!
How can I set the badge number on a specific tab only? So far I have a code that sets the badge number on all the tabs.. I've been reading around A LOT, but there doesn't seem to be a whole lot of information about this, so perhaps I will find a solution for this here.
I would like something like Adblock Plus, which sets the badge number for a specific tab. This is pretty easy in Chrome etc, but doesn't seem to be the case in Safari.
Does anyone know how extensions like Adblock plus shows the badge number on a specific tab?
So far I only have this code, but as mentioned, it sets the badge on all the tabs, which is not the result I want.
safari.extension.toolbarItems[0].badge = 2;
Edit:
I have been looking at the source code of Adblock plus, and a few other extensions that had this function. And it seems it is using some prototype.
Adblock plus background snippet:
BrowserAction.prototype = {
_set: function(name, value)
{
var toolbarItem = getToolbarItemForWindow(this._page._tab.browserWindow);
if (!toolbarItem)
{
return;
}
var property = toolbarItemProperties[name];
if (!property)
{
property = toolbarItemProperties[name] = {
pages: new ext.PageMap(),
global: toolbarItem[name]
};
}
property.pages.set(this._page, value);
if (isPageActive(this._page))
{
toolbarItem[name] = value;
}
},
setIcon: function(path)
{
this._set("image", safari.extension.baseURI + path.replace("$size", "16"));
},
setBadge: function(badge)
{
if (!badge)
{
this._set("badge", 0);
}
else if ("number" in badge)
{
this._set("badge", badge.number);
}
}
};
Content script (adblockplus.js)
FilterNotifier.on("filter.hitCount", function(filter, newValue, oldValue, page)
{
if (!(filter instanceof BlockingFilter) || !page)
{
return;
}
Prefs.blocked_total++;
var blocked = blockedPerPage.get(page) || 0;
blockedPerPage.set(page, ++blocked);
if (Prefs.show_statsinicon)
{
page.browserAction.setBadge(
{
color: badgeColor,
number: blocked
});
}
});
It seems this is how Adblock plus does it, but so far I haven't been able to replicate it. Still trying though..
Okay, so I finally found a solution for this, and thought I would share what I did, in case somebody else is in the same situation.
This morning I got the idea of storing the data in an array, when the user visits one of the websites I want to display the badge number on (doesn't store all websites the user visits), only if it matched one of the websites I wanted to target. I stored the following data in the array: root domain (example.com) and the badgeNumber.
For this to work, you need to make an array of the root domain of the websites you want to target, and then only execute the following when it matches, otherwise the array would fill up very quickly, and we don't want too much data in it.
In the global page, start by making an empty array to store the data
var badgeUpdateArray = [];
You then need to set up message handling in your global page as well.
safari.application.addEventListener('message', handleMessage, false);
function handleMessage(event) {
if(event.name === "setBadgeText"){
var id = badgeUpdateArray.length + 1;
var isFound = 0;
var found = badgeUpdateArray.some(function (el) {
if(el.identifier === event.message.identifier){
// Was found
isFound = 1;
}
});
if (isFound == 0) {
// Not found, add to the array
badgeUpdateArray.push({identifier:event.message.identifier,badgeNumber:event.message.badgeNumber});
}
// Set the badge number
safari.extension.toolbarItems[0].badge = event.message.badgeNumber;
}
}
Now we need to send the message from the content script to the global page. You need to get the root domain (example.com), I'm not getting into that here, as it's pretty easy. You will also need the badgeNumber value, this can be gathered from wherever (GET request, or elsewhere..)
Remember, only execute this code if the website matches your target domains.
var message = {
identifier: domain,
badgeNumber: rows.length
}
safari.self.tab.dispatchMessage("setBadgeText", message);
This will send the message, and store the data in the array, it will also set the badge number.
Now, for this to be working on different tabs, you will need to make an event handler for "activate" on the global page, this will run whenever a tab is active.
safari.application.addEventListener("activate", updateBadge, true);
function updateBadge(){
var cDomain = safari.application.activeBrowserWindow.activeTab.url;
cDomain = cDomain.replace("www3.","");
cDomain = cDomain.replace("www2.","");
cDomain = cDomain.replace("www1.","");
cDomain = cDomain.replace("www.","");
cDomain = new URL(cDomain);
cDomain = cDomain.hostname;
var id = badgeUpdateArray.length + 1;
var isFound = 0;
var badgeNumber = 0;
var found = badgeUpdateArray.some(function (el) {
badgeNumber = el.badgeNumber;
if(el.identifier === cDomain){
// Was found, set the badge number
isFound = 1;
safari.extension.toolbarItems[0].badge = el.badgeNumber;
}
});
if (isFound == 0) {
// Was not found
safari.extension.toolbarItems[0].badge = 0;
}
}
Hopefully I've got it all in here, and at least something that works, though I have to say that I would prefer an easier way of storing it.. like Chrome etc does it, with the tab API.
I have a script which does an ajax call to receive notifications from some page.The data received in the callback function is an integer(0,1,2,3...) only which is the number of rows selected in the query . Please find the query I am using in the description below.
I am using the following JS :
function myFunction() {
$.get("AjaxServicesNoty.aspx", function (data) {
var recievedCount = data;
var existingCount = $("lblEventCount").text();
if (existingCount == "") {
$(".lblEventCount").html(recievedCount);
$(".lblAcceptedCount").html(recievedCount);
var sound = new Audio("Sound/notificatonSound.wav"); // buffers automatically when created
sound.play();
}
else if (parseInt(recievedCount) > parseInt(existingCount)) {
$(".lblEventCount").html(recievedCount);
$(".lblAcceptedCount").html(recievedCount);
var sound = new Audio("Sound/notificatonSound.wav");
sound.play();
}
else {
$(".lblEventCount").html(existingCount);
$(".lblAcceptedCount").html(existingCount);
// var sound = new Audio("Sound/notificatonSound.wav");
// sound.play();
}
});
}
setInterval(myFunction, 3000);
The sound plays fine now. Now as the setInterval() function is called , every three seconds the beep keeps playing even though according to my if-else conditions . It shouldn't be playing so . It should be only played if there are new updates .
I have used the following query :
public int getAcceptedCount(int CustomerID)
{
string query = #"SELECT Status
FROM tblEventService
WHERE EventID IN (SELECT EventID FROM tblEvent WHERE (CustomerID = #cid)) AND (Status = 'Accepted' OR Status = 'Rejected')";
List<SqlParameter> lstP = new List<SqlParameter>();
lstP.Add(new SqlParameter("#cid", CustomerID));
DataTable dt = DBUtility.SelectData(query, lstP);
if (dt.Rows.Count > 0)
{
return dt.Rows.Count;
}
else
{
return 0;
}
}
The query selects all the rows whose Status is Accepted or Rejected. So if there are any new Accepted or Rejected events, the count will increase. And thus the JS should play the notification sound!
What am i doing wrong here ?
I think the problem on your code is this if statement if (existingCount == "") . if your exisitingCount should be an integer you don't have to test this, otherwise whenever your exisitingCount equals to 0 your sound will be played.
But if sometimes you can get an empty string then you can optimise your code and removing the first condition, and parse all the data before you start testing :
function myFunction() {
$.get("AjaxServicesNoty.aspx", function (data) {
var recievedCount = parseInt(data);
var existingCount = parseInt($("lblEventCount").text());
if ( recievedCount > existingCount ) {
$(".lblEventCount").html(recievedCount);
$(".lblAcceptedCount").html(recievedCount);
var sound = new Audio("Sound/notificatonSound.wav");
sound.play();
} else {
$(".lblEventCount").html(existingCount);
$(".lblAcceptedCount").html(existingCount);
}
});
}
setInterval(myFunction, 3000);
The browser is looking for the sound file at http://localhost:40825/EventManagement/Sound/notificationSound.wav. It sounds like the path is just wrong and the way you're referencing it in your JS is looking for a folder named Sound inside the folder the JS file is located (EventManagement).
Where exactly is this Sound folder located on your machine? If it's a sibling of the EventManagement folder, try new Audio("../Sound/notificatonSound.wav");. Otherwise, I'd just move that folder and audio inside the EventManagement folder :)
I am using PhaserIO in conjunction with Meteor to create a multiplayer html5 game and have run into a snag I cannot seem to figure out in a networking prototype I was making. First, the relevant code (also available as a gist):
if(Meteor.isClient) {
var game,
mainState,
mainStateInitialized,
characterData;
Template.game.game = function() {
characterData = Character.find().fetch();
if(!mainStateInitialized)
{
game = new Phaser.Game(500, 600, Phaser.AUTO, 'gameScreen');
createMainState()
}
}
}
function createMainState()
{
mainState = {
sprites: null,
playerLastFrame: characterData.length,
playerCurrentFrame: null,
charData: characterData,
preload: function() {
this.sprites = game.add.group();
game.stage.disableVisibilityChange = true;
game.load.image('hello', 'resources/hello.png');
},
create: function() {
$.each(characterData, function(index) {
var sprite = mainState.sprites.create(this.x, this.y, 'hello');
sprite.angle = this.angle;
});
},
update: function() {
this.charData = characterData;
this.playersCurrentFrame = this.charData.length;
if(this.playersLastFrame > this.playersCurrentFrame) {
//todo: remove player that left
}
else if(this.playersLastFrame < this.playersCurrentFrame) {
for(var i = playersLastFrame; i < playersCurrentFrame; i++) {
var thisData = this.charData[i],
sprite = null;
sprite = mainState.sprites.create(thisData.x, thisData.y, 'hello');
sprite.angle = thisData.angle;
}
}
for(var j = 0; j < mainState.sprites.length; j++) {
mainState.sprites.getAt(j).angle = this.charData[j].angle;
}
playersLastFrame = this.charData.length;
}
}
game.state.add('main', mainState);
game.state.start('main');
mainStateInitialized = true;
}
The idea of this prototype is to have a sprite shown in the canvas for each account in the DB. The main features I am testing are:
Dynamically adding sprites/player data seamlessly (as all proper multiplayer online games should be capable of. This will eventually pave the way for a proper join/leave system)
And to mess with creating efficient packets.
Right now, I am running into an issue with dynamically creating a new sprite when a player creates a new account. About 75% of the time, when a player makes a new account nothing happens. Meteor correctly pushes down the character data, which I can query, and mainState.sprites correctly shows the sprite data. However, nothing is rendered on the canvas.
The other 25% of the time, it works fine. In addition, if I have the code break-pointed it works 100% of the time as far as I can tell.
So, something intermittent is obviously occurring here but I can't figure out what the issue is. Is there something I am missing when adding a sprite during the update loop? Is there a better way to approach this?
I have my code on Nitrous.io, so I can run a localhost instance for you to hit if it would help in solving the problem.
The issue was I was not setting playersLastFrame = playersCurrentFrame.
I feel silly now considering this is a basic loop/compare structure. last = current at end of loop.
Sigh : (.
I'm not sure how to word this but is it possible to use the information I've captured and then play it back to the user?
for example.
User presses btn1, btn2 and btn3 (there is a separate sound that
responds to each button click)
Which button and time it was pressed is stored into an empty array ([{"time":1176,"elemId":"btn1"},{"time":1554,"elemId":"btn2"}])
Array is then saved into local storage
Array is then retrieved from local storage
Now from this point is it possible to playback the information in the array to mimic exactly what the user did?
Apologies if this is too vague but I'm not entirely sure what I'm looking for, thanks in advance!
EDIT: This is all I have so far, I can retreive my array and it shows in my console log but I can't think of how to progress from this point .
function get() {
// Retrieve the object from storage
var value = localStorage.getItem("eventlist");
console.log('value: ', JSON.stringify(value));
}
Here's a fiddle I wrote: http://jsfiddle.net/n6R9S/1/
I didn't assume jQuery, although that would have been easier for me.
The function-in-a-function in the timeout is due to a scoping issue.
var playback = [{
"time": 1176,
"elemId": "btn1"
}, {
"time": 1554,
"elemId": "btn2"
}]
// Do we assume that the array is in order? If not, we have to re-order it to make sure.
var output = document.getElementById('output');
document.getElementById('btn1').onclick = function () {
output.innerHTML += '1';
}
document.getElementById('btn2').onclick = function () {
output.innerHTML += '2';
}
for (i = 0; i < playback.length; i++) {
play = playback[i];
setTimeout((function (play) {
return function () {
document.getElementById(play.elemId).onclick();
}
})(play), play.time)
}