Media Manager: implemented insert link, insert image, and crop and insert features. Minor fixes in October core and third-party JS plugins.
This commit is contained in:
parent
790b464679
commit
c1740c479c
|
|
@ -143,6 +143,16 @@ body.slim-container .form-buttons{padding:0 20px 20px}
|
|||
.select2-drop .select2-results .select2-no-results{padding:7px !important}
|
||||
.select2-drop .select2-results .select2-highlighted{background:#4da7e8}
|
||||
.select2-drop .select2-results > li > div{padding:5px 7px 5px}
|
||||
[data-control=toolbar] .form-control{display:inline-block;margin-right:15px}
|
||||
[data-control=toolbar] .form-control.width-50{width:50px}
|
||||
[data-control=toolbar] .form-control.width-100{width:100px}
|
||||
[data-control=toolbar] .form-control.width-150{width:150px}
|
||||
[data-control=toolbar] input[type=text].form-control,[data-control=toolbar] label{position:relative;top:5px}
|
||||
[data-control=toolbar] label{margin-right:7px}
|
||||
[data-control=toolbar] label.standalone{margin-right:15px}
|
||||
[data-control=toolbar] .select2-container{display:inline-block;width:auto;height:36px}
|
||||
[data-control=toolbar] .select2-container .select2-choice{height:34px;line-height:34px}
|
||||
[data-control=toolbar] select.form-control.custom-select{display:none}
|
||||
table.table.data{font-size:12px}
|
||||
table.table.data thead{background:#ffffff}
|
||||
table.table.data thead td,table.table.data thead th{border-width:1px;border-top:1px solid #e2e2e2 !important;border-color:#e2e2e2;padding:0;font-weight:normal}
|
||||
|
|
@ -566,6 +576,7 @@ html.cssanimations .cursor-loading-indicator.hide{display:none}
|
|||
.control-toolbar .toolbar-item.layout-cell:last-child{padding-right:0}
|
||||
.control-toolbar .toolbar-item .btn,.control-toolbar .toolbar-item .btn-group,.control-toolbar .toolbar-item .dropdown{white-space:nowrap;float:none;display:inline-block;margin-right:10px}
|
||||
.control-toolbar .toolbar-item .btn:last-child,.control-toolbar .toolbar-item .btn-group:last-child,.control-toolbar .toolbar-item .dropdown:last-child{margin-right:0}
|
||||
.control-toolbar .toolbar-item .btn.standalone,.control-toolbar .toolbar-item .btn-group.standalone,.control-toolbar .toolbar-item .dropdown.standalone{margin-right:15px}
|
||||
.control-toolbar .toolbar-item .btn-group > .btn,.control-toolbar .toolbar-item .btn-group > .dropdown{margin-right:0;display:inline-block;float:none}
|
||||
.control-toolbar .toolbar-item .btn-group .dropdown > .btn{margin-right:0;border-bottom-right-radius:0;border-top-right-radius:0}
|
||||
.control-toolbar .toolbar-item .btn-group .dropdown.last > .btn{border-bottom-right-radius:2px !important;border-top-right-radius:2px !important}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
@import "../vendor/select2/select2.css"; html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}
|
||||
@import "../vendor/select2/select2.css";@import "../vendor/jcrop/css/jquery.Jcrop.min.css"; html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}
|
||||
body{margin:0}
|
||||
article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}
|
||||
audio,canvas,progress,video{display:inline-block;vertical-align:baseline}
|
||||
|
|
|
|||
|
|
@ -299,7 +299,9 @@ $(this.options.scrollMarkerContainer).append($('<span class="before scroll-marke
|
|||
$el.mousewheel(function(event){if(!self.options.allowScroll)
|
||||
return;var offset=self.options.vertical?((event.deltaFactor*event.deltaY)*-1):(event.deltaFactor*event.deltaX)
|
||||
return!scrollWheel(offset)})
|
||||
$el.on('mousedown.dragScroll',function(event){startDrag(event)
|
||||
$el.on('mousedown.dragScroll',function(event){if(event.target&&event.target.tagName==='INPUT')
|
||||
return
|
||||
startDrag(event)
|
||||
return false})
|
||||
$el.on('touchstart.dragScroll',function(event){var touchEvent=event.originalEvent;if(touchEvent.touches.length==1){startDrag(touchEvent.touches[0])
|
||||
event.stopPropagation()}})
|
||||
|
|
@ -397,6 +399,7 @@ $(window).off('resize',this.proxy(this.fixScrollClasses))
|
|||
this.el.off('.dragScroll')
|
||||
this.scrollClassContainer=null
|
||||
this.el.removeData('oc.dragScroll')
|
||||
this.el=null
|
||||
BaseProto.dispose.call(this)}
|
||||
var old=$.fn.dragScroll
|
||||
$.fn.dragScroll=function(option){var args=arguments;return this.each(function(){var $this=$(this)
|
||||
|
|
@ -875,7 +878,7 @@ this.$modal.on('shown.bs.modal',function(){self.triggerEvent('shown.oc.popup')})
|
|||
this.$modal.on('close.oc.popup',function(){self.hide()
|
||||
return false})
|
||||
this.init()}
|
||||
Popup.DEFAULTS={ajax:null,handler:null,keyboard:true,extraData:{},content:null,size:null,adaptiveHeight:false}
|
||||
Popup.DEFAULTS={ajax:null,handler:null,keyboard:true,extraData:{},content:null,size:null,adaptiveHeight:false,zIndex:null}
|
||||
Popup.prototype.init=function(){var self=this
|
||||
if(self.isOpen)
|
||||
return
|
||||
|
|
@ -897,6 +900,8 @@ if(this.options.size)
|
|||
modalDialog.addClass('size-'+this.options.size)
|
||||
if(this.options.adaptiveHeight)
|
||||
modalDialog.addClass('adaptive-height')
|
||||
if(this.options.zIndex!==null)
|
||||
modal.css('z-index',this.options.zIndex+20)
|
||||
return modal.append(modalDialog.append(modalContent))}
|
||||
Popup.prototype.setContent=function(contents){this.$content.html(contents)
|
||||
this.setLoading(false)
|
||||
|
|
@ -904,7 +909,10 @@ this.show()
|
|||
this.firstDiv=this.$content.find('>div:first')
|
||||
if(this.firstDiv.length>0)
|
||||
this.firstDiv.data('oc.popup',this)}
|
||||
Popup.prototype.setBackdrop=function(val){if(val&&!this.$backdrop){this.$backdrop=$('<div class="popup-backdrop fade" />').appendTo(document.body)
|
||||
Popup.prototype.setBackdrop=function(val){if(val&&!this.$backdrop){this.$backdrop=$('<div class="popup-backdrop fade" />')
|
||||
if(this.options.zIndex!==null)
|
||||
this.$backdrop.css('z-index',this.options.zIndex)
|
||||
this.$backdrop.appendTo(document.body)
|
||||
this.$backdrop.addClass('in')
|
||||
this.$backdrop.append($('<div class="modal-content popup-loading-indicator" />'))}
|
||||
else if(!val&&this.$backdrop){this.$backdrop.remove()
|
||||
|
|
|
|||
|
|
@ -71,6 +71,9 @@
|
|||
})
|
||||
|
||||
$el.on('mousedown.dragScroll', function(event){
|
||||
if (event.target && event.target.tagName === 'INPUT')
|
||||
return // Don't prevent clicking inputs in the toolbar
|
||||
|
||||
startDrag(event)
|
||||
return false
|
||||
})
|
||||
|
|
@ -338,6 +341,8 @@
|
|||
this.scrollClassContainer = null
|
||||
this.el.removeData('oc.dragScroll')
|
||||
|
||||
this.el = null
|
||||
|
||||
BaseProto.dispose.call(this)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -84,7 +84,8 @@
|
|||
extraData: {},
|
||||
content: null,
|
||||
size: null,
|
||||
adaptiveHeight: false
|
||||
adaptiveHeight: false,
|
||||
zIndex: null
|
||||
}
|
||||
|
||||
Popup.prototype.init = function(){
|
||||
|
|
@ -175,6 +176,9 @@
|
|||
if (this.options.adaptiveHeight)
|
||||
modalDialog.addClass('adaptive-height')
|
||||
|
||||
if (this.options.zIndex !== null)
|
||||
modal.css('z-index', this.options.zIndex + 20)
|
||||
|
||||
return modal.append(modalDialog.append(modalContent))
|
||||
}
|
||||
|
||||
|
|
@ -193,7 +197,11 @@
|
|||
Popup.prototype.setBackdrop = function(val) {
|
||||
if (val && !this.$backdrop) {
|
||||
this.$backdrop = $('<div class="popup-backdrop fade" />')
|
||||
.appendTo(document.body)
|
||||
|
||||
if (this.options.zIndex !== null)
|
||||
this.$backdrop.css('z-index', this.options.zIndex)
|
||||
|
||||
this.$backdrop.appendTo(document.body)
|
||||
|
||||
this.$backdrop.addClass('in')
|
||||
this.$backdrop.append($('<div class="modal-content popup-loading-indicator" />'))
|
||||
|
|
|
|||
|
|
@ -1869,4 +1869,244 @@ var carry=0;var v=Number.NaN;var prev;do{prev=v;v=d.getTime();ticks.push(v);if(u
|
|||
tickSize*(unit=="quarter"?3:1));}}else if(unit=="year"){d.setFullYear(d.getFullYear()+tickSize);}else{d.setTime(v+step);}}while(v<axis.max&&v!=prev);return ticks;};axis.tickFormatter=function(v,axis){var d=dateGenerator(v,axis.options);if(opts.timeformat!=null){return formatDate(d,opts.timeformat,opts.monthNames,opts.dayNames);}
|
||||
var useQuarters=(axis.options.tickSize&&axis.options.tickSize[1]=="quarter")||(axis.options.minTickSize&&axis.options.minTickSize[1]=="quarter");var t=axis.tickSize[0]*timeUnitSize[axis.tickSize[1]];var span=axis.max-axis.min;var suffix=(opts.twelveHourClock)?" %p":"";var hourCode=(opts.twelveHourClock)?"%I":"%H";var fmt;if(t<timeUnitSize.minute){fmt=hourCode+":%M:%S"+suffix;}else if(t<timeUnitSize.day){if(span<2*timeUnitSize.day){fmt=hourCode+":%M"+suffix;}else{fmt="%b %d "+hourCode+":%M"+suffix;}}else if(t<timeUnitSize.month){fmt="%b %d";}else if((useQuarters&&t<timeUnitSize.quarter)||(!useQuarters&&t<timeUnitSize.year)){if(span<timeUnitSize.year){fmt="%b";}else{fmt="%b %Y";}}else if(useQuarters&&t<timeUnitSize.year){if(span<timeUnitSize.year){fmt="Q%q";}else{fmt="Q%q %Y";}}else{fmt="%Y";}
|
||||
var rt=formatDate(d,fmt,opts.monthNames,opts.dayNames);return rt;};}});});}
|
||||
$.plot.plugins.push({init:init,options:options,name:'time',version:'1.0'});$.plot.formatDate=formatDate;})(jQuery);
|
||||
$.plot.plugins.push({init:init,options:options,name:'time',version:'1.0'});$.plot.formatDate=formatDate;})(jQuery);(function($){$.Jcrop=function(obj,opt){var options=$.extend({},$.Jcrop.defaults),docOffset,_ua=navigator.userAgent.toLowerCase(),is_msie=/msie/.test(_ua),ie6mode=/msie [1-6]\./.test(_ua);function px(n){return Math.round(n)+'px';}
|
||||
function cssClass(cl){return options.baseClass+'-'+cl;}
|
||||
function supportsColorFade(){return $.fx.step.hasOwnProperty('backgroundColor');}
|
||||
function getPos(obj)
|
||||
{var pos=$(obj).offset();return[pos.left,pos.top];}
|
||||
function mouseAbs(e)
|
||||
{return[(e.pageX-docOffset[0]),(e.pageY-docOffset[1])];}
|
||||
function setOptions(opt)
|
||||
{if(typeof(opt)!=='object')opt={};options=$.extend(options,opt);$.each(['onChange','onSelect','onRelease','onDblClick'],function(i,e){if(typeof(options[e])!=='function')options[e]=function(){};});}
|
||||
function startDragMode(mode,pos,touch)
|
||||
{docOffset=getPos($img);Tracker.setCursor(mode==='move'?mode:mode+'-resize');if(mode==='move'){return Tracker.activateHandlers(createMover(pos),doneSelect,touch);}
|
||||
var fc=Coords.getFixed();var opp=oppLockCorner(mode);var opc=Coords.getCorner(oppLockCorner(opp));Coords.setPressed(Coords.getCorner(opp));Coords.setCurrent(opc);Tracker.activateHandlers(dragmodeHandler(mode,fc),doneSelect,touch);}
|
||||
function dragmodeHandler(mode,f)
|
||||
{return function(pos){if(!options.aspectRatio){switch(mode){case'e':pos[1]=f.y2;break;case'w':pos[1]=f.y2;break;case'n':pos[0]=f.x2;break;case's':pos[0]=f.x2;break;}}else{switch(mode){case'e':pos[1]=f.y+1;break;case'w':pos[1]=f.y+1;break;case'n':pos[0]=f.x+1;break;case's':pos[0]=f.x+1;break;}}
|
||||
Coords.setCurrent(pos);Selection.update();};}
|
||||
function createMover(pos)
|
||||
{var lloc=pos;KeyManager.watchKeys();return function(pos){Coords.moveOffset([pos[0]-lloc[0],pos[1]-lloc[1]]);lloc=pos;Selection.update();};}
|
||||
function oppLockCorner(ord)
|
||||
{switch(ord){case'n':return'sw';case's':return'nw';case'e':return'nw';case'w':return'ne';case'ne':return'sw';case'nw':return'se';case'se':return'nw';case'sw':return'ne';}}
|
||||
function createDragger(ord)
|
||||
{return function(e){if(options.disabled){return false;}
|
||||
if((ord==='move')&&!options.allowMove){return false;}
|
||||
docOffset=getPos($img);btndown=true;startDragMode(ord,mouseAbs(e));e.stopPropagation();e.preventDefault();return false;};}
|
||||
function presize($obj,w,h)
|
||||
{var nw=$obj.width(),nh=$obj.height();if((nw>w)&&w>0){nw=w;nh=(w/$obj.width())*$obj.height();}
|
||||
if((nh>h)&&h>0){nh=h;nw=(h/$obj.height())*$obj.width();}
|
||||
xscale=$obj.width()/nw;yscale=$obj.height()/nh;$obj.width(nw).height(nh);}
|
||||
function unscale(c)
|
||||
{return{x:c.x*xscale,y:c.y*yscale,x2:c.x2*xscale,y2:c.y2*yscale,w:c.w*xscale,h:c.h*yscale};}
|
||||
function doneSelect(pos)
|
||||
{var c=Coords.getFixed();if((c.w>options.minSelect[0])&&(c.h>options.minSelect[1])){Selection.enableHandles();Selection.done();}else{Selection.release();}
|
||||
Tracker.setCursor(options.allowSelect?'crosshair':'default');}
|
||||
function newSelection(e)
|
||||
{if(options.disabled){return false;}
|
||||
if(!options.allowSelect){return false;}
|
||||
btndown=true;docOffset=getPos($img);Selection.disableHandles();Tracker.setCursor('crosshair');var pos=mouseAbs(e);Coords.setPressed(pos);Selection.update();Tracker.activateHandlers(selectDrag,doneSelect,e.type.substring(0,5)==='touch');KeyManager.watchKeys();e.stopPropagation();e.preventDefault();return false;}
|
||||
function selectDrag(pos)
|
||||
{Coords.setCurrent(pos);Selection.update();}
|
||||
function newTracker()
|
||||
{var trk=$('<div></div>').addClass(cssClass('tracker'));if(is_msie){trk.css({opacity:0,backgroundColor:'white'});}
|
||||
return trk;}
|
||||
if(typeof(obj)!=='object'){obj=$(obj)[0];}
|
||||
if(typeof(opt)!=='object'){opt={};}
|
||||
setOptions(opt);var img_css={border:'none',visibility:'visible',margin:0,padding:0,position:'absolute',top:0,left:0};var $origimg=$(obj),img_mode=true;if(obj.tagName=='IMG'){if($origimg[0].width!=0&&$origimg[0].height!=0){$origimg.width($origimg[0].width);$origimg.height($origimg[0].height);}else{var tempImage=new Image();tempImage.src=$origimg[0].src;$origimg.width(tempImage.width);$origimg.height(tempImage.height);}
|
||||
var $img=$origimg.clone().removeAttr('id').css(img_css).show();$img.width($origimg.width());$img.height($origimg.height());$origimg.after($img).hide();}else{$img=$origimg.css(img_css).show();img_mode=false;if(options.shade===null){options.shade=true;}}
|
||||
presize($img,options.boxWidth,options.boxHeight);var boundx=$img.width(),boundy=$img.height(),$div=$('<div />').width(boundx).height(boundy).addClass(cssClass('holder')).css({position:'relative',backgroundColor:options.bgColor}).insertAfter($origimg).append($img);if(options.addClass){$div.addClass(options.addClass);}
|
||||
var $img2=$('<div />'),$img_holder=$('<div />').width('100%').height('100%').css({zIndex:310,position:'absolute',overflow:'hidden'}),$hdl_holder=$('<div />').width('100%').height('100%').css('zIndex',320),$sel=$('<div />').css({position:'absolute',zIndex:600}).dblclick(function(){var c=Coords.getFixed();options.onDblClick.call(api,c);}).insertBefore($img).append($img_holder,$hdl_holder);if(img_mode){$img2=$('<img />').attr('src',$img.attr('src')).css(img_css).width(boundx).height(boundy),$img_holder.append($img2);}
|
||||
if(ie6mode){$sel.css({overflowY:'hidden'});}
|
||||
var bound=options.boundary;var $trk=newTracker().width(boundx+(bound*2)).height(boundy+(bound*2)).css({position:'absolute',top:px(-bound),left:px(-bound),zIndex:290}).mousedown(newSelection);var bgcolor=options.bgColor,bgopacity=options.bgOpacity,xlimit,ylimit,xmin,ymin,xscale,yscale,enabled=true,btndown,animating,shift_down;docOffset=getPos($img);var Touch=(function(){function hasTouchSupport(){var support={},events=['touchstart','touchmove','touchend'],el=document.createElement('div'),i;try{for(i=0;i<events.length;i++){var eventName=events[i];eventName='on'+eventName;var isSupported=(eventName in el);if(!isSupported){el.setAttribute(eventName,'return;');isSupported=typeof el[eventName]=='function';}
|
||||
support[events[i]]=isSupported;}
|
||||
return support.touchstart&&support.touchend&&support.touchmove;}
|
||||
catch(err){return false;}}
|
||||
function detectSupport(){if((options.touchSupport===true)||(options.touchSupport===false))return options.touchSupport;else return hasTouchSupport();}
|
||||
return{createDragger:function(ord){return function(e){if(options.disabled){return false;}
|
||||
if((ord==='move')&&!options.allowMove){return false;}
|
||||
docOffset=getPos($img);btndown=true;startDragMode(ord,mouseAbs(Touch.cfilter(e)),true);e.stopPropagation();e.preventDefault();return false;};},newSelection:function(e){return newSelection(Touch.cfilter(e));},cfilter:function(e){e.pageX=e.originalEvent.changedTouches[0].pageX;e.pageY=e.originalEvent.changedTouches[0].pageY;return e;},fixTouchSupport:function(e){if($(e.currentTarget).hasClass('jcrop-tracker'))e.stopPropagation();},isSupported:hasTouchSupport,support:detectSupport()};}());var Coords=(function(){var x1=0,y1=0,x2=0,y2=0,ox,oy;function setPressed(pos)
|
||||
{pos=rebound(pos);x2=x1=pos[0];y2=y1=pos[1];}
|
||||
function setCurrent(pos)
|
||||
{pos=rebound(pos);ox=pos[0]-x2;oy=pos[1]-y2;x2=pos[0];y2=pos[1];}
|
||||
function getOffset()
|
||||
{return[ox,oy];}
|
||||
function moveOffset(offset)
|
||||
{var ox=offset[0],oy=offset[1];if(0>x1+ox){ox-=ox+x1;}
|
||||
if(0>y1+oy){oy-=oy+y1;}
|
||||
if(boundy<y2+oy){oy+=boundy-(y2+oy);}
|
||||
if(boundx<x2+ox){ox+=boundx-(x2+ox);}
|
||||
x1+=ox;x2+=ox;y1+=oy;y2+=oy;}
|
||||
function getCorner(ord)
|
||||
{var c=getFixed();switch(ord){case'ne':return[c.x2,c.y];case'nw':return[c.x,c.y];case'se':return[c.x2,c.y2];case'sw':return[c.x,c.y2];}}
|
||||
function getFixed()
|
||||
{if(!options.aspectRatio){return getRect();}
|
||||
var aspect=options.aspectRatio,min_x=options.minSize[0]/xscale,max_x=options.maxSize[0]/xscale,max_y=options.maxSize[1]/yscale,rw=x2-x1,rh=y2-y1,rwa=Math.abs(rw),rha=Math.abs(rh),real_ratio=rwa/rha,xx,yy,w,h;if(max_x===0){max_x=boundx*10;}
|
||||
if(max_y===0){max_y=boundy*10;}
|
||||
if(real_ratio<aspect){yy=y2;w=rha*aspect;xx=rw<0?x1-w:w+x1;if(xx<0){xx=0;h=Math.abs((xx-x1)/aspect);yy=rh<0?y1-h:h+y1;}else if(xx>boundx){xx=boundx;h=Math.abs((xx-x1)/aspect);yy=rh<0?y1-h:h+y1;}}else{xx=x2;h=rwa/aspect;yy=rh<0?y1-h:y1+h;if(yy<0){yy=0;w=Math.abs((yy-y1)*aspect);xx=rw<0?x1-w:w+x1;}else if(yy>boundy){yy=boundy;w=Math.abs(yy-y1)*aspect;xx=rw<0?x1-w:w+x1;}}
|
||||
if(xx>x1){if(xx-x1<min_x){xx=x1+min_x;}else if(xx-x1>max_x){xx=x1+max_x;}
|
||||
if(yy>y1){yy=y1+(xx-x1)/aspect;}else{yy=y1-(xx-x1)/aspect;}}else if(xx<x1){if(x1-xx<min_x){xx=x1-min_x;}else if(x1-xx>max_x){xx=x1-max_x;}
|
||||
if(yy>y1){yy=y1+(x1-xx)/aspect;}else{yy=y1-(x1-xx)/aspect;}}
|
||||
if(xx<0){x1-=xx;xx=0;}else if(xx>boundx){x1-=xx-boundx;xx=boundx;}
|
||||
if(yy<0){y1-=yy;yy=0;}else if(yy>boundy){y1-=yy-boundy;yy=boundy;}
|
||||
return makeObj(flipCoords(x1,y1,xx,yy));}
|
||||
function rebound(p)
|
||||
{if(p[0]<0)p[0]=0;if(p[1]<0)p[1]=0;if(p[0]>boundx)p[0]=boundx;if(p[1]>boundy)p[1]=boundy;return[Math.round(p[0]),Math.round(p[1])];}
|
||||
function flipCoords(x1,y1,x2,y2)
|
||||
{var xa=x1,xb=x2,ya=y1,yb=y2;if(x2<x1){xa=x2;xb=x1;}
|
||||
if(y2<y1){ya=y2;yb=y1;}
|
||||
return[xa,ya,xb,yb];}
|
||||
function getRect()
|
||||
{var xsize=x2-x1,ysize=y2-y1,delta;if(xlimit&&(Math.abs(xsize)>xlimit)){x2=(xsize>0)?(x1+xlimit):(x1-xlimit);}
|
||||
if(ylimit&&(Math.abs(ysize)>ylimit)){y2=(ysize>0)?(y1+ylimit):(y1-ylimit);}
|
||||
if(ymin/yscale&&(Math.abs(ysize)<ymin/yscale)){y2=(ysize>0)?(y1+ymin/yscale):(y1-ymin/yscale);}
|
||||
if(xmin/xscale&&(Math.abs(xsize)<xmin/xscale)){x2=(xsize>0)?(x1+xmin/xscale):(x1-xmin/xscale);}
|
||||
if(x1<0){x2-=x1;x1-=x1;}
|
||||
if(y1<0){y2-=y1;y1-=y1;}
|
||||
if(x2<0){x1-=x2;x2-=x2;}
|
||||
if(y2<0){y1-=y2;y2-=y2;}
|
||||
if(x2>boundx){delta=x2-boundx;x1-=delta;x2-=delta;}
|
||||
if(y2>boundy){delta=y2-boundy;y1-=delta;y2-=delta;}
|
||||
if(x1>boundx){delta=x1-boundy;y2-=delta;y1-=delta;}
|
||||
if(y1>boundy){delta=y1-boundy;y2-=delta;y1-=delta;}
|
||||
return makeObj(flipCoords(x1,y1,x2,y2));}
|
||||
function makeObj(a)
|
||||
{return{x:a[0],y:a[1],x2:a[2],y2:a[3],w:a[2]-a[0],h:a[3]-a[1]};}
|
||||
return{flipCoords:flipCoords,setPressed:setPressed,setCurrent:setCurrent,getOffset:getOffset,moveOffset:moveOffset,getCorner:getCorner,getFixed:getFixed};}());var Shade=(function(){var enabled=false,holder=$('<div />').css({position:'absolute',zIndex:240,opacity:0}),shades={top:createShade(),left:createShade().height(boundy),right:createShade().height(boundy),bottom:createShade()};function resizeShades(w,h){shades.left.css({height:px(h)});shades.right.css({height:px(h)});}
|
||||
function updateAuto()
|
||||
{return updateShade(Coords.getFixed());}
|
||||
function updateShade(c)
|
||||
{shades.top.css({left:px(c.x),width:px(c.w),height:px(c.y)});shades.bottom.css({top:px(c.y2),left:px(c.x),width:px(c.w),height:px(boundy-c.y2)});shades.right.css({left:px(c.x2),width:px(boundx-c.x2)});shades.left.css({width:px(c.x)});}
|
||||
function createShade(){return $('<div />').css({position:'absolute',backgroundColor:options.shadeColor||options.bgColor}).appendTo(holder);}
|
||||
function enableShade(){if(!enabled){enabled=true;holder.insertBefore($img);updateAuto();Selection.setBgOpacity(1,0,1);$img2.hide();setBgColor(options.shadeColor||options.bgColor,1);if(Selection.isAwake())
|
||||
{setOpacity(options.bgOpacity,1);}
|
||||
else setOpacity(1,1);}}
|
||||
function setBgColor(color,now){colorChangeMacro(getShades(),color,now);}
|
||||
function disableShade(){if(enabled){holder.remove();$img2.show();enabled=false;if(Selection.isAwake()){Selection.setBgOpacity(options.bgOpacity,1,1);}else{Selection.setBgOpacity(1,1,1);Selection.disableHandles();}
|
||||
colorChangeMacro($div,0,1);}}
|
||||
function setOpacity(opacity,now){if(enabled){if(options.bgFade&&!now){holder.animate({opacity:1-opacity},{queue:false,duration:options.fadeTime});}
|
||||
else holder.css({opacity:1-opacity});}}
|
||||
function refreshAll(){options.shade?enableShade():disableShade();if(Selection.isAwake())setOpacity(options.bgOpacity);}
|
||||
function getShades(){return holder.children();}
|
||||
return{update:updateAuto,updateRaw:updateShade,getShades:getShades,setBgColor:setBgColor,enable:enableShade,disable:disableShade,resize:resizeShades,refresh:refreshAll,opacity:setOpacity};}());var Selection=(function(){var awake,hdep=370,borders={},handle={},dragbar={},seehandles=false;function insertBorder(type)
|
||||
{var jq=$('<div />').css({position:'absolute',opacity:options.borderOpacity}).addClass(cssClass(type));$img_holder.append(jq);return jq;}
|
||||
function dragDiv(ord,zi)
|
||||
{var jq=$('<div />').mousedown(createDragger(ord)).css({cursor:ord+'-resize',position:'absolute',zIndex:zi}).addClass('ord-'+ord);if(Touch.support){jq.bind('touchstart.jcrop',Touch.createDragger(ord));}
|
||||
$hdl_holder.append(jq);return jq;}
|
||||
function insertHandle(ord)
|
||||
{var hs=options.handleSize,div=dragDiv(ord,hdep++).css({opacity:options.handleOpacity}).addClass(cssClass('handle'));if(hs){div.width(hs).height(hs);}
|
||||
return div;}
|
||||
function insertDragbar(ord)
|
||||
{return dragDiv(ord,hdep++).addClass('jcrop-dragbar');}
|
||||
function createDragbars(li)
|
||||
{var i;for(i=0;i<li.length;i++){dragbar[li[i]]=insertDragbar(li[i]);}}
|
||||
function createBorders(li)
|
||||
{var cl,i;for(i=0;i<li.length;i++){switch(li[i]){case'n':cl='hline';break;case's':cl='hline bottom';break;case'e':cl='vline right';break;case'w':cl='vline';break;}
|
||||
borders[li[i]]=insertBorder(cl);}}
|
||||
function createHandles(li)
|
||||
{var i;for(i=0;i<li.length;i++){handle[li[i]]=insertHandle(li[i]);}}
|
||||
function moveto(x,y)
|
||||
{if(!options.shade){$img2.css({top:px(-y),left:px(-x)});}
|
||||
$sel.css({top:px(y),left:px(x)});}
|
||||
function resize(w,h)
|
||||
{$sel.width(Math.round(w)).height(Math.round(h));}
|
||||
function refresh()
|
||||
{var c=Coords.getFixed();Coords.setPressed([c.x,c.y]);Coords.setCurrent([c.x2,c.y2]);updateVisible();}
|
||||
function updateVisible(select)
|
||||
{if(awake){return update(select);}}
|
||||
function update(select)
|
||||
{var c=Coords.getFixed();resize(c.w,c.h);moveto(c.x,c.y);if(options.shade)Shade.updateRaw(c);awake||show();if(select){options.onSelect.call(api,unscale(c));}else{options.onChange.call(api,unscale(c));}}
|
||||
function setBgOpacity(opacity,force,now)
|
||||
{if(!awake&&!force)return;if(options.bgFade&&!now){$img.animate({opacity:opacity},{queue:false,duration:options.fadeTime});}else{$img.css('opacity',opacity);}}
|
||||
function show()
|
||||
{$sel.show();if(options.shade)Shade.opacity(bgopacity);else setBgOpacity(bgopacity,true);awake=true;}
|
||||
function release()
|
||||
{disableHandles();$sel.hide();if(options.shade)Shade.opacity(1);else setBgOpacity(1);awake=false;options.onRelease.call(api);}
|
||||
function showHandles()
|
||||
{if(seehandles){$hdl_holder.show();}}
|
||||
function enableHandles()
|
||||
{seehandles=true;if(options.allowResize){$hdl_holder.show();return true;}}
|
||||
function disableHandles()
|
||||
{seehandles=false;$hdl_holder.hide();}
|
||||
function animMode(v)
|
||||
{if(v){animating=true;disableHandles();}else{animating=false;enableHandles();}}
|
||||
function done()
|
||||
{animMode(false);refresh();}
|
||||
if(options.dragEdges&&$.isArray(options.createDragbars))
|
||||
createDragbars(options.createDragbars);if($.isArray(options.createHandles))
|
||||
createHandles(options.createHandles);if(options.drawBorders&&$.isArray(options.createBorders))
|
||||
createBorders(options.createBorders);$(document).bind('touchstart.jcrop-ios',Touch.fixTouchSupport);var $track=newTracker().mousedown(createDragger('move')).css({cursor:'move',position:'absolute',zIndex:360});if(Touch.support){$track.bind('touchstart.jcrop',Touch.createDragger('move'));}
|
||||
$img_holder.append($track);disableHandles();return{updateVisible:updateVisible,update:update,release:release,refresh:refresh,isAwake:function(){return awake;},setCursor:function(cursor){$track.css('cursor',cursor);},enableHandles:enableHandles,enableOnly:function(){seehandles=true;},showHandles:showHandles,disableHandles:disableHandles,animMode:animMode,setBgOpacity:setBgOpacity,done:done};}());var Tracker=(function(){var onMove=function(){},onDone=function(){},trackDoc=options.trackDocument;function toFront(touch)
|
||||
{$trk.css({zIndex:450});if(touch)
|
||||
$(document).bind('touchmove.jcrop',trackTouchMove).bind('touchend.jcrop',trackTouchEnd);else if(trackDoc)
|
||||
$(document).bind('mousemove.jcrop',trackMove).bind('mouseup.jcrop',trackUp);}
|
||||
function toBack()
|
||||
{$trk.css({zIndex:290});$(document).unbind('.jcrop');}
|
||||
function trackMove(e)
|
||||
{onMove(mouseAbs(e));return false;}
|
||||
function trackUp(e)
|
||||
{e.preventDefault();e.stopPropagation();if(btndown){btndown=false;onDone(mouseAbs(e));if(Selection.isAwake()){options.onSelect.call(api,unscale(Coords.getFixed()));}
|
||||
toBack();onMove=function(){};onDone=function(){};}
|
||||
return false;}
|
||||
function activateHandlers(move,done,touch)
|
||||
{btndown=true;onMove=move;onDone=done;toFront(touch);return false;}
|
||||
function trackTouchMove(e)
|
||||
{onMove(mouseAbs(Touch.cfilter(e)));return false;}
|
||||
function trackTouchEnd(e)
|
||||
{return trackUp(Touch.cfilter(e));}
|
||||
function setCursor(t)
|
||||
{$trk.css('cursor',t);}
|
||||
if(!trackDoc){$trk.mousemove(trackMove).mouseup(trackUp).mouseout(trackUp);}
|
||||
$img.before($trk);return{activateHandlers:activateHandlers,setCursor:setCursor};}());var KeyManager=(function(){var $keymgr=$('<input type="radio" />').css({position:'fixed',left:'-120px',width:'12px'}).addClass('jcrop-keymgr'),$keywrap=$('<div />').css({position:'absolute',overflow:'hidden'}).append($keymgr);function watchKeys()
|
||||
{if(options.keySupport){$keymgr.show();$keymgr.focus();}}
|
||||
function onBlur(e)
|
||||
{$keymgr.hide();}
|
||||
function doNudge(e,x,y)
|
||||
{if(options.allowMove){Coords.moveOffset([x,y]);Selection.updateVisible(true);}
|
||||
e.preventDefault();e.stopPropagation();}
|
||||
function parseKey(e)
|
||||
{if(e.ctrlKey||e.metaKey){return true;}
|
||||
shift_down=e.shiftKey?true:false;var nudge=shift_down?10:1;switch(e.keyCode){case 37:doNudge(e,-nudge,0);break;case 39:doNudge(e,nudge,0);break;case 38:doNudge(e,0,-nudge);break;case 40:doNudge(e,0,nudge);break;case 27:if(options.allowSelect)Selection.release();break;case 9:return true;}
|
||||
return false;}
|
||||
if(options.keySupport){$keymgr.keydown(parseKey).blur(onBlur);if(ie6mode||!options.fixedSupport){$keymgr.css({position:'absolute',left:'-20px'});$keywrap.append($keymgr).insertBefore($img);}else{$keymgr.insertBefore($img);}}
|
||||
return{watchKeys:watchKeys};}());function setClass(cname)
|
||||
{$div.removeClass().addClass(cssClass('holder')).addClass(cname);}
|
||||
function animateTo(a,callback)
|
||||
{var x1=a[0]/xscale,y1=a[1]/yscale,x2=a[2]/xscale,y2=a[3]/yscale;if(animating){return;}
|
||||
var animto=Coords.flipCoords(x1,y1,x2,y2),c=Coords.getFixed(),initcr=[c.x,c.y,c.x2,c.y2],animat=initcr,interv=options.animationDelay,ix1=animto[0]-initcr[0],iy1=animto[1]-initcr[1],ix2=animto[2]-initcr[2],iy2=animto[3]-initcr[3],pcent=0,velocity=options.swingSpeed;x1=animat[0];y1=animat[1];x2=animat[2];y2=animat[3];Selection.animMode(true);var anim_timer;function queueAnimator(){window.setTimeout(animator,interv);}
|
||||
var animator=(function(){return function(){pcent+=(100-pcent)/velocity;animat[0]=Math.round(x1+((pcent/100)*ix1));animat[1]=Math.round(y1+((pcent/100)*iy1));animat[2]=Math.round(x2+((pcent/100)*ix2));animat[3]=Math.round(y2+((pcent/100)*iy2));if(pcent>=99.8){pcent=100;}
|
||||
if(pcent<100){setSelectRaw(animat);queueAnimator();}else{Selection.done();Selection.animMode(false);if(typeof(callback)==='function'){callback.call(api);}}};}());queueAnimator();}
|
||||
function setSelect(rect)
|
||||
{setSelectRaw([rect[0]/xscale,rect[1]/yscale,rect[2]/xscale,rect[3]/yscale]);options.onSelect.call(api,unscale(Coords.getFixed()));Selection.enableHandles();}
|
||||
function setSelectRaw(l)
|
||||
{Coords.setPressed([l[0],l[1]]);Coords.setCurrent([l[2],l[3]]);Selection.update();}
|
||||
function tellSelect()
|
||||
{return unscale(Coords.getFixed());}
|
||||
function tellScaled()
|
||||
{return Coords.getFixed();}
|
||||
function setOptionsNew(opt)
|
||||
{setOptions(opt);interfaceUpdate();}
|
||||
function disableCrop()
|
||||
{options.disabled=true;Selection.disableHandles();Selection.setCursor('default');Tracker.setCursor('default');}
|
||||
function enableCrop()
|
||||
{options.disabled=false;interfaceUpdate();}
|
||||
function cancelCrop()
|
||||
{Selection.done();Tracker.activateHandlers(null,null);}
|
||||
function destroy()
|
||||
{$(document).unbind('touchstart.jcrop-ios',Touch.fixTouchSupport);$div.remove();$origimg.show();$origimg.css('visibility','visible');$(obj).removeData('Jcrop');}
|
||||
function setImage(src,callback)
|
||||
{Selection.release();disableCrop();var img=new Image();img.onload=function(){var iw=img.width;var ih=img.height;var bw=options.boxWidth;var bh=options.boxHeight;$img.width(iw).height(ih);$img.attr('src',src);$img2.attr('src',src);presize($img,bw,bh);boundx=$img.width();boundy=$img.height();$img2.width(boundx).height(boundy);$trk.width(boundx+(bound*2)).height(boundy+(bound*2));$div.width(boundx).height(boundy);Shade.resize(boundx,boundy);enableCrop();if(typeof(callback)==='function'){callback.call(api);}};img.src=src;}
|
||||
function colorChangeMacro($obj,color,now){var mycolor=color||options.bgColor;if(options.bgFade&&supportsColorFade()&&options.fadeTime&&!now){$obj.animate({backgroundColor:mycolor},{queue:false,duration:options.fadeTime});}else{$obj.css('backgroundColor',mycolor);}}
|
||||
function interfaceUpdate(alt)
|
||||
{if(options.allowResize){if(alt){Selection.enableOnly();}else{Selection.enableHandles();}}else{Selection.disableHandles();}
|
||||
Tracker.setCursor(options.allowSelect?'crosshair':'default');Selection.setCursor(options.allowMove?'move':'default');if(options.hasOwnProperty('trueSize')){xscale=options.trueSize[0]/boundx;yscale=options.trueSize[1]/boundy;}
|
||||
if(options.hasOwnProperty('setSelect')){setSelect(options.setSelect);Selection.done();delete(options.setSelect);}
|
||||
Shade.refresh();if(options.bgColor!=bgcolor){colorChangeMacro(options.shade?Shade.getShades():$div,options.shade?(options.shadeColor||options.bgColor):options.bgColor);bgcolor=options.bgColor;}
|
||||
if(bgopacity!=options.bgOpacity){bgopacity=options.bgOpacity;if(options.shade)Shade.refresh();else Selection.setBgOpacity(bgopacity);}
|
||||
xlimit=options.maxSize[0]||0;ylimit=options.maxSize[1]||0;xmin=options.minSize[0]||0;ymin=options.minSize[1]||0;if(options.hasOwnProperty('outerImage')){$img.attr('src',options.outerImage);delete(options.outerImage);}
|
||||
Selection.refresh();}
|
||||
if(Touch.support)$trk.bind('touchstart.jcrop',Touch.newSelection);$hdl_holder.hide();interfaceUpdate(true);var api={setImage:setImage,animateTo:animateTo,setSelect:setSelect,setOptions:setOptionsNew,tellSelect:tellSelect,tellScaled:tellScaled,setClass:setClass,disable:disableCrop,enable:enableCrop,cancel:cancelCrop,release:Selection.release,destroy:destroy,focus:KeyManager.watchKeys,getBounds:function(){return[boundx*xscale,boundy*yscale];},getWidgetSize:function(){return[boundx,boundy];},getScaleFactor:function(){return[xscale,yscale];},getOptions:function(){return options;},ui:{holder:$div,selection:$sel}};if(is_msie)$div.bind('selectstart',function(){return false;});$origimg.data('Jcrop',api);return api;};$.fn.Jcrop=function(options,callback)
|
||||
{var api;this.each(function(){if($(this).data('Jcrop')){if(options==='api')return $(this).data('Jcrop');else $(this).data('Jcrop').setOptions(options);}
|
||||
else{if(this.tagName=='IMG')
|
||||
$.Jcrop.Loader(this,function(){$(this).css({display:'block',visibility:'hidden'});api=$.Jcrop(this,options);if($.isFunction(callback))callback.call(api);});else{$(this).css({display:'block',visibility:'hidden'});api=$.Jcrop(this,options);if($.isFunction(callback))callback.call(api);}}});return this;};$.Jcrop.Loader=function(imgobj,success,error){var $img=$(imgobj),img=$img[0];function completeCheck(){if(img.complete){$img.unbind('.jcloader');if($.isFunction(success))success.call(img);}
|
||||
else window.setTimeout(completeCheck,50);}
|
||||
$img.bind('load.jcloader',completeCheck).bind('error.jcloader',function(e){$img.unbind('.jcloader');if($.isFunction(error))error.call(img);});if(img.complete&&$.isFunction(success)){$img.unbind('.jcloader');success.call(img);}};$.Jcrop.defaults={allowSelect:true,allowMove:true,allowResize:true,trackDocument:true,baseClass:'jcrop',addClass:null,bgColor:'black',bgOpacity:0.6,bgFade:false,borderOpacity:0.4,handleOpacity:0.5,handleSize:null,aspectRatio:0,keySupport:true,createHandles:['n','s','e','w','nw','ne','se','sw'],createDragbars:['n','s','e','w'],createBorders:['n','s','e','w'],drawBorders:true,dragEdges:true,fixedSupport:true,touchSupport:null,shade:null,boxWidth:0,boxHeight:0,boundary:2,fadeTime:400,animationDelay:20,swingSpeed:3,minSelect:[0,0],maxSize:[0,0],minSize:[0,0],onChange:function(){},onSelect:function(){},onDblClick:function(){},onRelease:function(){}};}(jQuery));
|
||||
|
|
@ -31,4 +31,6 @@
|
|||
=require ../../vendor/flot/jquery.flot.resize.js
|
||||
=require ../../vendor/flot/jquery.flot.time.js
|
||||
|
||||
=require ../../vendor/jcrop/js/jquery.Jcrop.js
|
||||
|
||||
*/
|
||||
|
|
@ -761,3 +761,54 @@ body.slim-container {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Controls inside toolbar
|
||||
//
|
||||
|
||||
[data-control=toolbar] {
|
||||
.form-control {
|
||||
display: inline-block;
|
||||
margin-right: 15px;
|
||||
|
||||
&.width-50 {
|
||||
width: 50px;
|
||||
}
|
||||
|
||||
&.width-100 {
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
&.width-150 {
|
||||
width: 150px;
|
||||
}
|
||||
}
|
||||
|
||||
input[type=text].form-control, label {
|
||||
position: relative;
|
||||
top: 5px;
|
||||
}
|
||||
|
||||
label {
|
||||
margin-right: 7px;
|
||||
|
||||
&.standalone {
|
||||
margin-right: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
.select2-container {
|
||||
display: inline-block;
|
||||
width: auto;
|
||||
height: 36px;
|
||||
|
||||
.select2-choice {
|
||||
height: 34px;
|
||||
line-height: 34px;
|
||||
}
|
||||
}
|
||||
|
||||
select.form-control.custom-select {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
|
@ -52,6 +52,10 @@
|
|||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
&.standalone {
|
||||
margin-right: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-group {
|
||||
|
|
|
|||
|
|
@ -38,3 +38,4 @@
|
|||
// Vendor
|
||||
@import "../vendor/sweet-alert/sweet-alert.less";
|
||||
@import "../vendor/select2/select2.css";
|
||||
@import "../vendor/jcrop/css/jquery.Jcrop.min.css";
|
||||
|
|
|
|||
|
|
@ -0,0 +1,22 @@
|
|||
Copyright (c) 2011 Tapmodo Interactive LLC,
|
||||
http://github.com/tapmodo/Jcrop
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
|
|
@ -0,0 +1 @@
|
|||
There's a hack in line 1090 in jquery.Jcrop.js. The hack prevents DOM element leakage through the event that is not unbound in the destroy() method. --ab Apr 08 2015
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
Jcrop Image Cropping Plugin
|
||||
===========================
|
||||
|
||||
Jcrop is the quick and easy way to add image cropping functionality to
|
||||
your web application. It combines the ease-of-use of a typical jQuery
|
||||
plugin with a powerful cross-platform DHTML cropping engine that is
|
||||
faithful to familiar desktop graphics applications.
|
||||
|
||||
Cross-platform Compatibility
|
||||
----------------------------
|
||||
|
||||
* Firefox 2+
|
||||
* Safari 3+
|
||||
* Opera 9.5+
|
||||
* Google Chrome 0.2+
|
||||
* Internet Explorer 6+
|
||||
|
||||
Feature Overview
|
||||
----------------
|
||||
|
||||
* Attaches unobtrusively to any image
|
||||
* Supports aspect ratio locking
|
||||
* Supports minSize/maxSize setting
|
||||
* Callbacks for selection done, or while moving
|
||||
* Keyboard support for nudging selection
|
||||
* API features to create interactivity, including animation
|
||||
* Support for CSS styling
|
||||
* Experimental touch-screen support (iOS, Android, etc)
|
||||
|
||||
Contributors
|
||||
============
|
||||
|
||||
**Special thanks to the following contributors:**
|
||||
|
||||
* [Bruno Agutoli](mailto:brunotla1@gmail.com)
|
||||
* dhorrigan
|
||||
* Phil-B
|
||||
* jaymecd
|
||||
* all others who have committed their time and effort to help improve Jcrop
|
||||
|
||||
MIT License
|
||||
===========
|
||||
|
||||
**Jcrop is free software under MIT License.**
|
||||
|
||||
#### Copyright (c) 2008-2012 Tapmodo Interactive LLC,<br />http://github.com/tapmodo/Jcrop
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 329 B |
|
|
@ -0,0 +1,29 @@
|
|||
/* jquery.Jcrop.min.css v0.9.12 (build:20130126) */
|
||||
.jcrop-holder{direction:ltr;text-align:left;}
|
||||
.jcrop-vline,.jcrop-hline{background:#FFF url(Jcrop.gif);font-size:0;position:absolute;}
|
||||
.jcrop-vline{height:100%;width:1px!important;}
|
||||
.jcrop-vline.right{right:0;}
|
||||
.jcrop-hline{height:1px!important;width:100%;}
|
||||
.jcrop-hline.bottom{bottom:0;}
|
||||
.jcrop-tracker{-webkit-tap-highlight-color:transparent;-webkit-touch-callout:none;-webkit-user-select:none;height:100%;width:100%;}
|
||||
.jcrop-handle{background-color:#333;border:1px #EEE solid;font-size:1px;height:7px;width:7px;}
|
||||
.jcrop-handle.ord-n{left:50%;margin-left:-4px;margin-top:-4px;top:0;}
|
||||
.jcrop-handle.ord-s{bottom:0;left:50%;margin-bottom:-4px;margin-left:-4px;}
|
||||
.jcrop-handle.ord-e{margin-right:-4px;margin-top:-4px;right:0;top:50%;}
|
||||
.jcrop-handle.ord-w{left:0;margin-left:-4px;margin-top:-4px;top:50%;}
|
||||
.jcrop-handle.ord-nw{left:0;margin-left:-4px;margin-top:-4px;top:0;}
|
||||
.jcrop-handle.ord-ne{margin-right:-4px;margin-top:-4px;right:0;top:0;}
|
||||
.jcrop-handle.ord-se{bottom:0;margin-bottom:-4px;margin-right:-4px;right:0;}
|
||||
.jcrop-handle.ord-sw{bottom:0;left:0;margin-bottom:-4px;margin-left:-4px;}
|
||||
.jcrop-dragbar.ord-n,.jcrop-dragbar.ord-s{height:7px;width:100%;}
|
||||
.jcrop-dragbar.ord-e,.jcrop-dragbar.ord-w{height:100%;width:7px;}
|
||||
.jcrop-dragbar.ord-n{margin-top:-4px;}
|
||||
.jcrop-dragbar.ord-s{bottom:0;margin-bottom:-4px;}
|
||||
.jcrop-dragbar.ord-e{margin-right:-4px;right:0;}
|
||||
.jcrop-dragbar.ord-w{margin-left:-4px;}
|
||||
.jcrop-light .jcrop-vline,.jcrop-light .jcrop-hline{background:#FFF;filter:alpha(opacity=70)!important;opacity:.70!important;}
|
||||
.jcrop-light .jcrop-handle{-moz-border-radius:3px;-webkit-border-radius:3px;background-color:#000;border-color:#FFF;border-radius:3px;}
|
||||
.jcrop-dark .jcrop-vline,.jcrop-dark .jcrop-hline{background:#000;filter:alpha(opacity=70)!important;opacity:.7!important;}
|
||||
.jcrop-dark .jcrop-handle{-moz-border-radius:3px;-webkit-border-radius:3px;background-color:#FFF;border-color:#000;border-radius:3px;}
|
||||
.solid-line .jcrop-vline,.solid-line .jcrop-hline{background:#FFF;}
|
||||
.jcrop-holder img,img.jcrop-preview{max-width:none;}
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -15,6 +15,7 @@ use Exception;
|
|||
use BackendAuth;
|
||||
use Backend\Models\UserPreferences;
|
||||
use Backend\Models\BackendPreferences;
|
||||
use Cms\Widgets\MediaManager;
|
||||
use System\Classes\ErrorHandler;
|
||||
use October\Rain\Exception\AjaxException;
|
||||
use October\Rain\Exception\SystemException;
|
||||
|
|
@ -147,6 +148,10 @@ class Controller extends Extendable
|
|||
$this->viewPath = $this->configPath = $this->guessViewPath();
|
||||
|
||||
parent::__construct();
|
||||
|
||||
// Media Manager widget is available on all back-end pages
|
||||
$manager = new MediaManager($this, 'ocmediamanager');
|
||||
$manager->bindToController();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1877,7 +1877,7 @@ self.$form.trigger('change')
|
|||
if(self.$dataLocker)
|
||||
self.$dataLocker.val(self.syncBefore(this.$editor.html()))}}
|
||||
if(this.options.fullpage){redactorOptions.fullpage=true}
|
||||
redactorOptions.plugins=['cleanup','fullscreen','figure','quote','table','mediamanager','image']
|
||||
redactorOptions.plugins=['cleanup','fullscreen','figure','quote','table','mediamanager']
|
||||
redactorOptions.buttons=['formatting','bold','italic','unorderedlist','orderedlist','link','horizontalrule','html'],this.$textarea.redactor(redactorOptions)}
|
||||
RichEditor.prototype.build=function(redactor){this.updateLayout()
|
||||
$(window).resize($.proxy(this.updateLayout,this))
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@
|
|||
// redactorOptions.plugins = ['cleanup', 'fullscreen', 'figure', 'image', 'quote', 'table']
|
||||
// redactorOptions.buttons = ['formatting', 'bold', 'italic', 'unorderedlist', 'orderedlist', 'link', 'horizontalrule', 'html'],
|
||||
|
||||
redactorOptions.plugins = ['cleanup', 'fullscreen', 'figure', 'quote', 'table', 'mediamanager', 'image']
|
||||
redactorOptions.plugins = ['cleanup', 'fullscreen', 'figure', 'quote', 'table', 'mediamanager']
|
||||
redactorOptions.buttons = ['formatting', 'bold', 'italic', 'unorderedlist', 'orderedlist', 'link', 'horizontalrule', 'html'],
|
||||
|
||||
this.$textarea.redactor(redactorOptions)
|
||||
|
|
|
|||
|
|
@ -17,6 +17,11 @@
|
|||
<script src="<?= Backend::skinAsset('assets/js/vendor/modernizr.min.js') ?>"></script>
|
||||
<script src="<?= Backend::skinAsset('assets/js/vendor/vendor-min.js') ?>"></script>
|
||||
<script src="<?= Backend::skinAsset('assets/js/october-min.js') ?>"></script>
|
||||
|
||||
<!-- TODO: combine and load combined -->
|
||||
<script src="<?= Backend::skinAsset('../cms/widgets/mediamanager/assets/js/mediamanager.popup.js') ?>"></script>
|
||||
<script src="<?= Backend::skinAsset('../cms/widgets/mediamanager/assets/js/mediamanager.redactor.js') ?>"></script>
|
||||
|
||||
<?= $this->makeAssets() ?>
|
||||
<?= Block::placeholder('head') ?>
|
||||
<?= $this->makeLayoutPartial('custom_styles') ?>
|
||||
|
|
@ -4,6 +4,7 @@ use ApplicationException;
|
|||
use SystemException;
|
||||
use Config;
|
||||
use Storage;
|
||||
use Lang;
|
||||
use Cache;
|
||||
use Str;
|
||||
|
||||
|
|
@ -385,6 +386,18 @@ class MediaLibrary
|
|||
return $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a public file URL.
|
||||
* @param string $path Specifies the file path relative the the Library root.
|
||||
* @return string
|
||||
*/
|
||||
public function getPathUrl($path)
|
||||
{
|
||||
$path = $this->validatePath($path);
|
||||
|
||||
return $this->storagePath.$path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a file or folder path with the prefixed storage folder.
|
||||
* @param string $path Specifies a path to process.
|
||||
|
|
|
|||
|
|
@ -276,6 +276,17 @@ return [
|
|||
'insert' => 'Insert',
|
||||
'crop_and_insert' => 'Crop & Insert',
|
||||
'select_single_image' => 'Please select a single image.',
|
||||
'selection_not_image' => 'The selected item is not an image.'
|
||||
'selection_not_image' => 'The selected item is not an image.',
|
||||
'restore' => 'Undo all changes',
|
||||
'resize' => 'Resize...',
|
||||
'selection_mode_normal' => 'Normal',
|
||||
'selection_mode_fixed_ratio' => 'Fixed ratio',
|
||||
'selection_mode_fixed_size' => 'Fixed size',
|
||||
'height' => 'Height',
|
||||
'width' => 'Width',
|
||||
'selection_mode' => 'Selection mode',
|
||||
'resize_image' => 'Resize image',
|
||||
'image_size' => 'Image size:',
|
||||
'selected_size' => 'Selected:'
|
||||
]
|
||||
];
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ use URL;
|
|||
use Str;
|
||||
use Lang;
|
||||
use File;
|
||||
use Form;
|
||||
use Input;
|
||||
use Request;
|
||||
use Response;
|
||||
|
|
@ -24,10 +25,15 @@ use October\Rain\Database\Attach\Resizer;
|
|||
class MediaManager extends WidgetBase
|
||||
{
|
||||
const FOLDER_ROOT = '/';
|
||||
|
||||
const VIEW_MODE_GRID = 'grid';
|
||||
const VIEW_MODE_LIST = 'list';
|
||||
const VIEW_MODE_TILES = 'tiles';
|
||||
|
||||
const SELECTION_MODE_NORMAL = 'normal';
|
||||
const SELECTION_MODE_FIXED_RATIO = 'fixed-ratio';
|
||||
const SELECTION_MODE_FIXED_SIZE = 'fixed-size';
|
||||
|
||||
const FILTER_EVERYTHING = 'everything';
|
||||
|
||||
protected $brokenImageHash = null;
|
||||
|
|
@ -203,14 +209,14 @@ class MediaManager extends WidgetBase
|
|||
$paths = Input::get('paths');
|
||||
|
||||
if (!is_array($paths))
|
||||
throw new SystemException('Invalid input data');
|
||||
throw new ApplicationException('Invalid input data');
|
||||
|
||||
$library = MediaLibrary::instance();
|
||||
|
||||
$filesToDelete = [];
|
||||
foreach ($paths as $pathInfo) {
|
||||
if (!isset($pathInfo['path']) || !isset($pathInfo['type']))
|
||||
throw new SystemException('Invalid input data');
|
||||
throw new ApplicationException('Invalid input data');
|
||||
|
||||
if ($pathInfo['type'] == 'file')
|
||||
$filesToDelete[] = $pathInfo['path'];
|
||||
|
|
@ -300,7 +306,7 @@ class MediaManager extends WidgetBase
|
|||
{
|
||||
$exclude = Input::get('exclude', []);
|
||||
if (!is_array($exclude))
|
||||
throw new SystemException('Invalid input data');
|
||||
throw new ApplicationException('Invalid input data');
|
||||
|
||||
$folders = MediaLibrary::instance()->listAllDirectories($exclude);
|
||||
|
||||
|
|
@ -336,11 +342,11 @@ class MediaManager extends WidgetBase
|
|||
|
||||
$files = Input::get('files', []);
|
||||
if (!is_array($files))
|
||||
throw new SystemException('Invalid input data');
|
||||
throw new ApplicationException('Invalid input data');
|
||||
|
||||
$folders = Input::get('folders', []);
|
||||
if (!is_array($folders))
|
||||
throw new SystemException('Invalid input data');
|
||||
throw new ApplicationException('Invalid input data');
|
||||
|
||||
$library = MediaLibrary::instance();
|
||||
|
||||
|
|
@ -376,9 +382,91 @@ class MediaManager extends WidgetBase
|
|||
|
||||
public function onLoadImageCropPopup()
|
||||
{
|
||||
$path = Input::get('path');
|
||||
$path = MediaLibrary::validatePath($path);
|
||||
|
||||
$selectionParams = $this->getSelectionParams();
|
||||
$this->vars['currentSelectionMode'] = $selectionParams['mode'];
|
||||
$this->vars['currentSelectionWidth'] = $selectionParams['width'];
|
||||
$this->vars['currentSelectionHeight'] = $selectionParams['height'];
|
||||
|
||||
$this->vars['cropSessionKey'] = $cropSessionKey = md5(Form::getSessionKey());
|
||||
|
||||
$urlAndSize = $this->getCropEditImageUrlAndSize($path, $cropSessionKey);
|
||||
$this->vars['imageUrl'] = $urlAndSize['url'];
|
||||
$this->vars['dimensions'] = $urlAndSize['dimensions'];
|
||||
|
||||
$width = $urlAndSize['dimensions'][0];
|
||||
$height = $urlAndSize['dimensions'][1] ? $urlAndSize['dimensions'][1] : 1;
|
||||
|
||||
$this->vars['originalRatio'] = round($width/$height, 5);
|
||||
$this->vars['path'] = $path;
|
||||
|
||||
return $this->makePartial('image-crop-popup-body');
|
||||
}
|
||||
|
||||
public function onEndCroppingSession()
|
||||
{
|
||||
$cropSessionKey = Input::get('cropSessionKey');
|
||||
if (!preg_match('/^[0-9a-z]+$/', $cropSessionKey))
|
||||
throw new ApplicationException('Invalid input data');
|
||||
|
||||
$this->removeCropEditDir($cropSessionKey);
|
||||
}
|
||||
|
||||
public function onCropImage()
|
||||
{
|
||||
$imageSrcPath = trim(Input::get('img'));
|
||||
$selectionData = Input::get('selection');
|
||||
$cropSessionKey = Input::get('cropSessionKey');
|
||||
$path = Input::get('path');
|
||||
$path = MediaLibrary::validatePath($path);
|
||||
|
||||
if (!strlen($imageSrcPath))
|
||||
throw new ApplicationException('Invalid input data');
|
||||
|
||||
if (!preg_match('/^[0-9a-z]+$/', $cropSessionKey))
|
||||
throw new ApplicationException('Invalid input data');
|
||||
|
||||
if (!is_array($selectionData))
|
||||
throw new ApplicationException('Invalid input data');
|
||||
|
||||
$result = $this->cropImage($imageSrcPath, $selectionData, $cropSessionKey, $path);
|
||||
|
||||
$selectionMode = Input::get('selectionMode');
|
||||
$selectionWidth = Input::get('selectionWidth');
|
||||
$selectionHeight = Input::get('selectionHeight');
|
||||
|
||||
$this->setSelectionParams($selectionMode, $selectionWidth, $selectionHeight);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function onResizeImage()
|
||||
{
|
||||
$cropSessionKey = Input::get('cropSessionKey');
|
||||
if (!preg_match('/^[0-9a-z]+$/', $cropSessionKey))
|
||||
throw new ApplicationException('Invalid input data');
|
||||
|
||||
$width = trim(Input::get('width'));
|
||||
if (!strlen($width) || !ctype_digit($width))
|
||||
throw new ApplicationException('Invalid input data');
|
||||
|
||||
$height = trim(Input::get('height'));
|
||||
if (!strlen($height) || !ctype_digit($height))
|
||||
throw new ApplicationException('Invalid input data');
|
||||
|
||||
$path = Input::get('path');
|
||||
$path = MediaLibrary::validatePath($path);
|
||||
|
||||
$params = array(
|
||||
'width' => $width,
|
||||
'height' => $height
|
||||
);
|
||||
|
||||
return $this->getCropEditImageUrlAndSize($path, $cropSessionKey, $params);
|
||||
}
|
||||
|
||||
//
|
||||
// Methods for th internal use
|
||||
//
|
||||
|
|
@ -447,7 +535,7 @@ class MediaManager extends WidgetBase
|
|||
MediaLibraryItem::FILE_TYPE_AUDIO,
|
||||
MediaLibraryItem::FILE_TYPE_DOCUMENT,
|
||||
MediaLibraryItem::FILE_TYPE_VIDEO]))
|
||||
throw new SystemException('Invalid input data');
|
||||
throw new ApplicationException('Invalid input data');
|
||||
|
||||
return $this->putSession('media_filter', $filter);
|
||||
}
|
||||
|
|
@ -473,7 +561,7 @@ class MediaManager extends WidgetBase
|
|||
MediaLibrary::SORT_BY_TITLE,
|
||||
MediaLibrary::SORT_BY_SIZE,
|
||||
MediaLibrary::SORT_BY_MODIFIED]))
|
||||
throw new SystemException('Invalid input data');
|
||||
throw new ApplicationException('Invalid input data');
|
||||
|
||||
return $this->putSession('media_sort_by', $sortBy);
|
||||
}
|
||||
|
|
@ -483,6 +571,51 @@ class MediaManager extends WidgetBase
|
|||
return $this->getSession('media_sort_by', MediaLibrary::SORT_BY_TITLE);
|
||||
}
|
||||
|
||||
protected function getSelectionParams()
|
||||
{
|
||||
$result = $this->getSession('media_crop_selection_params');
|
||||
|
||||
if ($result) {
|
||||
if (!isset($result['mode']))
|
||||
$result['mode'] = MediaManager::SELECTION_MODE_NORMAL;
|
||||
|
||||
if (!isset($result['width']))
|
||||
$result['width'] = null;
|
||||
|
||||
if (!isset($result['height']))
|
||||
$result['height'] = null;
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
return [
|
||||
'mode'=>MediaManager::SELECTION_MODE_NORMAL,
|
||||
'width'=>null,
|
||||
'height'=>null
|
||||
];
|
||||
}
|
||||
|
||||
protected function setSelectionParams($selectionMode, $selectionWidth, $selectionHeight)
|
||||
{
|
||||
if (!in_array($selectionMode, [
|
||||
MediaManager::SELECTION_MODE_NORMAL,
|
||||
MediaManager::SELECTION_MODE_FIXED_RATIO,
|
||||
MediaManager::SELECTION_MODE_FIXED_SIZE]))
|
||||
throw new ApplicationException('Invalid input data');
|
||||
|
||||
if (strlen($selectionWidth) && !ctype_digit($selectionWidth))
|
||||
throw new ApplicationException('Invalid input data');
|
||||
|
||||
if (strlen($selectionHeight) && !ctype_digit($selectionHeight))
|
||||
throw new ApplicationException('Invalid input data');
|
||||
|
||||
return $this->putSession('media_crop_selection_params', [
|
||||
'mode'=>$selectionMode,
|
||||
'width'=>$selectionWidth,
|
||||
'height'=>$selectionHeight
|
||||
]);
|
||||
}
|
||||
|
||||
protected function setSidebarVisible($visible)
|
||||
{
|
||||
return $this->putSession('sideba_visible', !!$visible);
|
||||
|
|
@ -540,7 +673,7 @@ class MediaManager extends WidgetBase
|
|||
protected function setViewMode($viewMode)
|
||||
{
|
||||
if (!in_array($viewMode, [self::VIEW_MODE_GRID, self::VIEW_MODE_LIST, self::VIEW_MODE_TILES]))
|
||||
throw new SystemException('Invalid input data');
|
||||
throw new ApplicationException('Invalid input data');
|
||||
|
||||
return $this->putSession('view_mode', $viewMode);
|
||||
}
|
||||
|
|
@ -807,4 +940,151 @@ class MediaManager extends WidgetBase
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// Cropping
|
||||
//
|
||||
|
||||
protected function getCropSessionDirPath($cropSessionKey)
|
||||
{
|
||||
return $this->getThumbnailDirectory().'edit-crop-'.$cropSessionKey;
|
||||
}
|
||||
|
||||
protected function getCropEditImageUrlAndSize($path, $cropSessionKey, $params = null)
|
||||
{
|
||||
$sessionDirectoryPath = $this->getCropSessionDirPath($cropSessionKey);
|
||||
$fullSessionDirectoryPath = temp_path($sessionDirectoryPath);
|
||||
$sessionDirectoryCreated = false;
|
||||
|
||||
if (!File::isDirectory($fullSessionDirectoryPath)) {
|
||||
File::makeDirectory($fullSessionDirectoryPath, 0777, true, true);
|
||||
$sessionDirectoryCreated = true;
|
||||
}
|
||||
|
||||
$tempFilePath = null;
|
||||
|
||||
try {
|
||||
$extension = pathinfo($path, PATHINFO_EXTENSION);
|
||||
$library = MediaLibrary::instance();
|
||||
$originalThumbFileName = 'original.'.$extension;
|
||||
|
||||
if (!$params) {
|
||||
// If the target dimensions are not provided, save the original image to the
|
||||
// crop session directory and return its URL.
|
||||
|
||||
$tempFilePath = $fullSessionDirectoryPath.'/'.$originalThumbFileName;
|
||||
|
||||
if (!@File::put($tempFilePath, $library->get($path)))
|
||||
throw new SystemException('Error saving remote file to a temporary location.');
|
||||
|
||||
$url = $this->getThumbnailImageUrl($sessionDirectoryPath.'/'.$originalThumbFileName);
|
||||
$dimensions = getimagesize($tempFilePath);
|
||||
|
||||
return [
|
||||
'url' => $url,
|
||||
'dimensions' => $dimensions
|
||||
];
|
||||
} else {
|
||||
// If the target dimensions are provided, resize the original image and return its URL
|
||||
// and dimensions.
|
||||
|
||||
$originalFilePath = $fullSessionDirectoryPath.'/'.$originalThumbFileName;
|
||||
if (!File::isFile($originalFilePath))
|
||||
throw new SystemException('The original image is not found in the cropping session directory.');
|
||||
|
||||
$resizedThumbFileName = 'resized-'.$params['width'].'-'.$params['height'].'.'.$extension;
|
||||
$tempFilePath = $fullSessionDirectoryPath.'/'.$resizedThumbFileName;
|
||||
|
||||
$resizer = Resizer::open($originalFilePath);
|
||||
|
||||
$resizer->resize($params['width'], $params['height'], 'exact');
|
||||
$resizer->save($tempFilePath, 95);
|
||||
|
||||
$url = $this->getThumbnailImageUrl($sessionDirectoryPath.'/'.$resizedThumbFileName);
|
||||
$dimensions = getimagesize($tempFilePath);
|
||||
|
||||
return [
|
||||
'url' => $url,
|
||||
'dimensions' => $dimensions
|
||||
];
|
||||
}
|
||||
} catch (Exception $ex) {
|
||||
if ($sessionDirectoryCreated)
|
||||
@File::deleteDirectory($fullSessionDirectoryPath);
|
||||
|
||||
if ($tempFilePath)
|
||||
File::delete($tempFilePath);
|
||||
|
||||
throw $ex;
|
||||
}
|
||||
}
|
||||
|
||||
protected function removeCropEditDir($cropSessionKey)
|
||||
{
|
||||
$sessionDirectoryPath = $this->getCropSessionDirPath($cropSessionKey);
|
||||
$fullSessionDirectoryPath = temp_path($sessionDirectoryPath);
|
||||
|
||||
if (File::isDirectory($fullSessionDirectoryPath))
|
||||
@File::deleteDirectory($fullSessionDirectoryPath);
|
||||
}
|
||||
|
||||
protected function cropImage($imageSrcPath, $selectionData, $cropSessionKey, $path)
|
||||
{
|
||||
$originalFileName = basename($path);
|
||||
|
||||
$path = rtrim(dirname($path), '/').'/';
|
||||
$fileName = basename($imageSrcPath);
|
||||
|
||||
if (strpos($fileName, '..') !== false || strpos($fileName, '/') !== false || strpos($fileName, '\\') !== false)
|
||||
throw new SystemException('Invalid image file name.');
|
||||
|
||||
$selectionParams = ['x', 'y', 'w', 'h'];
|
||||
|
||||
foreach ($selectionParams as $paramName) {
|
||||
if (!array_key_exists($paramName, $selectionData))
|
||||
throw new SystemException('Invalid selection data.');
|
||||
|
||||
if (!ctype_digit($selectionData[$paramName]))
|
||||
throw new SystemException('Invalid selection data.');
|
||||
}
|
||||
|
||||
$sessionDirectoryPath = $this->getCropSessionDirPath($cropSessionKey);
|
||||
$fullSessionDirectoryPath = temp_path($sessionDirectoryPath);
|
||||
|
||||
if (!File::isDirectory($fullSessionDirectoryPath))
|
||||
throw new SystemException('The image editing session is not found.');
|
||||
|
||||
// Find the image on the disk and resize it
|
||||
$imagePath = $fullSessionDirectoryPath.'/'.$fileName;
|
||||
if (!File::isFile($imagePath))
|
||||
throw new SystemException('The image is not found on the disk.');
|
||||
|
||||
$extension = pathinfo($originalFileName, PATHINFO_EXTENSION);
|
||||
|
||||
$targetImageName = basename($originalFileName, '.'.$extension).'-'.$selectionData['x']
|
||||
.'-'.$selectionData['y'].'-'.$selectionData['w'].'-'.$selectionData['h'].'-';
|
||||
$targetImageName .= time();
|
||||
$targetImageName .= '.'.$extension;
|
||||
|
||||
$targetTmpPath = $fullSessionDirectoryPath.'/'.$targetImageName;
|
||||
|
||||
if ($selectionData['w'] == 0 || $selectionData['h'] == 0) {
|
||||
// If cropping is not required, copy the oiginal image to the target destination.
|
||||
File::copy($imagePath, $targetTmpPath);
|
||||
}
|
||||
else {
|
||||
$resizer = Resizer::open($imagePath);
|
||||
|
||||
$resizer->resample($selectionData['x'], $selectionData['y'], $selectionData['w'], $selectionData['h'], $selectionData['w'], $selectionData['h']);
|
||||
$resizer->save($targetTmpPath, 95);
|
||||
}
|
||||
|
||||
// Upload the cropped file to the Library
|
||||
$targetPath = $path.'cropped-images/'.$targetImageName;
|
||||
|
||||
$library = MediaLibrary::instance();
|
||||
|
||||
$library->put($targetPath, file_get_contents($targetTmpPath));
|
||||
return $library->getPathUrl($targetPath);
|
||||
}
|
||||
}
|
||||
|
|
@ -25,7 +25,8 @@ div[data-control="media-manager"] .empty-library {
|
|||
}
|
||||
div[data-control="media-manager"] p.thumbnail-error-message {
|
||||
font-size: 12px;
|
||||
margin-top: 25px;
|
||||
margin: 10px;
|
||||
line-height: 160%;
|
||||
color: #bdc3c7;
|
||||
}
|
||||
div[data-control="media-manager"] .media-list {
|
||||
|
|
@ -279,8 +280,10 @@ div[data-control="media-manager"] .sidebar-image-placeholder.no-border {
|
|||
}
|
||||
div[data-control="media-manager"] .sidebar-image-placeholder p {
|
||||
font-size: 12px;
|
||||
margin-top: 25px;
|
||||
margin: 10px;
|
||||
line-height: 160%;
|
||||
color: #bdc3c7;
|
||||
margin-top: 25px;
|
||||
}
|
||||
div[data-control="media-manager"] .list-container {
|
||||
position: relative;
|
||||
|
|
@ -394,6 +397,97 @@ div[data-control="media-manager"] button[data-command="toggle-sidebar"].sidebar-
|
|||
-ms-transform: rotate(180deg) translate(0, 0);
|
||||
transform: rotate(180deg) translate(0, 0);
|
||||
}
|
||||
[data-control="media-manager-crop-tool"] .image_area {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
}
|
||||
[data-control="media-manager-crop-tool"] .image_area .jcrop-holder {
|
||||
background-color: transparent!important;
|
||||
}
|
||||
[data-control="media-manager-crop-tool"] img {
|
||||
cursor: crosshair;
|
||||
display: block;
|
||||
}
|
||||
[data-control="media-manager-crop-tool"].has-rulers .ruler-container .layout-relative {
|
||||
overflow: hidden;
|
||||
}
|
||||
[data-control="media-manager-crop-tool"].has-rulers .ruler-container.horizontal .layout-cell {
|
||||
height: 20px;
|
||||
}
|
||||
[data-control="media-manager-crop-tool"].has-rulers .ruler-container.horizontal .layout-relative {
|
||||
width: 100%;
|
||||
}
|
||||
[data-control="media-manager-crop-tool"].has-rulers .ruler-container.vertical {
|
||||
width: 20px;
|
||||
}
|
||||
[data-control="media-manager-crop-tool"].has-rulers .ruler-container.vertical .layout-relative {
|
||||
height: 100%;
|
||||
}
|
||||
[data-control="media-manager-crop-tool"].has-rulers .ruler {
|
||||
position: absolute;
|
||||
height: 20px;
|
||||
margin-left: -3px;
|
||||
background: #555;
|
||||
}
|
||||
[data-control="media-manager-crop-tool"].has-rulers .ruler ul {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
white-space: nowrap;
|
||||
font-size: 0;
|
||||
}
|
||||
[data-control="media-manager-crop-tool"].has-rulers .ruler li {
|
||||
margin: 0;
|
||||
padding: 0 0 0 40px;
|
||||
list-style: none;
|
||||
display: inline-block;
|
||||
width: 24px;
|
||||
margin: 0px -10px 0px -14px;
|
||||
-webkit-box-sizing: content-box;
|
||||
-moz-box-sizing: content-box;
|
||||
box-sizing: content-box;
|
||||
text-align: left;
|
||||
position: relative;
|
||||
font-size: 10px;
|
||||
line-height: 20px;
|
||||
color: #ecf0f1;
|
||||
font-family: Arial, sans-serif;
|
||||
}
|
||||
[data-control="media-manager-crop-tool"].has-rulers .ruler li:before,
|
||||
[data-control="media-manager-crop-tool"].has-rulers .ruler li:after {
|
||||
content: ' ';
|
||||
position: absolute;
|
||||
border-left: 1px solid #8e8e8e;
|
||||
}
|
||||
[data-control="media-manager-crop-tool"].has-rulers .ruler li:before {
|
||||
height: 20px;
|
||||
top: 0;
|
||||
left: -3px;
|
||||
}
|
||||
[data-control="media-manager-crop-tool"].has-rulers .ruler li:after {
|
||||
height: 3px;
|
||||
bottom: 0;
|
||||
left: 20px;
|
||||
}
|
||||
[data-control="media-manager-crop-tool"].has-rulers .ruler li:first-child:after {
|
||||
display: none;
|
||||
}
|
||||
[data-control="media-manager-crop-tool"].has-rulers .ruler[data-control=v-ruler] {
|
||||
-webkit-transform: rotateZ(90deg);
|
||||
-ms-transform: rotateZ(90deg);
|
||||
transform: rotateZ(90deg);
|
||||
-webkit-transform-origin: left top;
|
||||
-moz-transform-origin: left top;
|
||||
-ms-transform-origin: left top;
|
||||
transform-origin: left top;
|
||||
left: 23px;
|
||||
top: -23px;
|
||||
}
|
||||
[data-control="media-manager-crop-tool"].has-rulers .ruler[data-control=v-ruler] li:after {
|
||||
top: 0;
|
||||
left: auto;
|
||||
}
|
||||
body:not(.no-select) div[data-control="media-manager"] .media-list.tiles li:hover .icon-container {
|
||||
background: #4da7e8 !important;
|
||||
border-color: #2581b8;
|
||||
|
|
@ -408,17 +502,17 @@ body:not(.no-select) div[data-control="media-manager"] .media-list.tiles li:hove
|
|||
body:not(.no-select) div[data-control="media-manager"] .media-list.tiles li:hover h4 {
|
||||
padding-right: 20px!important;
|
||||
}
|
||||
body:not(.no-select) div[data-control="media-manager"] .media-list.list li:hover:not(:active) {
|
||||
body:not(.no-select) div[data-control="media-manager"] .media-list.list li:hover {
|
||||
background: #4da7e8 !important;
|
||||
}
|
||||
body:not(.no-select) div[data-control="media-manager"] .media-list.list li:hover:not(:active) i,
|
||||
body:not(.no-select) div[data-control="media-manager"] .media-list.list li:hover:not(:active) p.size {
|
||||
body:not(.no-select) div[data-control="media-manager"] .media-list.list li:hover i,
|
||||
body:not(.no-select) div[data-control="media-manager"] .media-list.list li:hover p.size {
|
||||
color: #ecf0f1;
|
||||
}
|
||||
body:not(.no-select) div[data-control="media-manager"] .media-list.list li:hover:not(:active) h4 {
|
||||
body:not(.no-select) div[data-control="media-manager"] .media-list.list li:hover h4 {
|
||||
color: white;
|
||||
}
|
||||
body:not(.no-select) div[data-control="media-manager"] .media-list.list li:hover:not(:active) .icon-container {
|
||||
body:not(.no-select) div[data-control="media-manager"] .media-list.list li:hover .icon-container {
|
||||
border-right-color: #4da7e8 !important;
|
||||
}
|
||||
body:not(.no-select) div[data-control="media-manager"] .media-list.list li:hover h4 {
|
||||
|
|
|
|||
|
|
@ -11,6 +11,17 @@
|
|||
|
||||
var MediaManagerImageCropPopup = function(path, options) {
|
||||
this.$popupRootElement = null
|
||||
this.$popupElement = null
|
||||
this.selectionSizeLabel = null
|
||||
this.imageArea = null
|
||||
this.hRulerHolder = null
|
||||
this.vRulerHolder = null
|
||||
|
||||
this.rulersVisible = false
|
||||
this.prevScrollTop = 0
|
||||
this.prevScrollLeft = 0
|
||||
|
||||
this.jCrop = null
|
||||
|
||||
this.options = $.extend({}, MediaManagerImageCropPopup.DEFAULTS, options)
|
||||
this.path = path
|
||||
|
|
@ -26,10 +37,15 @@
|
|||
|
||||
MediaManagerImageCropPopup.prototype.dispose = function() {
|
||||
this.unregisterHandlers()
|
||||
this.removeAttachedControls()
|
||||
|
||||
this.$popupRootElement.remove()
|
||||
this.$popupRootElement = null
|
||||
this.$popupElement = null
|
||||
this.selectionSizeLabel = null
|
||||
this.imageArea = null
|
||||
this.hRulerHolder = null
|
||||
this.vRulerHolder = null
|
||||
|
||||
BaseProto.dispose.call(this)
|
||||
}
|
||||
|
|
@ -51,7 +67,8 @@
|
|||
extraData: data,
|
||||
size: 'adaptive',
|
||||
adaptiveHeight: true,
|
||||
handler: this.options.alias + '::onLoadImageCropPopup'
|
||||
handler: this.options.alias + '::onLoadImageCropPopup',
|
||||
zIndex: 1200 // Media Manager can be opened in a popup, so this new popup should have a higher z-index
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -61,6 +78,31 @@
|
|||
}
|
||||
|
||||
MediaManagerImageCropPopup.prototype.unregisterHandlers = function() {
|
||||
this.$popupElement.off('change', '[data-control="selection-mode"]', this.proxy(this.onSelectionModeChanged))
|
||||
this.$popupElement.off('click', '[data-command]', this.proxy(this.onCommandClick))
|
||||
this.$popupElement.off('shown.oc.popup', 'button[data-command=resize]', this.proxy(this.onResizePopupShown))
|
||||
this.$popupElement.off('hidden.oc.popup', 'button[data-command=resize]', this.proxy(this.onResizePopupHidden))
|
||||
|
||||
if (this.rulersVisible) {
|
||||
var $cropToolRoot = this.$popupElement.find('[data-control=media-manager-crop-tool]')
|
||||
this.imageArea.removeEventListener('scroll', this.proxy(this.onImageScroll))
|
||||
}
|
||||
|
||||
this.getWidthInput().off('change', this.proxy(this.onSizeInputChange))
|
||||
this.getHeightInput().off('change', this.proxy(this.onSizeInputChange))
|
||||
}
|
||||
|
||||
MediaManagerImageCropPopup.prototype.removeAttachedControls = function() {
|
||||
if (this.$popupElement) {
|
||||
// Note - the controls are destroyed and removed from DOM. If they're just destroyed,
|
||||
// the JS plugins could be re-attached to them on window.onresize. -ab
|
||||
this.$popupElement.find('[data-control="selection-mode"]').select2('destroy').remove()
|
||||
this.$popupElement.find('[data-control=toolbar]').toolbar('dispose').remove()
|
||||
|
||||
this.jCrop.destroy()
|
||||
}
|
||||
|
||||
this.jCrop = null
|
||||
}
|
||||
|
||||
MediaManagerImageCropPopup.prototype.hide = function() {
|
||||
|
|
@ -68,10 +110,272 @@
|
|||
this.$popupElement.trigger('close.oc.popup')
|
||||
}
|
||||
|
||||
MediaManagerImageCropPopup.prototype.getSelectionMode = function() {
|
||||
return this.$popupElement.find('[data-control="selection-mode"]').val()
|
||||
}
|
||||
|
||||
MediaManagerImageCropPopup.prototype.initRulers = function() {
|
||||
if (!Modernizr.csstransforms)
|
||||
return
|
||||
|
||||
var $cropToolRoot = this.$popupElement.find('[data-control=media-manager-crop-tool]'),
|
||||
width = $cropToolRoot.data('image-width'),
|
||||
height = $cropToolRoot.data('image-height')
|
||||
|
||||
if (!width || !height)
|
||||
return
|
||||
|
||||
if ($cropToolRoot.width() > width)
|
||||
width = $(window).width()
|
||||
|
||||
if ($cropToolRoot.height() > height)
|
||||
height = $(window).height()
|
||||
|
||||
$cropToolRoot.find('.ruler-container').removeClass('hide')
|
||||
|
||||
$cropToolRoot.addClass('has-rulers')
|
||||
|
||||
var $hRuler = $cropToolRoot.find('[data-control=h-ruler]'),
|
||||
$vRuler = $cropToolRoot.find('[data-control=v-ruler]'),
|
||||
hTicks = width / 40 + 1,
|
||||
vTicks = height / 40 + 1
|
||||
|
||||
this.createRulerTicks($hRuler, hTicks)
|
||||
this.createRulerTicks($vRuler, vTicks)
|
||||
|
||||
this.rulersVisible = true
|
||||
|
||||
this.imageArea.addEventListener('scroll', this.proxy(this.onImageScroll))
|
||||
|
||||
this.hRulerHolder = $cropToolRoot.find('.ruler-container.horizontal .layout-relative').get(0)
|
||||
this.vRulerHolder = $cropToolRoot.find('.ruler-container.vertical .layout-relative').get(0)
|
||||
}
|
||||
|
||||
MediaManagerImageCropPopup.prototype.createRulerTicks = function($rulerElement, count) {
|
||||
var list = document.createElement('ul')
|
||||
|
||||
for (var i=0; i <= count; i++) {
|
||||
var li = document.createElement('li')
|
||||
li.textContent = i*40
|
||||
|
||||
list.appendChild(li)
|
||||
}
|
||||
|
||||
$rulerElement.append(list)
|
||||
}
|
||||
|
||||
MediaManagerImageCropPopup.prototype.initJCrop = function() {
|
||||
this.jCrop = $.Jcrop($(this.imageArea).find('img').get(0), {
|
||||
shade: true,
|
||||
onChange: this.proxy(this.onSelectionChanged)
|
||||
})
|
||||
}
|
||||
|
||||
MediaManagerImageCropPopup.prototype.fixDimensionValue = function(value) {
|
||||
var result = value.replace(/[^0-9]+/, '')
|
||||
|
||||
if (!result.length)
|
||||
result = 200
|
||||
|
||||
if (result == '0')
|
||||
result = 1
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
MediaManagerImageCropPopup.prototype.getWidthInput = function() {
|
||||
return this.$popupElement.find('[data-control="crop-width-input"]')
|
||||
}
|
||||
|
||||
MediaManagerImageCropPopup.prototype.getHeightInput = function() {
|
||||
return this.$popupElement.find('[data-control="crop-height-input"]')
|
||||
}
|
||||
|
||||
MediaManagerImageCropPopup.prototype.applySelectionMode = function() {
|
||||
if (!this.jCrop)
|
||||
return
|
||||
|
||||
var $widthInput = this.getWidthInput(),
|
||||
$heightInput = this.getHeightInput(),
|
||||
width = this.fixDimensionValue($widthInput.val()),
|
||||
height = this.fixDimensionValue($heightInput.val()),
|
||||
mode = this.getSelectionMode()
|
||||
|
||||
switch (mode) {
|
||||
case 'fixed-ratio' :
|
||||
this.jCrop.setOptions({
|
||||
aspectRatio: width/height,
|
||||
minSize: [0, 0],
|
||||
maxSize: [0, 0],
|
||||
allowResize: true
|
||||
})
|
||||
break
|
||||
case 'fixed-size' :
|
||||
this.jCrop.setOptions({
|
||||
aspectRatio: 0,
|
||||
minSize: [width, height],
|
||||
maxSize: [width, height],
|
||||
allowResize: false
|
||||
})
|
||||
break
|
||||
case 'normal' :
|
||||
this.jCrop.setOptions({
|
||||
aspectRatio: 0,
|
||||
minSize: [0, 0],
|
||||
maxSize: [0, 0],
|
||||
allowResize: true
|
||||
})
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
MediaManagerImageCropPopup.prototype.cropAndInsert = function() {
|
||||
var data = {
|
||||
img: $(this.imageArea).find('img').attr('src'),
|
||||
selection: this.jCrop.tellSelect()
|
||||
}
|
||||
|
||||
$.oc.stripeLoadIndicator.show()
|
||||
this.$popupElement.find('form').request(
|
||||
this.options.alias+'::onCropImage', {
|
||||
data: data
|
||||
}
|
||||
).always(function() {
|
||||
$.oc.stripeLoadIndicator.hide()
|
||||
}).done(this.proxy(this.onImageCropped))
|
||||
}
|
||||
|
||||
MediaManagerImageCropPopup.prototype.onImageCropped = function(response) {
|
||||
this.hide()
|
||||
|
||||
if (this.options.onDone !== undefined)
|
||||
this.options.onDone(response.result)
|
||||
}
|
||||
|
||||
MediaManagerImageCropPopup.prototype.showResizePopup = function() {
|
||||
this.$popupElement.find('button[data-command=resize]').popup({
|
||||
content: this.$popupElement.find('[data-control="resize-template"]').html(),
|
||||
zIndex: 1220
|
||||
})
|
||||
}
|
||||
|
||||
MediaManagerImageCropPopup.prototype.onResizePopupShown = function(ev, button, popup) {
|
||||
var $popup = $(popup),
|
||||
$widthControl = $popup.find('input[name=width]'),
|
||||
$heightControl = $popup.find('input[name=height]'),
|
||||
imageWidth = this.fixDimensionValue(this.$popupElement.find('input[data-control=dimension-width]').val()),
|
||||
imageHeight = this.fixDimensionValue(this.$popupElement.find('input[data-control=dimension-height]').val())
|
||||
|
||||
$widthControl.val(imageWidth)
|
||||
$heightControl.val(imageHeight)
|
||||
|
||||
$widthControl.focus()
|
||||
|
||||
$popup.on('submit.media', 'form', this.proxy(this.onResizeSubmit))
|
||||
$widthControl.on('keyup.media', this.proxy(this.onResizeDimensionKeyUp))
|
||||
$heightControl.on('keyup.media', this.proxy(this.onResizeDimensionKeyUp))
|
||||
|
||||
$widthControl.on('change.media', this.proxy(this.onResizeDimensionChanged))
|
||||
$heightControl.on('change.media', this.proxy(this.onResizeDimensionChanged))
|
||||
}
|
||||
|
||||
MediaManagerImageCropPopup.prototype.onResizePopupHidden = function(ev, button, popup) {
|
||||
var $popup = $(popup),
|
||||
$widthControl = $popup.find('input[name=width]'),
|
||||
$heightControl = $popup.find('input[name=height]')
|
||||
|
||||
$popup.off('.media', 'form')
|
||||
$widthControl.off('.media')
|
||||
$heightControl.off('.media')
|
||||
}
|
||||
|
||||
MediaManagerImageCropPopup.prototype.onResizeDimensionKeyUp = function(ev) {
|
||||
var $target = $(ev.target),
|
||||
targetValue = this.fixDimensionValue($target.val()),
|
||||
otherDimensionName = $target.attr('name') == 'width' ? 'height' : 'width',
|
||||
$otherInput = $target.closest('form').find('input[name='+otherDimensionName+']'),
|
||||
ratio = this.$popupElement.find('[data-control=original-ratio]').val(),
|
||||
value = otherDimensionName == 'height' ? targetValue / ratio : targetValue * ratio
|
||||
|
||||
$otherInput.val(Math.round(value))
|
||||
}
|
||||
|
||||
MediaManagerImageCropPopup.prototype.onResizeDimensionChanged = function(ev) {
|
||||
var $target = $(ev.target)
|
||||
|
||||
$target.val(this.fixDimensionValue($target.val()))
|
||||
}
|
||||
|
||||
MediaManagerImageCropPopup.prototype.onResizeSubmit = function(ev) {
|
||||
var data = {
|
||||
cropSessionKey: this.$popupElement.find('input[name=cropSessionKey]').val(),
|
||||
path: this.$popupElement.find('input[name=path]').val()
|
||||
}
|
||||
|
||||
$.oc.stripeLoadIndicator.show()
|
||||
$(ev.target).request(this.options.alias+'::onResizeImage', {
|
||||
data: data
|
||||
}).always(function() {
|
||||
$.oc.stripeLoadIndicator.hide()
|
||||
}).done(this.proxy(this.imageResized))
|
||||
|
||||
ev.preventDefault()
|
||||
return false
|
||||
}
|
||||
|
||||
MediaManagerImageCropPopup.prototype.imageResized = function(response) {
|
||||
this.$popupElement.find('button[data-command=resize]').popup('hide')
|
||||
|
||||
this.updateImage(response.url, response.dimensions[0], response.dimensions[1])
|
||||
}
|
||||
|
||||
MediaManagerImageCropPopup.prototype.updateImage = function(url, width, hegiht) {
|
||||
this.jCrop.destroy()
|
||||
|
||||
this.$popupElement.find('span[data-label=width]').text(width)
|
||||
this.$popupElement.find('span[data-label=height]').text(hegiht)
|
||||
|
||||
this.$popupElement.find('input[data-control=dimension-width]').val(width)
|
||||
this.$popupElement.find('input[data-control=dimension-height]').val(hegiht)
|
||||
|
||||
var $imageArea = $(this.imageArea)
|
||||
$imageArea.find('img').remove()
|
||||
|
||||
var $img = $('<img>').attr('src', url)
|
||||
$img.one('load', this.proxy(this.initJCrop))
|
||||
|
||||
$imageArea.append($img)
|
||||
|
||||
this.imageArea.scrollTop = 0
|
||||
this.imageArea.scrollLeft = 0
|
||||
this.onImageScroll()
|
||||
}
|
||||
|
||||
MediaManagerImageCropPopup.prototype.undoResizing = function() {
|
||||
this.updateImage(
|
||||
this.$popupElement.find('input[data-control=original-url]').val(),
|
||||
this.$popupElement.find('input[data-control=original-width]').val(),
|
||||
this.$popupElement.find('input[data-control=original-height]').val()
|
||||
)
|
||||
}
|
||||
|
||||
MediaManagerImageCropPopup.prototype.updateSelectionSizeLabel = function(width, height) {
|
||||
if (width == 0 && height == 0) {
|
||||
this.selectionSizeLabel.setAttribute('class', 'hide')
|
||||
return
|
||||
}
|
||||
|
||||
this.selectionSizeLabel.setAttribute('class', '')
|
||||
this.selectionSizeLabel.querySelector('[data-label=selection-width]').textContent = width
|
||||
this.selectionSizeLabel.querySelector('[data-label=selection-height]').textContent = height
|
||||
}
|
||||
|
||||
// EVENT HANDLERS
|
||||
// ============================
|
||||
|
||||
MediaManagerImageCropPopup.prototype.onPopupHidden = function(event, element, popup) {
|
||||
this.$popupElement.find('form').request(this.options.alias+'::onEndCroppingSession')
|
||||
|
||||
// Release clickedElement reference inside redactor.js
|
||||
// If we don't do it, the image editor popup DOM elements
|
||||
// won't be removed from the memory.
|
||||
|
|
@ -82,6 +386,83 @@
|
|||
|
||||
MediaManagerImageCropPopup.prototype.onPopupShown = function(event, element, popup) {
|
||||
this.$popupElement = popup
|
||||
this.$popupElement.on('change', '[data-control="selection-mode"]', this.proxy(this.onSelectionModeChanged))
|
||||
this.$popupElement.on('click', '[data-command]', this.proxy(this.onCommandClick))
|
||||
this.$popupElement.on('shown.oc.popup', 'button[data-command=resize]', this.proxy(this.onResizePopupShown))
|
||||
this.$popupElement.on('hidden.oc.popup', 'button[data-command=resize]', this.proxy(this.onResizePopupHidden))
|
||||
|
||||
this.imageArea = popup.find('[data-control=media-manager-crop-tool]').get(0).querySelector('.image_area')
|
||||
this.selectionSizeLabel = popup.find('[data-label="selection-size"]').get(0)
|
||||
|
||||
this.getWidthInput().on('change', this.proxy(this.onSizeInputChange))
|
||||
this.getHeightInput().on('change', this.proxy(this.onSizeInputChange))
|
||||
|
||||
this.initRulers()
|
||||
this.initJCrop()
|
||||
}
|
||||
|
||||
MediaManagerImageCropPopup.prototype.onSelectionModeChanged = function() {
|
||||
var mode = this.getSelectionMode(),
|
||||
$widthInput = this.getWidthInput(),
|
||||
$heightInput = this.getHeightInput()
|
||||
|
||||
if (mode === 'normal') {
|
||||
$widthInput.attr('disabled', 'disabled')
|
||||
$heightInput.attr('disabled', 'disabled')
|
||||
} else {
|
||||
$widthInput.removeAttr('disabled')
|
||||
$heightInput.removeAttr('disabled')
|
||||
|
||||
$widthInput.val(this.fixDimensionValue($widthInput.val()))
|
||||
$heightInput.val(this.fixDimensionValue($heightInput.val()))
|
||||
}
|
||||
|
||||
this.applySelectionMode()
|
||||
}
|
||||
|
||||
MediaManagerImageCropPopup.prototype.onImageScroll = function() {
|
||||
var scrollTop = this.imageArea.scrollTop,
|
||||
scrollLeft = this.imageArea.scrollLeft
|
||||
|
||||
if (this.prevScrollTop != scrollTop) {
|
||||
this.prevScrollTop = scrollTop
|
||||
|
||||
this.vRulerHolder.scrollTop = scrollTop
|
||||
}
|
||||
|
||||
if (this.prevScrollLeft != scrollLeft) {
|
||||
this.prevScrollLeft = scrollLeft
|
||||
|
||||
this.hRulerHolder.scrollLeft = scrollLeft
|
||||
}
|
||||
}
|
||||
|
||||
MediaManagerImageCropPopup.prototype.onSizeInputChange = function(ev) {
|
||||
var $target = $(ev.target)
|
||||
|
||||
$target.val(this.fixDimensionValue($target.val()))
|
||||
|
||||
this.applySelectionMode()
|
||||
}
|
||||
|
||||
MediaManagerImageCropPopup.prototype.onCommandClick = function(ev) {
|
||||
var command = $(ev.currentTarget).data('command')
|
||||
|
||||
switch (command) {
|
||||
case 'insert' :
|
||||
this.cropAndInsert()
|
||||
break
|
||||
case 'resize' :
|
||||
this.showResizePopup()
|
||||
break
|
||||
case 'undo-resizing' :
|
||||
this.undoResizing()
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
MediaManagerImageCropPopup.prototype.onSelectionChanged = function(c) {
|
||||
this.updateSelectionSizeLabel(c.w, c.h)
|
||||
}
|
||||
|
||||
MediaManagerImageCropPopup.DEFAULTS = {
|
||||
|
|
|
|||
|
|
@ -720,14 +720,21 @@
|
|||
return
|
||||
}
|
||||
|
||||
new $.oc.mediaManager.imageCropPopup(
|
||||
selectedItems[0].getAttribute('data-path'), {
|
||||
alias: this.options.alias
|
||||
var path = selectedItems[0].getAttribute('data-path')
|
||||
|
||||
new $.oc.mediaManager.imageCropPopup(path, {
|
||||
alias: this.options.alias,
|
||||
onDone: callback
|
||||
})
|
||||
}
|
||||
|
||||
MediaManager.prototype.onImageCropped = function(imageItem) {
|
||||
MediaManager.prototype.onImageCropped = function(imageUrl) {
|
||||
var item = {
|
||||
documentType: 'image',
|
||||
publicUrl: imageUrl
|
||||
}
|
||||
|
||||
this.$el.trigger('popupcommand', ['insert-cropped', item])
|
||||
}
|
||||
|
||||
//
|
||||
|
|
@ -819,7 +826,8 @@
|
|||
|
||||
MediaManager.prototype.createFolder = function(ev) {
|
||||
$(ev.target).popup({
|
||||
content: this.$el.find('[data-control="new-folder-template"]').html()
|
||||
content: this.$el.find('[data-control="new-folder-template"]').html(),
|
||||
zIndex: 1200 // Media Manager can be opened in a popup, so this new popup should have a higher z-index
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -882,7 +890,8 @@
|
|||
|
||||
$(ev.target).popup({
|
||||
handler: this.options.alias+'::onLoadMovePopup',
|
||||
extraData: data
|
||||
extraData: data,
|
||||
zIndex: 1200 // Media Manager can be opened in a popup, so this new popup should have a higher z-index
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@
|
|||
bottomToolbar: this.options.bottomToolbar ? 1 : 0,
|
||||
cropAndInsertButton: this.options.cropAndInsertButton ? 1 : 0
|
||||
}
|
||||
console.log(data)
|
||||
|
||||
this.$popupRootElement.popup({
|
||||
extraData: data,
|
||||
size: 'adaptive',
|
||||
|
|
@ -81,6 +81,11 @@ console.log(data)
|
|||
this.options.onInsert.call(this, items)
|
||||
}
|
||||
|
||||
MediaManagerPopup.prototype.insertCroppedImage = function(imageItem) {
|
||||
if (this.options.onInsert !== undefined)
|
||||
this.options.onInsert.call(this, [imageItem])
|
||||
}
|
||||
|
||||
// EVENT HANDLERS
|
||||
// ============================
|
||||
|
||||
|
|
@ -110,11 +115,14 @@ console.log(data)
|
|||
this.getMediaManagerElement().find('[data-control="sorting"]').focus().blur()
|
||||
}
|
||||
|
||||
MediaManagerPopup.prototype.onPopupCommand = function(ev, command) {
|
||||
MediaManagerPopup.prototype.onPopupCommand = function(ev, command, param) {
|
||||
switch (command) {
|
||||
case 'insert' :
|
||||
this.insertMedia()
|
||||
break;
|
||||
case 'insert-cropped' :
|
||||
this.insertCroppedImage(param)
|
||||
break;
|
||||
}
|
||||
|
||||
return false
|
||||
|
|
|
|||
|
|
@ -75,7 +75,8 @@ div[data-control="media-manager"] {
|
|||
|
||||
.icon-message() {
|
||||
font-size: 12px;
|
||||
margin-top: 25px;
|
||||
margin: 10px;
|
||||
line-height: 160%;
|
||||
color: #bdc3c7;
|
||||
}
|
||||
|
||||
|
|
@ -327,6 +328,7 @@ div[data-control="media-manager"] {
|
|||
|
||||
p {
|
||||
.icon-message();
|
||||
margin-top: 25px;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -466,6 +468,115 @@ div[data-control="media-manager"] {
|
|||
}
|
||||
}
|
||||
|
||||
[data-control="media-manager-crop-tool"] {
|
||||
.image_area {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
overflow: auto;
|
||||
|
||||
.jcrop-holder {
|
||||
background-color: transparent!important;
|
||||
}
|
||||
}
|
||||
|
||||
img {
|
||||
cursor: crosshair;
|
||||
display: block;
|
||||
}
|
||||
|
||||
&.has-rulers {
|
||||
.ruler-container {
|
||||
.layout-relative {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
&.horizontal {
|
||||
.layout-cell {
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.layout-relative {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
&.vertical {
|
||||
width: 20px;
|
||||
.layout-relative {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ruler {
|
||||
position: absolute;
|
||||
height: 20px;
|
||||
margin-left: -3px;
|
||||
background: #555;
|
||||
|
||||
ul {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
white-space: nowrap;
|
||||
font-size: 0;
|
||||
}
|
||||
|
||||
li {
|
||||
margin: 0;
|
||||
padding: 0 0 0 40px;
|
||||
list-style: none;
|
||||
display: inline-block;
|
||||
width: 24px;
|
||||
margin: 0px -10px 0px -14px;
|
||||
.box-sizing(content-box);
|
||||
text-align: left;
|
||||
position: relative;
|
||||
font-size: 10px;
|
||||
line-height: 20px;
|
||||
color: #ecf0f1;
|
||||
font-family: Arial, sans-serif;
|
||||
|
||||
&:before, &:after {
|
||||
content: ' ';
|
||||
position: absolute;
|
||||
border-left: 1px solid #8e8e8e;
|
||||
}
|
||||
|
||||
&:before {
|
||||
height: 20px;
|
||||
top: 0;
|
||||
left: -3px;
|
||||
}
|
||||
|
||||
&:after {
|
||||
height: 3px;
|
||||
bottom: 0;
|
||||
left: 20px;
|
||||
}
|
||||
|
||||
&:first-child:after {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ruler[data-control=v-ruler] {
|
||||
.transform(~'rotateZ(90deg)');
|
||||
.transform-origin(~'left top');
|
||||
left: 23px;
|
||||
top: -23px;
|
||||
|
||||
& li:after {
|
||||
top: 0;
|
||||
left: auto;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
body:not(.no-select) {
|
||||
div[data-control="media-manager"] .media-list {
|
||||
&.tiles {
|
||||
|
|
@ -479,7 +590,7 @@ body:not(.no-select) {
|
|||
}
|
||||
|
||||
&.list {
|
||||
li:hover:not(:active) {
|
||||
li:hover {
|
||||
.media-selected-list();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,33 @@
|
|||
<div class="layout"
|
||||
data-control="media-manager-crop-tool"
|
||||
<?php if ($dimensions): ?>
|
||||
data-image-width="<?= $dimensions[0] ?>"
|
||||
data-image-height="<?= $dimensions[1] ?>"
|
||||
<?php endif ?>
|
||||
>
|
||||
<div class="layout-row min-size ruler-container horizontal hide">
|
||||
<div class="layout-cell">
|
||||
<div class="layout-relative">
|
||||
<div class="ruler" data-control="h-ruler"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layout-row">
|
||||
<div class="layout">
|
||||
<div class="layout-row">
|
||||
<div class="layout-cell ruler-container vertical hide">
|
||||
<div class="layout-relative">
|
||||
<div class="ruler" data-control="v-ruler"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layout-cell">
|
||||
<div class="layout-relative">
|
||||
<div class="image_area">
|
||||
<img src="<?= $imageUrl ?>"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
<?php
|
||||
|
||||
$selectionModes = [
|
||||
Cms\Widgets\MediaManager::SELECTION_MODE_NORMAL => trans('cms::lang.media.selection_mode_normal'),
|
||||
Cms\Widgets\MediaManager::SELECTION_MODE_FIXED_RATIO => trans('cms::lang.media.selection_mode_fixed_ratio'),
|
||||
Cms\Widgets\MediaManager::SELECTION_MODE_FIXED_SIZE => trans('cms::lang.media.selection_mode_fixed_size')
|
||||
];
|
||||
|
||||
$sizeDisabledAttr = $currentSelectionMode == Cms\Widgets\MediaManager::SELECTION_MODE_NORMAL ? 'disabled="disabled"' : null;
|
||||
?>
|
||||
|
||||
<div class="layout control-toolbar standalone-paddings">
|
||||
<div class="layout-cell toolbar-item">
|
||||
<div data-control="toolbar">
|
||||
<label class="standalone"><?= e(trans('cms::lang.media.image_size')) ?> <span data-label="width"><?= $dimensions[0] ?></span> x <span data-label="height"><?= $dimensions[1] ?></span></label>
|
||||
|
||||
<div class="btn-group offset-right">
|
||||
<button type="button" class="btn btn-primary standalone" data-command="resize"
|
||||
><?= e(trans('cms::lang.media.resize')) ?></button>
|
||||
|
||||
<button type="button" class="btn btn-primary oc-icon-undo empty" data-command="undo-resizing"></button>
|
||||
</div>
|
||||
|
||||
<label for="mmcropimagewidth"><?= e(trans('cms::lang.media.selection_mode')) ?></label>
|
||||
<select name="selectionMode" class="form-control custom-select width-150" data-control="selection-mode">
|
||||
<?php foreach ($selectionModes as $mode=>$name): ?>
|
||||
<option <?= $mode == $currentSelectionMode ? 'selected="selected"' : null ?> value="<?= $mode ?>"><?= e($name) ?></option>
|
||||
<?php endforeach ?>
|
||||
</select>
|
||||
|
||||
<label for="mmcropimagewidth"><?= e(trans('cms::lang.media.width')) ?></label>
|
||||
<input id="mmcropimagewidth" type="text" class="form-control width-50" data-control="crop-width-input" name="selectionWidth" value="<?= e($currentSelectionWidth) ?>" <?= $sizeDisabledAttr ?>/>
|
||||
|
||||
<label for="mmcropimageheight"><?= e(trans('cms::lang.media.height')) ?></label>
|
||||
<input id="mmcropimageheight" type="text" class="form-control width-50" data-control="crop-height-input" name="selectionHeight" value="<?= e($currentSelectionHeight) ?>" <?= $sizeDisabledAttr ?>/>
|
||||
|
||||
<label class="standalone hide" data-label="selection-size"><?= e(trans('cms::lang.media.selected_size')) ?> <span data-label="selection-width"></span> x <span data-label="selection-height"></span></label>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -39,6 +39,7 @@
|
|||
href="#"
|
||||
data-rename
|
||||
data-control="popup"
|
||||
data-z-index="1200"
|
||||
data-request-data="path: '<?= e($item->path) ?>', listId: '<?= $listElementId ?>', type: '<?= $item->type ?>'"
|
||||
data-handler="<?= $this->getEventHandler('onLoadRenamePopup') ?>"
|
||||
><i data-rename-control class="icon-terminal"></i></a>
|
||||
|
|
|
|||
|
|
@ -1,3 +1,43 @@
|
|||
<?= Form::open(['class'=>'layout', 'onsubmit'=>'return false']) ?>
|
||||
<p>Something</p>
|
||||
<div class="layout-row min-size">
|
||||
<?= $this->makePartial('crop-toolbar') ?>
|
||||
</div>
|
||||
<div class="layout-row whiteboard">
|
||||
<?= $this->makePartial('crop-tool-image-area') ?>
|
||||
</div>
|
||||
<div class="layout-row min-size whiteboard">
|
||||
<div class="panel no-padding-bottom border-top">
|
||||
<div class="form-buttons">
|
||||
<div class="pull-right">
|
||||
<button
|
||||
type="button"
|
||||
data-command="insert"
|
||||
class="btn btn-primary">
|
||||
<?= e(trans('cms::lang.media.crop_and_insert')) ?>
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
data-dismiss="popup"
|
||||
class="btn btn-default no-margin-right">
|
||||
<?= e(trans('backend::lang.form.cancel')) ?>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<input type="hidden" name="cropSessionKey" value="<?= e($cropSessionKey) ?>">
|
||||
<input type="hidden" name="path" value="<?= e($path) ?>">
|
||||
|
||||
<input type="hidden" data-control="dimension-width" value="<?= $dimensions[0] ?>">
|
||||
<input type="hidden" data-control="dimension-height" value="<?= $dimensions[1] ?>">
|
||||
|
||||
<input type="hidden" data-control="original-width" value="<?= $dimensions[0] ?>">
|
||||
<input type="hidden" data-control="original-height" value="<?= $dimensions[1] ?>">
|
||||
|
||||
<input type="hidden" data-control="original-ratio" value="<?= $originalRatio ?>">
|
||||
<input type="hidden" data-control="original-url" value="<?= e($imageUrl) ?>">
|
||||
|
||||
<?= $this->makePartial('resize-image-form') ?>
|
||||
<?= Form::close() ?>
|
||||
|
|
@ -41,6 +41,7 @@
|
|||
data-control="popup"
|
||||
data-request-data="path: '<?= e($item->path) ?>', listId: '<?= $listElementId ?>', type: '<?= $item->type ?>'"
|
||||
data-handler="<?= $this->getEventHandler('onLoadRenamePopup') ?>"
|
||||
data-z-index="1200"
|
||||
><i data-rename-control class="icon-terminal"></i></a>
|
||||
</div>
|
||||
</td>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,31 @@
|
|||
<script type="text/template" data-control="resize-template">
|
||||
<?= Form::open() ?>
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="popup">×</button>
|
||||
<h4 class="modal-title"><?= e(trans('cms::lang.media.resize_image')) ?></h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="form-group span-left">
|
||||
<label><?= e(trans('cms::lang.media.width')) ?></label>
|
||||
<input type="text" class="form-control" name="width" value="" />
|
||||
</div>
|
||||
<div class="form-group span-right">
|
||||
<label><?= e(trans('cms::lang.media.height')) ?></label>
|
||||
<input type="text" class="form-control" name="height" value="" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button
|
||||
type="submit"
|
||||
class="btn btn-primary">
|
||||
<?= e(trans('backend::lang.form.apply')) ?>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-default"
|
||||
data-dismiss="popup">
|
||||
<?= e(trans('backend::lang.form.cancel')) ?>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</script>
|
||||
Loading…
Reference in New Issue