/********************************** Directory Upload Proposal Polyfill Author: Ali Alabbas (Microsoft) **********************************/ (function() { // Do not proceed with the polyfill if Directory interface is already natively available, // or if webkitdirectory is not supported (i.e. not Chrome, since the polyfill only works in Chrome) if (window.Directory || !('webkitdirectory' in document.createElement('input') && 'webkitGetAsEntry' in DataTransferItem.prototype)) { return; } var allowdirsAttr = 'allowdirs', getFilesMethod = 'getFilesAndDirectories', isSupportedProp = 'isFilesAndDirectoriesSupported', chooseDirMethod = 'chooseDirectory'; var separator = '/'; var Directory = function() { this.name = ''; this.path = separator; this._children = {}; this._items = false; }; Directory.prototype[getFilesMethod] = function() { var that = this; // from drag and drop and file input drag and drop (webkitEntries) if (this._items) { var getItem = function(entry) { if (entry.isDirectory) { var dir = new Directory(); dir.name = entry.name; dir.path = entry.fullPath; dir._items = entry; return dir; } else { return new Promise(function(resolve, reject) { entry.file(function(file) { resolve(file); }, reject); }); } }; if (this.path === separator) { var promises = []; for (var i = 0; i < this._items.length; i++) { var entry; // from file input drag and drop (webkitEntries) if (this._items[i].isDirectory || this._items[i].isFile) { entry = this._items[i]; } else { entry = this._items[i].webkitGetAsEntry(); } promises.push(getItem(entry)); } return Promise.all(promises); } else { return new Promise(function(resolve, reject) { var dirReader = that._items.createReader(); var promises = []; var readEntries = function() { dirReader.readEntries(function(entries) { if (!entries.length) { resolve(Promise.all(promises)); } else { for (var i = 0; i < entries.length; i++) { promises.push(getItem(entries[i])); } readEntries(); } }, reject); }; readEntries(); }); } // from file input manual selection } else { var arr = []; for (var child in this._children) { arr.push(this._children[child]); } return Promise.resolve(arr); } }; // set blank as default for all inputs HTMLInputElement.prototype[getFilesMethod] = function() { return Promise.resolve([]); }; // if OS is Mac, the combined directory and file picker is supported HTMLInputElement.prototype[isSupportedProp] = navigator.appVersion.indexOf("Mac") !== -1; HTMLInputElement.prototype[allowdirsAttr] = undefined; HTMLInputElement.prototype[chooseDirMethod] = undefined; // expose Directory interface to window window.Directory = Directory; /******************** **** File Input **** ********************/ var convertInputs = function(nodes) { var recurse = function(dir, path, fullPath, file) { var pathPieces = path.split(separator); var dirName = pathPieces.shift(); if (pathPieces.length > 0) { var subDir = new Directory(); subDir.name = dirName; subDir.path = separator + fullPath; if (!dir._children[subDir.name]) { dir._children[subDir.name] = subDir; } recurse(dir._children[subDir.name], pathPieces.join(separator), fullPath, file); } else { dir._children[file.name] = file; } }; for (var i = 0; i < nodes.length; i++) { var node = nodes[i]; if (node.tagName === 'INPUT' && node.type === 'file') { var getFiles = function() { var files = node.files; if (draggedAndDropped) { files = node.webkitEntries; draggedAndDropped = false; } else { if (files.length === 0) { files = node.shadowRoot.querySelector('#input1').files; if (files.length === 0) { files = node.shadowRoot.querySelector('#input2').files; if (files.length === 0) { files = node.webkitEntries; } } } } return files; }; var draggedAndDropped = false; node.addEventListener('drop', function(e) { draggedAndDropped = true; }, false); if (node.hasAttribute(allowdirsAttr)) { // force multiple selection for default behavior if (!node.hasAttribute('multiple')) { node.setAttribute('multiple', ''); } var shadow = node.createShadowRoot(); node[chooseDirMethod] = function() { // can't do this without an actual click console.log('This is unsupported. For security reasons the dialog cannot be triggered unless it is a response to some user triggered event such as a click on some other element.'); }; shadow.innerHTML = '