summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndreas Gohr <andi@splitbrain.org>2008-06-07 00:28:49 +0200
committerAndreas Gohr <andi@splitbrain.org>2008-06-07 00:28:49 +0200
commitd00ec4555f81210cd067f98d9bc7cef51f456462 (patch)
treeb9e42451c57ff91dadd2423bb550603de6f52ef8
parent09c2d803ca738992288c138eab41f35008e3e3df (diff)
downloadrpg-d00ec4555f81210cd067f98d9bc7cef51f456462.tar.gz
rpg-d00ec4555f81210cd067f98d9bc7cef51f456462.tar.bz2
experimental Flash based multi upload
This patch adds experimental support for uploading multiple files in one go. This is achieved by using Flash for selecting multiple images and intitiating the HTTP upload. When Flash 8 or higher is detected, an additional icon is added to the usual upload form. Clicking it will swith the form to the Flash based upload queue. Things that need work: * Better Icon * Feedback if Flash detection works on all Flash 8 supported platforms * Progress feedback seems not to work on Linux (might be Adobe bug) * No final feedback how many images were uploaded correctly The flash sources are located in lib/_fla/ Any feedback and help would be appreciated. darcs-hash:20080606222849-7ad00-738083445af275752aaebc29bfa51430f3d94459.gz
-rw-r--r--inc/lang/en/lang.php16
-rw-r--r--inc/media.php55
-rw-r--r--lib/_fla/.htaccess3
-rw-r--r--lib/_fla/MultipleUpload.as274
-rw-r--r--lib/_fla/README1
-rw-r--r--lib/_fla/multipleUpload.flabin0 -> 1812992 bytes
-rw-r--r--lib/exe/mediamanager.php16
-rw-r--r--lib/exe/multipleUpload.swfbin0 -> 64353 bytes
-rw-r--r--lib/images/multiupload.pngbin0 -> 698 bytes
-rw-r--r--lib/scripts/helpers.js278
-rw-r--r--lib/scripts/media.js21
-rw-r--r--lib/tpl/default/media.css3
12 files changed, 529 insertions, 138 deletions
diff --git a/inc/lang/en/lang.php b/inc/lang/en/lang.php
index 2a330ea46..527eaa7d2 100644
--- a/inc/lang/en/lang.php
+++ b/inc/lang/en/lang.php
@@ -229,4 +229,20 @@ $lang['i_pol2'] = 'Closed Wiki (read, write, upload for registered users o
$lang['i_retry'] = 'Retry';
+$lang['mu_intro'] = 'Here you can upload multiple files at once. Click the browse button to add them to the queue. Press upload when done.';
+$lang['js']['mu_btn'] = 'Upload multiple files at once';
+$lang['mu_gridname'] = 'Filename';
+$lang['mu_gridsize'] = 'Size';
+$lang['mu_gridstat'] = 'Status';
+$lang['mu_namespace'] = 'Namespace';
+$lang['mu_browse'] = 'Browse';
+$lang['mu_toobig'] = 'too big';
+$lang['mu_ready'] = 'ready for upload';
+$lang['mu_done'] = 'complete';
+$lang['mu_fail'] = 'failed';
+$lang['mu_authfail'] = 'session expired';
+$lang['mu_progress'] = '@PCT@% uploaded';
+$lang['mu_filetypes'] = 'Allowed Filetypes';
+
+
//Setup VIM: ex: et ts=2 enc=utf-8 :
diff --git a/inc/media.php b/inc/media.php
index 663a35051..a65b94e26 100644
--- a/inc/media.php
+++ b/inc/media.php
@@ -189,7 +189,7 @@ function media_delete($id,$auth){
* Handles media file uploads
*
* This generates an action event and delegates to _media_upload_action().
- * Action plugins are allowed to pre/postprocess the uploaded file.
+ * Action plugins are allowed to pre/postprocess the uploaded file.
* (The triggered event is preventable.)
*
* Event data:
@@ -239,7 +239,7 @@ function media_upload($ns,$auth){
// because a temp file was created already
if(preg_match('/\.('.$regex.')$/i',$fn)){
//check for overwrite
- if(@file_exists($fn) && (!$_POST['ow'] || $auth < AUTH_DELETE)){
+ if(@file_exists($fn) && (!$_REQUEST['ow'] || $auth < AUTH_DELETE)){
msg($lang['uploadexist'],0);
return false;
}
@@ -568,8 +568,9 @@ function media_uploadform($ns, $auth){
if($auth < AUTH_UPLOAD) return; //fixme print info on missing permissions?
- print '<div class="upload">' . $lang['mediaupload'] . '</div>';
+ // The default HTML upload form
$form = new Doku_Form('dw__upload', DOKU_BASE.'lib/exe/mediamanager.php', false, 'multipart/form-data');
+ $form->addElement('<div class="upload">' . $lang['mediaupload'] . '</div>');
$form->addElement(formSecurityToken());
$form->addHidden('ns', hsc($ns));
$form->addElement(form_makeOpenTag('p'));
@@ -585,8 +586,54 @@ function media_uploadform($ns, $auth){
$form->addElement(form_makeCheckboxField('ow', 1, $lang['txt_overwrt'], 'dw__ow', 'check'));
$form->addElement(form_makeCloseTag('p'));
}
-
html_form('upload', $form);
+
+ // prepare flashvars for multiupload
+ $opt = array(
+ 'L_gridname' => $lang['mu_gridname'] ,
+ 'L_gridsize' => $lang['mu_gridsize'] ,
+ 'L_gridstat' => $lang['mu_gridstat'] ,
+ 'L_namespace' => $lang['mu_namespace'] ,
+ 'L_overwrite' => $lang['txt_overwrt'],
+ 'L_browse' => $lang['mu_browse'],
+ 'L_upload' => $lang['btn_upload'],
+ 'L_toobig' => $lang['mu_toobig'],
+ 'L_ready' => $lang['mu_ready'],
+ 'L_done' => $lang['mu_done'],
+ 'L_fail' => $lang['mu_fail'],
+ 'L_authfail' => $lang['mu_authfail'],
+ 'L_progress' => $lang['mu_progress'],
+ 'L_filetypes' => $lang['mu_filetypes'],
+
+ 'O_ns' => ":$ns",
+ 'O_backend' => 'mediamanager.php?'.session_name().'='.session_id(),
+ 'O_size' => php_to_byte(ini_get('upload_max_filesize')),
+ 'O_extensions'=> join('|',array_keys(getMimeTypes())),
+ 'O_overwrite' => ($auth >= AUTH_DELETE),
+ 'O_sectok' => getSecurityToken(),
+ 'O_authtok' => auth_createToken(),
+ );
+ $var = buildURLparams($opt,'&');
+ // output the flash uploader
+ ?>
+ <div id="dw__flashupload" style="display:none">
+ <div class="upload"><?php echo $lang['mu_intro']?></div>
+ <object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="100%" height="100%"
+ codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab">
+ <param name="movie" value="multipleUpload.swf?t=<?=time()?>" />
+ <param name="quality" value="high" />
+ <param name="bgcolor" value="#ffffff" />
+ <param name="FlashVars" value="<?php echo $var?>" />
+ <embed src="multipleUpload.swf?t=<?=time()?>" quality="high" bgcolor="#ffffff"
+ width="100%" height="100%" name="fileUpload" align="middle"
+ play="true" loop="false" quality="high" FlashVars="<?php echo $var?>"
+ allowScriptAccess="sameDomain"
+ type="application/x-shockwave-flash"
+ pluginspage="http://www.macromedia.com/go/getflashplayer">
+ </embed>
+ </object>
+ </div>
+ <?php
}
/**
diff --git a/lib/_fla/.htaccess b/lib/_fla/.htaccess
new file mode 100644
index 000000000..9a7d38c12
--- /dev/null
+++ b/lib/_fla/.htaccess
@@ -0,0 +1,3 @@
+## no access to the fla directory
+order allow,deny
+deny from all
diff --git a/lib/_fla/MultipleUpload.as b/lib/_fla/MultipleUpload.as
new file mode 100644
index 000000000..8e7e0b008
--- /dev/null
+++ b/lib/_fla/MultipleUpload.as
@@ -0,0 +1,274 @@
+/**
+ * Flash Multi Upload
+ *
+ * Based on a example from Alastair Dawson
+ *
+ * @link http://blog.vixiom.com/2006/09/08/multiple-file-upload-with-flash-and-ruby-on-rails/
+ * @author Alastair Dawson
+ * @author Andreas Gohr <andi@splitbrain.org>
+ */
+
+// delegate
+import mx.utils.Delegate;
+// ui components
+import mx.controls.DataGrid;
+import mx.controls.gridclasses.DataGridColumn
+import mx.controls.Button;
+import mx.controls.TextInput;
+import mx.controls.CheckBox;
+import mx.controls.Label;
+// file reference
+import flash.net.FileReferenceList;
+import flash.net.FileReference;
+
+class MultipleUpload {
+
+ private var fileRef:FileReferenceList;
+ private var fileRefListener:Object;
+ private var list:Array;
+ private var dp:Array;
+
+ private var files_dg:DataGrid;
+ private var browse_btn:Button;
+ private var upload_btn:Button;
+ private var ns_input:TextInput;
+ private var ns_label:Label;
+ private var overwrite_cb:CheckBox;
+
+ /**
+ * Constructor.
+ *
+ * Initializes the needed objects and stage objects
+ */
+ public function MultipleUpload(fdg:DataGrid, bb:Button, ub:Button, nsi:TextInput, nsl:Label, ob:CheckBox) {
+ // references for objects on the stage
+ files_dg = fdg;
+ browse_btn = bb;
+ upload_btn = ub;
+ ns_input = nsi;
+ ns_label = nsl;
+ overwrite_cb = ob;
+
+ // file list references & listener
+ fileRef = new FileReferenceList();
+ fileRefListener = new Object();
+ fileRef.addListener(fileRefListener);
+
+ // setup
+ iniUI();
+ inifileRefListener();
+ }
+
+ /**
+ * Initializes the User Interface
+ *
+ * Uses flashvars to access possibly localized names
+ */
+ private function iniUI() {
+ // register button handlers
+ browse_btn.onRelease = Delegate.create(this, this.browse);
+ upload_btn.onRelease = Delegate.create(this, this.upload);
+
+ // columns for dataGrid
+ var col:DataGridColumn;
+ col = new DataGridColumn('name');
+ col.headerText = ( _root.L_gridname ? _root.L_gridname : 'Filename' );
+ col.sortable = false;
+ files_dg.addColumn(col);
+ col = new DataGridColumn('size');
+ col.headerText = ( _root.L_gridsize ? _root.L_gridsize : 'Size' );
+ col.sortable = false;
+ files_dg.addColumn(col);
+ col = new DataGridColumn('status');
+ col.headerText = ( _root.L_gridstat ? _root.L_gridstat : 'Status' );
+ col.sortable = false;
+ files_dg.addColumn(col);
+
+ // label translations
+ if(_root.L_overwrite) overwrite_cb.label = _root.L_overwrite;
+ if(_root.L_browse) browse_btn.label = _root.L_browse;
+ if(_root.L_upload) upload_btn.label = _root.L_upload;
+ if(_root.L_namespace) ns_label.text = _root.L_namespace;
+
+ // prefill input field
+ if(_root.O_ns) ns_input.text = _root.O_ns;
+
+ // disable buttons
+ upload_btn.enabled = false;
+ if(!_root.O_overwrite) overwrite_cb.visible = false;
+
+ // initalize the data provider list
+ dp = new Array();
+ list = new Array();
+ files_dg.spaceColumnsEqually();
+ }
+
+ /**
+ * Open files selection dialog
+ *
+ * Adds the allowed file types
+ */
+ private function browse() {
+ if(_root.O_extensions){
+ var exts:Array = _root.O_extensions.split('|');
+ var filter:Object = new Object();
+ filter.description = (_root.L_filetypes ? _root.L_filetypes : 'Allowed filetypes');
+ filter.extension = '';
+ for(var i:Number = 0; i<exts.length; i++){
+ filter.extension += '*.'+exts[i]+';';
+ }
+ filter.extension = filter.extension.substr(0,filter.extension.length-1);
+ var apply:Array = new Array();
+ apply.push(filter);
+ fileRef.browse(apply);
+ }else{
+ fileRef.browse();
+ }
+ }
+
+ /**
+ * Upload selected files
+ */
+ private function upload() {
+ // prepare backend URL
+ var url:String;
+ url = _root.O_backend; // from flashvars
+ url += '&ns='+escape(ns_input.text);
+
+ // prepare upload url
+ var upurl:String;
+ upurl = url;
+ upurl += '&sectok='+escape(_root.O_sectok);
+ upurl += '&authtok='+escape(_root.O_authtok);
+ if(overwrite_cb.selected) upurl += '&ow=1';
+
+ // disable buttons
+ upload_btn.enabled = false;
+ browse_btn.enabled = false;
+ ns_input.enabled = false;
+ overwrite_cb.enabled = false;
+
+ // upload the files
+ for(var i:Number = 0; i < list.length; i++) {
+ var file = list[i];
+ file.addListener(fileRefListener);
+ file.upload(upurl);
+ }
+
+ // when done redirect
+ getURL(url,'_self');
+ }
+
+ /**
+ * Set the status of a given file in the data grid
+ */
+ private function setStatus(file,msg){
+ for(var i:Number = 0; i < list.length; i++) {
+ if (list[i].name == file.name) {
+ files_dg.editField(i, 'status', msg);
+ }
+ }
+ }
+
+ /**
+ * Initialize the file reference listener
+ */
+ private function inifileRefListener() {
+ fileRefListener.onSelect = Delegate.create(this, this.onSelect);
+ fileRefListener.onCancel = Delegate.create(this, this.onCancel);
+ fileRefListener.onOpen = Delegate.create(this, this.onOpen);
+ fileRefListener.onProgress = Delegate.create(this, this.onProgress);
+ fileRefListener.onComplete = Delegate.create(this, this.onComplete);
+ fileRefListener.onHTTPError = Delegate.create(this, this.onHTTPError);
+ fileRefListener.onIOError = Delegate.create(this, this.onIOError);
+ fileRefListener.onSecurityError = Delegate.create(this, this.onSecurityError);
+ }
+
+ /**
+ * Handle file selection
+ *
+ * Files are added as in a list of references and beautified into the data grid dataprovider array
+ *
+ * Multiple browses will add to the list
+ */
+ private function onSelect(fileRefList:FileReferenceList) {
+ var sel = fileRefList.fileList;
+ for(var i:Number = 0; i < sel.length; i++) {
+ // check size
+ var stat:String;
+ if(_root.O_maxsize && sel[i].size > _root.O_maxsize){
+ stat = (_root.L_toobig ? _root.L_toobig : 'too big');
+ }else{
+ stat = (_root.L_ready ? _root.L_ready : 'ready for upload');
+ }
+ // add to grid
+ dp.push({name:sel[i].name, size:Math.round(sel[i].size / 1000) + " kb", status:stat});
+ // add to reference list
+ list.push(sel[i]);
+ }
+ // update dataGrid
+ files_dg.dataProvider = dp;
+ files_dg.spaceColumnsEqually();
+
+ if(list.length > 0) upload_btn.enabled = true;
+ }
+
+ /**
+ * Does nothing
+ */
+ private function onCancel() {
+ }
+
+ /**
+ * Does nothing
+ */
+ private function onOpen(file:FileReference) {
+ }
+
+ /**
+ * Set the upload progress
+ */
+ private function onProgress(file:FileReference, bytesLoaded:Number, bytesTotal:Number) {
+ var percentDone = Math.round((bytesLoaded / bytesTotal) * 100);
+ var msg:String = 'uploading @PCT@%';
+ if(_root.L_progress) msg = _root.L_progress;
+ msg = msg.split('@PCT@').join(percentDone);
+ this.setStatus(file,msg);
+ }
+
+ /**
+ * Handle upload completion
+ */
+ private function onComplete(file:FileReference) {
+ this.setStatus(file,(_root.L_done ? _root.L_done : 'complete'));
+ }
+
+ /**
+ * Handle upload errors
+ */
+ private function onHTTPError(file:FileReference, httpError:Number) {
+ if(httpError == 400){
+ this.setStatus(file,(_root.L_fail ? _root.L_fail : 'failed'));
+ }else if(httpError == 401){
+ this.setStatus(file,(_root.L_authfail ? _root.L_authfail : 'auth failed'));
+ }else{
+ this.setStatus(file,"HTTP Error " + httpError);
+ }
+ }
+
+ /**
+ * Handle IO errors
+ */
+ private function onIOError(file:FileReference) {
+ this.setStatus(file,"IO Error");
+ }
+
+ /**
+ * Handle Security errors
+ */
+ private function onSecurityError(file:FileReference, errorString:String) {
+ this.setStatus(file,"SecurityError: " + errorString);
+ }
+
+
+}
diff --git a/lib/_fla/README b/lib/_fla/README
new file mode 100644
index 000000000..beaa15d02
--- /dev/null
+++ b/lib/_fla/README
@@ -0,0 +1 @@
+Flash source files. Those will not be included in the tarball releases.
diff --git a/lib/_fla/multipleUpload.fla b/lib/_fla/multipleUpload.fla
new file mode 100644
index 000000000..a04237c1c
--- /dev/null
+++ b/lib/_fla/multipleUpload.fla
Binary files differ
diff --git a/lib/exe/mediamanager.php b/lib/exe/mediamanager.php
index 32849be62..c3754d309 100644
--- a/lib/exe/mediamanager.php
+++ b/lib/exe/mediamanager.php
@@ -1,6 +1,8 @@
<?php
if(!defined('DOKU_INC')) define('DOKU_INC',dirname(__FILE__).'/../../');
define('DOKU_MEDIAMANAGER',1);
+
+
require_once(DOKU_INC.'inc/init.php');
require_once(DOKU_INC.'inc/lang/en/lang.php');
require_once(DOKU_INC.'inc/lang/'.$conf['lang'].'/lang.php');
@@ -13,6 +15,7 @@
// handle passed message
if($_REQUEST['msg1']) msg(hsc($_REQUEST['msg1']),1);
+ if($_REQUEST['err']) msg(hsc($_REQUEST['err']),-1);
// get namespace to display (either direct or from deletion order)
@@ -37,6 +40,19 @@
// create the given namespace (just for beautification)
if($AUTH >= AUTH_UPLOAD) { io_createNamespace("$NS:xxx", 'media'); }
+ // handle flash upload
+ if($_FILES['Filedata']['tmp_name']){
+ $_FILES['upload'] =& $_FILES['Filedata'];
+ $JUMPTO = media_upload($NS,$AUTH);
+ if($JUMPTO == false){
+ header("HTTP/1.0 400 Bad Request");
+ echo 'Upload failed';
+ }
+ echo 'ok';
+ exit;
+ }
+
+
// handle upload
if($_FILES['upload']['tmp_name']){
$JUMPTO = media_upload($NS,$AUTH);
diff --git a/lib/exe/multipleUpload.swf b/lib/exe/multipleUpload.swf
new file mode 100644
index 000000000..6921cbb45
--- /dev/null
+++ b/lib/exe/multipleUpload.swf
Binary files differ
diff --git a/lib/images/multiupload.png b/lib/images/multiupload.png
new file mode 100644
index 000000000..1e8efa063
--- /dev/null
+++ b/lib/images/multiupload.png
Binary files differ
diff --git a/lib/scripts/helpers.js b/lib/scripts/helpers.js
index 7b500c226..8d4f3ea78 100644
--- a/lib/scripts/helpers.js
+++ b/lib/scripts/helpers.js
@@ -1,132 +1,146 @@
-/**
- * $Id: helpers.js 156 2006-12-23 08:48:25Z wingedfox $
- * $HeadURL: https://svn.debugger.ru/repos/jslibs/BrowserExtensions/trunk/helpers.js $
- *
- * File contains differrent helper functions
- *
- * @author Ilya Lebedev <ilya@lebedev.net>
- * @license LGPL
- * @version $Rev: 156 $
- */
-//-----------------------------------------------------------------------------
-// Variable/property checks
-//-----------------------------------------------------------------------------
-/**
- * Checks if property is undefined
- *
- * @param {Object} prop value to check
- * @return {Boolean} true if matched
- * @scope public
- */
-function isUndefined (prop /* :Object */) /* :Boolean */ {
- return (typeof prop == 'undefined');
-}
-/**
- * Checks if property is function
- *
- * @param {Object} prop value to check
- * @return {Boolean} true if matched
- * @scope public
- */
-function isFunction (prop /* :Object */) /* :Boolean */ {
- return (typeof prop == 'function');
-}
-/**
- * Checks if property is string
- *
- * @param {Object} prop value to check
- * @return {Boolean} true if matched
- * @scope public
- */
-function isString (prop /* :Object */) /* :Boolean */ {
- return (typeof prop == 'string');
-}
-/**
- * Checks if property is number
- *
- * @param {Object} prop value to check
- * @return {Boolean} true if matched
- * @scope public
- */
-function isNumber (prop /* :Object */) /* :Boolean */ {
- return (typeof prop == 'number');
-}
-/**
- * Checks if property is the calculable number
- *
- * @param {Object} prop value to check
- * @return {Boolean} true if matched
- * @scope public
- */
-function isNumeric (prop /* :Object */) /* :Boolean */ {
- return isNumber(prop)&&!isNaN(prop)&&isFinite(prop);
-}
-/**
- * Checks if property is array
- *
- * @param {Object} prop value to check
- * @return {Boolean} true if matched
- * @scope public
- */
-function isArray (prop /* :Object */) /* :Boolean */ {
- return (prop instanceof Array);
-}
-/**
- * Checks if property is regexp
- *
- * @param {Object} prop value to check
- * @return {Boolean} true if matched
- * @scope public
- */
-function isRegExp (prop /* :Object */) /* :Boolean */ {
- return (prop instanceof RegExp);
-}
-/**
- * Checks if property is a boolean value
- *
- * @param {Object} prop value to check
- * @return {Boolean} true if matched
- * @scope public
- */
-function isBoolean (prop /* :Object */) /* :Boolean */ {
- return ('boolean' == typeof prop);
-}
-/**
- * Checks if property is a scalar value (value that could be used as the hash key)
- *
- * @param {Object} prop value to check
- * @return {Boolean} true if matched
- * @scope public
- */
-function isScalar (prop /* :Object */) /* :Boolean */ {
- return isNumeric(prop)||isString(prop);
-}
-/**
- * Checks if property is empty
- *
- * @param {Object} prop value to check
- * @return {Boolean} true if matched
- * @scope public
- */
-function isEmpty (prop /* :Object */) /* :Boolean */ {
- if (isBoolean(prop)) return false;
- if (isRegExp(prop) && new RegExp("").toString() == prop.toString()) return true;
- if (isString(prop) || isNumber(prop)) return !prop;
- if (Boolean(prop)&&false != prop) {
- for (var i in prop) if(prop.hasOwnProperty(i)) return false
- }
- return true;
-}
-
-/*
-* Checks if property is derived from prototype, applies method if it is not exists
-*
-* @param string property name
-* @return bool true if prototyped
-* @access public
-*/
-if ('undefined' == typeof Object.hasOwnProperty) {
- Object.prototype.hasOwnProperty = function (prop) {
- return !('undefined' == typeof this[prop] || this.constructor && this.constructor.prototype[prop] && this[prop] === this.constructor.prototype[prop]);
- }
-}
+/**
+ * Differrent helper functions
+ *
+ * @author Ilya Lebedev <ilya@lebedev.net>
+ * @license LGPL
+ */
+//-----------------------------------------------------------------------------
+// Variable/property checks
+//-----------------------------------------------------------------------------
+/**
+ * Checks if property is undefined
+ *
+ * @param {Object} prop value to check
+ * @return {Boolean} true if matched
+ * @scope public
+ */
+function isUndefined (prop /* :Object */) /* :Boolean */ {
+ return (typeof prop == 'undefined');
+}
+/**
+ * Checks if property is function
+ *
+ * @param {Object} prop value to check
+ * @return {Boolean} true if matched
+ * @scope public
+ */
+function isFunction (prop /* :Object */) /* :Boolean */ {
+ return (typeof prop == 'function');
+}
+/**
+ * Checks if property is string
+ *
+ * @param {Object} prop value to check
+ * @return {Boolean} true if matched
+ * @scope public
+ */
+function isString (prop /* :Object */) /* :Boolean */ {
+ return (typeof prop == 'string');
+}
+/**
+ * Checks if property is number
+ *
+ * @param {Object} prop value to check
+ * @return {Boolean} true if matched
+ * @scope public
+ */
+function isNumber (prop /* :Object */) /* :Boolean */ {
+ return (typeof prop == 'number');
+}
+/**
+ * Checks if property is the calculable number
+ *
+ * @param {Object} prop value to check
+ * @return {Boolean} true if matched
+ * @scope public
+ */
+function isNumeric (prop /* :Object */) /* :Boolean */ {
+ return isNumber(prop)&&!isNaN(prop)&&isFinite(prop);
+}
+/**
+ * Checks if property is array
+ *
+ * @param {Object} prop value to check
+ * @return {Boolean} true if matched
+ * @scope public
+ */
+function isArray (prop /* :Object */) /* :Boolean */ {
+ return (prop instanceof Array);
+}
+/**
+ * Checks if property is regexp
+ *
+ * @param {Object} prop value to check
+ * @return {Boolean} true if matched
+ * @scope public
+ */
+function isRegExp (prop /* :Object */) /* :Boolean */ {
+ return (prop instanceof RegExp);
+}
+/**
+ * Checks if property is a boolean value
+ *
+ * @param {Object} prop value to check
+ * @return {Boolean} true if matched
+ * @scope public
+ */
+function isBoolean (prop /* :Object */) /* :Boolean */ {
+ return ('boolean' == typeof prop);
+}
+/**
+ * Checks if property is a scalar value (value that could be used as the hash key)
+ *
+ * @param {Object} prop value to check
+ * @return {Boolean} true if matched
+ * @scope public
+ */
+function isScalar (prop /* :Object */) /* :Boolean */ {
+ return isNumeric(prop)||isString(prop);
+}
+/**
+ * Checks if property is empty
+ *
+ * @param {Object} prop value to check
+ * @return {Boolean} true if matched
+ * @scope public
+ */
+function isEmpty (prop /* :Object */) /* :Boolean */ {
+ if (isBoolean(prop)) return false;
+ if (isRegExp(prop) && new RegExp("").toString() == prop.toString()) return true;
+ if (isString(prop) || isNumber(prop)) return !prop;
+ if (Boolean(prop)&&false != prop) {
+ for (var i in prop) if(prop.hasOwnProperty(i)) return false
+ }
+ return true;
+}
+
+/**
+ * Checks if property is derived from prototype, applies method if it is not exists
+ *
+ * @param string property name
+ * @return bool true if prototyped
+ * @access public
+ */
+if ('undefined' == typeof Object.hasOwnProperty) {
+ Object.prototype.hasOwnProperty = function (prop) {
+ return !('undefined' == typeof this[prop] || this.constructor && this.constructor.prototype[prop] && this[prop] === this.constructor.prototype[prop]);
+ }
+}
+
+/**
+ * Very simplistic Flash plugin check, probably works for Flash 8 and higher only
+ */
+function hasFlash(version){
+ var ver = 0;
+ try{
+ if(navigator.plugins != null && navigator.plugins.length > 0){
+ ver = navigator.plugins["Shockwave Flash"].description.split(' ')[2].split('.')[0];
+ }else{
+ var axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash");
+ ver = axo.GetVariable("$version").split(' ')[1].split(',')[0];
+ }
+ }catch(e){ }
+
+ if(ver >= version) return true;
+ return false;
+}
diff --git a/lib/scripts/media.js b/lib/scripts/media.js
index f616f8741..ebfd01322 100644
--- a/lib/scripts/media.js
+++ b/lib/scripts/media.js
@@ -265,12 +265,31 @@ media = {
text = text.substr(text.lastIndexOf('/')+1);
text = text.substr(text.lastIndexOf('\\')+1);
name.value = text;
- }
+ },
+
+ initFlashUpload: function(){
+ if(!hasFlash(8)) return;
+ var oform = $('dw__upload');
+ var oflash = $('dw__flashupload');
+ if(!oform || !oflash) return;
+
+ var clicky = document.createElement('img');
+ clicky.src = DOKU_BASE+'lib/images/multiupload.png';
+ clicky.title = LANG['mu_btn'];
+ clicky.alt = LANG['mu_btn'];
+ clicky.style.cursor = 'pointer';
+ clicky.onclick = function(){
+ oform.style.display = 'none';
+ oflash.style.display = '';
+ };
+ oform.appendChild(clicky);
+ }
};
addInitEvent(function(){
media.treeattach($('media__tree'));
media.selectorattach($('media__content'));
media.attachoptions($('media__opts'));
+ media.initFlashUpload();
});
diff --git a/lib/tpl/default/media.css b/lib/tpl/default/media.css
index 143595ba1..81a439289 100644
--- a/lib/tpl/default/media.css
+++ b/lib/tpl/default/media.css
@@ -137,7 +137,8 @@ it's dirty, so any "real" fixes are welcome */
padding: 0 0.5em 0.5em 0.5em;
}
-#media__content form#dw__upload {
+#media__content form#dw__upload,
+#media__content div#dw__flashupload {
display: block;
border-bottom: solid 1px __border__;
padding: 0 0.5em 1em 0.5em;