Download links with TideSDK - download

I have seen several posts coming close to solving my issue, but I am still not able to accomplish my simple task, which is this:
Imagine I have a window which contains a link to a remote file (most often it will be a zip file). How can I structure and call a function that accesses the file and opens a "Save As" dialogue so that the user can choose where to save the downloaded file? It would be nice to be able to pass different variables from other links to the same function to accomplish the same thing for other downloadable files.
And yes, I am completely new to TideSDK and not exactly a javascript expert, if this is causing much painful slapping of foreheads.

Try this.
Any link with a class of "save-as" will trigger the "Save as" dialog. The file will be saved AFTER the user selects the location, gives it a name and clicks Save. This does use jquery.
Download WordPress
<script>
$(function(){
var currentLink;
$('.save-as').click(function() {
var link = $(this).attr('href');
var filename = link.substring(link.lastIndexOf('/')+1);
currentLink = link;
Ti.UI.currentWindow.openSaveAsDialog(saveComplete, {
title: 'Save As...',
multiple: false,
defaultName : filename
});
return false;
}); // End save as.
var saveComplete = function(results) {
if(results.length>0) {
var downloadFile = results[0];
console.log("Download the file");
var httpClient = Ti.Network.createHTTPClient();
httpClient.open('GET', currentLink);
httpClient.receive(function(data) {
var file = Ti.Filesystem.getFile(downloadFile);
var fileStream = file.open(Ti.Filesystem.MODE_APPEND);
fileStream.write(data);
fileStream.close();
});
}
};
});
</script>

Related

Downloaded Excel file with Macros is corrupted via MVC ajax

I am trying to download an Excel file from MVC Ajax call, but everytime it downloads, I cannot open the file, because it says it is corrupted.
Following is my Ajax call:
function Download() {
$.ajax({
url: '/Home/ExcelDownload',
type: "POST",
success: function (result) {
if (result !== null || result !== "") {
$('<iframe src=' + result + ' frameborder="0" scrolling="no" id="myFrame"></iframe>')
.appendTo('body');
}
//var iframe = document.getElementById('invisible');
//iframe.src = result;
},
error: function (data) {
}
});}
From my controller I call the action method like this:
string host = Request.Url.Authority;
return Json("http://" + host + "/ExcelTemplates/EInvoiceTemplateNew.xlsm");
I am having macros enabled in Excel as well.
The file downloads properly but when I try to open it, it gives me a warning about being from a trusted source, and on clicking yes, I get another dialog saying "The workbook cannot be opened or repaired By Microsoft excel because it is corrupt".
Any help or suggestions or workarounds to make my code working.
Thanks In Advance!!!..
I'm not able to do a project and try the excel library right now as I'm at work, so I'm just doing this much quickly.
To start, I think you can get away from using ajax completely. Which is always nice because it's less code to maintain, you can use a normal anchor tag:
Download
This won't redirect the page, because the browser should interpret the content-disposition/Mime type, and just download the file.
Then you can try returning the file like this, where you first read the file (in this case into a stream, but you could do it as a byte[] as well as File has a overload for it), and then return it through your controller by using File and FileResult.
E.g:
public FileResult GetExcelDocument(int id)
{
string filePath = GetPathToDocumentWithId(id); //However you get the excel file path
return File(System.IO.File.OpenRead(filePath),
System.Web.MimeMapping.GetMimeMapping(filePath),
"filename.xlsm");
}
This website says that the content type/MIME of .xlsm is "application/vnd.ms-excel.sheet.macroEnabled.12", but I think GetMimeMapping should work.
Optionally, if you want the file to download in a new tab, you can set the target of the anchor tag to "_blank" and it should open a new tab and download the file there.
Let me know what the result is.
If you're set on using JQuery and ajax, I found this example which follows along your previous attempt:
From Here
var $idown; // Keep it outside of the function, so it's initialized once.
downloadURL : function(url) {
if ($idown) {
$idown.attr('src',url);
} else {
$idown = $('<iframe>', { id:'idown', src:url }).hide().appendTo('body');
}
},

pushState change - equivalent to Chrome Extension onHistoryStateUpdated

I'm porting a Chrome extension to a Firefox extension and due to the nature of the website that it runs on, I need to monitor the pushState.
Chrome Extensions has a handy way to handle this: chrome.webNavigation.onHistoryStateUpdated. The way that I use it in the Chrome extension is as follows:
chrome.webNavigation.onHistoryStateUpdated.addListener(function(details) {
var tabUrl = details.url;
if (isTabUrlValid(tabUrl)) {
$.get(tabUrl, function(data) {
var videoUrl = $(data).find('meta[itemprop=contentURL]').prop('content');
videoUrl = validateUrl(videoUrl);
videoUrl5k = make5kUrl(videoUrl);
});
}
});
I need to do the same thing for the Firefox Extension, but I haven't found any good answers. I've tried doing the answer mentioned here: How to get notified about changes of the history via history.pushState?
(function(history) {
var pushState = history.pushState;
history.pushState = function(state) {
if (typeof history.onpushstate == "function") {
history.onpushstate({state: state});
}
var tabUrl = tabs.activeTab.url;
console.log("UPDATED TAB URL: " + tabUrl);
if (isTabUrlValid(tabUrl)) {
$.get(tabUrl, function(data) {
var videoUrl = $(data).find('meta[itemprop=contentURL]').prop('content');
videoUrl = validateUrl(videoUrl);
videoUrl5k = make5kUrl(videoUrl);
});
}
return pushState.apply(history, arguments);
};
})(window.history);
The problem is that when I do cfx run it complains that history/window is undefined and therefore never gets detected. I think this is due to it being within the SDK, but I don't know of a good workaround.
Any thoughts?
Edit: I looked at #willma's answer below and I don't think that would work for me. The issue is that the URL is updated via pushState and the DOM is not... Is there any good way replicate what I do in the chrome extension?
Edit: Here's the pageMod portion
pageMod.PageMod({
attachTo: 'top', // Don't attach to iFrames --> http://goo.gl/b6b1Iv
include: [URLs],
contentScriptFile: [data.url("jquery-2.1.1.min.js"),
data.url("csScript.js")],
onAttach: function(worker) {
worker.port.on('url', function(url) {
var videoUrl = validateUrl(url);
videoUrl5k = make5kUrl(videoUrl);
console.log("--5K URL--: " + videoUrl5k);
});
}
});
That history code needs to get injected into a tab using a content script. Right now your logic says when the history event occurs, check to see if the tab URL is valid.
In Firefox, the logic will be the other way around: when a tab is opened, check if its URL is valid, and if so, then attach a script to it that will monitor for the history event. To do so you'll need to use a Page Mod.
Edit: All the code
One key concept you're missing is the difference between a content script and a main/library script. The library scripts are stored in lib and have access to all the SDK modules, but don't have access to the DOM, window object… The content scripts are stored in data, are injected into a page using the PageMod or tabs modules, can access the dom and window objects, but have no access to any SDK modules. Content scripts are essentially like the page scripts you'd attach your standard HTML page (with <script></script>) with the caveats that they can't share variables other page scripts but they can communicate with the main scripts.
The only reason I bring this up is because your initial problem was trying to access the window object from a main script and the problem in your fiddle is that you're trying to access the tabs module inside a content script. It's worth reading the topmost link in this answer if this is still confusing.
main.js
const { PageMod } = require('sdk/page-mod');
var sendXHR = function(url) {
// Do something with the new URL
// See Request Module docs (below) for sending XHRs from main script.
}
const pageMod = PageMod({
attachTo: 'top',
include: '*',
onAttach: function(worker) {
worker.port.on('newURL', sendXHR);
}
});
content.js
var sendNewUrlToMain = function() {
self.port.emit('newURL', location.href);
}
var pushState = window.history.pushState;
window.history.pushState = function(state) {
if (typeof history.onpushstate == "function") {
history.onpushstate({state: state});
}
sendNewUrlToMain();
return pushState.apply(history, arguments);
}
window.addEventListener('hashchange', sendNewUrlToMain);
Here are the request module docs, for making XHRs.
NB: if you don't want to use the request module (the only reason being that you already have standard XHR code for your chrome extension and don't want to take the time to learn/rewrite that code), you can send a standard XHR from the content script, but in doing so, you risk allowing the user to close the tab and thus destroy the script before your XHR callbacks are executed.

Can't import the nsIDOMGeoGeolocation XPCOM Interface

Using the addon-sdk of firefox I'm following a tutorial about creating reusable modules, the example uses the geolocation API built into Firefox, so the code is simple:
function getCurrentPosition(callback){
var xpcomGeolocation = Cc["#mozilla.org/geolocation;1"].getService(Ci.nsIDOMGeoGeolocation);
xpcomGeolocation.getCurrentPosition(callback);
}
var widget = require("sdk/widget").Widget({
id: "whereami",
label: "Where Am I?",
contentURL: "http://www.mozilla.org/favicon.ico",
onClick: function(){
console.log("clicked!");
getCurrentPosition(function(position){
console.log("latitude: " + position.coords.latitude);
console.log("longitude: " + position.coords.longitude);
});
}
});
When running firefox with the plugin on, clicking the widget give this error:
Message: [Exception... "Component returned failure code: 0x80570018 (NS_ERROR_XPC_BAD_IID) [nsIJSCID.getService]" nsresult: "0x80570018 (NS_ERROR_XPC_BAD_IID)" location: "JS frame :: resource://gre/modules/XPIProvider.jsm -> jar:file:///tmp/tmpTFowYc.mozrunner/extensions/jid1-LIBIfbK6zvWAiQ#jetpack.xpi!/bootstrap.js -> resource://gre/modules/commonjs/toolkit/loader.js -> resource://jid1-libifbk6zvwaiq-at-jetpack/whereami/lib/main.js :: getCurrentPosition :: line 7" data: no]
According to docs, it's possible to get an error when importing nsIDGeoGeolocation so you must use Cc["#mozilla.org/geolocation;1"].getService(Ci.nsISupports); instead. Also, you must ask for permission to access geolocation, please see section Prompting for permission in
Using geolocation
reference.
By the way, as an advice, I think it will be simpler to use navigator.geolocation.getCurrentPosition(successCallback, errorCallback) since it handles prompting for permission for you, but I don't know what are you trying to do beyond this. Just to explain it a little, you could have an addon page data/index.html where you tell the user you are going to prompt him for his geolocation. Besides this, you must have a PageMod that runs a data/script.js for that addon page page, which has access to navigator.geolocation. That content script may communicate with your lib/main.js file so it has access to the user geo location. Somethings like this:
data/index.html:
<html>
<body>
<h1>Hello user!</h1>
</body>
</html>
data/script.js:
var successCallback = function(position) {
/* this way this script will talk to the pagemod in lib/main.js the user's position */
self.port.emit("gotGeolocation", position.coords.latitude, position.coords.longitude);
};
navigator.geolocation.getCurrentPosition(successCallback, errorCallback);
lib/main.js:
var data = require("sdk/self").data;
var pageMod = require("sdk/page-mod");
var tabs = require("sdk/tabs");
pageMod.PageMod({
/* attach the contentScriptFile to this html page */
include: data.url("index.html"),
contentScriptFile: data.url("script.js"),
onAttach: function(worker) {
/* listen to the script.js worker "gotGeolocation" message */
worker.port.on("gotGeolocation", function(latitude, longitude) {
console.log("latitude:", latitude);
console.log("longitude:", longitude);
});
}
});
/* this will open the index.jtml page and promt the user to access his geo position */
tabs.open(data.url("index.html"));
All this code it's only to better describe the idea, it hasn't been tested and in fact there are some indefinded objects. But I hope to give you an idea of how you can acces navigator.geolocation and pass its coords to the main.js script.
I think that you just forgot this at the top:
var { Cc, Ci } = require('chrome');
Also there a third party module for geolocation https://github.com/ZER0/geolocation

Ajax file upload is not working when used the second time

I'm using this jquery plugin ajaxFileupload in our project. My design is I have a file upload control and set the opacity to 0.01 and then using an anchor link, I trigger the file upload control click event. This works fine until I try to click the anchor link the second time which it doesn't open the file dialog box.
Here is my code.
$(".btnUpload").live("click", function () {
$(".lblUploadError").text("");
$(".fleAttachment").trigger("click");
});
$(".fleAttachment").change(function () {
var reg = /^.*\.(jpg|JPG|gif|GIF|jpeg|JPEG)$/;
var vals = $(this).val(),
val = vals.length ? vals.split("\\").pop() : "";
if (reg.test(vals) == false) {
$(".lblUploadError").text("Invalid Image Type. We only accept .GIF or .JPG");
} else {
ajaxFileUpload();
eval($(".btnRefreshAttachmentList").attr("href"));
}
});
I don't see any error in the console so it makes it difficult to debug it.
Change
$(".fleAttachment").change(function() {
to
$(".fleAttachment").live('change', function() {
$( document ).on( "click", ".fleAttachment", function() {
//--> Logic Here // jQuery 1.7+
});
this.value="";
at the end should work

Limit a firefox extension to a specific domain

I would like to write a firefox extension. This extension is not a generic extension but work specifically for a domain where I need to highlight specific html components.
How should I do that? I just want the js loaded when the user is browsing a specific domain.
My current overaly.js is basically empty (generated by the Extension Wizard):
var myextension = {
onLoad: function() {
// initialization code
this.initialized = true;
this.strings = document.getElementById("myextension-strings");
},
onMenuItemCommand: function(e) {
var promptService = Components.classes["#mozilla.org/embedcomp/prompt-service;1"]
.getService(Components.interfaces.nsIPromptService);
promptService.alert(window, this.strings.getString("helloMessageTitle"),
this.strings.getString("helloMessage"));
},
onToolbarButtonCommand: function(e) {
// just reuse the function above. you can change this, obviously!
myextension.onMenuItemCommand(e);
}
};
window.addEventListener("load", myextension.onLoad, false);
And my ff-overlay.xul is:
myextension.onFirefoxLoad = function(event) {
document.getElementById("contentAreaContextMenu")
.addEventListener("popupshowing", function (e){ myextension.showFirefoxContextMenu(e); }, false);
};
myextension.showFirefoxContextMenu = function(event) {
// show or hide the menuitem based on what the context menu is on
document.getElementById("context-myextension").hidden = gContextMenu.onImage;
};
window.addEventListener("load", myextension.onFirefoxLoad, false);
I was thinking to go neanderthal and do a check inside myextension.onFirefoxLoad to see if the currentpage is the one I want but that requires the user to click the proper item on the context menu.
I'm not totally following what you have because both of those look like JS files, not XUL files. But what you probably want to do is listen for the load event coming from the web pages that are loaded. Then, in your event loader, just look at each page that loads and see whether it's coming from the specific domain you want.
A great (though not always quite as easy as it sounds) way to find out how to do something in a Firefox addon is to find another addon that does something similar. DOM Inspector and Inspect Context are your friends! The first such addon that comes to mind in this case is WikiTrust so you could try looking at that one to see if it gives you any inspiration.

Resources