From a19fea24163663fc8eff5c0d5605c77be4250f60 Mon Sep 17 00:00:00 2001 From: Will Rossiter Date: Mon, 25 Jan 2010 21:10:43 +0000 Subject: [PATCH] APICHANGE: removed SWFUpload. Refactored Content Editors uploader to use standard uploader. git-svn-id: svn://svn.silverstripe.com/silverstripe/open/modules/cms/branches/2.4@97489 467b73ca-7a2a-4603-9d3b-597d59a354a9 --- code/AssetAdmin.php | 6 - code/LeftAndMain.php | 3 - css/TinyMCEImageEnhancement.css | 20 - css/cms_right.css | 26 + javascript/CMSMain_upload.js | 166 ---- javascript/TinyMCEImageEnhancement.js | 221 ----- javascript/Upload.js | 284 ------ thirdparty/swfupload/license.txt | 11 - thirdparty/swfupload/swfupload.js | 1052 ----------------------- thirdparty/swfupload/swfupload.queue.js | 98 --- thirdparty/swfupload/swfupload.swf | Bin 33328 -> 0 bytes 11 files changed, 26 insertions(+), 1861 deletions(-) delete mode 100644 css/TinyMCEImageEnhancement.css delete mode 100644 javascript/CMSMain_upload.js delete mode 100644 javascript/TinyMCEImageEnhancement.js delete mode 100644 javascript/Upload.js delete mode 100644 thirdparty/swfupload/license.txt delete mode 100755 thirdparty/swfupload/swfupload.js delete mode 100755 thirdparty/swfupload/swfupload.queue.js delete mode 100755 thirdparty/swfupload/swfupload.swf diff --git a/code/AssetAdmin.php b/code/AssetAdmin.php index faef5cb1..a53739ae 100755 --- a/code/AssetAdmin.php +++ b/code/AssetAdmin.php @@ -70,10 +70,6 @@ class AssetAdmin extends LeftAndMain { Requirements::css(SAPPHIRE_DIR . "/css/ComplexTableField.css"); Requirements::javascript(CMS_DIR . "/javascript/AssetAdmin.js"); - - Requirements::javascript(CMS_DIR . "/javascript/CMSMain_upload.js"); - Requirements::javascript(CMS_DIR . "/javascript/Upload.js"); - Requirements::javascript(CMS_DIR . "/thirdparty/swfupload/swfupload.js"); Requirements::javascript(THIRDPARTY_DIR . "/greybox/AmiJS.js"); Requirements::javascript(THIRDPARTY_DIR . "/greybox/greybox.js"); @@ -121,8 +117,6 @@ JS * Return the form object shown in the uploadiframe. */ function UploadForm() { - // disabled flash upload javascript (CMSMain_upload()) below, - // see r54952 (originally committed in r42014) $form = new Form($this,'UploadForm', new FieldSet( new HiddenField("ID", "", $this->currentPageID()), new HiddenField("FolderID", "", $this->currentPageID()), diff --git a/code/LeftAndMain.php b/code/LeftAndMain.php index 4f332b3a..b2b6ca1c 100644 --- a/code/LeftAndMain.php +++ b/code/LeftAndMain.php @@ -268,9 +268,6 @@ class LeftAndMain extends Controller { CMS_DIR . '/javascript/LeftAndMain_right.js', SAPPHIRE_DIR . '/javascript/tree/tree.js', THIRDPARTY_DIR . '/tabstrip/tabstrip.js', - CMS_DIR . '/thirdparty/swfupload/swfupload.js', - CMS_DIR . '/javascript/Upload.js', - CMS_DIR . '/javascript/TinyMCEImageEnhancement.js', SAPPHIRE_DIR . '/javascript/TreeSelectorField.js', CMS_DIR . '/javascript/ThumbnailStripField.js', ) diff --git a/css/TinyMCEImageEnhancement.css b/css/TinyMCEImageEnhancement.css deleted file mode 100644 index aafadef7..00000000 --- a/css/TinyMCEImageEnhancement.css +++ /dev/null @@ -1,20 +0,0 @@ -.contentPanel .group { - display: inline; - margin-left: 2px; -} -.contentPanel .middleColumn { - margin-bottom: 2px; -} -.contentPanel .link { - text-decoration: none; - margin-left: 2px; -} - - -.contentPanel .tree_holder { - display: inline; -} -.contentPanel #NewFolderName { - width: 120px; - margin-top: 0; -} diff --git a/css/cms_right.css b/css/cms_right.css index df4e431e..0ff55828 100644 --- a/css/cms_right.css +++ b/css/cms_right.css @@ -490,3 +490,29 @@ div.filterBox { .autocompleteoptions { background-color: white; } + +/** + * Upload Image (Image Toolbar) + */ +#Form_EditorToolbarImageForm .file, +#Form_EditorToolbarImageForm #UploadFormResponse { + display: none; +} + +.showUploadField { + font-size: 11px; + padding: 1px 3px; +} + .showUploadField a { + display: none; + } + + #UploadFormResponse { + padding: 6px 0; + font-size: 12px; + } + #UploadFormResponse.loading + .loading#UploadFormResponse { + background: url(../../cms/images/network-save.gif) no-repeat left center; + padding-left: 20px; + } diff --git a/javascript/CMSMain_upload.js b/javascript/CMSMain_upload.js deleted file mode 100644 index 1960ae17..00000000 --- a/javascript/CMSMain_upload.js +++ /dev/null @@ -1,166 +0,0 @@ -/** - * This class is used for upload in asset part. - * If one of methods is not commented look for comment in Upload.js - -*/ -CMSMain_upload = Class.create(); -CMSMain_upload.prototype = { - initialize: function() { - - // If those 2 checks pass, we can provide upload capabilities to the user - this.iframe = window.top.document.getElementById('AssetAdmin_upload'); - this.onLoad(); - }, - - onLoad: function() { - this.upload = new Upload({ - fileUploadLimit : '6', - securityID : $('SecurityID').value, - beginUploadOnQueue : false, - fileQueued : this.uploadFileQueuedCallback.bind(this), - fileProgress : this.uploadProgressCallback.bind(this), - fileCancelled : this.uploadFileCancelCallback.bind(this), - fileComplete : this.uploadFileCompleteCallback.bind(this), - queueComplete : this.uploadQueueCompleteCallback.bind(this), - buildUI : this.extendForm.bind(this) - }); - }, - - /** - * Builds UI, called only when Upload object will be able to create flash uploader. - */ - - extendForm: function() { - if(this.iframe.contentDocument == undefined) this.iframe.contentDocument = document.frames[0].document;//IE HACK - element = this.iframe.contentDocument.getElementById('Form_UploadForm'); - inputFile = this.iframe.contentDocument.getElementById('Form_UploadForm_Files-0'); - inputFileParent = inputFile.parentNode; - inputFileParent.removeChild(inputFile); - inputFile = this.iframe.contentDocument.createElement('input'); - inputFile.type = 'text'; - inputFile.id = 'Form_UploadForm_Files-0'; - inputFileParent.appendChild(inputFile); - inputButton = this.iframe.contentDocument.getElementById('Form_UploadForm_Files-1'); - if(inputButton != null) inputButton.parentNode.removeChild(inputButton); - inputButton = this.iframe.contentDocument.createElement('input'); - inputButton.type = 'button'; - inputButton.id = 'Form_UploadForm_Files-1'; - inputButton.value = ' Browse...'; - inputButton.style.width = '66px'; - inputButton.style.height = '19px'; - inputButton.style.position = 'relative'; - inputButton.style.top = '1px'; - inputButton.style.fontFamily = 'Arial'; - inputButton.style.fontSize = '1.06em'; - inputFileParent.appendChild(inputButton); - Event.observe(inputButton,'click',this.onBrowseClick.bind(this)); - Event.observe(this.iframe.contentDocument.getElementById('Form_UploadForm_action_upload'),'click',this.onUploadClick.bind(this)); - }, - - onBrowseClick: function(event) { - this.upload.browse(); - }, - - onUploadClick: function(event) { - Event.stop(event); - this.upload.startUpload(); - }, - - uploadFileQueuedCallback: function(file,queueLength) { - this.upload.setFolderID(this.iframe.contentDocument.getElementById('Form_UploadForm_ID').value); - this.iframe.contentDocument.getElementById('Form_UploadForm_action_upload').disabled = false; - var fileContainer = this.iframe.contentDocument.getElementById('Form_UploadForm_FilesList'); - if(fileContainer == null) { - fileContainer = this.iframe.contentDocument.createElement('div'); - fileContainer.id = 'Form_UploadForm_FilesList'; - this.iframe.contentDocument.getElementById('Form_UploadForm').appendChild(fileContainer); - } - - var fileToUpload = this.iframe.contentDocument.createElement('div'); - fileToUpload.id = 'Form_UploadForm_FilesList_' + file.id; - fileToUpload.style.marginBottom = '3px'; - fileContainer.appendChild(fileToUpload); - - var fileName = this.iframe.contentDocument.createElement('div'); - fileName.id = 'Form_UploadForm_FilesList_Name_' + file.id; - fileName.style.position = 'relative'; - fileName.style.top = '-4px'; - fileName.style.display = 'inline'; - fileName.style.padding = '2px'; - fileName.innerHTML = file.name; - fileName.style.height = Element.getDimensions(fileName).height + 1 + 'px';//IE hack - fileToUpload.appendChild(fileName); - - var fileProgress = this.iframe.contentDocument.createElement('div'); - fileProgress.id = 'Form_UploadForm_FilesList_Progress_' + file.id; - Position.clone(fileName,fileProgress); - fileProgress.style.backgroundColor = 'black'; - fileProgress.style.display = 'inline'; - fileProgress.style.position = 'absolute'; - fileProgress.style.left = '5px'; - fileProgress.style.width = '0px'; - fileProgress.finished = false; - fileProgress.style.top = parseInt(fileProgress.style.top) + 6 + 'px'; - fileProgress.style.height = Element.getDimensions(fileName).height + 1 + 'px'; - fileToUpload.appendChild(fileProgress); - - var fileDelete = this.iframe.contentDocument.createElement('input'); - fileDelete.id = file.id; - fileDelete.type = 'button'; - fileDelete.value = 'Delete'; - Element.addClassName(fileDelete,'delete'); - fileToUpload.appendChild(fileDelete); - Event.observe(fileDelete,'click',this.uploadFileCancelCallback.bind(this)); - }, - - uploadProgressCallback: function(file,bytesLoaded) { - fileName = this.iframe.contentDocument.getElementById('Form_UploadForm_FilesList_Name_' + file.id); - fileName.style.border = 'solid 1px black'; - fileProgress = this.iframe.contentDocument.getElementById('Form_UploadForm_FilesList_Progress_' + file.id); - fileProgress.style.opacity = 0.3;fileProgress.style.filter = 'alpha(opacity=30)'; - if(!fileProgress.cloned) { - Position.clone(fileName,fileProgress); - fileProgress.style.width = '0px'; - fileProgress.cloned = true; - } - fileProgress.style.width = (bytesLoaded / file.size) * Element.getDimensions(fileName).width - 1 + 'px'; - }, - - uploadFileCompleteCallback: function(file,serverData) { - this.iframe.contentDocument.getElementById('Form_UploadForm_FilesList_Progress_' + file.id).finished = true; - }, - - uploadFileCancelCallback: function(event) { - element = Event.element(event); - fileId = element.id; - fileContainer = this.iframe.contentDocument.getElementById('Form_UploadForm_FilesList'); - elementToDelete = this.iframe.contentDocument.getElementById('Form_UploadForm_FilesList_' + fileId); - elementToDelete.parentNode.removeChild(elementToDelete); - filesToUpload = fileContainer.childNodes.length; - if(filesToUpload > 0) { - this.iframe.contentDocument.getElementById('Form_UploadForm_action_upload').disabled = false; - } else { - this.iframe.contentDocument.getElementById('Form_UploadForm_action_upload').disabled = true; - } - $A(fileContainer.childNodes).each( - function(item) { - $A(item.childNodes).each( - function(item) { - if(item.id.indexOf('Name') != -1) { - fileName = item; - } - if(item.id.indexOf('Progress') != -1) { - fileProgress = item; - } - }); - Position.clone(fileName,fileProgress); - if(fileProgress.finished == false) fileProgress.style.width = '0px'; - } - ); - }, - - uploadQueueCompleteCallback: function() { - eval(this.upload.getUploadMessage().replace(/Uploaded 1 files/g,'Uploaded ' + this.upload.getFilesUploaded() + ' files')); - } -} -window.top.document.CMSMain_upload = CMSMain_upload; diff --git a/javascript/TinyMCEImageEnhancement.js b/javascript/TinyMCEImageEnhancement.js deleted file mode 100644 index d5a43cad..00000000 --- a/javascript/TinyMCEImageEnhancement.js +++ /dev/null @@ -1,221 +0,0 @@ -/** - * This class is used for upload in TinyMCE editor. - * If one of methods is not commented look for comment in Upload.js. -*/ -TinyMCEImageEnhancement = Class.create(); -TinyMCEImageEnhancement.prototype = { - initialize: function() { - this.filesUploaded = 0; - this.processInProgress = false; - - Event.observe($('AddFolder'),'click',this.onAddFolder.bind(this)); - Event.observe($('FolderOk'),'click',this.onFolderOk.bind(this)); - Event.observe($('FolderCancel'),'click',this.onFolderCancel.bind(this)); - - this.onLoad(); - }, - - onLoad: function() { - this.upload = new Upload({ - fileUploadLimit : '6', - button_image_url : 'cms/images/swf-upload-button-small.jpg', - button_width : 59, - button_height: 18, - fileQueued: this.uploadFileQueuedCallback.bind(this), - fileComplete: this.uploadFileCompleteCallback.bind(this), - queueComplete: this.uploadQueueCompleteCallback.bind(this) - }); - }, - - /** - * Method creates HTML element, only reason for this method is DRY. - */ - addElement: function(tag, className, parent, properties) { - var e = document.createElement(tag); - Element.addClassName(e,className); - parent.appendChild(e); - Object.extend(e,properties); - return e; - }, - - /** - * Called when user clicks "add folder" anchor. - */ - - onAddFolder: function(event) { - Event.stop(event); - Element.hide('AddFolder'); - Element.show('NewFolderName','FolderOk','FolderCancel'); - this.applyIE6Hack(); - }, - - /** - * The user clicks the "ok" anchor link, the click event calls up - * this function which creates a new AJAX request to add a new folder - * using the addfolder function in AssetAdmin.php (admin/assets/addfolder). - */ - onFolderOk: function(event) { - Event.stop(event); - var folderName = $('NewFolderName').value; - var options = { - method: 'post', - postBody: 'ParentID=' + this.getParentID() + '&ajax=1&returnID=1&Name=' + folderName + ($('SecurityID') ? '&SecurityID=' + $('SecurityID').value : ''), - onSuccess: this.onFolderGetSuccess.bind(this), - onFailure: function(transport) { - errorMessage('Error: Folder not added', transport); - } - }; - - new Ajax.Request('admin/assets/addfolder', options); - }, - - /** - * If the "addFolderOk" function does a successful AJAX post, call this - * function. Take the folder ID that was created in "addFolderOk" - * via ajax and send data to modify that folder record. - */ - onFolderGetSuccess: function(transport) { - var folderID = transport.responseText; - - var date = new Date(); - var year = date.getFullYear(); - var month = date.getMonth() < 10 ? '0' + date.getMonth() : date.getMonth(); - var day = date.getDay() < 10 ? '0' + date.getDay() : date.getDay(); - var hours = date.getHours() < 10 ? '0' + date.getHours() : date.getHours(); - var minutes = date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes(); - var seconds = date.getSeconds() < 10 == 1 ? '0' + date.getSeconds() : date.getSeconds(); - var currentDate = year + '-' + month + '-' + day + ' ' + hours + ':' + minutes + ':' + seconds; - - var folderName = $('NewFolderName').value; - - this.folderID = folderID; - - statusMessage('Creating new folder'); - $('TreeDropdownField_Form_EditorToolbarImageForm_FolderID').itemTree = null; - $('TreeDropdownField_Form_EditorToolbarImageForm_FolderID').setValue(this.folderID); - $('NewFolderName').value = ''; - Element.show('AddFolder'); - Element.hide('NewFolderName','FolderOk','FolderCancel'); - this.removeIE6Hack(); - }, - - /** - * If user doesn't want to add folder return to default UI. - */ - - onFolderCancel: function(event) { - $('NewFolderName').value = ''; - Element.show('AddFolder'); - Element.hide('NewFolderName','FolderOk','FolderCancel'); - this.removeIE6Hack(); - Event.stop(event); - return false; - }, - - uploadFileQueuedCallback: function(file,queueLength) { - if(this.getParentID() == "root") { - statusMessage("Please choose folder","bad"); - } - else { - this.processInProgress = true; - this.upload.swfu.addPostParam('FolderID', this.getParentID()); - this.upload.swfu.addFileParam(file.id,'ID',this.folderID); - this.upload.swfu.addFileParam(file.id,'Files',file.name); - $('UploadFiles').innerHTML = "Uploading Files...("+ this.filesUploaded +")"; - this.upload.swfu.startUpload(file.id); - } - }, - - uploadFileCompleteCallback: function(file,serverData) { - this.filesUploaded++; - $('UploadFiles').innerHTML = 'Uploading Files..... ('+ this.filesUploaded +")"; - }, - - uploadQueueCompleteCallback: function(serverData) { - this.filesUploaded = this.upload.getFilesUploaded(); - $('UploadFiles').innerHTML = ""; - statusMessage('Uploaded Files Successfully','good'); - - $('FolderImages').ajaxGetFiles(this.getParentID(), null); - }, - - /** - * Iterates over all uploaded images and add them to TinyMCE editor - * - * @param transport object - */ - insertImages: function(transport) { - //HACK FOR STRANGE ERROR OCCURING UNDER SAFARI - if(transport.responseText == '') { - $('Image').ajaxGetFiles(this.getParentID(), null, this.insertImages.bind(this)); - return; - } - //END OF HACK - - $('Image').reapplyBehaviour(); - - this.addToTinyMCE = this.addToTinyMCE.bind(this); - this.processInProgress = false; - }, - - /** - * Adds particular image to TinyMCE. Most of code has been copied from tiny_mce_improvements.js / ImageThumbnail.onclick - * Sorry for not following DRY, I didn't want to break smth in tiny_mce_improvements. - * - * @param target object - */ - - addToTinyMCE: function(target) { - var formObj = $('Form_EditorToolbarImageForm'); - var altText = formObj.elements.AltText.value; - var cssClass = formObj.elements.CSSClass.value; - var baseURL = document.getElementsByTagName('base')[0].href; - var relativeHref = target.href.substr(baseURL.length) - if(!tinyMCE.selectedInstance) tinyMCE.selectedInstance = Toolbar.instance().editor; - if(tinyMCE.selectedInstance.contentWindow.focus) tinyMCE.selectedInstance.contentWindow.focus(); - // Extract dest width and dest height from the class name - var destWidth = null; - var destHeight = null; - try { - var imgTag = target.getElementsByTagName('img')[0]; - destWidth = imgTag.className.match(/destwidth=([0-9.\-]+)([, ]|$)/) ? RegExp.$1 : null; - destHeight = imgTag.className.match(/destheight=([0-9.\-]+)([, ]|$)/) ? RegExp.$1 : null; - } catch(er) { - } - TinyMCE_AdvancedTheme._insertImage(relativeHref, altText, 0, '', '', destWidth, destHeight, '', '', cssClass); - }, - - /** - * Under IE6 when we click on "add folder" anchor, rest of anchors loose their correct position - * - */ - - applyIE6Hack: function() { - if(/msie/i.test(navigator.userAgent)) { - elements = [$('FolderOk'),$('FolderCancel'),$('UploadFiles')]; - $A(elements).each(function(element) { - element.style.position = "relative"; - element.style.top = "-3px"; - }); - } - }, - - removeIE6Hack: function() { - if(/msie/i.test(navigator.userAgent)) { - elements = [$('FolderOk'),$('FolderCancel'),$('UploadFiles')]; - $A(elements).each(function(element) { - element.style.position = ""; - }); - } - }, - - /** - * Returns id of upload folder. - * - */ - - getParentID: function() { - return $('Form_EditorToolbarImageForm_FolderID').value == '' ? 'root' : $('Form_EditorToolbarImageForm_FolderID').value; - } -} -tinyMCEImageEnhancement = new TinyMCEImageEnhancement(); diff --git a/javascript/Upload.js b/javascript/Upload.js deleted file mode 100644 index 0805ab4c..00000000 --- a/javascript/Upload.js +++ /dev/null @@ -1,284 +0,0 @@ -/* - This class is wrapper for SWFUpload class. - If you want use SWFUpload, please use this class becuase it will take care of configuration - error handling and other things. - -*/ - -var Upload = Class.create(); -Upload.prototype = { - - /** - * Sets configuration data provided from user if smth is missing sets default value. - * - * @param params object contains all configuration data for upload. - */ - initialize: function(params) { - - this.filesUploaded = 0; - this.filesToUpload = 0; - this.folderID = 'root'; - this.uploadInProgress = false; - this.uploadMessage = ''; - this.queueComplete = this.uploadQueueCompleteCallback.bind(this); - this.fileComplete = this.uploadFileCompleteCallback.bind(this); - this.fileQueued = this.uploadFileQueuedCallback; - - if(typeof params.fileSizeLimit != 'undefined') this.setFileSizeLimit = params.fileSizeLimit; else this.fileSizeLimit = '30720'; - if(typeof params.fileTypes != 'undefined') this.fileTypes = params.fileTypes; else this.fileTypes = '*.*'; - if(typeof params.fileTypesDescription != 'undefined') this.fileTypesDescription = params.fileTypesDescription; else this.fileTypesDescription = 'All Files'; - if(typeof params.fileUploadLimit != 'undefined') this.fileUploadLimit = params.fileUploadLimit; else this.fileUploadLimit = '6'; - if(typeof params.beginUploadOnQueue != 'undefined') this.beginUploadOnQueue = params.beginUploadOnQueue; else this.beginUploadOnQueue = true; - if(typeof params.fileQueued != 'undefined') this.fileQueued = params.fileQueued; - - if(typeof params.fileProgress != 'undefined') this.fileProgress = params.fileProgress; else this.fileProgress = Prototype.emptyFunction; - if(typeof params.fileCancelled != 'undefined') this.fileCancelled = params.fileCancelled; - if(typeof params.fileComplete != 'undefined') this.fileComplete = params.fileComplete; - if(typeof params.queueComplete != 'undefined') this.queueComplete = params.queueComplete; - if(typeof params.buildUI != 'undefined') this.customBuildUI = params.buildUI; - if(typeof params.securityID != 'undefined') this.securityID = params.securityID; - if(typeof params.button_image_url != 'undefined') this.buttonImageURL = params.button_image_url; - if(typeof params.button_width != 'undefined') this.buttonWidth = params.button_width; - if(typeof params.button_height != 'undefined') this.buttonHeight = params.button_height; - - try { - this.onLoad(); - } catch (ex) { - // suppress the exception in case swfupload.js is loaded in a context that has no - // placeholder elements - if ( ex.indexOf('Could not find the placeholder element')==-1 ) - throw ex; - } - }, - - /** - * Creates SWFUpload object for uploading files. - * - */ - onLoad: function() { - - path = this.getBasePath(); - sessId = this.getSessionId();//Because flash doesn't send proper cookies, we need to set session id in URL. - this.swfu = new SWFUpload({ - upload_url: path + 'admin/assets/UploadForm?action_doUpload=1&PHPSESSID=' + sessId, // Relative to the SWF file - file_post_name: 'Files', - file_size_limit : this.fileSizeLimit, - file_types : this.fileTypes, - file_types_description : this.fileTypesDescription, - file_upload_limit : this.fileUploadLimit, - begin_upload_on_queue : this.beginUploadOnQueue, - use_server_data_event : true, - validate_files: false, - button_placeholder_id: "SWFUploadButton", - file_queued_handler : this.fileQueued, - upload_success_handler : this.queueComplete, - file_dialog_complete_handler : this.fileCompleted, - upload_progress_handler: this.uploadFileProgressCallback.bind(this), - error_handler : this.uploadErrorCallback.bind(this), - file_validation_handler : Prototype.emptyFunction, - file_cancelled_handler: Prototype.emptyFunction, - button_image_url : this.buttonImageURL, - button_window_mode : "transparent", - button_width : this.buttonWidth, - button_height : this.buttonHeight, - flash_url : 'cms/thirdparty/swfupload/swfupload.swf', // Relative to this file - swfupload_loaded_handler: this.buildUI.bind(this), - debug: false, - preserve_relative_urls: true - }); - - }, - - /** - * Retrieves base path from URL. - * TODO: Use base tag. - */ - - getBasePath: function() { - var path = 'http://' + window.location.host + window.location.pathname; - - if(path.match(/^(.*\/)admin/i)) return RegExp.$1; - else return path; - }, - - /** - * Retrieves sessionId from cookie. - * - */ - - getSessionId: function() { - var start = document.cookie.indexOf('PHPSESSID')+10; - var end = document.cookie.indexOf(';',start); - if(end == -1) end = document.cookie.length; - return document.cookie.substring(start,end); - }, - - /** - * Calls method defined by user, method should create user interface. - * - */ - - buildUI: function() { - return; - }, - - /** - * Called when new file is added to the queue - * - * @param file object - * @param queueLength int - */ - - uploadFileQueuedCallback: function(file,queueLength) { - this.filesToUpload++; -// this.swfu.fileQueued(file, queueLength); - this.addFileParam(file); - }, - - /** - * Called when uploading of particular file has finished - * - * @param file object - * @param servedData string - */ - uploadFileCompleteCallback: function(file,serverData) { - this.filesUploaded++; - if(serverData) { - var toEval = serverData.substr(serverData.indexOf('',''); - toEval = toEval.replace('',''); - this.uploadMessage = toEval; - } - - this.fileComplete(file, serverData); - - // Run the next file in the queue, if there is one - if(this.swfu.getStats().files_queued > 0) this.startUpload(); - // Otherwise indicate that the queue is finished - else { - this.queueComplete(); - this.uploadInProgress = false; - this.filesUploaded = 0; - this.filesToUpload = 0; - } - }, - - /** - * Called during uploading file. - * - * @param file object - * @param bytes_complete int - */ - - uploadFileProgressCallback: function(file, bytes_complete) { - this.uploadInProgress = true; - this.fileProgress(file, bytes_complete); - }, - - uploadQueueCompleteCallback: function(event) { - - }, - - /** - * Called on error. - * @param error_code int - * @param file object - * @param message string - */ - - uploadErrorCallback: function(error_code, file, message) { - this.swfu.cancelQueue(); - switch(error_code) { - case SWFUpload.ERROR_CODE_HTTP_ERROR: - alert('You have encountered an error. File hasn\'t been uploaded. Please hit the "Refresh" button in your web browser. Error Code: HTTP Error, File name: ' + file.name + ', Message: ' + msg); - break; - case SWFUpload.ERROR_CODE_IO_ERROR: - alert('You have encountered an error. File hasn\'t been uploaded. Please hit the "Refresh" button in your web browser. Error Code: IO Error, File name: ' + file.name + ', Message: ' + msg); - break; - case SWFUpload.ERROR_CODE_SECURITY_ERROR: - alert('You have encountered an error. File hasn\'t been uploaded. Please hit the "Refresh" button in your web browser. Error Code: Security Error, File name: ' + file.name + ', Message: ' + msg); - break; - case SWFUpload.ERROR_CODE_FILE_EXCEEDS_SIZE_LIMIT: - alert('Files cannot be bigger than ' + this.fileSizeLimit/1024 + ' MB.'); - break; - case SWFUpload.ERROR_CODE_ZERO_BYTE_FILE: - alert('Files cannot be empty'); - break; - case SWFUpload.ERROR_CODE_QUEUE_LIMIT_EXCEEDED: - alert('You can only have six files in queue'); - break; - case SWFUpload.ERROR_CODE_UPLOAD_FAILED: - alert('You have encountered an error. File hasn\'t has been uploaded. Please hit the "Refresh" button in your web browser'); - break; - case SWFUpload.ERROR_CODE_SPECIFIED_FILE_NOT_FOUND: - alert('You have encountered an error. File hasn\'t has been uploaded. Please hit the "Refresh" button in your web browser'); - break; - default: - alert('You have encountered an error. File hasn\'t has been uploaded. Please hit the "Refresh" button in your web browser'); - } - }, - - /** - * Because we are on top of standard upload we need to add some POST vars that - * normally are being sent as part of form. - * - * @param file object - */ - - addFileParam: function(file) { - this.swfu.addFileParam(file.id,'ID',this.folderID); - this.swfu.addFileParam(file.id,'action_doUpload','1'); - this.swfu.addFileParam(file.id,'Files',file.name); - this.swfu.addFileParam(file.id,'MAX_FILE_SIZE','31457280'); - }, - - /** - * Starts file explorer. - * - */ - - browse: function() { - this.swfu.selectFiles(); - }, - - /** - * Starts upload - * - */ - - startUpload: function() { - this.swfu.startUpload(); - }, - - /** - * Cancels uploading of file. - */ - - cancelUpload: function(fileId) { - this.filesToUpload--; - this.swfu.cancelUpload(fileId); - }, - - /* - * Getters and setters. - */ - setFolderID: function(id) { - this.folderID = id; - }, - - getFilesUploaded: function() { - return this.filesUploaded; - }, - - getFilesToUpload: function() { - return this.filesToUpload; - }, - - getUploadMessage: function() { - return this.uploadMessage; - }, - - isUploadInProgress: function() { - return this.uploadInProgress; - } -} diff --git a/thirdparty/swfupload/license.txt b/thirdparty/swfupload/license.txt deleted file mode 100644 index dd26171a..00000000 --- a/thirdparty/swfupload/license.txt +++ /dev/null @@ -1,11 +0,0 @@ -/** - * mmSWFUpload 1.0: Flash upload dialog - http://profandesign.se/swfupload/ - * - * SWFUpload is (c) 2006-2007 Lars Huring, Olov Nilzén and Mammon Media and is released under the MIT License: - * http://www.opensource.org/licenses/mit-license.php - * - * - * SWFUpload Revision 7.0 is (c) 2007 Jake Roberts and is released under the MIT License: - * http://www.opensource.org/licenses/mit-license.php - * - */ \ No newline at end of file diff --git a/thirdparty/swfupload/swfupload.js b/thirdparty/swfupload/swfupload.js deleted file mode 100755 index e1aaa3f7..00000000 --- a/thirdparty/swfupload/swfupload.js +++ /dev/null @@ -1,1052 +0,0 @@ -/** - * SWFUpload: http://www.swfupload.org, http://swfupload.googlecode.com - * - * mmSWFUpload 1.0: Flash upload dialog - http://profandesign.se/swfupload/, http://www.vinterwebb.se/ - * - * SWFUpload is (c) 2006-2007 Lars Huring, Olov Nilzén and Mammon Media and is released under the MIT License: - * http://www.opensource.org/licenses/mit-license.php - * - * SWFUpload 2 is (c) 2007-2008 Jake Roberts and is released under the MIT License: - * http://www.opensource.org/licenses/mit-license.php - * - */ - - -/* ******************* */ -/* Constructor & Init */ -/* ******************* */ -var SWFUpload; - -if (SWFUpload == undefined) { - SWFUpload = function (settings) { - this.initSWFUpload(settings); - }; -} - -SWFUpload.prototype.initSWFUpload = function (userSettings) { - try { - this.customSettings = {}; // A container where developers can place their own settings associated with this instance. - this.settings = {}; - this.eventQueue = []; - this.movieName = "SWFUpload_" + SWFUpload.movieCount++; - this.movieElement = null; - - - // Setup global control tracking - SWFUpload.instances[this.movieName] = this; - - // Load the settings. Load the Flash movie. - this.initSettings(userSettings); - this.loadFlash(); - this.displayDebugInfo(); - } catch (ex) { - delete SWFUpload.instances[this.movieName]; - throw ex; - } -}; - -/* *************** */ -/* Static Members */ -/* *************** */ -SWFUpload.instances = {}; -SWFUpload.movieCount = 0; -SWFUpload.version = "2.5.0 2009-11-04 Alpha"; -SWFUpload.QUEUE_ERROR = { - QUEUE_LIMIT_EXCEEDED : -100, - FILE_EXCEEDS_SIZE_LIMIT : -110, - ZERO_BYTE_FILE : -120, - INVALID_FILETYPE : -130 -}; -SWFUpload.UPLOAD_ERROR = { - HTTP_ERROR : -200, - MISSING_UPLOAD_URL : -210, - IO_ERROR : -220, - SECURITY_ERROR : -230, - UPLOAD_LIMIT_EXCEEDED : -240, - UPLOAD_FAILED : -250, - SPECIFIED_FILE_ID_NOT_FOUND : -260, - FILE_VALIDATION_FAILED : -270, - FILE_CANCELLED : -280, - UPLOAD_STOPPED : -290, - RESIZE_ERROR : -300 -}; -SWFUpload.FILE_STATUS = { - QUEUED : -1, - IN_PROGRESS : -2, - ERROR : -3, - COMPLETE : -4, - CANCELLED : -5 -}; -SWFUpload.UPLOAD_TYPE = { - NORMAL : -1, - RESIZED : -2 -}; - -SWFUpload.BUTTON_ACTION = { - SELECT_FILE : -100, - SELECT_FILES : -110, - START_UPLOAD : -120, - JAVASCRIPT : -130 -}; -SWFUpload.CURSOR = { - ARROW : -1, - HAND : -2 -}; -SWFUpload.WINDOW_MODE = { - WINDOW : "window", - TRANSPARENT : "transparent", - OPAQUE : "opaque" -}; - -SWFUpload.RESIZE_ENCODING = { - JPEG : -1, - PNG : -2 -}; - -// Private: takes a URL, determines if it is relative and converts to an absolute URL -// using the current site. Only processes the URL if it can, otherwise returns the URL untouched -SWFUpload.completeURL = function (url) { - try { - var path = "", indexSlash = -1; - if (typeof(url) !== "string" || url.match(/^https?:\/\//i) || url.match(/^\//) || url === "") { - return url; - } - - indexSlash = window.location.pathname.lastIndexOf("/"); - if (indexSlash <= 0) { - path = "/"; - } else { - path = window.location.pathname.substr(0, indexSlash) + "/"; - } - - return path + url; - } catch (ex) { - return url; - } -}; - - -/* ******************** */ -/* Instance Members */ -/* ******************** */ - -// Private: initSettings ensures that all the -// settings are set, getting a default value if one was not assigned. -SWFUpload.prototype.initSettings = function (userSettings) { - this.ensureDefault = function (settingName, defaultValue) { - var setting = userSettings[settingName]; - if (setting != undefined) { - this.settings[settingName] = setting; - } else { - this.settings[settingName] = defaultValue; - } - }; - - // Upload backend settings - this.ensureDefault("upload_url", ""); - - this.ensureDefault("preserve_relative_urls", false); - this.ensureDefault("file_post_name", "Filedata"); - this.ensureDefault("post_params", {}); - this.ensureDefault("use_query_string", false); - this.ensureDefault("requeue_on_error", false); - this.ensureDefault("http_success", []); - this.ensureDefault("assume_success_timeout", 0); - - // File Settings - this.ensureDefault("file_types", "*.*"); - this.ensureDefault("file_types_description", "All Files"); - this.ensureDefault("file_size_limit", 0); // Default zero means "unlimited" - this.ensureDefault("file_upload_limit", 0); - this.ensureDefault("file_queue_limit", 0); - - // Flash Settings - this.ensureDefault("flash_url", "swfupload.swf"); - this.ensureDefault("prevent_swf_caching", true); - - // Button Settings - this.ensureDefault("button_image_url", ""); - this.ensureDefault("button_width", 1); - this.ensureDefault("button_height", 1); - this.ensureDefault("button_text", ""); - this.ensureDefault("button_text_style", "color: #000000; font-size: 16pt;"); - this.ensureDefault("button_text_top_padding", 0); - this.ensureDefault("button_text_left_padding", 0); - this.ensureDefault("button_action", SWFUpload.BUTTON_ACTION.SELECT_FILES); - this.ensureDefault("button_disabled", false); - this.ensureDefault("button_placeholder_id", ""); - this.ensureDefault("button_placeholder", null); - this.ensureDefault("button_cursor", SWFUpload.CURSOR.ARROW); - this.ensureDefault("button_window_mode", SWFUpload.WINDOW_MODE.WINDOW); - - // Debug Settings - this.ensureDefault("debug", false); - this.settings.debug_enabled = this.settings.debug; // Here to maintain v2 API - - // Event Handlers - this.settings.return_upload_start_handler = this.returnUploadStart; - this.ensureDefault("swfupload_loaded_handler", null); - this.ensureDefault("file_dialog_start_handler", null); - this.ensureDefault("file_queued_handler", null); - this.ensureDefault("file_queue_error_handler", null); - this.ensureDefault("file_dialog_complete_handler", null); - - this.ensureDefault("upload_start_handler", null); - this.ensureDefault("upload_progress_handler", null); - this.ensureDefault("upload_error_handler", null); - this.ensureDefault("upload_success_handler", null); - this.ensureDefault("upload_complete_handler", null); - - this.ensureDefault("button_action_handler", null); - - this.ensureDefault("debug_handler", this.debugMessage); - - this.ensureDefault("custom_settings", {}); - - // Other settings - this.customSettings = this.settings.custom_settings; - - // Update the flash url if needed - if (!!this.settings.prevent_swf_caching) { - this.settings.flash_url = this.settings.flash_url + (this.settings.flash_url.indexOf("?") < 0 ? "?" : "&") + "preventswfcaching=" + new Date().getTime(); - } - - if (!this.settings.preserve_relative_urls) { - this.settings.upload_url = SWFUpload.completeURL(this.settings.upload_url); - this.settings.button_image_url = SWFUpload.completeURL(this.settings.button_image_url); - } - - delete this.ensureDefault; -}; - -// Private: loadFlash replaces the button_placeholder element with the flash movie. -SWFUpload.prototype.loadFlash = function () { - var targetElement, tempParent, wrapperType; - - // Make sure an element with the ID we are going to use doesn't already exist - if (document.getElementById(this.movieName) !== null) { - throw "ID " + this.movieName + " is already in use. The Flash Object could not be added"; - } - - // Get the element where we will be placing the flash movie - targetElement = document.getElementById(this.settings.button_placeholder_id) || this.settings.button_placeholder; - - if (targetElement == undefined) { - throw "Could not find the placeholder element: " + this.settings.button_placeholder_id; - } - - wrapperType = (targetElement.currentStyle && targetElement.currentStyle["display"] || window.getComputedStyle && document.defaultView.getComputedStyle(targetElement, null).getPropertyValue("display")) !== "block" ? "span" : "div"; - - // Append the container and load the flash - tempParent = document.createElement(wrapperType); - tempParent.innerHTML = this.getFlashHTML(); // Using innerHTML is non-standard but the only sensible way to dynamically add Flash in IE (and maybe other browsers) - - // Experiment -- try to get the movie element immediately - var els = tempParent.getElementsByTagName("object"); // FIXME - JSLint if this works - if (!els || els.length > 1 || els.length === 0) { - // Oh crap...bail - } else if (els.length === 1) { - this.movieElement = els[0]; - this.debug("Got the movie. WOOT!"); - } - - targetElement.parentNode.replaceChild(tempParent.firstChild, targetElement); - - // Fix IE Flash/Form bug - if (window[this.movieName] == undefined) { - window[this.movieName] = this.getMovieElement(); - } - -}; - -// Private: getFlashHTML generates the object tag needed to embed the flash in to the document -SWFUpload.prototype.getFlashHTML = function () { - // Flash Satay object syntax: http://www.alistapart.com/articles/flashsatay - return ['', - '', - '', - '', - '', - '', - '', - ''].join(""); -}; - -// Private: getFlashVars builds the parameter string that will be passed -// to flash in the flashvars param. -SWFUpload.prototype.getFlashVars = function () { - // Build a string from the post param object - var httpSuccessString, paramString; - - paramString = this.buildParamString(); - httpSuccessString = this.settings.http_success.join(","); - - // Build the parameter string - return ["movieName=", encodeURIComponent(this.movieName), - "&uploadURL=", encodeURIComponent(this.settings.upload_url), - "&useQueryString=", encodeURIComponent(this.settings.use_query_string), - "&requeueOnError=", encodeURIComponent(this.settings.requeue_on_error), - "&httpSuccess=", encodeURIComponent(httpSuccessString), - "&assumeSuccessTimeout=", encodeURIComponent(this.settings.assume_success_timeout), - "&params=", encodeURIComponent(paramString), - "&filePostName=", encodeURIComponent(this.settings.file_post_name), - "&fileTypes=", encodeURIComponent(this.settings.file_types), - "&fileTypesDescription=", encodeURIComponent(this.settings.file_types_description), - "&fileSizeLimit=", encodeURIComponent(this.settings.file_size_limit), - "&fileUploadLimit=", encodeURIComponent(this.settings.file_upload_limit), - "&fileQueueLimit=", encodeURIComponent(this.settings.file_queue_limit), - "&debugEnabled=", encodeURIComponent(this.settings.debug_enabled), - "&buttonImageURL=", encodeURIComponent(this.settings.button_image_url), - "&buttonWidth=", encodeURIComponent(this.settings.button_width), - "&buttonHeight=", encodeURIComponent(this.settings.button_height), - "&buttonText=", encodeURIComponent(this.settings.button_text), - "&buttonTextTopPadding=", encodeURIComponent(this.settings.button_text_top_padding), - "&buttonTextLeftPadding=", encodeURIComponent(this.settings.button_text_left_padding), - "&buttonTextStyle=", encodeURIComponent(this.settings.button_text_style), - "&buttonAction=", encodeURIComponent(this.settings.button_action), - "&buttonDisabled=", encodeURIComponent(this.settings.button_disabled), - "&buttonCursor=", encodeURIComponent(this.settings.button_cursor) - ].join(""); -}; - -// Public: get retrieves the DOM reference to the Flash element added by SWFUpload -// The element is cached after the first lookup -SWFUpload.prototype.getMovieElement = function () { - if (this.movieElement == undefined) { - this.movieElement = document.getElementById(this.movieName); - } - - if (this.movieElement === null) { - throw "Could not find Flash element"; - } - - return this.movieElement; -}; - -// Private: buildParamString takes the name/value pairs in the post_params setting object -// and joins them up in to a string formatted "name=value&name=value" -SWFUpload.prototype.buildParamString = function () { - var name, postParams, paramStringPairs = []; - - postParams = this.settings.post_params; - - if (typeof(postParams) === "object") { - for (name in postParams) { - if (postParams.hasOwnProperty(name)) { - paramStringPairs.push(encodeURIComponent(name.toString()) + "=" + encodeURIComponent(postParams[name].toString())); - } - } - } - - return paramStringPairs.join("&"); -}; - -// Public: Used to remove a SWFUpload instance from the page. This method strives to remove -// all references to the SWF, and other objects so memory is properly freed. -// Returns true if everything was destroyed. Returns a false if a failure occurs leaving SWFUpload in an inconsistant state. -// Credits: Major improvements provided by steffen -SWFUpload.prototype.destroy = function () { - var movieElement; - - try { - // Make sure Flash is done before we try to remove it - this.cancelUpload(null, false); - - // Stop the external interface check from running - this.callFlash("StopExternalInterfaceCheck"); - - movieElement = this.cleanUp(); - - // Remove the SWFUpload DOM nodes - if (movieElement) { - // Remove the Movie Element from the page - try { - movieElement.parentNode.removeChild(movieElement); - } catch (ex) {} - } - - // Remove IE form fix reference - window[this.movieName] = null; - - // Destroy other references - SWFUpload.instances[this.movieName] = null; - delete SWFUpload.instances[this.movieName]; - - this.movieElement = null; - this.settings = null; - this.customSettings = null; - this.eventQueue = null; - this.movieName = null; - - - return true; - } catch (ex2) { - return false; - } -}; - - -// Public: displayDebugInfo prints out settings and configuration -// information about this SWFUpload instance. -// This function (and any references to it) can be deleted when placing -// SWFUpload in production. -SWFUpload.prototype.displayDebugInfo = function () { - this.debug( - [ - "---SWFUpload Instance Info---\n", - "Version: ", SWFUpload.version, "\n", - "Movie Name: ", this.movieName, "\n", - "Settings:\n", - "\t", "upload_url: ", this.settings.upload_url, "\n", - "\t", "flash_url: ", this.settings.flash_url, "\n", - "\t", "use_query_string: ", this.settings.use_query_string.toString(), "\n", - "\t", "requeue_on_error: ", this.settings.requeue_on_error.toString(), "\n", - "\t", "http_success: ", this.settings.http_success.join(", "), "\n", - "\t", "assume_success_timeout: ", this.settings.assume_success_timeout, "\n", - "\t", "file_post_name: ", this.settings.file_post_name, "\n", - "\t", "post_params: ", this.settings.post_params.toString(), "\n", - "\t", "file_types: ", this.settings.file_types, "\n", - "\t", "file_types_description: ", this.settings.file_types_description, "\n", - "\t", "file_size_limit: ", this.settings.file_size_limit, "\n", - "\t", "file_upload_limit: ", this.settings.file_upload_limit, "\n", - "\t", "file_queue_limit: ", this.settings.file_queue_limit, "\n", - "\t", "debug: ", this.settings.debug.toString(), "\n", - - "\t", "prevent_swf_caching: ", this.settings.prevent_swf_caching.toString(), "\n", - - "\t", "button_placeholder_id: ", this.settings.button_placeholder_id.toString(), "\n", - "\t", "button_placeholder: ", (this.settings.button_placeholder ? "Set" : "Not Set"), "\n", - "\t", "button_image_url: ", this.settings.button_image_url.toString(), "\n", - "\t", "button_width: ", this.settings.button_width.toString(), "\n", - "\t", "button_height: ", this.settings.button_height.toString(), "\n", - "\t", "button_text: ", this.settings.button_text.toString(), "\n", - "\t", "button_text_style: ", this.settings.button_text_style.toString(), "\n", - "\t", "button_text_top_padding: ", this.settings.button_text_top_padding.toString(), "\n", - "\t", "button_text_left_padding: ", this.settings.button_text_left_padding.toString(), "\n", - "\t", "button_action: ", this.settings.button_action.toString(), "\n", - "\t", "button_disabled: ", this.settings.button_disabled.toString(), "\n", - - "\t", "custom_settings: ", this.settings.custom_settings.toString(), "\n", - "Event Handlers:\n", - "\t", "swfupload_loaded_handler assigned: ", (typeof this.settings.swfupload_loaded_handler === "function").toString(), "\n", - "\t", "file_dialog_start_handler assigned: ", (typeof this.settings.file_dialog_start_handler === "function").toString(), "\n", - "\t", "file_queued_handler assigned: ", (typeof this.settings.file_queued_handler === "function").toString(), "\n", - "\t", "file_queue_error_handler assigned: ", (typeof this.settings.file_queue_error_handler === "function").toString(), "\n", - "\t", "upload_start_handler assigned: ", (typeof this.settings.upload_start_handler === "function").toString(), "\n", - "\t", "upload_progress_handler assigned: ", (typeof this.settings.upload_progress_handler === "function").toString(), "\n", - "\t", "upload_error_handler assigned: ", (typeof this.settings.upload_error_handler === "function").toString(), "\n", - "\t", "upload_success_handler assigned: ", (typeof this.settings.upload_success_handler === "function").toString(), "\n", - "\t", "upload_complete_handler assigned: ", (typeof this.settings.upload_complete_handler === "function").toString(), "\n", - "\t", "debug_handler assigned: ", (typeof this.settings.debug_handler === "function").toString(), "\n" - ].join("") - ); -}; - -/* Note: addSetting and getSetting are no longer used by SWFUpload but are included - the maintain v2 API compatibility -*/ -// Public: (Deprecated) addSetting adds a setting value. If the value given is undefined or null then the default_value is used. -SWFUpload.prototype.addSetting = function (name, value, default_value) { - if (value == undefined) { - return (this.settings[name] = default_value); - } else { - return (this.settings[name] = value); - } -}; - -// Public: (Deprecated) getSetting gets a setting. Returns an empty string if the setting was not found. -SWFUpload.prototype.getSetting = function (name) { - if (this.settings[name] != undefined) { - return this.settings[name]; - } - - return ""; -}; - - - -// Private: callFlash handles function calls made to the Flash element. -// Calls are made with a setTimeout for some functions to work around -// bugs in the ExternalInterface library. -SWFUpload.prototype.callFlash = function (functionName, argumentArray) { - var movieElement, returnValue, returnString; - - argumentArray = argumentArray || []; - movieElement = this.getMovieElement(); - - // Flash's method if calling ExternalInterface methods (code adapted from MooTools). - try { - if (movieElement && movieElement.CallFunction) { - returnString = movieElement.CallFunction('' + __flash__argumentsToXML(argumentArray, 0) + ''); - returnValue = eval(returnString); - } else { - this.debug("Can't call flash because the movie wasn't found."); - } - } catch (ex) { - this.debug("Exception calling flash: " + ex.message); - } - - // Unescape file post param values - if (returnValue != undefined && typeof returnValue.post === "object") { - returnValue = this.unescapeFilePostParams(returnValue); - } - - return returnValue; -}; - -/* ***************************** - -- Flash control methods -- - Your UI should use these - to operate SWFUpload - ***************************** */ - -// WARNING: this function does not work in Flash Player 10 -// Public: selectFile causes a File Selection Dialog window to appear. This -// dialog only allows 1 file to be selected. -SWFUpload.prototype.selectFile = function () { - this.callFlash("SelectFile"); -}; - -// WARNING: this function does not work in Flash Player 10 -// Public: selectFiles causes a File Selection Dialog window to appear/ This -// dialog allows the user to select any number of files -// Flash Bug Warning: Flash limits the number of selectable files based on the combined length of the file names. -// If the selection name length is too long the dialog will fail in an unpredictable manner. There is no work-around -// for this bug. -SWFUpload.prototype.selectFiles = function () { - this.callFlash("SelectFiles"); -}; - - -// Public: startUpload starts uploading the first file in the queue unless -// the optional parameter 'fileID' specifies the ID -SWFUpload.prototype.startUpload = function (fileID) { - this.callFlash("StartUpload", [fileID]); -}; - -// Public: startUpload starts uploading the first file in the queue unless -// the optional parameter 'fileID' specifies the ID -SWFUpload.prototype.startResizedUpload = function (fileID, width, height, encoding, quality) { - this.callFlash("StartUpload", [fileID, { "width": width, "height" : height, "encoding" : encoding, "quality" : quality }]); -}; - -// Public: cancelUpload cancels any queued file. The fileID parameter may be the file ID or index. -// If you do not specify a fileID the current uploading file or first file in the queue is cancelled. -// If you do not want the uploadError event to trigger you can specify false for the triggerErrorEvent parameter. -SWFUpload.prototype.cancelUpload = function (fileID, triggerErrorEvent) { - if (triggerErrorEvent !== false) { - triggerErrorEvent = true; - } - this.callFlash("CancelUpload", [fileID, triggerErrorEvent]); -}; - -// Public: stopUpload stops the current upload and requeues the file at the beginning of the queue. -// If nothing is currently uploading then nothing happens. -SWFUpload.prototype.stopUpload = function () { - this.callFlash("StopUpload"); -}; - - -// Public: requeueUpload requeues any file. If the file is requeued or already queued true is returned. -// If the file is not found or is currently uploading false is returned. Requeuing a file bypasses the -// file size, queue size, upload limit and other queue checks. Certain files can't be requeued (e.g, invalid or zero bytes files). -SWFUpload.prototype.requeueUpload = function (indexOrFileID) { - return this.callFlash("RequeueUpload", [indexOrFileID]); -}; - - -/* ************************ - * Settings methods - * These methods change the SWFUpload settings. - * SWFUpload settings should not be changed directly on the settings object - * since many of the settings need to be passed to Flash in order to take - * effect. - * *********************** */ - -// Public: getStats gets the file statistics object. -SWFUpload.prototype.getStats = function () { - return this.callFlash("GetStats"); -}; - -// Public: setStats changes the SWFUpload statistics. You shouldn't need to -// change the statistics but you can. Changing the statistics does not -// affect SWFUpload accept for the successful_uploads count which is used -// by the upload_limit setting to determine how many files the user may upload. -SWFUpload.prototype.setStats = function (statsObject) { - this.callFlash("SetStats", [statsObject]); -}; - -// Public: getFile retrieves a File object by ID or Index. If the file is -// not found then 'null' is returned. -SWFUpload.prototype.getFile = function (fileID) { - if (typeof(fileID) === "number") { - return this.callFlash("GetFileByIndex", [fileID]); - } else { - return this.callFlash("GetFile", [fileID]); - } -}; - -// Public: getFileFromQueue retrieves a File object by ID or Index. If the file is -// not found then 'null' is returned. -SWFUpload.prototype.getQueueFile = function (fileID) { - if (typeof(fileID) === "number") { - return this.callFlash("GetFileByQueueIndex", [fileID]); - } else { - return this.callFlash("GetFile", [fileID]); - } -}; - - -// Public: addFileParam sets a name/value pair that will be posted with the -// file specified by the Files ID. If the name already exists then the -// exiting value will be overwritten. -SWFUpload.prototype.addFileParam = function (fileID, name, value) { - return this.callFlash("AddFileParam", [fileID, name, value]); -}; - -// Public: removeFileParam removes a previously set (by addFileParam) name/value -// pair from the specified file. -SWFUpload.prototype.removeFileParam = function (fileID, name) { - this.callFlash("RemoveFileParam", [fileID, name]); -}; - -// Public: setUploadUrl changes the upload_url setting. -SWFUpload.prototype.setUploadURL = function (url) { - this.settings.upload_url = url.toString(); - this.callFlash("SetUploadURL", [url]); -}; - -// Public: setPostParams changes the post_params setting -SWFUpload.prototype.setPostParams = function (paramsObject) { - this.settings.post_params = paramsObject; - this.callFlash("SetPostParams", [paramsObject]); -}; - -// Public: addPostParam adds post name/value pair. Each name can have only one value. -SWFUpload.prototype.addPostParam = function (name, value) { - this.settings.post_params[name] = value; - this.callFlash("SetPostParams", [this.settings.post_params]); -}; - -// Public: removePostParam deletes post name/value pair. -SWFUpload.prototype.removePostParam = function (name) { - delete this.settings.post_params[name]; - this.callFlash("SetPostParams", [this.settings.post_params]); -}; - -// Public: setFileTypes changes the file_types setting and the file_types_description setting -SWFUpload.prototype.setFileTypes = function (types, description) { - this.settings.file_types = types; - this.settings.file_types_description = description; - this.callFlash("SetFileTypes", [types, description]); -}; - -// Public: setFileSizeLimit changes the file_size_limit setting -SWFUpload.prototype.setFileSizeLimit = function (fileSizeLimit) { - this.settings.file_size_limit = fileSizeLimit; - this.callFlash("SetFileSizeLimit", [fileSizeLimit]); -}; - -// Public: setFileUploadLimit changes the file_upload_limit setting -SWFUpload.prototype.setFileUploadLimit = function (fileUploadLimit) { - this.settings.file_upload_limit = fileUploadLimit; - this.callFlash("SetFileUploadLimit", [fileUploadLimit]); -}; - -// Public: setFileQueueLimit changes the file_queue_limit setting -SWFUpload.prototype.setFileQueueLimit = function (fileQueueLimit) { - this.settings.file_queue_limit = fileQueueLimit; - this.callFlash("SetFileQueueLimit", [fileQueueLimit]); -}; - -// Public: setFilePostName changes the file_post_name setting -SWFUpload.prototype.setFilePostName = function (filePostName) { - this.settings.file_post_name = filePostName; - this.callFlash("SetFilePostName", [filePostName]); -}; - -// Public: setUseQueryString changes the use_query_string setting -SWFUpload.prototype.setUseQueryString = function (useQueryString) { - this.settings.use_query_string = useQueryString; - this.callFlash("SetUseQueryString", [useQueryString]); -}; - -// Public: setRequeueOnError changes the requeue_on_error setting -SWFUpload.prototype.setRequeueOnError = function (requeueOnError) { - this.settings.requeue_on_error = requeueOnError; - this.callFlash("SetRequeueOnError", [requeueOnError]); -}; - -// Public: setHTTPSuccess changes the http_success setting -SWFUpload.prototype.setHTTPSuccess = function (http_status_codes) { - if (typeof http_status_codes === "string") { - http_status_codes = http_status_codes.replace(" ", "").split(","); - } - - this.settings.http_success = http_status_codes; - this.callFlash("SetHTTPSuccess", [http_status_codes]); -}; - -// Public: setHTTPSuccess changes the http_success setting -SWFUpload.prototype.setAssumeSuccessTimeout = function (timeout_seconds) { - this.settings.assume_success_timeout = timeout_seconds; - this.callFlash("SetAssumeSuccessTimeout", [timeout_seconds]); -}; - -// Public: setDebugEnabled changes the debug_enabled setting -SWFUpload.prototype.setDebugEnabled = function (debugEnabled) { - this.settings.debug_enabled = debugEnabled; - this.callFlash("SetDebugEnabled", [debugEnabled]); -}; - -// Public: setButtonImageURL loads a button image sprite -SWFUpload.prototype.setButtonImageURL = function (buttonImageURL) { - if (buttonImageURL == undefined) { - buttonImageURL = ""; - } - - this.settings.button_image_url = buttonImageURL; - this.callFlash("SetButtonImageURL", [buttonImageURL]); -}; - -// Public: setButtonDimensions resizes the Flash Movie and button -SWFUpload.prototype.setButtonDimensions = function (width, height) { - this.settings.button_width = width; - this.settings.button_height = height; - - var movie = this.getMovieElement(); - if (movie != undefined) { - movie.style.width = width + "px"; - movie.style.height = height + "px"; - } - - this.callFlash("SetButtonDimensions", [width, height]); -}; -// Public: setButtonText Changes the text overlaid on the button -SWFUpload.prototype.setButtonText = function (html) { - this.settings.button_text = html; - this.callFlash("SetButtonText", [html]); -}; -// Public: setButtonTextPadding changes the top and left padding of the text overlay -SWFUpload.prototype.setButtonTextPadding = function (left, top) { - this.settings.button_text_top_padding = top; - this.settings.button_text_left_padding = left; - this.callFlash("SetButtonTextPadding", [left, top]); -}; - -// Public: setButtonTextStyle changes the CSS used to style the HTML/Text overlaid on the button -SWFUpload.prototype.setButtonTextStyle = function (css) { - this.settings.button_text_style = css; - this.callFlash("SetButtonTextStyle", [css]); -}; -// Public: setButtonDisabled disables/enables the button -SWFUpload.prototype.setButtonDisabled = function (isDisabled) { - this.settings.button_disabled = isDisabled; - this.callFlash("SetButtonDisabled", [isDisabled]); -}; -// Public: setButtonAction sets the action that occurs when the button is clicked -SWFUpload.prototype.setButtonAction = function (buttonAction) { - this.settings.button_action = buttonAction; - this.callFlash("SetButtonAction", [buttonAction]); -}; - -// Public: setButtonCursor changes the mouse cursor displayed when hovering over the button -SWFUpload.prototype.setButtonCursor = function (cursor) { - this.settings.button_cursor = cursor; - this.callFlash("SetButtonCursor", [cursor]); -}; - - -/* ******************************* - Flash Event Interfaces - These functions are used by Flash to trigger the various - events. - - All these functions a Private. - - Because the ExternalInterface library is buggy the event calls - are added to a queue and the queue then executed by a setTimeout. - This ensures that events are executed in a determinate order and that - the ExternalInterface bugs are avoided. -******************************* */ - -SWFUpload.prototype.queueEvent = function (handlerName, argumentArray) { - // Warning: Don't call this.debug inside here or you'll create an infinite loop - var self = this; - - if (argumentArray == undefined) { - argumentArray = []; - } else if (!(argumentArray instanceof Array)) { - argumentArray = [argumentArray]; - } - - if (typeof this.settings[handlerName] === "function") { - // Queue the event - this.eventQueue.push(function () { - this.settings[handlerName].apply(this, argumentArray); - }); - - // Execute the next queued event - setTimeout(function () { - self.executeNextEvent(); - }, 0); - - } else if (this.settings[handlerName] !== null) { - throw "Event handler " + handlerName + " is unknown or is not a function"; - } -}; - -// Private: Causes the next event in the queue to be executed. Since events are queued using a setTimeout -// we must queue them in order to garentee that they are executed in order. -SWFUpload.prototype.executeNextEvent = function () { - // Warning: Don't call this.debug inside here or you'll create an infinite loop - - var f = this.eventQueue ? this.eventQueue.shift() : null; - if (typeof(f) === "function") { - f.apply(this); - } -}; - -// Private: unescapeFileParams is part of a workaround for a flash bug where objects passed through ExternalInterface cannot have -// properties that contain characters that are not valid for JavaScript identifiers. To work around this -// the Flash Component escapes the parameter names and we must unescape again before passing them along. -SWFUpload.prototype.unescapeFilePostParams = function (file) { - var reg = /[$]([0-9a-f]{4})/i, unescapedPost = {}, uk, k, match; - - if (file != undefined) { - for (k in file.post) { - if (file.post.hasOwnProperty(k)) { - uk = k; - while ((match = reg.exec(uk)) !== null) { - uk = uk.replace(match[0], String.fromCharCode(parseInt("0x" + match[1], 16))); - } - unescapedPost[uk] = file.post[k]; - } - } - - file.post = unescapedPost; - } - - return file; -}; - -// Private: Called by Flash to see if JS can call in to Flash (test if External Interface is working) -SWFUpload.prototype.testExternalInterface = function () { - try { - return this.callFlash("TestExternalInterface"); - } catch (ex) { - return false; - } -}; - -// Private: This event is called by Flash when it has finished loading. Don't modify this. -// Use the swfupload_loaded_handler event setting to execute custom code when SWFUpload has loaded. -SWFUpload.prototype.flashReady = function () { - // Check that the movie element is loaded correctly with its ExternalInterface methods defined - var movieElement = this.getMovieElement(); - - if (!movieElement) { - this.debug("Flash called back ready but the flash movie can't be found."); - return; - } - - this.cleanUp(); - - this.queueEvent("swfupload_loaded_handler"); -}; - -// Private: removes Flash added fuctions to the DOM node to prevent memory leaks in IE. -// This function is called by Flash each time the ExternalInterface functions are created. -SWFUpload.prototype.cleanUp = function () { - var key, movieElement = this.getMovieElement(); - - // Pro-actively unhook all the Flash functions - try { - if (movieElement && typeof(movieElement.CallFunction) === "unknown") { // We only want to do this in IE - this.debug("Removing Flash functions hooks (this should only run in IE and should prevent memory leaks)"); - for (key in movieElement) { - try { - if (typeof(movieElement[key]) === "function") { - movieElement[key] = null; - } - } catch (ex) { - } - } - } - } catch (ex1) { - - } - - // Fix Flashes own cleanup code so if the SWF Movie was removed from the page - // it doesn't display errors. - window["__flash__removeCallback"] = function (instance, name) { - try { - if (instance) { - instance[name] = null; - } - } catch (flashEx) { - - } - }; - - return movieElement; -}; - -/* When the button_action is set to JavaScript this event gets fired and executes the button_action_handler */ -SWFUpload.prototype.buttonAction = function () { - this.queueEvent("button_action_handler"); -}; - -/* This is a chance to do something before the browse window opens */ -SWFUpload.prototype.fileDialogStart = function () { - this.queueEvent("file_dialog_start_handler"); -}; - - -/* Called when a file is successfully added to the queue. */ -SWFUpload.prototype.fileQueued = function (file) { - file = this.unescapeFilePostParams(file); - this.queueEvent("file_queued_handler", file); -}; - - -/* Handle errors that occur when an attempt to queue a file fails. */ -SWFUpload.prototype.fileQueueError = function (file, errorCode, message) { - file = this.unescapeFilePostParams(file); - this.queueEvent("file_queue_error_handler", [file, errorCode, message]); -}; - -/* Called after the file dialog has closed and the selected files have been queued. - You could call startUpload here if you want the queued files to begin uploading immediately. */ -SWFUpload.prototype.fileDialogComplete = function (numFilesSelected, numFilesQueued, numFilesInQueue) { - this.queueEvent("file_dialog_complete_handler", [numFilesSelected, numFilesQueued, numFilesInQueue]); -}; - -SWFUpload.prototype.uploadStart = function (file) { - file = this.unescapeFilePostParams(file); - this.queueEvent("return_upload_start_handler", file); -}; - -SWFUpload.prototype.returnUploadStart = function (file) { - var returnValue; - if (typeof this.settings.upload_start_handler === "function") { - file = this.unescapeFilePostParams(file); - returnValue = this.settings.upload_start_handler.call(this, file); - } else if (this.settings.upload_start_handler != undefined) { - throw "upload_start_handler must be a function"; - } - - // Convert undefined to true so if nothing is returned from the upload_start_handler it is - // interpretted as 'true'. - if (returnValue === undefined) { - returnValue = true; - } - - returnValue = !!returnValue; - - this.callFlash("ReturnUploadStart", [returnValue]); -}; - - - -SWFUpload.prototype.uploadProgress = function (file, bytesComplete, bytesTotal) { - file = this.unescapeFilePostParams(file); - this.queueEvent("upload_progress_handler", [file, bytesComplete, bytesTotal]); -}; - -SWFUpload.prototype.uploadError = function (file, errorCode, message) { - file = this.unescapeFilePostParams(file); - this.queueEvent("upload_error_handler", [file, errorCode, message]); -}; - -SWFUpload.prototype.uploadSuccess = function (file, serverData, responseReceived) { - file = this.unescapeFilePostParams(file); - this.queueEvent("upload_success_handler", [file, serverData, responseReceived]); -}; - -SWFUpload.prototype.uploadComplete = function (file) { - file = this.unescapeFilePostParams(file); - this.queueEvent("upload_complete_handler", file); -}; - -/* Called by SWFUpload JavaScript and Flash functions when debug is enabled. By default it writes messages to the - internal debug console. You can override this event and have messages written where you want. */ -SWFUpload.prototype.debug = function (message) { - this.queueEvent("debug_handler", message); -}; - - -/* ********************************** - Debug Console - The debug console is a self contained, in page location - for debug message to be sent. The Debug Console adds - itself to the body if necessary. - - The console is automatically scrolled as messages appear. - - If you are using your own debug handler or when you deploy to production and - have debug disabled you can remove these functions to reduce the file size - and complexity. -********************************** */ - -// Private: debugMessage is the default debug_handler. If you want to print debug messages -// call the debug() function. When overriding the function your own function should -// check to see if the debug setting is true before outputting debug information. -SWFUpload.prototype.debugMessage = function (message) { - var exceptionMessage, exceptionValues, key; - - if (this.settings.debug) { - exceptionValues = []; - - // Check for an exception object and print it nicely - if (typeof message === "object" && typeof message.name === "string" && typeof message.message === "string") { - for (key in message) { - if (message.hasOwnProperty(key)) { - exceptionValues.push(key + ": " + message[key]); - } - } - exceptionMessage = exceptionValues.join("\n") || ""; - exceptionValues = exceptionMessage.split("\n"); - exceptionMessage = "EXCEPTION: " + exceptionValues.join("\nEXCEPTION: "); - SWFUpload.Console.writeLine(exceptionMessage); - } else { - SWFUpload.Console.writeLine(message); - } - } -}; - -SWFUpload.Console = {}; -SWFUpload.Console.writeLine = function (message) { - var console, documentForm; - - try { - console = document.getElementById("SWFUpload_Console"); - - if (!console) { - documentForm = document.createElement("form"); - document.getElementsByTagName("body")[0].appendChild(documentForm); - - console = document.createElement("textarea"); - console.id = "SWFUpload_Console"; - console.style.fontFamily = "monospace"; - console.setAttribute("wrap", "off"); - console.wrap = "off"; - console.style.overflow = "auto"; - console.style.width = "400px"; - console.style.height = "450px"; - - // ss fixes - console.style.position = "absolute"; - console.style.top = "5px"; - console.style.zIndex = "100"; - - documentForm.appendChild(console); - } - - console.value += message + "\n"; - - console.scrollTop = console.scrollHeight - console.clientHeight; - } catch (ex) { - alert("Exception: " + ex.name + " Message: " + ex.message); - } -}; diff --git a/thirdparty/swfupload/swfupload.queue.js b/thirdparty/swfupload/swfupload.queue.js deleted file mode 100755 index 69e619da..00000000 --- a/thirdparty/swfupload/swfupload.queue.js +++ /dev/null @@ -1,98 +0,0 @@ -/* - Queue Plug-in - - Features: - *Adds a cancelQueue() method for cancelling the entire queue. - *All queued files are uploaded when startUpload() is called. - *If false is returned from uploadComplete then the queue upload is stopped. - If false is not returned (strict comparison) then the queue upload is continued. - *Adds a QueueComplete event that is fired when all the queued files have finished uploading. - Set the event handler with the queue_complete_handler setting. - - */ - -var SWFUpload; -if (typeof(SWFUpload) === "function") { - SWFUpload.queue = {}; - - SWFUpload.prototype.initSettings = (function (oldInitSettings) { - return function () { - if (typeof(oldInitSettings) === "function") { - oldInitSettings.call(this); - } - - this.queueSettings = {}; - - this.queueSettings.queue_cancelled_flag = false; - this.queueSettings.queue_upload_count = 0; - - this.queueSettings.user_upload_complete_handler = this.settings.upload_complete_handler; - this.queueSettings.user_upload_start_handler = this.settings.upload_start_handler; - this.settings.upload_complete_handler = SWFUpload.queue.uploadCompleteHandler; - this.settings.upload_start_handler = SWFUpload.queue.uploadStartHandler; - - this.settings.queue_complete_handler = this.settings.queue_complete_handler || null; - }; - })(SWFUpload.prototype.initSettings); - - SWFUpload.prototype.startUpload = function (fileID) { - this.queueSettings.queue_cancelled_flag = false; - this.callFlash("StartUpload", [fileID]); - }; - - SWFUpload.prototype.cancelQueue = function () { - this.queueSettings.queue_cancelled_flag = true; - this.stopUpload(); - - var stats = this.getStats(); - while (stats.files_queued > 0) { - this.cancelUpload(); - stats = this.getStats(); - } - }; - - SWFUpload.queue.uploadStartHandler = function (file) { - var returnValue; - if (typeof(this.queueSettings.user_upload_start_handler) === "function") { - returnValue = this.queueSettings.user_upload_start_handler.call(this, file); - } - - // To prevent upload a real "FALSE" value must be returned, otherwise default to a real "TRUE" value. - returnValue = (returnValue === false) ? false : true; - - this.queueSettings.queue_cancelled_flag = !returnValue; - - return returnValue; - }; - - SWFUpload.queue.uploadCompleteHandler = function (file) { - var user_upload_complete_handler = this.queueSettings.user_upload_complete_handler; - var continueUpload; - - if (file.filestatus === SWFUpload.FILE_STATUS.COMPLETE) { - this.queueSettings.queue_upload_count++; - } - - if (typeof(user_upload_complete_handler) === "function") { - continueUpload = (user_upload_complete_handler.call(this, file) === false) ? false : true; - } else if (file.filestatus === SWFUpload.FILE_STATUS.QUEUED) { - // If the file was stopped and re-queued don't restart the upload - continueUpload = false; - } else { - continueUpload = true; - } - - if (continueUpload) { - var stats = this.getStats(); - if (stats.files_queued > 0 && this.queueSettings.queue_cancelled_flag === false) { - this.startUpload(); - } else if (this.queueSettings.queue_cancelled_flag === false) { - this.queueEvent("queue_complete_handler", [this.queueSettings.queue_upload_count]); - this.queueSettings.queue_upload_count = 0; - } else { - this.queueSettings.queue_cancelled_flag = false; - this.queueSettings.queue_upload_count = 0; - } - } - }; -} \ No newline at end of file diff --git a/thirdparty/swfupload/swfupload.swf b/thirdparty/swfupload/swfupload.swf deleted file mode 100755 index 71ff884c33caf257d0d4bc101027d4b0a974d42e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 33328 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 E+CYH~eE