Tab independent jQuery on Firefox extension - javascript

I'm developping a Firefox based on jQuery as described in this Answer here.
After implementing the example provided in the answer, eveything works fine, but the problem is the code between Firefox Tabs is somehow linked, and example.doc always refers to the last opened tab.
Opened tab1 : the plugin-example has been added and to the current page.
this.doc refers to tab1.
Oepened tab2: the plugin-example has been added to to current page (tab2).
this.doc now refers to tab2
back to viewing tab1 : this.doc still refers to tab1.
clicking on plugin-example on tab1 will act on the plugin-example in tab2 instead.
How can I make my code independent between tabs?
Here is an excrept from the code:
(function() {
jQuery.noConflict();
$ = function(selector,context) {
return new jQuery.fn.init(selector,context||example.doc);
};
$.fn = $.prototype = jQuery.fn;
example = new function(){};
example.run = function(doc,aEvent) {
if (doc.getElementById("plugin-example")) return;
this.doc = doc;
this.main = main = $('<div id="plugin-example">').appendTo(doc.body).html('Example Loaded!');
this.main.click(function() { //<--- added this function
example.main.html(example.doc.location.href);
});
main.css({
background:'#FFF',color:'#000',position:'absolute',top:0,left:0,padding:8
});
};
// Bind Plugin
var delay = function(aEvent) {
var doc = aEvent.originalTarget; setTimeout(function() {
example.run(doc,aEvent);
}, 1);
};
var load = function() {
gBrowser.addEventListener("DOMContentLoaded", delay, true);
};
window.addEventListener("pageshow", load, false);
})();

Your code (overlay script) will only run once per window, not once per tab. So there is only one example instance per window. And hence example.doc will be set to whatever dispatched DOMContentLoaded last.
Your function should properly close over the document and avoid global state.
This is who I would write it (then again, I would avoid jquery (in add-ons) like the plague...)
// Use strict mode in particular to avoid implicitly var declarations
(function() {
"use strict";
// Main runner function for each content window.
// Similar to SDK page-mod, but without the security boundaries.
function run(window, document) {
// jquery setup. per https://stackoverflow.com/a/496970/484441
$ = function(selector,context) {
return new jq.fn.init(selector,context || document);
};
$.fn = $.prototype = jq.fn;
if (document.getElementById("my-example-addon-container")) {
return;
}
let main = $('<div id="my-example-addon-container">');
main.appendTo(document.body).text('Example Loaded!');
main.click(function() { //<--- added this function
main.text(document.location.href);
});
main.css({
background:'#FFF',color:'#000',position:'absolute',top:0,left:0,padding:8
});
};
const log = Components.utils.reportError.bind(Components.utils);
// Do not conflict with other add-ons using jquery.
const jq = jQuery.noConflict(true);
gBrowser.addEventListener("DOMContentLoaded", function load(evt) {
try {
// Call run with this == window ;)
let doc = evt.target.ownerDocument || evt.target;
if (!doc.location.href.startsWith("http")) {
// Do not even attempt to interact with non-http(s)? sites.
return;
}
run.call(doc.defaultView, doc.defaultView, doc);
}
catch (ex) {
log(ex);
}
}, true);
})();
Here is a complete add-on as a gist. Just drop in a copy of jquery and it should be good to go.
PS: Reposted this at in the jquery in extensions question

Related

How to get Chrome to throw exception when changing url if it fails

I have a reference to a new window opened with js
var theNewTab="";
theNewTab = window.open(theURL, 'winRef');
then I change the url in the as the user clicks on another link in the parent window using
theNewTab.location.href = targetLink;
theNewTab.focus();
The problem i'm having with chrome that id doesn't throw exception if the the window doesn't exist anymore "closed" unlink FF & IE which im using to open the window again.
try {
theNewTab.location.href = targetLink;
theNewTab.focus();
}catch(err) {
theNewTab = window.open(theURL, 'winRef');
theNewTab.focus();
}
PS: I tried to use "window.open" every time but if the window already open, id does not reload the page or it does but it doesn't re-execute the script I have in document ready I think.
I'm not sure what you need.
<script type="text/javascript">
var theNewTab = null;
function openNewTab(theURL) {
if (theNewTab == null || theNewTab.closed == true) {
theNewTab = window.open(theURL);
} else {
theNewTab.location.href = theURL;
}
theNewTab.focus();
};
// use the function when you need it
$('a').click(function() {
openNewTab($(this).attr('href'));
});
</script>
Is this example helpful for you?

How to code for onMouseOver event in Autodesk Forge

I have created a simple extension in autodesk forge.
The idea is that when I make a mouse-over event on a 3D object it has to show me the ID of the hovered object/sub-object my extension code runs like this.
AutodeskNamespace("Autodesk.ADN.Viewing.Extension");
Autodesk.ADN.Viewing.Extension.MouseEvent = function (viewer, options) {
Autodesk.Viewing.Extension.call(this, viewer, options);
var _self = this;
var _viewer = viewer;
var _selectedId = null;
//On Load of the exension function
_self.load = function () {
_viewer.addEventListener(
Autodesk.Viewing.MOUSE_OVER_EVENT,
_self.onMouseOver);
console.log("Autodesk.ADN.MouseEvent loaded");
return true;
};
//On unload of the exension function
_self.unload = function () {
_viewer.removeEventListener(
Autodesk.Viewing.MOUSE_OVER_EVENT,
_self.onMouseOver);
console.log("Autodesk.ADN.MouseEvent unloaded");
return true;
};
// Event function initialization
_self.onMouseOver = function (event) {
var dbId = event.dbIdArray[0];
if (typeof dbId !== 'undefined') {
_selectedId = dbId;
alert('ID: ' + _selectedId);
}
else _selectedId = null;
}
};
Autodesk.ADN.Viewing.Extension.MouseEvent.prototype =
Object.create(Autodesk.Viewing.Extension.prototype);
Autodesk.ADN.Viewing.Extension.MouseEvent.prototype.constructor =
Autodesk.ADN.Viewing.Extension.MouseEvent;
Autodesk.Viewing.theExtensionManager.registerExtension(
'Autodesk.ADN.Viewing.Extension.MouseEvent',
Autodesk.ADN.Viewing.Extension.MouseEvent);
but the onMouseOver function is not working, can anyone please help me? thanks in advance.
PS: I have included the extensions in the script tags and the extension is loaded likewise.
oViewer.loadExtension('Autodesk.ADN.Viewing.Extension.MouseEvent');
I also get a confirmation from the console that the extension is loaded successfully.
There is no such event as Autodesk.Viewing.MOUSE_OVER_EVENT ... did you just made that up or you got it from some - apparently incorrect - source?
The way to handle that would be to use a viewer tool (see that post for details), then in handleMouseMove callback, do the following:
handleMouseMove (event) {
var hitTest = _self.viewer.clientToWorld(
event.canvasX,
event.canvasY,
true)
if (hitTest) {
console.log(hitTest)
}
}
Here is another post I wrote about viewer events, it is a bit old, so there are a couple more now but can give you a good starting point.

Opening links in phonegap's inAppBrowser

i'm having difficulties trying to open up links in phonegap's (cordova's) inAppBrowser.
I need to be able to make it so the IAB can open both local phonegap links (file:///android_assets/www/example.html) and online links (https://www.google.com for instance)
I have achieved this so far by creating an object in index.js which creates an initiate IAB object, and then for every call from there onwards I was thinking that I could just open that link.
I've found a number of problems, for example when I try to use window.location = some_url it will change the index.html page to that some_url and hence my attached JS no longer works.
I've tried injecting a window.location from my index.html's JS into the IAB document however that breaks down when trying to fetch a local file
My current method is causing a memory leak because it's continuously opening IAB objects which I'm then having difficulties closing because ...
Using the .close() method on an IAB object shows (according to Chrome's Remote Devices view) that it doesn't actually delete the IAB window but rather turns it into an about:new tab
Heres my current code for the JS which is called in index.js (im sorry i know there's a lot of redundant code!)
var interceptor = {
// library dictionary object of <K,V> pairs in the form of <current page, desired page>
// insert your k,v pairs here:
library: {
'login/signup.php': 'example.html', //test data
'calendar/view.php': 'example.html',
'course/view.php?id=10': 'example.html'
},
// origin dictionary
// dictionary for redirecting from the phonegap page to the moodle page
// must be explicit in your page names and avoid duplication
// e.g. use full path notation -> full/path/to/file.html not file.html
origin: {
'current/attempt': ['example1.html', 'example2.html', 'course/index.php'] //test data
},
history_stack: [],
// stack of all current windows
browser_windows: [],
is_back: 'false',
windows: 0,
redirect_flag: false,
first: true,
currentWindow: null,
currentLocalFile: null,
get_origin: function() {
return this.origin;
},
get_library: function() {
return this.library;
},
// interceptor constructor
initialize: function(old_ref = null, default_url = config.moodleURL, android = true) {
// interval for checking our back flag
// the scope changes when you enter the anonymous closure and this changes. this is a hack for that
var self = this;
var ref;
console.log('default url: ' + default_url);
// check for android
if ((android) && (self.windows < 1)) {
// self.browser_windows[0] = cordova.InAppBrowser.open(default_url, '_blank', 'location=no,zoom=no');
ref = cordova.InAppBrowser.open(default_url, '_blank', 'location=yes,zoom=no');
self.first = false;
}
// otherwise iOS
else if ((!android) && (self.windows < 1)) {
// self.browser_windows[0] = cordova.InAppBrowser.open(default_url, '_blank', 'location=no,zoom=no,suppressesIncrementalRendering');
ref = cordova.InAppBrowser.open(default_url, '_blank', 'location=no,zoom=no,suppressesIncrementalRendering');
// self.browser_windows.push(ref);
self.first = false;
} else {
old_ref =
// old_ref = cordova.InAppBrowser.open(default_url, '_blank', 'location=yes,zoom=no');
ref = old_ref;
}
self.windows++;
var library_dictionary = this.get_library();
var origin_dictionary = this.get_origin();
var redirect_URL;
ref.addEventListener('loadstart', function(event) {
// push current page to histoy stack
self.history_stack.push(event.url);
// check to see if an element of one of our origin arrays is in the URL
for (var origin_list in origin_dictionary) {
if (event.url.includes(origin_list)) self.redirect_flag = true;
for (var elements in origin_dictionary[origin_list]) {
// if it is raise a flag and store the key that its array maps to
if ((event.url.includes(origin_dictionary[origin_list][elements])) && (self.redirect_flag)) {
redirect_URL = origin_list;
self.redirect_flag = false;
var temp_previous_pages = self.history_stack;
var temp_element;
// pop elements of stack until empty
while (temp_previous_pages.length !== 0) {
temp_element = temp_previous_pages.pop();
// if we find an element in the stack (our URL history) that matches the key that our array mapped to
if (temp_element.indexOf(redirect_URL) !== -1) {
// redirect and break from loop
self.initialize(ref, temp_element, android);
break;
}
}
}
}
}
for (var key in library_dictionary) {
if (event.url.includes(key)) {
self.initialize(ref, library_dictionary[key], android);
break;
}
}
});
ref.addEventListener('loadstop', function() {
// when we've finished loading our page set up an interval every 2.5 seconds to check
// if we've been signalled to change pages
console.log('here');
var is_back_interval = setInterval(function() {
ref.executeScript({
code: "localStorage.getItem('is_back')"
}, function(values) {
if (values[0] === 'true') {
// if we have been signalled then remove that signal from our html or moodle page
ref.executeScript({
code: "localStorage.setItem('is_back', '')"
});
// get 3rd last since the last will be the current page as will the 2nd last
prev_page = self.history_stack[self.history_stack.length - 3];
self.initialize(ref, prev_page, android);
}
});
}, 2500);
});
}
};
and heres index.js
var app = {
// Application Constructor
initialize: function() {
this.bindEvents();
},
// Bind Event Listeners
//
// Bind any events that are required on startup. Common events are:
// 'load', 'deviceready', 'offline', and 'online'.
bindEvents: function() {
document.addEventListener('deviceready', this.onDeviceReady, false);
},
// deviceready Event Handler
//
// The scope of 'this' is the event. In order to call the 'receivedEvent'
// function, we must explicitly call 'app.receivedEvent(...);'
onDeviceReady: function() {
app.receivedEvent('deviceready');
},
// Update DOM on a Received Event
receivedEvent: function(id) {
if (window.device.platform === "iOS"){
interceptor.initialize(config.moodleURL, false);
} else {
interceptor.initialize();
}
}
};
example.html: https://gist.github.com/anonymous/28a78ab0879878d7a9dac8eb89544cda

document.execCommand in iframe don't work

I'm working on my WYSIWYG editor. And I have code like this:
doc.execCommand(cmd, false, null);
The cmd argument would be 'Bold', 'Italic', etc. And the doc variable refer to the document in the iframe, which is initialized somewhere else:
doc = iframe1.contentWindow.document;
It works fine in Chrome, but in IE(my is IE9) doesn't work at all.
I debugged my code with the developer tool and found nothing wrong, the execCommand function just doesn't work.
I have searched through the Internet and couldn't found an available solution.
Would anyone give me a help?
Code sample:
function $see(e, o) {
var that = this;
...
this.e = $see.make('iframe', { 'class': 'editor' }); // editor iframe
this.e.onload = function () { // call when the document is loaded
var d = that.e.contentWindow || that.e.contentDocument;
if (d.document) d = d.document;
that.doc = d;
that.doc.write('<html><head></head><body></body></html>');
that.doc.body.innerHTML = that.ta.value; // that.ta refers to an textarea
that.doc.body.setAttribute('contenteditable', 'true');
...
};
}
$see.prototype.exec = function (cmd) {
// call in an <a> tag's onclick event outside the iframe
this.doc.execCommand(cmd, false, null);
};
That is because there are different methods to work with iframe in different browsers
here is how it should work
var doc= iframe1.contentWindow || iframe1.contentDocument;
if (doc.document)
doc=doc.document;
UPDATE
ok i think i made a little mistake here is how it should look like:
var doc = iframe.contentWindow || iframe.contentDocument.defaultView;
if (doc.document)
doc=doc.document;

Transform any JavaScript function into a page event

I need to be able to achieve the following (one way or another):
function ShowContent() {}
document.onShowContent = function ()
{
// anything I want to happen....
}
What I'm trying to do is to add a kind of listener to me Advertisement code on the page that will auto refresh the ad slot when a specific function is called. Instead of having that function "ShowContent()" directly refresh the ad code, I want the ad code to refresh if it detects that "ShowContent()" has been called.
Thanks.
Modern javascript libraries make this easy. You can do it "by hand" of course, but here's a quick example with jQuery
First, the listener
$(document).bind( 'ShowContent', function()
{
// anything you want
});
Then the trigger
$(document).trigger( 'ShowContent' );
You could even go this route if you want
function ShowContent()
{
$(document).trigger( 'ShowContent' );
}
Here is a quick sample i threw together
var ev = (function(){
var events = {};
return {
on: function(name, handler){
var listeners = (name in events) ? events[name] : (events[name] = []);
listeners.push(handler);
},
raise: function(name){
var listeners = events[name];
if (listeners) {
var i = listeners.length;
while (i--) {
listeners[i]();
}
}
}
};
})();
// add a listener
ev.on("foo", function(){
alert("bar");
});
If you cannot manually alter the method in question to trigger the event, then you can 'wrap' it.
function methodIHaveNoControlOver(){
....
}
// intercept the call
var originalFn = methodIHaveNoControlOver;
// here we replace the FunctionDeclaration with a FunctionExpression containing a reference to the original FunctionDeclaration
methodIHaveNoControlOver = function(){
originalFn();
ev.raise("foo");
};
But note that this will not work if methodIHaveNoControlOver uses this to reference anything; so that will require more work.

Categories