How to put delay while opening tab via Chrome Extension - google-chrome-extension

As chrome.tabs.create is asynchronous, it opens link immediately. I have to open a certain links with delay. Is it possible?

This is how I achieved:
chrome.browserAction.onClicked.addListener(function (tab)
{
chrome.tabs.query({active: true, currentWindow: true}, function(tabs)
{
chrome.tabs.sendMessage(tab.id, {method: "sendHTML"}, function(response)
{
var val = null;
if(response.data != null) {
lnks = parse_links(response.data);
val = setInterval(function(){
var total = lnks.length;
if (l < lnks.length) {
console.log(lnks[l]);
chrome.tabs.create({ url: lnks[l] });
l++;
} else {
clearInterval(val);
}
}, 5000);
}
}
);
});
});
I incremented array indices in loop and as soon as it meets the length, cleared it.

Related

Mongodb, Node - Updating multiple objects synchronously

Using Mongodb, Nodejs, Async.js and Express.js
I am trying to update multiple documents, where each document has its own update, at the same time. I want to wait for all documents to update so that I can notify user that all documents have updated.
The issue I am having now is that my callback function is not firing, or if it is, then nothing is happening. Here is my progress:
db.client.collection('page').find({page_id: page_id}).toArray(function(page_err, document_page) {
if(page_err) {
throw page_err;
} else if(document_page === '' || document_page === undefined || document_page === null) {
throw page_err;
} else {
var count = 0;
async.each(data, function iteratee(i, callback) {
var item_id = (i.item_id === '') ? new ObjectId() : new ObjectId(i.item_id);
var query = {item_id: item_id};
var update = {
_id : new ObjectId(),
page_id : page_id,
section_id : null,
item_id : item_id,
created : new Date().toISOString(),
item_type : "dish",
item: {
title: i.title,
description: i.description,
price: i.price,
star: false,
double_star: false
},
last_modified: new Date().toISOString()
};
var options = { upsert: true };
db.client.collection('item').updateOne(query, {$set: update}, options, function(item_err, results) {
if(item_err) {
res.sendStatus(500);
} else if(results === '' || results === undefined || results === null) {
res.sendStatus(400);
} else {
++count;
if(count === data.length) {
callback();
return;
}
}
});
}, function() {
console.log('sending 200 status');
res.sendStatus(200);
});
}
});
When I run the code I do enter the if statement where I call callback(). I have been stuck on this for a few hours and I cannot get it to work. If you need more info, I'd be happy to provide it. For simplicity's sake I removed many console.logs to avoid clutter as well.
All iterations need to fire the callback otherwise it will hang indefinitely. callback must be called in every iteration. Always.
If you encounter an error, you need to call callback(error). The problem you'll have is that async.each schedules all iterations beforehand so iteratee will fire data.length times regardless of whether an error is encountered half way through execution or not. If you need to run them in series you can use async.eachSeries which will take more time but gives you better control and no need to rollback.
So code wise it looks like this:
db.client.collection('page').find({page_id: page_id}).toArray(function(page_err, document_page) {
if(page_err) {
throw page_err;
} else if(document_page === '' || document_page === undefined || document_page === null) {
throw page_err;
} else {
async.each(data, function iteratee(i, callback) {
var item_id = (i.item_id === '') ? new ObjectId() : new ObjectId(i.item_id);
var query = {item_id: item_id};
var update = {
_id : new ObjectId(),
page_id : page_id,
section_id : null,
item_id : item_id,
created : new Date().toISOString(),
item_type : "dish",
item: {
title: i.title,
description: i.description,
price: i.price,
star: false,
double_star: false
},
last_modified: new Date().toISOString()
};
var options = { upsert: true };
db.client.collection('item').updateOne(query, {$set: update}, options, function(item_err, results) {
if(item_err) {
callback(500);
} else if(results === '' || results === undefined || results === null) {
callback(400)
} else {
callback();
}
});
}, function(err) {
// Passing the status code only for the example.
// `err` should be an object with more metadata probably
if(err) {
res.sendStatus(err);
return;
}
console.log('sending 200 status');
res.sendStatus(200);
});
}
});

Chrome Extension: Background js cannot receive message from Popup js occasionally

There is a button on the popup. When clicked, popup.js will send a message to background.js. It works well almost all the time. But after a long time not operating chrome, when I click the button, the message is sent(alert("Start to send message") executed) but cannot be received by background.js(alert("Start to render") not executed). When I click one more, it will sometimes work. If it still doesn't work, I will click again, the third time has much more chance to success. That's very strange.
I use jquery in the popup.html, but I don't think it matter.
Javascript code in popup.js
$('#create).html(chrome.i18n.getMessage('create'))
.on('click', function(){
_current = null;
chrome.runtime.sendMessage({
cmd: 'SITEMAP_DEFINE'
});
alert("Start to send message");
window.close();
})
Javascript code in background.js
var curTabId; //current tab in which scraper is going to scraping
var devtoolPort;
var coord;
var sitemap;
var finish = false;
function sendMessage(msg) {
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
chrome.tabs.sendMessage(tabs[0].id, msg);
});
}
//detective devtools
chrome.runtime.onConnect.addListener(function (port) {
if (port.name == "devtools-papa") {
devtoolPort = port;
}
});
//tab update
chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) {
if(finish === false && coord && changeInfo.status == 'loading'){
coord.onUpdate(tab);
}
})
chrome.tabs.onRemoved.addListener(function(tabId, removeInfo) {
if(finish === false && coord && removeInfo.isWindowClosing === false){
coord.onRemove(tabId);
}
})
chrome.runtime.onMessage.addListener(function(msg, sender, callback){
var p = msg.params;
//scrape
if(msg.cmd == 'START_SCRAPE'){
sitemap = p.sitemap;
if(sitemap){
finish = false;
coord = new Coordinator(sitemap, 0);
var page = coord.sitemap.pages[0];
coord.openTab(page.id, page.link);
}
}
else if(msg.cmd == 'TEST_SCRAPE'){
sitemap = p.sitemap;
if(sitemap){
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
var tab = tabs[0];
sitemap.pages[0].link = tab.url;
finish = false;
coord = new Coordinator(sitemap, 20);
var page = coord.sitemap.pages[0];
coord.openTab(page.id, page.link);
})
}
}
else if(msg.cmd == 'SCRAPE_READY'){
print(sender);
coord.readyForScrape(sender.tab.id);
}
else if(msg.cmd == 'TAB_CLICK'){
coord.currentPage = sitemap.getPage(p.page);
}
//define
else if(msg.cmd == 'START_SELECT'){
if(msg.tabId){
chrome.tabs.sendMessage(msg.tabId, msg);
}else{
sendMessage(msg);
}
}
else if(msg.cmd == 'END_SELECT'){
if(devtoolPort){
devtoolPort.postMessage(msg);
}
sendMessage(msg);
}
else if(msg.cmd == 'SITEMAP_DEFINE'){
alert("Start to render");
var url = p && p.url;
sitemap = p && p.sitemap;
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
var tab = tabs[0];
if(url && url != tab.url){
chrome.tabs.create({url: url}, function(newTab){
doInjectScript(newTab.id);
})
}else{
injectScript(tab.id);
}
});
}
else if(msg.cmd == 'SITEMAP_DEFINE_END'){
var _sm = p.sitemap;
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
var tab = tabs[0];
_sm.pages[0].link = tab.url;
saveSitemap(_sm);
})
}
else if(msg.cmd == 'SITEMAP_DELETE'){
deleteSitemap(p.sitemap);
}
else if(msg.cmd == 'DEFINE_PAGE_LOADED'){
callback(sitemap);
}
else{
sendMessage(msg);
}
});
var print = function(msg){
if(curTabId){
chrome.tabs.sendMessage(curTabId, {
cmd: 'LOG',
params: msg
});
}else{
sendMessage(msg)
}
}
function saveSitemap(_sm){
var id = _sm.id;
chrome.storage.local.get("sitemaps", function(db){
var _sitemaps = db["sitemaps"];
if(!_sitemaps || !_sitemaps.length){
_sitemaps = [_sm];
}else{
var _idx = _sitemaps.findIndex(function(s){return s.id == id});
if(_idx == -1){
_sitemaps.push(_sm);
}else{
_sitemaps.splice(_idx, 1, _sm);
}
}
var _db = {'sitemaps':_sitemaps}
chrome.storage.local.set(_db, function(){
alert(chrome.i18n.getMessage('saved'));
});
})
}
function deleteSitemap(id){
chrome.storage.local.get("sitemaps", function(db){
var _sitemaps = db["sitemaps"];
var index = _sitemaps.findIndex(function(s){return s.id == id})
_sitemaps.splice(index, 1);
var _db = {'sitemaps':_sitemaps}
chrome.storage.local.set(_db, function(){
alert(chrome.i18n.getMessage('deleted'));
});
})
}
function injectScript(tabId){
var flag;
chrome.tabs.sendMessage(tabId, {
cmd: 'SELF_CHECK'
}, function(r){
flag = r;
});
setTimeout(function(){
if(flag){
chrome.tabs.executeScript(tabId, {file: "injected/define/initUI.js"}, function() {});
}else{
doInjectScript(tabId);
}
}, 200)
}
//Injected scripts will be removed once the tab reload again.
function doInjectScript(tabId){
chrome.tabs.executeScript(tabId, {file: "injected/listener.js"});
chrome.tabs.executeScript(tabId, {file: "lib/jquery/jquery-2.1.1.min.js"}, function() {
chrome.tabs.executeScript(tabId, {file: "lib/model.js"});
chrome.tabs.executeScript(tabId, {file: "lib/css-selector-generator.js"}, function() {});
chrome.tabs.executeScript(tabId, {file: "injected/SelectTool.js"}, function() {});
chrome.tabs.executeScript(tabId, {file: "injected/define/initUI.js"}, function() {});
chrome.tabs.insertCSS(tabId, {file: "injected/injected.css", runAt: "document_end"});
});
}
function createResultTab(title, fields, data){
chrome.tabs.create({url: chrome.extension.getURL('result/table.html')}, function(newTab){
var tabId = newTab.id;
setTimeout(function(){
chrome.tabs.sendMessage(tabId, {
cmd: 'RESULT',
params:{
data: data,
title: title,
fields: fields
}
});
}, 200)
})
}
Thanks in advance for your help.
I find the answer in official document.
If we set persistent = false, the background js will be unload when it goes idle.
{
"name": "My extension",
...
"background": {
"scripts": ["eventPage.js"],
"persistent": false
},
...
}
The document tells sending message from content script will cause background js reload. But I guess message from popup script has issue.
So either change persistent true or chrome.runtime.getBackgroundPage in popup to cause background js reload will solve this issue.

Sharepoint Listview Javascript turn row color

I'm new to SharePoint and have almost never used JavaScript. I have written a short script just to test and it should turn each row yellow (Under Edit web parts I gave the Listview the location of the script.
When I look in the debugger it seems to see the script but nothing happens. Eventually I want it to change the color based on a value in the List view. But for the moment I just want to get something working.
(function () {
var overrideCtx = {};
overrideCtx.Templates = {};
overrideCtx.Templates.fields = {'Contractdaysleft': { 'View' : HighlightRowOverride }};
SPClientTemplates.TemplateManager.RegisterTemplateOverrides(overrideCtx);
})();
function HighlightRowOverride(inCtx) {
for (var i = 0; i < inCtx.ListData.Row.length; ++i) {
var listItem = inCtx.ListData.Row[i];
var idd = GenerateIIDForListItem(inCtx, listItem);
var row = document.getElementId(idd);
row.style.backgroundColor = "yellow";
}
}
There is a typo in the template declaration, you need change overrideCtx.Templates.fields to overrideCtx.Templates.Fields
The template skeleton is provided below:
SPClientTemplates.TemplateManager.RegisterTemplateOverrides({
// OnPreRender: function(ctx) { },
Templates: {
// View: function(ctx) { return ""; },
// Header: function(ctx) { return ""; },
// Body: function(ctx) { return ""; },
// Group: function(ctx) { return ""; },
// Item: function(ctx) { return ""; },
// Fields: {
// "<field internal name>": {
// View: function(ctx) { return ""; },
// EditForm: function(ctx) { return ""; },
// DisplayForm: function(ctx) { return ""; },
// NewForm: function(ctx) { return ""; }
// }
// },
// Footer: function(ctx) { return ""; }
},
// OnPostRender: function(ctx) { },
});
Form another hand, the syntax:
Templates: {
// Fields: {
// "<field internal name>": {
// View: function(ctx) { return ""; }
// }
// }
}
is commonly used for customizing the display of specific field in list view.
For highlighting a list view row, i would suggest a slightly different approach (via OnPostRender event) as demonstrated below
Template example to highlight row in list view
SP.SOD.executeFunc("clienttemplates.js", "SPClientTemplates", function() {
SPClientTemplates.TemplateManager.RegisterTemplateOverrides({
OnPostRender: function(ctx) {
var rows = ctx.ListData.Row;
for (var i=0;i<rows.length;i++)
{
var rowId = GenerateIIDForListItem(ctx, rows[i]);
var row = document.getElementById(rowId);
row.style.backgroundColor = "yellow";
}
}
});
});
Result

Phantomjs / Node.js - Refactoring block of code

I am writing a testing application, for the company where I work, using Node.js and Phantomjs. Right now, the relevant part of my code is something like this:
phantom.create(function (ph) {
ph.createPage(function (page) {
page.set('viewportSize', { width: 1920, height: 1080 });
page.set('settings.javascriptEnabled', true);
page.set('settings.loadImages', true);
page.set('settings.localToRemoteUrlAccess', true);
page.set('settings.userAgent', userAgentStrings[randomInt(0, 5)]);
page.set('settings.webSecurityEnabled', false);
page.set('settings.resourceTimeout', 5000);
page.open(URL, function (status) {
if (status == 'success') {
page.evaluate(function (result) {
return document.title;
}, function (result) {
setTimeout(function () {
log.info('Status: ', status);
ph.exit();
}, 60 * 1000);
});
} else if (status == 'fail') {
log.error('Status: ', status);
ph.exit();
}
});
});
});
My question is this: Is there a way to refactor my code in such a way that I can call the "page.open(..." function from outside the "phantom.create(..." block?
I plan to implement node-cron and have one block of code where I set up all the options for the page and another one that I will actually use to open the page. In the end, the opening part will be handled by node-cron, repeating ad infinitum.
Here is a quick example on how to do this. You just need to store your phantom object somewhere and reuse it. Be aware that I made this simple so that you can reuse the concept but that you would need more error handling.
var jobRunner = function() {
// The phantom object will be stored here
this.ph;
// The page object will be stored here
this.page;
};
jobRunner.prototype.start = function(readyCallback) {
var self = this;
phantom.create(function (ph) {
self.ph = ph;
self.ph.createPage(function (page) {
page.set('viewportSize', { width: 1920, height: 1080 });
page.set('settings.javascriptEnabled', true);
page.set('settings.loadImages', true);
page.set('settings.localToRemoteUrlAccess', true);
page.set('settings.userAgent', userAgentStrings[randomInt(0, 5)]);
page.set('settings.webSecurityEnabled', false);
page.set('settings.resourceTimeout', 5000);
self.page = page;
readyCallback();
});
});
};
jobRunner.prototype.doUrl = function(url) {
var self = this;
this.page.open(URL, function (status) {
if (status == 'success') {
page.evaluate(function (result) {
return document.title;
}, function (result) {
setTimeout(function () {
log.info('Status: ', status);
self.ph.exit();
}, 60 * 1000);
});
} else if (status == 'fail') {
log.error('Status: ', status);
self.ph.exit();
}
});
}
var CronJob = require('cron').CronJob;
var phantomJob = new jobRunner();
// Wait for Phantom to be ready then start the Cron Job.
phantomJob.start(function() {
var cron = new CronJob('*/5 * * * * *', function() {
phantomJob.doUrl("http://yoururl.com");
});
});

How to close a notification window automatically

Hi I am displaying a notification based on the output of a server file.The output of the server file is being checked for a period of 5 min interval.Now what I need is that when the output changes at any instant, notification should be automatically be closed at that moment.What I am trying to convey that if the output of the server file is 0,the notification is being displayed in every 5 min. If the output is 1, then notification will not be shown anymore.The problem of my code is that unless I close the notification manually it won't be closed automatically even if the output is 1. Anyone please help me to close my notification automatically.
here is my background.js
var myNotificationID = null;
var oldChromeVersion = !chrome.runtime;
function getGmailUrl() {
return "http://calpinemate.com/";
}
function isGmailUrl(url) {
return url.indexOf(getGmailUrl()) == 0;
}
chrome.browserAction.onClicked.addListener(function(tab) {
if(!localStorage.username){
chrome.windows.create({url : "userinfo.html",type: "popup", height: 200, width:300 , top :400 , left : 800});
}
else{
chrome.tabs.query({
url: "http://calpinemate.com/*",
currentWindow: true
},
function(tabs) {
if (tabs.length > 0) {
var tab = tabs[0];
console.log("Found (at least one) Gmail tab: " + tab.url);
console.log("Focusing and refreshing count...");
chrome.tabs.update(tab.id, { active: true });
updateIcon();
}
else {
console.log("Could not find Gmail tab. Creating one...");
chrome.tabs.create({ url: getGmailUrl() });
updateIcon();
}
});
}
});
function onInit() {
console.log('onInit');
updateIcon();
if (!oldChromeVersion) {
chrome.alarms.create('watchdog', {periodInMinutes:5});
}
}
function onAlarm(alarm) {
console.log('Got alarm', alarm);
if (alarm && alarm.name == 'watchdog') {
onWatchdog();
}
else {
updateIcon();
}
}
function onWatchdog() {
chrome.alarms.get('refresh', function(alarm) {
if (alarm) {
console.log('Refresh alarm exists. Yay.');
}
else {
console.log('Refresh alarm doesn\'t exist!? ' +
'Refreshing now and rescheduling.');
updateIcon();
}
});
}
if (oldChromeVersion) {
updateIcon();
onInit();
}
else {
chrome.runtime.onInstalled.addListener(onInit);
chrome.alarms.onAlarm.addListener(onAlarm);
}
function updateIcon(){
var urlPrefix = 'http://www.calpinemate.com/employees/attendanceStatus/';
var urlSuffix = '/2';
var req = new XMLHttpRequest();
req.addEventListener("readystatechange", function() {
if (req.readyState == 4) {
if (req.status == 200) {
var item=req.responseText;
if(item==1){
chrome.browserAction.setIcon({path:"calpine_logged_in.png"});
chrome.browserAction.setBadgeBackgroundColor({color:[190, 190, 190, 230]});
chrome.browserAction.setBadgeText({text:""});
chrome.notifications.clear(id1);//this is not working
}
else{
chrome.browserAction.setIcon({path:"calpine_not_logged_in.png"});
chrome.browserAction.setBadgeBackgroundColor({color:[190, 190, 190, 230]});
chrome.browserAction.setBadgeText({text:""});
chrome.notifications.create(
'id1',{
type: 'basic',
iconUrl: '/calpine_not_logged_in.png',
title: 'Warning : Attendance',
message: 'Please mark your Attendance !',
buttons: [{ title: 'Mark',
iconUrl: '/tick.jpg'
},{ title: 'Ignore',
iconUrl: '/cross.jpg'}],
priority: 0},
function(id) { myNotificationID = id;}
);
chrome.notifications.onButtonClicked.addListener(function(notifId, btnIdx) {
if (notifId === myNotificationID) {
if (btnIdx === 0) {
window.open("http://www.calpinemate.com/");
} else if (btnIdx === 1) {
notification.close();
}
}
});
chrome.notifications.onClosed.addListener(function() {
notification.close();
});
}
}
else {
// Handle the error
alert("ERROR: status code " + req.status);
}
}
});
var url = urlPrefix + encodeURIComponent(localStorage.username) + urlSuffix;
req.open("GET", url);
req.send(null);
}
Two possible problems:
You pass an undefined variable named id1 in chrome.notifications.clear(), when you actually mean the string 'id1'.
According to the docs on chrome.notifications.clear() method, the second argument (callback function) is not optional, yet you fail to supply one.
One possible solution:
// Replace that line:
chrome.notifications.clear(id1);//this is not working
// With this line:
chrome.notifications.clear('id1', function(){});//this is working perfectly
// Or with this line:
chrome.notifications.clear(myNotificationID, function(){});//this is working perfectly
BTW, you don't need to provide a notification ID yourself.
You can replace: chrome.notifications.create('id1',...
with: chrome.notifications.create('',...
(and then, of course, use: chrome.notifications.clear(myNotificationID,...)

Resources