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() { 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.get_id() + '&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.get_id() + "&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.get_id() + "&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('