Type.registerNamespace("Sys.Extended.UI.AjaxFileUpload");
// Utils
Sys.Extended.UI.AjaxFileUpload.Utils = function() {
this.generateGuid = function() {
var result, i, j;
result = '';
for(j = 0; j < 32; j++) {
if(j == 8 || j == 12 || j == 16 || j == 20)
result = result + '-';
i = Math.floor(Math.random() * 16).toString(16).toUpperCase();
result = result + i;
}
return result;
};
this.getFileName = function(fullPath) {
var result = "";
if(!fullPath)
return '';
if(!fullPath.value && fullPath.name)
result = fullPath.name;
else {
if(!fullPath.value && typeof (fullPath) !== "string")
throw "Invalid parameter. fullPath parameter must be a string of full path or file element.";
if(fullPath.value)
fullPath = fullPath.value;
if(fullPath) {
var startIndex = (fullPath.indexOf('\\') >= 0 ? fullPath.lastIndexOf('\\') : fullPath.lastIndexOf('/'));
var filename = fullPath.substring(startIndex);
if(filename.indexOf('\\') === 0 || filename.indexOf('/') === 0) {
filename = filename.substring(1);
}
result = filename;
}
}
return encodeURIComponent(result);
};
this.getFileType = function(file) {
if(!file)
throw 'file must defined or not null';
if(!file.value && file.name)
return file.name.substring(file.name.lastIndexOf('.') + 1);
if(file.value)
file = file.value;
if(typeof (file) !== "string")
throw "can't resolve file type.";
return file.substring(file.lastIndexOf('.') + 1);
};
this.sizeToString = function(bytes) {
if(!bytes || bytes <= 0)
return '0 Kb';
var s = ['bytes', 'kb', 'MB', 'GB', 'TB', 'PB'];
var e = Math.floor(Math.log(bytes) / Math.log(1024));
return (bytes / Math.pow(1024, Math.floor(e))).toFixed(2) + " " + s[e];
};
this.checkHtml5BrowserSupport = function() {
// IE10 Desktop (NOT Metro version) has FormData but it's buggy,
// so we want exclude it from Html5 supported browser
var browser = Sys.Browser;
if(browser.name == "Microsoft Internet Explorer" && browser.version <= 10)
return false;
return window.File
&& window.FileReader && window.FileList
&& window.Blob && (new XMLHttpRequest()).upload;
};
};
// Item
Sys.Extended.UI.AjaxFileUpload.Item = function(parentId, fileItem, onRemoveItem) {
this._deleteButton = null;
this._parentId = parentId;
this._inputElementValue = fileItem.value;
this._id = fileItem.id;
this._slices = fileItem.slices;
this._sliceIndex = 0;
this._fileInfoContainer = null;
this._fileStatusText = null;
this._isUploaded = false;
this._isUploading = false;
this._fileSize = 0;
this._fileName = "";
this._fileType = "";
this._bytesUploaded = 0;
this._ui = this.initUI(onRemoveItem);
};
Sys.Extended.UI.AjaxFileUpload.Item.prototype = {
initUI: function(onRemoveItem) {
var self = this, file = this._inputElementValue, utils = new Sys.Extended.UI.AjaxFileUpload.Utils(),
isHtml5Support = utils.checkHtml5BrowserSupport(),
// generate unique id for each item
id = this._id,
// create line item container
container = $common.createElementFromTemplate({
nodeName: "div",
properties: {
id: this._parentId + '_FileItemContainer_' + id
},
cssClasses: ['ajax__fileupload_fileItemInfo']
}),
// create file info/status container
fileInfoContainer = $common.createElementFromTemplate({
nodeName: "div",
properties: {
id: this._parentId + '_FileInfoContainer_' + id,
style: {
display: 'inline-block'
}
}
}),
// create file info placeholder
fileInfoText = $common.createElementFromTemplate({
nodeName: "span",
properties: {
id: this._parentId + '_FileItemInfo_' + id
},
cssClasses: ['ajax__fileupload_fileItemInfo']
}),
// create file status placeholder
fileStatusText = $common.createElementFromTemplate({
nodeName: "span",
properties: {
id: this._parentId + '_FileItemStatus_' + id
},
cssClasses: ['uploadstatus']
}),
// init remove button
deleteButton = $common.createElementFromTemplate({
nodeName: "div",
properties: {
id: this._parentId + '_FileItemDeleteButton_' + id
},
cssClasses: ['removeButton']
});
this._fileName = utils.getFileName(file);
var fileNameToDisplay = decodeURIComponent(this._fileName);
if(isHtml5Support) {
this._fileSize = file.size;
var fType = file.type ? '(' + file.type + ')' : '';
fileInfoText.innerHTML = '' + fileNameToDisplay + ' '
+ fType
+ ' - ' + utils.sizeToString(file.size) + ' ';
this._fileType = file.type;
} else {
fileInfoText.innerHTML = '' + fileNameToDisplay + '';
this._fileType = utils.getFileType(file);
}
fileInfoContainer.appendChild(fileInfoText);
fileInfoContainer.appendChild(fileStatusText);
$common.setText(deleteButton, Sys.Extended.UI.Resources.AjaxFileUpload_Remove);
$addHandlers(deleteButton, {
'click': Function.createDelegate(this, function() {
onRemoveItem(self);
})
});
// build the line item
if(Sys.Browser.agent == Sys.Browser.InternetExplorer && Sys.Browser.version <= 8) {
container.appendChild(deleteButton);
container.appendChild(fileInfoContainer);
}
else {
container.appendChild(fileInfoContainer);
container.appendChild(deleteButton);
}
this._fileInfoContainer = fileInfoContainer;
this._deleteButton = deleteButton;
this._fileStatusText = fileStatusText;
return container;
},
setStatus: function(fileStatusText, text) {
$common.setText(this._fileStatusText, ' (' + text + ')');
this._fileInfoContainer.setAttribute('class', fileStatusText + 'State');
},
disabled: function(on) {
if(on)
this._deleteButton.disabled = 'disabled';
else
this._deleteButton.disabled = '';
},
hide: function() {
this._deleteButton.style.visibility = 'hidden';
},
destroy: function() {
$common.removeElement(this._inputElementValue);
$common.removeElement(this._deleteButton);
$common.removeElement(this._ui);
},
get_inputElementValue: function() {
return this._inputElementValue;
},
appendNodeTo: function(parent) {
parent.appendChild(this._ui);
},
removeNodeFrom: function(parent) {
parent.removeChild(this._ui);
}
};
// Processor
Sys.Extended.UI.AjaxFileUpload.Processor = function(control, elements) {
var utils = new Sys.Extended.UI.AjaxFileUpload.Utils(),
xhrPoll = new XMLHttpRequest();
this._iframe = null;
this._iframeName = control.get_id() + '_uploadIframe';
this._form = null;
this.initialize = function() {
this.attachEvents();
this.createIFrame();
this.createForm();
};
this.attachEvents = function() {
this.onFileSelected$delegate = Function.createDelegate(this, this.onFileSelectedHandler);
this.attachFileInputEvents(elements.inputFile, true);
var self = this;
// Capture uploading percentage report from server
xhrPoll.onreadystatechange = function(e) {
if(xhrPoll.readyState == 4) {
if(xhrPoll.status == 200) {
// Update percentage
var percent = xhrPoll.responseText;
if(percent) {
percent = parseFloat(percent).toFixed(2);
control.setPercent(percent);
}
// Keep polling as long as upload is not completed
if(percent < 100)
setTimeout(function() {
self.pollingServerProgress(true);
}, 500);
} else {
// We won't do any error handler for polling, since upload error handled by upload request it self.
}
}
};
};
this.attachFileInputEvents = function(fileInput, attach) {
if(attach)
$addHandlers(fileInput, { 'change': this.onFileSelected$delegate });
else
$common.removeHandlers(fileInput, { 'change': this.onFileSelected$delegate });
},
this.onFileSelectedHandler = function(e) {
/// User selects file through browser open file dialog.
/// We generate file item and add it into file list, and recreate new element for next file.
// generate file item to be uploaded
var fileItem = {
id: utils.generateGuid(),
value: elements.inputFile,
type: utils.getFileType(elements.inputFile.value)
};
if(control.fileTypeIsValid(fileItem.type)) {
control.addFileToQueue(fileItem);
this.createInputFileElement();
} else {
control.confirmFileIsInvalid(fileItem);
}
};
this.createInputFileElement = function() {
var currentInputFile = elements.inputFile;
// disfunction current input file element
currentInputFile.style.zIndex = -999;
$common.setLocation(currentInputFile, { x: -99999, y: -99999 });
this.attachFileInputEvents(currentInputFile, false);
// create new input file element in same location
var id = control.get_id() + '_file_' + utils.generateGuid(),
newInputFile = $common.createElementFromTemplate({
nodeName: 'input',
properties: {
id: id,
name: "act-file-data",
type: 'file',
style: {
zIndex: 0,
cursor: 'pointer',
position: 'absolute'
}
}
}, currentInputFile.parentNode);
$common.setElementOpacity(newInputFile, 0);
this.attachFileInputEvents(newInputFile, true);
// set current input file with the new one
elements.inputFile = newInputFile;
};
this.startUpload = function() {
// Get un-uploaded file on the top from queue and start upload it.
var form = this._form,
fileItem = control.getNextFile();
if(!fileItem) {
control._currentFileId = null;
this.setThrobber(false);
control.done();
return;
}
// set file item status
control.setAsUploading(fileItem);
var inputElement = fileItem.get_inputElementValue();
// set current file id
control._currentFileId = fileItem._id;
this.setThrobber(true);
// clear all form children
while(form.firstChild) {
form.removeChild(form.firstChild);
}
inputElement.name = 'act-file-data';
// only add 1 file input element to be uploaded
form.appendChild(inputElement);
form.setAttribute("action", control._uploadUrl + '?contextKey=' + control._contextKey + '&fileId=' + control._currentFileId + '&fileName=' + fileItem._fileName + '&usePoll=' + (control.get_serverPollingSupport() ? "true" : "false"));
// upload it now
form.submit();
};
this.cancelUpload = function() {
// send message to server to cancel this upload
var xhr = new XMLHttpRequest(),
self = this;
// aborting server polling request
if(xhrPoll)
xhrPoll.abort();
xhr.open("POST", '?contextKey=' + control._contextKey + "&cancel=1&guid=" + control._currentFileId, true);
xhr.onreadystatechange = function() {
self.setThrobber(false);
if(xhr.readyState == 4) {
if(xhr.status == 200) {
control.cancelUpload();
} else {
// cancelation is error.
self.raiseUploadError(xhr);
throw "Failed to cancel upload.";
}
}
};
xhr.send(null);
},
this.createIFrame = function() {
var name = this._iframeName,
iframe = document.createElement("IFRAME");
iframe.width = "0";
iframe.height = "0";
iframe.style.display = "none";
iframe.src = "about:blank";
iframe.id = name;
iframe.name = name;
iframe.security = "restricted";
document.body.appendChild(iframe);
iframe.contentWindow.name = name;
$addHandlers(iframe, {
load: Function.createDelegate(this, this.onIFrameLoadedHandler)
});
this._iframe = iframe;
};
this.onIFrameLoadedHandler = function(e) {
// Event handler to capture when iframe receive response from server.
if(!control._currentFileId)
return;
try {
var iframe = this._iframe, doc = null;
// Only test the iframe data, exception should thrown if something went wrong.
if(iframe.contentDocument)
// Firefox, Opera
doc = iframe.contentDocument;
else if(iframe.contentWindow)
// Internet Explorer
doc = iframe.contentWindow.document;
else if(iframe.document)
// Others?
doc = iframe.document;
if(doc == null)
throw "Document not initialized";
// finalizing and upload next file
control.doneAndUploadNextFile(control.getCurrentFileItem());
} catch(e) {
// Cancelation / aborting upload can causing 'Access is denied' or 'Permission denied' on IE 9 bellow,
// let's consider this exception is not trully error exception from server.
if(!control._canceled || !(e.message && (e.message.indexOf("Access is denied") > -1 || e.message.indexOf("Permission denied") > -1))) {
this.raiseUploadError(e);
throw e;
}
}
};
this.setThrobber = function(value) {
// Show or hide throbber when processing upload.
if(control.get_serverPollingSupport()) {
control.setPercent(0);
$common.setVisible(elements.progressBar, value ? true : false);
$common.setVisible(elements.progressBarContainer, value ? true : false);
this.pollingServerProgress(value);
return;
}
if(control.get_throbber() != null) {
control.get_throbber().style.display = value ? "" : "none";
}
};
this.pollingServerProgress = function(value) {
// Get percentage of uploading progress from server.
if(!value || !control._currentFileId)
return;
xhrPoll.open("GET", '?contextKey=' + control._contextKey + "&poll=1&guid=" + control._currentFileId, true);
xhrPoll.send(null);
};
this.createForm = function() {
// Create form that we use to upload file one by one taken from queue.
var form,
formId = "___postForm" + control.get_id();
try {
form = document.createElement('