I am creating a simple bootstrapped add-on for Firefox. I need to capture the current URL from the browser through sidebar with a button click.
My bootstrap.js:
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
Cu.import('resource://gre/modules/Services.jsm');
Cu.import("resource://gre/modules/NetUtil.jsm");
Cu.import("resource://gre/modules/FileUtils.jsm");
/*start - windowlistener*/
var windowListener = {
//DO NOT EDIT HERE
onOpenWindow: function (aXULWindow) {
// Wait for the window to finish loading
let aDOMWindow =aXULWindow.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowInternal||Ci.nsIDOMWindow);
aDOMWindow.addEventListener("load", function () {
aDOMWindow.removeEventListener("load", arguments.callee, false);
windowListener.loadIntoWindow(aDOMWindow, aXULWindow);
}, false);
},
onCloseWindow: function (aXULWindow) {},
onWindowTitleChange: function (aXULWindow, aNewTitle) {},
register: function () {
// Load into any existing windows
let XULWindows = Services.wm.getXULWindowEnumerator(null);
while (XULWindows.hasMoreElements()) {
let aXULWindow = XULWindows.getNext();
let aDOMWindow = aXULWindow.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowInternal || Ci.nsIDOMWindow);
windowListener.loadIntoWindow(aDOMWindow, aXULWindow);
}
// Listen to new windows
Services.wm.addListener(windowListener);
},
unregister: function () {
// Unload from any existing windows
let XULWindows = Services.wm.getXULWindowEnumerator(null);
while (XULWindows.hasMoreElements()) {
let aXULWindow = XULWindows.getNext();
let aDOMWindow = aXULWindow.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowInternal || Ci.nsIDOMWindow);
windowListener.unloadFromWindow(aDOMWindow, aXULWindow);
}
//Stop listening so future added windows dont get this attached
Services.wm.removeListener(windowListener);
},
//END - DO NOT EDIT HERE
loadIntoWindow: function (aDOMWindow, aXULWindow) {
if (!aDOMWindow) {
return;
}
//START - EDIT BELOW HERE
var browser = aDOMWindow.document.querySelector('#browser')
if (browser) {
var splitter = aDOMWindow.document.createElement('splitter');
var propsToSet = {
id: 'demo-sidebar-with-html_splitter',
//I'm just copying what Mozilla does for their social sidebar splitter
// I left it out, but you can leave it in to see how you can style
// the splitter
class: 'sidebar-splitter'
}
for (var p in propsToSet) {
splitter.setAttribute(p, propsToSet[p]);
}
var sidebar = aDOMWindow.document.createElement('vbox');
var propsToSet = {
id: 'demo-sidebar-with-html_sidebar',
//Mozilla uses persist width here, I don't know what it does and can't
// see it how makes a difference so I left it out
//persist: 'width'
}
for (var p in propsToSet) {
sidebar.setAttribute(p, propsToSet[p]);
}
var htmlVal = loadJsonHTML(0);
var sidebarBrowser = aDOMWindow.document.createElement('browser');
var propsToSet = {
id: 'demo-sidebar-with-html_browser',
type: 'content',
context: 'contentAreaContextMenu',
disableglobalhistory: 'true',
tooltip: 'aHTMLTooltip',
autoscrollpopup: 'autoscroller',
flex: '1', //do not remove this
//you should change these widths to how you want
style: 'min-width: 14em; width: 18em; max-width: 36em;',
//or just set this to some URL like http://www.bing.com/
src: 'data:text/html,'+ htmlVal
}
for (var p in propsToSet) {
sidebarBrowser.setAttribute(p, propsToSet[p]);
}
browser.appendChild(splitter);
sidebar.appendChild(sidebarBrowser);
browser.appendChild(sidebar);
}
//END - EDIT BELOW HERE
},
unloadFromWindow: function (aDOMWindow, aXULWindow) {
if (!aDOMWindow) {
return;
}
//START - EDIT BELOW HERE
var splitter = aDOMWindow.document
.querySelector('#demo-sidebar-with-html_splitter');
if (splitter) {
var sidebar = aDOMWindow.document
.querySelector('#demo-sidebar-with-html_sidebar');
splitter.parentNode.removeChild(splitter);
sidebar.parentNode.removeChild(sidebar);
}
//END - EDIT BELOW HERE
}
};
/*end - windowlistener*/
function startup(aData, aReason) {
windowListener.register();
}
function shutdown(aData, aReason) {
if (aReason == APP_SHUTDOWN) return;
windowListener.unregister();
}
function loadJsonHTML(val=0){
var fileContent = "";
var localFile = Cc["#mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
//full path is okay if directory exists
localFile.initWithPath("/Users/tinuy/Desktop/test_addodn/input.txt");
//otherwise specify directory, create it if necessary, and append leaf.
//localFile.initWithPath("C:\Users\tinuy\Documents\test\input.txt");
if ( localFile.exists() == false ) {
fileContent = "File does not exist";
}
var istream = Cc["#mozilla.org/network/file-input-stream;1"]
.createInstance(Ci.nsIFileInputStream);
istream.init(localFile, 0x01, 4, null);
var fileScriptableIO = Cc["#mozilla.org/scriptableinputstream;1"]
.createInstance(Ci.nsIScriptableInputStream);
fileScriptableIO.init(istream);
// parse the XML into our internal document
istream.QueryInterface(Ci.nsILineInputStream);
//fileContent = fileScriptableIO.read( '1' );
var csize = 0;
while ((csize = fileScriptableIO.available()) != 0) {
fileContent += fileScriptableIO.read( csize );
}
var array = fileContent.split("&");
fileScriptableIO.close();
istream.close();
return makeHTML(array[val], val);
}
function makeHTML(value, key){
var arrValues = value.split(",");
var htmlContent = '<div name="content" class="content">' +
'<p> Name :' + arrValues[0] + '</p>';
htmlContent += '<p> Price :' + arrValues[2] + '</p>';
htmlContent += '<p> Color :' + arrValues[3] + '</p>';
htmlContent += '<p> UID :' + arrValues[1] + '</p>';
htmlContent += '<p><input type="radio" name="valid" value="yes" />Yes ' +
'<input type="radio" name="valid" value="no" /> No</p>' +
'<p><input type="text" placeholder="Reason" name="checkreason"></p>' +
'<p><input type="text" placeholder="Feedback" name="feedback"></p>' +
'</div><div><button name="load" type="button" id="loadit" onclick="loadHtml()" ' +
'loadurl="'+arrValues[4]+'">Load</button> <button name="save" type="button">' +
'Save </button> <button name="next" type="button" key="'+key+'">Next </button> ' +
'</div> <script> function loadHtml() {' +
'var a = gBrowser.contentWindow.location.href ;alert(a);' +
'} </script>';
return htmlContent;
}
function install() {}
function uninstall() {}
I tried all suggestions from Get current page URL from a firefox sidebar extension but nothing worked.
However, from the fact that you have set:
var propsToSet = {
id: 'demo-sidebar-with-html_browser',
type: 'content', //<---------------------------This
the <browser> type to content, I am assuming one of two things:
That you are not actually trying to get the active tab's URI from within the code you are loading into the <browser> through the src attribute.
If this is the case, please see my answer to "How to Get Active Tab Location by e10s add-on". In that case, the context from which you are running should allow you to use the code in that answer.
This possible assumption, #1, is not the case for your code.
You want to gain access to the URI form code within the <browser> you have loaded into the sidebar and are unaware of the restrictions which setting the <browser> type to content imposes upon the code you load into the <browser>.
It looks like you are trying to access the currant tab's URL from the code in your <browser>. You should consider restructuring your code such that access to chrome privileges is not required from the content that is in the <browser>. In other words, if possible, you should write your code such that you do not need to have access to the URL, or other information, directly from code running in the <browser> you insert. However, doing so may end up being significantly more complex.
Getting access to chrome privileges within the <browser> you are adding:
Setting the <browser> type to content is for:
A browser for content. The content that is loaded inside the browser is not allowed to access the chrome above it.
Setting the <browser> type to chrome is for:
(default behaviour): A browser, intended to be used for loading privileged content using a chrome:// URI. Don't use for content from web, as this may cause serious security problems!
To set the the <browser> type to chrome you can do:
var propsToSet = {
id: 'demo-sidebar-with-html_browser',
type: 'chrome', //<---------------------------This
The code provided by Noitidart comes close to what you need. However, it needs to be slightly modified, as gBrowser.currentURI is a nsIURI, not a string). Thus, you need to use gBrowser.currentURI.spec. You can get it working by changing your makeHTML() to:
function makeHTML(value, key){
var arrValues = value.split(",");
var htmlContent = '<html><head></head>'
+ '<body style="background-color: white;">'
+ ' <div name="content" class="content">'
+ ' <p> Name :' + arrValues[0] + '</p>';
htmlContent += '<p> Price :' + arrValues[2] + '</p>';
htmlContent += '<p> Color :' + arrValues[3] + '</p>';
htmlContent += '<p> UID :' + arrValues[1] + '</p>';
htmlContent +=
' <p><input type="radio" name="valid" value="yes" />Yes '
+ ' <input type="radio" name="valid" value="no" /> No</p>'
+ ' <p><input type="text" placeholder="Reason" name="checkreason" /></p>'
+ ' <p><input type="text" placeholder="Feedback" name="feedback" /></p>'
+ ' </div>'
+ ' <div>'
+ ' <button name="load" type="button" id="loadit" onclick="loadHtml()" '
+ ' loadurl="' + arrValues[4] + '">Load</button>'
+ ' <button name="save" type="button">Save </button>'
+ ' <button name="next" type="button" key="' + key + '">Next </button>'
+ ' </div>'
+ ' <script>'
+ ' function loadHtml() {'
+ ' const Cu = Components.utils;'
+ ' Cu.import("resource://gre/modules/Services.jsm");'
+ ' var win = Services.wm.getMostRecentWindow("navigator:browser");'
+ ' if (win) {'
+ ' var url = win.gBrowser.currentURI.spec;'
+ ' alert("URL=" + url);'
+ ' }'
+ ' }'
+ ' </script>'
+ '</body></html>';
return htmlContent;
}
Security concerns:
There are some really significant security concerns that you may be running into. Just from the fact that you want the URL for the current page implies that you may be rapidly approaching where these are a real issue. It is going to depend on what exactly you are going to do. Given that you are not providing that information, I am not going to go too in depth as to what the issues are. However, you should read "Firefox Security Basics for Developers" and "Security best practices in extensions"
Additional information about sidebars:
You should seriously consider creating an Add-on SDK based add-on, instead of a bootstrap one, and using the ui/sidebar API.
If you are interested, I created some code which can be used to create a "sidebar" that is located at the top, left, right, or bottom of the active tab, or in a separate window (similar to what the devtools panel will do). The sidebar created is associated with the currently active tab, not all tabs (I guess I should have made it an option to have it associated with all tabs, or just the current tab) in my answer to: "How to create a bottom-docked panel with XUL in Firefox?" and "Firefox Extension, Window related sidebar"
I'm trying to find all divs that have been created from my click event and split them into another div (.wrapAll) on a count of 3. I can't seem to get anything back when i console.log the vars length. I know this works when I do that same process on the html thats been statically typed. Below is my code and thank you fo the thoughts!
jQuery(document).ready(function($) {
// load default twitch channels
$.getJSON('https://api.twitch.tv/kraken/streams/freecodecamp?callback=?', function(data) {
//console.log(data);
});
// Bind 'Enter' to click event
$(document).bind('keypress', function(e) {
if (e.keyCode == 13) {
$('#search').trigger('click');
}
});
// manually search for games
$('#search').on("click", function() {
// clear previous results and get search term
$('#results').html('');
search = $('#searchTerm').val();
// begin API call
$.getJSON( "https://api.twitch.tv/kraken/search/streams?q=" + search + "", function(data2) {
// console.log(data2.streams.length);
data2.streams.forEach(function(entry) {
//console.log(entry._links);
var streamURL = entry.channel.url;
url = entry.preview.medium;
$('#results').append('<div class="searchResults"><img class="games" src=' + url + '/><p id="title"> Game: ' + entry.channel.game + '<br> Viewers: ' + entry.viewers +'<br> Is Mature: ' + entry.channel.mature + '<br> Status: ' + entry.channel.status + ' </p></div><hr>');
});
});
// Get 3 divs and slice into one div to style ** problem child **
var a = $('div[id^=searchResu]').find('div');
console.log(a.length);
for( var i = 0; i < a.length; i+=3 ) {
a.slice(i, i+3).wrapAll('<div class="slide"></div>');
}
});
});
Check out this plunker here. I believe this does what your looking for.
<!DOCTYPE html>
<html>
<head>
<script data-require="jquery#2.1.4" data-semver="2.1.4" src="https://code.jquery.com/jquery-2.1.4.js"></script>
<script src="https://cdn.jsdelivr.net/lodash/4.13.1/lodash.min.js"></script>
<script>
jQuery(document).ready(function($) {
function appendHtmlContent(resultHtmlContent) {
resultHtmlContent = '<div class="slide">' + resultHtmlContent + '</div>';
$('#results').append(resultHtmlContent);
}
function processSvcResponse(data2) {
var count = 0,
searchResultContents = '',
$div = $("<div>", { class: "searchResults"});
data2.streams.forEach(function(entry) {
var streamURL = entry.channel.url;
url = entry.preview.medium;
searchResultContents += '<div class="searchResults"><a href="' + streamURL
+ '" target="_blank"><img class="games" src=' + url + '/><p id="title"> Game: ' + entry.channel.game
+ '<br> Viewers: ' + entry.viewers + '<br> Is Mature: ' + entry.channel.mature
+ '<br> Status: ' + entry.channel.status + ' </p></a></div><hr>';
count++;
if(count === 3) {
appendHtmlContent(searchResultContents);
searchResultContents = '';
count = 0;
}
});
// more results that have not been appended?
if(searchResultContents) {
appendHtmlContent(searchResultContents);
}
}
// load default twitch channels
$.getJSON('https://api.twitch.tv/kraken/streams/freecodecamp?callback=?', function(data) {});
// Bind 'Enter' to click event
$(document).bind('keypress', function(e) {
if (e.keyCode == 13) {
$('#search').trigger('click');
}
});
// manually search for games
$('#search').on("click", function() {
// clear previous results and get search term
$('#results').html('');
search = $('#searchTerm').val();
// begin API call
$.getJSON("https://api.twitch.tv/kraken/search/streams?q=" + search, processSvcResponse);
});
});
</script>
</head>
<body>
<input id="searchTerm" type="text" />
<button id="search" type="button">Search</button>
<div id="results"></div>
</body>
</html>
If I understand correctly you are wanting to iterate over the results and for every third one wrap it inside a div with class "slider". As mentioned in the comments by #charlietfl in order to query newly created DOM elements using jQuery you have to query them after they are created. In the call to jQuery.getJSON the second argument accepts a callback function. The signature is jQuery.getJSON(url, someCallbackFunction). In order to make your code a bit more readable I moved "function(data2)" up and named it processSvcResponse. Inside processSvcResponse I build up an HTML string from the results and track how many results are processed by using a counter variable. Once the counter reaches 3 I append the contents to the results div and reset the counter. This solution does not "find" the divs and slice/wrapAll as you were intending to do originally, however, I believe this still accomplishes your goal.
As #charlietfl said, you'll need to place the code that wraps your divs in the callback for getJSON. Your click event listener would look something like this then:
$('#search').on("click", function() {
// clear previous results and get search term
$('#results').html('');
search = $('#searchTerm').val();
// begin API call
$.getJSON( "https://api.twitch.tv/kraken/search/streams?q=" + search + "", function(data2) {
// console.log(data2.streams.length);
data2.streams.forEach(function(entry) {
//console.log(entry._links);
var streamURL = entry.channel.url;
url = entry.preview.medium;
$('#results').append('<div class="searchResults"><img class="games" src=' + url + '/><p id="title"> Game: ' + entry.channel.game + '<br> Viewers: ' + entry.viewers +'<br> Is Mature: ' + entry.channel.mature + '<br> Status: ' + entry.channel.status + ' </p></div><hr>');
});
// Get 3 divs and slice into one div to style ** problem child **
var a = $('div[id^=searchResu]').find('div');
console.log(a.length);
for( var i = 0; i < a.length; i+=3 ) {
a.slice(i, i+3).wrapAll('<div class="slide"></div>');
}
});
});
I've built a Mapbox map that has a fairly involved custom popup structure, including photos and formatting. I'm using a .csv file and omnivore to feed my data, and creating the popup .on ready. I've also got a search box that searches the data using jQuery.
Independently, each one works. The popups load fine, and the search is working. But after filtering using the search thing, I lose the popups.
I've looked at this similar post but nothing suggested seems to be working. I think this has to do with my popup variable being contained inside of the .on ready function. Is there something I'm missing here? Do I need to restructure how the popups are created?
Here's my code:
var featureLayer = L.mapbox.featureLayer().addTo(map);
jQuery('#search').keyup(search);
Load data, format popups:
var csvLayer = omnivore.csv('Stats.csv', null, L.mapbox.featureLayer())
.on('ready', function() {
map.fitBounds(csvLayer.getBounds(), {paddingTopLeft: [0,25]});
function updownFormat(updown, change){
if (updown === '—') {
return "No change ";
} else if (updown === "N/A") {
return "New store - no data ";
} else if (updown === '▲') {
return '<span class="upPercent">' + updown + "</span> " + change;
} else {
return '<span class="downPercent">' + updown + "</span> " + change;
}
};
csvLayer.eachLayer(function(layer) {
var prop = layer.feature.properties
var popup = '<div class="popup"><h3>'+ prop.storename +'<\/h3>' +
'<h4>'+ prop.town +'</h4>' +
'<p><b>Rank:</b> #'+ prop.rank +' (of 80 stores) <br>' +
'<b>Sales: </b>' + prop.money14 +' (2014)<br>' +
'<b>Growth: </b>' + updownFormat(prop.updown, prop.change) + ' (from 2013)</p>' +
'<h5>Best selling bottles (2014)</h5>' +
'<img src="'+ prop.pop1img +'" class="liquorimg">' +
'<ol><li><b>'+prop.pop1+'</b></li><li>'+prop.pop2+'</li><li>'+prop.pop3+'</li><li>'+prop.pop4+'</li><li>'+prop.pop5+'</li></ol></div>';
layer.bindPopup(popup);
});
})
.addTo(map);
The search function:
function search() {
// get the value of the search input field
var searchString = jQuery('#search').val().toLowerCase();
csvLayer.setFilter(showStoreTown);
function showStoreTown(feature) {
return feature.properties.storetown
.toLowerCase()
.indexOf(searchString) !== -1;
}
}
Fixed, thanks to Twitter! Setting map bounds on "ready" and setting the popup creation on "addlayer" took care of it.
I'm trying to build a little app where the user enters a store name which will become the alt text to an image and the URL of their store which will become a h ref, which will then generate some copy and paste code to use on their website.
The problem I have is that when trying to use vanilla JavaScript to read the value of the store name nothing is showing.
Please see my fiddle:
http://jsfiddle.net/willwebdesigner/gVCcm/
$(document).ready(function() {
// Creates a method and prototype for the Array for splicing up words
Array.prototype.removeByValue = function(val) {
for(var i=0; i<this.length; i++) {
if(this[i] == val) {
this.splice(i, 1);
break;
}
}
}
var myStore = {
storeName: document.getElementById("storeName").value,
Url: document.getElementById("yourURL"),
instructions: "<p>Please copy the code above and paste it into your webpage to display the shop4support logo and a link to your store.</p>",
blackLogo: function() {
return "<img src="https://www.shop4support.com/Resources/home/information/InformationForProviders/Availables4s-bk.jpg" alt="" + this.storeName + " is available on shop4support" />";
}
}
$("#urlForm").submit(function(e) {
// Clear output form first
$(output).html(' ');
e.preventDefault();
// Create an array for conditional statements
var address = [];
address = $(myStore.Url).val().split("www");
// Need to put a conditional in here to detect if https://
if(address[0] === "https://") {
$(output)
.slideDown()
.prepend("<a href=""+ $(myStore.Url).val() + "">" + myStore.blackLogo() + "</a> ")
.append(myStore.instructions);
} else if(address[0] === "http://") {
// Remove the http part off the URL
var removeHttp = address;
removeHttp.removeByValue('http://');
$(output)
.slideDown()
.prepend("<a href="https://www" + removeHttp + "">" + myStore.blackLogo() + "</a> ")
.append(myStore.instructions);
} else {
$(output)
.slideDown()
.prepend("<a href="https://"+ $(myStore.Url).val() + "">" + myStore.blackLogo() + "</a> ")
.append(myStore.instructions);
}
});
});
Thanks Will
The moment you are initializing the myStore object the values aren't filled in yet. You will have to do this on submit.
So you can move the var myStore = {} into the submit callback:
http://jsfiddle.net/gVCcm/1/
update your code as :
$("#urlForm").submit(function(e) {
myStore.storeName= (document.getElementById("storeName").value);
/* The rest of your code */
}
Initialize your myStore variable in:
$("#urlForm").submit(function(e) {
var myStore = {}
});
I have a button that is generated for each record from a search.
The buttons function is to copy the file number of that record to the clipboard, however no matter which button is clicked it only copies the first records file number?
<imgage alt="Copy File No. <%=ShowFileNo%><delim> to clipboard" name="<%=ShowFileNo%>" src="Style/Copy2.png" onclick="copy('CopyImage');" id="CopyImage" />
function copy(controlId) {
var control = document.getElementById(controlId);
if (control) {
var controlValue = control.name + '<delim>';
window.clipboardData.setData('Text', controlValue);
alert("'" + controlValue + "'" + ' copied to clipboard')
window.close();
}
}
Thanks
Darren
IDs must be unique. This intrinsic to both HTML and XML.
You can change your onclick to this: onclick="copy(this);"
And change your reference as such:
function copy(control) {
// var control = document.getElementById(controlId);
if (control) {
var controlValue = control.name + '<delim>';
window.clipboardData.setData('Text', controlValue);
alert("'" + controlValue + "'" + ' copied to clipboard')
window.close();
}
}