chrome-app won't recognize id's that work fine when run by the chrome browser - debugging

I'm converting a standard browser based app that's working fine to a chrome-app.
Once the page loads up, it has already hit an error - Uncaught TypeError: Cannot call method 'appendChild' of null. This occurs after several hundred lines of JS have done their job but its the first time the code makes a reference to the document object, specifically document.getElementById('mainDiv').appendChild(...).
I can clearly see the div with the id="mainDiv" in the debuggers elements tab. Yet, document.getElementById('mainDiv') must be returning a null. Any attempt at putting in breakpoints fails as they are ignored. I've added them to the line that fails as well as to lines that lead up to it and breakpoints are never triggered. I've read some of the threads on SO and I'm certain the breakpoints issue is just a bug in the debugger, but not recognizing an id when I can clearly see it and the code when run in the browser works fine leaves me wondering what's going on. Is document in the browser different from document in the app version?
Any ideas?
If I choose "inspect background page", the breakpoints work but it still fails but in a different way. The elements tab does NOT show my html page, but the pseudo generated background one and I can't get the debugger to show my page at all.
Any enlightenment would be appreciated. I've searched and read what I could find, but much of the docs are clearly out of date.

You seem to be accessing the document object of the background page, instead of that of your POS.html file.
Try this:
chrome.app.window.create('POS.html',{
'bounds': {
'width': screen.availWidth,
'height': screen.availHeight
}
}, function(appWin) {
var pageWindow = appWin.contentWindow;
var pageDocument = pageWindow.document;
pageWindow.addEventListener('load',function() {
// now use
pageDocument.getElementById('yourid');
// instead of
document.getElementById('yourid');
},false);
});
Also to inspect elements in your page right-click anywhere in the app window and select Inspect Element (this works only when the app was loaded as an 'unpacked extension')
Alternatively you can navigate to chrome://extensions and click the page link next to your app entry.

As lostsource mentioned, you're probably accessing the wrong DOM's document. You should think about the javascript in your app running in different global contexts, one for each page. There is (at a minimum) a page for the background page, and a page for each window.
Each of these pages runs in its own global context. This means global variables like document and window are different.
In the background page will be scripts which you load via the background manifest tag. When you open a window, it can also load its own script via script tags (make sure you do not use inline or block script tags, but use script src="foo.js". See http://developer.chrome.com/apps/contentSecurityPolicy.html).
The code that runs in the callback to chrome.app.window.create runs in the background page's context, so its document variable is for the background page's DOM, which is usually empty. Instead you can make it refer to the window's DOM using win.contentWindow as lostsource suggested, or add a page.js file with the script in it, and include it from the page via a script src='page.js' tag.

Is your call occurring after the load event, e.g. the JS called in a function set on window.onload?

Related

Link directly to a notebook page in a view

I have an view that extends the current project view, where we add multiple tabs (notebook pages) to show information from other parts of a project.
One of these pages is an overview page that summarizes what is under the other tabs, and I'd like to link the headlines for each section directly to each displayed page. I've currently solved this by using the index of each tab and calling bootstrap's .tab('show') method on the link within the tab:
$(".overview-link").click(function (e) {
e.preventDefault();
var sel = '.nav-tabs a:eq(' + $(this).data('tab-index') + ')';
$(sel).tab('show');
});
This works since I've attached a data-tab-index="<int>" to each header link in my widget code, but it's brittle - if someone adds a tab later, the current indices will be broken. Earlier I relied on the anchor on each tab, but that broke as well (and would probably break if a new notebook page were inserted as well).
Triggering a web client redirect / form link directly works, but I want to show a specific page in the view:
this.do_action({
type: 'ir.actions.act_window',
res_model: 'my.model.name',
res_id: 'my.object.id',
view_mode: 'form',
view_type: 'form',
views: [[false, 'form']],
target: 'current'
});
Is there any way to link / redirect the web client directly to a specific notebook page tab through the do_action method or similar on FormWidget?
If I understood well you want to select the tab from the JavaScript (jQuery) FormWidget taking into account that the id could change if anybody install another module that adds another tab
Solution 0
You can add a class to the page in the xml form view. You can use the id of the element selected by this class name in order to call the right anchor and select the right tab item. This should happen when the page is completely loaded:
<page class="nb_page_to_select">
$('a[href=#' + $('.nb_page_to_select').attr('id') + ']').click()
NOTE: As you have said the following paragrah I assume that you know where to run this instruction. The solution I suggest is independent of the index.
This works since I've attached a data-tab-index="<int>" to each
header link in my widget code, but it's brittle - if someone adds a
tab later, the current indices will be broken. Earlier I relied on the
anchor on each tab, but that broke as well (and would probably break
if a new notebook page were inserted as well).
Solution 1
When the page is loaded you can get the tab list DOM object like this:
var tablist = $('ul[role="tablist"]')
And then you can click on the specifict tab, selecing by the text inside the anchor. So you don't depend on the tab index:
tablist.find('a:contains("Other Information")').click()
I think if you have two tabs with the same text does not make any sense, so this should be sufficient.
Solution 2
Even if you want to be more specific you can add a class to the notebook to make sure you are in the correct notebook
<notebook class="nt_to_change">
Now you can use one of this expressions in order to select the tab list
var tablist = $('div.nt_to_change ul.nav-tabs[role="tablist"]')
// or
var tablist = $('div.nt_to_change ul[role="tablist"]')
Solution 3
If the contains selector doesn't convince you because it should be equal you can do this as well to compare and filter
tablist.find('a').filter(function() {
return $.trim($(this).text()) === "Other Information";
}).click();
Where "Other Information" is the string of the notebook page
I didn't tried the solution I'm giving to you, but if it doesn't work at least may be it makes you come up with some idea.
There's a parameter for XML elements named autofocus (for buttons and fields is default_focus and takes 1 or 0 as value). If you add autofocus="autofocus" to a page in XML, this page will be the displayed one when you open the view.
So, you can try to add this through JavaScript, when the user clicks on the respective link -which honestly, I don't know how to achieve that by now-. But you can add a distinctive context parameter to each link in XML, for example context="{'page_to_display': 'page x'}". When you click on the link, I hope these context keys will arrive to your JS method.
If not, you can also modify the fields_view_get method (here I wrote how to do that: Odoo - Hide button for specific user) to check if you get the context you've added to your links and add the autofocus parameter to the respective page.
As you said:
This works since I've attached a data-tab-index="" to each header
link in my widget code, but it's brittle - if someone adds a tab
later, the current indices will be broken.
I assume that your app allow multi-user interaction in realtime, so you have to integrate somewhere in your code, an update part function.
This function will trig if something has changed and cleanout the data to rebuilt the index in order to avoid that the current indices will be broken.

Firefox Add-on SDK context-menu communicate with a content script loaded by page-mod

I'm creating a Firefox add-on with the Firefox Add-on SDK. This add-on does two things:
Inject a content script into every page with sdk/page-mod.
Add a context menu item using sdk/context-menu.
I want that when user clicks the context menu item, the add-on will call functions in the content script which was loaded by PageMod().
Unless your page-mod script is doing other things, it sounds like it might be more appropriate to load it using the context-menu contentScript or contentScriptFile properties. Alternately, load the portions of it that are needed by the context menu using this methodology. How best to split the script you are using depends on what you are actually doing. Without more information from you it is difficult to provide specific recommendations.
Communicating between content scripts loaded at different times or by different methods:
There is no method of directly doing what you desire. Content scripts that are not loaded at the same time by the same methodology are loaded into different contexts. They are unable to directly call functions between them. Multiple content scripts which are loaded at the same time and the same methodology share the same context/scope and can directly call functions between them.
However, you can communicate between content scripts. If they are not loaded into the same page, then you will need to communicate from one content script to another by using your main add-on script to first receive a message from one content script. Then, your main add-on script will need to send a second message (potentially containing exactly the same data) to the second content script. In other words, your main add-on code would need to relay the message between the two content scripts.
For content scripts that are loaded into the same page via different methods (e.g. one with page-mod and another as a context menu item – the situation in which you are interested), you can communicate directly between them using the DOM postMessage() API or a CustomEvent. Either can be used to send whatever JSON serializable data you desire between the two scripts. The DOM postMessage() API provides for more security, but is a bit more complex. With it you must also filter out any other "message" events that are sent on it by random code. It should probably be used if you are going to have code in a released add-on execute functions based on the content of the messages. This is a security issue which will depend on exactly what you are doing with the messages.
Example:
The following code will load a page-mod script into every page that matches "*.mozilla.org". It also creates a context menu item in those same pages which is displayed on links. Clicking on the context menu item will send an event from the context-menu content script with data containing the URL for which the context menu was displayed. The custom event will be received by the page-mod script. The page-mod script will then issue an alert with the URL for the link.
var pageMod = require("sdk/page-mod");
pageMod.PageMod({
include: "*.mozilla.org",
contentScript: 'function contextMenuAlert(href) {'
+ ' window.alert("The context menu click on a link with URL:\\n" + href);'
+ '};'
+ 'window.addEventListener("myAddonId-contextMenu-clicked",'
+ ' function(event){contextMenuAlert(event.detail);});'
});
//Context menu
let cm = require("sdk/context-menu");
cm.Item({
label: "Alert link URL",
context: [
cm.URLContext(["*.mozilla.org"]),
cm.SelectorContext("a[href]")
],
contentScript: 'self.on("click", function (node, data) {'
+ ' var event = new CustomEvent("myAddonId-contextMenu-clicked",'
+ ' {detail:node.href});'
+ ' window.dispatchEvent(event);'
+ '});'
});
The above code produces a context menu that looks like:
When clicked on, the page-mod added content script initiates the following alert:
Using the message sent to choose from multiple different functions:
The information passed through the event can be expanded to allow multiple different functions to be called depending on the content. One method of doing this is to send an object as the message. One property of the object can be the function desired and another can be data to use in that function. My answers to the following questions contain examples of doing this:
Add menu item created with the sdk/context-menu API to the top of the context menu: This answer has code which uses the same passed message to indicate that either a click was made on a context menu item and pass the URL on which the context menu item was clicked, or to tell the main script that the context menu is about to be displayed so it can be modified.
How to console.log from ChromeWorker (alternative to dump): This answer shows sending a message that will result in a call to one of a variety of different functions and pass data to the function which was called. It was implemented as a way of using console methods from a worker with just console.log("message"). I'd code this one a bit differently were I doing it today, but it works and demonstrates the concept.
Because the documentation on MDN (here and here) was not very clear on content script to content script communication, I have updated the pages I found on which it was discussed. I have also added the above code as an example.

Can I delay loading of some controls on an xPage to after page loaded

is it possible to delay loading of some controls on an xpage?
This is the problem: let's say you have a control that does a fultextsearch and displays the result in a repeat control. this ft search might take a long time and will hold the webpage loading in a waiting state until the search result is ready.
I want my page to load most of the data initally, and some "time consuming" controls should be loaded in to the page as a sperate request after the inital load.
this way the user will immediatly see the webpage, but some of the data on the page will load a little bit later without holding the webpage in a waiting state from the server.
possible?
The downside to using rendered is that all the value bindings will still evaluate, even if the corresponding markup isn't sent to the page. So the trick here is making sure the components don't even exist until you want them to.
Every component has a getChildren() method. This returns a mutable List of components, which has a add() method. This allows you to add components to the page on the fly, either while the page is loading, or later during an event. For the purposes of what you're trying to do, you would want to defer adding the "expensive" components until a subsequent event.
Create an event handler attached directly to the view root (), give it a unique ID (e.g. "loadExpensiveComponentsEvent", set its refresh mode to partial, set a refresh ID to whatever div or panel will contain the search results, and set its event name to an arbitrary event (e.g. "loadExpensiveComponents"). This prevents your event from being triggered by actual user behavior. Set the event's code to SSJS that will inject your components.
Then add a script block () to trigger the event after the page has loaded:
XSP.addOnLoad(function(){
XSP.firePartial(null, "#{id:loadExpensiveComponentsEvent}");
});
Your page will load without the search result components. Once the page has fully loaded, it will trigger the component injection event automatically.
For guidance on how to code the injection event, open the Java file that has been generated from your existing page to see what components need to be injected and what to set their values to.
You can pack them into a panel and set their rendered status to rendered=#{viewScope.pageFullyLoaded}. Then in the onLoad event have a XSP. partialRefresh request where you set viewScope.pageFullyLoaded=true
A little ugly but doable. Now you can wrap that code into your own custom control, so you could have a "lazyGrid", "lazyPanel" etc.
Not sure why I did not think of this before. the dynamic content control in extlib actually solves this problem. the dcc can be triggered onClientLoad both using javascript and ssjs afer the page has loaded.
one problem I am facing now is that I am already using the dcc on my site so I need to put another dcc within my dcc. and this seem to be a bit buggy. I have reported it to the extlib team on openNTF.

Watir and Ajax requests

In my webapp I have a simple textfield. To this textfield I have a jQuery function which will be always executed on every keyup. With this function there is an Ajax request assigned which loads every time the result of the SQL-Query. My code is equivalent to the code of
RailsCasts. Now I'm testing my webapp with Selenium. With this line of code
browser.text_field(:id => 'textfield').set("Search text")
the text will be written and the content will be changed. After it should click on a link which is placed on the dynamic content with this code
browser.a(:id => "link").click
The problem now is that the click event won't be executed. Has somebody an idea what the problem could be? Or maybe an example with Watir and Ajax?
Without an example site to test against it's hard to be sure but I will throw out a few potential solutions for you
If the client side javascript is looking for onkeyup events, you may need to fire one after setting the contents of the field. You can do that via the .fire_event method
You could just be looking at a timing issue. If the nature of the link is changing as a result of the input, it's possible that Watir is firing off the two comments in rapid succession and the client side code is still in the midst of doing it's thing (especially if there is back and forth between the jquery code and the webserver that as happening as that also induces networking delays. You may need a brief sleep between commands (brute force) or to wait for a particular element to assume an expected state (a little more work but also a bit more robust and not subject to breaking is the delay exceeds your sleep duration)
I'd suggest executing the commands manually via IRB (you could just cut and paste from your script as needed) to be able to watch the state of the browser, and note any delay in updating the screen/DOM after a particular action. If stuff works properly in IRB but not when executed via a script it's often taken as confirmation of a timing issue.
If this is the original Watir/Firewatir I would try getting it to hover over the link before it attempts to click it.
I've had this problem previously with links that appear after typing into an "autocomplete" field (i.e. it attempts to guess at the text you want by filtering down from a huge list of possibilities).
Try this:
browser.wait_until{browser.link(:id => "link").present?}
browser.link(:id => "link").fire_event("onmouseover")
browser.link(:id => "link").click
If it works, try it without the .fire_event("onmouseover"), because it could just be that it's trying to click the link before it's visible and failing to do so.
If this is actually a question regarding "Selenium" (wrongly labelled Watir) then ignore all of the above, because I that is an application I've never used.
you can use capybara method.
click_link("link")
for ajax set :js => true in you test case
http://opinionatedprogrammer.com/2011/02/capybara-and-selenium-with-rspec-and-rails-3/

onLoad or similar for Ajax?

Good day,
I was wondering if there is a way to make Ajax move on to the next code segment only when all the elements included in the server-side code page are fully loaded. When the retrieved data is text-only there’s no problem, but sometimes there are photos included.
This is part of the code I have been using:
xajx.onreadystatechange = function(){
if(xajx.readyState==4){
document.all.div1.innerHTML = xajx.responseText;
document.all.div1.style.display = “”;
}
}
The thing is that when the response is retrieved (readyState set to 4) and div1 is displayed, the Photo has not been completely loaded yet, so actually the user can see the process of the picture slowly appearing, as he would in any other “normal” case. What I want to do is making div1 available for display only once all the components are fully loaded while meanwhile the system does its stuff in the background. Before Ajax I used hidden iframes like everybody, so I could enclose an onload event handler within the iframe tag (or in an external script), so div1 would appear only after the iframe has fully loaded, photos included.
Any idea?
You can use the 'onload' event on images themselves. You'll need to work out how to attach that event when you are downloading the code dynamically as in your case.

Resources