Input file - if not changed keep previous file attached - javascript

When user click on input['type="file"'] and select a file... file get attached. But if user click again on input and browse files but doesn't select one and close the dialog, the selected file disappears (input filed resets). Is there any way to prevent that?

I'm pretty sure this is restricted by the browser as a security feature to prevent a user from uploading a file without first selecting it. I understand it was selected the first time but you can see how this can be used maliciously if we were able to set the value attribute or re-populate the input field after they hit "cancel" the second time.

as Eliel said it is not recommended to do so for security reasons, Ex: The second time if you retain the path value but the file gets changed to a malicious one it is a purely insecure
But I show you how to retain the old path value here
var file_name = this.value;
$('input[type="file"]').on('change', function (event, files, label) {
file_name = this.value;
});
there is no direct way to find if cancel is clicked on dialog(Not exposed to browser)
But use this
document.body.onfocus = function(){
document.getElementById('#fileInput').value = file_name;
} // to detect dialog closed
then the next time the dialog opened set the value to file_name (works Only in firefox with the below addon)
var pageMod = require('page-mod');
var self = require('self');
pageMod.PageMod({
include: "url of app",
contentScriptFile: [self.data.url('url of script file'),
self.data.url('url of script file'),...]
});
Ref:https://forums.mozilla.org/addons/viewtopic.php?p=25153&sid=b6380f9e2acbf759e8833979561dd6f1
Hope it helps

It's old but many customers are still asking for this to be fixed.
I did this to get around mine (with jQuery)
var oldSel;
$('input[type="file"]').on('change', function() {
if ($(this).val()) oldSel = $(this).clone();
else $(this).replaceWith(oldSel);
});

Related

Trying to programmically paste

I am having trouble with execCommand('paste');
My code:
var copy = document.createElement("BUTTON");
copy.innerText = "Copy";
Sections.contextmenu.appendChild(copy);
copy.addEventListener("click", function(e) {
document.execCommand("copy");
});
var paste = document.createElement("BUTTON");
Sections.contextmenu.appendChild(paste);
paste.innerText = "Paste";
paste.addEventListener("click", function(e) {
console.log("Paste");
if (document.execCommand("paste")) {
console.log("pasted");
}
});
Copy worked right out of the box. I cannot get paste to work. I see "Paste" in the console, but nothing is pasted. I've read some things that say that this functionality needs to be explicitly turned on in Firefox. Is there no way (other than using flash... This is talked about in the research I've done) to execute "paste" in a content-editable element programmatically?
The paste command is disabled in web content (it’s only available in a browser extension). It’s disabled presumably because it would allow any website to steal the clipboard’s content. From the MDN documentation on execCommand:
paste
Pastes the clipboard contents at the insertion point (replaces current selection). Disabled for web content.
try the following code
console.log(document.exeCommand('paste')
If false maybe you need a permission to use it or your navigator can't support it
You can also use the Clipboard API, which would obliterate the exeCommand

Switch focus to new browser tab once it is opened

I the below script, once the button is pressed, a new browser tab with Google is opened. I then want to fill the search box on this newly opened tab.
How do I switch focus to this new tab, so that I can wait for it to load completely and then fill the search box?
<button id="start">Google</button>
<script>
function open_google_in_new_tab() {
var url = "https://www.google.com";
window.open(url);
window.onload(fill_search_box()); // here it will try to operate on the old tab
};
function fill_search_box() {
var search_box = document.getElementsByName("q")[0];
search_box.value = "Some text";
};
var start_button = document.getElementById("start");
start_button.addEventListener("click", open_google_in_new_tab);
</script>
You can't exactly target another element in another document from your script, however, you can set the textbox value of the Google textbox using a query string:
www.google.com?q=SearchTerm
When creating the SearchTerm you should also use encodeURI to ensure that your attached search query meets URL standards
Also, if you wish to open the window in another tag you can use window.open(url, "_blank") to open.
See example below:
Note:- The page will not open due to snippet restrictions - so run in your own browser.
<button id="start">Google</button>
<script>
function open_google_in_new_tab() {
var text = "Some text";
var url = "https://www.google.com?q=" + encodeURI(text);
console.log(url)
window.open(url, '_blank');
};
var start_button = document.getElementById("start");
start_button.addEventListener("click", open_google_in_new_tab);
</script>
You cannot do that with Javascript unfortunately.
However, this might make you achieve something similar if you are willing to try.
You can manipulate the search string via parameter named "q" in the query string of the Google website. So, in order to make the user to go the Google and search this particular word, you attach the keyword to it. Something like this:
var url = "https://www.google.com?q=searchKeyword";

Use iframe to download file

Currently, I'm downloading the file using POST form:
var form = document.createElement("form");
var element1 = document.createElement("input");
var element2 = document.createElement("input");
form.target = "_blank";
form.method = "POST";
form.action = path;
element1.value = authService.getToken();
element1.name = "Authorization";
form.appendChild(element1);
element.append(form);
form.submit();
element.empty();
In order to prevent current page's location change when the server doesn't send correct headers, I open set the form's target to "_blank", so that if an error occurs it is shown on the other page. But the problem here is that browsers block new tabs by default, and I don't want to force users to allow such behaviour. I've read that also there an option to specify iframe's id as a target. Is it going to work in my case? How can I then read an error from the iframe to show to a user?
I am working on this very problem right now. The best answer I've found for returning state from the iframe is to set a cookie in it. The cookie's name should ideally be unique to the particular download event (I'm using a guid), known to both pages, and its value can be an error message or empty for success. The parent page then polls for this cookie in javascript. I make sure that the download url always renders the cookie if there's an error, because it's hard to learn anything else about the state of the iframe. On success, the JS poller can hide a ”your download will begin shortly" message, and delete the cookie. On failure, do those and also show the error.
The big unanswered question is how well it'll work in mobile browsers. Popups are a terrible choice with them because they mostly default to blocking them with no prompt... but nevertheless there's a jQuery plugin out there for iframe downloads which, when it sees mobile, falls back to popups. That scares me.

How to add a right click on my Firefox extension's icon?

Good Day!
I've searched and searched again and I didn't find any help for this problem...
Context :
I've developed a Google Chrome extension that is very simple: send email to somebody with one click. To configure this, there is an option page on this extension to set the email address to which to send. My Google Chrome extension is available here (English translation, just the text, not the install).
Users have asked me to make this extension for Firefox so I'm working on it!
I've read tutorials on cfx and it's OK. But I need to have my extension respond to a right-click on my extension's icon in the toolbar. Not on the page, on my icon.
I'm using ActionButton (or ToggleButton) to add my icon to this the toolbar but I can't find a way to add a menu on the right click (there's already the default Firefox context menu but I want to add "Options".)
If somebody has the solution it would be great!
(I'm not familiar with XUL. So, if possible, a JavaScript only solution, please ^^ )
PS : I'm French so please excuse me for my bad English
EDIT : I've found how to set preferences in my "package.json" file but I want my own window.
And if we could "bind" the button "Options" in Add-on Manager to my own window it would be perfect!
EDIT 2 : as it's not clear for everyone I would detail here what I want for my extension :
- simple click (left click) on the icon get the current URL and send it to a mail address (OK for this)
- simple click ONLY DO THIS. This extension aims to be very very simple !
- right click on the icon shows Firefox's context-menu and I want to add "Options" here to show my options page
- Addon Manager could have a "Options" button near "Deactivate" and "Debug" and I want this option button to show my options page.
=> 2 way to see my option page : by the right click or by the addon manager and this is why I need your help !
General UI comments
Using right-click to directly activate your function is contrary to the general UI that is used system wide. Right-click is, system-wide (on some systems), used to open the context menu. In Firefox in the toolbar this is used to bring up the context menu for the toolbar area. This is what your users are generally going to expect to happen when they use right-click. You are probably better off either using something like shift-left-click, or letting the user define what combination is to be used to activate your function. If it is that you are attempting to add an option to the context menu, then that would normally be accessed via right-click.
Alternatives used in other add-ons:
A second section to your button with a down arrow. Clicking on the down arrow opens an expanded action or options menu.
Use the tooltip to display an action or options menu when the mouse is hovered over your button. This is done by creating a custom tooltip by enclosing the popup within a <tooltip id="myTooltip"></tooltip> element and referencing it in the button with <button tooltip="myTooltip"/> (tooltip property, tooltip attribute).
Using right-click
The problem appears to be that the Add-on SDK ActionButton has abstracted away your ability to have listeners for arbitrary events. It also does not give you access to the actual event object which is normally passed to event handlers (listeners). Further, its click event appears to actually be a command event, not a click event. One of the significant differences between a click and a command event is that the command event does not normally fire on a right-click.
You are going to need to gain access to the button outside of the ActionButton interface and add a listener for click events and then in your click event handler, you can make a choice to perform your action based on the state of event.button and event.shiftKey.
Adapting some code based on what I posted as an answer for a different question, you are going to want something like (not tested with modifications):
function loadUi(buttonId) {
if (window === null || typeof window !== "object") {
//If you do not already have a window reference, you need to obtain one:
// Add a "/" to un-comment the code appropriate for your add-on type.
/* Add-on SDK:
var window = require('sdk/window/utils').getMostRecentBrowserWindow();
//*/
/* Overlay and bootstrap (from almost any context/scope):
var window=Components.classes["#mozilla.org/appshell/window-mediator;1"]
.getService(Components.interfaces.nsIWindowMediator)
.getMostRecentWindow("navigator:browser");
//*/
}
forEachCustomizableUiById(buttonId, loadIntoButton, window);
}
function forEachCustomizableUiById(buttonId ,func, myWindow) {
let groupWidgetWrap = myWindow.CustomizableUI.getWidget(buttonId);
groupWidgetWrap.instances.forEach(function(windowUiWidget) {
//For each button do the load task.
func(windowUiWidget.node);
});
}
function loadIntoButton(buttonElement) {
//Make whatever changes to the button you want to here.
//You may need to save some information about the original state
// of the button.
buttonElement.addEventListener("click",handleClickEvent,true);
}
function unloadUi(buttonId) {
if (window === null || typeof window !== "object") {
//If you do not already have a window reference, you need to obtain one:
// Add a "/" to un-comment the code appropriate for your add-on type.
/* Add-on SDK:
var window = require('sdk/window/utils').getMostRecentBrowserWindow();
//*/
/* Overlay and bootstrap (from almost any context/scope):
var window=Components.classes["#mozilla.org/appshell/window-mediator;1"]
.getService(Components.interfaces.nsIWindowMediator)
.getMostRecentWindow("navigator:browser");
//*/
}
forEachCustomizableUiById(buttonId, unloadFromButton, window);
}
function unloadFromButton(buttonElement) {
//Return the button to its original state
buttonElement.removeEventListener("click",handleClickEvent,true);
}
function handleClickEvent(event) {
If( (event.button & 2) == 2 && event.shiftKey){
event.preventDefault();
event.stopPropagation();
doMyThing();
}
}
function doMyThing() {
//Whatever it is that you are going to do.
}
As should be implied by the above code, you will need to make sure to remove your listener when uninstalling/disabling your add-on. You will also want to make sure that loadUi() gets called when a new window opens so that your handler is added to the new button.
Adding to the context menu
There is no direct way to change the context menu just for your icon. The ID for the context menu is toolbar-context-menu. What you can do is add items to the context menu which are normally hidden="true". When you get the event that the right-click has happened on your icon you can change the state of hidden for those items you added. Then in an event handler that is called on the the popuphidden event for the context menu (<menupopup id="toolbar-context-menu">) you can set the state of hidden="true" for the <menuitem> element(s) which you have added to the <menupopup id="toolbar-context-menu"> in each browser window.
Something along the lines of:
function loadIntoContextMenu(win){
let doc = win.ownerDocument;
let contextPopupEl = doc.getElementById("toolbar-context-menu");
contextPopupEl.insertAdjacentHTML("beforeend",
'<menuitem label="My Item A" id="myExtensionPrefix-context-itemA"'
+ ' oncommand="doMyThingA();" hidden="true" />'
+ '<menuitem label="My Item B" id="myExtensionPrefix-context-itemB"'
+ ' oncommand="doMyThingB();" hidden="true" />');
contextPopupEl.addEventListener("popuphidden",hideMyContextMenuItems,true);
}
function unloadFromContextMenu(win){
let doc = win.ownerDocument;
let contextPopupEl = doc.getElementById("toolbar-context-menu");
let itemA = doc.getElementById("myExtensionPrefix-context-itemA");
let itemB = doc.getElementById("myExtensionPrefix-context-itemB");
contextPopupEl.removeChild(itemA);
contextPopupEl.removeChild(itemB);
contextPopupEl.removeEventListener("popuphidden",hideContextMenuItems,true);
}
function setHiddenMyContextMenuItems(element,text){
//The element is the context menu.
//text is what you want the "hidden" attribute to be set to.
let child = element.firstChild;
while(child !== null){
if(/myExtensionPrefix-context-item[AB]/.test(child.id)){
child.setAttribute("hidden",text);
}
child = child.nextSibling;
}
}
function showContextMenuItems(event){
//The target of this event is the button for which you want to change the
// context menu. We need to find the context menu element.
let contextmenuEl = event.target.ownerDocument
.getElementById("toolbar-context-menu");
setHiddenMyContextMenuItems(contextmenuEl,"false");
}
function hideContextMenuItems(event){
//This is called for the popuphidden event of the context menu.
setHiddenMyContextMenuItems(event.target,"true");
}
//Change the handleClickEvent function in the code within the earlier section:
function handleClickEvent(event) {
If( (event.button & 2) == 2){
//don't prevent propagation, nor the default as the context menu
// showing is desired.
showContextMenuItems(event);
}
}
Again, I have not tested this. It should demonstrate one way to accomplish what you desire.
However, given that we are talking about the context-menu, it is probably better to use the contextmenu event rather than the click event and testing for a right-click. In which case, we would change some of the functions above to be the following:
function loadIntoButton(buttonElement) {
//Make whatever changes to the button you want to here.
buttonElement.addEventListener("contextmenu",handleContextmenuEvent,true);
}
function handleContextmenuEvent(event) {
showContextMenuItems(event);
}
You can obtain each open primary browser window through the use of nsIWindowMediator. The following function, from MDN, will run the function you pass to it once for each open window:
Components.utils.import("resource://gre/modules/Services.jsm");
function forEachOpenWindow(todo) // Apply a function to all open browser windows
{
var windows = Services.wm.getEnumerator("navigator:browser");
while (windows.hasMoreElements()) {
todo(windows.getNext().QueryInterface(Components.interfaces.nsIDOMWindow));
}
}
In the Add-on SDK:
function forEachOpenWindow(todo) // Apply a function to all open browser windows
var windows = require("sdk/windows");
for (let window of windows.browserWindows) {
todo(windows.getNext().QueryInterface(Components.interfaces.nsIDOMWindow));
}
}
You can add a listener which calls loadIntoContextMenu for new windows with the following code (also from MDN):
Components.utils.import("resource://gre/modules/Services.jsm");
Services.wm.addListener(WindowListener);
var WindowListener =
{
onOpenWindow: function(xulWindow)
{
var window = xulWindow.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
.getInterface(Components.interfaces.nsIDOMWindow);
function onWindowLoad()
{
window.removeEventListener("load",onWindowLoad);
if (window.document.documentElement.getAttribute("windowtype") == "navigator:browser"){
loadIntoContextMenu(window);
//It would be better to only do this for the current window, but
// it does not hurt to do it to all of them again.
loadUi(buttonId);
}
}
window.addEventListener("load",onWindowLoad);
},
onCloseWindow: function(xulWindow) { },
onWindowTitleChange: function(xulWindow, newTitle) { }
};
I have implemented a menu-button that has a primary and secondary action. Although it isn't right/left click, the button has two sides:
This allows you to associate two different actions with your button without altering the usual context menu flow of Firefox. Download the files on GitHub and store them in your lib folder.
Usage is similar to other button types. Include the following code in main.js (or any js file in the lib directory)
const { MenuButton } = require('./menu-button');
var btn = MenuButton({
id: 'my-menu-button',
label: 'My menu-button',
icon: {
"16": "./firefox-16.png",
"32": "./firefox-32.png"
},
onClick: click
});
The click function will be passed the same state object as the toggle and action buttons, and will be passed an additional boolean argument: isMenu. It should be used like so
function click(state, isMenu) {
if (isMenu) {
//menu-button clicked
} else {
//icon clicked
}
}
I tried your extension on Chrome after answering this question and see that my answer probably isn't what you're looking for, so I'll make a different suggestion (leaving the other answer up because I think it is useful for people looking for multiple actions on a single button).
One thing I would say is that (some) Chrome users know that the Options menu item refers to the extension and not browser options. Those users know that the menu item is there, and use it to change their extension settings. Firefox users wouldn't expect that to be the case, because the context menu actions all affect the browser, not the extension. In the same way, (some) Firefox users know that to change their extension settings, they must navigate to about:addons (or Tools/Addons) and click the Preferences button next to the extension. This is the expected route to changing your preferences. So I would argue that adding a context-menu option is very complicated and not a good solution.
Instead, if your users haven't yet set their preferences, I think you should do what you already do in Chrome: create a Panel, associate it with your button (by using position: button in the panel constructor), and tell your users that they need to set their preferences by navigating to Tools/Addons. If you use the simple prefs module, a Preferences button will appear next to your extension and the options that you set in your package.json will be editable there.
Unfortunately, this is a very basic page, and won't look like the nice HTML options page you made.
Bonne chance.
Besides all the reservations in the other answers and without reusing the existing toolbar contextmenu, here is how:
const utils = require('sdk/window/utils');
const window = utils.getMostRecentBrowserWindow();
const doc = window.document;
const XUL_NS = 'http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul';
const { getNodeView } = require("sdk/view/core");
let ButContext = doc.createElementNS(XUL_NS,'menupopup');
ButContext.setAttribute("id","customIDString");
menuitem = doc.createElementNS(XUL_NS,"menuitem");
menuitem.setAttribute("label", "customLabelString");
ButContext.appendChild(item);
var myBut = require("sdk/ui/button/action").ActionButton({
id: "myButton",
label: "Right click me!"
// And other options
});
//Here is the context magic
getNodeView(myBut).setAttribute('context', "customIDString");
//either ; gets removed together with the button
getNodeView(myBut).appendChild(ButContext);
//or ; the correct place but needs to be removed manually
doc.getElementById("mainPopupSet").appendChild(ButContext);

How do I determine the input field the user has selected from a web page downloaded from a server

I have tried the following with no success, any help will be appreciated.
UIPasteboard *pb = [UIPasteboard generalPasteboard];
[pb setString:passwordName];
NSString *jScriptString;
jScriptString = [NSString stringWithFormat:#"var output = {document.getElementById;" " ;output.innerHTML = document.activeElement.value = '%#'}", passwordName];
[self.viewWebSite stringByEvaluatingJavaScriptFromString:jScriptString];
just guessing here, but when you uiwebview loads pages, you could add a .js file, local to your ios app, that adds a onfocus method to every input field found in the dom. Then you can have that onfocus event pass up the element id to iOS via the window.location property, which webView:shouldStartLoadWithRequest:navigationType: can monitor.

Categories