diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/exe/ajax.php | 92 | ||||
-rw-r--r-- | lib/exe/css.php | 4 | ||||
-rw-r--r-- | lib/exe/fetch.php | 10 | ||||
-rw-r--r-- | lib/exe/js.php | 2 | ||||
-rw-r--r-- | lib/exe/mediamanager.php | 24 | ||||
-rw-r--r-- | lib/images/icon-file.png | bin | 0 -> 3363 bytes | |||
-rw-r--r-- | lib/images/icon-list.png | bin | 0 -> 3342 bytes | |||
-rw-r--r-- | lib/images/icon-sort.png | bin | 0 -> 316 bytes | |||
-rw-r--r-- | lib/images/icon-thumb.png | bin | 0 -> 969 bytes | |||
-rw-r--r-- | lib/plugins/acl/script.js | 12 | ||||
-rw-r--r-- | lib/plugins/popularity/lang/uk/submitted.txt | 2 | ||||
-rw-r--r-- | lib/scripts/behaviour.js | 4 | ||||
-rw-r--r-- | lib/scripts/cookie.js | 4 | ||||
-rw-r--r-- | lib/scripts/fileuploader.js | 1247 | ||||
-rw-r--r-- | lib/scripts/fileuploaderextended.js | 339 | ||||
-rw-r--r-- | lib/scripts/media.js | 449 | ||||
-rw-r--r-- | lib/tpl/default/_fileuploader.css | 106 | ||||
-rw-r--r-- | lib/tpl/default/design.css | 6 | ||||
-rw-r--r-- | lib/tpl/default/detail.php | 48 | ||||
-rw-r--r-- | lib/tpl/default/main.php | 1 | ||||
-rw-r--r-- | lib/tpl/default/media.css | 5 | ||||
-rw-r--r-- | lib/tpl/default/mediamanager.css | 419 | ||||
-rw-r--r-- | lib/tpl/default/style.ini | 2 |
23 files changed, 2724 insertions, 52 deletions
diff --git a/lib/exe/ajax.php b/lib/exe/ajax.php index 2bfa3680c..a6e45c4de 100644 --- a/lib/exe/ajax.php +++ b/lib/exe/ajax.php @@ -208,7 +208,97 @@ function ajax_medialist(){ global $NS; $NS = $_POST['ns']; - tpl_mediaContent(true); + if ($_POST['do'] == 'media') { + tpl_mediaFileList(); + } else { + tpl_mediaContent(true); + } +} + +/** + * Return the content of the right column + * (image details) for the Mediamanager + * + * @author Kate Arzamastseva <pshns@ukr.net> + */ +function ajax_mediadetails(){ + global $DEL, $NS, $IMG, $AUTH, $JUMPTO, $REV, $lang, $fullscreen, $conf; + $fullscreen = true; + require_once(DOKU_INC.'lib/exe/mediamanager.php'); + + if ($_REQUEST['image']) $image = cleanID($_REQUEST['image']); + if (isset($IMG)) $image = $IMG; + if (isset($JUMPTO)) $image = $JUMPTO; + if (isset($REV) && !$JUMPTO) $rev = $REV; + + html_msgarea(); + tpl_mediaFileDetails($image, $rev); +} + +/** + * Returns image diff representation for mediamanager + * @author Kate Arzamastseva <pshns@ukr.net> + */ +function ajax_mediadiff(){ + global $NS; + + if ($_REQUEST['image']) $image = cleanID($_REQUEST['image']); + $NS = $_POST['ns']; + $auth = auth_quickaclcheck("$ns:*"); + media_diff($image, $NS, $auth, true); +} + +function ajax_mediaupload(){ + global $NS, $MSG; + + $NS = $_REQUEST['ns']; + $AUTH = auth_quickaclcheck("$NS:*"); + if($AUTH >= AUTH_UPLOAD) { io_createNamespace("$NS:xxx", 'media'); } + + if ($_FILES['qqfile']['error']) unset($_FILES['qqfile']); + + if ($_FILES['qqfile']['tmp_name']) { + $res = media_upload($NS, $AUTH, $_FILES['qqfile']); + $id = ((empty($_POST['mediaid'])) ? $_FILES['qqfile']['name'] : $_POST['mediaid']); + } + if (isset($_GET['qqfile'])) { + $res = media_upload_xhr($NS, $AUTH); + $id = $_GET['qqfile']; + } + $id = cleanID($id, false, true); + + if ($res) $result = array('success' => true, + 'link' => media_managerURL(array('ns' => $NS, 'image' => $NS.':'.$id), '&'), + 'id' => $NS.':'.$id, 'ns' => $NS); + + if (!$result) { + $error = ''; + if (isset($MSG)) { + foreach($MSG as $msg) $error .= $msg['msg']; + } + $result = array('error' => $msg['msg'], 'ns' => $NS); + } + echo htmlspecialchars(json_encode($result), ENT_NOQUOTES); +} + +function dir_delete($path) { + if (!is_string($path) || $path == "") return false; + + if (is_dir($path) && !is_link($path)) { + if (!$dh = @opendir($path)) return false; + + while ($f = readdir($dh)) { + if ($f == '..' || $f == '.') continue; + dir_delete("$path/$f"); + } + + closedir($dh); + return @rmdir($path); + } else { + return @unlink($path); + } + + return false; } /** diff --git a/lib/exe/css.php b/lib/exe/css.php index 8f86f2433..81f47d8fa 100644 --- a/lib/exe/css.php +++ b/lib/exe/css.php @@ -189,7 +189,7 @@ function css_interwiki(){ function css_filetypes(){ // default style - echo 'a.mediafile {'; + echo '.mediafile {'; echo ' background: transparent url('.DOKU_BASE.'lib/images/fileicons/file.png) 0px 1px no-repeat;'; echo ' padding-left: 18px;'; echo ' padding-bottom: 1px;'; @@ -212,7 +212,7 @@ function css_filetypes(){ } foreach($exts as $ext=>$type){ $class = preg_replace('/[^_\-a-z0-9]+/','_',$ext); - echo "a.mf_$class {"; + echo ".mf_$class {"; echo ' background-image: url('.DOKU_BASE.'lib/images/fileicons/'.$ext.$type.')'; echo '}'; } diff --git a/lib/exe/fetch.php b/lib/exe/fetch.php index 3ad4f1937..143d40f22 100644 --- a/lib/exe/fetch.php +++ b/lib/exe/fetch.php @@ -20,6 +20,10 @@ $CACHE = calc_cache($_REQUEST['cache']); $WIDTH = (int) $_REQUEST['w']; $HEIGHT = (int) $_REQUEST['h']; + $REV = (int) @$_REQUEST['rev']; + //sanitize revision + $REV = preg_replace('/[^0-9]/','',$REV); + list($EXT,$MIME,$DL) = mimetype($MEDIA,false); if($EXT === false){ $EXT = 'unknown'; @@ -28,7 +32,7 @@ } // check for permissions, preconditions and cache external files - list($STATUS, $STATUSMESSAGE) = checkFileStatus($MEDIA, $FILE); + list($STATUS, $STATUSMESSAGE) = checkFileStatus($MEDIA, $FILE, $REV); // prepare data for plugin events $data = array('media' => $MEDIA, @@ -147,7 +151,7 @@ function sendFile($file,$mime,$dl,$cache){ * @param $file reference to the file variable * @returns array(STATUS, STATUSMESSAGE) */ -function checkFileStatus(&$media, &$file) { +function checkFileStatus(&$media, &$file, $rev='') { global $MIME, $EXT, $CACHE; //media to local file @@ -172,7 +176,7 @@ function checkFileStatus(&$media, &$file) { if(auth_quickaclcheck(getNS($media).':X') < AUTH_READ){ return array( 403, 'Forbidden' ); } - $file = mediaFN($media); + $file = mediaFN($media, $rev); } //check file existance diff --git a/lib/exe/js.php b/lib/exe/js.php index e96d45ee6..33f8c695d 100644 --- a/lib/exe/js.php +++ b/lib/exe/js.php @@ -43,6 +43,8 @@ function js_out(){ DOKU_INC."lib/scripts/jquery/jquery$min.js", DOKU_INC.'lib/scripts/jquery/jquery.cookie.js', DOKU_INC."lib/scripts/jquery/jquery-ui$min.js", + DOKU_INC."lib/scripts/fileuploader.js", + DOKU_INC."lib/scripts/fileuploaderextended.js", DOKU_INC.'lib/scripts/helpers.js', DOKU_INC.'lib/scripts/delay.js', DOKU_INC.'lib/scripts/cookie.js', diff --git a/lib/exe/mediamanager.php b/lib/exe/mediamanager.php index 02fde5a8d..5f09fe1f8 100644 --- a/lib/exe/mediamanager.php +++ b/lib/exe/mediamanager.php @@ -35,7 +35,7 @@ $AUTH = auth_quickaclcheck("$NS:*"); // do not display the manager if user does not have read access - if($AUTH < AUTH_READ) { + if($AUTH < AUTH_READ && !$fullscreen) { header('HTTP/1.0 403 Forbidden'); die($lang['accessdenied']); } @@ -76,10 +76,20 @@ } // handle meta saving - if($IMG && $_REQUEST['do']['save']){ + if($IMG && @array_key_exists('save', $_REQUEST['do'])){ $JUMPTO = media_metasave($IMG,$AUTH,$_REQUEST['meta']); } + if($IMG && ($_REQUEST['mediado'] == 'save' || @array_key_exists('save', $_REQUEST['mediado']))) { + $JUMPTO = media_metasave($IMG,$AUTH,$_REQUEST['meta']); + } + + if ($_REQUEST['rev'] && $conf['mediarevisions']) $REV = (int) $_REQUEST['rev']; + + if($_REQUEST['mediado'] == 'restore' && $conf['mediarevisions']){ + $JUMPTO = media_restore($_REQUEST['image'], $REV, $AUTH); + } + // handle deletion if($DEL) { $res = 0; @@ -88,7 +98,7 @@ } if ($res & DOKU_MEDIA_DELETED) { $msg = sprintf($lang['deletesucc'], noNS($DEL)); - if ($res & DOKU_MEDIA_EMPTY_NS) { + if ($res & DOKU_MEDIA_EMPTY_NS && !$fullscreen) { // current namespace was removed. redirecting to root ns passing msg along send_redirect(DOKU_URL.'lib/exe/mediamanager.php?msg1='. rawurlencode($msg).'&edid='.$_REQUEST['edid']); @@ -102,9 +112,11 @@ msg(sprintf($lang['deletefail'],noNS($DEL)),-1); } } - // finished - start output - header('Content-Type: text/html; charset=utf-8'); - include(template('mediamanager.php')); + + if (!$fullscreen) { + header('Content-Type: text/html; charset=utf-8'); + include(template('mediamanager.php')); + } /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ diff --git a/lib/images/icon-file.png b/lib/images/icon-file.png Binary files differnew file mode 100644 index 000000000..d350c8c31 --- /dev/null +++ b/lib/images/icon-file.png diff --git a/lib/images/icon-list.png b/lib/images/icon-list.png Binary files differnew file mode 100644 index 000000000..ecfeed92d --- /dev/null +++ b/lib/images/icon-list.png diff --git a/lib/images/icon-sort.png b/lib/images/icon-sort.png Binary files differnew file mode 100644 index 000000000..c6403dd3c --- /dev/null +++ b/lib/images/icon-sort.png diff --git a/lib/images/icon-thumb.png b/lib/images/icon-thumb.png Binary files differnew file mode 100644 index 000000000..ccc7a101d --- /dev/null +++ b/lib/images/icon-thumb.png diff --git a/lib/plugins/acl/script.js b/lib/plugins/acl/script.js index 4fc97aeb5..2598bcef8 100644 --- a/lib/plugins/acl/script.js +++ b/lib/plugins/acl/script.js @@ -33,7 +33,7 @@ var dw_acl = { show_sublist, 'html' ); - }, + }, toggle_display: function ($clicky, opening) { $clicky.attr('src', @@ -93,13 +93,13 @@ var dw_acl = { $link = jQuery(this); - // remove highlighting - jQuery('#acl__tree a.cur').removeClass('cur'); + // remove highlighting + jQuery('#acl__tree a.cur').removeClass('cur'); - // add new highlighting + // add new highlighting $link.addClass('cur'); - // set new page to detail form + // set new page to detail form $frm = jQuery('#acl__detail form'); if ($link.hasClass('wikilink1')) { $frm.find('input[name=ns]').val(''); @@ -107,7 +107,7 @@ var dw_acl = { } else if ($link.hasClass('idx_dir')) { $frm.find('input[name=ns]').val(dw_acl.parseatt($link[0].search).ns); $frm.find('input[name=id]').val(''); - } + } dw_acl.loadinfo(); return false; diff --git a/lib/plugins/popularity/lang/uk/submitted.txt b/lib/plugins/popularity/lang/uk/submitted.txt new file mode 100644 index 000000000..90213858d --- /dev/null +++ b/lib/plugins/popularity/lang/uk/submitted.txt @@ -0,0 +1,2 @@ +====== Відгук популярності ====== +Дані були успішно відправлені.
\ No newline at end of file diff --git a/lib/scripts/behaviour.js b/lib/scripts/behaviour.js index afb210840..cfdc89157 100644 --- a/lib/scripts/behaviour.js +++ b/lib/scripts/behaviour.js @@ -63,7 +63,9 @@ var dw_behaviour = { quickSelect: function(){ jQuery('select.quickselect') .change(function(e){ e.target.form.submit(); }) - .parents('form').find('input[type=submit]').hide(); + .parents('form').find('input[type=submit]').each(function(){ + if (!jQuery(this).hasClass('show')) jQuery(this).hide(); + }); }, /** diff --git a/lib/scripts/cookie.js b/lib/scripts/cookie.js index f97882855..3f3375fa7 100644 --- a/lib/scripts/cookie.js +++ b/lib/scripts/cookie.js @@ -27,7 +27,7 @@ DokuCookie = { jQuery.each(this.data, function (val, key) { if (this.data.hasOwnProperty(key)) { text += '#'+encodeURIComponent(key)+'#'+encodeURIComponent(val); - } + } }); jQuery.cookie(this.name,text.substr(1), {expires: 365, path: DOKU_BASE}); }, @@ -59,5 +59,5 @@ DokuCookie = { this.data[decodeURIComponent(parts[i])] = decodeURIComponent(parts[i+1]); } } - } + } }; diff --git a/lib/scripts/fileuploader.js b/lib/scripts/fileuploader.js new file mode 100644 index 000000000..e75b8d3a5 --- /dev/null +++ b/lib/scripts/fileuploader.js @@ -0,0 +1,1247 @@ +/** + * http://github.com/valums/file-uploader + * + * Multiple file upload component with progress-bar, drag-and-drop. + * © 2010 Andrew Valums ( andrew(at)valums.com ) + * + * Licensed under GNU GPL 2 or later and GNU LGPL 2 or later, see license.txt. + */ + +// +// Helper functions +// + +var qq = qq || {}; + +/** + * Adds all missing properties from second obj to first obj + */ +qq.extend = function(first, second){ + for (var prop in second){ + first[prop] = second[prop]; + } +}; + +/** + * Searches for a given element in the array, returns -1 if it is not present. + * @param {Number} [from] The index at which to begin the search + */ +qq.indexOf = function(arr, elt, from){ + if (arr.indexOf) return arr.indexOf(elt, from); + + from = from || 0; + var len = arr.length; + + if (from < 0) from += len; + + for (; from < len; from++){ + if (from in arr && arr[from] === elt){ + return from; + } + } + return -1; +}; + +qq.getUniqueId = (function(){ + var id = 0; + return function(){ return id++; }; +})(); + +// +// Events + +qq.attach = function(element, type, fn){ + if (element.addEventListener){ + element.addEventListener(type, fn, false); + } else if (element.attachEvent){ + element.attachEvent('on' + type, fn); + } +}; +qq.detach = function(element, type, fn){ + if (element.removeEventListener){ + element.removeEventListener(type, fn, false); + } else if (element.attachEvent){ + element.detachEvent('on' + type, fn); + } +}; + +qq.preventDefault = function(e){ + if (e.preventDefault){ + e.preventDefault(); + } else{ + e.returnValue = false; + } +}; + +// +// Node manipulations + +/** + * Insert node a before node b. + */ +qq.insertBefore = function(a, b){ + b.parentNode.insertBefore(a, b); +}; +qq.remove = function(element){ + element.parentNode.removeChild(element); +}; + +qq.contains = function(parent, descendant){ + // compareposition returns false in this case + if (parent == descendant) return true; + + if (parent.contains){ + return parent.contains(descendant); + } else { + return !!(descendant.compareDocumentPosition(parent) & 8); + } +}; + +/** + * Creates and returns element from html string + * Uses innerHTML to create an element + */ +qq.toElement = (function(){ + var div = document.createElement('div'); + return function(html){ + div.innerHTML = html; + var element = div.firstChild; + div.removeChild(element); + return element; + }; +})(); + +// +// Node properties and attributes + +/** + * Sets styles for an element. + * Fixes opacity in IE6-8. + */ +qq.css = function(element, styles){ + if (styles.opacity != null){ + if (typeof element.style.opacity != 'string' && typeof(element.filters) != 'undefined'){ + styles.filter = 'alpha(opacity=' + Math.round(100 * styles.opacity) + ')'; + } + } + qq.extend(element.style, styles); +}; +qq.hasClass = function(element, name){ + var re = new RegExp('(^| )' + name + '( |$)'); + return re.test(element.className); +}; +qq.addClass = function(element, name){ + if (!qq.hasClass(element, name)){ + element.className += ' ' + name; + } +}; +qq.removeClass = function(element, name){ + var re = new RegExp('(^| )' + name + '( |$)'); + element.className = element.className.replace(re, ' ').replace(/^\s+|\s+$/g, ""); +}; +qq.setText = function(element, text){ + element.innerText = text; + element.textContent = text; +}; + +// +// Selecting elements + +qq.children = function(element){ + var children = [], + child = element.firstChild; + + while (child){ + if (child.nodeType == 1){ + children.push(child); + } + child = child.nextSibling; + } + + return children; +}; + +qq.getByClass = function(element, className){ + if (element.querySelectorAll){ + return element.querySelectorAll('.' + className); + } + + var result = []; + var candidates = element.getElementsByTagName("*"); + var len = candidates.length; + + for (var i = 0; i < len; i++){ + if (qq.hasClass(candidates[i], className)){ + result.push(candidates[i]); + } + } + return result; +}; + +/** + * obj2url() takes a json-object as argument and generates + * a querystring. pretty much like jQuery.param() + * + * how to use: + * + * `qq.obj2url({a:'b',c:'d'},'http://any.url/upload?otherParam=value');` + * + * will result in: + * + * `http://any.url/upload?otherParam=value&a=b&c=d` + * + * @param Object JSON-Object + * @param String current querystring-part + * @return String encoded querystring + */ +qq.obj2url = function(obj, temp, prefixDone){ + var uristrings = [], + prefix = '&', + add = function(nextObj, i){ + var nextTemp = temp + ? (/\[\]$/.test(temp)) // prevent double-encoding + ? temp + : temp+'['+i+']' + : i; + if ((nextTemp != 'undefined') && (i != 'undefined')) { + uristrings.push( + (typeof nextObj === 'object') + ? qq.obj2url(nextObj, nextTemp, true) + : (Object.prototype.toString.call(nextObj) === '[object Function]') + ? encodeURIComponent(nextTemp) + '=' + encodeURIComponent(nextObj()) + : encodeURIComponent(nextTemp) + '=' + encodeURIComponent(nextObj) + ); + } + }; + + if (!prefixDone && temp) { + prefix = (/\?/.test(temp)) ? (/\?$/.test(temp)) ? '' : '&' : '?'; + uristrings.push(temp); + uristrings.push(qq.obj2url(obj)); + } else if ((Object.prototype.toString.call(obj) === '[object Array]') && (typeof obj != 'undefined') ) { + // we wont use a for-in-loop on an array (performance) + for (var i = 0, len = obj.length; i < len; ++i){ + add(obj[i], i); + } + } else if ((typeof obj != 'undefined') && (obj !== null) && (typeof obj === "object")){ + // for anything else but a scalar, we will use for-in-loop + for (var i in obj){ + add(obj[i], i); + } + } else { + uristrings.push(encodeURIComponent(temp) + '=' + encodeURIComponent(obj)); + } + + return uristrings.join(prefix) + .replace(/^&/, '') + .replace(/%20/g, '+'); +}; + +// +// +// Uploader Classes +// +// + +var qq = qq || {}; + +/** + * Creates upload button, validates upload, but doesn't create file list or dd. + */ +qq.FileUploaderBasic = function(o){ + this._options = { + // set to true to see the server response + debug: false, + action: '/server/upload', + params: {}, + button: null, + multiple: true, + maxConnections: 3, + // validation + allowedExtensions: [], + sizeLimit: 0, + minSizeLimit: 0, + // events + // return false to cancel submit + onSubmit: function(id, fileName){}, + onProgress: function(id, fileName, loaded, total){}, + onComplete: function(id, fileName, responseJSON){}, + onCancel: function(id, fileName){}, + // messages + messages: { + typeError: "{file} has invalid extension. Only {extensions} are allowed.", + sizeError: "{file} is too large, maximum file size is {sizeLimit}.", + minSizeError: "{file} is too small, minimum file size is {minSizeLimit}.", + emptyError: "{file} is empty, please select files again without it.", + onLeave: "The files are being uploaded, if you leave now the upload will be cancelled." + }, + showMessage: function(message){ + alert(message); + } + }; + qq.extend(this._options, o); + + // number of files being uploaded + this._filesInProgress = 0; + this._handler = this._createUploadHandler(); + + if (this._options.button){ + this._button = this._createUploadButton(this._options.button); + } + + this._preventLeaveInProgress(); +}; + +qq.FileUploaderBasic.prototype = { + setParams: function(params){ + this._options.params = params; + }, + getInProgress: function(){ + return this._filesInProgress; + }, + _createUploadButton: function(element){ + var self = this; + + return new qq.UploadButton({ + element: element, + multiple: this._options.multiple && qq.UploadHandlerXhr.isSupported(), + onChange: function(input){ + self._onInputChange(input); + } + }); + }, + _createUploadHandler: function(){ + var self = this, + handlerClass; + + if(qq.UploadHandlerXhr.isSupported()){ + handlerClass = 'UploadHandlerXhr'; + } else { + handlerClass = 'UploadHandlerForm'; + } + + var handler = new qq[handlerClass]({ + debug: this._options.debug, + action: this._options.action, + maxConnections: this._options.maxConnections, + onProgress: function(id, fileName, loaded, total){ + self._onProgress(id, fileName, loaded, total); + self._options.onProgress(id, fileName, loaded, total); + }, + onComplete: function(id, fileName, result){ + self._onComplete(id, fileName, result); + self._options.onComplete(id, fileName, result); + }, + onCancel: function(id, fileName){ + self._onCancel(id, fileName); + self._options.onCancel(id, fileName); + } + }); + + return handler; + }, + _preventLeaveInProgress: function(){ + var self = this; + + qq.attach(window, 'beforeunload', function(e){ + if (!self._filesInProgress){return;} + + var e = e || window.event; + // for ie, ff + e.returnValue = self._options.messages.onLeave; + // for webkit + return self._options.messages.onLeave; + }); + }, + _onSubmit: function(id, fileName){ + this._filesInProgress++; + }, + _onProgress: function(id, fileName, loaded, total){ + }, + _onComplete: function(id, fileName, result){ + this._filesInProgress--; + if (result.error){ + this._options.showMessage(result.error); + } + }, + _onCancel: function(id, fileName){ + this._filesInProgress--; + }, + _onInputChange: function(input){ + if (this._handler instanceof qq.UploadHandlerXhr){ + this._uploadFileList(input.files); + } else { + if (this._validateFile(input)){ + this._uploadFile(input); + } + } + this._button.reset(); + }, + _uploadFileList: function(files){ + for (var i=0; i<files.length; i++){ + if ( !this._validateFile(files[i])){ + return; + } + } + + for (var i=0; i<files.length; i++){ + this._uploadFile(files[i]); + } + }, + _uploadFile: function(fileContainer){ + var id = this._handler.add(fileContainer); + var fileName = this._handler.getName(id); + + if (this._options.onSubmit(id, fileName) !== false){ + this._onSubmit(id, fileName); + this._handler.upload(id, this._options.params); + } + }, + _validateFile: function(file){ + var name, size; + + if (file.value){ + // it is a file input + // get input value and remove path to normalize + name = file.value.replace(/.*(\/|\\)/, ""); + } else { + // fix missing properties in Safari + name = file.fileName != null ? file.fileName : file.name; + size = file.fileSize != null ? file.fileSize : file.size; + } + + if (! this._isAllowedExtension(name)){ + this._error('typeError', name); + return false; + + } else if (size === 0){ + this._error('emptyError', name); + return false; + + } else if (size && this._options.sizeLimit && size > this._options.sizeLimit){ + this._error('sizeError', name); + return false; + + } else if (size && size < this._options.minSizeLimit){ + this._error('minSizeError', name); + return false; + } + + return true; + }, + _error: function(code, fileName){ + var message = this._options.messages[code]; + function r(name, replacement){ message = message.replace(name, replacement); } + + r('{file}', this._formatFileName(fileName)); + r('{extensions}', this._options.allowedExtensions.join(', ')); + r('{sizeLimit}', this._formatSize(this._options.sizeLimit)); + r('{minSizeLimit}', this._formatSize(this._options.minSizeLimit)); + + this._options.showMessage(message); + }, + _formatFileName: function(name){ + if (name.length > 33){ + name = name.slice(0, 19) + '...' + name.slice(-13); + } + return name; + }, + _isAllowedExtension: function(fileName){ + var ext = (-1 !== fileName.indexOf('.')) ? fileName.replace(/.*[.]/, '').toLowerCase() : ''; + var allowed = this._options.allowedExtensions; + + if (!allowed.length){return true;} + + for (var i=0; i<allowed.length; i++){ + if (allowed[i].toLowerCase() == ext){ return true;} + } + + return false; + }, + _formatSize: function(bytes){ + var i = -1; + do { + bytes = bytes / 1024; + i++; + } while (bytes > 99); + + return Math.max(bytes, 0.1).toFixed(1) + ['kB', 'MB', 'GB', 'TB', 'PB', 'EB'][i]; + } +}; + + +/** + * Class that creates upload widget with drag-and-drop and file list + * @inherits qq.FileUploaderBasic + */ +qq.FileUploader = function(o){ + // call parent constructor + qq.FileUploaderBasic.apply(this, arguments); + + // additional options + qq.extend(this._options, { + element: null, + // if set, will be used instead of qq-upload-list in template + listElement: null, + + template: '<div class="qq-uploader">' + + '<div class="qq-upload-drop-area"><span>Drop files here to upload</span></div>' + + '<div class="qq-upload-button">Upload a file</div>' + + '<ul class="qq-upload-list"></ul>' + + '</div>', + + // template for one item in file list + fileTemplate: '<li>' + + '<span class="qq-upload-file"></span>' + + '<span class="qq-upload-spinner"></span>' + + '<span class="qq-upload-size"></span>' + + '<a class="qq-upload-cancel" href="#">Cancel</a>' + + '<span class="qq-upload-failed-text">Failed</span>' + + '</li>', + + classes: { + // used to get elements from templates + button: 'qq-upload-button', + drop: 'qq-upload-drop-area', + dropActive: 'qq-upload-drop-area-active', + list: 'qq-upload-list', + + file: 'qq-upload-file', + spinner: 'qq-upload-spinner', + size: 'qq-upload-size', + cancel: 'qq-upload-cancel', + + // added to list item when upload completes + // used in css to hide progress spinner + success: 'qq-upload-success', + fail: 'qq-upload-fail' + } + }); + // overwrite options with user supplied + qq.extend(this._options, o); + + this._element = this._options.element; + this._element.innerHTML = this._options.template; + this._listElement = this._options.listElement || this._find(this._element, 'list'); + + this._classes = this._options.classes; + + this._button = this._createUploadButton(this._find(this._element, 'button')); + + this._bindCancelEvent(); + this._setupDragDrop(); +}; + +// inherit from Basic Uploader +qq.extend(qq.FileUploader.prototype, qq.FileUploaderBasic.prototype); + +qq.extend(qq.FileUploader.prototype, { + /** + * Gets one of the elements listed in this._options.classes + **/ + _find: function(parent, type){ + var element = qq.getByClass(parent, this._options.classes[type])[0]; + if (!element){ + throw new Error('element not found ' + type); + } + + return element; + }, + _setupDragDrop: function(){ + var self = this, + dropArea = this._find(this._element, 'drop'); + + var dz = new qq.UploadDropZone({ + element: dropArea, + onEnter: function(e){ + qq.addClass(dropArea, self._classes.dropActive); + e.stopPropagation(); + }, + onLeave: function(e){ + e.stopPropagation(); + }, + onLeaveNotDescendants: function(e){ + qq.removeClass(dropArea, self._classes.dropActive); + }, + onDrop: function(e){ + dropArea.style.display = 'none'; + qq.removeClass(dropArea, self._classes.dropActive); + self._uploadFileList(e.dataTransfer.files); + } + }); + + dropArea.style.display = 'none'; + + qq.attach(document, 'dragenter', function(e){ + if (!dz._isValidFileDrag(e)) return; + + dropArea.style.display = 'block'; + }); + qq.attach(document, 'dragleave', function(e){ + if (!dz._isValidFileDrag(e)) return; + + var relatedTarget = document.elementFromPoint(e.clientX, e.clientY); + // only fire when leaving document out + if ( ! relatedTarget || relatedTarget.nodeName == "HTML"){ + dropArea.style.display = 'none'; + } + }); + }, + _onSubmit: function(id, fileName){ + qq.FileUploaderBasic.prototype._onSubmit.apply(this, arguments); + this._addToList(id, fileName); + }, + _onProgress: function(id, fileName, loaded, total){ + qq.FileUploaderBasic.prototype._onProgress.apply(this, arguments); + + var item = this._getItemByFileId(id); + var size = this._find(item, 'size'); + size.style.display = 'inline'; + + var text; + if (loaded != total){ + text = Math.round(loaded / total * 100) + '% from ' + this._formatSize(total); + } else { + text = this._formatSize(total); + } + + qq.setText(size, text); + }, + _onComplete: function(id, fileName, result){ + qq.FileUploaderBasic.prototype._onComplete.apply(this, arguments); + + // mark completed + var item = this._getItemByFileId(id); + qq.remove(this._find(item, 'cancel')); + qq.remove(this._find(item, 'spinner')); + + if (result.success){ + qq.addClass(item, this._classes.success); + } else { + qq.addClass(item, this._classes.fail); + } + }, + _addToList: function(id, fileName){ + var item = qq.toElement(this._options.fileTemplate); + item.qqFileId = id; + + var fileElement = this._find(item, 'file'); + qq.setText(fileElement, this._formatFileName(fileName)); + this._find(item, 'size').style.display = 'none'; + + this._listElement.appendChild(item); + }, + _getItemByFileId: function(id){ + var item = this._listElement.firstChild; + + // there can't be txt nodes in dynamically created list + // and we can use nextSibling + while (item){ + if (item.qqFileId == id) return item; + item = item.nextSibling; + } + }, + /** + * delegate click event for cancel link + **/ + _bindCancelEvent: function(){ + var self = this, + list = this._listElement; + + qq.attach(list, 'click', function(e){ + e = e || window.event; + var target = e.target || e.srcElement; + + if (qq.hasClass(target, self._classes.cancel)){ + qq.preventDefault(e); + + var item = target.parentNode; + self._handler.cancel(item.qqFileId); + qq.remove(item); + } + }); + } +}); + +qq.UploadDropZone = function(o){ + this._options = { + element: null, + onEnter: function(e){}, + onLeave: function(e){}, + // is not fired when leaving element by hovering descendants + onLeaveNotDescendants: function(e){}, + onDrop: function(e){} + }; + qq.extend(this._options, o); + + this._element = this._options.element; + + this._disableDropOutside(); + this._attachEvents(); +}; + +qq.UploadDropZone.prototype = { + _disableDropOutside: function(e){ + // run only once for all instances + if (!qq.UploadDropZone.dropOutsideDisabled ){ + + qq.attach(document, 'dragover', function(e){ + if (e.dataTransfer){ + e.dataTransfer.dropEffect = 'none'; + e.preventDefault(); + } + }); + + qq.UploadDropZone.dropOutsideDisabled = true; + } + }, + _attachEvents: function(){ + var self = this; + + qq.attach(self._element, 'dragover', function(e){ + if (!self._isValidFileDrag(e)) return; + + var effect = e.dataTransfer.effectAllowed; + if (effect == 'move' || effect == 'linkMove'){ + e.dataTransfer.dropEffect = 'move'; // for FF (only move allowed) + } else { + e.dataTransfer.dropEffect = 'copy'; // for Chrome + } + + e.stopPropagation(); + e.preventDefault(); + }); + + qq.attach(self._element, 'dragenter', function(e){ + if (!self._isValidFileDrag(e)) return; + + self._options.onEnter(e); + }); + + qq.attach(self._element, 'dragleave', function(e){ + if (!self._isValidFileDrag(e)) return; + + self._options.onLeave(e); + + var relatedTarget = document.elementFromPoint(e.clientX, e.clientY); + // do not fire when moving a mouse over a descendant + if (qq.contains(this, relatedTarget)) return; + + self._options.onLeaveNotDescendants(e); + }); + + qq.attach(self._element, 'drop', function(e){ + if (!self._isValidFileDrag(e)) return; + + e.preventDefault(); + self._options.onDrop(e); + }); + }, + _isValidFileDrag: function(e){ + var dt = e.dataTransfer, + // do not check dt.types.contains in webkit, because it crashes safari 4 + isWebkit = navigator.userAgent.indexOf("AppleWebKit") > -1; + + // dt.effectAllowed is none in Safari 5 + // dt.types.contains check is for firefox + return dt && dt.effectAllowed != 'none' && + (dt.files || (!isWebkit && dt.types.contains && dt.types.contains('Files'))); + + } +}; + +qq.UploadButton = function(o){ + this._options = { + element: null, + // if set to true adds multiple attribute to file input + multiple: false, + // name attribute of file input + name: 'file', + onChange: function(input){}, + hoverClass: 'qq-upload-button-hover', + focusClass: 'qq-upload-button-focus' + }; + + qq.extend(this._options, o); + + this._element = this._options.element; + + // make button suitable container for input + qq.css(this._element, { + position: 'relative', + overflow: 'hidden', + // Make sure browse button is in the right side + // in Internet Explorer + direction: 'ltr' + }); + + this._input = this._createInput(); +}; + +qq.UploadButton.prototype = { + /* returns file input element */ + getInput: function(){ + return this._input; + }, + /* cleans/recreates the file input */ + reset: function(){ + if (this._input.parentNode){ + qq.remove(this._input); + } + + qq.removeClass(this._element, this._options.focusClass); + this._input = this._createInput(); + }, + _createInput: function(){ + var input = document.createElement("input"); + + if (this._options.multiple){ + input.setAttribute("multiple", "multiple"); + } + + input.setAttribute("type", "file"); + input.setAttribute("name", this._options.name); + + qq.css(input, { + position: 'absolute', + // in Opera only 'browse' button + // is clickable and it is located at + // the right side of the input + right: 0, + top: 0, + fontFamily: 'Arial', + // 4 persons reported this, the max values that worked for them were 243, 236, 236, 118 + fontSize: '118px', + margin: 0, + padding: 0, + cursor: 'pointer', + opacity: 0 + }); + + this._element.appendChild(input); + + var self = this; + qq.attach(input, 'change', function(){ + self._options.onChange(input); + }); + + qq.attach(input, 'mouseover', function(){ + qq.addClass(self._element, self._options.hoverClass); + }); + qq.attach(input, 'mouseout', function(){ + qq.removeClass(self._element, self._options.hoverClass); + }); + qq.attach(input, 'focus', function(){ + qq.addClass(self._element, self._options.focusClass); + }); + qq.attach(input, 'blur', function(){ + qq.removeClass(self._element, self._options.focusClass); + }); + + // IE and Opera, unfortunately have 2 tab stops on file input + // which is unacceptable in our case, disable keyboard access + if (window.attachEvent){ + // it is IE or Opera + input.setAttribute('tabIndex', "-1"); + } + + return input; + } +}; + +/** + * Class for uploading files, uploading itself is handled by child classes + */ +qq.UploadHandlerAbstract = function(o){ + this._options = { + debug: false, + action: '/upload.php', + // maximum number of concurrent uploads + maxConnections: 999, + onProgress: function(id, fileName, loaded, total){}, + onComplete: function(id, fileName, response){}, + onCancel: function(id, fileName){} + }; + qq.extend(this._options, o); + + this._queue = []; + // params for files in queue + this._params = []; +}; +qq.UploadHandlerAbstract.prototype = { + log: function(str){ + if (this._options.debug && window.console) console.log('[uploader] ' + str); + }, + /** + * Adds file or file input to the queue + * @returns id + **/ + add: function(file){}, + /** + * Sends the file identified by id and additional query params to the server + */ + upload: function(id, params){ + var len = this._queue.push(id); + + var copy = {}; + qq.extend(copy, params); + this._params[id] = copy; + + // if too many active uploads, wait... + if (len <= this._options.maxConnections){ + this._upload(id, this._params[id]); + } + }, + /** + * Cancels file upload by id + */ + cancel: function(id){ + this._cancel(id); + this._dequeue(id); + }, + /** + * Cancells all uploads + */ + cancelAll: function(){ + for (var i=0; i<this._queue.length; i++){ + this._cancel(this._queue[i]); + } + this._queue = []; + }, + /** + * Returns name of the file identified by id + */ + getName: function(id){}, + /** + * Returns size of the file identified by id + */ + getSize: function(id){}, + /** + * Returns id of files being uploaded or + * waiting for their turn + */ + getQueue: function(){ + return this._queue; + }, + /** + * Actual upload method + */ + _upload: function(id){}, + /** + * Actual cancel method + */ + _cancel: function(id){}, + /** + * Removes element from queue, starts upload of next + */ + _dequeue: function(id){ + var i = qq.indexOf(this._queue, id); + this._queue.splice(i, 1); + + var max = this._options.maxConnections; + + if (this._queue.length >= max && i < max){ + var nextId = this._queue[max-1]; + this._upload(nextId, this._params[nextId]); + } + } +}; + +/** + * Class for uploading files using form and iframe + * @inherits qq.UploadHandlerAbstract + */ +qq.UploadHandlerForm = function(o){ + qq.UploadHandlerAbstract.apply(this, arguments); + + this._inputs = {}; +}; +// @inherits qq.UploadHandlerAbstract +qq.extend(qq.UploadHandlerForm.prototype, qq.UploadHandlerAbstract.prototype); + +qq.extend(qq.UploadHandlerForm.prototype, { + add: function(fileInput){ + fileInput.setAttribute('name', 'qqfile'); + var id = 'qq-upload-handler-iframe' + qq.getUniqueId(); + + this._inputs[id] = fileInput; + + // remove file input from DOM + if (fileInput.parentNode){ + qq.remove(fileInput); + } + + return id; + }, + getName: function(id){ + // get input value and remove path to normalize + return this._inputs[id].value.replace(/.*(\/|\\)/, ""); + }, + _cancel: function(id){ + this._options.onCancel(id, this.getName(id)); + + delete this._inputs[id]; + + var iframe = document.getElementById(id); + if (iframe){ + // to cancel request set src to something else + // we use src="javascript:false;" because it doesn't + // trigger ie6 prompt on https + iframe.setAttribute('src', 'javascript:false;'); + + qq.remove(iframe); + } + }, + _upload: function(id, params){ + var input = this._inputs[id]; + + if (!input){ + throw new Error('file with passed id was not added, or already uploaded or cancelled'); + } + + var fileName = this.getName(id); + + var iframe = this._createIframe(id); + var form = this._createForm(iframe, params); + form.appendChild(input); + + var self = this; + this._attachLoadEvent(iframe, function(){ + self.log('iframe loaded'); + + var response = self._getIframeContentJSON(iframe); + + self._options.onComplete(id, fileName, response); + self._dequeue(id); + + delete self._inputs[id]; + // timeout added to fix busy state in FF3.6 + setTimeout(function(){ + qq.remove(iframe); + }, 1); + }); + + form.submit(); + qq.remove(form); + + return id; + }, + _attachLoadEvent: function(iframe, callback){ + qq.attach(iframe, 'load', function(){ + // when we remove iframe from dom + // the request stops, but in IE load + // event fires + if (!iframe.parentNode){ + return; + } + + // fixing Opera 10.53 + if (iframe.contentDocument && + iframe.contentDocument.body && + iframe.contentDocument.body.innerHTML == "false"){ + // In Opera event is fired second time + // when body.innerHTML changed from false + // to server response approx. after 1 sec + // when we upload file with iframe + return; + } + + callback(); + }); + }, + /** + * Returns json object received by iframe from server. + */ + _getIframeContentJSON: function(iframe){ + // iframe.contentWindow.document - for IE<7 + var doc = iframe.contentDocument ? iframe.contentDocument: iframe.contentWindow.document, + response; + + this.log("converting iframe's innerHTML to JSON"); + this.log("innerHTML = " + doc.body.innerHTML); + + try { + response = eval("(" + doc.body.innerHTML + ")"); + } catch(err){ + response = {}; + } + + return response; + }, + /** + * Creates iframe with unique name + */ + _createIframe: function(id){ + // We can't use following code as the name attribute + // won't be properly registered in IE6, and new window + // on form submit will open + // var iframe = document.createElement('iframe'); + // iframe.setAttribute('name', id); + + var iframe = qq.toElement('<iframe src="javascript:false;" name="' + id + '" />'); + // src="javascript:false;" removes ie6 prompt on https + + iframe.setAttribute('id', id); + + iframe.style.display = 'none'; + document.body.appendChild(iframe); + + return iframe; + }, + /** + * Creates form, that will be submitted to iframe + */ + _createForm: function(iframe, params){ + // We can't use the following code in IE6 + // var form = document.createElement('form'); + // form.setAttribute('method', 'post'); + // form.setAttribute('enctype', 'multipart/form-data'); + // Because in this case file won't be attached to request + var form = qq.toElement('<form method="post" enctype="multipart/form-data"></form>'); + + var queryString = qq.obj2url(params, this._options.action); + + form.setAttribute('action', queryString); + form.setAttribute('target', iframe.name); + form.style.display = 'none'; + document.body.appendChild(form); + + return form; + } +}); + +/** + * Class for uploading files using xhr + * @inherits qq.UploadHandlerAbstract + */ +qq.UploadHandlerXhr = function(o){ + qq.UploadHandlerAbstract.apply(this, arguments); + + this._files = []; + this._xhrs = []; + + // current loaded size in bytes for each file + this._loaded = []; +}; + +// static method +qq.UploadHandlerXhr.isSupported = function(){ + var input = document.createElement('input'); + input.type = 'file'; + + return ( + 'multiple' in input && + typeof File != "undefined" && + typeof (new XMLHttpRequest()).upload != "undefined" ); +}; + +// @inherits qq.UploadHandlerAbstract +qq.extend(qq.UploadHandlerXhr.prototype, qq.UploadHandlerAbstract.prototype); + +qq.extend(qq.UploadHandlerXhr.prototype, { + /** + * Adds file to the queue + * Returns id to use with upload, cancel + **/ + add: function(file){ + if (!(file instanceof File)){ + throw new Error('Passed obj in not a File (in qq.UploadHandlerXhr)'); + } + + return this._files.push(file) - 1; + }, + getName: function(id){ + var file = this._files[id]; + // fix missing name in Safari 4 + return file.fileName != null ? file.fileName : file.name; + }, + getSize: function(id){ + var file = this._files[id]; + return file.fileSize != null ? file.fileSize : file.size; + }, + /** + * Returns uploaded bytes for file identified by id + */ + getLoaded: function(id){ + return this._loaded[id] || 0; + }, + /** + * Sends the file identified by id and additional query params to the server + * @param {Object} params name-value string pairs + */ + _upload: function(id, params){ + var file = this._files[id], + name = this.getName(id), + size = this.getSize(id); + + this._loaded[id] = 0; + + var xhr = this._xhrs[id] = new XMLHttpRequest(); + var self = this; + + xhr.upload.onprogress = function(e){ + if (e.lengthComputable){ + self._loaded[id] = e.loaded; + self._options.onProgress(id, name, e.loaded, e.total); + } + }; + + xhr.onreadystatechange = function(){ + if (xhr.readyState == 4){ + self._onComplete(id, xhr); + } + }; + + // build query string + params = params || {}; + params['qqfile'] = name; + var queryString = qq.obj2url(params, this._options.action); + + xhr.open("POST", queryString, true); + xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest"); + xhr.setRequestHeader("X-File-Name", encodeURIComponent(name)); + xhr.setRequestHeader("Content-Type", "application/octet-stream"); + xhr.send(file); + }, + _onComplete: function(id, xhr){ + // the request was aborted/cancelled + if (!this._files[id]) return; + + var name = this.getName(id); + var size = this.getSize(id); + + this._options.onProgress(id, name, size, size); + + if (xhr.status == 200){ + this.log("xhr - server response received"); + this.log("responseText = " + xhr.responseText); + + var response; + + try { + response = eval("(" + xhr.responseText + ")"); + } catch(err){ + response = {}; + } + + this._options.onComplete(id, name, response); + + } else { + this._options.onComplete(id, name, {}); + } + + this._files[id] = null; + this._xhrs[id] = null; + this._dequeue(id); + }, + _cancel: function(id){ + this._options.onCancel(id, this.getName(id)); + + this._files[id] = null; + + if (this._xhrs[id]){ + this._xhrs[id].abort(); + this._xhrs[id] = null; + } + } +});
\ No newline at end of file diff --git a/lib/scripts/fileuploaderextended.js b/lib/scripts/fileuploaderextended.js new file mode 100644 index 000000000..ed631a9ea --- /dev/null +++ b/lib/scripts/fileuploaderextended.js @@ -0,0 +1,339 @@ +qq.extend(qq.FileUploader.prototype, { + _createUploadHandler: function(){ + var self = this, + handlerClass; + + if(qq.UploadHandlerXhr.isSupported()){ + handlerClass = 'UploadHandlerXhr'; + //handlerClass = 'UploadHandlerForm'; + } else { + handlerClass = 'UploadHandlerForm'; + } + + var handler = new qq[handlerClass]({ + debug: this._options.debug, + action: this._options.action, + maxConnections: this._options.maxConnections, + onProgress: function(id, fileName, loaded, total){ + self._onProgress(id, fileName, loaded, total); + self._options.onProgress(id, fileName, loaded, total); + }, + onComplete: function(id, fileName, result){ + self._onComplete(id, fileName, result); + self._options.onComplete(id, fileName, result); + }, + onCancel: function(id, fileName){ + self._onCancel(id, fileName); + self._options.onCancel(id, fileName); + }, + onUpload: function(){ + self._onUpload(); + } + }); + + return handler; + }, + + _onUpload: function(){ + this._handler.uploadAll(this._options.params); + }, + + _uploadFile: function(fileContainer){ + var id = this._handler.add(fileContainer); + var fileName = this._handler.getName(id); + + if (this._options.onSubmit(id, fileName) !== false){ + this._onSubmit(id, fileName); + } + }, + + _addToList: function(id, fileName){ + var item = qq.toElement(this._options.fileTemplate); + item.qqFileId = id; + + var fileElement = this._find(item, 'file'); + qq.setText(fileElement, fileName); + this._find(item, 'size').style.display = 'none'; + + var nameElement = this._find(item, 'nameInput'); + fileName = fileName.toLowerCase(); + fileName = fileName.replace(/([^a-z0-9_\.\-]+)/g, '_'); + nameElement.value = fileName; + nameElement.id = 'mediamanager__upload_item'+id; + + this._listElement.appendChild(item); + } + +}); + +qq.FileUploaderExtended = function(o){ + // call parent constructor + qq.FileUploaderBasic.apply(this, arguments); + + qq.extend(this._options, { + element: null, + // if set, will be used instead of qq-upload-list in template + listElement: null, + + template: '<div class="qq-uploader">' + + '<div class="qq-upload-drop-area"><span>' + LANG.media_drop + '</span></div>' + + '<div class="qq-upload-button">' + LANG.media_select + '</div>' + + '<div class="qq-upload-list"></div>' + + '<div><input class="button" type="submit" value="' + LANG.media_upload_btn + '" id="mediamanager__upload_button">' + + '<label class="check"><input class="dw__ow" type="checkbox" value="1" name="ow"><span>' + LANG.media_overwrt + '</span></label>' + + '</div>' + + '</div>', + + // template for one item in file list + fileTemplate: '<div class="li">' + + '<span class="qq-upload-file qq-upload-file-hidden"></span>' + + '<input class="qq-upload-name-input edit" type="text">' + + '<span class="qq-upload-spinner-hidden"></span>' + + '<span class="qq-upload-size"></span>' + + '<a class="qq-upload-cancel" href="#">' + LANG.media_cancel + '</a>' + + '<span class="qq-upload-failed-text">Failed</span>' + + '</div>', + + classes: { + // used to get elements from templates + button: 'qq-upload-button', + drop: 'qq-upload-drop-area', + dropActive: 'qq-upload-drop-area-active', + list: 'qq-upload-list', + nameInput: 'qq-upload-name-input', + file: 'qq-upload-file', + + spinner: 'qq-upload-spinner', + size: 'qq-upload-size', + cancel: 'qq-upload-cancel', + + // added to list item when upload completes + // used in css to hide progress spinner + success: 'qq-upload-success', + fail: 'qq-upload-fail', + failedText : 'qq-upload-failed-text' + } + }); + + qq.extend(this._options, o); + + this._element = this._options.element; + this._element.innerHTML = this._options.template; + this._listElement = this._options.listElement || this._find(this._element, 'list'); + + this._classes = this._options.classes; + + this._button = this._createUploadButton(this._find(this._element, 'button')); + + this._bindCancelEvent(); + this._bindUploadEvent(); + this._setupDragDrop(); +}; + +qq.extend(qq.FileUploaderExtended.prototype, qq.FileUploader.prototype); + +qq.extend(qq.FileUploaderExtended.prototype, { + _bindUploadEvent: function(){ + var self = this, + list = this._listElement; + + qq.attach(document.getElementById('mediamanager__upload_button'), 'click', function(e){ + e = e || window.event; + var target = e.target || e.srcElement; + qq.preventDefault(e); + self._handler._options.onUpload(); + + jQuery(".qq-upload-name-input").each(function (i) { + jQuery(this).attr('disabled', 'disabled'); + }); + }); + }, + + _onComplete: function(id, fileName, result){ + this._filesInProgress--; + + // mark completed + var item = this._getItemByFileId(id); + qq.remove(this._find(item, 'cancel')); + qq.remove(this._find(item, 'spinner')); + + var nameInput = this._find(item, 'nameInput'); + var fileElement = this._find(item, 'file'); + qq.setText(fileElement, nameInput.value); + qq.removeClass(fileElement, 'qq-upload-file-hidden'); + qq.remove(nameInput); + jQuery('.qq-upload-button, #mediamanager__upload_button').remove(); + jQuery('.dw__ow').parent().hide(); + jQuery('.qq-upload-drop-area').remove(); + + if (result.success){ + qq.addClass(item, this._classes.success); + $link = '<a href="' + result.link + '" name="h_:' + result.id + '" class="select">' + nameInput.value + '</a>'; + jQuery(fileElement).html($link); + + } else { + qq.addClass(item, this._classes.fail); + var fail = this._find(item, 'failedText'); + if (result.error) qq.setText(fail, result.error); + } + + if (document.getElementById('media__content') && !document.getElementById('mediamanager__done_form')) { + var action = document.location.href; + var i = action.indexOf('?'); + if (i) action = action.substr(0, i); + var button = '<form method="post" action="' + action + '" id="mediamanager__done_form"><div>'; + button += '<input type="hidden" value="' + result.ns + '" name="ns">'; + button += '<input class="button" type="submit" value="' + LANG.media_done_btn + '"></div></form>'; + jQuery('#mediamanager__uploader').append(button); + } + } + +}); + +qq.extend(qq.UploadHandlerForm.prototype, { + uploadAll: function(params){ + this._uploadAll(params); + }, + + getName: function(id){ + var file = this._inputs[id]; + var name = document.getElementById('mediamanager__upload_item'+id); + if (name != null) { + return name.value; + } else { + if (file != null) { + // get input value and remove path to normalize + return file.value.replace(/.*(\/|\\)/, ""); + } else { + return null; + } + } + }, + + _uploadAll: function(params){ + jQuery(".qq-upload-spinner-hidden").each(function (i) { + jQuery(this).addClass('qq-upload-spinner'); + }); + for (key in this._inputs) { + this.upload(key, params); + } + + }, + + _upload: function(id, params){ + var input = this._inputs[id]; + + if (!input){ + throw new Error('file with passed id was not added, or already uploaded or cancelled'); + } + + var fileName = this.getName(id); + + var iframe = this._createIframe(id); + var form = this._createForm(iframe, params); + form.appendChild(input); + + var nameInput = qq.toElement('<input name="mediaid" value="' + fileName + '" type="text">'); + form.appendChild(nameInput); + + var checked = jQuery('.dw__ow').attr('checked'); + var owCheckbox = jQuery('.dw__ow').clone(); + owCheckbox.attr('checked', checked); + jQuery(form).append(owCheckbox); + + var self = this; + this._attachLoadEvent(iframe, function(){ + self.log('iframe loaded'); + + var response = self._getIframeContentJSON(iframe); + + self._options.onComplete(id, fileName, response); + self._dequeue(id); + + delete self._inputs[id]; + // timeout added to fix busy state in FF3.6 + setTimeout(function(){ + qq.remove(iframe); + }, 1); + }); + + form.submit(); + qq.remove(form); + + return id; + } +}); + +qq.extend(qq.UploadHandlerXhr.prototype, { + uploadAll: function(params){ + this._uploadAll(params); + }, + + getName: function(id){ + var file = this._files[id]; + var name = document.getElementById('mediamanager__upload_item'+id); + if (name != null) { + return name.value; + } else { + if (file != null) { + // fix missing name in Safari 4 + return file.fileName != null ? file.fileName : file.name; + } else { + return null; + } + } + }, + + getSize: function(id){ + var file = this._files[id]; + if (file == null) return null; + return file.fileSize != null ? file.fileSize : file.size; + }, + + _upload: function(id, params){ + var file = this._files[id], + name = this.getName(id), + size = this.getSize(id); + if (name == null || size == null) return; + + this._loaded[id] = 0; + + var xhr = this._xhrs[id] = new XMLHttpRequest(); + var self = this; + + xhr.upload.onprogress = function(e){ + if (e.lengthComputable){ + self._loaded[id] = e.loaded; + self._options.onProgress(id, name, e.loaded, e.total); + } + }; + + xhr.onreadystatechange = function(){ + if (xhr.readyState == 4){ + self._onComplete(id, xhr); + } + }; + + // build query string + params = params || {}; + params['qqfile'] = name; + params['ow'] = jQuery('.dw__ow').attr('checked'); + var queryString = qq.obj2url(params, this._options.action); + + xhr.open("POST", queryString, true); + xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest"); + xhr.setRequestHeader("X-File-Name", encodeURIComponent(name)); + xhr.setRequestHeader("Content-Type", "application/octet-stream"); + xhr.send(file); + }, + + _uploadAll: function(params){ + jQuery(".qq-upload-spinner-hidden").each(function (i) { + jQuery(this).addClass('qq-upload-spinner'); + }); + for (key in this._files) { + this.upload(key, params); + } + + } +}); diff --git a/lib/scripts/media.js b/lib/scripts/media.js index 1402ad4bf..7103727c5 100644 --- a/lib/scripts/media.js +++ b/lib/scripts/media.js @@ -22,6 +22,11 @@ var dw_mediamanager = { size: false, forbidden_opts: {}, + // File list view type + view: false, + + layout_width: 0, + init: function () { var $content, $tree; $content = jQuery('#media__content'); @@ -38,7 +43,8 @@ var dw_mediamanager = { .delegate('a.select', 'click', dw_mediamanager.select) // Attach deletion confirmation dialog to the delete buttons .delegate('#media__content a.btn_media_delete', 'click', - dw_mediamanager.confirmattach); + dw_mediamanager.confirmattach) + .delegate('#mediamanager__done_form', 'submit', dw_mediamanager.list); $tree.dw_tree({toggle_selector: 'img', load_data: function (show_sublist, $clicky) { @@ -59,6 +65,43 @@ var dw_mediamanager = { (opening ? 'minus' : 'plus') + '.gif'); }}); $tree.delegate('a', 'click', dw_mediamanager.list); + + dw_mediamanager.set_filelist_view(dw_mediamanager.view, false); + jQuery('#mediamanager__form_sort').find('input[type=submit]').hide(); + dw_mediamanager.image_diff(); + dw_mediamanager.init_ajax_uploader(); + + // changing opened tab in the file list panel + jQuery('#mediamanager__layout_list').delegate('#mediamanager__tabs_files a', 'click', dw_mediamanager.list) + // changing type of the file list view + .delegate('#mediamanager__tabs_list a', 'click', dw_mediamanager.list_view) + // loading file details + .delegate('#mediamanager__file_list a', 'click', dw_mediamanager.details) + // search form + .delegate('#dw__mediasearch', 'submit', dw_mediamanager.list) + // "upload as" field autofill + .delegate('#upload__file', 'change', dw_mediamanager.suggest) + // sort type selection + .delegate('#mediamanager__form_sort select', 'change', dw_mediamanager.list) + // uploaded images + .delegate('.qq-upload-file a', 'click', dw_mediamanager.details); + + // changing opened tab in the file details panel + jQuery('#mediamanager__layout_detail').delegate('#mediamanager__tabs_details a', 'click', dw_mediamanager.details) + // "update new version" button + .delegate('#mediamanager__btn_update', 'submit', dw_mediamanager.list) + // revisions form + .delegate('#page__revisions', 'submit', dw_mediamanager.details) + .delegate('#page__revisions a', 'click', dw_mediamanager.details) + // meta edit form + .delegate('#mediamanager__save_meta', 'submit', dw_mediamanager.details) + // delete button + .delegate('#mediamanager__btn_delete', 'submit', dw_mediamanager.details) + // "restore this version" button + .delegate('#mediamanager__btn_restore', 'submit', dw_mediamanager.details) + // less/more recent buttons in media revisions form + .delegate('.btn_newer, .btn_older', 'submit', dw_mediamanager.details); + }, /** @@ -201,6 +244,9 @@ var dw_mediamanager = { $file = jQuery(this); $name = jQuery('#upload__name'); + + if ($name.val() != '') return; + if(!$file.length || !$name.length) { return; } @@ -218,33 +264,416 @@ var dw_mediamanager = { * @author Pierre Spring <pierre.spring@caillou.ch> */ list: function (event) { - var $link, $content; + var $link, $content, params; + $link = jQuery(this); event.preventDefault(); jQuery('div.success, div.info, div.error, div.notify').remove(); - $link = jQuery(this); - $content = jQuery('#media__content'); - $content.html('<img src="' + DOKU_BASE + 'lib/images/loading.gif" alt="..." class="load" />'); + if (document.getElementById('media__content')) { + //popup + $content = jQuery('#media__content'); + } else { + //fullscreen media manager + $content = jQuery('#mediamanager__layout_list'); + + if ($link.hasClass('idx_dir')) { + //changing namespace + jQuery('#mediamanager__layout_detail').empty(); + jQuery('#media__tree .selected').each(function(){ + jQuery(this).removeClass('selected'); + }); + $link.addClass('selected'); + } + } + + params = ''; + + if ($link[0].search) { + params = $link[0].search.substr(1)+'&call=medialist'; + } else if ($link[0].action) { + params = dw_mediamanager.form_params($link)+'&call=medialist'; + } else if ($link.parents('form')) { + params = dw_mediamanager.form_params($link.parents('form'))+'&call=medialist'; + + if ($link.parents('form')[0].id == 'mediamanager__form_sort') { + DokuCookie.setValue('sort', $link[0].value); + } + } // fetch the subtree + dw_mediamanager.update_content($content, params); + + if (document.getElementById('media__content')) { + //popup + $content = jQuery('#media__content'); + $content.html('<img src="' + DOKU_BASE + 'lib/images/loading.gif" alt="..." class="load" />'); + } else { + //fullscreen media manager + jQuery('.scroll-container', $content).html('<img src="' + DOKU_BASE + 'lib/images/loading.gif" alt="..." class="load" />'); + } + }, + + /** + * Returns form parameters + * + * @author Kate Arzamastseva <pshns@ukr.net> + */ + form_params: function ($form) { + if (!$form.length) return; + var elements = $form.serialize(); + var action = ''; + var i = $form[0].action.indexOf('?'); + if (i >= 0) action = $form[0].action.substr(i+1); + return elements+'&'+action; + }, + + /** + * Changes view of media files list + * + * @author Kate Arzamastseva <pshns@ukr.net> + */ + list_view: function (event) { + var $link, $content; + $link = jQuery(this); + + event.preventDefault(); + + $content = jQuery('#mediamanager__file_list'); + + if ($link[0].id == 'mediamanager__link_thumbs') { + dw_mediamanager.set_filelist_view('thumbs', true); + + } else if ($link[0].id == 'mediamanager__link_list') { + dw_mediamanager.set_filelist_view('list', true); + } + }, + + set_filelist_view: function (type, cookies) { + var $content = jQuery('#mediamanager__file_list'); + if (!type) type = DokuCookie.getValue('view'); + + if (type == 'thumbs') { + $content.removeClass('mediamanager-list'); + $content.addClass('mediamanager-thumbs'); + if (cookies) DokuCookie.setValue('view', 'thumbs'); + dw_mediamanager.view = 'thumbs'; + + } else if (type == 'list') { + $content.removeClass('mediamanager-thumbs'); + $content.addClass('mediamanager-list'); + if (cookies) DokuCookie.setValue('view', 'list'); + dw_mediamanager.view = 'list'; + } + }, + + /** + * Lists the content of the right column (image details) using AJAX + * + * @author Kate Arzamastseva <pshns@ukr.net> + */ + details: function (event) { + var $link, $content, params, update_list; + $link = jQuery(this); + + event.preventDefault(); + + jQuery('div.success, div.info, div.error, div.notify').remove(); + + if ($link[0].id == 'mediamanager__btn_delete' && !confirm(LANG['del_confirm'])) return false; + if ($link[0].id == 'mediamanager__btn_restore' && !confirm(LANG['restore_confirm'])) return false; + + $content = jQuery('#mediamanager__layout_detail'); + params = ''; + + if ($link[0].search) { + params = $link[0].search.substr(1)+'&call=mediadetails'; + } else if ($link[0].action) { + params = dw_mediamanager.form_params($link)+'&call=mediadetails'; + } else if ($link.parents('form')) { + params = dw_mediamanager.form_params($link.parents('form'))+'&call=mediadetails'; + } + + update_list = ($link[0].id == 'mediamanager__btn_delete' || $link[0].id == 'mediamanager__btn_restore'); + dw_mediamanager.update_content($content, params, update_list); + + if (jQuery('.scroll-container', $content).length) { + jQuery('.scroll-container', $content).html('<img src="' + DOKU_BASE + 'lib/images/loading.gif" alt="..." class="load" />'); + } else { + jQuery($content).html('<img src="' + DOKU_BASE + 'lib/images/loading.gif" alt="..." class="load" />'); + } + }, + + update_content: function ($content, params, update_list) { jQuery.post( DOKU_BASE + 'lib/exe/ajax.php', - $link[0].search.substr(1)+'&call=medialist', + params, function (data) { + jQuery('.ui-resizable').each(function(){ + jQuery(this).resizable('destroy'); + }); + $content.html(data); + dw_mediamanager.prepare_content($content); dw_mediamanager.updatehide(); + + dw_mediamanager.update_resizable(); + dw_behaviour.revisionBoxHandler(); + jQuery('#mediamanager__form_sort').find('input[type=submit]').hide(); + dw_mediamanager.set_filelist_view(dw_mediamanager.view, false); + dw_mediamanager.image_diff(); + dw_mediamanager.init_ajax_uploader(); + + if (update_list) { + var $link1, $content1, params1; + $link1 = jQuery('a.files'); + params1 = $link1[0].search.substr(1)+'&call=medialist'; + $content1 = jQuery('#mediamanager__layout_list'); + dw_mediamanager.update_content($content1, params1); + jQuery('.scroll-container', $content1).html('<img src="' + DOKU_BASE + 'lib/images/loading.gif" alt="..." class="load" />'); + } }, 'html' ); }, + window_resize: function () { + if (jQuery('#mediamanager__layout').width() == dw_mediamanager.layout_width) { + return; + } + + dw_mediamanager.layout_width = jQuery('#mediamanager__layout').width(); + $r = jQuery("#mediamanager__layout .layout-resizable, #mediamanager__layout .layout"); + + var w = 0, wSum = 0, mCount = 0, mArray = []; + $r.each(function() { + w = jQuery(this).width(); + if (w == parseFloat(jQuery(this).css("min-width"))) { + wSum += w; + } else { + mArray[mCount] = jQuery(this); + mCount++; + } + }); + + if (mCount > 0) { + var width = (0.95 * jQuery('#mediamanager__layout').width() - wSum - 30); + wSum = 0; + for(var i = 0; i < mArray.length; i++) { + wSum += mArray[i].width(); + } + for(var i = 0; i < mArray.length; i++) { + w = mArray[i].width(); + w = 100*w / wSum; + mArray[i].width(width*w/100); + } + } + + $r.each(function() { + w = jQuery(this).width(); + w = (100 * w / jQuery('#mediamanager__layout').width()); + w += "%"; + jQuery(this).width(w); + }); + + dw_mediamanager.opacity_slider(); + dw_mediamanager.portions_slider(); + }, + + /** + * Updates mediamanager layout + * + * @author Kate Arzamastseva <pshns@ukr.net> + */ + update_resizable: function () { + $resizable = jQuery("#mediamanager__layout .layout-resizable"); + + $resizable.resizable({ handles: 'e' , + resize: function(event, ui){ + var w = 0; + $resizable.each(function() { + w += jQuery(this).width(); + }); + wSum = w + parseFloat(jQuery('#mediamanager__layout_detail').css("min-width")); + + // max width of resizable column + var maxWidth = 0.95 * jQuery('#mediamanager__layout').width() - wSum + jQuery(this).width() - 30; + $resizable.resizable( "option", "maxWidth", maxWidth ); + + // percentage width of the first two columns + var wLeft = ( 100*(w+30) / jQuery('#mediamanager__layout').width() ); + + // width of the third column + var wRight = 95-wLeft; + wRight += "%"; + jQuery('#mediamanager__layout_detail').width(wRight); + + $resizable.each(function() { + w = jQuery(this).width(); + w = (100 * w / jQuery('#mediamanager__layout').width()); + w += "%"; + jQuery(this).width(w); + }); + + dw_mediamanager.opacity_slider(); + dw_mediamanager.portions_slider(); + } + }); + + var windowHeight = jQuery(window).height(); + var height = windowHeight - 300; + jQuery('#mediamanager__layout .scroll-container').each(function (i) { + jQuery(this).height(height); + }); + $resizable.each(function() { + jQuery(this).height(height+100); + }); + }, + + /** + * Prints 'select' for image difference representation type + * + * @author Kate Arzamastseva <pshns@ukr.net> + */ + image_diff: function () { + if (jQuery('#mediamanager__difftype').length) return; + + $form = jQuery('#mediamanager__form_diffview'); + if (!$form.length) return; + + $label = jQuery(document.createElement('label')); + $label.append('<span>'+LANG.media_diff+'</span>'); + $select = jQuery(document.createElement('select')) + .attr('id', 'mediamanager__difftype') + .attr('name', 'difftype') + .change(dw_mediamanager.change_diff_type); + $select.append(new Option(LANG.media_diff_both, "both")); + $select.append(new Option(LANG.media_diff_opacity, "opacity")); + $select.append(new Option(LANG.media_diff_portions, "portions")); + $label.append($select); + $form.append($label); + + // for IE + var select = document.getElementById('mediamanager__difftype'); + select.options[0].text = LANG.media_diff_both; + select.options[1].text = LANG.media_diff_opacity; + select.options[2].text = LANG.media_diff_portions; + }, + + /** + * Handles selection of image difference representation type + * + * @author Kate Arzamastseva <pshns@ukr.net> + */ + change_diff_type: function () { + $select = jQuery('#mediamanager__difftype'); + $content = jQuery('#mediamanager__diff'); + + params = dw_mediamanager.form_params($select.parents('form'))+'&call=mediadiff'; + jQuery.post( + DOKU_BASE + 'lib/exe/ajax.php', + params, + function (data) { + $content.html(data); + dw_mediamanager.portions_slider(); + dw_mediamanager.opacity_slider(); + }, + 'html' + ); + }, + + /** + * Sets options for opacity diff slider + * + * @author Kate Arzamastseva <pshns@ukr.net> + */ + opacity_slider: function () { + var $slider = jQuery( "#mediamanager__opacity_slider" ); + if (!$slider.length) return; + + var $image = jQuery('#mediamanager__diff_opacity_image1 img'); + if (!$image.length) return; + $slider.width($image.width()-20); + + $slider.slider(); + $slider.slider("option", "min", 0); + $slider.slider("option", "max", 0.999); + $slider.slider("option", "step", 0.001); + $slider.slider("option", "value", 0.5); + $slider.bind("slide", function(event, ui) { + jQuery('#mediamanager__diff_opacity_image2').css({ opacity: $slider.slider("option", "value")}); + }); + }, + + /** + * Sets options for red line diff slider + * + * @author Kate Arzamastseva <pshns@ukr.net> + */ + portions_slider: function () { + var $image1 = jQuery('#mediamanager__diff_portions_image1 img'); + var $image2 = jQuery('#mediamanager__diff_portions_image2 img'); + if (!$image1.length || !$image2.length) return; + + var $div = jQuery("#mediamanager__diff_layout"); + if (!$div.length) return; + + $div.width('100%'); + $image2.parent().width('97%'); + $image1.width('100%'); + $image2.width('100%'); + + if ($image1.width() < $div.width()) { + $div.width($image1.width()); + } + + $image2.parent().width('50%'); + $image2.width($image1.width()); + $image1.width($image1.width()); + + var $slider = jQuery("#mediamanager__portions_slider"); + if (!$slider.length) return; + $slider.width($image1.width()-20); + + $slider.slider(); + $slider.slider("option", "min", 0); + $slider.slider("option", "max", 97); + $slider.slider("option", "step", 1); + $slider.slider("option", "value", 50); + $slider.bind("slide", function(event, ui) { + jQuery('#mediamanager__diff_portions_image2').css({ width: $slider.slider("option", "value")+'%'}); + }); + }, + + params_toarray: function (str) { + var vars = [], hash; + var hashes = str.split('&'); + for(var i = 0; i < hashes.length; i++) { + hash = hashes[i].split('='); + vars[hash[0]] = hash[1]; + } + return vars; + }, + + init_ajax_uploader: function () { + if (!jQuery('#mediamanager__uploader').length) return; + if (jQuery('.qq-upload-list').length) return; + + var params = dw_mediamanager.form_params(jQuery('#dw__upload'))+'&call=mediaupload'; + params = dw_mediamanager.params_toarray(params); + + var uploader = new qq.FileUploaderExtended({ + element: document.getElementById('mediamanager__uploader'), + action: DOKU_BASE + 'lib/exe/ajax.php', + params: params + }); + }, + prepare_content: function ($content) { // hide syntax example $content.find('div.example:visible').hide(); - dw_mediamanager.initFlashUpload(); }, /** @@ -521,4 +950,10 @@ function hasFlash(version){ return ver >= version; } +jQuery(document).ready(function() { + dw_mediamanager.update_resizable(); + dw_mediamanager.layout_width = jQuery("#mediamanager__layout").width(); + jQuery(window).resize(dw_mediamanager.window_resize); +}); + jQuery(dw_mediamanager.init); diff --git a/lib/tpl/default/_fileuploader.css b/lib/tpl/default/_fileuploader.css new file mode 100644 index 000000000..9e60393a0 --- /dev/null +++ b/lib/tpl/default/_fileuploader.css @@ -0,0 +1,106 @@ +.qq-uploader { + position: relative; + width: 100%; +} + +.qq-upload-button { + display: inline-block; + border: 1px solid __border__; + color: __text__; + background-color: __background__; + text-decoration: none; + font-size: 100%; + cursor: pointer; + margin: 1px; + margin-bottom: 5px; + padding: 0.125em 0.4em; + background: __background__ url(images/buttonshadow.png) repeat-x bottom; +} + +* html .qq-upload-button, +*+html .qq-upload-button { + display: inline; +} + +.qq-upload-button-focus { + outline: 1px dotted black; +} + +.qq-upload-drop-area { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + min-height: 70px; + z-index: 2; + background: __background_neu__; + text-align: center; +} + +.qq-upload-drop-area span { + display:block; + position:absolute; + top: 50%; + width:100%; + margin-top:-8px; + font-size: 120%; +} + +.qq-upload-drop-area-active { + background: __background_alt__; +} + +.qq-upload-file, .qq-upload-spinner, .qq-upload-size, .qq-upload-cancel, .qq-upload-failed-text { + margin-right: 7px; +} + +.qq-upload-spinner { + display: inline-block; + background: url("../../images/throbber.gif"); + width: 15px; + height: 15px; + vertical-align: text-bottom; +} + +.qq-upload-size,.qq-upload-cancel { + font-size: 85%; +} + +.qq-upload-failed-text { + display:none; +} + +.qq-upload-fail .qq-upload-failed-text { + display:inline; +} + +.qq-upload-file { + display: block; + font-weight: bold; +} + +.qq-upload-file-hidden { + display:none; +} + +.qq-upload-cancel { + padding-left: 10px; +} + +.qq-uploader .dw__ow { + margin-left: 10px; + margin-right: 3px; +} + +.qq-uploader .check { + line-height: 18px; +} + +.qq-uploader #mediamanager__upload_button { + margin-bottom: 10px; +} + +.qq-uploader div.li { + margin-bottom: 5px; +}
\ No newline at end of file diff --git a/lib/tpl/default/design.css b/lib/tpl/default/design.css index 1fdf2bfac..a94f814aa 100644 --- a/lib/tpl/default/design.css +++ b/lib/tpl/default/design.css @@ -246,6 +246,12 @@ div.dokuwiki div.pagenav-next { width: 49% } +/* ----------- type of recent changes select -------- */ + +div.dokuwiki form#dw__recent select { + margin-bottom: 10px; +} + /* --------------- Links ------------------ */ div.dokuwiki a:link, diff --git a/lib/tpl/default/detail.php b/lib/tpl/default/detail.php index 4f42b116e..503c0ef71 100644 --- a/lib/tpl/default/detail.php +++ b/lib/tpl/default/detail.php @@ -49,33 +49,33 @@ if (!defined('DOKU_INC')) die(); </p> <p>← <?php echo $lang['img_backto']?> <?php tpl_pagelink($ID)?></p> + <?php + $imgNS = getNS($IMG); + $authNS = auth_quickaclcheck("$imgNS:*"); + if ($authNS >= AUTH_UPLOAD) { + echo '<p><a href="'.media_managerURL(array('ns' => $imgNS, 'image' => $IMG)).'">'.$lang['img_manager'].'</a></p>'; + } + ?> <dl class="img_tags"> <?php - $t = tpl_img_getTag('Date.EarliestTime'); - if($t) print '<dt>'.$lang['img_date'].':</dt><dd>'.dformat($t).'</dd>'; - - $t = tpl_img_getTag('File.Name'); - if($t) print '<dt>'.$lang['img_fname'].':</dt><dd>'.hsc($t).'</dd>'; - - $t = tpl_img_getTag(array('Iptc.Byline','Exif.TIFFArtist','Exif.Artist','Iptc.Credit')); - if($t) print '<dt>'.$lang['img_artist'].':</dt><dd>'.hsc($t).'</dd>'; - - $t = tpl_img_getTag(array('Iptc.CopyrightNotice','Exif.TIFFCopyright','Exif.Copyright')); - if($t) print '<dt>'.$lang['img_copyr'].':</dt><dd>'.hsc($t).'</dd>'; - - $t = tpl_img_getTag('File.Format'); - if($t) print '<dt>'.$lang['img_format'].':</dt><dd>'.hsc($t).'</dd>'; - - $t = tpl_img_getTag('File.NiceSize'); - if($t) print '<dt>'.$lang['img_fsize'].':</dt><dd>'.hsc($t).'</dd>'; - - $t = tpl_img_getTag('Simple.Camera'); - if($t) print '<dt>'.$lang['img_camera'].':</dt><dd>'.hsc($t).'</dd>'; - - $t = tpl_img_getTag(array('IPTC.Keywords','IPTC.Category','xmp.dc:subject')); - if($t) print '<dt>'.$lang['img_keywords'].':</dt><dd>'.hsc($t).'</dd>'; - + $config_files = getConfigFiles('mediameta'); + foreach ($config_files as $config_file) { + if(@file_exists($config_file)) include($config_file); + } + + foreach($fields as $key => $tag){ + $t = array(); + if (!empty($tag[0])) $t = array($tag[0]); + if(is_array($tag[3])) $t = array_merge($t,$tag[3]); + $value = tpl_img_getTag($t); + if ($value) { + echo '<dt>'.$lang[$tag[1]].':</dt><dd>'; + if ($tag[2] == 'date') echo dformat($value); + else echo hsc($value); + echo '</dd>'; + } + } ?> </dl> <?php //Comment in for Debug// dbg(tpl_img_getTag('Simple.Raw'));?> diff --git a/lib/tpl/default/main.php b/lib/tpl/default/main.php index 26e84fde6..3e85c58f2 100644 --- a/lib/tpl/default/main.php +++ b/lib/tpl/default/main.php @@ -116,6 +116,7 @@ if (!defined('DOKU_INC')) die(); </div> <div class="bar-right" id="bar__bottomright"> <?php tpl_button('subscribe')?> + <?php tpl_button('media')?> <?php tpl_button('admin')?> <?php tpl_button('profile')?> <?php tpl_button('login')?> diff --git a/lib/tpl/default/media.css b/lib/tpl/default/media.css index 7b558a80b..b325a1ef6 100644 --- a/lib/tpl/default/media.css +++ b/lib/tpl/default/media.css @@ -151,6 +151,11 @@ it's dirty, so any "real" fixes are welcome */ border-bottom: solid 1px __border__; padding: 0 0.5em 1em 0.5em; } + +#media__content form#dw__upload { + border-bottom: 0; +} + #media__content form#dw__upload fieldset { padding: 0; margin: 0; diff --git a/lib/tpl/default/mediamanager.css b/lib/tpl/default/mediamanager.css new file mode 100644 index 000000000..16982d98d --- /dev/null +++ b/lib/tpl/default/mediamanager.css @@ -0,0 +1,419 @@ +/* Layout */ + +#mediamanager__page { + width: 100%; + overflow-x: auto; +} + +#mediamanager__layout { + min-width: 840px; +} + +#mediamanager__layout .layout, +#mediamanager__layout .layout-resizable { + margin-left: 5px; + margin-right: 5px; + float: left; +} + +#mediamanager__layout .scroll-container { + overflow-y: auto; + overflow-x: hidden; + padding: 0; + margin: 0; + text-align: left; + position: relative; +} + +#mediamanager__layout .background-container { + background-color: __background_alt__; + margin-bottom: 10px; + padding: 10px; + text-align: left; +} + +#mediamanager__layout .background-container .icon { + margin-right: 5px; +} + +#mediamanager__layout_namespaces { + width: 15%; + min-width: 125px; +} + +#mediamanager__layout_list { + width: 45%; + min-width: 375px; +} + +#mediamanager__layout_detail { + width: 35%; + min-width: 290px; +} + +.ui-resizable-e:hover { + background-color: #dadada; +} + +/* Namespaces tree */ + +.idx .selected { + background-color: __highlight__; + font-weight: bold; +} + +/* Tabs */ + +.mediamanager-tabs a { + font-weight: bold; + display: block; + float: left; + padding: 10px; + padding-bottom: 5px; + padding-top: 5px; + margin-right: 2px; + -moz-border-radius-topright: 10px; + -webkit-border-top-right-radius: 10px; + -moz-border-radius-topleft: 10px; + -webkit-border-top-left-radius: 10px; + border-top-right-radius: 10px; + border-top-left-radius: 10px; +} + +.mediamanager-tabs .selected { + background-color: __background_alt__; +} + +.mediamanager-tabs a:hover { + background-color: __background_alt__; + opacity: 0.5; +} + +/* Title links */ + +#mediamanager__tabs_list { + display: inline; +} + +#mediamanager__link_thumbs { + background: url('../../images/icon-thumb.png') 0 -4px no-repeat; + padding-left: 30px; + display: inline-block; + width: 0; + overflow: hidden; + margin-left: 10px; +} + +#mediamanager__link_list { + background: url('../../images/icon-list.png') 0 -4px no-repeat; + padding-left: 30px; + display: inline-block; + width: 0; + overflow: hidden; +} + +#mediamanager__link_thumbs:hover, +#mediamanager__link_list:hover { + width: auto; + margin-right: 10px; +} + +#mediamanager__sort { + background: url('../../images/icon-sort.png') 0 -4px no-repeat; + padding-left: 30px; + display: block; + float: right; +} + +* html #mediamanager__sort, +*+html #mediamanager__sort { + position: relative; + margin-top: -18px; +} + +/* File list */ + +#mediamanager__file_list { + padding: 0; + margin: 0 !important; +} + +#mediamanager__file_list li:hover { + background-color: #dadada; +} + +/* Files thumbs view */ + +.mediamanager-thumbs li { + width: 100px; + min-height: 130px; + display: inline-block; + margin: 0; + margin-right: 10px; + margin-bottom: 10px; + background-color: __background_alt__; + padding: 10px; + vertical-align: top; + display: -moz-inline-stack; + text-align: center; + zoom: 1; + position: relative; +} + +* html .mediamanager-thumbs li { + display: inline; +} + +*+html .mediamanager-thumbs li { + display: inline; +} + +.mediamanager-thumbs li .image, +.mediamanager-thumbs li .image0 { + width: 100%; + height: 90px; + display: block; + overflow: hidden; +} + +.mediamanager-thumbs li .image1 { + display: none; +} + +.mediamanager-thumbs li .image span, +.mediamanager-thumbs li .image0 span { + vertical-align: middle; + display: table-cell; + width: 100px; + height: 90px; +} + +.mediamanager-thumbs li .name, +.mediamanager-thumbs li .size, +.mediamanager-thumbs li .filesize, +.mediamanager-thumbs li .date { + display: block; + overflow: hidden; + width: 90px; + white-space: nowrap; +} + +.mediamanager-thumbs li .date { + font-style: italic; + white-space: normal; +} + +.mediamanager-thumbs li input[type=checkbox] { + display: none; + float: left; + margin: 3px; +} + +.mediamanager-thumbs li:hover input[type=checkbox], +.mediamanager-thumbs li input[type=checkbox]:checked { + display: block; +} + +/* Files list view */ + +.mediamanager-list li { + list-style: none; + display: block; + position: relative; + max-height: 50px; + margin: 0; + margin-bottom: 3px; +} + +.mediamanager-list li:nth-child(2n+1) { + background-color: __background_neu__; +} + +.mediamanager-list li .image, +.mediamanager-list li .image1 { + width: 10%; + display: block; + overflow: hidden; + float: left; + height: 40px; + text-align: center; +} + +.mediamanager-list li .image0 { + display: none; +} + +.mediamanager-list li .name, +.mediamanager-list li .size, +.mediamanager-list li .filesize, +.mediamanager-list li .date { + overflow: hidden; + float: left; + width: 19%; + margin-left: 1%; + white-space: nowrap; +} + +.mediamanager-list li .date { + font-style: italic; + white-space: normal; +} + +.mediamanager-list .icon { + max-width: 16px; + max-height: 16px; +} + +.mediamanager-list li .image span, +.mediamanager-list li .image1 span { + vertical-align: middle; + text-align: center; + display: table-cell; + width: 100px; + height: 40px; +} + +.mediamanager-list li input[type=checkbox] { + display: none; + float: left; + margin: 3px; +} + +.mediamanager-list li:hover input[type=checkbox], +.mediamanager-list li input[type=checkbox]:checked { + display: block; +} + +/* Upload panel */ + +#mediamanager__layout div.upload { + padding-bottom: 0.5em; +} + +#media__content #mediamanager__uploader { + border-bottom: 1px solid __border__; + padding-bottom: 0.5em; +} + +/* File preview */ + +.mediamanager__preview, +.mediamanager__preview_buttons { + text-align: center; + margin-bottom: 5px; +} + +.mediamanager__preview img { + width: 99%; +} + +/* Meta data edit form */ + +form.meta textarea.edit { + height: 8em; + width: 95%; + min-width: 95%; + max-width: 95%; +} + +/* Revisions form */ + +#mediamanager__details #page__revisions ul { + margin-left: 10px; + list-style-type: none; +} + +#mediamanager__details #page__revisions ul li div.li div { + font-size: 90%; + color: __text_neu__; + padding-left: 18px; +} + +#mediamanager__details #page__revisions ul li div.li input { + position: relative; + top: 1px; +} + +/* File diff */ + +#mediamanager__diff { + margin-top: 10px; +} + +#mediamanager__diff_table { + padding: 0; + margin: 0; + margin-top: 10px; +} + +#mediamanager__diff_table li { + width: 48%; + display: inline-block; + margin: 0; + margin-bottom: 10px; + padding: 2px; + vertical-align: top; + zoom: 1; + color: __text__; +} + +* html #mediamanager__diff_table li { + display: inline; +} + +*+html #mediamanager__diff_table li { + display: inline; +} + +/* For IE7 */ +*:first-child+html #mediamanager__diff_table li { + width: 45%; +} + +/* Image diff */ + +#mediamanager__layout dl.img_tags dd.highlighted{ + background-color: __highlight__; +} + +#mediamanager__form_diffview { + margin-bottom: 10px; +} + +#mediamanager__diff_layout { + position: relative; +} + +#mediamanager__diff_layout div { + position: absolute; + top: 0; + left: 0; +} + +#mediamanager__diff_opacity_image1, +#mediamanager__diff_portions_image1 { + width: 97%; +} + +#mediamanager__diff_layout div img { + width: 100%; +} + +#mediamanager__diff_opacity_image2 { + width: 97%; + -moz-opacity: 0.5; + -khtml-opacity: 0.5; + opacity: 0.5; +} + +#mediamanager__diff_portions_image2 { + width: 97%; + border-right: 1px solid red; + overflow: hidden; +} + +#mediamanager__opacity_slider, +#mediamanager__portions_slider { + margin: 10px; + width: 95%; +} diff --git a/lib/tpl/default/style.ini b/lib/tpl/default/style.ini index c5b2c31a5..05914012e 100644 --- a/lib/tpl/default/style.ini +++ b/lib/tpl/default/style.ini @@ -15,6 +15,8 @@ _mediaoptions.css = screen _admin.css = screen _linkwiz.css = screen _subscription.css = screen +mediamanager.css = screen +_fileuploader.css = screen rtl.css = rtl print.css = print |