var tabs = require("sdk/tabs");
var iofile = require("sdk/io/file");
var widgets = require("sdk/widget");
var selection = require("sdk/selection");
function console_log(text) {
console.log(selection.text);
}
function print(text) {
console.log(text);
}
function dir_object(object_to_parse) {
var name = '';
for (name in object_to_parse) {
print(name);
}
}
function write_text(filename, text) {
var fh = iofile.open(filename, 'w');
var content = fh.read();
dir_object(fh);
selected_text = text + "\n";
fh.write(selected_text);
fh.flush();
fh.close()
}
function select_text_handler() {
write_text('/tmp/foo', selection.text);
}
var widget = widgets.Widget({
id: "scribus-link",
label: "Scribus website",
contentURL: "http://www.mozilla.org/favicon.ico",
onClick: function() {
}
});
selection.on('select', function () { select_text_handler(); });
'open' the file in 'w' and that truncates my existing file! How do i open in 'append' mode and then 'seek'?? https://addons.mozilla.org/en-US/developers/docs/sdk/latest/modules/sdk/io/file.htm
The file module of the SDK is pretty limited. When opening a file for writing it will always be truncated (code). Also, it is uses entirely synchronous I/O on the main thread, which isn't really a good thing to do, as it will block the entire UI during the I/O.
You should probably use another mechanism via the chrome module. See OS.File and/or the MDN File I/O snippets.
Related
In this weird situation, I need to convert a JS function into a string, and then then that string into a dynamically generated <script> tag (which uses type javascript/worker so as to not be executed).
I'm working on a user script, injecting code to create a shared web worker between two third party sites. The thing about user scripts is you usually just want to use one file for the script's functionality, and web workers like to use a separate JS file for the worker. Well there's a way around that using a Blob. However, remember I'm only working with a script file, not an HTML file for the user script, so I want to store this part (the shared worker code) as a function in my script:
var worker = function() {
self.addEventListener('connect', function(e) {
var port = e.ports[0];
port.addEventListener('message', function(e) {
var message = e.data;
port.postMessage(message);
});
port.start();
});
};
then use the toString() on it so I can inject it into a dynamically generated script tag in the third party pages, so it ends up like this without the script calling on any extra files:
<script id="worker1" type="javascript/worker">
self.addEventListener('connect', function(e) {
var port = e.ports[0];
port.addEventListener('message', function(e) {
var message = e.data;
port.postMessage(message);
});
port.start();
});
</script>
However using the toString() method results in the word function being included, as shown in this example:
Remember I was going to append the function to a script tag with type
javascript/worker instead of text/javascript so the function
wouldn't be executed until it was converted to a blob and used as a
file source for the worker, but here I'm going to use
text/javascript so you can see the error when it executes.
var worker = function() {
self.addEventListener('connect', function(e) {
var port = e.ports[0];
port.addEventListener('message', function(e) {
var message = e.data;
port.postMessage(message);
});
port.start();
});
};
var func = worker.toString();
var script = document.createElement("script");
script.type = "text/javascript";
script.id = "worker1";
$("head").append(script);
$('body').append(script);
$('#worker1').append(func);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Just so you can understand what I'm doing, because it might be confusing,
a function like this would then convert the above
non-executed script tag into a Blob and use that instead of an
external file to create the shared worker:
var blob = new Blob([document.querySelector('#worker1').textContent],
{ type: "text/javascript" });
var sharedWorker = new SharedWorker(window.URL.createObjectURL(blob));
So, how can I inject the contents of the function into a script tag without including the function wrapper syntax(function (){)?
You can use string's methods to do this:
I think this can help you or something like this, to remove the function name.
function test(){
console.log('TEST TEST');
}
var fBody = test.toString()
var index = fBody.indexOf('{');
var functionBodyAsArray = fBody.substring(index + 1);
functionBodyAsArray = functionBodyAsArray.substring(0, functionBodyAsArray.length - 1);
console.log(functionBodyAsArray );
I have a array where i have specified the files i need to load in javascript before calling specific script. Lets call those particular lines of code as myscript.
I did as follows
var fileNamesArray = new Array();
fileNamesArray.push("abc.js");
fileNamesArray.push("pqr.js");
fileNamesArray.push("xyz.js");
fileNamesArray.push("klm.js");
var totalFiles = jQuery(fileNamesArray).length;
var tempCount = 0;
jQuery.each(fileNamesArray, function(key, value) {
jQuery.getScript(value, function() {
tempCount++;
});
});
to check whether all files are being loaded or not, i done following thing but doesn't seems to be effective
var refreshIntervalId = setInterval(function() {
if (tempCount == totalFiles) {
clearInterval(refreshIntervalId);
return;
}
}, 10);
i have implemented these in object oriented javascript as follows
function Loader() {
this.jQuery = null;
// check for specifically jQuery 1.8.2+, if not, load it
if (jQuery == undefined) {
jQuery.getScript(
"/Common/javascript/jquery/map/javascript/jquery-1.8.2.js",
function() {
this.jQuery = jQuery.noConflict();
});
} else {
var jQueryVersion = $.fn.jquery;
jQueryVersion = parseInt(jQueryVersion.split('.').join(""));
if (182 > jQueryVersion) {
jQuery.getScript(
"/Common/javascript/jquery/map/javascript/jquery-1.8.2.js",
function() {
this.jQuery = jQuery.noConflict();
});
}
}
}
Loader.prototype.LoadAllFile = function() {
//here i am loading all files
}
Loader.prototype.bindMap = function(options) {
this.LoadAllFile();
//execute the script after loading the files... which we called as myscript
}
i am loading more than 12-14 js files via ajax.
if you observe Loader.prototype.bindMap, i am loading all the files first and then executing the script.
But it seems that myscript the script start executing before all files being loaded.
what are the better ways to execute the script only after all js files are loaded.
Take a look at jQuery's .load() http://api.jquery.com/load-event/
$('script').load(function () { });
Based on the documentation on Jquery.getScript , it is a shorthand for Jquery.ajax. By default this in async call. You might want to change it to do a synchronous call.
To set this property, you can refer to this
So instead of doing a setInterval, you can just loop in your array and do a Jquery.getScript.
I am new to phantomjs, Java script and WebScraping in General. What I want to do is basic http authentication and then visit another URL to get some information. Here is what I have till now. Please tell me what I am doing wrong.
var page = require('webpage').create();
var system = require('system');
page.onConsoleMessage = function(msg) {
console.log(msg);
};
page.onAlert = function(msg) {
console.log('alert!!>' + msg);
};
page.settings.userName = "foo";
page.settings.password = "bar";
page.open("http://localhost/login", function(status) {
console.log(status);
var retval = page.evaluate(function() {
return "test";
});
console.log(retval);
page.open("http://localhost/ticket/" + system.args[1], function(status) {
if ( status === "success" ) {
page.injectJs("jquery.min.js");
var k = page.evaluate(function () {
var a = $("div.description > h3 + p");
if (a.length == 2) {
console.log(a.slice(-1).text())
}
else {
console.log(a.slice(-2).text())
}
//return document.getElementById('addfiles');
});
}
});
phantom.exit();
});
I am passing an argument to this file: a ticket number which gets appended to the 2nd URL.
I would recommend CasperJS highly for this.
CasperJS is an open source navigation scripting & testing utility written in Javascript and based on PhantomJS — the scriptable headless WebKit engine. It eases the process of defining a full navigation scenario and provides useful high-level functions, methods & syntactic sugar for doing common tasks such as:
defining & ordering browsing navigation steps
filling & submitting forms
clicking & following links
capturing screenshots of a page (or part of it)
testing remote DOM
logging events
downloading resources, including binary ones
writing functional test suites, saving results as JUnit XML
scraping Web contents
(from the CasperJS website)
I recently spent a day trying to get PhantomJS by itself to do things like fill out a log-in form and navigate to the next page.
CasperJS has a nice API purpose built for forms as well:
http://docs.casperjs.org/en/latest/modules/casper.html#fill
var casper = require('casper').create();
casper.start('http://some.tld/contact.form', function() {
this.fill('form#contact-form', {
'subject': 'I am watching you',
'content': 'So be careful.',
'civility': 'Mr',
'name': 'Chuck Norris',
'email': 'chuck#norris.com',
'cc': true,
'attachment': '/Users/chuck/roundhousekick.doc'
}, true);
});
casper.then(function() {
this.evaluateOrDie(function() {
return /message sent/.test(document.body.innerText);
}, 'sending message failed');
});
casper.run(function() {
this.echo('message sent').exit();
});
Referring to this question: Add-on Builder: ContentScript and back to Addon code?
Here is my addon code:
var widget = widgets.Widget({
id: "addon",
contentURL: data.url("icon.png"),
onClick: function() {
var workers = [];
for each (var tab in windows.activeWindow.tabs) {
var worker = tab.attach({contentScriptFile: [data.url("jquery.js"), data.url("myScript.js")]});
workers.push(worker);
}
}
});
And here is myScript.js:
var first = $(".avatar:first");
if (first.length !== 0) {
var url = first.attr("href");
self.port.emit('got-url', {url: url});
}
Now that I have multiple workers where do I put
worker.port.on('got-url', function(data) {
worker.tab.url = data.url;
});
Since in the other question I only had one worker but now I have an array of workers.
The code would be:
// main.js:
var data = require("self").data;
var windows = require("windows").browserWindows;
var widget = require("widget").Widget({
id: "addon",
label: "Some label",
contentURL: data.url("favicon.png"),
onClick: function() {
//var workers = [];
for each (var tab in windows.activeWindow.tabs) {
var worker = tab.attach({
contentScriptFile: [data.url("jquery.js"),
data.url("inject.js")]
});
worker.port.on('got-url', function(data) {
console.log(data.url);
// worker.tab.url = data.url;
});
worker.port.emit('init', true);
console.log("got here");
//workers.push(worker);
}
}
});
// inject.js
$(function() {
self.port.on('init', function() {
console.log('in init');
var first = $(".avatar:first");
if (first.length !== 0) {
var url = first.attr("href");
console.log('injected!');
self.port.emit('got-url', {url: url});
}
});
});
Edit: sorry, should have actually run the code, we had a timing issue there where the content script was injected before the worker listener was set up, so the listener was not yet created when the 'got-url' event was emitted. I work around this by deferring any action in the content script until the 'init' event is emitted into the content script.
Here's a working example on builder:
https://builder.addons.mozilla.org/addon/1045470/latest/
The remaining issue with this example is that there is no way to tell if a tab has been injected by our add-on, so we will 'leak' or use more memory every time the widget is clicked. A better approach might be to inject the content script using a page-mod when it is loaded, and only emit the 'init' event in the widget's onclick handler.
I am trying to add plus one button on a XUL window, but I get this error:
Error: Permission denied for <https://plusone.google.com> to call method ChromeWindow.postMessage
Source file: https://ssl.gstatic.com/webclient/js/gc/22431124-0a127465/googleapis.client__plusone.js
Line: 14
I am adding an iframe to https://plusone.google.com/u/0/_/+1/fastbutton?url= ...
Is there a way to add the +1 button on a XUL window and make it accept the postMessage?
The addon I am trying to develop is in the image bellow. The only problem is that it does not register the click due the permission.
bootstrap.js (bootstrap-vsdoc.js)
/// <reference path="bootstrap-vsdoc.js" />
/// <summary>
/// Made by Bruno Leonardo Michels
/// </summary>
var watcher = Components.classes["#mozilla.org/embedcomp/window-watcher;1"]
.getService(Components.interfaces.nsIWindowWatcher);
var listenOpen = {
observe : function(cWindow, cEvent) {
if (cEvent != "domwindowopened") return;
cWindow.addEventListener("load", start, false);
}
};
function startup(data, reason) {
watcher.registerNotification(listenOpen);
var mWindows = watcher.getWindowEnumerator();
while (mWindows.hasMoreElements()) {
start(mWindows.getNext());
}
}
function shutdown(data, reason) {
watcher.unregisterNotification(listenOpen);
var mWindows = watcher.getWindowEnumerator();
while (mWindows.hasMoreElements()) {
end(mWindows.getNext());
}
}
function install(data, reason) {
}
function uninstall(data, reason) {
}
/// #region Methods
function getWindow(cWindow)
{
try
{
if (cWindow instanceof Components.interfaces.nsIDOMEvent)
{
cWindow = cWindow.currentTarget;
}
if (cWindow.document.documentElement.getAttribute("windowtype") != "navigator:browser")
return;
}
catch(ex) { }
return cWindow;
}
function ajaxGet(cWindow, url, cb)
{
var xmlhttp;
xmlhttp = new cWindow.XMLHttpRequest();
xmlhttp.open("GET", url, true);
xmlhttp.onreadystatechange=function()
{
if (xmlhttp.readyState == 4 && xmlhttp.status == 200)
{
cb(xmlhttp);
}
};
xmlhttp.send();
}
var eventList = [];
function bind(gBrowser, cWindow, target, eventName, fn)
{
var ev = function(e) { fn(gBrowser, cWindow, e); };
eventList.push(ev);
target.addEventListener(eventName, eventList[eventList.length-1], false);
}
function unbind(target, eventName, fn)
{
var b = target.removeEventListener ?
function( elem, type, handle ) {
if ( elem.removeEventListener ) {
elem.removeEventListener( type, handle, false );
}
} :
function( elem, type, handle ) {
if ( elem.detachEvent ) {
elem.detachEvent( "on" + type, handle );
}
};
b(target, eventName, fn);
}
function unbindAll(target, eventName)
{
for (var i in eventList)
{
unbind(target, eventName, eventList[i]);
}
}
/// #endregion
/// #region Events
function start(cWindow) {
cWindow = getWindow(cWindow);
if (!cWindow) return;
with (cWindow)
{
bind(gBrowser, cWindow, gBrowser.tabContainer, "TabAttrModified", tabChange);
var window = cWindow;
var document = cWindow.document;
var url = window.location.href;
if (!/^http/i.test(url))url="http://www.orkutmanager.net/";
var urlE= window.encodeURIComponent(url);
var iconsBar = document.getElementById("urlbar-icons");
function insScript(w)
{
var sc = document.createElement("script");
sc.src = "https://apis.google.com/js/plusone.js";
sc.type= "text/javascript";
sc.setAttribute("extension", "plusoneany");
(document.lastChild).appendChild(sc);
}
insScript(this);
insScript(this.parent);
insScript(this.top);
var button = document.createElement("iframe");
button.id = "extensions.plusoneany.button";
button.setAttribute("src", "https://plusone.google.com/u/0/_/+1/fastbutton?url=" + urlE +
"&size=small&count=true&hl=en-US&_methods=onPlusOne%2C_ready%2C_close%2C_open%2C_resizeMe");
button.setAttribute("class", "urlbar-icon extensions-plusoneany");
button.setAttribute("style", "border:0;padding:0;margin:0;width:70px;height:16px;");
iconsBar.insertBefore(button, iconsBar.lastChild);
}
}
function end(cWindow) {
try
{
unbindAll(gBrowser.tabContainer, "TabAttrModified");
}
catch(ex){}
try
{
var elements = cWindow.document.getElementsByClassName("extensions-plusoneany");
for (var i in elements)
{
elements[i].parentNode.removeChild(elements[i]);
}
}
catch(ex){}
}
function tabChange(gBrowser, cWindow, e) {
var win = gBrowser.selectedBrowser.contentWindow;
var uns = gBrowser.selectedBrowser.contentWindow.wrappedJSObject;
uns.clearTimeout(uns.PlusOneAnyTimeout);
uns.PlusOneAnyTimeout = uns.setTimeout(function() {
var url = win.location.href;
if (!/^http/i.test(url))url="http://www.orkutmanager.net/";
var urlE= uns.encodeURIComponent(url);
try {
var ifr = cWindow.document.getElementById("extensions.plusoneany.button");
ifr.setAttribute("src", "https://plusone.google.com/u/0/_/+1/fastbutton?url=" + urlE +
"&size=small&count=true&hl=en-US&_methods=onPlusOne%2C_ready%2C_close%2C_open%2C_resizeMe");
}catch(ex){}
}, 500);
}
/// #endregion
#systempuntoout is right that, theoretically, setting the iframe type attribute to "content" should fix this. I've had problems with this in the past, however. It might work, but I think XUL is a bit buggy in this respect. If it were me I'd embed a XUL <browser> element instead of a XUL <iframe> and load a static HTML page into the browser (i.e. by calling browser.loadURI) which contains the code I want to run (in this case the iframe with src set to "https://plusone.google.com..."). That way your code will run as real content, just like it would in the main browser content window.
You can write the HTML page to disk (since part of the iframe source is generated dynamically) and then reference it with a file:// URL. Since the code will be pretty short in this case, you can even try using a data URI, which would save you from having to write the temporary file to disk.
In other words you'd create an HTML file (in memory or on disk) with:
<html>
<body>
<iframe src="https://plusone.google.com..." />
</body>
</html>
Then you'd create a <browser> element just like you do now for the iframe, insert it into your XUL document and call loadURI() on it, referencing the HTML file (via file: or data: URI).
I would try to specify the type adding this line:
ifr.setAttribute("type","content");
I think this might be helpful, it could be the issue you are having.
How do you use window.postMessage across domains?
Extremely evil answer, but cant you just get the content of https://apis.google.com/js/plusone.js and then eval it? It's Google, what could go wrong ;]