/** * plupload.html4.js * * Copyright 2010, Ryan Demmer * Copyright 2009, Moxiecode Systems AB * Released under GPL License. * * License: http://www.plupload.com/license * Contributing: http://www.plupload.com/contributing */ // JSLint defined globals /*global plupload:false, window:false */ (function(window, document, plupload, undef) { function getById(id) { return document.getElementById(id); } /** * HTML4 implementation. This runtime has no special features it uses an form that posts files into an hidden iframe. * * @static * @class plupload.runtimes.Html4 * @extends plupload.Runtime */ plupload.runtimes.Html4 = plupload.addRuntime("html4", { /** * Returns a list of supported features for the runtime. * * @return {Object} Name/value object with supported features. */ getFeatures : function() { // Only multipart feature return { multipart: true, /* WebKit let you trigger file dialog programmatically while FF and Opera - do not, so we sniff for it here... probably not that good idea, but impossibillity of controlling cursor style on top of add files button obviously feels even worse */ canOpenDialog: navigator.userAgent.indexOf('WebKit') !== -1 }; }, /** * Initializes the upload runtime. * * @method init * @param {plupload.Uploader} uploader Uploader instance that needs to be initialized. * @param {function} callback Callback to execute when the runtime initializes or fails to initialize. If it succeeds an object with a parameter name success will be set to true. */ init : function(uploader, callback) { uploader.bind("Init", function(up) { var container = document.body, iframe, url = "javascript", currentFile, input, currentFileId, fileIds = [], IE = /MSIE/.test(navigator.userAgent), mimes = [], filters = up.settings.filters, i, ext, type, y; // Convert extensions to mime types list for (i = 0; i < filters.length; i++) { ext = filters[i].extensions.split(/,/); for (y = 0; y < ext.length; y++) { type = plupload.mimeTypes[ext[y]]; if (type) { mimes.push(type); } } } mimes = mimes.join(','); function createForm() { var form, input, bgcolor, browseButton; // Setup unique id for form currentFileId = plupload.guid(); // Save id for Destroy handler fileIds.push(currentFileId); // Create form form = document.createElement('form'); form.setAttribute('id', 'form_' + currentFileId); form.setAttribute('method', 'post'); form.setAttribute('enctype', 'multipart/form-data'); form.setAttribute('encoding', 'multipart/form-data'); form.setAttribute("target", up.id + '_iframe'); form.style.position = 'absolute'; // Create input and set attributes input = document.createElement('input'); input.setAttribute('id', 'input_' + currentFileId); input.setAttribute('type', 'file'); input.setAttribute('accept', mimes); input.setAttribute('size', 1); browseButton = getById(up.settings.browse_button); // Route click event to input element programmatically, if possible if (up.features.canOpenDialog && browseButton) { plupload.addEvent(getById(up.settings.browse_button), 'click', function(e) { input.click(); e.preventDefault(); }, up.id); } // Set input styles plupload.extend(input.style, { width : '100%', height : '100%', opacity : 0, fontSize: '2em' // force input element to be bigger then needed to occupy whole space }); plupload.extend(form.style, { overflow: 'hidden' }); // Show the container if shim_bgcolor is specified bgcolor = up.settings.shim_bgcolor; if (bgcolor) { form.style.background = bgcolor; } // no opacity in IE if (IE) { plupload.extend(input.style, { filter : "alpha(opacity=0)" }); } // add change event plupload.addEvent(input, 'change', function(e) { var element = e.target, name, files = [], topElement; if (element.value) { getById('form_' + currentFileId).style.top = -0xFFFFF + "px"; // Get file name name = element.value.replace(/\\/g, '/'); name = name.substring(name.length, name.lastIndexOf('/') + 1); // Push files files.push(new plupload.File(currentFileId, name)); // Clean-up events - they won't be needed anymore if (!up.features.canOpenDialog) { plupload.removeAllEvents(form, up.id); } else { plupload.removeEvent(browseButton, 'click', up.id); } plupload.removeEvent(input, 'change', up.id); // Create and position next form createForm(); // Fire FilesAdded event if (files.length) { uploader.trigger("FilesAdded", files); } } }, up.id); // append to container form.appendChild(input); container.appendChild(form); up.refresh(); } function createIframe() { var temp = document.createElement('div'); // Create iframe using a temp div since IE 6 won't be able to set the name using setAttribute or iframe.name temp.innerHTML = ''; iframe = temp.firstChild; container.appendChild(iframe); // Add IFrame onload event plupload.addEvent(iframe, 'load', function(e) { var n = e.target, el, result; // Ignore load event if there is no file if (!currentFile) { return; } try { el = n.contentWindow.document || n.contentDocument || window.frames[n.id].document; } catch (ex) { // Probably a permission denied error up.trigger('Error', { code : plupload.SECURITY_ERROR, message : plupload.translate('Security error.'), file : currentFile }); return; } // Get result result = el.documentElement.innerText || el.documentElement.textContent; // Assume no error if (result) { currentFile.status = plupload.DONE; currentFile.loaded = 1025; currentFile.percent = 100; up.trigger('UploadProgress', currentFile); up.trigger('FileUploaded', currentFile, { response : result }); } }, up.id); } // end createIframe if (up.settings.container) { container = getById(up.settings.container); container.style.position = 'relative'; } // Upload file up.bind("UploadFile", function(up, file) { var form, input; // File upload finished if (file.status == plupload.DONE || file.status == plupload.FAILED || up.state == plupload.STOPPED) { return; } // Get the form and input elements form = getById('form_' + file.id); input = getById('input_' + file.id); // Set input element name attribute which allows it to be submitted input.setAttribute('name', up.settings.file_data_name); // Store action form.setAttribute("action", up.settings.url); // Append multipart parameters plupload.each(plupload.extend({name : file.target_name || file.name}, up.settings.multipart_params), function(value, name) { var hidden = document.createElement('input'); plupload.extend(hidden, { type : 'hidden', name : name, value : value }); form.insertBefore(hidden, form.firstChild); }); currentFile = file; // Hide the current form getById('form_' + currentFileId).style.top = -0xFFFFF + "px"; form.submit(); form.parentNode.removeChild(form); }); up.bind('FileUploaded', function(up) { up.refresh(); // just to get the form back on top of browse_button }); up.bind('StateChanged', function(up) { if (up.state == plupload.STARTED) { createIframe(); } if (up.state == plupload.STOPPED) { window.setTimeout(function() { plupload.removeEvent(iframe, 'load', up.id); iframe.parentNode.removeChild(iframe); }, 0); } }); // Refresh button, will reposition the input form up.bind("Refresh", function(up) { var browseButton, topElement, hoverClass, activeClass, browsePos, browseSize, inputContainer, inputFile, pzIndex; browseButton = getById(up.settings.browse_button); if (browseButton) { browsePos = plupload.getPos(browseButton, getById(up.settings.container)); browseSize = plupload.getSize(browseButton); inputContainer = getById('form_' + currentFileId); inputFile = getById('input_' + currentFileId); plupload.extend(inputContainer.style, { top : browsePos.y + 'px', left : browsePos.x + 'px', width : browseSize.w + 'px', height : browseSize.h + 'px' }); // for IE and WebKit place input element underneath the browse button and route onclick event // TODO: revise when browser support for this feature will change if (up.features.canOpenDialog) { pzIndex = parseInt(browseButton.parentNode.style.zIndex, 10); if (isNaN(pzIndex)) { pzIndex = 0; } plupload.extend(browseButton.style, { position : 'relative', zIndex : pzIndex }); plupload.extend(inputContainer.style, { zIndex : pzIndex - 1 }); } /* Since we have to place input[type=file] on top of the browse_button for some browsers (FF, Opera), browse_button loses interactivity, here we try to neutralize this issue highlighting browse_button with a special class TODO: needs to be revised as things will change */ hoverClass = up.settings.browse_button_hover; activeClass = up.settings.browse_button_active; topElement = up.features.canOpenDialog ? browseButton : inputContainer; if (hoverClass) { plupload.addEvent(topElement, 'mouseover', function() { plupload.addClass(browseButton, hoverClass); }, up.id); plupload.addEvent(topElement, 'mouseout', function() { plupload.removeClass(browseButton, hoverClass); }, up.id); } if (activeClass) { plupload.addEvent(topElement, 'mousedown', function() { plupload.addClass(browseButton, activeClass); }, up.id); plupload.addEvent(document.body, 'mouseup', function() { plupload.removeClass(browseButton, activeClass); }, up.id); } } }); // Remove files uploader.bind("FilesRemoved", function(up, files) { var i, n; for (i = 0; i < files.length; i++) { n = getById('form_' + files[i].id); if (n) { n.parentNode.removeChild(n); } } }); // Completely destroy the runtime uploader.bind("Destroy", function(up) { var name, element, form, elements = { inputContainer: 'form_' + currentFileId, inputFile: 'input_' + currentFileId, browseButton: up.settings.browse_button }; // Unbind event handlers for (name in elements) { element = getById(elements[name]); if (element) { plupload.removeAllEvents(element, up.id); } } plupload.removeAllEvents(document.body, up.id); // Remove mark-up plupload.each(fileIds, function(id, i) { form = getById('form_' + id); if (form) { container.removeChild(form); } }); }); // Create initial form createForm(); }); callback({success : true}); } }); })(window, document, plupload);