I'm currently building a HTML/JS AIR application. The application needs to display to the user a different 'window' - dependant on whether this is the first time they've launched the application or not. This part is actually fine and I have the code below to do that:
if(!startUp()) { // this simply returns a boolean from a local preferences database that gets shipped with the .air
// do first time stuff
var windowOptions = new air.NativeWindowInitOptions();
windowOptions.systemChrome = 'none';
windowOptions.type = 'lightweight';
windowOptions.transparent = 'true';
windowOptions.resizable = 'false';
var windowBounds = new air.Rectangle(300, 300, 596, 490);
var newHtmlLoader = air.HTMLLoader.createRootWindow(true, windowOptions, true, windowBounds);
newHtmlLoader.load(new air.URLRequest('cover.html'));
}
else {
// display default window
// just set nativeWindow.visible = true (loaded from application.xml)
}
However, what I want to be able to do is manipulate the html content from within cover.html after it has loaded up. There seems to be plenty of tutorials online of how to move, resize, etc. the NativeWindow, but I simply want access to the NativeWindow's HTML content.
For example, how would I add a new paragraph to that page? I've tried the following:
newHtmlLoader.window.opener = window;
var doc = newHtmlLoader.window.opener.document.documentElement;
Using AIR's Introspector console, ....log(doc) returns [object HTMLHtmlElement].
Hmm, seems promising right? I then go on to try:
var p = document.createElement('p');
var t = document.createTextNode('Insert Me');
p.appendChild(t);
doc.appendChild(p);
...but nothing gets inserted. I've also tried the following replacements for doc:
var doc = newHtmlLoader.window.opener.document.body; // .log(doc) -> [object HTMLBodyElement]
var doc = newHtmlLoader.window.opener.document; // .log(doc) -> Error: HIERARCHY_REQUEST_ERR: DOM Exception 3
...as well as the following with jQuery:
$(doc).append('<p>Insert Me</p>'); // again, nothing
So, anyone had any experience in accessing a NativeWindow's inner content programmatically? Any help will be greatly appreciated.
Hmm, so I think I may have found out how to do it...If we amend the original code and add an event listener on the loader:
var newHtmlLoader = air.HTMLLoader.createRootWindow(true, windowOptions, true, windowBounds);
newHtmlLoader.addEventListener(air.Event.COMPLETE, doEventComplete);
newHtmlLoader.load(new air.URLRequest('cover.html'));
You can then interact (assuming you're using jQuery) with the contents of the newly created window by using:
function doEventComplete(event) {
doc = $(event.currentTarget.window.document.body);
doc.append('<p>Insert Me!</p>')
}
:)
I'm not sure this has the effect you intended:
newHtmlLoader.window.opener = window;
var doc = newHtmlLoader.window.opener.document.documentElement;
What is does is set var doc = window.document.documentElement;, so 'doc' is your local document, not the one in the other window.
I think what you want is
var doc = newHtmlLoader.window.document.documentElement;
Do note that this will not work until the document has loaded.
Related
With the code below (intended for a bookmarklet), I am trying to open a new window, look for certain span-elements, and click each of them. However, I cannot access the code of the new window through XPath.
Inserting the code (copy & paste) of the clickElem function directly in the new tab works fine
CORS is not a problem since it's the same domain/subdomain/protocol
I've been also following this answer.
JavaScript:
const w = window.open('https://example.com', 'Example', 'width=500, height=500');
w.clickElem = () => {
const xpath = '//span[text()="Click here"]';
const selectedNodeElements = w.document.evaluate(xpath, document, null, XPathResult.ANY_TYPE, null);
let currentNode = selectedNodeElements.iterateNext();
while (currentNode) {
currentNode.click();
currentNode = selectedNodeElements.iterateNext();
}
}
setTimeout(w.clickElem, 8000);
When I try to access the text via currentNode.textContent I receive following error:
"Error in protected function: Cannot read properties of null (reading 'textContent')"
Grateful for every hint!
Are you sure you are already inside your tab? I'm thinking about something you need to get the active tab like this one?
I finally found my own mistake after going through my code on and on and coming across this answer. The .iterateNext() didn't work because the context node was wrongly set to document. Instead, it should be w.document to reference the newly opened window.
const selectedNodeElements = w.document.evaluate(xpath, w.document, null, XPathResult.ANY_TYPE, null);
I'm trying to dynamically change the style of a tab using the add-on SDK. How can I do this?
Here's what I have tried:
I can access tab objects like this:
var tabs=require('sdk/tabs');
tabs.on('ready',function(tab){
console.log('url is: '+tab.url); //-> url is http://www.google.com
console.log('stlye is: '+tab.url); //-> style is null
});
But the style attribute is null and none of the following work:
tab.setAttribute('style','background-color:blue'); // the method doesn't exist
tab.style.backgroundColor='blue'; // type error because style is null
tab.style='background-color:blue'; // has no effect
So how can I change the style of a tab dynamically? Another thing I have tried is converting the tab to a XUL object using code from the docs:
var { modelFor } = require("sdk/model/core");
var { viewFor } = require("sdk/view/core");
var tabs = require("sdk/tabs");
var tab_utils = require("sdk/tabs/utils");
function mapHighLevelToLowLevel(tab) {
// get the XUL tab that corresponds to this high-level tab
var lowLevelTab = viewFor(tab);
// now we can, for example, access the tab's content directly
var browser = tab_utils.getBrowserForTab(lowLevelTab);
console.log(browser.contentDocument.body.innerHTML);
// get the high-level tab back from the XUL tab
var highLevelTab = modelFor(lowLevelTab);
console.log(highLevelTab.url);
}
tabs.on("ready", mapHighLevelToLowLevel);
But the code throws an error: Module 'sdk/model/core' is not found at resource://gre/modules/commonjs/sdk/model/core.js
even though I followed the directions and created the core.js file. Also, I don't understand the what the curly braces are doing in the var { modelFor}= syntax.
You need to access the xul tab element.
Try this:
var tabsLib = require("tabs/tab.js");
var tab = tabsLib.getTabForWindow(htmlWindow);
tab.style.color = 'red'; //makes the tab text red
pass htmlWindow as the topmost window of the html document. So like document.defaultView.top. document.defaultView is the window of the document
if that doesnt work then just get the browser window, this example gets the most recent browser window (keeep in mind there may be multiple browser windows open (browser window is a firefox window that has tabs, [and maybe popup windows- im not sure in sdk])
const { getMostRecentBrowserWindow } = require('sdk/window/utils');
var aDOMWindow = getMostRecentBrowserWindow();
if (aDOMWindow.gBrowser && aDOMWindow.gBrowser.tabContainer) {
var tabs = aDOMWindow.gBrowser.tabContainer.childNodes;
for (var i=0; i<tabs.length; i++) {
tabs[i].style.color = 'red'; //makes the tab text red;
}
}
I have this weird problem on Chrome - on my page I am injecting elements using Mootools specifically a lightbox that contains a jwplayer video. The problem on chrome is that referring to an element ie. $('grid_01'); returns null the second time I click on it. To get around this I'm trying to test the element is null and re-inject it
var ss = $('holderdiv');
var x = $('mb_inline_0_-1');
if(x == null)
{
var el = new Element("div", {id: "mb_inline_0_-1"});
//if this line below runs without error...
ss.inject(el);
}
//.........why would x2 be null?
var x2 = $('mb_inline_0_-1');
Chrome says its null. Is there something I can do to ensure the DOM is updated ?
Thanks
You mean to use grab, not inject.
ss.grab(el); // Or el.inject(ss);
Your code has ss being injected into el, which is never attached to the DOM.
Injecting a newly created Element is written in the format newElement.inject(existingElement, position);.
So in your case, if you are planning to inject the newly created div (contained in el) into $('holderdiv'), it should be done thusly:
var el = new Element('div', {
. . .
});
el.inject('holderdiv', 'bottom'); // Although 'bottom' is assumed if nothing is passed
How can I send messages to or manipulate the contents of a NativeWindow instance from the parent window that created it?
I have read several places that to communicate between NativeWindow instances in the same application you need to "maintain a LocalConnection or write a whole whack of JavaScript". As it happens, I have no issue with writing a whole whack of JavaScript, but there doesn't seem to be any documentation on how to do it. Does anyone know what to do?
Thanks for any help you can give me!
Answering my own question here. "A whole whack of JavaScript" can be summed up in one ridiculous line:
var myWindow = air.NativeApplication.nativeApplication.openedWindows[intWindowCount].stage.getChildAt(0).window
myWindow.document.getElementById('status').innerHTML = "success";
This assumes you are using NativeWindow and loading HTML into using HTMLLoader and you're only loading one child. intWindowCount represents the number of opened windows (including the Introspector). 0 represents the number of children you created using the stage.addChild() method. The code I'm using is below in its entirety. There is likely some cleaning up to do, but it should be a good starting point for anyone that needs to do the same thing:
var htmlView = new air.HTMLLoader();
htmlView.width = 300;
htmlView.height = 500;
var objWindowOptions = new air.NativeWindowInitOptions();
objWindowOptions.transparent = false;
objWindowOptions.systemChrome = air.NativeWindowSystemChrome.STANDARD;
objWindowOptions.type= air.NativeWindowType.NORMAL;
var wWindow = new air.NativeWindow(objWindowOptions);
wWindow.x = objScreen.x;
wWindow.y = objScreen.y;
wWindow.width = objScreen.width;
wWindow.height = objScreen.height;
wWindow.activate();
wWindow.stage.align = "TL";
wWindow.stage.displayState = runtime.flash.display.StageDisplayState.FULL_SCREEN_INTERACTIVE;
wWindow.stage.scaleMode = "noScale";
wWindow.stage.addChild( htmlView );
htmlView.load( new air.URLRequest("pageTwo.html") );
setTimeout(function(){
objScreen.setWindowReference(air.NativeApplication.nativeApplication.openedWindows[intWindowCount].stage.getChildAt(0).window);
objScreen.setClock(cClock);
cClock.screen = objScreen;
},500);
The timeout at the end is a horrible, embarrassing hack. I'm only using it because I haven't found the right event yet to use with addEventListener().
I am trying to use popupNode in a little javascript based firefox extension. So if a user right click on a link and then clicks on an additional menu item a new tab opens with the link (sorta like "open in new tab"):
`
var foo = {
onLoad: function() {
// initialization code
this.initialized = true;
},
onMenuItemCommand: function() {
var tBrowser = document.getElementById("content");
var target = document.popupNode;
tBrowser.selectedTab = tab;
var tab = tBrowser.addTab(target);
}
};
window.addEventListener("load", function(e) { foo.onLoad(e); }, false);
`
It works mostly, but I am wondering in that is the right use. The problem is I want replace some characters on the var target, but somehow that partdoes not work. something like target.replace() will cause problems. So I am guessing target is not a string.
Mostly I would like to know what popupNode actually does ...
thanks
Peter
I haven't really used "popupNode", but in general nodes aren't the same as strings. I suggest reading up on the Document Object Model (DOM) to learn more.
As far as replacing text, assuming popupNodes work like other nodes then something like this may work for you:
var target = document.popupNode;
target.innerHTML = target.innerHTML.replace("old_string", "new_string")