summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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;