diff --git a/css/commands.css b/css/commands.css index 50eecfd1e..e4ed541db 100644 --- a/css/commands.css +++ b/css/commands.css @@ -3,12 +3,10 @@ /******************************************************************/ /********************** COMMAND "RESIZE" ****************************/ - -.elfinder-resize-control { padding-top:1em; } - -.elfinder-resize-control input[type=text] { border:1px solid #aaa; } - - +.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; height:400px; @@ -45,8 +43,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; @@ -55,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; @@ -112,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; @@ -122,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; @@ -147,6 +124,12 @@ margin-bottom:-11px;; } +.elfinder-resize-rotate-slider { + float: left; + width: 195px; + margin: 7px 7px 0; +} + /********************** COMMAND "EDIT" ****************************/ /* edit text file textarea */ .elfinder-file-edit { 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 ddfb9bc6f..3b51326db 100644 Binary files a/img/toolbar.png and b/img/toolbar.png differ diff --git a/js/commands/resize.js b/js/commands/resize.js index a4c602494..6869dc288 100644 --- a/js/commands/resize.js +++ b/js/commands/resize.js @@ -4,6 +4,9 @@ * Open dialog to resize image * * @author Dmitry (dio) Levashov + * @author Alexey Sukhotin + * @author Naoki Sawada + * @author Sergio Jovani **/ elFinder.prototype.commands.resize = function() { @@ -31,24 +34,52 @@ elFinder.prototype.commands.resize = function() { rhandlec = $('
'), uiresize = $('
'), uicrop = $('
'), + uibuttonset = '
', + uibutton = '
', + uiseparator = '', + uirotate = $('
'), + uideg270 = $(uibutton).attr('title',fm.i18n('rotate-cw')).append($('') + .click(function(){ + rdegree = rdegree - 90; + rotate.update(rdegree); + })), + uideg90 = $(uibutton).attr('title',fm.i18n('rotate-ccw')).append($('') + .click(function(){ + rdegree = rdegree + 90; + rotate.update(rdegree); + })), uiprop = $(''), reset = $('
'), - uitype = $('
'+fm.i18n('mode')+'
') - .append('') - .append(''), - type = $('input', uitype) + //uitype = $('
'+fm.i18n('mode')+'
') + uitype = $('
') + .append('') + .append('') + .append(''), + type = $('input', uitype) .change(function() { - uiresize.add(uicrop).toggle(); + var val = $('input:checked', uitype).val(); + resetView(); resizable(true); croppable(true); - - var val = $('input:checked', uitype).val(); + rotateable(true); + 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 = $('') @@ -82,6 +113,25 @@ elFinder.prototype.commands.resize = function() { pointY = $(input), offsetX = $(input), offsetY = $(input), + degree = $('') + .change(function() { + rotate.update(); + }), + uidegslider = $('
') + .slider({ + min: 0, + max: 359, + value: degree.val(), + animate: true, + change: function(event, ui) { + if (ui.value != uidegslider.slider('value')) { + rotate.update(ui.value); + } + }, + slide: function(event, ui) { + rotate.update(ui.value, false); + } + }), ratio = 1, prop = 1, owidth = 0, @@ -89,18 +139,26 @@ elFinder.prototype.commands.resize = function() { cratio = true, pwidth = 0, pheight = 0, + rwidth = 0, + rheight = 0, + rdegree = 0, img = $('') .load(function() { spinner.remove(); owidth = img.width(); oheight = img.height(); - ratio = (owidth/oheight).toFixed(2); + ratio = owidth/oheight; resize.updateView(owidth, oheight); 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; @@ -142,7 +200,10 @@ elFinder.prototype.commands.resize = function() { .error(function() { spinner.text('Unable to load image').css('background', 'transparent'); }), + basec = $('
'), imgc = $(''), + coverc = $('
'), + imgr = $(''), resetView = function() { width.val(owidth); height.val(oheight); @@ -155,14 +216,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 > 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(); @@ -198,6 +261,101 @@ 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, animate) { + if (typeof value == 'undefined') { + rdegree = value = parseInt(degree.val()); + } + if (typeof animate == 'undefined') { + animate = true; + } + if (! animate || $.browser.opera || ($.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)); + + uidegslider.slider('value', degree.val()); + }, + + 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); + + uidegslider.slider('value', degree.val()); + + 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) { @@ -220,33 +378,54 @@ 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()); + + coverc.width(img.width()) + .height(img.height()); - rhandlec.show() - .css('position', 'absolute') - .width(imgc.width()) + rhandlec.width(imgc.width()) .height(imgc.height()) .offset(imgc.offset()) .resizable({ - containment : imgc, + alsoResize : coverc, + containment : basec, resize : crop.update }) .draggable({ + handle : rhandlec, containment : imgc, drag : crop.update }); + crop.update(); } } }, + rotateable = function(destroy) { + if ($.fn.draggable && $.fn.resizable) { + if (destroy) { + imgr.hide(); + } + else { + imgr.show() + .width(rwidth) + .height(rheight) + .css('margin-top', (pheight-rheight)/2 + 'px') + .css('margin-left', (pwidth-rwidth)/2 + 'px'); + + } + } + }, save = function() { - var w, h, x, y; + var w, h, x, y, d; var mode = $('input:checked', uitype).val(); width.add(height).change(); @@ -259,14 +438,28 @@ 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 = 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) { - 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'); @@ -279,6 +472,7 @@ elFinder.prototype.commands.resize = function() { height : h, x : x, y : y, + degree : d, mode : mode }, notify : {type : 'resize', cnt : 1} @@ -297,7 +491,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)) @@ -307,13 +503,27 @@ 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($('
') + .append(degree) + .append($('').text(fm.i18n('degree'))) + ) + .append($(uibuttonset).append(uideg270).append($(uiseparator)).append(uideg90)) + ) + .append(uidegslider) + ); + dialog.append(uitype); control.append($(row)) .append(uiresize) .append(uicrop.hide()) + .append(uirotate.hide()) .find('input,select').attr('disabled', 'disabled'); rhandle.append('
') @@ -326,7 +536,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('
') @@ -334,8 +545,12 @@ elFinder.prototype.commands.resize = function() { .append('
') .append('
') - preview.append(rhandlec.hide()).append(imgc.hide()); - + preview.append(basec.hide().append(imgc).append(rhandlec.append(coverc))); + + preview.append(imgr.hide()); + + preview.css('overflow', 'hidden'); + dialog.append(preview).append(control); buttons[fm.i18n('btnCancel')] = function() { dialog.elfinderdialog('close'); }; @@ -350,18 +565,38 @@ 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'}), + rhandlec.css('cursor', 'move'); + rhandlec.find('.elfinder-resize-handle-point').css({ + 'background-color' : '#fff', + 'opacity': 0.5, + 'border-color':'#000' + }); + + imgr.css('cursor', 'pointer'); + + uitype.buttonset(); + 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(); } @@ -374,9 +609,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) { + var name = findProperty( elem.style , + ['WebkitTransform', 'MozTransform', 'OTransform' , 'msTransform' , 'transform'] ); + return elem.style[name]; + }, + set: function(elem, value) { + var 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.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 9e1fd8f57..66b597625 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,12 @@ if (elFinder && elFinder.prototype && typeof(elFinder.prototype.i18) == 'object' 'scale' : 'Scale', 'width' : 'Width', 'height' : 'Height', - 'mode' : 'Mode', - 'resize' : 'Resize', - 'crop' : 'Crop', + 'resize' : 'Resize', + 'crop' : 'Crop', + 'rotate' : 'Rotate', + 'rotate-cw' : 'Rotate 90 degrees CW', + 'rotate-ccw' : 'Rotate 90 degrees CCW', + 'degree' : '°', /********************************** mimetypes **********************************/ 'kindUnknown' : 'Unknown', diff --git a/js/i18n/elfinder.jp.js b/js/i18n/elfinder.jp.js index 2cab79c8a..9d60e3e4d 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 ***********************************/ @@ -226,9 +226,12 @@ if (elFinder && elFinder.prototype && typeof(elFinder.prototype.i18) == 'object' 'scale' : '表示縮尺', 'width' : '幅', 'height' : '高さ', - 'mode' : 'モード', 'resize' : 'リサイズ', 'crop' : '切り抜き', + 'rotate' : '回転', + 'rotate-cw' : '90度左回転', + 'rotate-ccw' : '90度右回転', + 'degree' : '度', /********************************** mimetypes **********************************/ 'kindUnknown' : '不明', 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' : 'Папка', diff --git a/php/elFinder.class.php b/php/elFinder.class.php index ab3305093..5b1508505 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 9d1cfb96f..a1b926ab4 100644 --- a/php/elFinderVolumeDriver.class.php +++ b/php/elFinderVolumeDriver.class.php @@ -1624,12 +1624,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 +1663,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 +2643,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 +2750,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 +2826,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 +2842,9 @@ abstract class elFinderVolumeDriver { switch ($this->imgLib) { case 'imagick': - try { $img = new imagick($path); } catch (Exception $e) { - return false; } @@ -2879,7 +2888,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 +2901,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 * 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); }