I am creating a chrome extension UI using angular. I want to make it so when the user clicks the icon in the upper right the correct screen appears. If the user is not logged in they should go to the login page. If the user is logged in and in drawing mode then they should go to the drawing screen, if they are logged in and not drawing then they should go to the main menu.
My main problem is checking whether or not they are already in drawing mode. I am sending a message to my content scripts to check whether or not I am in drawing mode, but for some reason this callback is never getting called! Very disappointing. I'm not sure when code in app.config gets called; when does it?
app.js
app.config(function($stateProvider, $urlRouterProvider) {
var rootRef = new Firebase(mysterious_url);
var user = rootRef.getAuth();
chrome.tabs.sendMessage('isInDrawingMode', {action: 'isInDrawingMode'}, function(response) {
if (!user) {
$urlRouterProvider.otherwise('/login');
} else if (response.inDrawingMode) {
$urlRouterProvider.otherwise('/draw');
} else {
$urlRouterProvider.otherwise('/main');
}
});
contentscripts.js
chrome.runtime.onMessage.addListener(
function (request, sender, sendResponse){
// Toggle User Canvas Messages
if ( request.toggle === 'off' ){
// toggleUserCanvasOff();
disableDrawingMode();
sendResponse({confirm:'canvas turned off'});
} else if ( request.toggle === 'on' ){
enableDrawingMode();
// toggleUserCanvasOn();
sendResponse({confirm:'canvas turned on'});
// Initialize toggle status for popup button
} else if ( request.getStatus === true ){
sendResponse({status:toggle});
} else if (request.canvasData) { // new Canvas data
onCanvasData(request.site, request.user, request.data);
} else if (request.erase){
eraseUserCanvas();
} else if (request.changeColor){
lineColor = request.changeColor;
} else if (request.image){
getCurrentUser(function(user){
var userCanvas = $('.'+ user);
addOneTimeClickEvent(userCanvas, addImage, request.image);
});
} else if (request.action) {
sendResponse({inDrawingMode: "true"});
}
}
);
Related
Is there a way to check if the service worker found an update before loading custom functions?
i have this function which is working, but it runs the custom functions twice, and seems very untidy..
I'm looking for a way to only run the custom functions once, and not when an update was found and installed. When an update is found, the user || the page will reload automatically and then the custom functions can run normally..
I added the reg.events in this function to determine where to place my custom functions. I hope this question is understandable..
function installApp(path, scope) {
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register(path, {
scope: scope
}).then((reg) => {
// event listener to catch the prompt if any and store in
// an instance for later use with add to homescreen() function.
getPrompt();
// this is a custom alert type notification
makeProgress('System','is ok');
/* THIS IS THE UPDATE FOUND FUNCTION */
reg.onupdatefound = function() {
var installingWorker = reg.installing;
installingWorker.onstatechange = function() {
switch (installingWorker.state) {
case 'installed':
if (navigator.serviceWorker.controller) {
// the _clear() function removes items from the locaforage db to
// force the app to not auto login, but let the user
// login again to refresh any data when the page reloads
_clear('uuid');
_clear('user');
_clear('token');
makeProgress('new version','reload app');
} else {
// removes any custom notifications
clearProgress();
//just go into the app because everything is loaded.
//We dont need to reinstall the
//homescreen or listen for the homescreen because this
//is an update and the homescreen should already be installed?
enterApp();
}
break;
case 'redundant':
// removes any custom notifications cause
//the install is complete
clearProgress();
enterApp();
console.log('The installing service worker became redundant.');
break;
}
};
return;
};
/** Here is the events that fire during the install
// process and where i am currently stuck **/
if (reg.installing) {
makeProgress('updating','files');
/* THE SERVICE WORKER IS DOWNLOADING THE CACHE FROM THE SERVER */
} else if (reg.waiting) {
/* what message here ?*/
/* as far as i can tell, THE SERVICE WORKER IS WAITING FOR
*//*PREVIOUS SERVICE WORKER TO BEREFRESHED SO A RELOAD */
/*UI SHOULD COME HERE??*/
} else if (reg.active) {
/* what message here ?*/
/* IS THIS THE BEST PLACE TO RUN THE BELOW CUSTOM
*//*FUNCTIONS?? WILL //THEY ALWAYS FIRE */
}
/** AT WHICH OF THE EVENTS ABOVE WILL I ADD THE FUNCTIONS FROM HERE **/
requestWakeLock();
const browserFeatures = detectFeatures(reg);
setCompatibilityArray(browserFeatures);
localforage.ready().then(function() {
localforage.getItem('homescreen').then(function (value) {
if(value != 1){
if (platform == 'iPhone' || platform == 'iPad') {
installHome();
} else {
makeProgress('waiting', 'prompt');
waitPrompt();
}
return;
} else {
enterApp();
return;
}
}).catch(function (err) {
alertIt('something went wrong. Please refresh the page to try again. If the problem persists, try another browser.</br>', 'warning', 0);
return;
});
}).catch(function (err) {
alertIt('Something went wrong.<br>Please refresh the page to restart the installation process.<br>'+err, 'danger', 0);
return;
});
/** TO HERE, WITHOUT RUNNING THESE FUNCTION DURING*/
/*THE ONUPDATEFOUND EVENT AS THEN THEY WILL RUN TWICE**/
}, (err) => {
alertIt('Something went wrong.<br>Please refresh the page to restart the installation process.<br>', 'danger', 0);
})
} else {
alertIt('This browser is not compatible with this app.<br>Please try to use a different browser to install this application.<br>', 'danger', 0);
return;
}
}
I initialize this script like so:
window.addEventListener("load", () => {
makeProgress('Checking','system');
installApp(appsPath, appScope);
})
basically they must not be invoked if a new update is found..
I discovered that the onupdate function runs when old service worker is active..
If the onupdate function fires it changes a variable to a true value
I then used a time out function in the active event to see if a variable had changed... if it did change then i return false, and let the onupdate functions continue their course.. otherwise i continue to load my custom functions...Its working, but it doesn't seem like the best way.
Do you have a better method?
so like this:
function installApp(path, scope) {
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register(path, {
scope: scope
}).then((reg) => {
getPrompt();
makeProgress('refreshing','files');
var entApp = true;
reg.onupdatefound = function() {
entApp = false;
var installingWorker = reg.installing;
installingWorker.onstatechange = function() {
switch (installingWorker.state) {
case 'installed':
if (navigator.serviceWorker.controller) {
_clear('uuid');
_clear('user');
_clear('token');
makeProgress('new version','reloading app');
setTimeout(function(){
location.reload();
}, 2500);
return;
} else {
/*NOT SURE WHAT IS SUPPOSED TO GO HERE, SO I JUST RELOADED THE PAGE*/
makeProgress('new version','reloading app');
setTimeout(function(){
location.reload();
}, 2500);
return;
}
break;
case 'redundant':
/*NOT SURE WHAT IS SUPPOSED TO GO HERE, SO I JUST RELOADED THE PAGE*/
makeProgress('new version','reloading app');
setTimeout(function(){
location.reload();
}, 2500);
return;
break;
}
};
return;
};
if (reg.active) {
/** RIGHT HERE IS WHERE THE ONUPDATE FIRES. I GAVE IT A
2.5 SECONDS TO DO ITS THING, THEN CHECKED TO SEE IF THERE WAS
AN UPDATE, IF NO UPDATE THEN I RUN MY CUSTOM FUNCTIONS, OTHERWISE
THE ONUPDATE FUNCTION RELOADS THE PAGE AND THE UPDATED SW.JS FILE
WILL THEN RUN THESE FUNCTIONS WHEN ITS ACTIVE.. IS THERE A BETTER
IN-BUILT METHOD TO DO THIS?**/
setTimeout(function(){
if(entApp === true){
requestWakeLock();
const browserFeatures = detectFeatures(reg);
setCompatibilityArray(browserFeatures);
localforage.ready().then(function() {
localforage.getItem('homescreen').then(function (value) {
if(value != 1){
if (platform == 'iPhone' || platform == 'iPad') {
installHome();
} else {
makeProgress('waiting', 'prompt');
waitPrompt();
}
return;
} else {
enterApp();
return;
}
}).catch(function (err) {
alertIt('something went wrong. Please refresh the page to try again. If the problem persists, try another browser.</br>', 'warning', 0);
return;
});
}).catch(function (err) {
alertIt('Something went wrong.<br>Please refresh the page to restart the installation process.<br>'+err, 'danger', 0);
return;
});
}
}, 2500);
}
I have 2 webcam connected to my pc. I am using this library https://github.com/infusion/jQuery-webcam.
I am able to view the webcam no problem and I can also change to other camera by right clicking, and the Adobe Flash Player will popup where I can select other camera.
Following this website:
http://sshilko.com/examples/jQuery-AS3-Webcam/example.html
The example shown in that website, it can change the camera by selecting from the dropdownlist.
But my code stucks at
debug: function (type, string) {
if (type == 'error') {
$("#lblCameraList").html(string);
} else {
$("#lblCameraList").html('');
}
},
Error says "No camera mode present, falling back...".
Below that code is another code
cameraEnabled: function () {
this.debug('notice', 'Camera enabled');
var cameraApi = this;
if (cameraApi.isCameraEnabled) {
return;
} else {
cameraApi.isCameraEnabled = true;
}
var cams = cameraApi.getCameraList();
for (var i in cams) {
$("#cboCamera").append("<asp:ListItem Value='" + i + "'>" + cams[i] + "</asp:ListItem>");
}
$("#cboCamera").change(function () {
var success = cameraApi.setCamera($(this).val());
if (!success) {
//webcam.debug('error', 'Unable to select camera');
console.log("Failed to set camera");
} else {
//webcam.debug('notice', 'Camera Changed');
console.log("Success set camera");
}
});
The program wont event enter this cameraEnabled because of the error.
Any helps?
Problem solved. I followed exactly from this website http://sshilko.com/examples/jQuery-AS3-Webcam/example.html
The error was i was not using the same javascript file as the website.
So, I have the following code:
var clicks = 0; // click counter
// Make sure this only runs on facebook
chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) {
if (tab.url.indexOf("facebook.com") > -1) {
chrome.pageAction.show(tabId);
}
});
// Called when the user clicks on the page action.
chrome.pageAction.onClicked.addListener(function(tab) {
if (clicks == 0) {
chrome.pageAction.setIcon({path: "dontlike.png", tabId: tab.id}); // Update icon
chrome.pageAction.setTitle({title: "idontlike", tabId: tab.id}); // Update title
chrome.tabs.executeScript({ // Hide like buttons
code: 'var like = document.getElementsByClassName("UFILikeLink"); for (index = 0; index < like.length; ++index) { like[index].style.display="none"; }'
});
}
else {
chrome.pageAction.setIcon({path: "like.png", tabId: tab.id}); // Update icon
chrome.pageAction.setTitle({title: "like", tabId: tab.id}); // Update title
chrome.tabs.executeScript({ // Show like buttons
code: 'var like = document.getElementsByClassName("UFILikeLink"); for (index = 0; index < like.length; ++index) { like[index].style.display=""; }'
});
}
// wrap coutner around
clicks++;
if (clicks > 1)
clicks = 0;
});
for a chrome extension that hides all "Like" buttons on facebook when a pageaction icon is clicked. This works; however, any time a new facebook url is loaded, the state of the extension is lost, e.g. if the button is in dislike mode (hide all likes), if I go to a new page, it is reset to like mode.
I had an idea to persist the state of the extension using the click counter, and to make the code more functional with something like the following
var clicks = 0; // click counter
function like() {
chrome.pageAction.setIcon({path: "like.png", tabId: tab.id}); // Update icon
chrome.pageAction.setTitle({title: "like", tabId: tab.id}); // Update title
chrome.tabs.executeScript({ // Show like buttons
code: 'var like = document.getElementsByClassName("UFILikeLink"); for (index = 0; index < like.length; ++index) { like[index].style.display="none"; }'
});
clicks++;
if (clicks > 1) {
clicks = 0;
}
}
function dislike() {
chrome.pageAction.setIcon({path: "like.png", tabId: tab.id}); // Update icon
chrome.pageAction.setTitle({title: "like", tabId: tab.id}); // Update title
chrome.tabs.executeScript({ // Show like buttons
code: 'var like = document.getElementsByClassName("UFILikeLink"); for (index = 0; index < like.length; ++index) { like[index].style.display=""; }'
});
clicks++;
if (clicks > 1) {
clicks = 0;
}
}
// Make sure this only runs on facebook
chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) {
if (tab.url.indexOf("facebook.com") > -1) {
chrome.pageAction.show(tabId);
if (clicks == 0) {
like();
}
else {
dislike();
}
}
});
// Called when the user clicks on the page action.
chrome.pageAction.onClicked.addListener(function(tab) {
if (clicks == 0) {
like();
}
else {
dislike();
}
});
But that code doesn't work at all (when I click on the page action icon, nothing happens and no error messages appear in the chrome console).
I'm new to JS and Chrome Extensions. Is there an easy way to persist the state of my extension, and a better way to execute the script I need to hide all like buttons?
Thank you!
The question of states in chrome extension can have several answers. The choice depend of the situation. Whet I have understand in your case is that you only have tow states, so I will give you some idea.
1. Persistent background script
By default, background script is loaded at chrome startup, so it lives during the whole execution of chrome, until the user explicitly close chrome. In combination with Content Script, you can have a state full system.
So You can use this background script to save a state during the execution and inform listening content scripts of the changes :
background.js
var state = 0;
chrome.pageAction.onClicked.addListener(function(tab) {
if (state = 0) {
state = 1;
state0Actions(); //Do what you want
}
else {
state = 0;
state1Actions(); //Do what you want
}
//Inform content scripts that the state have changed
chrome.tabs.sendMessage(tab.id, {state : state});
});
//At initialisation, Content scipts will request the current state to background script.
chrome.runtime.onMessage(function(message, sender, callback){
if(message.getState) callback({state : state});
});
You can then inject a content script to all facebook pages by adding this to your manifest.json file
"content_scripts" :
[
{
"matches": ["https://www.facebook.com/*","http://www.facebook.com/*"],
"all_frames": true,
"js": ["contentScript.js"]
}
]
It will automatically inject the contentScipt.js script to all page beginning with http(s)://www.facebook.com.
contenScript.js
//the actions to do for each states
function state0Actions()
{
//Do what you want for the state 0
}
function state1Actions()
{
//Do what you want for the state 1
}
//Message will be received at each update of state in the background page
chrome.runtime.onMessage.addListner(function(message, sender, callback))
{
//Check the message is valid
if(message.state == null)
{
console.log("Unreconized message");
return;
}
//Do actions for the right state
//You also can use if statements here... Switch are more used when there is lots of states
switch(message.state) {
case 0 : state0Actions(); break;
case 1 : state1Actions(); break;
}
}
//Request the current state to initialise the script
chrome.runtime.sendMessage({getState: true});
Here, the onMessage handler will be call a first time when he is loaded and then each time the background change the state.
Pay attention that the state will be reset at the chrome startup.
2. Chrome storage
You can use chrome.storage API to manage the state. The main point of this is that the state will be saved and will not be reset at chrome startup.
To do this, you have pretty the same background code :
chrome.pageAction.onClicked.addListener(function(tab) {
chrome.storage.local.get("state", function(result)
{
//First initialisation of the state in the local storage
if(result.state == null)
{
chrome.storage.local.set({state: 0});
state0Actions(); //Do what you want
}
else if (result.state == 0) {
result.state = 1;
state0Actions(); //Do what you want
}
else {
result.state = 0;
state1Actions(); //Do what you want
}
//Save the new state to the storage
chrome.storage.set({state: result.state});
}
});
And the content script will listen changes of the local storage instead of wating update notification from the background page :
//the actions to do for each states
function state0Actions()
{
//Do what you want for the state 0
}
function state1Actions()
{
//Do what you want for the state 1
}
chrome.storage.local.onChanged.addListener(function(changes, areaName)
{
if(areaName != "local" || changes.state == null) return;
switch(changes.state)
{
case 0 : state0Actions(); break;
case 1 : state1Actions(); break;
}
})
chrome.storage.local.get("state", function(result){
if(result.state == null) state0Actions(); //Do what you want if the state is not yet initialised
else if (result.state == 0) state0Actions(); //Do what you want
else if (result.state == 1) state1Actions(); //Do what you want
})
You also can use chrome.storage.sync instead of chrome.storage.local for a shared state with all user's devices.
This are to way to play with state. you have to compare what is the better for your use case. The code I have written is not tested, they are only example to illustrate my explanation.
Don't forget to check Chrome API documentation
When the extension is using a non-persistent background page aka Event page it is unloaded after ~5 seconds of inactivity. And every time it's reloaded the code runs again and all variables are re-initialized, thus losing the previous state.
The solution is to store the state in localStorage which doesn't require any additional permissions in manifest.json:
Initialization:
var clicks = localStorage.clicks || 0; // click counter
Toggling and storing (no need for ++ and if):
var clicks = localStorage.clicks = 1 - clicks;
The value will be stringified and stored as "0" or "1" but for the above arithmetic it's not a problem.
So i've got a main page where there's 3 buttons - login, register and recover account. I want to disable all those buttons and display a message when geolocation is unavaliable or the user has not allowed the browser to share it's location.
$scope.btnDisabled = false;
$scope.errorMsg = null;
$scope.checkBrowser = function () {
if (navigator.geolocation.getCurrentPosition) {
// let's find out where you are!
console.log("Got your location");
$scope.btnDisabled = false;
} else {
$scope.errorMsg = "Warning. Could not locate your position. Please enable your GPS device.";
//disable the buttons
$scope.btnDisabled = true;
//Show errorMessage
return true
}
//errorMessage visibility is set false
return false;
}
So far i haven't managed to disable the buttons or show errorMessage(except for an alert message).
This is how i got the alert message:
function getPosition() {
console.group("geoloc getPosition");
if (window.navigator) {
console.log("api supported");
navigator.geolocation.getCurrentPosition(success, error);
} else {
console.log("api fallback");
}
console.groupEnd();
}
function success(pos) {
console.log("geoloc pos ", pos);
}
function error(err) {
alert("Geolocation identification failed.");
$scope.btnDisabled = true;
}
Why doesn't the $scope.btnDisable work in error() function?
You should use
navigator.geolocation.getCurrentPosition(
function() {
$scope.btnDisabled=false;
$scope.$apply();
}, function() {
$scope.btnDisabled=true;
$scope.$apply();
}
});
It is verry important to know, that getCurrentPosition will call a callback instead of returning something! See https://developer.mozilla.org/en-US/docs/Web/API/Geolocation.getCurrentPosition
You've also to call $scope.$apply() as you'll disable any angular updates while processing non angular callbacks!
Do so in your 2nd example and the button state should get updated
I'm writing a phonegap/jquery mobile app and have an issue I cant seem to solve.
When the app loads (device ready and jqm_mobile_init) fire and the app creates/opens a database and checks if a user is signed in (just a flag in the db). If so the app calls $.mobile.changePage("#home", {transition:"none"}); to redirect them to the "home" page.
Then on the "home" page pageshow event I grab a load of info from the db and append it to a listview within the home page.
However, the first time this runs (with the $.mobile.changePage event) the pageshow event isn't trigged (so none of my data gets appended to the listview). If I navigate around the app and then visit the page the data shows fine. This only happens when using $.mobile.changePage to change to the home page.
How can I make pageshow() fire on $.mobile.changePage? or is there another way to do it?
Heres my code:
/************************************************
Try to create/open the DB, if not catch the error
***********************************************/
try {
if (!window.openDatabase) {
alert('not supported');
} else {
var shortName = 'test';
var version = '1.0';
var displayName = 'test Database';
var maxSize = 200000; // in bytes
// database instance in db.
var db = openDatabase(shortName, version, displayName, maxSize);
// Create tables
createTables(db);
// Check if there is a signedin user
isUserSignedInQuery(db);
}
} catch(e) {
// Error handling code goes here.
if (e == 2) {
// Version number mismatch.
alert("Invalid database version.");
} else {
alert("Unknown error "+e+".");
}
return;
}
// Universal null/blank data handler
function nullDataHandler(transaction, results) { }
// Universal error callback
function errorHandler(error) {
//alert("Error processing SQL: " +error.message+ " Error Code: " +error.code);
}
// Create tables if dont already exist
function createTables(db) {
db.transaction(
function (transaction) {
// create tables
}
);
}
/**********************************************************************************************
Check if there is a signed in user, if so redirect to listings page, if not display login page
**********************************************************************************************/
function isUserSignedInQuery(db) {
db.transaction(
function (transaction) {
transaction.executeSql("SELECT * FROM USERS WHERE signedIn=1;",
[], // array of values for the ? placeholders
isUserSignedInDataHandler, errorHandler);
}
);
}
function isUserSignedInDataHandler(transaction, results) {
// Handle the results
if (results.rows.length > 0) {
//console.log("someones logged in!");
// Assign signed in user to global var
console.log("logged in user = " + results.rows.item(0).id);
window.currentSignedInUserId = results.rows.item(0).id;
$.mobile.changePage( "#home", { transition: "none"} );
} else {
$.mobile.changePage( "#login", { transition: "none"} );
}
}
/**********************************************************************************************
Sign in page:
**********************************************************************************************/
function doesSigningInUserAlreadyExistQuery(db) {
db.transaction(
function (transaction) {
transaction.executeSql("SELECT * FROM USERS WHERE username='"+usernameValue+"' ORDER BY id LIMIT 0,1;",
[], // array of values for the ? placeholders
doesSigningInUserAlreadyExistDataHandler, errorHandler);
}
);
}
function doesSigningInUserAlreadyExistDataHandler(transaction, results) {
// User exists, sign them in.
if (results.rows.length > 0) {
//console.log("user exists");
// Find number of rows
var len = results.rows.length;
//console.log(len);
for (var i=0; i<len; i++){
//console.log(results.rows.item(i));
db.transaction(
function (transaction) {
transaction.executeSql('UPDATE USERS SET signedIn = 1 WHERE username="'+usernameValue+'"');
}
);
// Assign signed in user to global var
window.currentSignedInUserId = results.rows.item(0).id;
// Redirect to home/listings page
$.mobile.changePage( "#home", { transition: "slidefade"} );
}
// User is new, create them and sign them in
} else {
db.transaction(
function (transaction) {
transaction.executeSql('INSERT INTO USERS (username, password, userId, defaultHandler, autoSync, updateCaseTypes'
+', updateHistorical, updateFavorite, signedIn) '
+'VALUES ("'+usernameValue+'", "eclipse", "userid321", "Another User", 1, 1, 1, 1, 1);', [],
function (transaction, resultSet) {
if (!resultSet.rowsAffected) {
// Previous insert failed.
alert('No rows affected!');
return false;
}
alert('insert ID was '+resultSet.insertId);
//Assign signed in user to global var
window.currentSignedInUserId = resultSet.insertId;
});
}
);
// Redirect to home/listings page
$.mobile.changePage( "#home", {
reloadPage: true,
transition: "slidefade"} );
}
}
$('#login').live('pageshow', function(event) {
console.log(window.currentSignedInUserId); // This is empty - global var not working
// Should this be tap??????? Find out. -----------
$('a#signIn').click(function() {
// Get values of all fields & buld vars
var username = $('#login-username');
var password = $('#login-password');
// Check if fields are empty
if( !username.val() ) {
username.addClass('empty');
$('label.login-username').addClass('blank');
}
if( !password.val() ) {
password.addClass('empty');
$('label.login-password').addClass('blank');
}
// If both not empty, check if user exists, if so sql update if not sql insert
if (username.val() && password.val()) {
// Get username
usernameValue = username.val();
// Run function
doesSigningInUserAlreadyExistQuery(db);
}
});
});
$('#home').live('pageshow', function(event) {
console.log("Page show fired on recordings page");
db.transaction(getRecordingsQuery, getRecordingsDataHandler, errorHandler);
// get stuff, loop through it and append
// Refresh the list to add JQM styles etc
$('#recordings-list').listview('refresh');
}
});
I've managed to resolve it, its not really a proper fix but it works at the expense of a screen flicker whilst the screen refreshes.
If it helps anyone, I added allowSamePageTransitions: true which solved the issue (at the expense of a flicker).
You should be using on() instead of live(). live() has been deprecated.
Have you tried putting it in the beforepageshow instead of pageshow? It seems like a better place to put data gathering/dynamic page element generation.