How can I hook up to Excel events in Javascript - javascript

In a client-side web application, I would like to:
open an Excel spreadsheet,
export some application data to Excel,
allow the user to work with it, and
when they are done, read the (potentially changed) data back into my application.
I would like the user to have a fluid experience and detect when they are done with excel by hooking up to the BeforeClose event, but I find that I am unable to hook up to Excel's events in javascript/HTML.
function BeforeCloseEventHandler(cancel) {
// TODO: read values from spreadsheet
alert("Closing...");
}
function openExcel() {
var excel = new ActiveXObject("Excel.Application");
var workbook = excel.Workbooks.Add();
var worksheet = workbook.Worksheets(1);
worksheet.Cells(1, 1).Value = "First Cell";
worksheet.Cells(1, 2).Value = "Second Cell";
workbook.BeforeClose = BeforeCloseEventHandler; // THIS DOESN'T WORK
excel.Visible = true;
excel.UserControl = true;
}
Does anyone have any suggestions?

After doing some research, I have discovered that I cannot hook up events to dynamic ActiveX objects (i.e., the ones that are created by the new ActiveXObject constructor) in javascript.
One idea is that I create a wrapper Windows Form user control that would be hosted inside of an <object> tag in the web app. The user control would call Excel and receive events, and raise events back to javascript, which I could hook up to using the <script for="..." event="..."> mechanism. Not sure that this will work, but I will try it.
Even if it does work, I am not particularly happy about this solution. There are too many layers--the javascript is being called from a silverlight control meaning that my data has to cross 3 boundaries there and back: Silverlight -> Javascript -> Hosted Winform User Control -> Excel.
It would be nice to eliminate some of these boundaries.

I think also your second approach using a Windows From control hosted in IE will not work.
IE behaves different as a scripting host. There are certain limitations as a blog post by Eric Lippert mentions:
Implementing Event Handling, Part Two

I don't believe this is possible. The reason being, when you call the following code:
var excel = new ActiveXObject("Excel.Application");
You're actually opening up Excel. So with the following line:
workbook.BeforeClose = BeforeCloseEventHandler;
Its like you're telling the Excel application to run Javascript, which isn't possible. I've tried researching alternatives, like creating an event object, defining the code behind it, then assigning it to workbook.BeforeClose, but I would run into the same problem: Excel events can't be detected by javascript. Mainly because it runs as a seperate process.
So here's some more alternatives you may consider:
Save the Excel data on the user's computer, then when the user loses excel, have them click somewhere that calls your 1st method, which reads that file and displays it.
Read the data from the excel file and then display it.
Don't close your excel object (this will probably leave excel open as a process on your computer), and have a timer event in javascript. Every 5 seconds, check if Excel is still open, and if it is not open, read the file and display it.
Sorry I couldn't be anymore help, and I'm not too sure if any of those alternatives would work, but good luck!

Related

IE 11 window.open() with javascript script loading race condition issue

Goal:
I have a ReactJs web app called "ImageGallery" that's currently integrated in our business suite system(which is a huge system contains older technologies, mainly web forms). In our system, we need to add a button to launch a browser window to spin the app.
ImageGallery app rely on the current system to pass data to it to show, which means, when the ImageGallery app loads, it calls a function on the business suite system side like
window.parent.loadData()
Since the data is passed in by JavaScript, there is no ajax call.
Code:
function showImage() {
var imageGalleryWindow = window.open('/ImageGallery', '_blank', 'height=' + window.screen.availHeight + ',width=' + window.screen.availWidth + ',scrollbars=yes,menubar=no,resizable=yes,toolbar=no,location=no,status=no');
imageGalleryWindow.loadData= loadData;
}
loadData() function is available on the current JavaScript file and will be passed to the new window so it can be used by the ReactJS app.
Problem:
The app works perfectly with Chrome/Firefox/Edge, but not IE 11. IE 11 sometimes loads, sometimes not. After taking a closer look, I found there's a race condition.
Basically whenever the app failed, loadData() function has not been passed to the newly created window. Again, that's only for IE11, all other browsers seems fine.
What I've tried:
I tried the following:
1> Tried to put a wait function in ImageGallery app like this:
static resolveDocuments(){
if(typeof window.parent.loadData === 'function'){
// do the work to show image
} else {
setTimeout(this.resolveDocuments, 2000);
}
}
I am using redux thunk, I didn't get any luck in getting this to work.
2> put imageGalleryWindow.loadData= loadData; into window.onload(), bad idea but tried anyways
3> put imageGalleryWindow.loadData= loadData; into imageViewerWindow.document.addEventListener("DOMContentLoaded", function (event) {} still bad idea but tried anyways
4> created a new ImageGallery.html, use an iframe to host ImageGallery app. In section, include the JavaScript file from parent. No luck
5> created a webform page ImageGallery.aspx, use an iframe to host ImageGallery app. In section, include the JavaScript file from parent. No luck.
Thanks for taking time to finish reading this problem, and please let me know if you have an idea I can try.
Thanks so much!
Ray
The solution I end up having is html 5 local storage:
Before calling window.open() to populate the new window, create a entry in html 5 local storage with all the data I need to pass in.
When the image gallery app loads, check if local storage items exists, if so, pull the data. The data will always be there, since it sets before new window generated, and since both pages are from the same domain, they could both access the same local storage.
To avoid security issues, after image gallery app read data from the local storage, I keep a copy of the data within the app, then delete the local storage entry.
Hope this can be helpful to other people.

How to programmatically open a new spreadsheet

In setActiveSpreadSheet doc the example is very clear:
// The code below will make the spreadsheet with key "1234567890" the active spreadsheet
var ss = SpreadsheetApp.openById("1234567890");
SpreadsheetApp.setActiveSpreadsheet(ss);
however implementing that inside my Google Scripts, nothing happens at all (not even an error message).. ideas?
update
Based on the answers below, it's clear that the above code has no effect on UI.
I tried doing this:
function goToEstimate(sheetId)
{
window.open('https://docs.google.com/spreadsheets/d/'+sheetId);
}
but then I got this error:
ReferenceError: "window" is not defined.
Is there a way to access the JavaScript global window variable from within Google Apps Scripts?
An Apps Script cannot make a spreadsheet appear in a client's browser, since it is a server side script.
The method openById returns a handle on a spreadsheet which makes it possible to manipulate with the spreadsheet from the Apps Script. This is what "to open a spreadsheet" means for a script. This action has no effect on user's interface.
The method setActiveSpreadsheet is pretty much useless; it only changes which spreadsheet will be returned when a script calls getActiveSpreadsheet.
One method that does have effect on UI is setActiveSheet: if it is applied to a spreadsheet that a user has opened in their browser, the method can change which tab/sheet of that spreadsheet is facing the user.
I guess, you can't just open new tab in browser and open new spreadsheet by script. That's because script can't control browsers. But you could get data from closed Spreadsheet:
function openSheetTest() {
var newWs = SpreadsheetApp.openById('1234567890');
var sheet = newWs.getSheetByName('Sheet1');
var data = sheet.getDataRange().getValues();
Logger.log(data);
}
There's is a class Browser in G-sheets. And all we can for now is to make promts or MsgBoxes.

MS Excel and Power Point cannot properly open local hosted file through WebDAV

I am currently making a project with WebDAV to make some kind of Document Management System. It is an ASP .NET Web Application, hosted in IIS. (Although it's not using IIS WebDAV, but a modification of this project:
http://mvc4webdav.codeplex.com/
For the last few months, it was working properly, but a few days ago, Excel and PowerPoint behave wrongly.
I was using the FFWinplugin or the Sharepoint ActiveXObject (the OpenDocument Control) depending on the browser.
When the user clicks on the document link, it will trigger this function:
function editDocument(event, path) {
event.preventDefault();
if (fNewDoc) {
if (!EditDocumentButton.EditDocument(path)) {
alert(L_EditDocumentRuntimeError_Text);
}
} else {
try {
//************************ This part works for word but not excel or power point
//var ffWinPlugin = document.getElementById("winFirefoxPlugin");
//var ov = ffWinPlugin.GetOfficeVersion();
// ffWinPlugin.EditDocument(path, ov);
//*********************************
window.location.replace('ms-powerpoint:ofe|u|' + path); //But this works for excel and powerpoint
} catch (e) {
alert(L_EditDocumentError_Text);
}
}
}
fNewDoc was a flag I set up at page load to determine whether the OpenDocument Control was initialized or not in IE.
The path is something like:
http://localhost/appName/EditDocument/cb72e81f-fb9c-40af-962b-aa981b79bb72/Test.pptx
The problem is this:
When I try to open an Excel/PowerPoint file by calling the EditDocument function above, using the FFWinPlugin or OpenDocument, it is not opened for editing properly. Both just open without protected view but cannot be edited.
In Excel, it does not show read-only mode, but when I tried saving, it says Document not Saved.
In PowerPoint, it opens in read-only mode.
I debugged to see the WebDAV Requests that was made, and it turns out that both of them only requests PROPFIND over and over again after the first OPTIONS.
While if I use the window.location.replace(.....), all 3 application (Word, Excel, PP) opens the documents fine, in protected view, and can be edited. Also, it follows the usual WebDAV Request cycle (OPTIONS-HEAD-OPTIONS-LOCK-GET-PROPFIND-UNLOCK). If I enable editing, it works just fine.
I tested the application first on Office 2013 (365), and for backward compatibility, I installed Office 2010 (I looked up online, and I know afterwards that this was a bad idea to have them side by side). And this whole problem occurs after I uninstalled the 2010 version a while ago.
I tested it on another computer, the problem did not occur. Tested also on an online WebDAV demo, and no problem occured as well. So it appears that the problem only happens between Excel/PowerPoint and the localhost.
I could have just use the working method to fix this, but it will make it inflexible, since I will have to have lots of if-else statement to determine which ms office application to use. While if I use the FFWinPlugin I don't have to take care of that. So I really want to know what's happening, but I have got nothing after looking up online for a while.
How can I fix this? At first I thought that the Office 2013 installation was corrupted after uninstalling 2010, but it works when not using the FFWinplugin. So, now I am not sure what went wrong.

flashfirebug getting data from actionscript 3 console

My need was to capture data (text data) from flash in a web page.
The data is always changing (wheather data) and this should be exported do a text file so i could manipulate this data.
I tried do this with and my first approach was using a websniffer like fiddler or wireshark.
I used that but could't get data from both because it is embedded in flash.
I used fidler as man-in-midle with wireshark deciphering the data (with the private key from the site cer) but it didn't worked.
After that i tried using flashfirebug pro (the pro allows to run as3 comands in the console). This addon loads the dom tree and refreshes it. After selecting in the page the desired element with inspector (it shows in the left panel the instance and position in the dom) i have acess to the instance properties (and the only one needed is the "html-text" in the right panel).
My problem with this last approach was that it could not communicate with the local file system (if i make "trace(this.text);" in the console it shows the text value but it just shows in the console). The only way to communicate to the file in the hard drive, that i could think of was to throw some error to the log file but could't do that also.
Does anyone have any idea to work with flashfirebug or have some other approach to do this.
Regards,
if you want to work on local filesystems use adobe air.
if you can't, try to work around the browsers sandbox with javascript as bridge to some browser-plugin/-addon which gives you access to local processes and filesystems. to use javascript from flash the ExternalInterface class is your friend.

Take Screenshot of Browser via JavaScript (or something else)

For support reasons I want to be able for a user to take a screenshot of the current browser window as easy as possible and send it over to the server.
Any (crazy) ideas?
That would appear to be a pretty big security hole in JavaScript if you could do this. Imagine a malicious user installing that code on your site with a XSS attack and then screenshotting all of your daily work. Imagine that happening with your online banking...
However, it is possible to do this sort of thing outside of JavaScript. I developed a Swing application that used screen capture code like this which did a great job of sending an email to the helpdesk with an attached screenshot whenever the user encountered a RuntimeException.
I suppose you could experiment with a signed Java applet (shock! horror! noooooo!) that hung around in the corner. If executed with the appropriate security privileges given at installation it might be coerced into executing that kind of screenshot code.
For convenience, here is the code from the site I linked to:
import java.awt.Dimension;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;
import java.io.File;
...
public void captureScreen(String fileName) throws Exception {
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
Rectangle screenRectangle = new Rectangle(screenSize);
Robot robot = new Robot();
BufferedImage image = robot.createScreenCapture(screenRectangle);
ImageIO.write(image, "png", new File(fileName));
}
...
Please see the answer shared here for a relatively successful implementation of this:
https://stackoverflow.com/a/6678156/291640
Utilizing:
https://github.com/niklasvh/html2canvas
You could try to render the whole page in canvas and save this image back to server. have fun :)
A webpage can't do this (or at least, I would be very surprised if it could, in any browser) but a Firefox extension can. See https://developer.mozilla.org/en/Drawing_Graphics_with_Canvas#Rendering_Web_Content_Into_A_Canvas -- when that page says "Chrome privileges" that means an extension can do it, but a web page can't.
Seems to me that support needs (at least) the answers for two questions:
What does the screen look like? and
Why does it look that way?
A screenshot -- a visual -- is very necessary and answers the first question, but it can't answer the second.
As a first attempt, I'd try to send the entire page up to support. The support tech could display that page in his browser (answers the first question); and could also see the current state of the customer's html (helps to answer the second question).
I'd try to send as much of the page as is available to the client JS by way of AJAX or as the payload of a form. I'd also send info not on the page: anything that affects the state of the page, like cookies or session IDs or whatever.
The cust might have a submit-like button to start the process.
I think that would work. Let's see: it needs some CGI somewhere on the server that catches the incoming user page and makes it available to support, maybe by writing a disk file. Then the support person can load (or have loaded automatically) that same page. All the other info (cookies and so on) can be put into the page that support sees.
PLUS: the client JS that handles the submit-button onclick( ) could also include any useful JS variable values!
Hey, this can work! I'm getting psyched :-)
HTH
-- pete
I've seen people either do this with two approaches:
setup a separate server for screenshotting and run a bunch of firefox instances on there, check out these two gem if you're doing it in ruby: selenium-webdriver and headless
use a hosted solution like http://url2png.com (way easier)
You can also do this with the Fireshot plugin. I use the following code (that I extracted from the API code so I don't need to include the API JS) to make a direct call to the Fireshot object:
var element = document.createElement("FireShotDataElement");
element.setAttribute("Entire", true);
element.setAttribute("Action", 1);
element.setAttribute("Key", "");
element.setAttribute("BASE64Content", "");
element.setAttribute("Data", "C:/Users/jagilber/Downloads/whatev.jpg");
if (typeof(CapturedFrameId) != "undefined")
element.setAttribute("CapturedFrameId", CapturedFrameId);
document.documentElement.appendChild(element);
var evt = document.createEvent("Events");
evt.initEvent("capturePageEvt", true, false);
element.dispatchEvent(evt);
Note: I don't know if this functionality is only available for the paid version or not.
Perhaps http://html2canvas.hertzen.com/ could be used. Then you can capture the display and then process it.
You might try PhantomJs, a headlesss browsing toolkit.
http://phantomjs.org/
The following Javascript example demonstrates basic screenshot functionality:
var page = require('webpage').create();
page.settings.userAgent = 'UltimateBrowser/100';
page.viewportSize = { width: 1200, height: 1200 };
page.clipRect = { top: 0, left: 0, width: 1200, height: 1200 };
page.open('https://google.com/', function () {
page.render('output.png');
phantom.exit();
});
I understand this post is 5 years old, but for the sake of future visits I'll add my own solution here which I think solves the original post's question without any third-party libraries apart from jQuery.
pageClone = $('html').clone();
// Make sure that CSS and images load correctly when opening this clone
pageClone.find('head').append("<base href='" + location.href + "' />");
// OPTIONAL: Remove potentially interfering scripts so the page is totally static
pageClone.find('script').remove();
htmlString = pageClone.html();
You could remove other parts of the DOM you think are unnecessary, such as the support form if it is in a modal window. Or you could choose not to remove scripts if you prefer to maintain some interaction with dynamic controls.
Send that string to the server, either in a hidden field or by AJAX, and then on the server side just attach the whole lot as an HTML file to the support email.
The benefits of this are that you'll get not just a screenshot but the entire scrollable page in its current form, plus you can even inspect and debug the DOM.
Print Screen? Old school and a couple of keypresses, but it works!
This may not work for you, but on IE you can use the snapsie plugin. It doesn't seem to be in development anymore, but the last release is available from the linked site.
i thing you need a activeX controls. without it i can't imagine. you can force user to install them first after the installation on client side activex controls should work and you can capture.
We are temporarily collecting Ajax states, data in form fields and session information. Then we re-render it at the support desk. Since we test and integrate for all browsers, there are hardly any support cases for display reasons.
Have a look at the red button at the bottom on holidaycheck
Alternatively there is html2canvas of Google. But it is only applicable for never browsers and I've never tried it.
In JavaScript? No. I do work for a security company (sort of NetNanny type stuff) and the only effective way we've found to do screen captures of the user is with a hidden application.

Categories

Resources