Initial YakPanel commit
This commit is contained in:
324
YakPanel/static/js/polyfill.js
Normal file
324
YakPanel/static/js/polyfill.js
Normal file
@@ -0,0 +1,324 @@
|
||||
/**********************************
|
||||
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 = '<div style="border: 1px solid #999; padding: 3px; width: 235px; box-sizing: content-box; font-size: 14px; height: 21px;">'
|
||||
+ '<div id="fileButtons" style="box-sizing: content-box;">'
|
||||
+ '<button id="button1" style="width: 100px; box-sizing: content-box;">Choose file(s)...</button>'
|
||||
+ '<button id="button2" style="width: 100px; box-sizing: content-box; margin-left: 3px;">Choose folder...</button>'
|
||||
+ '</div>'
|
||||
+ '<div id="filesChosen" style="padding: 3px; display: none; box-sizing: content-box;"><span id="filesChosenText">files selected...</span>'
|
||||
+ '<a id="clear" title="Clear selection" href="javascript:;" style="text-decoration: none; float: right; margin: -3px -1px 0 0; padding: 3px; font-weight: bold; font-size: 16px; color:#999; box-sizing: content-box;">×</a>'
|
||||
+ '</div>'
|
||||
+ '</div>'
|
||||
+ '<input id="input1" type="file" multiple style="display: none;">'
|
||||
+ '<input id="input2" type="file" webkitdirectory style="display: none;">'
|
||||
+ '</div>';
|
||||
|
||||
shadow.querySelector('#button1').onclick = function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
shadow.querySelector('#input1').click();
|
||||
};
|
||||
|
||||
shadow.querySelector('#button2').onclick = function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
shadow.querySelector('#input2').click();
|
||||
};
|
||||
|
||||
var toggleView = function(defaultView, filesLength) {
|
||||
shadow.querySelector('#fileButtons').style.display = defaultView ? 'block' : 'none';
|
||||
shadow.querySelector('#filesChosen').style.display = defaultView ? 'none' : 'block';
|
||||
|
||||
if (!defaultView) {
|
||||
shadow.querySelector('#filesChosenText').innerText = filesLength + ' file' + (filesLength > 1 ? 's' : '') + ' selected...';
|
||||
}
|
||||
};
|
||||
|
||||
var changeHandler = function(e) {
|
||||
node.dispatchEvent(new Event('change'));
|
||||
|
||||
toggleView(false, getFiles().length);
|
||||
};
|
||||
|
||||
shadow.querySelector('#input1').onchange = shadow.querySelector('#input2').onchange = changeHandler;
|
||||
|
||||
var clear = function (e) {
|
||||
toggleView(true);
|
||||
|
||||
var form = document.createElement('form');
|
||||
node.parentNode.insertBefore(form, node);
|
||||
node.parentNode.removeChild(node);
|
||||
form.appendChild(node);
|
||||
form.reset();
|
||||
|
||||
form.parentNode.insertBefore(node, form);
|
||||
form.parentNode.removeChild(form);
|
||||
|
||||
// reset does not instantly occur, need to give it some time
|
||||
setTimeout(function() {
|
||||
node.dispatchEvent(new Event('change'));
|
||||
}, 1);
|
||||
};
|
||||
|
||||
shadow.querySelector('#clear').onclick = clear;
|
||||
}
|
||||
|
||||
node.addEventListener('change', function() {
|
||||
var dir = new Directory();
|
||||
|
||||
var files = getFiles();
|
||||
|
||||
if (files.length > 0) {
|
||||
if (node.hasAttribute(allowdirsAttr)) {
|
||||
toggleView(false, files.length);
|
||||
}
|
||||
|
||||
// from file input drag and drop (webkitEntries)
|
||||
if (files[0].isFile || files[0].isDirectory) {
|
||||
dir._items = files;
|
||||
} else {
|
||||
for (var j = 0; j < files.length; j++) {
|
||||
var file = files[j];
|
||||
var path = file.webkitRelativePath;
|
||||
var fullPath = path.substring(0, path.lastIndexOf(separator));
|
||||
|
||||
recurse(dir, path, fullPath, file);
|
||||
}
|
||||
}
|
||||
} else if (node.hasAttribute(allowdirsAttr)) {
|
||||
toggleView(true, files.length);
|
||||
}
|
||||
|
||||
this[getFilesMethod] = function() {
|
||||
return dir[getFilesMethod]();
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// polyfill file inputs when the DOM loads
|
||||
document.addEventListener('DOMContentLoaded', function(event) {
|
||||
convertInputs(document.getElementsByTagName('input'));
|
||||
});
|
||||
|
||||
// polyfill file inputs that are created dynamically and inserted into the body
|
||||
var observer = new MutationObserver(function(mutations, observer) {
|
||||
for (var i = 0; i < mutations.length; i++) {
|
||||
if (mutations[i].addedNodes.length > 0) {
|
||||
convertInputs(mutations[i].addedNodes);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
observer.observe(document.body, {childList: true, subtree: true});
|
||||
|
||||
/***********************
|
||||
**** Drag and drop ****
|
||||
***********************/
|
||||
// keep a reference to the original method
|
||||
var _addEventListener = EventTarget.prototype.addEventListener;
|
||||
|
||||
DataTransfer.prototype[getFilesMethod] = function() {
|
||||
return Promise.resolve([]);
|
||||
};
|
||||
|
||||
EventTarget.prototype.addEventListener = function(type, listener, useCapture) {
|
||||
if (type === 'drop') {
|
||||
var _listener = listener;
|
||||
|
||||
listener = function(e) {
|
||||
var dir = new Directory();
|
||||
dir._items = e.dataTransfer.items;
|
||||
|
||||
e.dataTransfer[getFilesMethod] = function() {
|
||||
return dir[getFilesMethod]();
|
||||
};
|
||||
|
||||
_listener(e);
|
||||
};
|
||||
}
|
||||
|
||||
// call the original method
|
||||
return _addEventListener.apply(this, arguments);
|
||||
};
|
||||
}());
|
||||
Reference in New Issue
Block a user