Detecting tab closed (after closed) from Firefox extension - javascript

I'm trying to get my Firefox extension to create a url list from all tabs in the browser. To keep the list updated I need to know when a tab has been closed.
I've tried using:
window.addEventListener("TabClose", tabRemoved, false);
However, this gets called BEFORE the tab is actually closed, which results in my updated tablist still containing the closed tabs url.
I update the tab list by iterating all browsers, like so:
function ()
{
gBrowser = window.getBrowser();
tabs = gBrowser.browsers;
urls = [];
for (var i = 0; i < tabs.length; i++)
{
if (typeof(tabs[i]) != "undefined") {
urls.push(tabs[i].webNavigation.currentURI.spec);
}
}
return urls;
}
So what I'm looking for is an event that gets called AFTER a tab has been closed, or a way to get the index of the tab that was closed so that I can skip it while iterating browsers.
Anyone know of any such event or other solutions to my problem?

There is no special event for that - you simply catch the regular event and update the tab list delayed. Something along these lines:
var tabRemoveTimeout = null;
window.addEventListener("TabClose", tabRemoved, false);
function tabRemoved(event)
{
// If we already scheduled an async action - cancel it
if (tabRemoveTimeout)
window.clearTimeout(tabRemoveTimeout);
tabRemoveTimeout = window.setTimeout(function()
{
tabRemoveTimeout = null;
updateTabList();
}, 0);
}
A less generic approach would be updating the list immediately but excluding the tab that is being closed.

Here is what I did to solve the problem:
tabRemoved = function (ev)
{
logTabs([ev.target._tPos]); /* ev.target._tPos = index of tab that was closed */
}
logTabs = function (excludelist)
{
...
urls = getTabURLs();
for(var i = 0; i < urls.length; i++)
{
if(typeof(excludelist) != "undefined" && excludelist.indexOf(i) != -1)
continue;
doStuff(urls[i]);
...

Related

Invalid calling object in IE at the start of a for loop

I have a search function that grabs some 1700 items from an XML list and will search through them by the user's query. Beyond that the user can further filter their results by selecting various filters.
It works flawlessly in Chrome but when testing in IE, anytime I click a filter I get an error "invalid calling object" which references the function sortList() specifically the line for(var i=0; i < loadedList.length; i++) as shown in more detail below.
Again, Chrome has no problem with this, its a total IE thing. Setting watches in Chrome, loadedList is an HTMLCollection and can have a .length method applied but for some reason in IE this does not work.
This script is fairly lengthy but I've tried to include the relevant functions below.
So mapping this out conceptually:
var results = [];
var loadedList;
window.onLoad = loadList();
// actual function
function loadList() {
var items = new XMLHttpRequest();
items.onreadystatechange = function() {
//puts all the xml into the loadedList variable
if (this.readyState == 4 && this.status == 200) {
var xmlDoc = this.responseXML;
loadedList = xmlDoc.getElementsByTagName("itm");
sortList();
}
};
items.open("GET", "item-list-file.xml", true);
items.send();
};
function sortList() {
for (var i = 0; i < loadedList.length; i++) {
for (var j = 0; j < loadedList.length; j++) {
if (boxed[j].checked && boxed[j].id.substr(0, 3) == getElement("category", i).toLowerCase().substring(0, 3)) {
// getElement is just a function with a catch statement to handle any missing info on the xml list
//pushes any relevant results into results array
results.push("<li>" + getElement("title", i) + "</li>");
}
}
}
}
};
That's the gist of what happens when the page loads. There is a search() function which just grab's the user's query and passes it to a returnSearch() function. There are no problems with any of that.
The problem arises after the user has searched once and wants to narrow the search by selecting one or more filters.
There is a function, updateURL(), which both updates the window.location.href (so we can link to the search with specific filters already selected), and then runs sortList() again.
function updateURL(searchType) {
//this resents the results array so that
results = [];
//resorts results based on new criteria
sortList();
//runs search again so that the filters are applied asynchronously
search();
};
From what I could tell for some reason IE did not like having the variable loadedList declared outside of the loadList() function. I don't know why but after the items initially loaded there would always be problems with loadedList whenever sortList() was called again.
I ended up removing the function loadList() and just having the xml in the body (not in a function).
var items = new XMLHttpRequest();
items.onreadystatechange = function() {
//puts all the xml into the loadedList variable
if (this.readyState == 4 && this.status == 200) {
var xmlDoc = this.responseXML;
loadedList = xmlDoc.getElementsByTagName("itm");
sortList();
}
};
items.open("GET", "item-list-file.xml", true);
items.send();
function sortList() {
Still not sure what IE's issue with this was but it works now.

Is it possible to change a string parameter with a For Loop?

I'm not a programmer, neither studying something related to it, but just someone who wants to run a code to make my work life easier.
I need to open 50 tabs. Opening one by one takes so much time because when I click the open button, it shows me the new tab opened and then I have to go back to the original page to open the next one and so on.
After a weekend of doing some research, I found that Google Chrome has a "Console" that can be modified to make a webpage work as you want.
The code that runs to open a tab in this webpage is the following. I've run this code in the console and surprisingly it works:
if(typeof jsfcljs == 'function'){
jsfcljs(document.getElementById('ngFindListForm'), {'ngFindListForm:tblDataTable:0:j_id178':'ngFindListForm:tblDataTable:1:j_id178'},'_blank');}
And to open the next tab is:
if(typeof jsfcljs == 'function'){
jsfcljs(document.getElementById('ngFindListForm'),{'ngFindListForm:tblDataTable:1:j_id178':'ngFindListForm:tblDataTable:1:j_id178'},'_blank');}
As you see, the "only" part of code that changes is the number between colons (0 and 1).
So, according to my basic-high school-programming skills, I think I can change those number with a For Loop from 0 to 49 (50 tabs). I've tried that like this:
for (i = 0; i < 50; i++) {
param = 'ngFindListForm:tblDataTable:' + i +':j_id178';}
And then using this param something like this:
if(typeof jsfcljs == 'function'){
jsfcljs(document.getElementById('ngFindListForm'),{param:'ngFindListForm:tblDataTable:1:j_id178'},'_blank');}
But it's not working. It just makes to open the same page I'm on in a new tab.
Maybe the logic I have figured out how to make this work is totally wrong, but this is why I came here to ask you.
Thanks
move your code inside foreach and use [param] (ES6)
var form = document.getElementById('ngFindListForm');
if (typeof jsfcljs == 'function') {
for (i = 0; i < 50; i++) {
var param = 'ngFindListForm:tblDataTable:' + i + ':j_id178';
jsfcljs(form, {
[param]: 'ngFindListForm:tblDataTable:1:j_id178'
}, '_blank');
}
}
OR
var form = document.getElementById('ngFindListForm');
if (typeof jsfcljs == 'function') {
for (i = 0; i < 50; i++) {
var obj = {};
obj['ngFindListForm:tblDataTable:' + i + ':j_id178'] = 'ngFindListForm:tblDataTable:1:j_id178';
jsfcljs(form, obj, '_blank');
}
}
There are a few ways to do it, the easiest would be to use an object and set the string
function jsfcljs(e, o, t) {
console.log(o);
}
for (let i = 0; i < 5; i++) {
var obj = {}
obj['ngFindListForm:tblDataTable:' + i + ':j_id178'] = 'ngFindListForm:tblDataTable:' + i + ':j_id178'
jsfcljs(document.getElementById('ngFindListForm'), obj, '_blank');
}
<div id="ngFindListForm"></div>

execCommand('copy') not working programatically, only when executed via DevTools console

Source:
const package = document.querySelector('td[data-bind="text: packageName"');
if (package.textContent.indexOf('Adaptive') !== -1) {
package.click();
const stacks_tab = document.querySelector('ul[class="tabsExpanded"]').children[5];
stacks_tab.click();
function get_sources() {
const sources = [];
const stacks = document.querySelectorAll('span[data-bind="text:duration"]');
for (let i = 0; i < stacks.length; i++) {
stacks[i].click();
let renditions = document.querySelectorAll('span[class="blockUnSelected"]');
renditions[(i+1) * 8 - 1].click();
sources.push(document.querySelectorAll('p[data-bind="text: $data.name"]')[0].textContent);
}
let copy = '';
for (let i = 0; i < sources.length; i++) {
const change_brackets = sources[i].replace(/\/tveorigin\/vod\/ae\//, '');
const no_pd1 = change_brackets.replace(/-pd1/g, '');
copy += no_pd1 + ',';
}
if (copy === '') {
setTimeout(get_sources, 500);
} else {
const hidden = document.createElement('input');
hidden.value = copy;
document.querySelector('body').appendChild(hidden);
hidden.select();
function copy_sources() {
console.log('running');
hidden.select();
if (!document.execCommand('copy')) {
setTimeout(copy_sources, 500);
} else {
console.log('Sources copied!');
}
}
copy_sources();
}
}
get_sources();
} else {
console.log('There is no Adaptive package in this content.');
}
Line 45 is what isn't working.
That code won't make a lot of sense, but here's the use case:
I'm trying to automate part of my job by injecting some JavaScript into the Chrome DevTools console on our CMS that we use for video content where I work. What the script does is click a few elements, then grabs some file locations and copies them to the clipboard as comma separated values. I had this working just fine before, but I decided to try and make the script better...and now the document.execCommand('copy') is just not working.
As you can see, I use some recursion to continuously select the hidden input value and then I try to copy it, and if it fails, I try again in 500 ms. I also log 'running' to ensure the function is actually running (it is). The execCommand() function keeps returning false every 500ms. BUT, if I type it into the console manually and run it, it returns true and works fine even as the recursive function continues to return false. So for some reason, it won't work in the context of my script, but works totally fine when run manually.
Like I said before, it WAS working programatically before, but I changed some stuff to make the script better and more automated, and it won't work anymore. Here's the code with execCommand() working fine:
const sources = [];
const stacks = document.querySelectorAll('span[data-bind="text:duration"]');
for (let i = 0; i < stacks.length; i++) {
stacks[i].click();
let renditions = document.querySelectorAll('span[class="blockUnSelected"]');
renditions[(i+1) * 8 - 1].click();
sources.push(document.querySelectorAll('p[data-bind="text: $data.name"]')[0].textContent);
}
let copy = '';
for (let i = 0; i < sources.length; i++) {
const change_brackets = sources[i].replace(/\/tveorigin\/vod\/ae\//, '');
const no_pd1 = change_brackets.replace(/-pd1/g, '');
copy += no_pd1 + ',';
}
const hidden = document.createElement('input');
hidden.value = copy;
document.querySelector('body').appendChild(hidden);
hidden.select();
document.execCommand('copy');
I just tested that code and it still works, and copies the text to the clipboard as intended. The only notable different I see is that in the older code, I run execCommand() in the global context, whereas in the new script, it's in a function context. Could this have something to do with it?
So the solution to this was odd. execCommand() can only be triggered by a user event handler, so what I had to do was attach a click listener to the window, then invoke a click event on the hidden node. Because that triggered a click handler, that made it work!

Openning Links then searching each one of urls then closing each window and adding the urls to an array

I just started getting into javascript and decided to create a script that will take a list of urls and open each url. Once it opens each url, it will search the page for a certain Href and then add it to a global var array. So far I have functions that creates the list of urls that I want to search, but my functions that searches each window doesn't seem to work. When I debugged it to print out the current document url in the Onload function it only prints out only 1 url.
How can I do the process mentioned above without using jquery?
Thank you for your help!
Here is my code.
var yearLinksArray =[];
var deletePages=[];
function deleter(){
for (var i = 0; i< document.getElementsByTagName("a").length;i++){
if (document.getElementsByTagName("a")[i].href.indexOf("remove")!= -1){
console.log(document.getElementsByTagName("a")[i].href);
}
}
}
function yearLinks(){
var links = [];
var deletePages =[];
var yearLink = "month_2015_2"
for (var i = 0; i< document.getElementsByTagName("a").length;i++){
if (document.getElementsByTagName("a")[i].href.indexOf("year_")!= -1){
links.push(document.getElementsByTagName("a")[i].href);
deletePages.push(document.getElementsByTagName("a")[i].href);
}
}
return links;
}
function addPages(){
console.log(document.URL);
for(var year =2015;year!= 2008;year--){
for (var month =12;month!=0;month--){
var yearLink = "month_"+year+"_"+month;
for (var i = 0; i< window.document.getElementsByTagName("a").length;i++){
if (window.document.getElementsByTagName("a")[i].href.indexOf(yearLink)!= -1){
//console.log(document.getElementsByTagName("a")[i].href);
deletePages.push(window.document.getElementsByTagName("a")[i].href);
}
}
}
}
}
function searcher(link){
window.open(link);
window.onLoad= addPages();
}
yearLinksArray = yearLinks();
for(var i=0;i <yearLinksArray.length;i++){
searcher(yearLinksArray[i]);
};
This function is wrong:
function searcher(link){
window.open(link);
window.onLoad= addPages();
}
It's adding the onload handler to the current window, not the window you just opened. It's also calling the addPages function immediately, and assigning the result to window.onload; you should be setting the onload property to the function itself.
function searcher(link) {
var win = window.open(link);
win.onload = addPages;
}
Note that none of this will work if the pages you're loading are in a different domain from your script. Javascript can't access contents of windows in a different domain.

Chrome and Firefox incompatibility

I have two frames, the expression running in first frame and calling highlightElements function in another frame. This expression works fine in Firefox:
parent.frames[0].highlightElements(lineNumbers, stringObj);
The highlightElements function (just for sure):
function highlightElements(lineNumbers, stringObj) {
// run through the cycle and highlight them
//for (var ln in lineNumbers) {
var length = lineNumbers.length;
for (var ln=0; ln<length; ln++) {
var elements = $('.no');
//for (var i in elements) {
var el_length = elements.length;
for (var i=0; i<el_length; i++) {
if (parseInt(elements[i].innerHTML) == lineNumbers[ln]) {
var badThing = "yes";
for (var nextElement = elements[i].next();
nextElement.className != '.no'; nextElement = elements[i].next()) {
if (nextElement.innerHTML == stringObj) {
badThing = "no";
nextElement.effect('highlight', {}, 'slow');
scrollIntoView(nextElement);
}
}
if (badThing == "yes") alert("Didn't find the object");
}
}
}
}
But in Chrome it produces the error "Uncaught TypeError: Property 'highlightElement' of object[objectDOMWindow] is not a function".
How to change the expression to make it runnable in Chrome? Thanks
Make sure both frames are under same domain and protocol. Chome blocks javascript access from frames to another if the domains/protocols don't match. If you are working locally, and not under a local domain (i.e. the url is something like file:///C:/etc/etc.html) then it won't work either.

Categories

Resources