How to get video's metadata (length) before uploading? - javascript

First thing first, I am trying to extract the video duration from the file and then display it without having to actually upload the file.
When a user selected a video - the information will be displayed below it includes file name, file size, file type. However much to my terrible skills - I cannot get the duration to display. I tried some code snippets i found from other sites as well as here but none of them seems to work. Just trying to find a simple code that would do the task.
I tried onloadedmetadata but I don't think that would even work.
Please note : I'm still learning javascript.
I also tried some sites tutorial & some code snippet I found via stackoverflow
function uploadFunction(){
//Variables
var cVideo = document.getElementById("fileUp");
var txtInfo = "";
//function
if ('files' in cVideo){
if(cVideo.files.length == 0){
txtInfo = "Please select a video";
} else {
for (var v = 0; v < cVideo.files.length; v++){
txtInfo += "<br><strong>#" + (v+1) + " File Information:</strong> <br>";
var infoFile = cVideo.files[v];
if('name' in infoFile){
txtInfo += "File name: " +infoFile.name +"<br>";
}
if('size' in infoFile){
txtInfo += "File size: " +infoFile.size +" Bytes <br>";
}
if('type' in infoFile){
txtInfo += "File Type: "+infoFile.type +" <br>";
}
if('duration' in infoFile){
txtInfo += "Duration : "+infoFile.duration +"<br>";
}
}
}
}
document.getElementById("information").innerHTML = txtInfo ;
}
HTML
<input type="file" id="fileUp" name="fileUpload" multiple size="50" onchange="uploadFunction()">
<p id="information"></p>
Can't get the duration to appear at all.

It's actually reasonably simple, but since this requires turning the file into a blob and then checking its duration with a video element, uploading a video longer than a couple of minutes will take a lot of processing and slow down your page immensly. I have added a filesize restriction (which is a reasonable start in my opinion). Here's a snippet (which will not work on Stack Overflow due to sandbox restrictions, but I did test it on a local server). I obviously also don't check MIME types or whether its a video at all, although loadedmetadata will not trigger if you upload anything that is not a video.
const fileInput = document.querySelector( 'input[type=file]' );
const fileDuration = document.getElementById( 'file-duration' )
// Listen for any change to the value of the file input
fileInput.addEventListener( 'change', event => {
fileDuration.textContent = 'Fetching video duration...';
// When the file selected by the user changes:
// - create a fresh <video> element that has not yet fired any events
// - create a file reader to safely retrieve and manipulate the file
const file = event.target.files[0];
const video = document.createElement( 'video' );
const reader = new FileReader();
// Cancel if the initial filesize just seems too large.
// If the maximum allowed is 30 seconds, then a Gigabyte of file seems too much.
if( file.size > 10000000 ){
fileDuration.textContent = `File is too large (${file.size}). Refused to read duration.`;
return;
}
// Before setting any source to the <video> element,
// add a listener for `loadedmetadata` - the event that fires
// when metadata such as duration is available.
// As well as an error event is case something goes wrong.
video.addEventListener( 'loadedmetadata', event => {
// When the metadata is loaded, duration can be read.
fileDuration.textContent = `Video is ${video.duration} seconds long.`;
});
video.addEventListener( 'error', event => {
// If the file isn't a video or something went wrong, print out an error message.
fileDuration.textContent = `Could not get duration of video, an error has occurred.`;
});
// Before reading any file, attach an event listener for
// when the file is fully read by the reader.
// After that we can use this read result as a source for the <video>
reader.addEventListener( 'loadend', function(){
// reader.result now contains a `blob:` url. This seems like a
// nonsensical collection of characters, but the <video> element
// should be able to play it.
video.src = reader.result;
// After we assigned it to the `src` of the <video>, it will be
// act like any other video source, and will trigger the related
// events such as `loadedmetadata`, `canplay`, etc...
});
// Instruct the reader to read the file as a url. When its done
// it will trigger the `loadend` event handler above.
reader.readAsDataURL( file );
});
<input type="file" />
<p id="file-duration">No video file</p>

Related

HTML IFrame not allowed to download file

im trying to download a file that constructs itself based on the value it recives. This is my code
<html>
<head>
<script>
var myList=[];
window.onmessage = function(event){
if (event.data) {
myList = event.data;
if (myList.length>0) {
buildHtmlTable();
}
}
else {
myList = [];
}
};
function buildHtmlTable() {
var columns = addAllColumnHeaders(myList);
for (var i = 0 ; i < myList.length ; i++) {
var row$ = $('<tr/>');
for (var colIndex = 0 ; colIndex < columns.length ; colIndex++) {
var cellValue = myList[i][columns[colIndex]];
if (cellValue == null) { cellValue = ""; }
row$.append($('<td/>').html(cellValue));
}
$("#excelDataTable").append(row$);
}
return exportF(); // Make Excel file download now
}
function addAllColumnHeaders(myList)
{
var columnSet = [];
var headerTr$ = $('<tr/>');
for (var i = 0 ; i < myList.length ; i++) {
var rowHash =`enter code here` myList[i];
for (var key in rowHash) {
if ($.inArray(key, columnSet) == -1){
columnSet.push(key);
headerTr$.append($('<th/>').html(key));
}
}
}
$("#excelDataTable").append(headerTr$);
return columnSet;
}
function exportF() {
var table = document.getElementById("excelDataTable");
var html = table.outerHTML;
var url = 'data:application/vnd.ms-excel,' + escape(html);
var link = document.getElementById("downloadLink");
link.setAttribute("href", url);
link.setAttribute("download", "export.xls"); // Choose the file name here
link.click(); // Download your excel file
return false;
}
</script>
</head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<body onLoad="">
<table id="excelDataTable" border="1">
</table>
<a style="display: none" id="downloadLink"></a>
</body>
</html>
The code itself works, but the error i get is "Download is disallowed. The frame initiating or instantiating the download is sandboxed, but the flag ‘allow-downloads’ is not set. See https://www.chromestatus.com/feature/5706745674465280 for more details."
What can i do to work around this? It feels like ive tried everything i can get my hands on but nothing seems to work for it to download
As the warning message says, you can't initialize downloads from a sandboxed iframe if it doesn't have the allow-downloads permission.
All solutions will imply having access to the page where the iframe is displayed (hereafter "the embedder").
The easiest and recommended way,
is to ask the embedder to add this permission when they define their iframe:
<iframe src="yourpage.html" sandbox="allow-scripts allow-downloads"></iframe>
An other way would be to ask them to not sandbox that iframe at all,
<iframe src="yourpage.html"></iframe>
but I guess that if they did, it's because they don't trust your page enough.
Finally a more complex way would be to pass the generated file back to the parent window.
For this you'd need to define a new API with your clients.
You could obviously just emit a global message event back to them, but I guess the cleanest is to make them pass a MessageChannel's MessagePort along with the myList data, so they can wait for the response there easily and be sure they'll only catch the response and no other unrelated message.
So in the embedder page they'd do
frame.onload = (evt) => {
const channel = new MessageChannel();
// handle the response from the iframe
channel.port2.onmessage = (evt) => {
const file = evt.data;
saveAs( file, "file.html" ); // the embedder is reponsible to initialize the download
};
frame.contentWindow.postMessage( embedders_data, [ channel.port1 ] );
};
And in your page you'd do
window.onmessage = (evt) => {
const myList = evt.data;
// get the MessageChannel's port out of the transfer-list
const port = evt.ports[ 0 ];
// buildHtmlTable has to return the final file, not to make it download
const file = buildHtmlTable( myList );
if( port ) {
port.postMessage( file ); // send back to embedder
}
};
See it as a live plnkr.
Ps: note that your files are not xlsx files but HTML markup.
The correct answer
Under normal circumstances Kaiido's answer is indeed the correct solution to your problem. They will NOT work in your case though.
The answer that will work on WixSince you are using Wix there is no way for you to directly edit the Sandbox attribute of the iframe element. This is just how Wix does things. You can, however, use custom code (only applies to premium websites) to get the class name of the iframe and programatically use javascript to set the new attribute to the existing iframe.
You must use the web inspector to find out the class name (iframes in Wix do not have ids) then add "allow-downloads" to the sandbox attribute. You might then need to reload the iframe using js as well. Go to your website's settings -> Custom Code -> Create custom code at the end of the body tag
If you do not have a premium website then you unfortunately cannot do this. This is due to Wix's own limitations as a platform. If this is an absolute "must" for you project, I recommend you to not use Wix since they limit your freedom as a developer when it comes to working with
technologies that were not made by them. Not to mention that they lock features such as custom elements behind a pay wall. So we can't even test our ideas before committing to a hosting plan. For anyone reading this in the future, take this into consideration and look into other platforms.
Thanks for answers, i didnt find a sollution with the recomended answers. What i did is that i made a completely new page, instead of initializing a html iframe i redirected the current window to the new page i created. The new page took a variable from "www.page.com/?page={value} and downloaded what i needed from there instead. Its messy but it works so if anyone else has this problem i recomend this if you are using wix.

Endless looping when src value is changed in Internet Explorer

I have a problem with some javascript in Internet Explorer.
It works fine in other browsers.
I have the following method, that changes the src property of an images and when this happens a download of that image should start. See below:
for (var i = 0; i < imagesStartedDownloading.length; i++) {
if (imagesStartedDownloading[i] == false && responseItems[i] == true) {
console.log("image", i);
var url = baseurl + "/ImageDownload/?imageName=" + hash + "_" + imageDegrees[i] + ".jpg" + "&r=" + Math.random();
imagesStartedDownloading[i] = true;
images.eq(i).attr("src", url);
}
}
The problem is that in when changing this property Internet Explorer starts an endless loop of downloading images. Notice that i have put a console.log in the for-loop. I can confirm that this for-loop does not run in an endles loop. It is only run once for each image that should be downloaded. So that is not the problem.
The behaviour can actually be seen on this page: http://www.energy-frames.dk/Konfigurator. Hit F12 and check in the network tab. Make a change to image on the homepage so a download of new images is started, e.g. Bredde(Width in english), see below:
When this change is made new images are downloaded in an endless loop(it happens almost every time in IE). See below of what you could change
I have really spent a lot of time debugging in this and i cant see why it behaves like this in IE but not in all other browsers.
So does anyone have any idea why this happens? Or have some suggestions on what i could try?
EDIT:
#gxoptg
I have tried what you suggested. using "javascript:void 0" like this:
var newimg = $("<img class='rotator" + (isMainImage ? " active" : "") + "' src='javascript:void 0' />");
and this:
img.attr("src", "javascript:void 0");
gives me this error:
On the other hand, if i completely remove the line img.attr("src", "");
in the imgLoadError method, then i see that images are not downloaded in an endless loop. On the other hand they are not displayed. So am i using the javascript:void 0 wrong?
When i do the following:
img.attr("src", "void(0)");
Then the there is not endless loop but the image wont appear in IE - still works fine in chrome.
Here’s the reason:
for (var i = 0; i < totalnumberofimages; i++) {
var url = "";
var isMainImage = i == currentDragImg;
var newimg = $("<img class='rotator" + (isMainImage ? " active" : "") + "' src='' />");
newimg.on("error", imgLoadError);
newimg.on("load", imgLoaded);
imgcontainer.append(newimg);
}
Note the var newimg = $(...) line. In Internet Explorer, setting an empty src attribute on an image triggers the error event. Due to the error, the imgLoadError function is called. It looks like this:
function imgLoadError(e) {
var img = $(e.currentTarget);
var imgSrc = img.attr("src");
if (imgSrc.length > 0 && img.width() <= 100) {
setTimeout(function () {
var imgUrl = img.attr("src");
img.attr("src", "");
img.attr("src", imgUrl);
}, 200);
}
}
In this function, you run img.attr("src", ""), which sets the empty src attribute, triggers the error event, and calls imgLoadError function again. This causes the endless loop.
To prevent the error (and therefore the endless loop), set image source to "javascript:void 0" instead of "" in both code pieces. This source is valid and should work properly.
(According to comments, all the code is located in /Assets/Scripts/directives/image.rotation.directive.js file.)
An alternative solution is to set the src attribute to a valid, minimal, Base64 encoded image, as in http://jsfiddle.net/leonardobraga/1gefL8L5/
This would avoid triggering the endless error handling and it doesn't impact the code size that much.

How to display a progress status/spinner when outputting file via Response.ContentType, Response.End?

I have a webform with a Download LinkButton, on the button's click event I'm fetching data and then generating an .XLSX file for download. During the generation of the file, Response.Clear() is called, Response.ContentType is set and eventually Response.End() is called.
I need to display a spinner .gif during that operation. Once the file has been generated and the file "Open/Save" dialog pops up, the spinner should not display. Unfortunately, since I'm changing the content type and calling Response.End there is no response returned back to the page to work with.
Can anyone please provide a little help with this scenario?
I actually ended up going with a solution that did not require the use of an iFrame. Instead I make use of a Cookie, which the file generation process writes into it simple a Name Value pair. This cookie can then be checked later from the client side via JavaScript to determine when the response (.xlsx file) has completed.
The result should be that the loading spinner image should display until the .xls file has been generated and returned to the client (Cookie contains the DownloadComplete=true Name Value pair).
In the OnClientClick event for the LinkButton:
function startFileDownload(){
// Set Timout for calling checkState function again
setTimeout(checkState, 1100);
setCookie('DownloadComplete', '', 1);
// Download is starting, Hide Download LinkButton
document.getElementById('<%= btnDownloadExcel.ClientID%>').style.display = "none";
// Display progress spinner
var img = document.getElementById("image1");
img.src = "Images/99.GIF";
document.getElementById('image1').className = "spinnerDisplay";
}
Here is the JavaScript code for the checkState function:
function checkState()
{
var img = document.getElementById("image1");
var finished = getCookie("DownloadComplete");
// Check to see if download is complete
if (!isEmpty(finished)) {
setCookie('DownloadComplete', '');
// Download is complete, Hide progress spinner
img.className = "spinnerHide";
document.getElementById('<%= btnDownloadExcel.ClientID%>').style.display = "";
} else {
// Refresh progress spinner, Set Timout for calling checkState function again
img.src = "Images/99.GIF";
setTimeout(checkState, 1100);
}
}
Here is the JavaScript code for the setCookie and the getCookie functions:
function setCookie(cName, value){
var now = new Date();
var time = now.getTime();
time += 3600 * 1000;
now.setTime(time);
document.cookie = cName + "=" + value
+ '; expires=' + now.toGMTString() + '; path=/';
}
function getCookie(cName)
{
var i, x, y, ARRcookies = document.cookie.split(";");
for (i = 0; i < ARRcookies.length; i++) {
x = ARRcookies[i].substr(0, ARRcookies[i].indexOf("="));
y = ARRcookies[i].substr(ARRcookies[i].indexOf("=") + 1);
x = x.replace(/^\s+|\s+$/g, "");
if (x == cName) {
return unescape(y);
}
}
}
Then on the server side in the class that generates the .xlsx file, after clearing the HTTP Response add a Name Value pair to the Cookie:
HttpContext.Current.Response.Clear();
// Append a cookie that will tell the browser the file has finished processing
// and is included in that stream (note specific path must match same path for cookie set in JavaScript)
HttpCookie cookie = new HttpCookie("DownloadComplete", "true");
cookie.Expires = DateTime.Now.AddMinutes(60);
cookie.Path = "/";
HttpContext.Current.Response.AppendCookie(cookie);
//Other code here to specify the MIME type, setup the HTTP header
//and Response.BinaryWrite out the file
HttpContext.Current.Response.End();
It should go like this : (one solution among others)
1) create an iframe on your page which will actually download your file ( so your main page wont be blank )
2) Add a cookie to the response. via
Response.AddHeader("content-disposition", "attachment;filename=1.jpg");
3) when a user press "download" you set (via js) the src for the iframe to download the file. and you show the spinner. after this , you start reading via setInterval , if theres a cookie. if thereis - the download has finished so you can hide the spinner.

Return address of image or error if it doesn't exist

I know my problem is the fact that I can't check if the image is good until it has time to load. I'm trying to check width and return after it or the src changes (using onerror to set an src.) I'm always getting stuck with a race condition though, and it errors out long before the height and width or src change. When I reload, the image is cached and it works fine. I don't know how to avoid this. Here is my current code (also not working, it loops until Firefox complains.) I'm setting the return of this function to a variable. There are some writes in there I'm using to see how far it gets, and it stops in the while loop. I tried using getTimeout('tstit = chkload("thisisatest",tinypth);',250);, but that didn't work either. I wish I could force this to load in order...
function buildimg(tehname,myc,pth1,pth2,tinypth)
{
var myimg = pth1+tehname+"/"+tehname+"-"+myc+".jpg";
buildtest("thisisatest",myimg,tinypth);
var testimg = document.getElementById("thisisatest");
var tstit = chkload("thisisatest",tinypth);
while(tstit == false) {
document.write(tstit);
tstit = chkload("thisisatest",tinypth);
}
alert(testimg.src+'-'+testimg.width+'-'+testimg.height);
if((testimage.width > 20) && (testimage.height > 20)) {
return myimg;
}
else if(typeof pth2 == "undefined") {
myimg = "error";
return myimg;
}
else {
myimg = buildimg(tehname,myc,pth2);
return myimg;
}
document.write('No return error in buildimg.');
return "error";
}
/*Builds a hidden img tag for testing images*/
function buildtest(itsid,itssrc,tinypath) {
if(document.getElementById(itsid)) {
var imgobj = document.getElementById(itsid);
imgobj.remove();
}
document.write('<img id="'+itsid+'" style="display: none;" src="'+itssrc+'" onerror="swaponerr(\''+itsid+'\',\''+tinypath+'\')" />');
}
/*Swaps the image to a small picture, so we can detect if it worked*/
function swaponerr(tagid, tinypath) {
var theimg = document.getElementById(tagid);
theimg.onerror = '';
theimg.src = tinypath;
}
/*Recurses to return when the image is loaded*/
function chkload(loadid,tinychk) {
var tehobj = document.getElementById(loadid);
document.write(tehobj.width+'x'+tehobj.height+'x'+tehobj.src);
if((tehobj.naturalWidth > 20) && (tehobj.naturalHeight > 20)) {
return true;
}
if(tehobj.src == tinychk) {
return true;
}
return false;
}
I need to test for an image, and return error if it is non-existent. The code below works fine on my server:
/*Checks if the image at /tehname/tehname-c.jpg exists in either of the paths
outputs it's address if it does, and "error" if not.*/
function buildimg(tehname,index,pth1,pth2)
{
var myimg = pth1+tehname+"/"+tehname+"-"+index+".jpg";
$.ajax({
url:myimg,
type:'HEAD',
error: function()
{
myimg=pth2+tehname+"/"+tehname+"-"+index+".jpg";
$.ajax({
url:myimg,
type:'HEAD',
error: function()
{
myimg="error";
return;
},
});
return;
},
});
return myimg;
}
Unfortunately, I'm trying to do this on a messed up system my work uses. We do have jquery, but the system stores user files on a separate server from code, so ajax won't work. This will eventually be in a .js file, I hope.
Now I've got code starting with:
function buildimg(tehname,myc,pth1,pth2)
{
var myimg = pth1+tehname+"/"+tehname+"-"+myc+".jpg";
var tehimage = new Image();
tehimg.src = myimg;
I tried to have the function load the image, and check its width, but I always get 0, since I can't pre-load the images without knowing how many they are (and I don't want to have some outrageously high number of requests with most being errors.) For some reason (at least on Firefox 4, as that's what my goal is to get working first) tehimage.complete always returns false. I've tried using onerror and onload by a global variable, and a few other methods. I must admit though, I'm not very versed in Javascript, so my callbacks may not have worked.
Please help, I'm getting desperate!
Do you hold the 'power to execute php code' on that user files server? If so, check them in php and output to images.js in that server, than in your html, first load images.js, then your usual javascript.
By the way, jQuery had memory leaks in ajax for ears, don't know about 1.6, but 1.5 definately had those. if you wan't to use ajax to get data from server to javascript - use plain javascript: http://www.w3schools.com/ajax/ajax_xmlhttprequest_send.asp
Edit: Definately worth checking out: http://fettig.net/weblog/2005/11/28/how-to-make-xmlhttprequest-connections-to-another-server-in-your-domain/
Since I'm getting no answers, I'm closing the question. What I ended up doing was a modification to make the image change class in the onload, have the image onerror delete it, and have the rest of the code run in the window onload. Unfortunately, this means the 404s aren't caught before it tries to load more images, so I'm forced to limit the max number of images that can be used (changeable in the function call,) and the images that aren't there just waste a little time.
See the final result here.

Javascript - getting path of file dropped into HTA

I am building a little HTA for personal use, and would like to be able to drag and drop a file to the interface. Once the file is dropped, I would either automatically run it (assuming it fits some parameters I set, like file extension), or at least fill in the input box on the HTA interface.
I've searched extensively, but can't find a solution. Thoughts?
Tomalak, is incorrect in his statement...there is way to do what you want except that you have to add the DropHandler in the registry for HTA files it's really easy to do and once done you will be able to do exactly what your trying to do. I couldn't find much documentation on it, but here is a link to an HTA that was written a long time ago by a guy named Michel Gallant, that shows you how to it: https://www.jensign.com/www/wsh/imager/
When the HTA is launched it looks to see if you have the DropHandler already configured. If you don't it gives you the option for it to configure it for you. Once configure all you have to do is close and reopen the HTA and wallah, there you go Drag and Drop support in HTA files.
If you don't want to enable the drop handler, I could imagine a way that this might be possible. It's a bit of a comedy chain, but I could see myself implementing this if I was backed into a corner and needed that functionality.
You can create an IFRAME which has its src as a temp folder somewhere. This folder will be displayed as an Explorer view. You can then drag files into that. Set up a polling routine against that folder to see if there are any new files. And voila, you have a lame way to support drag and drop operations with a given file.
An HTA obviously cannot be target of a shell drop operation – at least on my system, dropping something on an HTA is impossible.
This would imply you cannot directly do what you intend.
A .vbs can however be a drop target. Full paths of the dropped files are available via the WScript.Arguments.Unnamed collection.
HTA has access to it's command line arguments via the commandLine Property. This would mean you could build a small helper VBScript that translates the dropped files into a command line and calls the HTA for you.
Note that you cannot drop endless amounts of files on a .vbs, and command lines are not unlimited either. There will be a length limit in the area of a few kB (I have not tried to find where exactly the limit is, just be prepared to face a limit.)
Go and try google gears which supplies drag and drop.
You can even use mysql in hta.
Google Gears is not available in hta, however, you can create the activexobject in a html file, then include it using an iframe(<iframe application="true" src=".."></iframe>)
After that, you can use the activexobject through the iframe.
Regarding …
“would like to be able to drag and drop a file to the [HTA] interface”
… which I interpret as a desire to drop files to the HTA’ running window, rather than dropping files on the HTA file itself or a shortcut to it.
With HTML5 the dropping itself is easy. Use e.g. a <div> element as a drop area. For this element handle the events dragenter, dragover and drop. E.g. the drop handler can look like this:
function on_drop( e )
{
e.preventDefault(); // stops the browser from redirecting off to the file
var dt = e.dataTransfer
var is_file_transfer = false;
for( var i = 0; i < dt.types.length; ++i )
{
if( dt.types[i].toLowerCase() == 'files' )
{
is_file_transfer = true;
}
}
if( !is_file_transfer )
{
return false;
}
on_files_dropped( dt.files );
return true;
}
… where on_files_dropped is a function defined by you that handles a files drop.
Adding the event handlers dynamically in the document loaded event, can look like this:
var dropbox = document.getElementById( 'blah' );
dropbox.addEventListener( 'dragenter', on_dragenter, false );
dropbox.addEventListener( 'dragover', on_dragover, false );
dropbox.addEventListener( 'drop', on_drop, false );
So far so good.
However, security intervenes with a restriction: you do not get direct knowledge of the original file paths, only the file names and the file sizes. For this functionality is designed for the web, not for local trusted HTML applications. So it may or may not necessarily be a problem.
For the purpose of using a dropped file as a source for an HTML element, and generally for reading a dropped file, HTML5 provides a FileReader (there are a number of tutorials available, which link further to technical documentation).
Where a local path is needed, e.g. for playing a file in Windows Mediaplayer, you can assume that the drag operation originated with Windows Explorer, now also called File Explorer, and then just check which Explorer window, if any, contains a file with that name and size.
Hopefully not more than one such originating window will be found.
var shell = new ActiveXObject( "Shell.Application" );
var fso = new ActiveXObject( "Scripting.FileSystemObject" );
function possible_paths_for( filename )
{
var windows = shell.windows(); // Windows Explorer windows.
var n_windows = windows.Count;
var lowercase_filename = filename.toLowerCase();
var paths = Array();
for( var i = 0; i < n_windows; ++i )
{
var url = windows.Item(i).LocationURL;
var path = decodeURI( url.substr( 8 ) ).replace( /\//g, '\\' );
// The path can be the path of this HTML application (.hta file), so:
if( fso.FolderExists( path ) )
{
var folder = fso.GetFolder( path );
for( var it = new Enumerator( folder.Files ); !it.atEnd(); it.moveNext() )
{
var file = it.item();
if( file.Name.toLowerCase() == lowercase_filename )
{
paths.push( file.Path.toLowerCase() );
}
}
}
}
return paths;
}
Essentially that’s it. Except, maybe, since HTAs default to IE7, how does one get HTML5 functionality. Well may through doctype declaration, but so far in my little experimentation I just use the following:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<!-- A windows web control defaults to quirky IE7 semantics. Request for better: -->
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta http-equiv="MSThemeCompatible" content="yes">
This gives you latest Internet Explorer engine, but at the cost of no HTA element, and hence no direct access to the command line. I found that the command line can be retrieved by running Windows’ wmic program, but that’s an awful hack. This whole problem area, with most apparently open roads turning out to be closed, appears to be a consequence of Microsoft now considering HTAs a legacy technology, to be quietly phased out in favor of fullscreen ad-ridden Windows 8 AppStore apps.
Anyway, good luck!
I can confirm it is possible to obtain drag and drop functionality for files and folders in HTA window with "Web Browser" (no need to use any third party ActiveX objects, HTML5 or manipulations with the registry).
Its "RegisterAsDropTarget" parameter switched on allows such operations while "Web Browser" itself is built-in in every Windows since XP or 2000 by default. This way it works for input files named in any locale (Unicode names support) while, for example, WScript and CScript don't support this by default.
Below is a sample implemented as a self-contained component with many customized features including styles and backgrounds. It works both for 64 and 32 bit folder paths and can be inserted into DOM tree for certain window.
Save the source code below as a text file and then change its extension to "hta".
Then launch the obtained application by double click on it.
<script>
/*
Extended Drop Target v1.1.4 (https://tastyscriptsforfree.wix.com/page/scripts)
Copyright 2017-2020 Vladimir Samarets. All rights reserved.
tastyscriptsforfree#protonmail.com
Release date: November 9, 2020.
Use this script sample entirely at your own risk.
This script sample is copyrighted freeware and I am not responsible for any damage or data loss it could unintentionally cause.
You may modify it but please leave a comment with direct link to https://tastyscriptsforfree.wix.com/page/scripts in that case.
*/
offscreenBuffering = true; //postpone the application window appearance till its UI is ready
var O = function(o){return new ActiveXObject(o);},
WSS = O('WScript.Shell'),
env = WSS.Environment('Process'),
head = document.documentElement.firstChild, //head
PID; //PID of 64 bit HTA instance
if(!env('is64bit')) //indicates whether the application is launched as 64 bit or not
{
!function hide(e){try{moveTo(10000, 10000);}catch(e){try{hide();}catch(e){hide();}}}(); //hiding the application window
head.insertBefore(document.createElement('<hta:application showInTaskBar=0>'), head.firstChild); //hiding the application in the Taskbar
var WMI= //a small library written by me for obtaining WMI instance, its common methods and properties
{ //below is a sample of creating a process with certain window shifts and environment variables
//and obtaining its <ProcessId> by using WMI
SWL:new ActiveXObject('WbemScripting.SWbemLocator'),
PRMS:function(p)
{
var s = WMI.PS.SpawnInstance_();
for(var i in p)
s[i] = p[i];
return s;
},
Create:function(c, s, d)
{
var CreateIn = WMI.CreateIn.SpawnInstance_();
CreateIn.CommandLine = c;
CreateIn.ProcessStartupInformation = s;
CreateIn.CurrentDirectory = d;
return WMI.PRC.ExecMethod_('Create', CreateIn).ProcessId;
}
};
WMI.PRC = (WMI.WM = WMI.SWL.ConnectServer('.', 'root/cimv2')).Get('Win32_Process');
WMI.PS = WMI.WM.Get('Win32_ProcessStartup');
WMI.CreateIn = WMI.PRC.Methods_('Create').InParameters;
var ID = O('Scriptlet.TypeLib').GUID.substr(0, 38), //the unique ID obtaining
EV = 'is64bit='+ID; //passing the unique ID to 64 bit HTA instance as an Environment variable
for(var items = new Enumerator(env); !items.atEnd(); items.moveNext())
EV += '?' + items.item(); //obtaining all Environment variables for current process
PID = WMI.Create //obtaining PID of 64 bit HTA instance
(
'mshta "' + decodeURIComponent(location.pathname) + '"', //current path
WMI.PRMS
(
{
X:10000, Y:10000, //hiding the application window before it is shown in order to resize it smoothly
EnvironmentVariables:
EV.split('?') //obtaining an array of all Environment variables by using this approach is universal for different
//versions of Windows
/*
[ //another way to pass certain Environment variables
'is64bit='+ID, //indicates that the application is launched as 64 bit
'SystemRoot='+env('SystemRoot'), //for start
'SystemDrive='+env('SystemDrive'), //for hyperlinks
'TEMP='+env('TEMP'), //for "mailto" links
'CommonProgramW6432='+env('CommonProgramW6432') //for ADODB.Stream
]
*/
}
)
);
head.firstChild.insertAdjacentHTML('afterEnd', '<object id=' + ID + ' PID=' + PID +
' classid=clsid:8856F961-340A-11D0-A96B-00C04FD705A2><param name=RegisterAsBrowser value=1>'); //registering current HTA window in collection of windows
showModalDialog('javascript:for(var ws=new ActiveXObject("Shell.Application").Windows(),i=ws.Count;i-->0;)if((w=ws.Item(i))&&w.id=="'+ID+'"){w.s=document.Script;break;}', 0,
'dialogWidth:0;unadorned:1;'); //silent stop of the script and obtaining window focus for "AppActivate"
close();onerror=function(){close();};throw 0; //declining any further attempts of executing the rest statements of the code
}
var w,dt=new Date();
head.insertBefore(document.createElement('<hta:application contextMenu=no selection=no scroll=no>'), head.firstChild); //adding custom HTA header dynamically
document.title='Extended Drop Target';
resizeTo(800, 400);
for(var ws = O('Shell.Application').Windows(), i = ws.Count; i -- > 0;)
if((w = ws.Item(i)) && w.id == env('is64bit'))
{
PID = w.PID;
w.document.Script.WSS.AppActivate(PID); //using "WScript.Shell" in focus to activate
//the application window of 64 bit HTA instance;
//remember that "WScript.Shell" should be
//in focus in order "AppActivate" to work properly
break;
}
document.write('<body>'); //obtaining body
if(w && w.id == env('is64bit'))
w.s.close(); //closing previous 32 bit HTA instance while being in safe focus
document.body.appendChild(document.createTextNode('Debug screen (for test purposes only):'));
document.body.appendChild(document.createElement('br'));
document.body.appendChild(document.createElement('<textarea id=result cols=85 rows=5>'));
document.body.appendChild(document.createElement('p'));
document.body.appendChild(document.createTextNode('Extended Drop Target:'));
document.body.appendChild(document.createElement('br'));
document.body.appendChild
(
(
function createDropTarget(doc, filesAllowed, foldersAllowed, dTStyle, hdFont, wMColor, dMColor, pMColor, eMColor, process, resolve, msg, dBgImage, bBgImage,
description, redirect)
{
var dropTarget = doc.createElement('<span style="' + dTStyle + '">'),
ms = dropTarget.appendChild
(
doc.createElement('<span style="width:100%;height:100%;padding:10px;overflow:hidden;">')
), //message screen that hides Web Browser during dropped items processing
WB = '<object classid=clsid:8856F961-340A-11D0-A96B-00C04FD705A2 style="width:100%;height:100%;"><param name=Location value="about:<body onload=\'b=0;\'' +
' ondragover=(function(){clearTimeout(b);b=setTimeout(\'location.reload();\',100);}()) bgColor=' + dMColor +
' style=\'width:100%;height:100%;position:absolute;margin:0;border:0;overflow:hidden;\'>'+ (description || '') +'">',
processing = 1, //indicates whether a dropped item processing is started or not
processed = 1, //indicates whether a dropped item is processed or not
DBcatched = 1, //indicates whether DownloadBegin Web Browser event has been catched or not
allowed, //indicates whether drop target is allowed or not
allowedText = (filesAllowed ? foldersAllowed ? msg[32] : msg[33] : foldersAllowed ? msg[34] : ''), //"Drop a file or folder here."
WBTReset, //timeout for Web Browser reset
startProcessing = function(p) //processing the item path received after item drop (item path)
{
clearTimeout(WBTReset);
dropTarget.children[processed = 1].removeNode();
createDropTarget();
setTimeout(function()
{
var delay = 0;
if(p) //the item can be accessed
{
sM(msg[38] + p + '</div>', pMColor); //show "Processing"
var dt = new Date(), //date before processing
e; //error ID
try{e = process(p);}catch(e){e = 43;} //unknown error occured
dt = new Date() - dt; //date after processing
delay += dt>1000 ? 0 : 1000 - dt;
if(!e) //no errors occured
setTimeout(function(){sM(msg[39] + createDropTarget.timeToHMSR(dt) + ' =-</div>', pMColor);}, delay); //show "Processed in"
else //an error occured
{
var err;
try{resolve(e);}catch(err){;}
setTimeout(function(){sM(msg[39] + createDropTarget.timeToHMSR(dt) + ' =-</div><br>' + msg[e], eMColor);}, delay); //show "Processed in" with error
if(!redirect)
delay += 1000;
}
}
else //the item can't be accessed
{
sM(msg[40] + msg[41] + allowedText + msg[42], eMColor); //show "The item is not a file or folder, can't be accessed or its size is too big."
delay += 1000;
}
sDM(delay + 1000);
}, 1000);
},
setWBTReset = function(r) //setting a timeout for Web Browser reset (reset)
{
if(!processing)
{
processing = 1;
ms.style.display = '';
if(r)
WBTReset = setTimeout(startProcessing, 1000);
}
},
DB = function() //catching "DownloadBegin" Web Browser event
{
DBcatched = 1;
setWBTReset(1);
},
STC = function(p) //catching "StatusTextChange" Web Browser event (item path)
{
setWBTReset(p);
if(!processed && /file:|</.test(p))
{
if(/file:/.test(p))
startProcessing(filesAllowed ? decodeURIComponent(p).replace(/.+:((?:\/{3})|(?=\/{2}))(.+)...$/,'$2').replace(/\//g,'\\') : 0);
else if(/</.test(p))
{
if(!DBcatched) //indicates that drop target is leaved without drop
{
processed = 1;
clearTimeout(WBTReset);
sM(msg[31] + allowedText + msg[35] + '</div>', dMColor, dBgImage); //show "Drop a file or folder here."
allowed = 1;
ms.style.display = '';
}
else //shortcuts with complex structure
startProcessing();
}
}
},
NC2 = function(o, p) //catching "NavigateComplete2" Web Browser event (Web Browser object, item path)
{
if(!processed)
startProcessing(foldersAllowed && typeof p == 'string' && p.match(/^[^:]/) ? p : 0);
},
NE = function() //catching "NavigateError" Web Browser event
{
if(!processed)
startProcessing();
},
sM = function(m, c, bgImage) //show message (message, background or text color, background image)
{
if(dBgImage || bBgImage)
{
if(bgImage)
ms.style.backgroundImage = 'url(' + bgImage + ')';
ms.style.color = c;
}
else
ms.style.backgroundColor = c;
m = '<div style="font:' + hdFont + ';">' + m;
if(!redirect)
ms.innerHTML = m;
else
redirect(m);
},
sDM = function(delay) //show default message (delay)
{
setTimeout(function(){allowed = 1;}, delay);
setTimeout(function(){if(allowed)sM((allowedText ? msg[31] + allowedText + msg[35] : msg[36]) + '</div>', dMColor, dBgImage);}, delay + 100); //show "Drop a file or folder
//here." or "Drop Target is
} //disabled."
sM(msg[30], wMColor, dBgImage); //show welcome message
ms.ondragenter=function()
{
if(allowed && (filesAllowed || foldersAllowed) && !event.dataTransfer.getData('text')) //text dropping is not allowed
{
event.dataTransfer.dropEffect='move';
return false;
}
}
ms.ondragover = function()
{
if(allowed && (filesAllowed || foldersAllowed) && !event.dataTransfer.getData('text')) //text dropping is not allowed
{
event.dataTransfer.dropEffect='move';
if(!Math.abs(event.x - this.x) && !Math.abs(event.y - this.y)) //accepting only slow mouse motion
{
this.style.display = 'none';
DBcatched = allowed = processing = processed = 0;
sM(msg[37], dMColor, bBgImage); //show "Analysing..."
}
this.x = event.x;
this.y = event.y;
return false;
}
}
!(createDropTarget = function()
{
dropTarget.insertAdjacentHTML('beforeEnd', WB);
with(dropTarget.children[1])
{
RegisterAsDropTarget = Silent = Offline = 1;
attachEvent('DownloadBegin', DB);
attachEvent('StatusTextChange', STC);
attachEvent('NavigateComplete2', NC2);
attachEvent('NavigateError', NE);
}
})();
createDropTarget.timeToHMSR = function(d) //convert date to hours, minutes, seconds and remainder (milliseconds) notation (date)
{
var r = d % 3600000,
h = d / 3600000 ^ 0, //hours
m = r / 60000 ^ 0, //minutes
s = r % 60000 / 1000 ^ 0; //seconds
r = d % 1000; //remainder (milliseconds)
return ((h ? h + 'h' : '') + (m ? (h ? ' ' : '') + m + 'm' : '') + (s ? (h || m ? ' ' : '') + s + 's' : '') + (r ? (h || m || s ? ' ' : '') + r + 'ms' : '')) || '0ms';
},
sDM(3000); //postponing Web Browser access while it generates its events at start
return dropTarget;
}
(
//BEGIN of Extended Drop Target custom settings
document, //"document" object of certain window
1, //indicates whether processing of files is allowed or not
1, //indicates whether processing of folders is allowed or not
'width:350px;height:150px;border:2px blue solid;font:bold 10pt Arial;text-align:center;cursor:default;overflow:hidden;word-break:break-all;', //drop target style
'bold 12pt Tahoma', //message header font
'yellow', //welcome message background color if background image is not set or text color otherwise
'mediumaquamarine', //default message background color if background image is not set or text color otherwise
'limegreen', //processing message background color if background image is not set or text color otherwise
'salmon', //error message background color if background image is not set or text color otherwise
function(p) //data processing sample (file or folder path)
{
alert('Here goes data processing sample.\n\nProcessing:\n' + p);
//throw 1; //unknown error occured
//return 1; //certain error 1 occured
return 0; //no errors
},
function(e) //error resolving sample (error ID)
{
switch(e)
{
case 1:
result.value = '\nCertain error 1 is catched.'; //additional action sample for certain error 1
updateData1();
break;
default:
result.value = '\nAn unknown error is catched.'; //additional action sample for an unknown error
sendEmail();
break;
}
file.Close(); //built-in silent catching of errors if certain error resolving method is still inaccessible
},
{ //list of all messages for Extended Drop Target
30: 'Welcome!</div><br>Hello World!', //welcome message
31: 'Drop a ', //31, 32, 33, 34, 35 - "Drop a file or folder here."
32: 'file or folder',
33: 'file',
34: 'folder',
35: ' here.',
36: 'Drop Target is disabled.',
37: '-= Analysing... =-</div>',
38: '-= Processing =-</div><br><div style="text-align:left;">',
39: '-= Processed in ',
40: "-= Can't be processed =-</div><br>",
41: 'The item is not a ', //41, 32, 33, 34, 42 - "The item is not a file or folder,<br>can't be accessed or its size is too big."
42: ",<br>can't be accessed or its size is too big.",
43: 'An unknown error occured.', //unknown error message
1: 'Certain error 1 occured.' //certain error 1 message
//certain error # message
//...
}
//,'C:\\Windows\\Web\\Screen\\img103.png' //default background image or "undefined" (optional)
//,'C:\\Windows\\Web\\Screen\\img102.jpg' //busy mode background image or "undefined" (optional)
//,'<div style=\'font:10pt Tahoma;padding:20px;\'>List of files supported by default.</div>'
//description length depends on the message language or its actual bytes count or "undefined" (optional)
//,function(m){result.value = m;} //sample for receiving messages or "undefined" (optional)
//END of Extended Drop Target custom settings
)
)
);
result.value = '\nLoading time for 64 bit instance (if possible): ' + createDropTarget.timeToHMSR(new Date() - dt) + '.';
moveTo(screen.availWidth / 2 - 400, screen.availHeight / 2 - 200);
</script>

Categories

Resources