How to get dynamic element HTML use Addon SDK with Timers? - firefox-addon-sdk

I want to scrape a page, the HTML content of this page auto change in a time frame. So i want to use pageMod and Timers of Addon Sdk to get the element innerHtml which change often.
Here are my scripts :
In main.js :
var tag = "container1";
var data = require("sdk/self").data;
var pageMod = require("sdk/page-mod");
var timer = require("sdk/timers");
var i = 0;
function scrapeData()
{
i = i + 1;
console.log("Begin pageMod : " + i);
pageMod.PageMod({
include: "*.test.com",
contentScriptFile: data.url("element-getter.js"),
contentScriptWhen: 'ready',
onAttach: function(worker) {
worker.port.emit("getElements", tag);
worker.port.on("gotElement", function(elementContent) {
console.log(elementContent);
});
}
});
console.log("End pageMod : " + i);
}
timer.setInterval(scrapeData, 10000);
And in data/element-getter.js :
self.port.on("getElements", function(tag) {
var elements = document.getElementById(tag);
self.port.emit("gotElement", elements.innerHTML);
});
After install this Firefox Add-on, when timers is running, it can only get the innerHtml one time, and the other time, it only display Begin pageMod and End pageMode in console log. Please help.

What you're currently doing is attaching the same page mod multiple times.
What you should do is move the timer inside the content script:
data/element-getter.js:
function scrapeData() {
var elements = document.getElementById(tag);
self.port.emit("gotElement", elements.innerHTML);
}
setInterval(scrapeData, 10000);
If you really want to keep the timer in the main page, then you need to maintain an array of worker instances, and loop through this array to emit your custom event. See this answer for more details.
(PS. Depending on your use case, the sdk/frame/hidden-frame module might be of interest.)

Related

Mapbox GL Popup .setDOMContent example

I'm trying to create a customized button to appear on a pop up which generates a dynamic link (a URL). I don't seem to be able to do this via the .setHTML because of the timing, can't bind a button to a function at runtime. So I thought I'd try the newish .setDOMContent
There's zero information online as to how this feature works. I'm wondering if anyone has an example of this where a button is added to the popup that can run a function and send data.
Here's my very poor attempt at setting this up.
This function creates the popup
function GameObjectPopup(myObject) {
var features = map.queryRenderedFeatures(myObject.point, {
layers: ['seed']
});
if (!features.length) {
return;
}
var feature = features[0];
// Populate the popup and set its coordinates
// based on the feature found.
var popup = new mapboxgl.Popup()
.setLngLat(feature.geometry.coordinates)
.setHTML(ClickedGameObject(feature))
.setDOMContent(ClickedGameObject2(feature))
.addTo(map);
};
This function adds the html via the .setHTML
function ClickedGameObject(feature){
console.log("clicked on button");
var html = '';
html += "<div id='mapboxgl-popup'>";
html += "<h2>" + feature.properties.title + "</h2>";
html += "<p>" + feature.properties.description + "</p>";
html += "<button class='content' id='btn-collectobj' value='Collect'>";
html += "</div>";
return html;
}
This function wants to add the DOM content via the .setDOMContent
function ClickedGameObject2(feature){
document.getElementById('btn-collectobj').addEventListener('click', function()
{
console.log("clicked a button");
AddGameObjectToInventory(feature.geometry.coordinates);
});
}
I'm trying to pipe the variable from features.geometry.coordinates into the function AddGameObjectToInventory()
the error I'm getting when clicking on an object (so as popup is being generated)
Uncaught TypeError: Cannot read property 'addEventListener' of null
Popup#setHTML takes a string that represents some HTML content:
var str = "<h1>Hello, World!</h1>"
popup.setHTML(str);
while Popup#setDOMContent takes actual HTML nodes. i.e:
var h1 = document.createElement('h1');
h1.innerHTML="Hello, World";
popup.setDOMContent(h1);
both of those code snippets would result in identical Popup HTML contents. You wouldn't want to use both methods on a single popup because they are two different ways to do the same thing.
The problem in the code you shared is that you're trying to use the setDOMContent to add an event listener to your button, but you don't need to access the Popup object to add the event listener once the popup DOM content has been added to the map. Here is a working version of what I think you're trying to do: https://jsfiddle.net/h4j554sk/

firefox extension content script not reload for multiple page?

i work with firefox addon sdk, i develop an extension and i have problem with content script i want to include content script in multiple page in same website whitout reload of script, with pagemod we can include script for all match pattern :
pageMod.PageMod({
include : "*.mywebsite.com",
contentScriptWhen: "ready",
contentScriptFile : "myscript.js"
});
but i don't find the way to load myscript.js in multiple page (for example : mywebsite.com/page1 or mywebsite.com/page2) whitout reload myscript.js ?
pageMod will load the script every time a tab/window is opened, page is refreshed or url changes and matches the requirements in the include.
you can try the tabs api. From the setup I threw together real quick, it will reload if the page is refreshed, a url is manually entered, but not when a user clicks a link.
in the "index.js" file
var self = require('sdk/self');
var data = require("sdk/self").data;
var tabs = require("sdk/tabs");
tabs.on('ready', function (tab) {
var url = tab.url;
if(url.indexOf('https://www.facebook.com') != -1){
tab.attach({
contentScriptFile: ['./index.js']
});
}
});
in the content script file I had a timer outputting to the console.
var cnt = 0;
setInterval(function(){
unsafeWindow.console.log(cnt);
cnt++;
}, 1000);

How do I force iron-router to react when the hash has not apparently changed?

When using iron-router to scroll to a hash using an anchor button referencing the id of the next section, such as this:
<a class="button" href="{{pathFor 'home' hash='about'}}">
iron-router happily takes us to the about section the first time the button is clicked.
if you scroll back up using the mouse and click the same button a second time, no scrolling takes place.
I presume this is because the destination is apparently the same as the current router location, hence no reaction is triggered.
How can I force a reaction?
I've tried clearing the hash in the window.location in an override to the scrollToHash function:
Router._scrollToHash = function(hash) {
var section = $(hash);
if (section.length) {
var sectionTop = section.offset().top;
$("html, body").animate({
scrollTop: sectionTop
}, "slow");
}
window.location.hash = '';
};
And this allows a second click but no more, which has me puzzled.
Setting window.location.hash to a space rather than an empty string works:
Router._scrollToHash = function(hash) {
var section = $(hash);
if (section.length) {
var sectionTop = section.offset().top;
$("html, body").animate({
scrollTop: sectionTop
}, "slow");
}
window.location.hash = ' ';
};
Probably setting it to a non-existent hash would also.
In addition to cover cases where there is a menu link that can be activated from a second page to take you to the hash you need this package:
meteor add okgrow:iron-router-autoscroll
Used in combination it covers every case I have thought of.

Triggering shouldStartLoadWithRequest with multiple window.location.href calls

Im trying to pass multiple things from a webpage inside a UIWebView back to my iPhone app via the shouldStartLoadWithRequest method of the UIWebView.
Basically my webpage calls window.location.href = "command://foo=bar" and i am able to intercept that in my app no problem. Now if i create a loop and do multiple window.location.href calls at once, then shouldStartLoadWithRequest only appears to get called on once and the call it gets is the very last firing of window.location.href at the end of the loop.
The same thing happens with the webview for Android, only the last window.location.href gets processed.
iFrame = document.createElement("IFRAME");
iFrame.setAttribute("src", "command://foo=bar");
document.body.appendChild(iFrame);
iFrame.parentNode.removeChild(iFrame);
iFrame = null;
So this creates an iframe, sets its source to a command im trying to pass to the app, then as soon as its appended to the body shouldStartLoadWithRequest gets called, then we remove the iframe from the body, and set it to null to free up the memory.
I also tested this on an Android webview using shouldOverrideUrlLoading and it also worked properly!
I struck this problem also and here is my solution that works for me.
All my JavaScript functions use this function __js2oc(msg) to pass data
and events to Objective-C via shouldStartLoadWithRequest:
P.S. replace "command:" with your "appname:" trigger you use.
/* iPhone JS2Objective-C bridge interface */
var __js2oc_wait = 300; // min delay between calls in milliseconds
var __prev_t = 0;
function __js2oc(m) {
// It's a VERY NARROW Bridge so traffic must be throttled
var __now = new Date();
var __curr_t = __now.getTime();
var __diff_t = __curr_t - __prev_t;
if (__diff_t > __js2oc_wait) {
__prev_t = __curr_t;
window.location.href = "command:" + m;
} else {
__prev_t = __curr_t + __js2oc_wait - __diff_t;
setTimeout( function() {
window.location.href = "command:" + m;
}, (__js2oc_wait - __diff_t));
}
}
No, iframe's url changing won't trigger shouldOverrideUrlLoading, at least no in Android 2.2.

YUI AutoComplete Example Problem

I was hunting for an implementations of YUI AutoComplete and I came across this script from the site asklaila.com -
<script type="text/JavaScript">
YAHOO.example.ACJson = new function() {
this.oACDS = new YAHOO.widget.DS_XHR("/AutoComplete.do",
["Suggestions[0].Results","Name"]);
this.oACDS.queryMatchContains = true;
this.oACDS.scriptQueryAppend = "city=Mysore"; // Needed for YWS
function fnCallback(e, args) {
document.searchForm.where.focus();
acSelected = true;
return false;
}
this.oAutoComp = new YAHOO.widget.AutoComplete('what','whatContainer', this.oACDS);
this.oAutoComp.itemSelectEvent.subscribe(fnCallback);
this.oAutoComp.formatResult = function (oResultItem,sQuery) {
return oResultItem[0];
}
this.oAutoComp.queryDelay = 0;
this.oAutoComp.useIFrame = true;
this.oAutoComp.prehighlightClassName = "yui-ac-prehighlight";
this.oAutoComp.minQueryLength = 2;
this.oAutoComp.autoHighlight = false;
this.oAutoComp.textboxFocusEvent.subscribe(function() {
this.oAutoComp.sendQuery("");
});
this.oAutoComp.doBeforeExpandContainer = function(oTextbox, oContainer, sQuery, aResults) {
var pos = YAHOO.util.Dom.getXY(oTextbox);
pos[1] += YAHOO.util.Dom.get(oTextbox).offsetHeight + 2;
YAHOO.util.Dom.setXY(oContainer,pos);
return true;
};
}
</script>
It is implenting the YUI AutoComplete Dropdown. What I want to understand is what this
this.oACDS = new YAHOO.widget.DS_XHR("/AutoComplete.do", ["Suggestions[0].Results","Name"]);
does and its effects on code.
That's using an older version of YUI, but it is setting up a DataSource for the autocomplete to read from. This particular DataSource uses XHR to request information from the server to populate the autocomplete field.
"Autocomplete.do"
Is a relative URL that is being queried by the DataSource every time the autocomplete fires while the user is typing.
["Suggestions[0].Results","Name"]
Is the responseSchema that tells the DataSource how to parse the results from the request to the URL. It needs to know how to parse the data so that it can show the proper results.
this.oACDS = new YAHOO.widget.DS_XHR("/AutoComplete.do", ["Suggestions[0].Results","Name"]);
On every key press, it fetches a json response from the server, and uses it to populate the autocomplete dropdown. The json contains names to display only at this node, "Suggestions[0].Results" in the "name" field.
If you have any trouble, ask ahead. I wrote that piece of code for asklaila.com
I was hunting for implementations of
YUI Autocomplete and I came across
this script...
Why not take a look at YUI AutoComplete page for in-depth examples.
Yahoo! UI Library: AutoComplete