From 54f0d03f0b44778b701aaddf22ccfe6105bbcfa2 Mon Sep 17 00:00:00 2001 From: Troex Nevelin Date: Sun, 4 Mar 2012 10:45:53 +0200 Subject: [PATCH 01/16] rotate in progress --- css/toolbar.css | 5 +- img/toolbar.png | Bin 16028 -> 17160 bytes js/commands/resize.js | 52 ++++++++++++-- js/i18n/elfinder.en.js | 14 ++-- js/i18n/elfinder.jp.js | 8 ++- php/elFinder.class.php | 6 +- php/elFinderVolumeDriver.class.php | 111 ++++++++++++++++++++++++----- 7 files changed, 162 insertions(+), 34 deletions(-) diff --git a/css/toolbar.css b/css/toolbar.css index 8ece0cbbc..dd7bc04e2 100644 --- a/css/toolbar.css +++ b/css/toolbar.css @@ -108,8 +108,9 @@ .elfinder-button-icon-help { background-position: 0 -480px; } .elfinder-button-icon-resize { background-position: 0 -512px; } .elfinder-button-icon-search { background-position: 0 -561px; } -.elfinder-button-icon-sort { background-position: 0 -577px; } - +.elfinder-button-icon-sort { background-position: 0 -577px; } +.elfinder-button-icon-rotate-r { background-position: 0 -625px; } +.elfinder-button-icon-rotate-l { background-position: 0 -641px; } /* button with dropdown menu*/ .elfinder .elfinder-menubutton { overflow:visible; } diff --git a/img/toolbar.png b/img/toolbar.png index ddfb9bc6fd85c28a5251e05e60deef0fd408be36..3b51326dbb0aea89dffa8d0b9ae10c871a6be371 100644 GIT binary patch literal 17160 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ⅅ#Dd1aC?ihZEaVlH=ue;U^u^1RyEQOD-Sx~|((*>*+!rD> zNoO7;B85)V7ZzYs59jA+x9)8Pkeup1Zd3N+!j5F;Mv zEiP}4{tvt7+e!gO(|@od9;Ew1T^zNsLSkKT;t7#C_ctDqX!gJO9=CBFT#*34Zlkl4 z-dsz$y4F+i?bR~+lzOD=*Ut?G3p!o5=LXviEAo!Iw0-!vH1}JypRG?>{%Al6y?f`s zm5c3G09>t`$3rNLvG~+MTj+Sm1F(&BMBNw7_X<)U7#7gJ-p$ZfsbzW$eN-o9^1hfY z6l}UL+(DsDWdZ>f;p*hCWdUJSuq_0+diT#$p+V`gEZIv7E)Sy}WZh%5_<{lTKG_AC z%{FhM_Gz*#LRDRLZ*dq;7!QsIqrMp2W3(KaCucx{6841O@k(ga6|JTr`>LucamWNd zbmL6YHXV85dxju_4Dl0wmXBYx$(8I>oiG-W>R>gUva!+F(FPt&mv+97kD0cSq?6kx zB{X0&L;-v*Q4H|=`?%MqW_jKH)90gM>&^}1^u1e*Qt0$rFBC-z4MJ3UPbWTW&0_({ zF^A-AH+Du8yohC54Zj;8A$j_=7Icwg>g%yYEoXk1L72%IkL|AbOx6xq0P#PPXy1`J z44+|a($DT1KW2'), uiresize = $('
'), uicrop = $('
'), + uibuttonset = '
', + uibutton = '
', + uiseparator = '', + uirotate = $('
'), + uideg270 = $(uibutton).attr('title',fm.i18n('rotate-cw')).append($('') + .click(function(){ + var val = (parseInt(degree.val()) || 0) - 90; + //val = parseInt(val / 90) * 90; + if (val < 0) { + val = 360 + val; + } + degree.val(val); + })), + uideg90 = $(uibutton).attr('title',fm.i18n('rotate-ccw')).append($('') + .click(function(){ + var val = (parseInt(degree.val()) || 0) + 90; + //val = parseInt(val / 90) * 90; + if (val >= 360) { + val = val - 360; + } + degree.val(val); + })), uiprop = $(''), reset = $('
'), uitype = $('
'+fm.i18n('mode')+'
') - .append('') - .append(''), + .append('') + .append('') + .append(''), type = $('input', uitype) .change(function() { uiresize.add(uicrop).toggle(); resetView(); resizable(true); croppable(true); + // TODO rotateable var val = $('input:checked', uitype).val(); if (val == 'resize') { @@ -82,6 +106,7 @@ elFinder.prototype.commands.resize = function() { pointY = $(input), offsetX = $(input), offsetY = $(input), + degree = $(''), ratio = 1, prop = 1, owidth = 0, @@ -95,7 +120,7 @@ elFinder.prototype.commands.resize = function() { owidth = img.width(); oheight = img.height(); - ratio = (owidth/oheight).toFixed(2); + ratio = owidth/oheight; resize.updateView(owidth, oheight); rhandle.append(img.show()).show(); @@ -246,7 +271,7 @@ elFinder.prototype.commands.resize = function() { } }, save = function() { - var w, h, x, y; + var w, h, x, y, d; var mode = $('input:checked', uitype).val(); width.add(height).change(); @@ -259,6 +284,13 @@ elFinder.prototype.commands.resize = function() { h = parseInt(offsetY.val()) || 0; x = parseInt(pointX.val()) || 0; y = parseInt(pointY.val()) || 0; + } else if (mode = 'rotate') { + w = parseInt(offsetX.val()) || 0; + h = parseInt(offsetY.val()) || 0; + d = parseInt(degree.val()) || 0; + if (d < 0 || d > 360) { + return fm.error('Invalid rotate degree'); + } } if (w <= 0 || h <= 0) { @@ -279,6 +311,7 @@ elFinder.prototype.commands.resize = function() { height : h, x : x, y : y, + degree : d, mode : mode }, notify : {type : 'resize', cnt : 1} @@ -307,13 +340,22 @@ elFinder.prototype.commands.resize = function() { uicrop.append($(row).append($(label).text('X')).append(pointX)) .append($(row).append($(label).text('Y')).append(pointY)) .append($(row).append($(label).text(fm.i18n('width'))).append(offsetX)) - .append($(row).append($(label).text(fm.i18n('height'))).append(offsetY)) + .append($(row).append($(label).text(fm.i18n('height'))).append(offsetY)); + + uirotate.append($(row) + .append($(label).text(fm.i18n('rotate'))) + .append($('
') + .append(degree) + .append($('').text(fm.i18n('degree'))) + ).append($(uibuttonset).append(uideg270).append($(uiseparator)).append(uideg90)) + ); dialog.append(uitype); control.append($(row)) .append(uiresize) .append(uicrop.hide()) + .append(uirotate.hide()) .find('input,select').attr('disabled', 'disabled'); rhandle.append('
') diff --git a/js/i18n/elfinder.en.js b/js/i18n/elfinder.en.js index 9e1fd8f57..2be7105ea 100644 --- a/js/i18n/elfinder.en.js +++ b/js/i18n/elfinder.en.js @@ -1,7 +1,7 @@ /** * English translation * @author Troex Nevelin - * @version 2011-07-10 + * @version 2012-02-25 */ if (elFinder && elFinder.prototype && typeof(elFinder.prototype.i18) == 'object') { elFinder.prototype.i18.en = { @@ -101,7 +101,7 @@ if (elFinder && elFinder.prototype && typeof(elFinder.prototype.i18) == 'object' 'cmdup' : 'Go to parent directory', 'cmdupload' : 'Upload files', 'cmdview' : 'View', - 'cmdresize' : 'Resize image', + 'cmdresize' : 'Resize & Rotate', 'cmdsort' : 'Sort', /*********************************** buttons ***********************************/ @@ -226,9 +226,13 @@ if (elFinder && elFinder.prototype && typeof(elFinder.prototype.i18) == 'object' 'scale' : 'Scale', 'width' : 'Width', 'height' : 'Height', - 'mode' : 'Mode', - 'resize' : 'Resize', - 'crop' : 'Crop', + 'mode' : 'Mode', + 'resize' : 'Resize', + 'crop' : 'Crop', + 'rotate' : 'Rotate', + 'rotate-l90' : '90-degree left rotation', + 'rotate-r90' : '90-degree right rotation', + 'deg' : 'Deg', /********************************** mimetypes **********************************/ 'kindUnknown' : 'Unknown', diff --git a/js/i18n/elfinder.jp.js b/js/i18n/elfinder.jp.js index 2cab79c8a..d8b96d905 100644 --- a/js/i18n/elfinder.jp.js +++ b/js/i18n/elfinder.jp.js @@ -1,7 +1,7 @@ /** * Japanese translation * @author Tomoaki Yoshida - * @version 2012-02-22 + * @version 2012-02-25 */ if (elFinder && elFinder.prototype && typeof(elFinder.prototype.i18) == 'object') { elFinder.prototype.i18.jp = { @@ -101,7 +101,7 @@ if (elFinder && elFinder.prototype && typeof(elFinder.prototype.i18) == 'object' 'cmdup' : '親ディレクトリーへ移動', 'cmdupload' : 'ファイルアップロード', 'cmdview' : 'ビュー', - 'cmdresize' : 'リサイズ', + 'cmdresize' : 'リサイズと回転', 'cmdsort' : 'ソート', /*********************************** buttons ***********************************/ @@ -229,6 +229,10 @@ if (elFinder && elFinder.prototype && typeof(elFinder.prototype.i18) == 'object' 'mode' : 'モード', 'resize' : 'リサイズ', 'crop' : '切り抜き', + 'rotate' : '回転', + 'rotate-l90' : '90度左回転', + 'rotate-r90' : '90度右回転', + 'deg' : '度', /********************************** mimetypes **********************************/ 'kindUnknown' : '不明', diff --git a/php/elFinder.class.php b/php/elFinder.class.php index 2de5260dc..2d07327f4 100644 --- a/php/elFinder.class.php +++ b/php/elFinder.class.php @@ -67,7 +67,7 @@ class elFinder { 'search' => array('q' => true, 'mimes' => false), 'info' => array('targets' => true), 'dim' => array('target' => true), - 'resize' => array('target' => true, 'width' => true, 'height' => true, 'mode' => false, 'x' => false, 'y' => false) + 'resize' => array('target' => true, 'width' => true, 'height' => true, 'mode' => false, 'x' => false, 'y' => false, 'degree' => false) ); /** @@ -1020,13 +1020,15 @@ class elFinder { $x = (int)$args['x']; $y = (int)$args['y']; $mode = $args['mode']; + $bg = null; + $degree = (int)$args['degree']; if (($volume = $this->volume($target)) == false || ($file = $volume->file($target)) == false) { return array('error' => $this->error(self::ERROR_RESIZE, '#'.$target, self::ERROR_FILE_NOT_FOUND)); } - return ($file = $volume->resize($target, $width, $height, $x, $y, $mode)) + return ($file = $volume->resize($target, $width, $height, $x, $y, $mode, $bg, $degree)) ? array('changed' => array($file)) : array('error' => $this->error(self::ERROR_RESIZE, $volume->path($target), $volume->error())); } diff --git a/php/elFinderVolumeDriver.class.php b/php/elFinderVolumeDriver.class.php index 18eebcefd..a264fa9cf 100644 --- a/php/elFinderVolumeDriver.class.php +++ b/php/elFinderVolumeDriver.class.php @@ -1601,12 +1601,16 @@ abstract class elFinderVolumeDriver { * @param string $hash image file * @param int $width new width * @param int $height new height - * @param bool $crop crop image + * @param int $x X start poistion for crop + * @param int $y Y start poistion for crop + * @param string $mode action how to mainpulate image * @return array|false * @author Dmitry (dio) Levashov * @author Alexey Sukhotin + * @author nao-pon + * @author Troex Nevelin **/ - public function resize($hash, $width, $height, $x, $y, $mode = 'resize', $bg='') { + public function resize($hash, $width, $height, $x, $y, $mode = 'resize', $bg = '', $degree = 0) { if ($this->commandDisabled('resize')) { return $this->setError(elFinder::ERROR_PERM_DENIED); } @@ -1636,14 +1640,18 @@ abstract class elFinderVolumeDriver { break; case 'fitsquare': - $result = $this->imgSquareFit($path, $width, $height, 'center', 'middle', $bg ? $bg : $this->options['tmbBgColor']); + $result = $this->imgSquareFit($path, $width, $height, 'center', 'middle', ($bg ? $bg : $this->options['tmbBgColor'])); break; - + + case 'rotate': + $result = $this->imgRotate($path, $degree, ($bg ? $bg : $this->options['tmbBgColor'])); + break; + default: $result = $this->imgResize($path, $width, $height, false, true); - break; - } - + break; + } + if ($result) { if (!empty($file['tmb']) && $file['tmb'] != "1") { $this->rmTmb($file['tmb']); @@ -2612,7 +2620,8 @@ abstract class elFinderVolumeDriver { * @param bool $resizeByBiggerSide resize image based on bigger side if true * @param string $destformat image destination format * @return string|false - * @author Dmitry (dio) Levashov, Alexey Sukhotin + * @author Dmitry (dio) Levashov + * @author Alexey Sukhotin **/ protected function imgResize($path, $width, $height, $keepProportions = false, $resizeByBiggerSide = true, $destformat = null) { if (($s = @getimagesize($path)) == false) { @@ -2718,7 +2727,8 @@ abstract class elFinderVolumeDriver { * @param bool $y crop top offset * @param string $destformat image destination format * @return string|false - * @author Dmitry (dio) Levashov, Alexey Sukhotin + * @author Dmitry (dio) Levashov + * @author Alexey Sukhotin **/ protected function imgCrop($path, $width, $height, $x, $y, $destformat = null) { if (($s = @getimagesize($path)) == false) { @@ -2793,9 +2803,10 @@ abstract class elFinderVolumeDriver { * @param string $bgcolor square background color in #rrggbb format * @param string $destformat image destination format * @return string|false - * @author Dmitry (dio) Levashov, Alexey Sukhotin + * @author Dmitry (dio) Levashov + * @author Alexey Sukhotin **/ - protected function imgSquareFit($path, $width, $height, $align = 'center', $valign = 'middle', $bgcolor = '#0000ff', $destformat = null) { + protected function imgSquareFit($path, $width, $height, $align = 'center', $valign = 'middle', $bgcolor = '#0000ff', $destformat = null) { if (($s = @getimagesize($path)) == false) { return false; } @@ -2808,11 +2819,9 @@ abstract class elFinderVolumeDriver { switch ($this->imgLib) { case 'imagick': - try { $img = new imagick($path); } catch (Exception $e) { - return false; } @@ -2856,7 +2865,7 @@ abstract class elFinderVolumeDriver { if (!imagecopy($tmp, $img, $x, $y, 0, 0, $s[0], $s[1])) { return false; } - + if ($destformat == 'jpg' || ($destformat == null && $s['mime'] == 'image/jpeg')) { $result = imagejpeg($tmp, $path, 100); } else if ($destformat == 'gif' || ($destformat == null && $s['mime'] == 'image/gif')) { @@ -2869,14 +2878,80 @@ abstract class elFinderVolumeDriver { imagedestroy($tmp); return $result ? $path : false; - } break; } - return false; - } - + return false; + } + + /** + * Rotate image + * + * @param string $path image file + * @param int $degree rotete degrees + * @param string $bgcolor square background color in #rrggbb format + * @param string $destformat image destination format + * @return string|false + * @author nao-pon + * @author Troex Nevelin + **/ + protected function imgRotate($path, $degree, $bgcolor = '#ffffff', $destformat = null) { + if (($s = @getimagesize($path)) == false) { + return false; + } + + $result = false; + + switch ($this->imgLib) { + case 'imagick': + try { + $img = new imagick($path); + } catch (Exception $e) { + return false; + } + + $img->rotateImage(new ImagickPixel($bgcolor), $degree); + $result = $img->writeImage($path); + return $result ? $path : false; + + break; + + case 'gd': + if ($s['mime'] == 'image/jpeg') { + $img = imagecreatefromjpeg($path); + } elseif ($s['mime'] == 'image/png') { + $img = imagecreatefrompng($path); + } elseif ($s['mime'] == 'image/gif') { + $img = imagecreatefromgif($path); + } elseif ($s['mime'] == 'image/xbm') { + $img = imagecreatefromxbm($path); + } + + $degree = 360 - $degree; + list($r, $g, $b) = sscanf($bgcolor, "#%02x%02x%02x"); + $bgcolor = imagecolorallocate($img, $r, $g, $b); + $tmp = imageRotate($img, $degree, (int)$bgcolor); + + if ($destformat == 'jpg' || ($destformat == null && $s['mime'] == 'image/jpeg')) { + $result = imagejpeg($tmp, $path, 100); + } else if ($destformat == 'gif' || ($destformat == null && $s['mime'] == 'image/gif')) { + $result = imagegif($tmp, $path, 7); + } else { + $result = imagepng($tmp, $path, 7); + } + + imageDestroy($img); + imageDestroy($tmp); + + return $result ? $path : false; + + break; + } + + return false; + } + /** * Execute shell command * From b5f54d2172672a2885da8a21b278c6010a680cf5 Mon Sep 17 00:00:00 2001 From: Troex Nevelin Date: Sun, 4 Mar 2012 11:06:00 +0200 Subject: [PATCH 02/16] fix strict warning --- php/elFinderVolumeMySQL.class.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/php/elFinderVolumeMySQL.class.php b/php/elFinderVolumeMySQL.class.php index a540ca277..1a17c71d5 100644 --- a/php/elFinderVolumeMySQL.class.php +++ b/php/elFinderVolumeMySQL.class.php @@ -231,7 +231,7 @@ class elFinderVolumeMySQL extends elFinderVolumeDriver { * @author Dmitry (dio) Levashov * @author Alexey Sukhotin **/ - public function resize($hash, $width, $height, $x, $y, $mode = 'resize', $bg='') { + public function resize($hash, $width, $height, $x, $y, $mode = 'resize', $bg = '', $degree = 0) { if ($this->commandDisabled('resize')) { return $this->setError(elFinder::ERROR_PERM_DENIED); } From 3cc21ac53ca853c4ec1f4d0a85b937f50c2b7724 Mon Sep 17 00:00:00 2001 From: ph0enix Date: Tue, 6 Mar 2012 06:53:14 +0400 Subject: [PATCH 03/16] Resize UI update --- js/commands/resize.js | 62 +++++++- js/i18n/elfinder.en.js | 4 +- php/elFinderVolumeDriver.class.php | 238 +++++++++++------------------ 3 files changed, 152 insertions(+), 152 deletions(-) diff --git a/js/commands/resize.js b/js/commands/resize.js index d8f98aea4..9ec2232b2 100644 --- a/js/commands/resize.js +++ b/js/commands/resize.js @@ -29,6 +29,7 @@ elFinder.prototype.commands.resize = function() { spinner = $('
'+fm.i18n('ntfloadimg')+'
'), rhandle = $('
'), rhandlec = $('
'), + rhandler = $('
'), uiresize = $('
'), uicrop = $('
'), uibuttonset = '
', @@ -43,6 +44,7 @@ elFinder.prototype.commands.resize = function() { val = 360 + val; } degree.val(val); + rotatepreview(val); })), uideg90 = $(uibutton).attr('title',fm.i18n('rotate-ccw')).append($('') .click(function(){ @@ -52,6 +54,7 @@ elFinder.prototype.commands.resize = function() { val = val - 360; } degree.val(val); + rotatepreview(val); })), uiprop = $(''), reset = $('
'), @@ -61,18 +64,32 @@ elFinder.prototype.commands.resize = function() { .append(''), type = $('input', uitype) .change(function() { - uiresize.add(uicrop).toggle(); + var val = $('input:checked', uitype).val(); + //uiresize.add(uicrop).toggle(); + resetView(); resizable(true); croppable(true); + rotateable(true); // TODO rotateable - var val = $('input:checked', uitype).val(); + if (val == 'resize') { + uiresize.show(); + uirotate.hide(); + uicrop.hide(); resizable(); } else if (val == 'crop') { + uirotate.hide(); + uiresize.hide(); + uicrop.show(); croppable(); + } else if (val == 'rotate') { + uiresize.hide(); + uicrop.hide(); + uirotate.show(); + rotateable(); } }), constr = $('') @@ -168,6 +185,7 @@ elFinder.prototype.commands.resize = function() { spinner.text('Unable to load image').css('background', 'transparent'); }), imgc = $(''), + imgr = $(''), resetView = function() { width.val(owidth); height.val(oheight); @@ -270,6 +288,38 @@ elFinder.prototype.commands.resize = function() { } } }, + rotatepreview = function(degree) { + + var img = $('.elfinder-resize-preview img'); + img.css('-moz-transform', 'rotate('+degree+'deg)'); + img.css('-webkit-transform', 'rotate('+degree+'deg)'); + img.css('-o-transform', 'rotate('+degree+'deg)'); + img.css('-ms-transform', 'rotate('+degree+'deg)'); + }, + rotateable = function(destroy) { + if ($.fn.draggable && $.fn.resizable) { + if (destroy) { + rhandler.resizable('destroy'); + rhandler.draggable('destroy'); + rhandler.hide(); + imgr.hide(); + } + else { + imgr.show() + + .width(img.width()) + .height(img.height()); + $('.elfinder-resize-preview img').show(); + rhandler.show() + .css('position', 'absolute') + .width(imgr.width()) + .height(imgr.height()) + .offset(imgr.offset()); + + //rotate.update(); + } + } + }, save = function() { var w, h, x, y, d; var mode = $('input:checked', uitype).val(); @@ -285,8 +335,8 @@ elFinder.prototype.commands.resize = function() { x = parseInt(pointX.val()) || 0; y = parseInt(pointY.val()) || 0; } else if (mode = 'rotate') { - w = parseInt(offsetX.val()) || 0; - h = parseInt(offsetY.val()) || 0; + w = parseInt(width.val()) || 0; + h = parseInt(height.val()) || 0; d = parseInt(degree.val()) || 0; if (d < 0 || d > 360) { return fm.error('Invalid rotate degree'); @@ -297,7 +347,7 @@ elFinder.prototype.commands.resize = function() { return fm.error('Invalid image size'); } - if (w == owidth && h == oheight) { + if (w == owidth && h == oheight && mode != 'rotate') { return fm.error('Image size not changed'); } @@ -350,6 +400,8 @@ elFinder.prototype.commands.resize = function() { ).append($(uibuttonset).append(uideg270).append($(uiseparator)).append(uideg90)) ); + + dialog.append(uitype); control.append($(row)) diff --git a/js/i18n/elfinder.en.js b/js/i18n/elfinder.en.js index 2be7105ea..39eb5b589 100644 --- a/js/i18n/elfinder.en.js +++ b/js/i18n/elfinder.en.js @@ -230,8 +230,8 @@ if (elFinder && elFinder.prototype && typeof(elFinder.prototype.i18) == 'object' 'resize' : 'Resize', 'crop' : 'Crop', 'rotate' : 'Rotate', - 'rotate-l90' : '90-degree left rotation', - 'rotate-r90' : '90-degree right rotation', + 'rotate-cw' : '90-degree left rotation', + 'rotate-ccw' : '90-degree right rotation', 'deg' : 'Deg', /********************************** mimetypes **********************************/ diff --git a/php/elFinderVolumeDriver.class.php b/php/elFinderVolumeDriver.class.php index a264fa9cf..9d1cfb96f 100644 --- a/php/elFinderVolumeDriver.class.php +++ b/php/elFinderVolumeDriver.class.php @@ -153,63 +153,61 @@ abstract class elFinderVolumeDriver { protected $options = array( 'id' => '', // root directory path - 'path' => '', + 'path' => '', // open this path on initial request instead of root path - 'startPath' => '', - // how many subdirs levels return per request - 'treeDeep' => 1, - // root url, not set to disable sending URL to client (replacement for old "fileURL" option) - 'URL' => '', - // directory separator. required by client to show paths correctly + 'startPath' => '', + // how many subdirs levels return per request + 'treeDeep' => 1, + // root url, not set to disable sending URL to client (replacement for old "fileURL" option) + 'URL' => '', + // directory separator. required by client to show paths correctly 'separator' => DIRECTORY_SEPARATOR, // library to crypt/uncrypt files names (not implemented) 'cryptLib' => '', // how to detect files mimetypes. (auto/internal/finfo/mime_content_type) - 'mimeDetect' => 'auto', + 'mimeDetect' => 'auto', // mime.types file path (for mimeDetect==internal) - 'mimefile' => '', - // directory for thumbnails - 'tmbPath' => '.tmb', + 'mimefile' => '', + // directory for thumbnails + 'tmbPath' => '.tmb', // mode to create thumbnails dir - 'tmbPathMode' => 0777, - // thumbnails dir URL. Set it if store thumbnails outside root directory - 'tmbURL' => '', - // thumbnails size (px) - 'tmbSize' => 48, + 'tmbPathMode' => 0777, + // thumbnails dir URL. Set it if store thumbnails outside root directory + 'tmbURL' => '', + // thumbnails size (px) + 'tmbSize' => 48, // thumbnails crop (true - crop, false - scale image to fit thumbnail size) 'tmbCrop' => true, // thumbnails background color (hex #rrggbb or 'transparent') 'tmbBgColor' => '#ffffff', // image manipulations library - 'imgLib' => 'auto', - // how frequiently clean thumbnails dir (0 - never, 100 - every init request) - 'tmbCleanProb' => 0, + 'imgLib' => 'auto', // on paste file - if true - old file will be replaced with new one, if false new file get name - original_name-number.ext - 'copyOverwrite' => true, - // if true - join new and old directories content on paste - 'copyJoin' => true, - // on upload - if true - old file will be replaced with new one, if false new file get name - original_name-number.ext - 'uploadOverwrite' => true, + 'copyOverwrite' => true, + // if true - join new and old directories content on paste + 'copyJoin' => true, + // on upload - if true - old file will be replaced with new one, if false new file get name - original_name-number.ext + 'uploadOverwrite' => true, // mimetypes allowed to upload - 'uploadAllow' => array('all'), + 'uploadAllow' => array(), // mimetypes not allowed to upload - 'uploadDeny' => array(), + 'uploadDeny' => array(), // order to proccess uploadAllow and uploadDeny options - 'uploadOrder' => 'deny,allow', + 'uploadOrder' => array('deny', 'allow'), // maximum upload file size. NOTE - this is size for every uploaded files 'uploadMaxSize' => 0, // files dates format - 'dateFormat' => 'j M Y H:i', + 'dateFormat' => 'j M Y H:i', // files time format 'timeFormat' => 'H:i', // if true - every folder will be check for children folders, otherwise all folders will be marked as having subfolders 'checkSubfolders' => true, // allow to copy from this volume to other ones? - 'copyFrom' => true, + 'copyFrom' => true, // allow to copy from other volumes to this one? - 'copyTo' => true, + 'copyTo' => true, // list of commands disabled on this root - 'disabled' => array(), + 'disabled' => array(), // regexp or function name to validate new file name 'acceptedName' => '/^\w[\w\s\.\%\-\(\)\[\]]*$/u', // function/class method to control files permissions @@ -217,15 +215,15 @@ abstract class elFinderVolumeDriver { // some data required by access control 'accessControlData' => null, // default permissions. not set hidden/locked here - take no effect - 'defaults' => array( + 'defaults' => array( 'read' => true, 'write' => true ), // files attributes - 'attributes' => array(), - // Allowed archive's mimetypes to create. Leave empty for all available types. - 'archiveMimes' => array(), - // Manual config for archivers. See example below. Leave empty for auto detect + 'attributes' => array(), + // Allowed archive's mimetypes to create. Leave empty for all available types. + 'archiveMimes' => array(), + // Manual config for archivers. See example below. Leave empty for auto detect 'archivers' => array(), // required to fix bug on macos 'utf8fix' => false, @@ -233,7 +231,7 @@ abstract class elFinderVolumeDriver { 'utf8patterns' => array("\u0438\u0306", "\u0435\u0308", "\u0418\u0306", "\u0415\u0308", "\u00d8A", "\u030a"), 'utf8replace' => array("\u0439", "\u0451", "\u0419", "\u0401", "\u00d8", "\u00c5") ); - + /** * Defaults permissions * @@ -605,9 +603,13 @@ abstract class elFinderVolumeDriver { $this->uploadDeny = isset($this->options['uploadDeny']) && is_array($this->options['uploadDeny']) ? $this->options['uploadDeny'] : array(); - - $parts = explode(',', isset($this->options['uploadOrder']) ? $this->options['uploadOrder'] : 'deny,allow'); - $this->uploadOrder = array(trim($parts[0]), trim($parts[1])); + + if (is_string($this->options['uploadOrder'])) { // telephat_mode on, compatibility with 1.x + $parts = explode(',', isset($this->options['uploadOrder']) ? $this->options['uploadOrder'] : 'deny,allow'); + $this->uploadOrder = array(trim($parts[0]), trim($parts[1])); + } else { // telephat_mode off + $this->uploadOrder = $this->options['uploadOrder']; + } if (!empty($this->options['uploadMaxSize'])) { $size = ''.$this->options['uploadMaxSize']; @@ -861,14 +863,23 @@ abstract class elFinderVolumeDriver { /** * Return true if mime is required mimes list * - * @param string $mime mime type to check - * @param array $mimes allowed mime types list or not set to use client mimes list - * @return bool + * @param string $mime mime type to check + * @param array $mimes allowed mime types list or not set to use client mimes list + * @param bool|null $empty what to return on empty list + * @return bool|null * @author Dmitry (dio) Levashov + * @author Troex Nevelin **/ - public function mimeAccepted($mime, $mimes=null) { - $mimes = is_array($mimes) ? $mimes : $this->onlyMimes; - return empty($mimes) || $mime == 'directory' || in_array($mime, $mimes) || in_array(substr($mime, 0, strpos($mime, '/')), $mimes); + public function mimeAccepted($mime, $mimes = array(), $empty = true) { + $mimes = !empty($mimes) ? $mimes : $this->onlyMimes; + if (empty($mimes)) { + return $empty; + } + return $mime == 'directory' + || in_array('all', $mimes) + || in_array('All', $mimes) + || in_array($mime, $mimes) + || in_array(substr($mime, 0, strpos($mime, '/')), $mimes); } /** @@ -1320,14 +1331,26 @@ abstract class elFinderVolumeDriver { if ($mime == 'unknown' && $this->mimeDetect == 'internal') { $mime = elFinderVolumeDriver::mimetypeInternalDetect($name); } - $allow = in_array('all', $this->uploadAllow) || $this->mimeAccepted($mime, $this->uploadAllow); - $deny = in_array('all', $this->uploadDeny) || $this->mimeAccepted($mime, $this->uploadDeny); - // dont ask me what this mean. I forgot it, but its work :) - // for details see python connector - if (!($this->uploadOrder[0] == 'allow' ? ($allow && !$deny) : ($allow || !$deny))) { + + // logic based on http://httpd.apache.org/docs/2.2/mod/mod_authz_host.html#order + $allow = $this->mimeAccepted($mime, $this->uploadAllow, null); + $deny = $this->mimeAccepted($mime, $this->uploadDeny, null); + $upload = true; // default to allow + if (strtolower($this->uploadOrder[0]) == 'allow') { // array('allow', 'deny'), default is to 'deny' + $upload = false; // default is deny + if (!$deny && ($allow === true)) { // match only allow + $upload = true; + }// else (both match | no match | match only deny) { deny } + } else { // array('deny', 'allow'), default is to 'allow' - this is the default rule + $upload = true; // default is allow + if (($deny === true) && !$allow) { // match only deny + $upload = false; + } // else (both match | no match | match only allow) { allow } + } + if (!$upload) { return $this->setError(elFinder::ERROR_UPLOAD_FILE_MIME); } - + if ($this->uploadMaxSize > 0 && filesize($tmpPath) > $this->uploadMaxSize) { return $this->setError(elFinder::ERROR_UPLOAD_FILE_SIZE); } @@ -1601,16 +1624,12 @@ abstract class elFinderVolumeDriver { * @param string $hash image file * @param int $width new width * @param int $height new height - * @param int $x X start poistion for crop - * @param int $y Y start poistion for crop - * @param string $mode action how to mainpulate image + * @param bool $crop crop image * @return array|false * @author Dmitry (dio) Levashov * @author Alexey Sukhotin - * @author nao-pon - * @author Troex Nevelin **/ - public function resize($hash, $width, $height, $x, $y, $mode = 'resize', $bg = '', $degree = 0) { + public function resize($hash, $width, $height, $x, $y, $mode = 'resize', $bg='') { if ($this->commandDisabled('resize')) { return $this->setError(elFinder::ERROR_PERM_DENIED); } @@ -1640,18 +1659,14 @@ abstract class elFinderVolumeDriver { break; case 'fitsquare': - $result = $this->imgSquareFit($path, $width, $height, 'center', 'middle', ($bg ? $bg : $this->options['tmbBgColor'])); + $result = $this->imgSquareFit($path, $width, $height, 'center', 'middle', $bg ? $bg : $this->options['tmbBgColor']); break; - - case 'rotate': - $result = $this->imgRotate($path, $degree, ($bg ? $bg : $this->options['tmbBgColor'])); - break; - + default: $result = $this->imgResize($path, $width, $height, false, true); - break; - } - + break; + } + if ($result) { if (!empty($file['tmb']) && $file['tmb'] != "1") { $this->rmTmb($file['tmb']); @@ -2620,8 +2635,7 @@ abstract class elFinderVolumeDriver { * @param bool $resizeByBiggerSide resize image based on bigger side if true * @param string $destformat image destination format * @return string|false - * @author Dmitry (dio) Levashov - * @author Alexey Sukhotin + * @author Dmitry (dio) Levashov, Alexey Sukhotin **/ protected function imgResize($path, $width, $height, $keepProportions = false, $resizeByBiggerSide = true, $destformat = null) { if (($s = @getimagesize($path)) == false) { @@ -2727,8 +2741,7 @@ abstract class elFinderVolumeDriver { * @param bool $y crop top offset * @param string $destformat image destination format * @return string|false - * @author Dmitry (dio) Levashov - * @author Alexey Sukhotin + * @author Dmitry (dio) Levashov, Alexey Sukhotin **/ protected function imgCrop($path, $width, $height, $x, $y, $destformat = null) { if (($s = @getimagesize($path)) == false) { @@ -2803,10 +2816,9 @@ abstract class elFinderVolumeDriver { * @param string $bgcolor square background color in #rrggbb format * @param string $destformat image destination format * @return string|false - * @author Dmitry (dio) Levashov - * @author Alexey Sukhotin + * @author Dmitry (dio) Levashov, Alexey Sukhotin **/ - protected function imgSquareFit($path, $width, $height, $align = 'center', $valign = 'middle', $bgcolor = '#0000ff', $destformat = null) { + protected function imgSquareFit($path, $width, $height, $align = 'center', $valign = 'middle', $bgcolor = '#0000ff', $destformat = null) { if (($s = @getimagesize($path)) == false) { return false; } @@ -2819,9 +2831,11 @@ abstract class elFinderVolumeDriver { switch ($this->imgLib) { case 'imagick': + try { $img = new imagick($path); } catch (Exception $e) { + return false; } @@ -2865,7 +2879,7 @@ abstract class elFinderVolumeDriver { if (!imagecopy($tmp, $img, $x, $y, 0, 0, $s[0], $s[1])) { return false; } - + if ($destformat == 'jpg' || ($destformat == null && $s['mime'] == 'image/jpeg')) { $result = imagejpeg($tmp, $path, 100); } else if ($destformat == 'gif' || ($destformat == null && $s['mime'] == 'image/gif')) { @@ -2878,80 +2892,14 @@ abstract class elFinderVolumeDriver { imagedestroy($tmp); return $result ? $path : false; + } break; } - return false; - } - - /** - * Rotate image - * - * @param string $path image file - * @param int $degree rotete degrees - * @param string $bgcolor square background color in #rrggbb format - * @param string $destformat image destination format - * @return string|false - * @author nao-pon - * @author Troex Nevelin - **/ - protected function imgRotate($path, $degree, $bgcolor = '#ffffff', $destformat = null) { - if (($s = @getimagesize($path)) == false) { - return false; - } - - $result = false; - - switch ($this->imgLib) { - case 'imagick': - try { - $img = new imagick($path); - } catch (Exception $e) { - return false; - } - - $img->rotateImage(new ImagickPixel($bgcolor), $degree); - $result = $img->writeImage($path); - return $result ? $path : false; - - break; - - case 'gd': - if ($s['mime'] == 'image/jpeg') { - $img = imagecreatefromjpeg($path); - } elseif ($s['mime'] == 'image/png') { - $img = imagecreatefrompng($path); - } elseif ($s['mime'] == 'image/gif') { - $img = imagecreatefromgif($path); - } elseif ($s['mime'] == 'image/xbm') { - $img = imagecreatefromxbm($path); - } - - $degree = 360 - $degree; - list($r, $g, $b) = sscanf($bgcolor, "#%02x%02x%02x"); - $bgcolor = imagecolorallocate($img, $r, $g, $b); - $tmp = imageRotate($img, $degree, (int)$bgcolor); - - if ($destformat == 'jpg' || ($destformat == null && $s['mime'] == 'image/jpeg')) { - $result = imagejpeg($tmp, $path, 100); - } else if ($destformat == 'gif' || ($destformat == null && $s['mime'] == 'image/gif')) { - $result = imagegif($tmp, $path, 7); - } else { - $result = imagepng($tmp, $path, 7); - } - - imageDestroy($img); - imageDestroy($tmp); - - return $result ? $path : false; - - break; - } - - return false; - } - + return false; + } + /** * Execute shell command * From 5699166665c58f87ad0c1e260c47f46e5a3625c0 Mon Sep 17 00:00:00 2001 From: ph0enix Date: Tue, 6 Mar 2012 07:02:24 +0400 Subject: [PATCH 04/16] Restored broken file --- php/elFinderVolumeDriver.class.php | 238 ++++++++++++++++++----------- 1 file changed, 145 insertions(+), 93 deletions(-) diff --git a/php/elFinderVolumeDriver.class.php b/php/elFinderVolumeDriver.class.php index 9d1cfb96f..a264fa9cf 100644 --- a/php/elFinderVolumeDriver.class.php +++ b/php/elFinderVolumeDriver.class.php @@ -153,61 +153,63 @@ abstract class elFinderVolumeDriver { protected $options = array( 'id' => '', // root directory path - 'path' => '', + 'path' => '', // open this path on initial request instead of root path - 'startPath' => '', - // how many subdirs levels return per request - 'treeDeep' => 1, - // root url, not set to disable sending URL to client (replacement for old "fileURL" option) - 'URL' => '', - // directory separator. required by client to show paths correctly + 'startPath' => '', + // how many subdirs levels return per request + 'treeDeep' => 1, + // root url, not set to disable sending URL to client (replacement for old "fileURL" option) + 'URL' => '', + // directory separator. required by client to show paths correctly 'separator' => DIRECTORY_SEPARATOR, // library to crypt/uncrypt files names (not implemented) 'cryptLib' => '', // how to detect files mimetypes. (auto/internal/finfo/mime_content_type) - 'mimeDetect' => 'auto', + 'mimeDetect' => 'auto', // mime.types file path (for mimeDetect==internal) - 'mimefile' => '', - // directory for thumbnails - 'tmbPath' => '.tmb', + 'mimefile' => '', + // directory for thumbnails + 'tmbPath' => '.tmb', // mode to create thumbnails dir - 'tmbPathMode' => 0777, - // thumbnails dir URL. Set it if store thumbnails outside root directory - 'tmbURL' => '', - // thumbnails size (px) - 'tmbSize' => 48, + 'tmbPathMode' => 0777, + // thumbnails dir URL. Set it if store thumbnails outside root directory + 'tmbURL' => '', + // thumbnails size (px) + 'tmbSize' => 48, // thumbnails crop (true - crop, false - scale image to fit thumbnail size) 'tmbCrop' => true, // thumbnails background color (hex #rrggbb or 'transparent') 'tmbBgColor' => '#ffffff', // image manipulations library - 'imgLib' => 'auto', + 'imgLib' => 'auto', + // how frequiently clean thumbnails dir (0 - never, 100 - every init request) + 'tmbCleanProb' => 0, // on paste file - if true - old file will be replaced with new one, if false new file get name - original_name-number.ext - 'copyOverwrite' => true, - // if true - join new and old directories content on paste - 'copyJoin' => true, - // on upload - if true - old file will be replaced with new one, if false new file get name - original_name-number.ext - 'uploadOverwrite' => true, + 'copyOverwrite' => true, + // if true - join new and old directories content on paste + 'copyJoin' => true, + // on upload - if true - old file will be replaced with new one, if false new file get name - original_name-number.ext + 'uploadOverwrite' => true, // mimetypes allowed to upload - 'uploadAllow' => array(), + 'uploadAllow' => array('all'), // mimetypes not allowed to upload - 'uploadDeny' => array(), + 'uploadDeny' => array(), // order to proccess uploadAllow and uploadDeny options - 'uploadOrder' => array('deny', 'allow'), + 'uploadOrder' => 'deny,allow', // maximum upload file size. NOTE - this is size for every uploaded files 'uploadMaxSize' => 0, // files dates format - 'dateFormat' => 'j M Y H:i', + 'dateFormat' => 'j M Y H:i', // files time format 'timeFormat' => 'H:i', // if true - every folder will be check for children folders, otherwise all folders will be marked as having subfolders 'checkSubfolders' => true, // allow to copy from this volume to other ones? - 'copyFrom' => true, + 'copyFrom' => true, // allow to copy from other volumes to this one? - 'copyTo' => true, + 'copyTo' => true, // list of commands disabled on this root - 'disabled' => array(), + 'disabled' => array(), // regexp or function name to validate new file name 'acceptedName' => '/^\w[\w\s\.\%\-\(\)\[\]]*$/u', // function/class method to control files permissions @@ -215,15 +217,15 @@ abstract class elFinderVolumeDriver { // some data required by access control 'accessControlData' => null, // default permissions. not set hidden/locked here - take no effect - 'defaults' => array( + 'defaults' => array( 'read' => true, 'write' => true ), // files attributes - 'attributes' => array(), - // Allowed archive's mimetypes to create. Leave empty for all available types. - 'archiveMimes' => array(), - // Manual config for archivers. See example below. Leave empty for auto detect + 'attributes' => array(), + // Allowed archive's mimetypes to create. Leave empty for all available types. + 'archiveMimes' => array(), + // Manual config for archivers. See example below. Leave empty for auto detect 'archivers' => array(), // required to fix bug on macos 'utf8fix' => false, @@ -231,7 +233,7 @@ abstract class elFinderVolumeDriver { 'utf8patterns' => array("\u0438\u0306", "\u0435\u0308", "\u0418\u0306", "\u0415\u0308", "\u00d8A", "\u030a"), 'utf8replace' => array("\u0439", "\u0451", "\u0419", "\u0401", "\u00d8", "\u00c5") ); - + /** * Defaults permissions * @@ -603,13 +605,9 @@ abstract class elFinderVolumeDriver { $this->uploadDeny = isset($this->options['uploadDeny']) && is_array($this->options['uploadDeny']) ? $this->options['uploadDeny'] : array(); - - if (is_string($this->options['uploadOrder'])) { // telephat_mode on, compatibility with 1.x - $parts = explode(',', isset($this->options['uploadOrder']) ? $this->options['uploadOrder'] : 'deny,allow'); - $this->uploadOrder = array(trim($parts[0]), trim($parts[1])); - } else { // telephat_mode off - $this->uploadOrder = $this->options['uploadOrder']; - } + + $parts = explode(',', isset($this->options['uploadOrder']) ? $this->options['uploadOrder'] : 'deny,allow'); + $this->uploadOrder = array(trim($parts[0]), trim($parts[1])); if (!empty($this->options['uploadMaxSize'])) { $size = ''.$this->options['uploadMaxSize']; @@ -863,23 +861,14 @@ abstract class elFinderVolumeDriver { /** * Return true if mime is required mimes list * - * @param string $mime mime type to check - * @param array $mimes allowed mime types list or not set to use client mimes list - * @param bool|null $empty what to return on empty list - * @return bool|null + * @param string $mime mime type to check + * @param array $mimes allowed mime types list or not set to use client mimes list + * @return bool * @author Dmitry (dio) Levashov - * @author Troex Nevelin **/ - public function mimeAccepted($mime, $mimes = array(), $empty = true) { - $mimes = !empty($mimes) ? $mimes : $this->onlyMimes; - if (empty($mimes)) { - return $empty; - } - return $mime == 'directory' - || in_array('all', $mimes) - || in_array('All', $mimes) - || in_array($mime, $mimes) - || in_array(substr($mime, 0, strpos($mime, '/')), $mimes); + public function mimeAccepted($mime, $mimes=null) { + $mimes = is_array($mimes) ? $mimes : $this->onlyMimes; + return empty($mimes) || $mime == 'directory' || in_array($mime, $mimes) || in_array(substr($mime, 0, strpos($mime, '/')), $mimes); } /** @@ -1331,26 +1320,14 @@ abstract class elFinderVolumeDriver { if ($mime == 'unknown' && $this->mimeDetect == 'internal') { $mime = elFinderVolumeDriver::mimetypeInternalDetect($name); } - - // logic based on http://httpd.apache.org/docs/2.2/mod/mod_authz_host.html#order - $allow = $this->mimeAccepted($mime, $this->uploadAllow, null); - $deny = $this->mimeAccepted($mime, $this->uploadDeny, null); - $upload = true; // default to allow - if (strtolower($this->uploadOrder[0]) == 'allow') { // array('allow', 'deny'), default is to 'deny' - $upload = false; // default is deny - if (!$deny && ($allow === true)) { // match only allow - $upload = true; - }// else (both match | no match | match only deny) { deny } - } else { // array('deny', 'allow'), default is to 'allow' - this is the default rule - $upload = true; // default is allow - if (($deny === true) && !$allow) { // match only deny - $upload = false; - } // else (both match | no match | match only allow) { allow } - } - if (!$upload) { + $allow = in_array('all', $this->uploadAllow) || $this->mimeAccepted($mime, $this->uploadAllow); + $deny = in_array('all', $this->uploadDeny) || $this->mimeAccepted($mime, $this->uploadDeny); + // dont ask me what this mean. I forgot it, but its work :) + // for details see python connector + if (!($this->uploadOrder[0] == 'allow' ? ($allow && !$deny) : ($allow || !$deny))) { return $this->setError(elFinder::ERROR_UPLOAD_FILE_MIME); } - + if ($this->uploadMaxSize > 0 && filesize($tmpPath) > $this->uploadMaxSize) { return $this->setError(elFinder::ERROR_UPLOAD_FILE_SIZE); } @@ -1624,12 +1601,16 @@ abstract class elFinderVolumeDriver { * @param string $hash image file * @param int $width new width * @param int $height new height - * @param bool $crop crop image + * @param int $x X start poistion for crop + * @param int $y Y start poistion for crop + * @param string $mode action how to mainpulate image * @return array|false * @author Dmitry (dio) Levashov * @author Alexey Sukhotin + * @author nao-pon + * @author Troex Nevelin **/ - public function resize($hash, $width, $height, $x, $y, $mode = 'resize', $bg='') { + public function resize($hash, $width, $height, $x, $y, $mode = 'resize', $bg = '', $degree = 0) { if ($this->commandDisabled('resize')) { return $this->setError(elFinder::ERROR_PERM_DENIED); } @@ -1659,14 +1640,18 @@ abstract class elFinderVolumeDriver { break; case 'fitsquare': - $result = $this->imgSquareFit($path, $width, $height, 'center', 'middle', $bg ? $bg : $this->options['tmbBgColor']); + $result = $this->imgSquareFit($path, $width, $height, 'center', 'middle', ($bg ? $bg : $this->options['tmbBgColor'])); break; - + + case 'rotate': + $result = $this->imgRotate($path, $degree, ($bg ? $bg : $this->options['tmbBgColor'])); + break; + default: $result = $this->imgResize($path, $width, $height, false, true); - break; - } - + break; + } + if ($result) { if (!empty($file['tmb']) && $file['tmb'] != "1") { $this->rmTmb($file['tmb']); @@ -2635,7 +2620,8 @@ abstract class elFinderVolumeDriver { * @param bool $resizeByBiggerSide resize image based on bigger side if true * @param string $destformat image destination format * @return string|false - * @author Dmitry (dio) Levashov, Alexey Sukhotin + * @author Dmitry (dio) Levashov + * @author Alexey Sukhotin **/ protected function imgResize($path, $width, $height, $keepProportions = false, $resizeByBiggerSide = true, $destformat = null) { if (($s = @getimagesize($path)) == false) { @@ -2741,7 +2727,8 @@ abstract class elFinderVolumeDriver { * @param bool $y crop top offset * @param string $destformat image destination format * @return string|false - * @author Dmitry (dio) Levashov, Alexey Sukhotin + * @author Dmitry (dio) Levashov + * @author Alexey Sukhotin **/ protected function imgCrop($path, $width, $height, $x, $y, $destformat = null) { if (($s = @getimagesize($path)) == false) { @@ -2816,9 +2803,10 @@ abstract class elFinderVolumeDriver { * @param string $bgcolor square background color in #rrggbb format * @param string $destformat image destination format * @return string|false - * @author Dmitry (dio) Levashov, Alexey Sukhotin + * @author Dmitry (dio) Levashov + * @author Alexey Sukhotin **/ - protected function imgSquareFit($path, $width, $height, $align = 'center', $valign = 'middle', $bgcolor = '#0000ff', $destformat = null) { + protected function imgSquareFit($path, $width, $height, $align = 'center', $valign = 'middle', $bgcolor = '#0000ff', $destformat = null) { if (($s = @getimagesize($path)) == false) { return false; } @@ -2831,11 +2819,9 @@ abstract class elFinderVolumeDriver { switch ($this->imgLib) { case 'imagick': - try { $img = new imagick($path); } catch (Exception $e) { - return false; } @@ -2879,7 +2865,7 @@ abstract class elFinderVolumeDriver { if (!imagecopy($tmp, $img, $x, $y, 0, 0, $s[0], $s[1])) { return false; } - + if ($destformat == 'jpg' || ($destformat == null && $s['mime'] == 'image/jpeg')) { $result = imagejpeg($tmp, $path, 100); } else if ($destformat == 'gif' || ($destformat == null && $s['mime'] == 'image/gif')) { @@ -2892,14 +2878,80 @@ abstract class elFinderVolumeDriver { imagedestroy($tmp); return $result ? $path : false; - } break; } - return false; - } - + return false; + } + + /** + * Rotate image + * + * @param string $path image file + * @param int $degree rotete degrees + * @param string $bgcolor square background color in #rrggbb format + * @param string $destformat image destination format + * @return string|false + * @author nao-pon + * @author Troex Nevelin + **/ + protected function imgRotate($path, $degree, $bgcolor = '#ffffff', $destformat = null) { + if (($s = @getimagesize($path)) == false) { + return false; + } + + $result = false; + + switch ($this->imgLib) { + case 'imagick': + try { + $img = new imagick($path); + } catch (Exception $e) { + return false; + } + + $img->rotateImage(new ImagickPixel($bgcolor), $degree); + $result = $img->writeImage($path); + return $result ? $path : false; + + break; + + case 'gd': + if ($s['mime'] == 'image/jpeg') { + $img = imagecreatefromjpeg($path); + } elseif ($s['mime'] == 'image/png') { + $img = imagecreatefrompng($path); + } elseif ($s['mime'] == 'image/gif') { + $img = imagecreatefromgif($path); + } elseif ($s['mime'] == 'image/xbm') { + $img = imagecreatefromxbm($path); + } + + $degree = 360 - $degree; + list($r, $g, $b) = sscanf($bgcolor, "#%02x%02x%02x"); + $bgcolor = imagecolorallocate($img, $r, $g, $b); + $tmp = imageRotate($img, $degree, (int)$bgcolor); + + if ($destformat == 'jpg' || ($destformat == null && $s['mime'] == 'image/jpeg')) { + $result = imagejpeg($tmp, $path, 100); + } else if ($destformat == 'gif' || ($destformat == null && $s['mime'] == 'image/gif')) { + $result = imagegif($tmp, $path, 7); + } else { + $result = imagepng($tmp, $path, 7); + } + + imageDestroy($img); + imageDestroy($tmp); + + return $result ? $path : false; + + break; + } + + return false; + } + /** * Execute shell command * From a61343112c8e9b81ff8a6f01865e59db781bee11 Mon Sep 17 00:00:00 2001 From: nao-pon Date: Wed, 7 Mar 2012 18:13:38 +0900 Subject: [PATCH 05/16] Image rotateable with mouse drag. --- js/commands/resize.js | 393 ++++++++++++++++++++++++++++++++++------- js/i18n/elfinder.en.js | 4 +- js/i18n/elfinder.jp.js | 6 +- 3 files changed, 330 insertions(+), 73 deletions(-) diff --git a/js/commands/resize.js b/js/commands/resize.js index 9ec2232b2..9800492fc 100644 --- a/js/commands/resize.js +++ b/js/commands/resize.js @@ -29,7 +29,6 @@ elFinder.prototype.commands.resize = function() { spinner = $('
'+fm.i18n('ntfloadimg')+'
'), rhandle = $('
'), rhandlec = $('
'), - rhandler = $('
'), uiresize = $('
'), uicrop = $('
'), uibuttonset = '
', @@ -38,23 +37,13 @@ elFinder.prototype.commands.resize = function() { uirotate = $('
'), uideg270 = $(uibutton).attr('title',fm.i18n('rotate-cw')).append($('') .click(function(){ - var val = (parseInt(degree.val()) || 0) - 90; - //val = parseInt(val / 90) * 90; - if (val < 0) { - val = 360 + val; - } - degree.val(val); - rotatepreview(val); + rdegree = rdegree - 90; + rotate.update(rdegree); })), uideg90 = $(uibutton).attr('title',fm.i18n('rotate-ccw')).append($('') .click(function(){ - var val = (parseInt(degree.val()) || 0) + 90; - //val = parseInt(val / 90) * 90; - if (val >= 360) { - val = val - 360; - } - degree.val(val); - rotatepreview(val); + rdegree = rdegree + 90; + rotate.update(rdegree); })), uiprop = $(''), reset = $('
'), @@ -123,7 +112,10 @@ elFinder.prototype.commands.resize = function() { pointY = $(input), offsetX = $(input), offsetY = $(input), - degree = $(''), + degree = $('') + .change(function() { + rotate.update(); + }), ratio = 1, prop = 1, owidth = 0, @@ -131,6 +123,9 @@ elFinder.prototype.commands.resize = function() { cratio = true, pwidth = 0, pheight = 0, + rwidth = 0, + rheight = 0, + rdegree = 0, img = $('') .load(function() { spinner.remove(); @@ -143,6 +138,11 @@ elFinder.prototype.commands.resize = function() { rhandle.append(img.show()).show(); width.val(owidth); height.val(oheight); + + var r_scale = Math.min(pwidth, pheight) / Math.sqrt(Math.pow(owidth, 2) + Math.pow(oheight, 2)); + rwidth = owidth * r_scale; + rheight = oheight * r_scale; + control.find('input,select').removeAttr('disabled') .filter(':text').keydown(function(e) { var c = e.keyCode, i; @@ -177,6 +177,18 @@ elFinder.prototype.commands.resize = function() { .filter(':first').focus(); resizable(); + + // for ie mouse event problem + if ($.browser.msie && parseInt($.browser.version) <= 9) { + rhandlec.css({ + 'opacity': 0, + 'background-color': 'fff' + }); + rhandlec.find('.elfinder-resize-handle-point').css({ + 'opacity': 0.7, + 'background-color': 'fff' + }); + } reset.hover(function() { reset.toggleClass('ui-state-hover'); }).click(resetView); @@ -184,8 +196,9 @@ elFinder.prototype.commands.resize = function() { .error(function() { spinner.text('Unable to load image').css('background', 'transparent'); }), + basec = $('
'), imgc = $(''), - imgr = $(''), + imgr = $('').css('cursor', 'pointer'), resetView = function() { width.val(owidth); height.val(oheight); @@ -198,14 +211,16 @@ elFinder.prototype.commands.resize = function() { }, updateView : function(w, h) { - if (ratio >= 1 && w > pwidth) { - img.width(pwidth).height(Math.ceil(img.width()/ratio)); - } else if (h > pheight) { - img.height(pheight).width(Math.ceil(img.height()*ratio)); - } else if (w <= pwidth && h <= pheight) { - img.width(w).height(h) + if (w > pwidth || h > pheight) { + if (w > pwidth && (w - pwidth) > (h - pheight)) { + img.width(pwidth).height(Math.ceil(img.width()/ratio)); + } else { + img.height(pheight).width(Math.ceil(img.height()*ratio)); + } + } else { + img.width(w).height(h); } - + prop = img.width()/w; uiprop.text('1 : '+(1/prop).toFixed(2)) resize.updateHandle(); @@ -241,6 +256,94 @@ elFinder.prototype.commands.resize = function() { pointY.val(parseInt((rhandlec.offset().top-imgc.offset().top)/prop)); } }, + rotate = { + mouseStartAngle : 0, + imageStartAngle : 0, + imageBeingRotated : false, + + update : function(value) { + if (typeof value == 'undefined') { + rdegree = value = parseInt(degree.val()); + } + if ($.browser.msie && parseInt($.browser.version) <= 9) { + imgr.rotate(value); + } else { + imgr.animate({rotate: value + 'deg'}); + } + value = value % 360; + if (value < 0) { + value += 360; + } + degree.val(parseInt(value)); + }, + + execute : function ( e ) { + + if ( !rotate.imageBeingRotated ) return; + + var imageCentre = rotate.getCenter( imgr ); + var mouseXFromCentre = e.pageX - imageCentre[0]; + var mouseYFromCentre = e.pageY - imageCentre[1]; + var mouseAngle = Math.atan2( mouseYFromCentre, mouseXFromCentre ); + + var rotateAngle = mouseAngle - rotate.mouseStartAngle + rotate.imageStartAngle; + rotateAngle = Math.round(parseFloat(rotateAngle) * 180 / Math.PI); + + if ( e.shiftKey ) { + rotateAngle = Math.round((rotateAngle + 6)/15) * 15; + } + + imgr.rotate(rotateAngle); + + rotateAngle = rotateAngle % 360; + if (rotateAngle < 0) { + rotateAngle += 360; + } + degree.val(rotateAngle); + + return false; + }, + + start : function ( e ) { + + rotate.imageBeingRotated = true; + + var imageCentre = rotate.getCenter( imgr ); + var mouseStartXFromCentre = e.pageX - imageCentre[0]; + var mouseStartYFromCentre = e.pageY - imageCentre[1]; + rotate.mouseStartAngle = Math.atan2( mouseStartYFromCentre, mouseStartXFromCentre ); + + rotate.imageStartAngle = parseFloat(imgr.rotate()) * Math.PI / 180.0; + + $(document).mousemove( rotate.execute ); + + return false; + }, + + stop : function ( e ) { + + if ( !rotate.imageBeingRotated ) return; + + $(document).unbind( 'mousemove' , rotate.execute); + + setTimeout( function() { rotate.imageBeingRotated = false; }, 10 ); + return false; + }, + + getCenter : function ( image ) { + + var currentRotation = imgr.rotate(); + imgr.rotate(0); + + var imageOffset = imgr.offset(); + var imageCentreX = imageOffset.left + imgr.width() / 2; + var imageCentreY = imageOffset.top + imgr.height() / 2; + + imgr.rotate(currentRotation); + + return Array( imageCentreX, imageCentreY ); + } + }, resizable = function(destroy) { if ($.fn.resizable) { if (destroy) { @@ -263,24 +366,26 @@ elFinder.prototype.commands.resize = function() { if (destroy) { rhandlec.resizable('destroy'); rhandlec.draggable('destroy'); - rhandlec.hide(); - imgc.hide(); + basec.hide(); } else { - imgc.show() - .width(img.width()) + basec.show() + .width(img.width()+10) + .height(img.height()+10); + + imgc.width(img.width()) .height(img.height()); - rhandlec.show() - .css('position', 'absolute') + rhandlec.css('position', 'absolute') .width(imgc.width()) .height(imgc.height()) .offset(imgc.offset()) .resizable({ - containment : imgc, + containment : basec, resize : crop.update }) .draggable({ + handle : rhandlec, containment : imgc, drag : crop.update }); @@ -288,35 +393,18 @@ elFinder.prototype.commands.resize = function() { } } }, - rotatepreview = function(degree) { - - var img = $('.elfinder-resize-preview img'); - img.css('-moz-transform', 'rotate('+degree+'deg)'); - img.css('-webkit-transform', 'rotate('+degree+'deg)'); - img.css('-o-transform', 'rotate('+degree+'deg)'); - img.css('-ms-transform', 'rotate('+degree+'deg)'); - }, rotateable = function(destroy) { if ($.fn.draggable && $.fn.resizable) { if (destroy) { - rhandler.resizable('destroy'); - rhandler.draggable('destroy'); - rhandler.hide(); imgr.hide(); } else { imgr.show() + .width(rwidth) + .height(rheight) + .css('margin-top', (pheight-rheight)/2 + 'px') + .css('margin-left', (pwidth-rwidth)/2 + 'px'); - .width(img.width()) - .height(img.height()); - $('.elfinder-resize-preview img').show(); - rhandler.show() - .css('position', 'absolute') - .width(imgr.width()) - .height(imgr.height()) - .offset(imgr.offset()); - - //rotate.update(); } } }, @@ -335,20 +423,27 @@ elFinder.prototype.commands.resize = function() { x = parseInt(pointX.val()) || 0; y = parseInt(pointY.val()) || 0; } else if (mode = 'rotate') { - w = parseInt(width.val()) || 0; - h = parseInt(height.val()) || 0; + w = owidth; + h = oheight; d = parseInt(degree.val()) || 0; if (d < 0 || d > 360) { return fm.error('Invalid rotate degree'); } + if (d == 0 || d == 360) { + return fm.error('Image dose not rotated'); + } } - if (w <= 0 || h <= 0) { - return fm.error('Invalid image size'); - } - - if (w == owidth && h == oheight && mode != 'rotate') { - return fm.error('Image size not changed'); + if (mode != 'rotate') { + + if (w <= 0 || h <= 0) { + return fm.error('Invalid image size'); + } + + if (w == owidth && h == oheight) { + return fm.error('Image size not changed'); + } + } dialog.elfinderdialog('close'); @@ -380,7 +475,9 @@ elFinder.prototype.commands.resize = function() { rpoint = 'elfinder-resize-handle-point', src = fm.url(file.hash) ; - + + imgr.mousedown( rotate.start ); + $(document).mouseup( rotate.stop ); uiresize.append($(row).append($(label).text(fm.i18n('width'))).append(width).append(reset)) .append($(row).append($(label).text(fm.i18n('height'))).append(height)) @@ -400,8 +497,6 @@ elFinder.prototype.commands.resize = function() { ).append($(uibuttonset).append(uideg270).append($(uiseparator)).append(uideg90)) ); - - dialog.append(uitype); control.append($(row)) @@ -428,8 +523,12 @@ elFinder.prototype.commands.resize = function() { .append('
') .append('
') - preview.append(rhandlec.hide()).append(imgc.hide()); - + preview.append(basec.hide().append(imgc).append(rhandlec)); + + preview.append(imgr.hide()); + + preview.css('overflow', 'hidden'); + dialog.append(preview).append(control); buttons[fm.i18n('btnCancel')] = function() { dialog.elfinderdialog('close'); }; @@ -448,14 +547,16 @@ elFinder.prototype.commands.resize = function() { pwidth = preview.width() - (rhandle.outerWidth() - rhandle.width()); pheight = preview.height() - (rhandle.outerHeight() - rhandle.height()); - + img.attr('src', src + (src.indexOf('?') === -1 ? '?' : '&')+'_='+Math.random()); imgc.attr('src', img.attr('src')); + imgr.attr('src', img.attr('src')); }, id, dialog ; + if (!files.length || files[0].mime.indexOf('image/') === -1) { return dfrd.reject(); } @@ -468,9 +569,165 @@ elFinder.prototype.commands.resize = function() { return dfrd.resolve(); } - open(files[0], id) + open(files[0], id); return dfrd; } -} +}; + +(function ($) { + + var findProperty = function (styleObject, styleArgs) { + var i = 0 ; + for( i in styleArgs) { + if (typeof styleObject[styleArgs[i]] != 'undefined') + return styleArgs[i]; + } + styleObject[styleArgs[i]] = ''; + return styleArgs[i]; + }; + + $.cssHooks.rotate = { + get: function(elem, computed, extra) { + return $(elem).rotate(); + }, + set: function(elem, value) { + $(elem).rotate(value); + return value; + } + }; + $.cssHooks.transform = { + get: function(elem, computed, extra) { + name = findProperty( elem.style , + ['WebkitTransform', 'MozTransform', 'OTransform' , 'msTransform' , 'transform'] ); + return elem.style[name]; + }, + set: function(elem, value) { + name = findProperty( elem.style , + ['WebkitTransform', 'MozTransform', 'OTransform' , 'msTransform' , 'transform'] ); + elem.style[name] = value; + return value; + } + }; + + $.fn.rotate = function(val) { + if (typeof val == 'undefined') { + if ($.browser.opera) { + var r = this.css('transform').match(/rotate\((.*?)\)/); + return ( r && r[1])? + Math.round(parseFloat(r[1]) * 180 / Math.PI) : 0; + } else { + var r = this.css('transform').match(/rotate\((.*?)\)/); + return ( r && r[1])? parseInt(r[1]) : 0; + } + } + this.css('transform', + this.css('transform').replace(/none|rotate\(.*?\)/, '') + 'rotate(' + parseInt(val) + 'deg)'); + return this; + }; + + $.fx.step.rotate = function(fx) { + if ( fx.state == 0 ) { + fx.start = $(fx.elem).rotate(); + fx.now = fx.start; + } + $(fx.elem).rotate(fx.now); + }; + + if ($.browser.msie && parseInt($.browser.version) <= 9) { + var GetAbsoluteXY = function(element) { + var pnode = element; + var x = pnode.offsetLeft; + var y = pnode.offsetTop; + + while ( pnode.offsetParent ) { + pnode = pnode.offsetParent; + if (pnode != document.body && pnode.currentStyle['position'] != 'static') { + break; + } + if (pnode != document.body && pnode != document.documentElement) { + x -= pnode.scrollLeft; + y -= pnode.scrollTop; + } + x += pnode.offsetLeft; + y += pnode.offsetTop; + } + + return { x: x, y: y }; + }; + + var StaticToAbsolute = function (element) { + if ( element.currentStyle['position'] != 'static') { + return ; + } + + var xy = GetAbsoluteXY(element); + element.style.position = 'absolute' ; + element.style.left = xy.x + 'px'; + element.style.top = xy.y + 'px'; + }; + + var IETransform = function(element,transform){ + + var r; + var m11 = 1; + var m12 = 1; + var m21 = 1; + var m22 = 1; + + if (typeof element.style['msTransform'] != 'undefined'){ + return true; + } + + StaticToAbsolute(element); + + r = transform.match(/rotate\((.*?)\)/); + var rotate = ( r && r[1]) ? parseInt(r[1]) : 0; + + rotate = rotate % 360; + if (rotate < 0) rotate = 360 + rotate; + + var radian= rotate * Math.PI / 180; + var cosX =Math.cos(radian); + var sinY =Math.sin(radian); + + m11 *= cosX; + m12 *= -sinY; + m21 *= sinY; + m22 *= cosX; + + element.style.filter = (element.style.filter || '').replace(/progid:DXImageTransform\.Microsoft\.Matrix\([^)]*\)/, "" ) + + ("progid:DXImageTransform.Microsoft.Matrix(" + + "M11=" + m11 + + ",M12=" + m12 + + ",M21=" + m21 + + ",M22=" + m22 + + ",FilterType='bilinear',sizingMethod='auto expand')") + ; + + var ow = parseInt(element.style.width || element.width || 0 ); + var oh = parseInt(element.style.height || element.height || 0 ); + + var radian = rotate * Math.PI / 180; + var absCosX =Math.abs(Math.cos(radian)); + var absSinY =Math.abs(Math.sin(radian)); + + var dx = (ow - (ow * absCosX + oh * absSinY)) / 2; + var dy = (oh - (ow * absSinY + oh * absCosX)) / 2; + + element.style.marginLeft = Math.floor(dx) + "px"; + element.style.marginTop = Math.floor(dy) + "px"; + + return(true); + }; + + var transform_set = $.cssHooks.transform.set; + $.cssHooks.transform.set = function(elem, value) { + transform_set.apply(this, [elem, value] ); + IETransform(elem,value); + return value; + }; + } + +})(jQuery); diff --git a/js/i18n/elfinder.en.js b/js/i18n/elfinder.en.js index 39eb5b589..9e46de7ba 100644 --- a/js/i18n/elfinder.en.js +++ b/js/i18n/elfinder.en.js @@ -230,9 +230,9 @@ if (elFinder && elFinder.prototype && typeof(elFinder.prototype.i18) == 'object' 'resize' : 'Resize', 'crop' : 'Crop', 'rotate' : 'Rotate', - 'rotate-cw' : '90-degree left rotation', + 'rotate-cw' : '90-degree left rotation', 'rotate-ccw' : '90-degree right rotation', - 'deg' : 'Deg', + 'degree' : 'Deg', /********************************** mimetypes **********************************/ 'kindUnknown' : 'Unknown', diff --git a/js/i18n/elfinder.jp.js b/js/i18n/elfinder.jp.js index d8b96d905..34ae45a1e 100644 --- a/js/i18n/elfinder.jp.js +++ b/js/i18n/elfinder.jp.js @@ -230,9 +230,9 @@ if (elFinder && elFinder.prototype && typeof(elFinder.prototype.i18) == 'object' 'resize' : 'リサイズ', 'crop' : '切り抜き', 'rotate' : '回転', - 'rotate-l90' : '90度左回転', - 'rotate-r90' : '90度右回転', - 'deg' : '度', + 'rotate-cw' : '90度左回転', + 'rotate-ccw' : '90度右回転', + 'degree' : '度', /********************************** mimetypes **********************************/ 'kindUnknown' : '不明', From ae0fe7d783988e004cb98e49a81c8d2986e8ba55 Mon Sep 17 00:00:00 2001 From: nao-pon Date: Wed, 7 Mar 2012 22:56:44 +0900 Subject: [PATCH 06/16] Correction for IE, and refactoring. --- js/commands/resize.js | 43 ++++++++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/js/commands/resize.js b/js/commands/resize.js index 9800492fc..5c07a01b0 100644 --- a/js/commands/resize.js +++ b/js/commands/resize.js @@ -177,18 +177,6 @@ elFinder.prototype.commands.resize = function() { .filter(':first').focus(); resizable(); - - // for ie mouse event problem - if ($.browser.msie && parseInt($.browser.version) <= 9) { - rhandlec.css({ - 'opacity': 0, - 'background-color': 'fff' - }); - rhandlec.find('.elfinder-resize-handle-point').css({ - 'opacity': 0.7, - 'background-color': 'fff' - }); - } reset.hover(function() { reset.toggleClass('ui-state-hover'); }).click(resetView); @@ -198,7 +186,8 @@ elFinder.prototype.commands.resize = function() { }), basec = $('
'), imgc = $(''), - imgr = $('').css('cursor', 'pointer'), + coverc = $('
'), + imgr = $(''), resetView = function() { width.val(owidth); height.val(oheight); @@ -265,7 +254,7 @@ elFinder.prototype.commands.resize = function() { if (typeof value == 'undefined') { rdegree = value = parseInt(degree.val()); } - if ($.browser.msie && parseInt($.browser.version) <= 9) { + if ($.browser.msie && parseInt($.browser.version) < 9) { imgr.rotate(value); } else { imgr.animate({rotate: value + 'deg'}); @@ -375,12 +364,15 @@ elFinder.prototype.commands.resize = function() { imgc.width(img.width()) .height(img.height()); + + coverc.width(img.width()) + .height(img.height()); - rhandlec.css('position', 'absolute') - .width(imgc.width()) + rhandlec.width(imgc.width()) .height(imgc.height()) .offset(imgc.offset()) .resizable({ + alsoResize : coverc, containment : basec, resize : crop.update }) @@ -389,6 +381,7 @@ elFinder.prototype.commands.resize = function() { containment : imgc, drag : crop.update }); + crop.update(); } } @@ -515,7 +508,8 @@ elFinder.prototype.commands.resize = function() { preview.append(spinner).append(rhandle.hide()).append(img.hide()); - rhandlec.append('
') + rhandlec.css('position', 'absolute') + .append('
') .append('
') .append('
') .append('
') @@ -523,7 +517,7 @@ elFinder.prototype.commands.resize = function() { .append('
') .append('
') - preview.append(basec.hide().append(imgc).append(rhandlec)); + preview.append(basec.hide().append(imgc).append(rhandlec.append(coverc))); preview.append(imgr.hide()); @@ -545,12 +539,23 @@ elFinder.prototype.commands.resize = function() { reset.css('left', width.position().left + width.width() + 12); + coverc.css({ 'opacity': 0.2, 'background-color': '#fff', 'position': 'absolute'}), + rhandlec.css('cursor', 'move'); + rhandlec.find('.elfinder-resize-handle-point').css({ + 'background-color' : '#fff', + 'opacity': 0.5, + 'border-color':'#000' + }); + + imgr.css('cursor', 'pointer'); + pwidth = preview.width() - (rhandle.outerWidth() - rhandle.width()); pheight = preview.height() - (rhandle.outerHeight() - rhandle.height()); img.attr('src', src + (src.indexOf('?') === -1 ? '?' : '&')+'_='+Math.random()); imgc.attr('src', img.attr('src')); imgr.attr('src', img.attr('src')); + }, id, dialog @@ -635,7 +640,7 @@ elFinder.prototype.commands.resize = function() { $(fx.elem).rotate(fx.now); }; - if ($.browser.msie && parseInt($.browser.version) <= 9) { + if ($.browser.msie && parseInt($.browser.version) < 9) { var GetAbsoluteXY = function(element) { var pnode = element; var x = pnode.offsetLeft; From bb9bbea4e787156465b836167ef877dc58b667e7 Mon Sep 17 00:00:00 2001 From: ph0enix Date: Wed, 7 Mar 2012 21:43:58 +0400 Subject: [PATCH 07/16] Chrome error fix --- js/commands/resize.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/js/commands/resize.js b/js/commands/resize.js index 9800492fc..3e1709483 100644 --- a/js/commands/resize.js +++ b/js/commands/resize.js @@ -599,12 +599,12 @@ elFinder.prototype.commands.resize = function() { }; $.cssHooks.transform = { get: function(elem, computed, extra) { - name = findProperty( elem.style , + var name = findProperty( elem.style , ['WebkitTransform', 'MozTransform', 'OTransform' , 'msTransform' , 'transform'] ); return elem.style[name]; }, set: function(elem, value) { - name = findProperty( elem.style , + var name = findProperty( elem.style , ['WebkitTransform', 'MozTransform', 'OTransform' , 'msTransform' , 'transform'] ); elem.style[name] = value; return value; From 13b3c36f09d9e37fa0a5dc480e3f0fcbe0d2d149 Mon Sep 17 00:00:00 2001 From: nao-pon Date: Thu, 8 Mar 2012 11:13:29 +0900 Subject: [PATCH 08/16] #229 Fixed dialog missing at open second+ time with IE8. Use jQuery UI radio. --- js/commands/resize.js | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/js/commands/resize.js b/js/commands/resize.js index f2b96b7c4..d18226b1f 100644 --- a/js/commands/resize.js +++ b/js/commands/resize.js @@ -47,22 +47,20 @@ elFinder.prototype.commands.resize = function() { })), uiprop = $(''), reset = $('
'), - uitype = $('
'+fm.i18n('mode')+'
') + //uitype = $('
'+fm.i18n('mode')+'
') + uitype = $('
') .append('') .append('') .append(''), - type = $('input', uitype) + type = $('input', uitype) .change(function() { var val = $('input:checked', uitype).val(); - //uiresize.add(uicrop).toggle(); - + resetView(); resizable(true); croppable(true); rotateable(true); - // TODO rotateable - - + if (val == 'resize') { uiresize.show(); uirotate.hide(); @@ -537,6 +535,11 @@ elFinder.prototype.commands.resize = function() { open : function() { preview.zIndex(1+$(this).parent().zIndex()); } }).attr('id', id); + // for IE < 9 dialog mising at open second+ time. + if ($.browser.msie && parseInt($.browser.version) < 9) { + $('.elfinder-dialog').css('filter', ''); + } + reset.css('left', width.position().left + width.width() + 12); coverc.css({ 'opacity': 0.2, 'background-color': '#fff', 'position': 'absolute'}), @@ -546,9 +549,11 @@ elFinder.prototype.commands.resize = function() { 'opacity': 0.5, 'border-color':'#000' }); - + imgr.css('cursor', 'pointer'); + $('.elfinder-resize-type').buttonset(); + pwidth = preview.width() - (rhandle.outerWidth() - rhandle.width()); pheight = preview.height() - (rhandle.outerHeight() - rhandle.height()); From 9879d50c07ff9668b6e0280008d3404b3d15ca3f Mon Sep 17 00:00:00 2001 From: nao-pon Date: Thu, 8 Mar 2012 15:30:36 +0900 Subject: [PATCH 09/16] #229 Use "uitype" object for buttonset(). --- js/commands/resize.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/commands/resize.js b/js/commands/resize.js index d18226b1f..83556b7e3 100644 --- a/js/commands/resize.js +++ b/js/commands/resize.js @@ -552,7 +552,7 @@ elFinder.prototype.commands.resize = function() { imgr.css('cursor', 'pointer'); - $('.elfinder-resize-type').buttonset(); + uitype.buttonset(); pwidth = preview.width() - (rhandle.outerWidth() - rhandle.width()); pheight = preview.height() - (rhandle.outerHeight() - rhandle.height()); From 05c9a3787c8a8caf27fc281a0ee981f1a76e5b2a Mon Sep 17 00:00:00 2001 From: nao-pon Date: Thu, 8 Mar 2012 17:00:39 +0900 Subject: [PATCH 10/16] #229 Bug-fix of the scale calculation for preview. --- js/commands/resize.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/commands/resize.js b/js/commands/resize.js index 83556b7e3..3a566a052 100644 --- a/js/commands/resize.js +++ b/js/commands/resize.js @@ -199,7 +199,7 @@ elFinder.prototype.commands.resize = function() { updateView : function(w, h) { if (w > pwidth || h > pheight) { - if (w > pwidth && (w - pwidth) > (h - pheight)) { + if (w > pwidth && (w / pwidth) > (h / pheight)) { img.width(pwidth).height(Math.ceil(img.width()/ratio)); } else { img.height(pheight).width(Math.ceil(img.height()*ratio)); From 0325d3f0d02b153024cf609514665656816f2d60 Mon Sep 17 00:00:00 2001 From: nao-pon Date: Thu, 8 Mar 2012 20:14:52 +0900 Subject: [PATCH 11/16] #229 preview's scale calculation simplify. --- js/commands/resize.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/commands/resize.js b/js/commands/resize.js index 3a566a052..3bb62846d 100644 --- a/js/commands/resize.js +++ b/js/commands/resize.js @@ -199,7 +199,7 @@ elFinder.prototype.commands.resize = function() { updateView : function(w, h) { if (w > pwidth || h > pheight) { - if (w > pwidth && (w / pwidth) > (h / pheight)) { + if (w / pwidth > h / pheight) { img.width(pwidth).height(Math.ceil(img.width()/ratio)); } else { img.height(pheight).width(Math.ceil(img.height()*ratio)); From 221b27b76f14ec140e3d1b7ce039b63d274ae015 Mon Sep 17 00:00:00 2001 From: nao-pon Date: Fri, 9 Mar 2012 01:23:25 +0900 Subject: [PATCH 12/16] #229 Disabled rotate animation with Opera. --- js/commands/resize.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/commands/resize.js b/js/commands/resize.js index 3bb62846d..61eb82056 100644 --- a/js/commands/resize.js +++ b/js/commands/resize.js @@ -252,7 +252,7 @@ elFinder.prototype.commands.resize = function() { if (typeof value == 'undefined') { rdegree = value = parseInt(degree.val()); } - if ($.browser.msie && parseInt($.browser.version) < 9) { + if ($.browser.opera || ($.browser.msie && parseInt($.browser.version) < 9)) { imgr.rotate(value); } else { imgr.animate({rotate: value + 'deg'}); From 33859af6982fa8ab916b4598b18def25c3cf6aa0 Mon Sep 17 00:00:00 2001 From: Sergio Jovani Date: Thu, 8 Mar 2012 20:40:32 +0100 Subject: [PATCH 13/16] Added slider to rotate --- css/commands.css | 6 ++++++ js/commands/resize.js | 25 +++++++++++++++++++++++-- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/css/commands.css b/css/commands.css index 50eecfd1e..b0e993830 100644 --- a/css/commands.css +++ b/css/commands.css @@ -147,6 +147,12 @@ margin-bottom:-11px;; } +.elfinder-resize-rotate-slider { + float: left; + width: 200px; + margin: 5px; +} + /********************** COMMAND "EDIT" ****************************/ /* edit text file textarea */ .elfinder-file-edit { diff --git a/js/commands/resize.js b/js/commands/resize.js index 3bb62846d..2984b5a50 100644 --- a/js/commands/resize.js +++ b/js/commands/resize.js @@ -114,6 +114,20 @@ elFinder.prototype.commands.resize = function() { .change(function() { rotate.update(); }), + uidegslider = $('
') + .slider({ + min: 0, + max: 270, + value: degree.val(), + animate: true, + change: function(event, ui) { + degree.val(ui.value); + }, + slide: function(event, ui) { + degree.val(ui.value); + rotate.update(ui.value, false); + } + }), ratio = 1, prop = 1, owidth = 0, @@ -248,11 +262,14 @@ elFinder.prototype.commands.resize = function() { imageStartAngle : 0, imageBeingRotated : false, - update : function(value) { + update : function(value, animate) { if (typeof value == 'undefined') { rdegree = value = parseInt(degree.val()); } - if ($.browser.msie && parseInt($.browser.version) < 9) { + if (typeof animate == 'undefined') { + animate = true; + } + if (! animate || ($.browser.msie && parseInt($.browser.version) < 9)) { imgr.rotate(value); } else { imgr.animate({rotate: value + 'deg'}); @@ -262,6 +279,8 @@ elFinder.prototype.commands.resize = function() { value += 360; } degree.val(parseInt(value)); + + uidegslider.slider('value', degree.val()); }, execute : function ( e ) { @@ -486,7 +505,9 @@ elFinder.prototype.commands.resize = function() { .append(degree) .append($('').text(fm.i18n('degree'))) ).append($(uibuttonset).append(uideg270).append($(uiseparator)).append(uideg90)) + .append(uidegslider) ); + dialog.append(uitype); From fc666f171fa07bd0d76c58ce20b945bc2586eed9 Mon Sep 17 00:00:00 2001 From: Sergio Jovani Date: Thu, 8 Mar 2012 21:08:45 +0100 Subject: [PATCH 14/16] Improved slider integration --- js/commands/resize.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/js/commands/resize.js b/js/commands/resize.js index 2984b5a50..113042b23 100644 --- a/js/commands/resize.js +++ b/js/commands/resize.js @@ -117,14 +117,15 @@ elFinder.prototype.commands.resize = function() { uidegslider = $('
') .slider({ min: 0, - max: 270, + max: 359, value: degree.val(), animate: true, change: function(event, ui) { - degree.val(ui.value); + if (ui.value != uidegslider.slider('value')) { + rotate.update(ui.value); + } }, slide: function(event, ui) { - degree.val(ui.value); rotate.update(ui.value, false); } }), @@ -306,6 +307,8 @@ elFinder.prototype.commands.resize = function() { rotateAngle += 360; } degree.val(rotateAngle); + + uidegslider.slider('value', degree.val()); return false; }, From 56c9d6d78012ad9f69821d7928704c45b0095653 Mon Sep 17 00:00:00 2001 From: Troex Nevelin Date: Thu, 8 Mar 2012 23:32:38 +0200 Subject: [PATCH 15/16] add missing translations, fix markup, refs #229 --- css/commands.css | 9 ++------- js/commands/resize.js | 7 +++++-- js/i18n/elfinder.LANG.js | 5 ++++- js/i18n/elfinder.en.js | 7 +++---- js/i18n/elfinder.jp.js | 1 - js/i18n/elfinder.ru.js | 8 +++++++- 6 files changed, 21 insertions(+), 16 deletions(-) diff --git a/css/commands.css b/css/commands.css index 50eecfd1e..0f2368fe8 100644 --- a/css/commands.css +++ b/css/commands.css @@ -3,12 +3,9 @@ /******************************************************************/ /********************** COMMAND "RESIZE" ****************************/ - +.elfinder-resize-type.ui-buttonset { margin-bottom: 3px; } .elfinder-resize-control { padding-top:1em; } - -.elfinder-resize-control input[type=text] { border:1px solid #aaa; } - - +.elfinder-resize-control input[type=text] { border:1px solid #aaa; text-align: right; } .elfinder-resize-preview { width:400px; height:400px; @@ -45,8 +42,6 @@ .elfinder-resize-handle-vline-left { top:0; left:0; } .elfinder-resize-handle-vline-right { top:0; right:0; } - - .elfinder-resize-handle-point { position:absolute; width:8px; diff --git a/js/commands/resize.js b/js/commands/resize.js index 61eb82056..fe98ed1c1 100644 --- a/js/commands/resize.js +++ b/js/commands/resize.js @@ -4,6 +4,8 @@ * Open dialog to resize image * * @author Dmitry (dio) Levashov + * @author Alexey Sukhotin + * @author nao-pon **/ elFinder.prototype.commands.resize = function() { @@ -482,10 +484,11 @@ elFinder.prototype.commands.resize = function() { uirotate.append($(row) .append($(label).text(fm.i18n('rotate'))) - .append($('
') + .append($('
') .append(degree) .append($('').text(fm.i18n('degree'))) - ).append($(uibuttonset).append(uideg270).append($(uiseparator)).append(uideg90)) + .append($(uibuttonset).append(uideg270).append($(uiseparator)).append(uideg90)) + ) ); dialog.append(uitype); diff --git a/js/i18n/elfinder.LANG.js b/js/i18n/elfinder.LANG.js index 1c9a882d9..8356e8ac7 100644 --- a/js/i18n/elfinder.LANG.js +++ b/js/i18n/elfinder.LANG.js @@ -233,7 +233,10 @@ if (elFinder && elFinder.prototype && typeof(elFinder.prototype.i18) == 'object' 'mode' : 'Mode', 'resize' : 'Resize', 'crop' : 'Crop', - + 'rotate' : 'Rotate', + 'rotate-cw' : 'Rotate 90 degrees CW', + 'rotate-ccw' : 'Rotate 90 degrees CCW', + 'degree' : 'Degree', /********************************** mimetypes **********************************/ 'kindUnknown' : 'Unknown', diff --git a/js/i18n/elfinder.en.js b/js/i18n/elfinder.en.js index 9e46de7ba..a63740eee 100644 --- a/js/i18n/elfinder.en.js +++ b/js/i18n/elfinder.en.js @@ -226,13 +226,12 @@ if (elFinder && elFinder.prototype && typeof(elFinder.prototype.i18) == 'object' 'scale' : 'Scale', 'width' : 'Width', 'height' : 'Height', - 'mode' : 'Mode', 'resize' : 'Resize', 'crop' : 'Crop', 'rotate' : 'Rotate', - 'rotate-cw' : '90-degree left rotation', - 'rotate-ccw' : '90-degree right rotation', - 'degree' : 'Deg', + 'rotate-cw' : 'Rotate 90 degrees CW', + 'rotate-ccw' : 'Rotate 90 degrees CCW', + 'degree' : 'Degree', /********************************** mimetypes **********************************/ 'kindUnknown' : 'Unknown', diff --git a/js/i18n/elfinder.jp.js b/js/i18n/elfinder.jp.js index 34ae45a1e..9d60e3e4d 100644 --- a/js/i18n/elfinder.jp.js +++ b/js/i18n/elfinder.jp.js @@ -226,7 +226,6 @@ if (elFinder && elFinder.prototype && typeof(elFinder.prototype.i18) == 'object' 'scale' : '表示縮尺', 'width' : '幅', 'height' : '高さ', - 'mode' : 'モード', 'resize' : 'リサイズ', 'crop' : '切り抜き', 'rotate' : '回転', diff --git a/js/i18n/elfinder.ru.js b/js/i18n/elfinder.ru.js index 2aa2677b2..439e0c840 100644 --- a/js/i18n/elfinder.ru.js +++ b/js/i18n/elfinder.ru.js @@ -222,7 +222,13 @@ if (elFinder && elFinder.prototype && typeof(elFinder.prototype.i18) == 'object' 'scale' : 'Масштаб', 'width' : 'Ширина', 'height' : 'Высота', - + 'resize' : 'Размер', + 'crop' : 'Кадрировать', + 'rotate' : 'Поворот', + 'rotate-cw' : 'Поворот на 90 градусов по часовой стрелке', + 'rotate-ccw' : 'Поворот на 90 градусов против часовой стрелке', + 'degree' : '°', + /********************************** mimetypes **********************************/ 'kindUnknown' : 'Неизвестный', 'kindFolder' : 'Папка', From 78040c7d53b8461916510caa0ebe64f898235c13 Mon Sep 17 00:00:00 2001 From: Troex Nevelin Date: Fri, 9 Mar 2012 09:31:06 +0200 Subject: [PATCH 16/16] better markup, refs #229, refs #233 --- css/commands.css | 50 ++++++++++++++---------------------------- js/commands/resize.js | 12 ++++++---- js/i18n/elfinder.en.js | 2 +- 3 files changed, 25 insertions(+), 39 deletions(-) diff --git a/css/commands.css b/css/commands.css index 4673a7aab..e4ed541db 100644 --- a/css/commands.css +++ b/css/commands.css @@ -3,8 +3,9 @@ /******************************************************************/ /********************** COMMAND "RESIZE" ****************************/ -.elfinder-resize-type.ui-buttonset { margin-bottom: 3px; } -.elfinder-resize-control { padding-top:1em; } +.elfinder-dialog-resize { margin-top:.3em; } +.elfinder-resize-type { float:left; margin-bottom: .4em; } +.elfinder-resize-control { padding-top:3em; } .elfinder-resize-control input[type=text] { border:1px solid #aaa; text-align: right; } .elfinder-resize-preview { width:400px; @@ -50,48 +51,29 @@ background:transparent; } -.elfinder-resize-handle-point-n { - -} - -.elfinder-resize-handle-point-ne { - -} - -.elfinder-resize-handle-point-e { +.elfinder-resize-handle-point-n {} +.elfinder-resize-handle-point-ne {} +.elfinder-resize-handle-point-e { top:50%; right:0; margin-top:-5px; margin-right:-5px; } - -.elfinder-resize-handle-point-se { +.elfinder-resize-handle-point-se { bottom:0; right:0; margin-bottom:-5px; margin-right:-5px; } - -.elfinder-resize-handle-point-s { +.elfinder-resize-handle-point-s { bottom:0; left:50%; margin-bottom:-5px; margin-left:-5px; } - -.elfinder-resize-handle-point-sw { - -} - -.elfinder-resize-handle-point-w { - -} - -.elfinder-resize-handle-point-nw { - -} - - +.elfinder-resize-handle-point-sw {} +.elfinder-resize-handle-point-w {} +.elfinder-resize-handle-point-nw {} .elfinder-resize-spinner { position:absolute; @@ -107,7 +89,7 @@ .elfinder-resize-row { margin-bottom:7px; position:relative;} -.elfinder-resize-label { float:left; width:80px;} +.elfinder-resize-label { float:left; width:80px; padding-top: 3px; } .elfinder-resize-reset { width:16px; @@ -117,8 +99,8 @@ margin-top:-8px; } -.elfinder-dialog-resize .ui-resizable-e { height:100%; width:10px; } -.elfinder-dialog-resize .ui-resizable-s { width:100%; height:10px;} +.elfinder-dialog-resize .ui-resizable-e { height:100%; width:10px; } +.elfinder-dialog-resize .ui-resizable-s { width:100%; height:10px; } .elfinder-dialog-resize .ui-resizable-se { background:transparent; bottom:0; @@ -144,8 +126,8 @@ .elfinder-resize-rotate-slider { float: left; - width: 200px; - margin: 5px; + width: 195px; + margin: 7px 7px 0; } /********************** COMMAND "EDIT" ****************************/ diff --git a/js/commands/resize.js b/js/commands/resize.js index e59eb8be4..6869dc288 100644 --- a/js/commands/resize.js +++ b/js/commands/resize.js @@ -5,7 +5,8 @@ * * @author Dmitry (dio) Levashov * @author Alexey Sukhotin - * @author nao-pon + * @author Naoki Sawada + * @author Sergio Jovani **/ elFinder.prototype.commands.resize = function() { @@ -507,9 +508,12 @@ elFinder.prototype.commands.resize = function() { uirotate.append($(row) .append($(label).text(fm.i18n('rotate'))) .append($('
') - .append(degree) - .append($('').text(fm.i18n('degree'))) - ).append($(uibuttonset).append(uideg270).append($(uiseparator)).append(uideg90)) + .append($('
') + .append(degree) + .append($('').text(fm.i18n('degree'))) + ) + .append($(uibuttonset).append(uideg270).append($(uiseparator)).append(uideg90)) + ) .append(uidegslider) ); diff --git a/js/i18n/elfinder.en.js b/js/i18n/elfinder.en.js index a63740eee..66b597625 100644 --- a/js/i18n/elfinder.en.js +++ b/js/i18n/elfinder.en.js @@ -231,7 +231,7 @@ if (elFinder && elFinder.prototype && typeof(elFinder.prototype.i18) == 'object' 'rotate' : 'Rotate', 'rotate-cw' : 'Rotate 90 degrees CW', 'rotate-ccw' : 'Rotate 90 degrees CCW', - 'degree' : 'Degree', + 'degree' : '°', /********************************** mimetypes **********************************/ 'kindUnknown' : 'Unknown',