I am working on a function for a Chrome extension, and I need some help debugging.
The function should get a user's String input from a popup.html text box, and look through all the tabs in the current window to see if the String input is present in any of the tabs. Finally, I give an alert telling the user which tab(s) (indices: 0, 1, ...) the string was found in.
background.js
//searches all tabs for the input string
function searchEverywhere(searchString) {
var indices = [];
chrome.tabs.query({}, function(tabs) {
var numOfTabs = tabs.length;
for(i = 0; i < numOfTabs; i++) {
chrome.tabs.update(tabs[i].id, updateProperties, function(tab) {
if(getText().indexOf(searchString) > -1) {
indices.push(i);
}
});
}
});
return indices;
}
function makeAlert(pagesFound) {
alert("Your input was found on tabs: " + pagesFound.toString());
}
function getText() {
return document.body.innerText;
}
popup_script.js
document.addEventListener("DOMContentLoaded", function() {
var backgroundPage = chrome.extension.getBackgroundPage();
document.querySelector('#btnSearch').addEventListener('click', function() {
var searchString = document.querySelector('#textToSearch');
var pagesFound = backgroundPage.searchEverywhere(String(searchString.value));
backgroundPage.makeAlert(pagesFound);
});
});
popup.html
<!DOCTYPE html>
<html>
<head>
<title>Work with tabs</title>
<script src="popup_script.js"></script>
</head>
<body><div class="searchEverywhere">
Search all tabs for word:<br>
<input type="text" name="textToSearch" id="textToSearch">
<br>
<input type="button" id="btnSearch" value="Search">
</div>
</body>
</html>
Using chrome.tabs.executeScript we can retrieve the innerText of the tab.
background.js
function searchEverywhere(searchPhrase, callback) {
chrome.tabs.query({
//query only tabs that you have enabled permissions for or you will get errors in console
}, tabs => {
let foundInTabIds = [];
for (let tab of tabs) {
chrome.tabs.executeScript(tab.id, {
code: `document.body.innerText`
}, (results => {
if (results && results.length > 0) {
let tabText = results[0];
if(tabText.indexOf(searchPhrase) > -1) {
foundInTabIds.push(tab.id);
}
}
}));
}
setTimeout(() => {
//your alert
callback(foundInTabIds);
}, 2000);
});
}
popup.js
...
var pagesFound = backgroundPage
.searchEverywhere(String(searchString.value), backgroundPage.makeAlert);
...
For simplicity, I use setTimeout, but that's not an optimal way to wait for all callbacks to complete. I suggest using promises.
Related
for my MIT-licensed (totally free) CMS and web-apps platform https://github.com/nicerapp/nicerapp, i am using a photo uploader component called plupload.com, which is HTML5 based, and which can handle fairly large uploads just fine out of the box,
but not very large uploads (8887 files in 169 folders spanning 20.4GB in my test case).
i wondered why the files in very large uploads ended up in the wrong folders (higher level folders), for instance.
i eventually found the answer, and will share that with you here on stackoverflow.com because the plupload.com forums are closed to new registrations and i was pointed here by the people running those forums, telling me to tag such a post with the plupload tag.
Feel free to copy all the code you need for your own web apps and CMSes to make plupload.com actually work, from the following links and sample code, even for use in commercial and/or mixed copyright environments :
(c) humanity, 2021. MIT-licensed.
Please note that plupload.com-3.1.3, on Ubuntu-Linux=>Chrome is not stable enough yet to facilitate 20GB photo collection uploads. You'll need the 2.3.7 version, which is advertised by the people of plupload.com as being more stable.
https://github.com/nicerapp/nicerapp/blob/58f6e58e5ff1acd638c76edc68bac933f056bb34/nicerapp/apps/nicer.app/cms/na.blog.source.js#L120 the glue code that calls up these next two worker files :
https://github.com/nicerapp/nicerapp/blob/58f6e58e5ff1acd638c76edc68bac933f056bb34/nicerapp/userInterface/photoAlbum/4.0.0/upload.2.3.7.php#L182 uses the linux imagemagick 'convert' OS app / library to generate the thumbnails
https://github.com/nicerapp/nicerapp/blob/58f6e58e5ff1acd638c76edc68bac933f056bb34/nicerapp/userInterface/photoAlbum/4.0.0/jquery_ui_widget.2.3.7.php#L250 the javascript end, with the proper way to get all the correct relativePath settings for each of the files being uploaded. The old code listed at the plupload site was buggy because it didn't account for the async nature of .createReader().readEntries() browser function.
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" dir="ltr">
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8"/>
<title>Plupload - jQuery UI Widget</title>
<link rel="stylesheet" href="//ajax.googleapis.com/ajax/libs/jqueryui/1.8.9/themes/base/jquery-ui.css" type="text/css" />
<link rel="stylesheet" href="/nicerapp/userInterface/photoAlbum/4.0.0/jquery.ui.plupload.2.3.7.css" type="text/css" />
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js"></script>
<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jqueryui/1.10.2/jquery-ui.min.js"></script>
<!-- production -->
<!--<script type="text/javascript" src="/nicerapp/3rd-party/plupload-2.3.7/js/plupload.full.min.js"></script>
<script type="text/javascript" src="/nicerapp/3rd-party/plupload-2.3.7/js/jquery.ui.plupload/jquery.ui.plupload.js"></script>-->
<!-- debug -->
<script type="text/javascript" src="/nicerapp/3rd-party/plupload-2.3.7/js/moxie.js"></script>
<script type="text/javascript" src="/nicerapp/3rd-party/plupload-2.3.7/js/plupload.dev.js"></script>
<script type="text/javascript" src="/nicerapp/3rd-party/plupload-2.3.7/js/jquery.ui.plupload/jquery.ui.plupload.js"></script>
<!-- -->
</head>
<body style="font: 13px Verdana; background: #eee; color: #333">
<h1>jQuery UI Widget</h1>
<p>You can see this example with different themes on the www.plupload.com website.</p>
<form id="form" method="post" action="../dump.php">
<div id="uploader">
<p>Your browser doesn't have Flash, Silverlight or HTML5 support.</p>
</div>
<br />
<input type="submit" value="Submit" />
</form>
<script type="text/javascript">
// Initialize the widget when the DOM is ready
$(function() {
$("#uploader").plupload({
// General settings
runtimes : 'html5,flash,silverlight,html4',
url : './upload.2.3.7.php?basePath=<?php echo $_GET['basePath']?>',
// User can upload no more then 20 files in one go (sets multiple_queues to false)
max_file_count: 100 * 1000,
chunk_size: '1mb',
// Resize images on clientside if we can
/*
resize : {
width : 200,
height : 200,
quality : 90,
crop: true // crop to exact dimensions
},*/
filters : {
// Maximum file size
max_file_size : '1000mb',
// Specify what files to browse for
mime_types: [
{title : "Image files", extensions : "jpg,jpeg,gif,png"},
{title : "Zip files", extensions : "zip"}
]
},
// Rename files by clicking on their titles
rename: true,
// Sort files
sortable: true,
// Enable ability to drag'n'drop files onto the widget (currently only HTML5 supports that)
dragdrop: true,
// Views to activate
views: {
list: true,
thumbs: true, // Show thumbs
active: 'thumbs'
},
// Flash settings
flash_swf_url : '/nicerapp/userInterface/photoAlbum/4.0.0/Moxie.2.3.7.swf',
// Silverlight settings
silverlight_xap_url : '/nicerapp/userInterface/photoAlbum/4.0.0/Moxie.2.3.7.xap',
init: {
PostInit: function() {
/*document.getElementById('uploader').onclick = function() {
uploader.start();
return false;
};*/
},
BeforeUpload: function (up, file) {
// send relativePath along
if(map[file.name] !== undefined) {
var mapName = map[file.name];
window.top.na.m.log (555, {file:file,relativePath:mapName});
file.relativePath = mapName;
up.setOption('multipart_params', {
relativePath: mapName
});
var rp = mapName.substr(1);
debugger;
//if (rp && rp!=='') rp = rp.replace(/[^\/]*$/,'');
//if (rp && rp!=='') rp = rp.replace(/\/$/,'');
if (rp && rp!=='') {
window.top.na.blog.currentNode_createPath (rp);
} else debugger;
} else debugger;
},
UploadComplete : function (up, files) {
window.top.na.blog.mediaUploadComplete (up, files);
}
}
});
// Handle the case when form was submitted before uploading has finished
$('#form').submit(function(e) {
// Files in queue upload them first
if ($('#uploader').plupload('getFiles').length > 0) {
// When all files are uploaded submit form
$('#uploader').on('complete', function() {
$('#form')[0].submit();
});
$('#uploader').plupload('start');
} else {
alert("You must have at least one file in the queue.");
}
return false; // Keep the form from submitting
});
});
let map = {};
// all relative paths are built here
// this code is old code found at the plupload site, and does not work because .readEntries() is an async function.
// working code is included below this commented-out code..
/*
traverseFileTree = function (item, path) {
var dirReader = null;
path = path || '';
//debugger;
if (item.isFile) {
let filename = item.name;
item.file(function(file) {
// careful here, could be several files of the same name
// we assume files will be in the same order here than in plupload
if(map[filename] === undefined) {
map[filename] = [];
}
map[filename].push(path);
console.log ('FILE : '+path);
});
} else if (item.isDirectory) {
dirReader = item.createReader();
dirReader.readEntries(function (entries) {
var n = 0;
for (n = 0; n < entries.length; n++) {
console.log ('DIR : '+path+item.name+'/');
traverseFileTree(entries[n], path + item.name + "/");
}
});
}
};
// bind another handler to the drop event to build an object representing the folder structure
document.getElementById('uploader').addEventListener('drop', function(e) {
var items = e.dataTransfer.items, n, item;
for(n = 0; n < items.length; n++) {
item = items[n].webkitGetAsEntry();
if(item) {
traverseFileTree(item);
} else debugger;
}
debugger;
}, false);
*/
// many thanks to http://120.27.46.75:81/webhtml/.svn/pristine/5a/5aa3ae5cf228450c853910d093eea41ea2aa6741.svn-base
"use strict";"object"!=typeof window.CP&&(window.CP={}),window.CP.PenTimer={programNoLongerBeingMonitored:!1,timeOfFirstCallToShouldStopLoop:0,_loopExits:{},_loopTimers:{},START_MONITORING_AFTER:2e3,STOP_ALL_MONITORING_TIMEOUT:5e3,MAX_TIME_IN_LOOP_WO_EXIT:2200,exitedLoop:function(o){this._loopExits[o]=!0},shouldStopLoop:function(o){if(this.programKilledSoStopMonitoring)return!0;if(this.programNoLongerBeingMonitored)return!1;if(this._loopExits[o])return!1;var t=this._getTime();if(0===this.timeOfFirstCallToShouldStopLoop)return this.timeOfFirstCallToShouldStopLoop=t,!1;var i=t-this.timeOfFirstCallToShouldStopLoop;if(i<this.START_MONITORING_AFTER)return!1;if(i>this.STOP_ALL_MONITORING_TIMEOUT)return this.programNoLongerBeingMonitored=!0,!1;try{this._checkOnInfiniteLoop(o,t)}catch(n){return this._sendErrorMessageToEditor(),this.programKilledSoStopMonitoring=!0,!0}return!1},_sendErrorMessageToEditor:function(){try{if(this._shouldPostMessage()){var o={action:"infinite-loop",line:this._findAroundLineNumber()};parent.postMessage(JSON.stringify(o),"*")}else this._throwAnErrorToStopPen()}catch(t){this._throwAnErrorToStopPen()}},_shouldPostMessage:function(){return document.location.href.match(/boomerang/)},_throwAnErrorToStopPen:function(){throw"We found an infinite loop in your Pen. We've stopped the Pen from running. Please correct it or contact support#codepen.io."},_findAroundLineNumber:function(){var o=new Error,t=0;if(o.stack){var i=o.stack.match(/boomerang\S+:(\d+):\d+/);i&&(t=i[1])}return t},_checkOnInfiniteLoop:function(o,t){if(!this._loopTimers[o])return this._loopTimers[o]=t,!1;var i=t-this._loopTimers[o];if(i>this.MAX_TIME_IN_LOOP_WO_EXIT)throw"Infinite Loop found on loop: "+o},_getTime:function(){return+new Date}},window.CP.shouldStopExecution=function(o){return window.CP.PenTimer.shouldStopLoop(o)},window.CP.exitedLoop=function(o){window.CP.PenTimer.exitedLoop(o)};
// Drop handler function to get all files
// many thanks to https://codepen.io/anon/pen/gBJrOP
async function getAllFileEntries(dataTransferItemList) {
let fileEntries = [];
// Use BFS to traverse entire directory/file structure
let queue = [];
// Unfortunately dataTransferItemList is not iterable i.e. no forEach
for (let i = 0; i < dataTransferItemList.length; i++) {if (window.CP.shouldStopExecution(0)) break;
queue.push(dataTransferItemList[i].webkitGetAsEntry());
}window.CP.exitedLoop(0);
while (queue.length > 0) {if (window.CP.shouldStopExecution(1)) break;
let entry = queue.shift();
if (entry.isFile) {
fileEntries.push(entry);
} else if (entry.isDirectory) {
let reader = entry.createReader();
queue.push(...(await readAllDirectoryEntries(reader)));
}
}window.CP.exitedLoop(1);
return fileEntries;
}
// Get all the entries (files or sub-directories) in a directory by calling readEntries until it returns empty array
async function readAllDirectoryEntries(directoryReader) {
let entries = [];
let readEntries = await readEntriesPromise(directoryReader);
while (readEntries.length > 0) {if (window.CP.shouldStopExecution(2)) break;
entries.push(...readEntries);
readEntries = await readEntriesPromise(directoryReader);
}window.CP.exitedLoop(2);
return entries;
}
// Wrap readEntries in a promise to make working with readEntries easier
async function readEntriesPromise(directoryReader) {
try {
return await new Promise((resolve, reject) => {
directoryReader.readEntries(resolve, reject);
});
} catch (err) {
console.log(err);
}
}
var elDrop = document.getElementById('uploader');
/*
we don't need this:
var elItems = document.getElementById('items');
elDrop.addEventListener('dragover', function (event) {
event.preventDefault();
elItems.innerHTML = 0;
});*/
elDrop.addEventListener('drop', async function (event) {
event.preventDefault();
let items = await getAllFileEntries(event.dataTransfer.items);
for (var i=0; i<items.length; i++) {
var mapName = items[i].fullPath.replace('/'+items[i].name, '');
map[items[i].name] = mapName; // the only problem remaining is having files with the same name put into multiple sub-folders that you're uploading in the same batch (something to fix another time)
}
});
</script>
</body>
</html>
I am getting some errors with this code. Apparently split is not a function. I am also getting this error:
GET http://localhost:8000/favicon.ico 404 (File not found)
Basically what I am trying to do is convert a csv to a 2D array through which I loop and do some analysis. I'm not great with javascript so please don't crucify me lol. But please feel free to point out any other issues I really need this to work!
<!DOCTYPE html>
<html>
<head>
<script src = "https://d3js.org/d3.v4.min.js"></script>
</head>
<body>
<script>
d3.csv('enrollment.csv', function(data) {
function csvToArray(csv) {
rows = csv.split("\n");
return rows.map(function (row) {
return row.split(",");
});
};
let count = 0;
let enroll = csvToArray(data);
console.log(enroll);
let dup_Tracker = [];
for (i = 1; i < enroll.length; i++) {
if (enroll[i][1] === enroll[i-1][1]) {
if (enroll[i][0] !== enroll[i-1][0]) {
dup_Tracker.push(enroll[i]);
}
} else {
count += 1;
}
}
});
// console.log(dup_Tracker);
</script>
</body>
</html>
I made a chrome extension where my popup button calls a script. The other script uses jQuery but I get an error saying jQuery is not defined.
My popup.html:
<!DOCTYPE html>
<html>
<head>
<title>HomAttendance</title>
</head>
<body>
<h1 style="color:#E54E4E">Hom<span style="color:#4E97E5">Attendance</span></h1>
<button type id="record" style="background-color:White"><h1 style="color:Black">Record Attendance</h1></button>
</body>
<script src="jquery-3.4.1.min.js"></script>
<script src="popup.js"></script>
</html>
My popup.js:
document.addEventListener('DOMContentLoaded', function() {
var login = document.getElementById('record');
login.addEventListener('click', function() {
chrome.tabs.executeScript({file: 'markStudents.js'});
});
});
myScript.js:
var arrays = []
$.get('Attendance.txt', function(data){
var splitted = data.split("\n"); // --> should return an array for each line
// Loop through all lines
for(var i = 0; i < splitted.length; i++)
{
var line = splitted[i];
// Now you could remove [ and ] from string
var removed = line.replace('[','').replace(']','');
var refined = removed.replace(' ', '');
// Now you can split all values by using , delimiter
var values = refined.split(',');
var array = [];
// Now you can iterate through all values and add them to your array
for(var c = 0; c < values.length; c++)
{
var value = values[c];
array.push(value);
}
arrays.push(array);
}
});
var present = arrays[0];
console.log(present);
var absent = arrays[1];
console.log(absent);
var user = present[0];
var pass = absent[0];
var loginField = document.getElementById('fieldAccount');
var passwordField = document.getElementById('fieldPassword');
loginField.value = user;
passwordField.value = pass;
var loginForm = document.getElementById('btn-enter-sign-in');
Is there any way to include my jquery.js in myScript.js?
Console Error
Just import jquery before you import popup.js
Like this
<!DOCTYPE html>
<html>
<head>
<title>HomAttendance</title>
</head>
<body>
<h1 style="color:#E54E4E">Hom<span style="color:#4E97E5">Attendance</span></h1>
<button type id="record" style="background-color:White"><h1 style="color:Black">Record Attendance</h1></button>
</body>
<script src="jquery-3.4.1.min.js"></script>
<script src="popup.js"></script>
</html>
Inside Your popup.js, when you load markStudents.js which uses jQuery, you'd again have to load jQuery before same
Like this
document.addEventListener('DOMContentLoaded', function () {
var login = document.getElementById('record');
login.addEventListener('click', function () {
chrome.tabs.executeScript(null, { file: "jquery-3.4.1.min.js" }, function () {
chrome.tabs.executeScript(null, { file: "markStudents.js" });
});
});
});
Just reorder your script tags and put jQuery before your popup.js. That way it will be loaded when you try to call it.
yo can use this code to include another jquery file in your jquery:
$.getScript("file address");
like this:
$.getScript("/assets/pages/scripts/ui-blockui.min.js");
I have following code for checking whether there is duplicate in an array. The code works fine. But it uses a new array named newUniqueArray. Is there a better code for this purpose without using a new array? Is there any optimization possible on this code?
Note: I have used inArray and in keywords from jQuery
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<script type="text/javascript" src="http://ajax.aspnetcdn.com/ajax/jquery/jquery-1.4.1.js"></script>
<script type="text/javascript">
$(document).ready(function () {
$('#btnSave').click(function (e) {
var reportRecipients = "A, a , b,";
reportRecipients = reportRecipients.toLowerCase();
checkDuplicate(reportRecipients);
});
function checkDuplicate(reportRecipients) {
if (reportRecipients.length > 1) {
var recipientsArray = reportRecipients.split(',');
var newUniqueArray = [];
for (a in recipientsArray) {
var email = $.trim(recipientsArray[a]);
if ($.inArray(email, newUniqueArray) == -1) {
newUniqueArray.push(email);
}
}
if (newUniqueArray.length < recipientsArray.length) {
alert('Duplicate Exists');
}
return false;
}
}
});
</script>
</head>
<body>
<input name="txtName" type="text" id="txtName" />
<input type="submit" name="btnSave" value="Save" id="btnSave" />
</body>
</html>
If you just want to test on string arrays, you can use a JavaScript object's property to test. It used a hash-table to look up properties, which is faster than array iteration.
example: http://jsfiddle.net/jmDEZ/8/
function checkDuplicate(reportRecipients) {
var recipientsArray = reportRecipients.split(','),
textHash = {};
for(var i=0; i<recipientsArray.length;i++){
var key = $.trim(recipientsArray[i].toLowerCase());
console.log("lower:" + key);
if(textHash[key]){
alert("duplicated:" + key);
return true;
}else{
textHash[key] = true;
}
}
alert("no duplicate");
return false;
}
I can't see any reason to use jQuery for this purpose:
checkDuplicate = function (reportRecipients) {
if (reportRecipients.length > 1) {
var recipientsArray = reportRecipients.split(',');
for (a in recipientsArray) {
if(reportRecipients.indexOf(a) != reportRecipients.lastIndexOf(a)){
return true;
}
}
}
return false;
}
$('#btnSave').click(function (e) {
var reportRecipients = "A, a , b,";
reportRecipients = reportRecipients.toLowerCase();
if(checkDuplicate(reportRecipients)) alert('Duplicate Exists');
});
I've reduced my code to the following short example. In it, I want to query for a set of iterations, and then in the callback, loop over the iterations and sum the resources. There is a global variable in which I'd like to store the sum... but I can't get this to work.
The specific problem is that the query (and associated callback) are run after the other processing.
<html><!-- COMMENT -->
<meta name="Name" content="YOUR APP NAME HERE" />
<meta name="Version" content="0.1" />
<meta name="Vendor" content="YOUR COMPANY NAME HERE" />
<!-- Rally SDK --> <script type="text/javascript" src="/apps/1.25/sdk.js"></script>
<!-- App script --> <script>
var rallyDataSource; var resourceSum = -1;
function ProcessIterations(results) {
alert("In ProcessIterations");
var resourceSum = 0;
for (iIter = 0; iIter < results.iterations.length; iIter++) {
var iteration = results.iterations[iIter] ;
resourceSum += iteration.Resources;
}
alert("In ProcessIterations, resourceSum="+resourceSum); }
function queryError () {
alert("A query error occurred"); }
function runMainQuery() { var today = dojo.date.stamp.toISOString(new Date(), {milliseconds: true, zulu: true});
var queryObject = {
key: "iterations",
type: "Iteration",
fetch: "Name,ObjectID,Resources,Project",
order: "EndDate asc",
query: "(Project.ObjectID != \"__PROJECT_OID__\")"
};
rallyDataSource.findAll(queryObject, ProcessIterations, queryError); }
function Main() { rallyDataSource = new rally.sdk.data.RallyDataSource("__WORKSPACE_OID__", "__PROJECT_OID__",
"__PROJECT_SCOPING_UP__", "__PROJECT_SCOPING_DOWN__");
runMainQuery() ;
var tableConfig = {
'columnKeys' : ['planEst'],
'columnHeaders': ['Plan Estimate'] }; var table = new rally.sdk.ui.Table(tableConfig); table.setCell(0,0,resourceSum) ; table.display("app_div");
}
rally.addOnLoad(Main);
</script> <body>
<div id="app_div"></div>
<div id="error_div"></div> </body> </html>
It can be a little tricky to get the hang of asynchronous callbacks but you're really close.
Basically if you just move the creation of your table from Main to up in your ProcessIterations callback you should be good:
function ProcessIterations(results) {
alert("In ProcessIterations");
var resourceSum = 0;
for (iIter = 0; iIter < results.iterations.length; iIter++) {
var iteration = results.iterations[iIter] ;
resourceSum += iteration.Resources;
}
alert("In ProcessIterations, resourceSum="+resourceSum); }
var tableConfig = {
'columnKeys' : ['planEst'],
'columnHeaders': ['Plan Estimate'] };
var table = new rally.sdk.ui.Table(tableConfig);
table.setCell(0,0,resourceSum) ;
table.display("app_div");
}
function Main() {
rallyDataSource = new rally.sdk.data.RallyDataSource("__WORKSPACE_OID__", "__PROJECT_OID__",
"__PROJECT_SCOPING_UP__", "__PROJECT_SCOPING_DOWN__");
runMainQuery() ;
}
That way you won't display your table until the data is available from your rallyDataSource.findAll call and your resourceSum has been calculated.
As an additional resource checkout some of the examples in our help documentation on using RallyDataSource and async callbacks:
http://developer.rallydev.com/help/rally-data-source