Change content of Html page on the 'fly' in Firefox extension - firefox-addon

When user click on the button in toolbar of Firefox I need to change content
of the current active html page. In standart]d implementation it's look like this :
function injectNewContent() {
var pageHtml =
[
"<html>",
"<head>",
"</head>",
"<frameset cols='270,*' frameborder='0'>",
"<frame name='frameI' src='http://www.123.com/default.html'>",
"<frame name='frameII' src='" + document.location + "'>",
"<noframes>",
"<body>",
"noframes",
"</body>",
"</noframes>",
"</frameset>",
"</html>"
];
var fullPageHtml = "";
for (var i in pageHtml)
{
fullPageHtml += pageHtml[i];
}
window.document.write(fullPageHtml);
}
What I need to change in this code to get same functionality ?
var windowMediator = Components.classes['#mozilla.org/appshell/window-mediator;1'].
getService(Components.interfaces.nsIWindowMediator);
var recentWindow = windowMediator.getMostRecentWindow("navigator:browser");
recentWindow. ???
Or may be I do something wrong ?
Thanks for any help...

You don't need to go looking for the browser window, your button is already sitting on one. To access the content area of the current tab simply use window.content. This should do what you want:
var doc = window.content.document;
doc.open("text/html", true);
doc.write(fullPageHtml);
doc.close();
Though personally I would rather assign HTML code to doc.documentElement.innerHTML.

Related

Save current page from firefox add-on

I'm need a way to save the current page (including, images, CSS, etc.) from an add-on.
Of course I found the saveDocument() function in the SDK but I was not able to make it work.
from add-on script, I do not have access to actual DOM content
from content script, I do not have access to SDK function 'saveDocument()'
I miss something, I would be very happy if someone could help me.
Best regards,
Fred
here's one way to access the DOM document from addon script.
var winutil = require('sdk/window/utils');
function findDocument(predicate){
// searching in focused window only
// you can also get all windows with winutil.windows('navigator:browser')
var win = winutil.getMostRecentBrowserWindow();
var gBrowser = win.gBrowser;
// traverse tabs of focused window
for (var i=0, l=gBrowser.browsers.length; i<l; i++)
{
var br = gBrowser.getBrowserAtIndex(i);
var doc = br.contentDocument;
if (predicate(doc))
return doc;
}
}
// and this is probably how you would save (not tested)
var {Cc, Ci} = require('chrome');
var wbp = Cc["#mozilla.org/embedding/browser/nsWebBrowserPersist;1"]
.createInstance(Ci.nsIWebBrowserPersist);
var doc = findDocument(/* whatever */);
if (doc)
wbp.saveDocument(doc, /* figure out other args */);

Pass variable to dialog variable in jQuery Mobile

I have a button that opens up a dialog containing another html file. What I would like to be able to do is pass a variable to the dialog. This is what I have.
var html = "";
var PLAN_ID = '1234';
html += "<div>"
html += 'Final'
html += "</div>"
The PLAN_ID variable is the one that I wish to push to the formFinal.html. I can populate a hidden text input on the dialog with the following code.
$(document).on('pageshow', '#FinalPostPage', function(e) {
var page = $(this);
var query = page.data("url").split("?")[1];
var planid = query.split("=")[1];
$("input[name=Title]",this).val(planid);
})
I can use this but the variable doesn't populate until after the formFinal.html page loads it seems. I need the variable to be used while the page develops. Hope this makes sense.
You could use localStorage to transfer data from this page to the other page.
When you're creating the button, add the PLAN_ID variable as a data-* attribute to the a and also create an id/class for it :
var html = "";
var PLAN_ID = '1234';
html += "<div>"
html += 'Final'
html += "</div>"
Write a click event for the button click :
$(document).on("click", '#dialog-send' ,function(e) {
//prevent default action. we dont want it to redirect. Just yet.
e.preventDefault();
//set item in localStorage.
localStorage["plan_id"] = $(this).data("planid");
//then use changePage to redirect
$.mobile.changePage(this.href, {transition: 'pop', role: 'dialog'});
});
In your pageshow event,
$(document).on('pageshow', '#FinalPostPage', function(e) {
var page = $(this);
//var query = page.data("url").split("?")[1];
//get plan id from localStorage
var planid = localStorage["plan_id"];
$("input[name=Title]",this).val(planid);
});
Hope this helps.

initialize a listview outside of a page

I am building a single page application using jQuery Mobile.
In the header I have a "menu" button that opens a popup with a listview.
I would like to define this popup dialog and reuse it for all pages.
Reusing a popup outside a page works fine, but listview inside the popup does not get enhanced (because listview internally uses parent page to resolve links included in the list).
Question: is it possible to use a listview widget outside of a page? (I do understand that I might have to specify a base url for this to work, but I don't think that will be a problem).
No, its not possible. When you create a dynamic popup, which doesnt reside in a single page, IT WILL WORK, because popups were never meant to be used/page. But, listview on the other hand, requires the list to lie in a parent page. The refresh method of listview expects it to be in parent. Look at this function which is called when a listview refresh happens :
_createSubPages: function () {
var parentList = this.element,
parentPage = parentList.closest(".ui-page"), //<-- This line
parentUrl = parentPage.jqmData("url"),
parentId = parentUrl || parentPage[0][$.expando],
parentListId = parentList.attr("id"),
o = this.options,
dns = "data-" + $.mobile.ns,
self = this,
persistentFooterID = parentPage.find(":jqmData(role='footer')").jqmData("id"),
hasSubPages;
// blah blee blah
}).listview();
See the second line in the function. It expects a page to be there. Tough luck bro :)
PS : This is the closest i could get : http://jsfiddle.net/hungerpain/8qq62/1/
EDIT
Here's a hacky way. You could dynamically create a hidden <span/> in your data-role=page div and add the <ul/> in it. Then you could refresh it, after which you could move it to the dynamic popup. Then you'll have to remove that hidden span element. Here's the code :
$.extend({
"makePopup": function (array) { //you could add more if you want here, such as callbacks to the click function of the button,etc.
var $popup;
//creat popup element
$popup = $("<div/>", {
"data-role": "popup",
"data-theme": "a",
"data-overlay-theme": "a",
"data-transition": "pop"
}).popup();
//create list
$list = $("<ul/>", {
"data-role": "listview",
"id": "templist"
}).html(function () {
var li = [];
for (var i = 0; i < array.length; i++) {
li.push($("<li/>").html(array[i]));
}
return li;
})
//create close element
var $close = $("<a/>", {
"data-role": "button",
"html": "Close",
"href": "#",
"data-theme": "a"
}).on("click", function () {
//click event of close element
$(this).closest("[data-role=popup]").popup("close");
}).buttonMarkup();
//add a span to page, refresh list, etc
$.mobile.activePage.append("<span id='temp'></span>").find("#temp").hide().append($list).promise().done(function () {
$(this).find("#templist").listview().listview("refresh");
});
//create content div - makes a nice jQM page structure.
var $content = $("<div/>", {
"data-role": "content",
//move li to popup
}).append($("#temp").find("#templist"), $close);
//remove span
$("#temp").remove();
//append $close to $content, then append $content to $popup
$content.appendTo($popup)
return $popup;
}
});
And you could pass an array to this :
$.makePopup(["Sweet Child 'O Mine", "Summer of '69", "Smoke in the Water", "Enter Sandman", "I thought I've seen everything"]).popup("open");
Here's an updated demo : http://jsfiddle.net/hungerpain/8qq62/7/
But obviously this is hacky.

Google Script - Can't combine on the same script multiple selection from listBox and SubmitButton

I'm trying to code on google app and I've encountered one issue. For example, let's consider this example code on google website ( link's here https://developers.google.com/apps-script/class_formpanel )
function doGet() {
var app = UiApp.createApplication();
var form = app.createFormPanel();
var flow = app.createFlowPanel();
flow.add(app.createTextBox().setName("textBox"));
flow.add(app.createListBox().setName("listBox").addItem("option 1").addItem("option 2"));
flow.add(app.createSubmitButton("Submit"));
form.add(flow);
app.add(form);
return app;
}
function doPost(eventInfo) {
var app = UiApp.getActiveApplication();
app.add(app.createLabel("Form submitted. The text box's value was '" +
eventInfo.parameter.textBox +
"' and the list box's value was '" +
eventInfo.parameter.listBox + "'"));
return app;
}
My issue is that I want to select multiple values on the listbox. I change then line 6 in
flow.add(app.createListBox(true).setName("listBox").addItem("option 1").addItem("option 2"));
to allow multiple selection. But the result is that only the last selected value is taken, preventing multiple selections. Apparently, it is due to the submitButton. I need to keep the formPanel because on a further code I'll like to combine uploading files and listBox multiple selection. How may I fix that? Thank you a lot
As a complement to Mogsdad's answer, note that this bug / issue is only concerning the doPost structured handler... if you don't need the file upload feature you could use a simple doGet + handler with callbackElement and in this case the multiselect list is available and works as expected.
test function :
function doGet() {
var app = UiApp.createApplication().setTitle('test listBox');
var panel = app.createHorizontalPanel().setStyleAttribute('padding','40px');
var sHdlr = app.createServerHandler('validateList').addCallbackElement(panel);
var items = ['category 1','category 2','category 3'];
var list1 = app.createListBox(true).setName('list1');
for(var i =0;i<items.length;++i){list1.addItem(items[i],i)}
panel.add(list1).add(app.createButton('validate',sHdlr));
app.add(panel);
return app;
}
function validateList(e){
var app = UiApp.getActiveApplication();
app.add(app.createLabel("Value(s) in list : "+e.parameter.list1).setStyleAttribute('margin-left','40'));
return app;
}
and below is a working example of the workaround described in the issue tracker.
I used a textBox to show the process, set it to visible(false) or use a hidden widget in a 'real' app. ( Test available here )
function doGet() {
var app = UiApp.createApplication().setTitle('test listBox');
var panel = app.createHorizontalPanel().setStyleAttribute('padding','40px');
var listHandler = app.createServerHandler('updlistVal').addCallbackElement(panel);
var items = ['category 1','category 2','category 3'];
var list1 = app.createListBox(true).setName('list1').addChangeHandler(listHandler);
for(var i =0;i<items.length;++i){list1.addItem(items[i])}
panel.add(list1).add(app.createTextBox().setText(items[0]).setId('listboxVal').setName('listboxVal').setWidth('200'));// set a default value in case the user is happy with that and doesn't touch the listBox
var submitBtn = app.createSubmitButton('test').setStyleAttribute('margin-left','40');
app.add(app.createFormPanel().add(panel.add(submitBtn)));
return app;
}
function doPost(e){
var app = UiApp.getActiveApplication();
app.add(app.createLabel().setStyleAttribute('padding','40').setText("Submitted value(s) from list : "+e.parameter.listboxVal));
return app;
}
function updlistVal(e){
var app = UiApp.getActiveApplication();
app.getElementById('listboxVal').setValue(e.parameter.list1);
return app;
}
EDIT 2 :
As Mentioned in the comments on this post we must find a way to prevent going through the submission before the value of the hidden/text widget has been updated with a valid value. The "default value" I used above is a possible solution, another one is to use a client handler to validate the submit button only if the listValue (or its value in the hidden widget) is right. Here is a code that does it (only the doGet is reproduced, all other functions being identical.
function doGet() {
var app = UiApp.createApplication().setTitle('test listBox');
var panel = app.createHorizontalPanel().setStyleAttribute('padding','40px');
var submitBtn = app.createSubmitButton('test').setStyleAttribute('margin-left','40').setEnabled(false);
var listHandler = app.createServerHandler('updlistVal').addCallbackElement(panel);
var items = ['category 1','category 2','category 3'];
var listVal = app.createTextBox().setText('not defined yet').setId('listboxVal').setName('listboxVal').setWidth('200');
var list1 = app.createListBox(true).setName('list1').addChangeHandler(listHandler).addItem('choose one or more item(s)');
for(var i =0;i<items.length;++i){list1.addItem(items[i])}
var clientH = app.createClientHandler().forTargets(submitBtn).setEnabled(true).validateMatches(list1, 'category');
list1.addClickHandler(clientH);
panel.add(list1).add(listVal);// set a default value in case the user is happy with that and doesn't touch the listBox
app.add(app.createFormPanel().add(panel.add(submitBtn)));
return app;
}
Using the hidden widget as validation source causes a small issue as we need to click twice on the listBox to make it work... in case there are other questions on the form this will be solved by triggering the client handler with every other widgets so that the double click won't be necessary anymore but this is becoming a bit "out of subject" I'm afraid.
EDIT 3 :
just for the fun of it, a last version that works apparently without issue...
test here
function doGet() {
var app = UiApp.createApplication().setTitle('test listBox');
var panel = app.createHorizontalPanel().setStyleAttribute('padding','40px');
var submitBtn = app.createSubmitButton('test').setStyleAttribute('margin-left','40').setEnabled(false).setId('sbmt');
var wait = app.createImage('https://dl.dropboxusercontent.com/u/211279/loading3T.gif').setId('wait').setVisible(false);
var listHandler = app.createServerHandler('updlistVal').addCallbackElement(panel);
var items = ['category 1','category 2','category 3'];
var listVal = app.createTextBox().setText('not defined yet').setId('listboxVal').setName('listboxVal').setWidth('200');
var list1 = app.createListBox(true).setName('list1').addChangeHandler(listHandler).addItem('choose one or more item(s)');
for(var i =0;i<items.length;++i){list1.addItem(items[i])}
var clientH = app.createClientHandler().forTargets(wait).setVisible(true).forTargets(submitBtn).setEnabled(false);
list1.addChangeHandler(clientH);
panel.add(list1).add(listVal);// set a default value in case the user is happy with that and doesn't touch the listBox
app.add(app.createFormPanel().add(panel.add(submitBtn).add(wait)));
return app;
}
function doPost(e){
var app = UiApp.getActiveApplication();
app.add(app.createLabel().setStyleAttribute('padding','40').setText("Submitted value(s) from list : "+e.parameter.listboxVal));
return app;
}
function updlistVal(e){
var app = UiApp.getActiveApplication();
app.getElementById('listboxVal').setValue(e.parameter.list1);
app.getElementById('sbmt').setEnabled(true);
app.getElementById('wait').setVisible(false);
return app;
}
This is a known bug in the issue tracker, Issue 959. Visit and star it for updates.
It's been known and "worked on" since Dec 2011, if you believe the notes added by the support team. Other users have provided a work-around, and a modified version of it appears below.
The idea is to attach a handler function to the ListBox, which will receive all the selected items from the ListBox. The handler will then write those values to a hidden element in the form. When the form is submitted, the list of selections will be available to the doPost(), via the hidden element.
...
var listbox = app.createListBox(true).setName("listBox").addItem("option 1").addItem("option 2");
flow.add(listbox);
// Issue 959 ListBox Workaround: http://code.google.com/p/google-apps-script-issues/issues/detail?id=959
var listboxHidden= app.createHidden("listboxHidden", "").setId("listboxHidden");
flow.add(listboxHidden);
var fixListBoxHandler = app.createServerHandler('fixListBoxHandler');
fixListBoxHandler.addCallbackElement(listbox);
listbox.addChangeHandler(fixListBoxHandler);
...
}
function fixListBoxHandler(e) {
var app = UiApp.getActiveApplication();
app.getElementById('listboxHidden').setValue(e.parameter.listbox);
return app;
}
NOTE: Unfortunately, this work-around is time-sensitive; it can take several seconds for the handler to update the hidden value. If the submit button is hit before the handler completes its job, then post() receives what was in the hidden element before the call to the handler.

jQuery UI portlets - toggle portlets to save to a cookie (half way there!)

I'm a bit of a jQuery n00b so please excuse me if this seems like a stupid question. I am creating a site using the jQuery UI more specifically the sortable portlets. I have been able store whether or not a portlet is has been open or closed to a cookie. This is done using the following code. The slider ID is currently where the controls are stored to turn each portlet on and off.
var cookie = $.cookie("hidden");
var hidden = cookie ? cookie.split("|").getUnique() : [];
var cookieExpires = 7; // cookie expires in 7 days, or set this as a date object to specify a date
// Remember content that was hidden
$.each( hidden, function(){
var pid = this; //parseInt(this,10);
$('#' + pid).hide();
$("#slider div[name='" + pid + "']").addClass('add');
})
// Add Click functionality
$("#slider div").click(function(){
$(this).toggleClass('add');
var el = $("div#" + $(this).attr('name'));
el.toggle();
updateCookie(el);
});
$('a.toggle').click(function(){
$(this).parents(".portlet").hide();
// *** Below line just needs to select the correct 'id' and insert as selector i.e ('#slider div#block-1') and then update cookie! ***
$('#slider div').addClass('add');
});
// Update the Cookie
function updateCookie(el){
var indx = el.attr('id');
var tmp = hidden.getUnique();
if (el.is(':hidden')) {
// add index of widget to hidden list
tmp.push(indx);
} else {
// remove element id from the list
tmp.splice( tmp.indexOf(indx) , 1);
}
hidden = tmp.getUnique();
$.cookie("hidden", hidden.join('|'), { expires: cookieExpires } );
}
})
// Return a unique array.
Array.prototype.getUnique = function() {
var o = new Object();
var i, e;
for (i = 0; e = this[i]; i++) {o[e] = 1};
var a = new Array();
for (e in o) {a.push (e)};
return a;
}
What I would like to do is also add a [x] into the corner of each portlet to give the user another way of hiding it but I'm unable to currently get this to store within the Cookie using the code above.
Can anyone give me a pointer of how I would do this?
Thanks in advance!
Gareth
Have you tried adding another selector to the click function?
$("#slider div, #slider div > span.ui-icon-x").click(function(){
$(this).toggleClass('add');
var el = $("div#" + $(this).attr('name'));
el.toggle();
updateCookie(el);
});

Resources