",options:{disabled:!1,create:null},_createWidget:function(e,i){i=t(i||this.defaultElement||this)[0],this.element=t(i),this.uuid=s++,this.eventNamespace="."+this.widgetName+this.uuid,this.bindings=t(),this.hoverable=t(),this.focusable=t(),i!==this&&(t.data(i,this.widgetFullName,this),this._on(!0,this.element,{remove:function(t){t.target===i&&this.destroy()}}),this.document=t(i.style?i.ownerDocument:i.document||i),this.window=t(this.document[0].defaultView||this.document[0].parentWindow)),this.options=t.widget.extend({},this.options,this._getCreateOptions(),e),this._create(),this._trigger("create",null,this._getCreateEventData()),this._init()},_getCreateOptions:t.noop,_getCreateEventData:t.noop,_create:t.noop,_init:t.noop,destroy:function(){this._destroy(),this.element.unbind(this.eventNamespace).removeData(this.widgetFullName).removeData(t.camelCase(this.widgetFullName)),this.widget().unbind(this.eventNamespace).removeAttr("aria-disabled").removeClass(this.widgetFullName+"-disabled "+"ui-state-disabled"),this.bindings.unbind(this.eventNamespace),this.hoverable.removeClass("ui-state-hover"),this.focusable.removeClass("ui-state-focus")},_destroy:t.noop,widget:function(){return this.element},option:function(e,i){var s,n,a,o=e;if(0===arguments.length)return t.widget.extend({},this.options);if("string"==typeof e)if(o={},s=e.split("."),e=s.shift(),s.length){for(n=o[e]=t.widget.extend({},this.options[e]),a=0;s.length-1>a;a++)n[s[a]]=n[s[a]]||{},n=n[s[a]];if(e=s.pop(),1===arguments.length)return void 0===n[e]?null:n[e];n[e]=i}else{if(1===arguments.length)return void 0===this.options[e]?null:this.options[e];o[e]=i}return this._setOptions(o),this},_setOptions:function(t){var e;for(e in t)this._setOption(e,t[e]);return this},_setOption:function(t,e){return this.options[t]=e,"disabled"===t&&(this.widget().toggleClass(this.widgetFullName+"-disabled",!!e),e&&(this.hoverable.removeClass("ui-state-hover"),this.focusable.removeClass("ui-state-focus"))),this},enable:function(){return this._setOptions({disabled:!1})},disable:function(){return this._setOptions({disabled:!0})},_on:function(e,i,s){var n,a=this;"boolean"!=typeof e&&(s=i,i=e,e=!1),s?(i=n=t(i),this.bindings=this.bindings.add(i)):(s=i,i=this.element,n=this.widget()),t.each(s,function(s,o){function r(){return e||a.options.disabled!==!0&&!t(this).hasClass("ui-state-disabled")?("string"==typeof o?a[o]:o).apply(a,arguments):void 0}"string"!=typeof o&&(r.guid=o.guid=o.guid||r.guid||t.guid++);var h=s.match(/^([\w:-]*)\s*(.*)$/),l=h[1]+a.eventNamespace,u=h[2];u?n.delegate(u,l,r):i.bind(l,r)})},_off:function(e,i){i=(i||"").split(" ").join(this.eventNamespace+" ")+this.eventNamespace,e.unbind(i).undelegate(i),this.bindings=t(this.bindings.not(e).get()),this.focusable=t(this.focusable.not(e).get()),this.hoverable=t(this.hoverable.not(e).get())},_delay:function(t,e){function i(){return("string"==typeof t?s[t]:t).apply(s,arguments)}var s=this;return setTimeout(i,e||0)},_hoverable:function(e){this.hoverable=this.hoverable.add(e),this._on(e,{mouseenter:function(e){t(e.currentTarget).addClass("ui-state-hover")},mouseleave:function(e){t(e.currentTarget).removeClass("ui-state-hover")}})},_focusable:function(e){this.focusable=this.focusable.add(e),this._on(e,{focusin:function(e){t(e.currentTarget).addClass("ui-state-focus")},focusout:function(e){t(e.currentTarget).removeClass("ui-state-focus")}})},_trigger:function(e,i,s){var n,a,o=this.options[e];if(s=s||{},i=t.Event(i),i.type=(e===this.widgetEventPrefix?e:this.widgetEventPrefix+e).toLowerCase(),i.target=this.element[0],a=i.originalEvent)for(n in a)n in i||(i[n]=a[n]);return this.element.trigger(i,s),!(t.isFunction(o)&&o.apply(this.element[0],[i].concat(s))===!1||i.isDefaultPrevented())}},t.each({show:"fadeIn",hide:"fadeOut"},function(e,i){t.Widget.prototype["_"+e]=function(s,n,a){"string"==typeof n&&(n={effect:n});var o,r=n?n===!0||"number"==typeof n?i:n.effect||i:e;n=n||{},"number"==typeof n&&(n={duration:n}),o=!t.isEmptyObject(n),n.complete=a,n.delay&&s.delay(n.delay),o&&t.effects&&t.effects.effect[r]?s[e](n):r!==e&&s[r]?s[r](n.duration,n.easing,a):s.queue(function(i){t(this)[e](),a&&a.call(s[0]),i()})}}),t.widget;var a=!1;t(document).mouseup(function(){a=!1}),t.widget("ui.mouse",{version:"1.11.4",options:{cancel:"input,textarea,button,select,option",distance:1,delay:0},_mouseInit:function(){var e=this;this.element.bind("mousedown."+this.widgetName,function(t){return e._mouseDown(t)}).bind("click."+this.widgetName,function(i){return!0===t.data(i.target,e.widgetName+".preventClickEvent")?(t.removeData(i.target,e.widgetName+".preventClickEvent"),i.stopImmediatePropagation(),!1):void 0}),this.started=!1},_mouseDestroy:function(){this.element.unbind("."+this.widgetName),this._mouseMoveDelegate&&this.document.unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate)},_mouseDown:function(e){if(!a){this._mouseMoved=!1,this._mouseStarted&&this._mouseUp(e),this._mouseDownEvent=e;var i=this,s=1===e.which,n="string"==typeof this.options.cancel&&e.target.nodeName?t(e.target).closest(this.options.cancel).length:!1;return s&&!n&&this._mouseCapture(e)?(this.mouseDelayMet=!this.options.delay,this.mouseDelayMet||(this._mouseDelayTimer=setTimeout(function(){i.mouseDelayMet=!0},this.options.delay)),this._mouseDistanceMet(e)&&this._mouseDelayMet(e)&&(this._mouseStarted=this._mouseStart(e)!==!1,!this._mouseStarted)?(e.preventDefault(),!0):(!0===t.data(e.target,this.widgetName+".preventClickEvent")&&t.removeData(e.target,this.widgetName+".preventClickEvent"),this._mouseMoveDelegate=function(t){return i._mouseMove(t)},this._mouseUpDelegate=function(t){return i._mouseUp(t)},this.document.bind("mousemove."+this.widgetName,this._mouseMoveDelegate).bind("mouseup."+this.widgetName,this._mouseUpDelegate),e.preventDefault(),a=!0,!0)):!0}},_mouseMove:function(e){if(this._mouseMoved){if(t.ui.ie&&(!document.documentMode||9>document.documentMode)&&!e.button)return this._mouseUp(e);if(!e.which)return this._mouseUp(e)}return(e.which||e.button)&&(this._mouseMoved=!0),this._mouseStarted?(this._mouseDrag(e),e.preventDefault()):(this._mouseDistanceMet(e)&&this._mouseDelayMet(e)&&(this._mouseStarted=this._mouseStart(this._mouseDownEvent,e)!==!1,this._mouseStarted?this._mouseDrag(e):this._mouseUp(e)),!this._mouseStarted)},_mouseUp:function(e){return this.document.unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate),this._mouseStarted&&(this._mouseStarted=!1,e.target===this._mouseDownEvent.target&&t.data(e.target,this.widgetName+".preventClickEvent",!0),this._mouseStop(e)),a=!1,!1},_mouseDistanceMet:function(t){return Math.max(Math.abs(this._mouseDownEvent.pageX-t.pageX),Math.abs(this._mouseDownEvent.pageY-t.pageY))>=this.options.distance},_mouseDelayMet:function(){return this.mouseDelayMet},_mouseStart:function(){},_mouseDrag:function(){},_mouseStop:function(){},_mouseCapture:function(){return!0}}),function(){function e(t,e,i){return[parseFloat(t[0])*(p.test(t[0])?e/100:1),parseFloat(t[1])*(p.test(t[1])?i/100:1)]}function i(e,i){return parseInt(t.css(e,i),10)||0}function s(e){var i=e[0];return 9===i.nodeType?{width:e.width(),height:e.height(),offset:{top:0,left:0}}:t.isWindow(i)?{width:e.width(),height:e.height(),offset:{top:e.scrollTop(),left:e.scrollLeft()}}:i.preventDefault?{width:0,height:0,offset:{top:i.pageY,left:i.pageX}}:{width:e.outerWidth(),height:e.outerHeight(),offset:e.offset()}}t.ui=t.ui||{};var n,a,o=Math.max,r=Math.abs,h=Math.round,l=/left|center|right/,u=/top|center|bottom/,c=/[\+\-]\d+(\.[\d]+)?%?/,d=/^\w+/,p=/%$/,f=t.fn.position;t.position={scrollbarWidth:function(){if(void 0!==n)return n;var e,i,s=t("
"),a=s.children()[0];return t("body").append(s),e=a.offsetWidth,s.css("overflow","scroll"),i=a.offsetWidth,e===i&&(i=s[0].clientWidth),s.remove(),n=e-i},getScrollInfo:function(e){var i=e.isWindow||e.isDocument?"":e.element.css("overflow-x"),s=e.isWindow||e.isDocument?"":e.element.css("overflow-y"),n="scroll"===i||"auto"===i&&e.width
i?"left":e>0?"right":"center",vertical:0>a?"top":s>0?"bottom":"middle"};c>m&&m>r(e+i)&&(h.horizontal="center"),d>g&&g>r(s+a)&&(h.vertical="middle"),h.important=o(r(e),r(i))>o(r(s),r(a))?"horizontal":"vertical",n.using.call(this,t,h)}),u.offset(t.extend(M,{using:l}))})},t.ui.position={fit:{left:function(t,e){var i,s=e.within,n=s.isWindow?s.scrollLeft:s.offset.left,a=s.width,r=t.left-e.collisionPosition.marginLeft,h=n-r,l=r+e.collisionWidth-a-n;e.collisionWidth>a?h>0&&0>=l?(i=t.left+h+e.collisionWidth-a-n,t.left+=h-i):t.left=l>0&&0>=h?n:h>l?n+a-e.collisionWidth:n:h>0?t.left+=h:l>0?t.left-=l:t.left=o(t.left-r,t.left)},top:function(t,e){var i,s=e.within,n=s.isWindow?s.scrollTop:s.offset.top,a=e.within.height,r=t.top-e.collisionPosition.marginTop,h=n-r,l=r+e.collisionHeight-a-n;e.collisionHeight>a?h>0&&0>=l?(i=t.top+h+e.collisionHeight-a-n,t.top+=h-i):t.top=l>0&&0>=h?n:h>l?n+a-e.collisionHeight:n:h>0?t.top+=h:l>0?t.top-=l:t.top=o(t.top-r,t.top)}},flip:{left:function(t,e){var i,s,n=e.within,a=n.offset.left+n.scrollLeft,o=n.width,h=n.isWindow?n.scrollLeft:n.offset.left,l=t.left-e.collisionPosition.marginLeft,u=l-h,c=l+e.collisionWidth-o-h,d="left"===e.my[0]?-e.elemWidth:"right"===e.my[0]?e.elemWidth:0,p="left"===e.at[0]?e.targetWidth:"right"===e.at[0]?-e.targetWidth:0,f=-2*e.offset[0];0>u?(i=t.left+d+p+f+e.collisionWidth-o-a,(0>i||r(u)>i)&&(t.left+=d+p+f)):c>0&&(s=t.left-e.collisionPosition.marginLeft+d+p+f-h,(s>0||c>r(s))&&(t.left+=d+p+f))},top:function(t,e){var i,s,n=e.within,a=n.offset.top+n.scrollTop,o=n.height,h=n.isWindow?n.scrollTop:n.offset.top,l=t.top-e.collisionPosition.marginTop,u=l-h,c=l+e.collisionHeight-o-h,d="top"===e.my[1],p=d?-e.elemHeight:"bottom"===e.my[1]?e.elemHeight:0,f="top"===e.at[1]?e.targetHeight:"bottom"===e.at[1]?-e.targetHeight:0,m=-2*e.offset[1];0>u?(s=t.top+p+f+m+e.collisionHeight-o-a,(0>s||r(u)>s)&&(t.top+=p+f+m)):c>0&&(i=t.top-e.collisionPosition.marginTop+p+f+m-h,(i>0||c>r(i))&&(t.top+=p+f+m))}},flipfit:{left:function(){t.ui.position.flip.left.apply(this,arguments),t.ui.position.fit.left.apply(this,arguments)},top:function(){t.ui.position.flip.top.apply(this,arguments),t.ui.position.fit.top.apply(this,arguments)}}},function(){var e,i,s,n,o,r=document.getElementsByTagName("body")[0],h=document.createElement("div");e=document.createElement(r?"div":"body"),s={visibility:"hidden",width:0,height:0,border:0,margin:0,background:"none"},r&&t.extend(s,{position:"absolute",left:"-1000px",top:"-1000px"});for(o in s)e.style[o]=s[o];e.appendChild(h),i=r||document.documentElement,i.insertBefore(e,i.firstChild),h.style.cssText="position: absolute; left: 10.7432222px;",n=t(h).offset().left,a=n>10&&11>n,e.innerHTML="",i.removeChild(e)}()}(),t.ui.position,t.widget("ui.draggable",t.ui.mouse,{version:"1.11.4",widgetEventPrefix:"drag",options:{addClasses:!0,appendTo:"parent",axis:!1,connectToSortable:!1,containment:!1,cursor:"auto",cursorAt:!1,grid:!1,handle:!1,helper:"original",iframeFix:!1,opacity:!1,refreshPositions:!1,revert:!1,revertDuration:500,scope:"default",scroll:!0,scrollSensitivity:20,scrollSpeed:20,snap:!1,snapMode:"both",snapTolerance:20,stack:!1,zIndex:!1,drag:null,start:null,stop:null},_create:function(){"original"===this.options.helper&&this._setPositionRelative(),this.options.addClasses&&this.element.addClass("ui-draggable"),this.options.disabled&&this.element.addClass("ui-draggable-disabled"),this._setHandleClassName(),this._mouseInit()},_setOption:function(t,e){this._super(t,e),"handle"===t&&(this._removeHandleClassName(),this._setHandleClassName())},_destroy:function(){return(this.helper||this.element).is(".ui-draggable-dragging")?(this.destroyOnClear=!0,void 0):(this.element.removeClass("ui-draggable ui-draggable-dragging ui-draggable-disabled"),this._removeHandleClassName(),this._mouseDestroy(),void 0)},_mouseCapture:function(e){var i=this.options;return this._blurActiveElement(e),this.helper||i.disabled||t(e.target).closest(".ui-resizable-handle").length>0?!1:(this.handle=this._getHandle(e),this.handle?(this._blockFrames(i.iframeFix===!0?"iframe":i.iframeFix),!0):!1)},_blockFrames:function(e){this.iframeBlocks=this.document.find(e).map(function(){var e=t(this);return t("").css("position","absolute").appendTo(e.parent()).outerWidth(e.outerWidth()).outerHeight(e.outerHeight()).offset(e.offset())[0]})},_unblockFrames:function(){this.iframeBlocks&&(this.iframeBlocks.remove(),delete this.iframeBlocks)},_blurActiveElement:function(e){var i=this.document[0];if(this.handleElement.is(e.target))try{i.activeElement&&"body"!==i.activeElement.nodeName.toLowerCase()&&t(i.activeElement).blur()}catch(s){}},_mouseStart:function(e){var i=this.options;return this.helper=this._createHelper(e),this.helper.addClass("ui-draggable-dragging"),this._cacheHelperProportions(),t.ui.ddmanager&&(t.ui.ddmanager.current=this),this._cacheMargins(),this.cssPosition=this.helper.css("position"),this.scrollParent=this.helper.scrollParent(!0),this.offsetParent=this.helper.offsetParent(),this.hasFixedAncestor=this.helper.parents().filter(function(){return"fixed"===t(this).css("position")}).length>0,this.positionAbs=this.element.offset(),this._refreshOffsets(e),this.originalPosition=this.position=this._generatePosition(e,!1),this.originalPageX=e.pageX,this.originalPageY=e.pageY,i.cursorAt&&this._adjustOffsetFromHelper(i.cursorAt),this._setContainment(),this._trigger("start",e)===!1?(this._clear(),!1):(this._cacheHelperProportions(),t.ui.ddmanager&&!i.dropBehaviour&&t.ui.ddmanager.prepareOffsets(this,e),this._normalizeRightBottom(),this._mouseDrag(e,!0),t.ui.ddmanager&&t.ui.ddmanager.dragStart(this,e),!0)},_refreshOffsets:function(t){this.offset={top:this.positionAbs.top-this.margins.top,left:this.positionAbs.left-this.margins.left,scroll:!1,parent:this._getParentOffset(),relative:this._getRelativeOffset()},this.offset.click={left:t.pageX-this.offset.left,top:t.pageY-this.offset.top}},_mouseDrag:function(e,i){if(this.hasFixedAncestor&&(this.offset.parent=this._getParentOffset()),this.position=this._generatePosition(e,!0),this.positionAbs=this._convertPositionTo("absolute"),!i){var s=this._uiHash();if(this._trigger("drag",e,s)===!1)return this._mouseUp({}),!1;this.position=s.position}return this.helper[0].style.left=this.position.left+"px",this.helper[0].style.top=this.position.top+"px",t.ui.ddmanager&&t.ui.ddmanager.drag(this,e),!1},_mouseStop:function(e){var i=this,s=!1;return t.ui.ddmanager&&!this.options.dropBehaviour&&(s=t.ui.ddmanager.drop(this,e)),this.dropped&&(s=this.dropped,this.dropped=!1),"invalid"===this.options.revert&&!s||"valid"===this.options.revert&&s||this.options.revert===!0||t.isFunction(this.options.revert)&&this.options.revert.call(this.element,s)?t(this.helper).animate(this.originalPosition,parseInt(this.options.revertDuration,10),function(){i._trigger("stop",e)!==!1&&i._clear()}):this._trigger("stop",e)!==!1&&this._clear(),!1},_mouseUp:function(e){return this._unblockFrames(),t.ui.ddmanager&&t.ui.ddmanager.dragStop(this,e),this.handleElement.is(e.target)&&this.element.focus(),t.ui.mouse.prototype._mouseUp.call(this,e)},cancel:function(){return this.helper.is(".ui-draggable-dragging")?this._mouseUp({}):this._clear(),this},_getHandle:function(e){return this.options.handle?!!t(e.target).closest(this.element.find(this.options.handle)).length:!0},_setHandleClassName:function(){this.handleElement=this.options.handle?this.element.find(this.options.handle):this.element,this.handleElement.addClass("ui-draggable-handle")},_removeHandleClassName:function(){this.handleElement.removeClass("ui-draggable-handle")},_createHelper:function(e){var i=this.options,s=t.isFunction(i.helper),n=s?t(i.helper.apply(this.element[0],[e])):"clone"===i.helper?this.element.clone().removeAttr("id"):this.element;return n.parents("body").length||n.appendTo("parent"===i.appendTo?this.element[0].parentNode:i.appendTo),s&&n[0]===this.element[0]&&this._setPositionRelative(),n[0]===this.element[0]||/(fixed|absolute)/.test(n.css("position"))||n.css("position","absolute"),n},_setPositionRelative:function(){/^(?:r|a|f)/.test(this.element.css("position"))||(this.element[0].style.position="relative")},_adjustOffsetFromHelper:function(e){"string"==typeof e&&(e=e.split(" ")),t.isArray(e)&&(e={left:+e[0],top:+e[1]||0}),"left"in e&&(this.offset.click.left=e.left+this.margins.left),"right"in e&&(this.offset.click.left=this.helperProportions.width-e.right+this.margins.left),"top"in e&&(this.offset.click.top=e.top+this.margins.top),"bottom"in e&&(this.offset.click.top=this.helperProportions.height-e.bottom+this.margins.top)},_isRootNode:function(t){return/(html|body)/i.test(t.tagName)||t===this.document[0]},_getParentOffset:function(){var e=this.offsetParent.offset(),i=this.document[0];return"absolute"===this.cssPosition&&this.scrollParent[0]!==i&&t.contains(this.scrollParent[0],this.offsetParent[0])&&(e.left+=this.scrollParent.scrollLeft(),e.top+=this.scrollParent.scrollTop()),this._isRootNode(this.offsetParent[0])&&(e={top:0,left:0}),{top:e.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:e.left+(parseInt(this.offsetParent.css("borderLeftWidth"),10)||0)}},_getRelativeOffset:function(){if("relative"!==this.cssPosition)return{top:0,left:0};var t=this.element.position(),e=this._isRootNode(this.scrollParent[0]);return{top:t.top-(parseInt(this.helper.css("top"),10)||0)+(e?0:this.scrollParent.scrollTop()),left:t.left-(parseInt(this.helper.css("left"),10)||0)+(e?0:this.scrollParent.scrollLeft())}},_cacheMargins:function(){this.margins={left:parseInt(this.element.css("marginLeft"),10)||0,top:parseInt(this.element.css("marginTop"),10)||0,right:parseInt(this.element.css("marginRight"),10)||0,bottom:parseInt(this.element.css("marginBottom"),10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var e,i,s,n=this.options,a=this.document[0];return this.relativeContainer=null,n.containment?"window"===n.containment?(this.containment=[t(window).scrollLeft()-this.offset.relative.left-this.offset.parent.left,t(window).scrollTop()-this.offset.relative.top-this.offset.parent.top,t(window).scrollLeft()+t(window).width()-this.helperProportions.width-this.margins.left,t(window).scrollTop()+(t(window).height()||a.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top],void 0):"document"===n.containment?(this.containment=[0,0,t(a).width()-this.helperProportions.width-this.margins.left,(t(a).height()||a.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top],void 0):n.containment.constructor===Array?(this.containment=n.containment,void 0):("parent"===n.containment&&(n.containment=this.helper[0].parentNode),i=t(n.containment),s=i[0],s&&(e=/(scroll|auto)/.test(i.css("overflow")),this.containment=[(parseInt(i.css("borderLeftWidth"),10)||0)+(parseInt(i.css("paddingLeft"),10)||0),(parseInt(i.css("borderTopWidth"),10)||0)+(parseInt(i.css("paddingTop"),10)||0),(e?Math.max(s.scrollWidth,s.offsetWidth):s.offsetWidth)-(parseInt(i.css("borderRightWidth"),10)||0)-(parseInt(i.css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left-this.margins.right,(e?Math.max(s.scrollHeight,s.offsetHeight):s.offsetHeight)-(parseInt(i.css("borderBottomWidth"),10)||0)-(parseInt(i.css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top-this.margins.bottom],this.relativeContainer=i),void 0):(this.containment=null,void 0)},_convertPositionTo:function(t,e){e||(e=this.position);var i="absolute"===t?1:-1,s=this._isRootNode(this.scrollParent[0]);return{top:e.top+this.offset.relative.top*i+this.offset.parent.top*i-("fixed"===this.cssPosition?-this.offset.scroll.top:s?0:this.offset.scroll.top)*i,left:e.left+this.offset.relative.left*i+this.offset.parent.left*i-("fixed"===this.cssPosition?-this.offset.scroll.left:s?0:this.offset.scroll.left)*i}},_generatePosition:function(t,e){var i,s,n,a,o=this.options,r=this._isRootNode(this.scrollParent[0]),h=t.pageX,l=t.pageY;return r&&this.offset.scroll||(this.offset.scroll={top:this.scrollParent.scrollTop(),left:this.scrollParent.scrollLeft()}),e&&(this.containment&&(this.relativeContainer?(s=this.relativeContainer.offset(),i=[this.containment[0]+s.left,this.containment[1]+s.top,this.containment[2]+s.left,this.containment[3]+s.top]):i=this.containment,t.pageX-this.offset.click.left
i[2]&&(h=i[2]+this.offset.click.left),t.pageY-this.offset.click.top>i[3]&&(l=i[3]+this.offset.click.top)),o.grid&&(n=o.grid[1]?this.originalPageY+Math.round((l-this.originalPageY)/o.grid[1])*o.grid[1]:this.originalPageY,l=i?n-this.offset.click.top>=i[1]||n-this.offset.click.top>i[3]?n:n-this.offset.click.top>=i[1]?n-o.grid[1]:n+o.grid[1]:n,a=o.grid[0]?this.originalPageX+Math.round((h-this.originalPageX)/o.grid[0])*o.grid[0]:this.originalPageX,h=i?a-this.offset.click.left>=i[0]||a-this.offset.click.left>i[2]?a:a-this.offset.click.left>=i[0]?a-o.grid[0]:a+o.grid[0]:a),"y"===o.axis&&(h=this.originalPageX),"x"===o.axis&&(l=this.originalPageY)),{top:l-this.offset.click.top-this.offset.relative.top-this.offset.parent.top+("fixed"===this.cssPosition?-this.offset.scroll.top:r?0:this.offset.scroll.top),left:h-this.offset.click.left-this.offset.relative.left-this.offset.parent.left+("fixed"===this.cssPosition?-this.offset.scroll.left:r?0:this.offset.scroll.left)}},_clear:function(){this.helper.removeClass("ui-draggable-dragging"),this.helper[0]===this.element[0]||this.cancelHelperRemoval||this.helper.remove(),this.helper=null,this.cancelHelperRemoval=!1,this.destroyOnClear&&this.destroy()},_normalizeRightBottom:function(){"y"!==this.options.axis&&"auto"!==this.helper.css("right")&&(this.helper.width(this.helper.width()),this.helper.css("right","auto")),"x"!==this.options.axis&&"auto"!==this.helper.css("bottom")&&(this.helper.height(this.helper.height()),this.helper.css("bottom","auto"))},_trigger:function(e,i,s){return s=s||this._uiHash(),t.ui.plugin.call(this,e,[i,s,this],!0),/^(drag|start|stop)/.test(e)&&(this.positionAbs=this._convertPositionTo("absolute"),s.offset=this.positionAbs),t.Widget.prototype._trigger.call(this,e,i,s)},plugins:{},_uiHash:function(){return{helper:this.helper,position:this.position,originalPosition:this.originalPosition,offset:this.positionAbs}}}),t.ui.plugin.add("draggable","connectToSortable",{start:function(e,i,s){var n=t.extend({},i,{item:s.element});s.sortables=[],t(s.options.connectToSortable).each(function(){var i=t(this).sortable("instance");i&&!i.options.disabled&&(s.sortables.push(i),i.refreshPositions(),i._trigger("activate",e,n))})},stop:function(e,i,s){var n=t.extend({},i,{item:s.element});s.cancelHelperRemoval=!1,t.each(s.sortables,function(){var t=this;t.isOver?(t.isOver=0,s.cancelHelperRemoval=!0,t.cancelHelperRemoval=!1,t._storedCSS={position:t.placeholder.css("position"),top:t.placeholder.css("top"),left:t.placeholder.css("left")},t._mouseStop(e),t.options.helper=t.options._helper):(t.cancelHelperRemoval=!0,t._trigger("deactivate",e,n))})},drag:function(e,i,s){t.each(s.sortables,function(){var n=!1,a=this;a.positionAbs=s.positionAbs,a.helperProportions=s.helperProportions,a.offset.click=s.offset.click,a._intersectsWith(a.containerCache)&&(n=!0,t.each(s.sortables,function(){return this.positionAbs=s.positionAbs,this.helperProportions=s.helperProportions,this.offset.click=s.offset.click,this!==a&&this._intersectsWith(this.containerCache)&&t.contains(a.element[0],this.element[0])&&(n=!1),n
7 | })),n?(a.isOver||(a.isOver=1,s._parent=i.helper.parent(),a.currentItem=i.helper.appendTo(a.element).data("ui-sortable-item",!0),a.options._helper=a.options.helper,a.options.helper=function(){return i.helper[0]},e.target=a.currentItem[0],a._mouseCapture(e,!0),a._mouseStart(e,!0,!0),a.offset.click.top=s.offset.click.top,a.offset.click.left=s.offset.click.left,a.offset.parent.left-=s.offset.parent.left-a.offset.parent.left,a.offset.parent.top-=s.offset.parent.top-a.offset.parent.top,s._trigger("toSortable",e),s.dropped=a.element,t.each(s.sortables,function(){this.refreshPositions()}),s.currentItem=s.element,a.fromOutside=s),a.currentItem&&(a._mouseDrag(e),i.position=a.position)):a.isOver&&(a.isOver=0,a.cancelHelperRemoval=!0,a.options._revert=a.options.revert,a.options.revert=!1,a._trigger("out",e,a._uiHash(a)),a._mouseStop(e,!0),a.options.revert=a.options._revert,a.options.helper=a.options._helper,a.placeholder&&a.placeholder.remove(),i.helper.appendTo(s._parent),s._refreshOffsets(e),i.position=s._generatePosition(e,!0),s._trigger("fromSortable",e),s.dropped=!1,t.each(s.sortables,function(){this.refreshPositions()}))})}}),t.ui.plugin.add("draggable","cursor",{start:function(e,i,s){var n=t("body"),a=s.options;n.css("cursor")&&(a._cursor=n.css("cursor")),n.css("cursor",a.cursor)},stop:function(e,i,s){var n=s.options;n._cursor&&t("body").css("cursor",n._cursor)}}),t.ui.plugin.add("draggable","opacity",{start:function(e,i,s){var n=t(i.helper),a=s.options;n.css("opacity")&&(a._opacity=n.css("opacity")),n.css("opacity",a.opacity)},stop:function(e,i,s){var n=s.options;n._opacity&&t(i.helper).css("opacity",n._opacity)}}),t.ui.plugin.add("draggable","scroll",{start:function(t,e,i){i.scrollParentNotHidden||(i.scrollParentNotHidden=i.helper.scrollParent(!1)),i.scrollParentNotHidden[0]!==i.document[0]&&"HTML"!==i.scrollParentNotHidden[0].tagName&&(i.overflowOffset=i.scrollParentNotHidden.offset())},drag:function(e,i,s){var n=s.options,a=!1,o=s.scrollParentNotHidden[0],r=s.document[0];o!==r&&"HTML"!==o.tagName?(n.axis&&"x"===n.axis||(s.overflowOffset.top+o.offsetHeight-e.pageY=0;d--)h=s.snapElements[d].left-s.margins.left,l=h+s.snapElements[d].width,u=s.snapElements[d].top-s.margins.top,c=u+s.snapElements[d].height,h-m>v||g>l+m||u-m>b||_>c+m||!t.contains(s.snapElements[d].item.ownerDocument,s.snapElements[d].item)?(s.snapElements[d].snapping&&s.options.snap.release&&s.options.snap.release.call(s.element,e,t.extend(s._uiHash(),{snapItem:s.snapElements[d].item})),s.snapElements[d].snapping=!1):("inner"!==f.snapMode&&(n=m>=Math.abs(u-b),a=m>=Math.abs(c-_),o=m>=Math.abs(h-v),r=m>=Math.abs(l-g),n&&(i.position.top=s._convertPositionTo("relative",{top:u-s.helperProportions.height,left:0}).top),a&&(i.position.top=s._convertPositionTo("relative",{top:c,left:0}).top),o&&(i.position.left=s._convertPositionTo("relative",{top:0,left:h-s.helperProportions.width}).left),r&&(i.position.left=s._convertPositionTo("relative",{top:0,left:l}).left)),p=n||a||o||r,"outer"!==f.snapMode&&(n=m>=Math.abs(u-_),a=m>=Math.abs(c-b),o=m>=Math.abs(h-g),r=m>=Math.abs(l-v),n&&(i.position.top=s._convertPositionTo("relative",{top:u,left:0}).top),a&&(i.position.top=s._convertPositionTo("relative",{top:c-s.helperProportions.height,left:0}).top),o&&(i.position.left=s._convertPositionTo("relative",{top:0,left:h}).left),r&&(i.position.left=s._convertPositionTo("relative",{top:0,left:l-s.helperProportions.width}).left)),!s.snapElements[d].snapping&&(n||a||o||r||p)&&s.options.snap.snap&&s.options.snap.snap.call(s.element,e,t.extend(s._uiHash(),{snapItem:s.snapElements[d].item})),s.snapElements[d].snapping=n||a||o||r||p)}}),t.ui.plugin.add("draggable","stack",{start:function(e,i,s){var n,a=s.options,o=t.makeArray(t(a.stack)).sort(function(e,i){return(parseInt(t(e).css("zIndex"),10)||0)-(parseInt(t(i).css("zIndex"),10)||0)});o.length&&(n=parseInt(t(o[0]).css("zIndex"),10)||0,t(o).each(function(e){t(this).css("zIndex",n+e)}),this.css("zIndex",n+o.length))}}),t.ui.plugin.add("draggable","zIndex",{start:function(e,i,s){var n=t(i.helper),a=s.options;n.css("zIndex")&&(a._zIndex=n.css("zIndex")),n.css("zIndex",a.zIndex)},stop:function(e,i,s){var n=s.options;n._zIndex&&t(i.helper).css("zIndex",n._zIndex)}}),t.ui.draggable,t.widget("ui.resizable",t.ui.mouse,{version:"1.11.4",widgetEventPrefix:"resize",options:{alsoResize:!1,animate:!1,animateDuration:"slow",animateEasing:"swing",aspectRatio:!1,autoHide:!1,containment:!1,ghost:!1,grid:!1,handles:"e,s,se",helper:!1,maxHeight:null,maxWidth:null,minHeight:10,minWidth:10,zIndex:90,resize:null,start:null,stop:null},_num:function(t){return parseInt(t,10)||0},_isNumber:function(t){return!isNaN(parseInt(t,10))},_hasScroll:function(e,i){if("hidden"===t(e).css("overflow"))return!1;var s=i&&"left"===i?"scrollLeft":"scrollTop",n=!1;return e[s]>0?!0:(e[s]=1,n=e[s]>0,e[s]=0,n)},_create:function(){var e,i,s,n,a,o=this,r=this.options;if(this.element.addClass("ui-resizable"),t.extend(this,{_aspectRatio:!!r.aspectRatio,aspectRatio:r.aspectRatio,originalElement:this.element,_proportionallyResizeElements:[],_helper:r.helper||r.ghost||r.animate?r.helper||"ui-resizable-helper":null}),this.element[0].nodeName.match(/^(canvas|textarea|input|select|button|img)$/i)&&(this.element.wrap(t("").css({position:this.element.css("position"),width:this.element.outerWidth(),height:this.element.outerHeight(),top:this.element.css("top"),left:this.element.css("left")})),this.element=this.element.parent().data("ui-resizable",this.element.resizable("instance")),this.elementIsWrapper=!0,this.element.css({marginLeft:this.originalElement.css("marginLeft"),marginTop:this.originalElement.css("marginTop"),marginRight:this.originalElement.css("marginRight"),marginBottom:this.originalElement.css("marginBottom")}),this.originalElement.css({marginLeft:0,marginTop:0,marginRight:0,marginBottom:0}),this.originalResizeStyle=this.originalElement.css("resize"),this.originalElement.css("resize","none"),this._proportionallyResizeElements.push(this.originalElement.css({position:"static",zoom:1,display:"block"})),this.originalElement.css({margin:this.originalElement.css("margin")}),this._proportionallyResize()),this.handles=r.handles||(t(".ui-resizable-handle",this.element).length?{n:".ui-resizable-n",e:".ui-resizable-e",s:".ui-resizable-s",w:".ui-resizable-w",se:".ui-resizable-se",sw:".ui-resizable-sw",ne:".ui-resizable-ne",nw:".ui-resizable-nw"}:"e,s,se"),this._handles=t(),this.handles.constructor===String)for("all"===this.handles&&(this.handles="n,e,s,w,se,sw,ne,nw"),e=this.handles.split(","),this.handles={},i=0;e.length>i;i++)s=t.trim(e[i]),a="ui-resizable-"+s,n=t(""),n.css({zIndex:r.zIndex}),"se"===s&&n.addClass("ui-icon ui-icon-gripsmall-diagonal-se"),this.handles[s]=".ui-resizable-"+s,this.element.append(n);this._renderAxis=function(e){var i,s,n,a;e=e||this.element;for(i in this.handles)this.handles[i].constructor===String?this.handles[i]=this.element.children(this.handles[i]).first().show():(this.handles[i].jquery||this.handles[i].nodeType)&&(this.handles[i]=t(this.handles[i]),this._on(this.handles[i],{mousedown:o._mouseDown})),this.elementIsWrapper&&this.originalElement[0].nodeName.match(/^(textarea|input|select|button)$/i)&&(s=t(this.handles[i],this.element),a=/sw|ne|nw|se|n|s/.test(i)?s.outerHeight():s.outerWidth(),n=["padding",/ne|nw|n/.test(i)?"Top":/se|sw|s/.test(i)?"Bottom":/^e$/.test(i)?"Right":"Left"].join(""),e.css(n,a),this._proportionallyResize()),this._handles=this._handles.add(this.handles[i])},this._renderAxis(this.element),this._handles=this._handles.add(this.element.find(".ui-resizable-handle")),this._handles.disableSelection(),this._handles.mouseover(function(){o.resizing||(this.className&&(n=this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i)),o.axis=n&&n[1]?n[1]:"se")}),r.autoHide&&(this._handles.hide(),t(this.element).addClass("ui-resizable-autohide").mouseenter(function(){r.disabled||(t(this).removeClass("ui-resizable-autohide"),o._handles.show())}).mouseleave(function(){r.disabled||o.resizing||(t(this).addClass("ui-resizable-autohide"),o._handles.hide())})),this._mouseInit()},_destroy:function(){this._mouseDestroy();var e,i=function(e){t(e).removeClass("ui-resizable ui-resizable-disabled ui-resizable-resizing").removeData("resizable").removeData("ui-resizable").unbind(".resizable").find(".ui-resizable-handle").remove()};return this.elementIsWrapper&&(i(this.element),e=this.element,this.originalElement.css({position:e.css("position"),width:e.outerWidth(),height:e.outerHeight(),top:e.css("top"),left:e.css("left")}).insertAfter(e),e.remove()),this.originalElement.css("resize",this.originalResizeStyle),i(this.originalElement),this},_mouseCapture:function(e){var i,s,n=!1;for(i in this.handles)s=t(this.handles[i])[0],(s===e.target||t.contains(s,e.target))&&(n=!0);return!this.options.disabled&&n},_mouseStart:function(e){var i,s,n,a=this.options,o=this.element;return this.resizing=!0,this._renderProxy(),i=this._num(this.helper.css("left")),s=this._num(this.helper.css("top")),a.containment&&(i+=t(a.containment).scrollLeft()||0,s+=t(a.containment).scrollTop()||0),this.offset=this.helper.offset(),this.position={left:i,top:s},this.size=this._helper?{width:this.helper.width(),height:this.helper.height()}:{width:o.width(),height:o.height()},this.originalSize=this._helper?{width:o.outerWidth(),height:o.outerHeight()}:{width:o.width(),height:o.height()},this.sizeDiff={width:o.outerWidth()-o.width(),height:o.outerHeight()-o.height()},this.originalPosition={left:i,top:s},this.originalMousePosition={left:e.pageX,top:e.pageY},this.aspectRatio="number"==typeof a.aspectRatio?a.aspectRatio:this.originalSize.width/this.originalSize.height||1,n=t(".ui-resizable-"+this.axis).css("cursor"),t("body").css("cursor","auto"===n?this.axis+"-resize":n),o.addClass("ui-resizable-resizing"),this._propagate("start",e),!0},_mouseDrag:function(e){var i,s,n=this.originalMousePosition,a=this.axis,o=e.pageX-n.left||0,r=e.pageY-n.top||0,h=this._change[a];return this._updatePrevProperties(),h?(i=h.apply(this,[e,o,r]),this._updateVirtualBoundaries(e.shiftKey),(this._aspectRatio||e.shiftKey)&&(i=this._updateRatio(i,e)),i=this._respectSize(i,e),this._updateCache(i),this._propagate("resize",e),s=this._applyChanges(),!this._helper&&this._proportionallyResizeElements.length&&this._proportionallyResize(),t.isEmptyObject(s)||(this._updatePrevProperties(),this._trigger("resize",e,this.ui()),this._applyChanges()),!1):!1},_mouseStop:function(e){this.resizing=!1;var i,s,n,a,o,r,h,l=this.options,u=this;return this._helper&&(i=this._proportionallyResizeElements,s=i.length&&/textarea/i.test(i[0].nodeName),n=s&&this._hasScroll(i[0],"left")?0:u.sizeDiff.height,a=s?0:u.sizeDiff.width,o={width:u.helper.width()-a,height:u.helper.height()-n},r=parseInt(u.element.css("left"),10)+(u.position.left-u.originalPosition.left)||null,h=parseInt(u.element.css("top"),10)+(u.position.top-u.originalPosition.top)||null,l.animate||this.element.css(t.extend(o,{top:h,left:r})),u.helper.height(u.size.height),u.helper.width(u.size.width),this._helper&&!l.animate&&this._proportionallyResize()),t("body").css("cursor","auto"),this.element.removeClass("ui-resizable-resizing"),this._propagate("stop",e),this._helper&&this.helper.remove(),!1},_updatePrevProperties:function(){this.prevPosition={top:this.position.top,left:this.position.left},this.prevSize={width:this.size.width,height:this.size.height}},_applyChanges:function(){var t={};return this.position.top!==this.prevPosition.top&&(t.top=this.position.top+"px"),this.position.left!==this.prevPosition.left&&(t.left=this.position.left+"px"),this.size.width!==this.prevSize.width&&(t.width=this.size.width+"px"),this.size.height!==this.prevSize.height&&(t.height=this.size.height+"px"),this.helper.css(t),t},_updateVirtualBoundaries:function(t){var e,i,s,n,a,o=this.options;a={minWidth:this._isNumber(o.minWidth)?o.minWidth:0,maxWidth:this._isNumber(o.maxWidth)?o.maxWidth:1/0,minHeight:this._isNumber(o.minHeight)?o.minHeight:0,maxHeight:this._isNumber(o.maxHeight)?o.maxHeight:1/0},(this._aspectRatio||t)&&(e=a.minHeight*this.aspectRatio,s=a.minWidth/this.aspectRatio,i=a.maxHeight*this.aspectRatio,n=a.maxWidth/this.aspectRatio,e>a.minWidth&&(a.minWidth=e),s>a.minHeight&&(a.minHeight=s),a.maxWidth>i&&(a.maxWidth=i),a.maxHeight>n&&(a.maxHeight=n)),this._vBoundaries=a},_updateCache:function(t){this.offset=this.helper.offset(),this._isNumber(t.left)&&(this.position.left=t.left),this._isNumber(t.top)&&(this.position.top=t.top),this._isNumber(t.height)&&(this.size.height=t.height),this._isNumber(t.width)&&(this.size.width=t.width)},_updateRatio:function(t){var e=this.position,i=this.size,s=this.axis;return this._isNumber(t.height)?t.width=t.height*this.aspectRatio:this._isNumber(t.width)&&(t.height=t.width/this.aspectRatio),"sw"===s&&(t.left=e.left+(i.width-t.width),t.top=null),"nw"===s&&(t.top=e.top+(i.height-t.height),t.left=e.left+(i.width-t.width)),t},_respectSize:function(t){var e=this._vBoundaries,i=this.axis,s=this._isNumber(t.width)&&e.maxWidth&&e.maxWidtht.width,o=this._isNumber(t.height)&&e.minHeight&&e.minHeight>t.height,r=this.originalPosition.left+this.originalSize.width,h=this.position.top+this.size.height,l=/sw|nw|w/.test(i),u=/nw|ne|n/.test(i);return a&&(t.width=e.minWidth),o&&(t.height=e.minHeight),s&&(t.width=e.maxWidth),n&&(t.height=e.maxHeight),a&&l&&(t.left=r-e.minWidth),s&&l&&(t.left=r-e.maxWidth),o&&u&&(t.top=h-e.minHeight),n&&u&&(t.top=h-e.maxHeight),t.width||t.height||t.left||!t.top?t.width||t.height||t.top||!t.left||(t.left=null):t.top=null,t},_getPaddingPlusBorderDimensions:function(t){for(var e=0,i=[],s=[t.css("borderTopWidth"),t.css("borderRightWidth"),t.css("borderBottomWidth"),t.css("borderLeftWidth")],n=[t.css("paddingTop"),t.css("paddingRight"),t.css("paddingBottom"),t.css("paddingLeft")];4>e;e++)i[e]=parseInt(s[e],10)||0,i[e]+=parseInt(n[e],10)||0;return{height:i[0]+i[2],width:i[1]+i[3]}},_proportionallyResize:function(){if(this._proportionallyResizeElements.length)for(var t,e=0,i=this.helper||this.element;this._proportionallyResizeElements.length>e;e++)t=this._proportionallyResizeElements[e],this.outerDimensions||(this.outerDimensions=this._getPaddingPlusBorderDimensions(t)),t.css({height:i.height()-this.outerDimensions.height||0,width:i.width()-this.outerDimensions.width||0})},_renderProxy:function(){var e=this.element,i=this.options;this.elementOffset=e.offset(),this._helper?(this.helper=this.helper||t(""),this.helper.addClass(this._helper).css({width:this.element.outerWidth()-1,height:this.element.outerHeight()-1,position:"absolute",left:this.elementOffset.left+"px",top:this.elementOffset.top+"px",zIndex:++i.zIndex}),this.helper.appendTo("body").disableSelection()):this.helper=this.element},_change:{e:function(t,e){return{width:this.originalSize.width+e}},w:function(t,e){var i=this.originalSize,s=this.originalPosition;return{left:s.left+e,width:i.width-e}},n:function(t,e,i){var s=this.originalSize,n=this.originalPosition;return{top:n.top+i,height:s.height-i}},s:function(t,e,i){return{height:this.originalSize.height+i}},se:function(e,i,s){return t.extend(this._change.s.apply(this,arguments),this._change.e.apply(this,[e,i,s]))},sw:function(e,i,s){return t.extend(this._change.s.apply(this,arguments),this._change.w.apply(this,[e,i,s]))},ne:function(e,i,s){return t.extend(this._change.n.apply(this,arguments),this._change.e.apply(this,[e,i,s]))},nw:function(e,i,s){return t.extend(this._change.n.apply(this,arguments),this._change.w.apply(this,[e,i,s]))}},_propagate:function(e,i){t.ui.plugin.call(this,e,[i,this.ui()]),"resize"!==e&&this._trigger(e,i,this.ui())},plugins:{},ui:function(){return{originalElement:this.originalElement,element:this.element,helper:this.helper,position:this.position,size:this.size,originalSize:this.originalSize,originalPosition:this.originalPosition}}}),t.ui.plugin.add("resizable","animate",{stop:function(e){var i=t(this).resizable("instance"),s=i.options,n=i._proportionallyResizeElements,a=n.length&&/textarea/i.test(n[0].nodeName),o=a&&i._hasScroll(n[0],"left")?0:i.sizeDiff.height,r=a?0:i.sizeDiff.width,h={width:i.size.width-r,height:i.size.height-o},l=parseInt(i.element.css("left"),10)+(i.position.left-i.originalPosition.left)||null,u=parseInt(i.element.css("top"),10)+(i.position.top-i.originalPosition.top)||null;i.element.animate(t.extend(h,u&&l?{top:u,left:l}:{}),{duration:s.animateDuration,easing:s.animateEasing,step:function(){var s={width:parseInt(i.element.css("width"),10),height:parseInt(i.element.css("height"),10),top:parseInt(i.element.css("top"),10),left:parseInt(i.element.css("left"),10)};n&&n.length&&t(n[0]).css({width:s.width,height:s.height}),i._updateCache(s),i._propagate("resize",e)}})}}),t.ui.plugin.add("resizable","containment",{start:function(){var e,i,s,n,a,o,r,h=t(this).resizable("instance"),l=h.options,u=h.element,c=l.containment,d=c instanceof t?c.get(0):/parent/.test(c)?u.parent().get(0):c;d&&(h.containerElement=t(d),/document/.test(c)||c===document?(h.containerOffset={left:0,top:0},h.containerPosition={left:0,top:0},h.parentData={element:t(document),left:0,top:0,width:t(document).width(),height:t(document).height()||document.body.parentNode.scrollHeight}):(e=t(d),i=[],t(["Top","Right","Left","Bottom"]).each(function(t,s){i[t]=h._num(e.css("padding"+s))}),h.containerOffset=e.offset(),h.containerPosition=e.position(),h.containerSize={height:e.innerHeight()-i[3],width:e.innerWidth()-i[1]},s=h.containerOffset,n=h.containerSize.height,a=h.containerSize.width,o=h._hasScroll(d,"left")?d.scrollWidth:a,r=h._hasScroll(d)?d.scrollHeight:n,h.parentData={element:d,left:s.left,top:s.top,width:o,height:r}))},resize:function(e){var i,s,n,a,o=t(this).resizable("instance"),r=o.options,h=o.containerOffset,l=o.position,u=o._aspectRatio||e.shiftKey,c={top:0,left:0},d=o.containerElement,p=!0;d[0]!==document&&/static/.test(d.css("position"))&&(c=h),l.left<(o._helper?h.left:0)&&(o.size.width=o.size.width+(o._helper?o.position.left-h.left:o.position.left-c.left),u&&(o.size.height=o.size.width/o.aspectRatio,p=!1),o.position.left=r.helper?h.left:0),l.top<(o._helper?h.top:0)&&(o.size.height=o.size.height+(o._helper?o.position.top-h.top:o.position.top),u&&(o.size.width=o.size.height*o.aspectRatio,p=!1),o.position.top=o._helper?h.top:0),n=o.containerElement.get(0)===o.element.parent().get(0),a=/relative|absolute/.test(o.containerElement.css("position")),n&&a?(o.offset.left=o.parentData.left+o.position.left,o.offset.top=o.parentData.top+o.position.top):(o.offset.left=o.element.offset().left,o.offset.top=o.element.offset().top),i=Math.abs(o.sizeDiff.width+(o._helper?o.offset.left-c.left:o.offset.left-h.left)),s=Math.abs(o.sizeDiff.height+(o._helper?o.offset.top-c.top:o.offset.top-h.top)),i+o.size.width>=o.parentData.width&&(o.size.width=o.parentData.width-i,u&&(o.size.height=o.size.width/o.aspectRatio,p=!1)),s+o.size.height>=o.parentData.height&&(o.size.height=o.parentData.height-s,u&&(o.size.width=o.size.height*o.aspectRatio,p=!1)),p||(o.position.left=o.prevPosition.left,o.position.top=o.prevPosition.top,o.size.width=o.prevSize.width,o.size.height=o.prevSize.height)},stop:function(){var e=t(this).resizable("instance"),i=e.options,s=e.containerOffset,n=e.containerPosition,a=e.containerElement,o=t(e.helper),r=o.offset(),h=o.outerWidth()-e.sizeDiff.width,l=o.outerHeight()-e.sizeDiff.height;e._helper&&!i.animate&&/relative/.test(a.css("position"))&&t(this).css({left:r.left-n.left-s.left,width:h,height:l}),e._helper&&!i.animate&&/static/.test(a.css("position"))&&t(this).css({left:r.left-n.left-s.left,width:h,height:l})}}),t.ui.plugin.add("resizable","alsoResize",{start:function(){var e=t(this).resizable("instance"),i=e.options;t(i.alsoResize).each(function(){var e=t(this);e.data("ui-resizable-alsoresize",{width:parseInt(e.width(),10),height:parseInt(e.height(),10),left:parseInt(e.css("left"),10),top:parseInt(e.css("top"),10)})})},resize:function(e,i){var s=t(this).resizable("instance"),n=s.options,a=s.originalSize,o=s.originalPosition,r={height:s.size.height-a.height||0,width:s.size.width-a.width||0,top:s.position.top-o.top||0,left:s.position.left-o.left||0};t(n.alsoResize).each(function(){var e=t(this),s=t(this).data("ui-resizable-alsoresize"),n={},a=e.parents(i.originalElement[0]).length?["width","height"]:["width","height","top","left"];t.each(a,function(t,e){var i=(s[e]||0)+(r[e]||0);i&&i>=0&&(n[e]=i||null)}),e.css(n)})},stop:function(){t(this).removeData("resizable-alsoresize")}}),t.ui.plugin.add("resizable","ghost",{start:function(){var e=t(this).resizable("instance"),i=e.options,s=e.size;e.ghost=e.originalElement.clone(),e.ghost.css({opacity:.25,display:"block",position:"relative",height:s.height,width:s.width,margin:0,left:0,top:0}).addClass("ui-resizable-ghost").addClass("string"==typeof i.ghost?i.ghost:""),e.ghost.appendTo(e.helper)},resize:function(){var e=t(this).resizable("instance");e.ghost&&e.ghost.css({position:"relative",height:e.size.height,width:e.size.width})},stop:function(){var e=t(this).resizable("instance");e.ghost&&e.helper&&e.helper.get(0).removeChild(e.ghost.get(0))}}),t.ui.plugin.add("resizable","grid",{resize:function(){var e,i=t(this).resizable("instance"),s=i.options,n=i.size,a=i.originalSize,o=i.originalPosition,r=i.axis,h="number"==typeof s.grid?[s.grid,s.grid]:s.grid,l=h[0]||1,u=h[1]||1,c=Math.round((n.width-a.width)/l)*l,d=Math.round((n.height-a.height)/u)*u,p=a.width+c,f=a.height+d,m=s.maxWidth&&p>s.maxWidth,g=s.maxHeight&&f>s.maxHeight,v=s.minWidth&&s.minWidth>p,_=s.minHeight&&s.minHeight>f;s.grid=h,v&&(p+=l),_&&(f+=u),m&&(p-=l),g&&(f-=u),/^(se|s|e)$/.test(r)?(i.size.width=p,i.size.height=f):/^(ne)$/.test(r)?(i.size.width=p,i.size.height=f,i.position.top=o.top-d):/^(sw)$/.test(r)?(i.size.width=p,i.size.height=f,i.position.left=o.left-c):((0>=f-u||0>=p-l)&&(e=i._getPaddingPlusBorderDimensions(this)),f-u>0?(i.size.height=f,i.position.top=o.top-d):(f=u-e.height,i.size.height=f,i.position.top=o.top+a.height-f),p-l>0?(i.size.width=p,i.position.left=o.left-c):(p=l-e.width,i.size.width=p,i.position.left=o.left+a.width-p))}}),t.ui.resizable});
--------------------------------------------------------------------------------
/chrome/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "background": {
3 | "page": "background.html"
4 | },
5 | "options_ui": {
6 | "page": "options.html",
7 | "open_in_tab": false
8 | },
9 | "content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'",
10 | "description": "SVG Screenshot",
11 | "icons": {
12 | "128": "image/icon.png",
13 | "48" : "image/icon.png",
14 | "16" : "image/icon.png"
15 | },
16 | "browser_action": {
17 | "default_icon": {
18 | "19": "image/icon-l.png",
19 | "38": "image/icon-l.png"
20 | },
21 | "default_title": "SVG Screenshot",
22 | "default_popup": "popup.html"
23 | },
24 | "manifest_version": 2,
25 | "name": "SVG Screenshot",
26 | "content_scripts": [{
27 | "matches": [""],
28 | "css": [
29 | "lib/jquery-ui.min.css",
30 | "css/insite.css"
31 | ],
32 | "js": [
33 | "lib/jquery.js",
34 | "lib/jquery-ui.min.js",
35 | "build/bundle.js",
36 | "build/common.js",
37 | "build/inline_viewer.js",
38 | "build/content_script.js"
39 | ],
40 | "run_at": "document_end"
41 | }],
42 |
43 | "web_accessible_resources": [
44 | "image/x.png",
45 | "image/linkcard/*"
46 | ],
47 |
48 | "permissions": [
49 | "contextMenus",
50 | "pageCapture",
51 | "tabs",
52 | "activeTab",
53 | "cookies",
54 | ""
55 | ],
56 | "version": "0.7.7"
57 | }
58 |
--------------------------------------------------------------------------------
/chrome/options.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Settings - SVG ScreenShot
6 |
7 |
20 |
21 |
22 |
23 | The following feature can be used only when logged in to
24 |
Gyazo.
25 |
26 |
27 |
28 | Gyazoにもアップロードする
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/chrome/popup.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | DynamicGazo
6 |
33 |
39 |
40 |
41 |
42 | SVG Screenshot
43 |
44 |
45 |
46 | 範囲選択して撮影できます。
47 |
48 |
49 |
64 |
65 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
--------------------------------------------------------------------------------
/client/background.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | let META = {}
3 |
4 | const uploadToGyazo = async ({svgScreenshotImageId, hashTag, base64Img, devicePixelRatio}) => {
5 | const {baseUri, title} = META
6 | await window.dynamicGazo.uploadToGyazo({
7 | title,
8 | referer: baseUri,
9 | image: base64Img,
10 | scale: devicePixelRatio,
11 | svgScreenshotImageId,
12 | hashTag
13 | })
14 | }
15 |
16 | const uploadToSVGScreenshot = async ({devicePixelRatio}) => {
17 | const {baseUri, title, base64Img} = META
18 | const {useGyazo, gyazoHashtag} = readOptions()
19 |
20 | // SVGタグを生成する
21 | const svg = createSVGTag(META)
22 | const imageDataURI = dynamicGazo.addPhysChunk(base64Img, devicePixelRatio)
23 | const res = await dynamicGazo.uploadToDynamicGazo({
24 | svg,
25 | title,
26 | referer: baseUri,
27 | base64Img: imageDataURI,
28 | devicePixelRatio
29 | })
30 | if (res.status === 200 && res.data.x_key) {
31 | updateLocalStorage({
32 | item_url: `${window.dynamicGazo.appOrigin}/x/${res.data.x_key}`,
33 | item_img: `${window.dynamicGazo.appOrigin}/c/x/${res.data.x_key}.png`,
34 | message: 'y'
35 | })
36 |
37 | if (useGyazo === 'yes') {
38 | setBadgeUploadingToGyazo()
39 | await uploadToGyazo({
40 | svgScreenshotImageId: res.data.x_key,
41 | hashTag: gyazoHashtag || '',
42 | base64Img: imageDataURI,
43 | devicePixelRatio
44 | })
45 | }
46 | clearBadge()
47 | } else {
48 | // XXX: 適切なstatus codeが返ってきていない!
49 | handleError(res.data)
50 | }
51 | return res
52 | }
53 |
54 |
55 | const updateLocalStorage = ({item_url, item_img, message} = {item_url: '', item_img: ''}) => {
56 | localStorage.item_url = item_url
57 | localStorage.item_img = item_img
58 | localStorage.is_error = message
59 | }
60 |
61 | const handleError = ({ status }) => {
62 | chrome.browserAction.setBadgeBackgroundColor({ color: 'red' })
63 | chrome.browserAction.setBadgeText({ text: '😇' })
64 | switch (status) {
65 | case 'exceed-screenshots-upper-limit': {
66 | updateLocalStorage({
67 | message: 'ファイルの上限数に達しています。'
68 | })
69 | break
70 | }
71 | case 'no-login': {
72 | updateLocalStorage({
73 | message: 'ウェブアプリにログインしていません。'
74 | })
75 | break
76 | }
77 | default: {
78 | updateLocalStorage({
79 | message: 'アップロードに失敗しました。'
80 | })
81 | break
82 | }
83 | }
84 | }
85 |
86 | // Canvasに画像をセットして,必要部分のみ切り出す
87 | const renderImage = function (linkdata, base64Img, devicePixelRatio) {
88 | var rat = devicePixelRatio;
89 | var canvas = document.querySelector("#cav");
90 | var pos_cropper = linkdata.cropperRect;
91 | var baseUri = linkdata.baseUri;
92 | var title = linkdata.title;
93 | var w = +pos_cropper.width;
94 | var h = +pos_cropper.height;
95 | canvas.width = rat * w;
96 | canvas.height = rat * h;
97 |
98 | var ctx = canvas.getContext('2d');
99 | var img = new Image();
100 | img.onload = function () {
101 | ctx.drawImage(img, rat * pos_cropper.orgX, rat * pos_cropper.orgY, rat * w, rat * h,
102 | 0, 0, rat * w, rat * h)
103 | const screenshot = canvas.toDataURL('image/png')
104 | keepMetaData(
105 | linkdata.aTagRects,
106 | linkdata.elementRects,
107 | linkdata.text,
108 | w,
109 | h,
110 | baseUri,
111 | title,
112 | rat,
113 | screenshot)
114 | uploadToSVGScreenshot({
115 | devicePixelRatio: rat
116 | })
117 | };
118 | img.src = base64Img;
119 | };
120 |
121 | const keepMetaData = (aTagRects, elementRects, text, width, height, baseUri, title, devicePixelRatio, base64Img) => {
122 | META = { aTagRects, elementRects, text, width, height, baseUri, title, devicePixelRatio, base64Img }
123 | }
124 |
125 | // ポップアップ画面から命令を受ける
126 | chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
127 | var opts = request.options;
128 |
129 | if (request.command === 'make-screen-shot') {
130 | // スクリーンショットの撮影
131 | var linkdata = opts.sitedata;
132 | chrome.tabs.captureVisibleTab({ format: 'png' }, function (dataUrl) {
133 | setBadgeCaptureCompleted()
134 | renderImage(linkdata, dataUrl, opts.dpr)
135 | });
136 | }
137 | });
138 |
139 | var initScreenShotMenu = () => {
140 | // ユーザーが閲覧中のページに専用の右クリックメニューを設ける
141 | // ウェブページ向け
142 | chrome.contextMenus.create({
143 | title: 'Capture whole page',
144 | contexts: [
145 | 'page',
146 | 'selection'
147 | ],
148 | onclick: function (clicked, tab) {
149 | clearBadge()
150 | chrome.tabs.sendRequest(tab.id, {
151 | event: 'capture-whole-page'
152 | });
153 | }
154 | })
155 | };
156 |
157 | initScreenShotMenu();
158 |
159 | chrome.tabs.onUpdated.addListener(function(tabId, info, tab) {
160 | if (info.status === 'complete') {
161 | chrome.tabs.sendRequest(tab.id, {
162 | event: 'updated-location-href'
163 | });
164 | }
165 | })
166 | })();
167 |
--------------------------------------------------------------------------------
/client/common.js:
--------------------------------------------------------------------------------
1 | // BrowserActionのBadgeをクリア
2 | const clearBadge = () => {
3 | chrome.browserAction.setBadgeText({ 'text': '' })
4 | }
5 |
6 | const setBadgeCaptureCompleted = () => {
7 | chrome.browserAction.setBadgeBackgroundColor({ color: '#4abb0c' })
8 | chrome.browserAction.setBadgeText({ text: '○' })
9 | }
10 |
11 | const setBadgeUploadingToGyazo = () => {
12 | chrome.browserAction.setBadgeBackgroundColor({ color: '#4abb0c' })
13 | chrome.browserAction.setBadgeText({ text: 'G' })
14 | }
15 |
16 | const readOptions = () => {
17 | if (!localStorage.svgscreenshot_settings) return {}
18 | return JSON.parse(localStorage.svgscreenshot_settings)
19 | }
20 |
21 | // ブラウザ側でもa.href, titleを確認する
22 | var validateUrl = (url='') => {
23 | // http, https で始まるもののみOK
24 | var prot = url.split(':')[0].toLowerCase();
25 | if (prot && (prot === 'http' || prot === 'https')) {
26 | // OK
27 | }else {
28 | return '';
29 | }
30 | // <, > を除去
31 | url = url.replace(//g, '');
32 | return url;
33 | };
34 |
35 | var validateTitle = (title='') => {
36 | // <, > を除去
37 | title = title.replace(//g, '');
38 | return title;
39 | };
40 |
41 | const setAttributeNS = (elem, namespace, attrs) => {
42 | const keys = Object.keys(attrs)
43 | for (const key of keys) {
44 | elem.setAttributeNS(namespace || null, key, attrs[key])
45 | }
46 | return elem
47 | }
48 |
--------------------------------------------------------------------------------
/client/content_script.js:
--------------------------------------------------------------------------------
1 | const APP_PREFIX = 'dynamic_gazo';
2 | const dynamicGazo = window.dynamicGazo
3 | const ESC = 27
4 | let flagMousedown = false
5 |
6 | const sendChromeMsg = (json, callback) => {
7 | chrome.runtime.sendMessage(json, callback);
8 | };
9 |
10 | class ScreenShot {
11 | constructor () {
12 | this.CROP_BOX_SIZE = 150;
13 | this.uiInit();
14 | this.positionLastRclick = [200, 200];
15 | this.linkdata = null;
16 | this.tmp = {
17 | // 右クリックされた画像要素
18 | '$contextMenuImg': []
19 | };
20 | this.inlineViewer = null;
21 |
22 | // アプリケーションとしてSVG撮影を使う場合,アプリ名がセットされる
23 | this.app = null;
24 | }
25 |
26 | renderCropper (boxParams = []) {
27 | var self = this;
28 | self.initCropperMain(boxParams, null)
29 | }
30 |
31 | uiInit () {
32 | this.bindEvents();
33 | }
34 |
35 | // 切り抜きボックス, a要素カバーボックス
36 | $genCropper () {
37 | var $cropper = $(``);
38 | $cropper.css({
39 | top : 0,
40 | left : 0,
41 | width : this.CROP_BOX_SIZE,
42 | height: this.CROP_BOX_SIZE
43 | });
44 | return $cropper;
45 | }
46 |
47 | // true : 表示中のウェブページをスクロール不可にする
48 | // false: 解除する
49 | fixHtml (fg) {
50 | var fg = fg || false;
51 | if (fg) {
52 | $('html').css({
53 | height : '100%',
54 | width : '100%',
55 | overflow: 'hidden'
56 | })
57 | }else {
58 | $('html').css({
59 | height : '',
60 | width : '',
61 | overflow: 'auto'
62 | })
63 | }
64 | }
65 |
66 | $showWrapper () {
67 | const $body = $('body')
68 | const $wrapper = $(``)
69 | $wrapper.css({
70 | position: 'fixed',
71 | top: 0,
72 | left: 0,
73 | width: '100%',
74 | height: '100%',
75 | cursor: 'crosshair',
76 | zIndex: 2147483646
77 | })
78 | $body.append($wrapper)
79 | return $wrapper
80 | }
81 |
82 | // 範囲指定のための長方形を表示する
83 | initCropperMain () {
84 | const self = this
85 | const $cropper = this.$genCropper()
86 | const $wrapper = this.$showWrapper()
87 | const startPosition = {
88 | left: 0,
89 | top: 0
90 | }
91 |
92 | $cropper[0].className = 'daiz-ss-cropper-main'
93 | $cropper[0].id = `${APP_PREFIX}-daiz-ss-cropper-main`
94 |
95 | $wrapper.on('mousedown', event => {
96 | startPosition.left = event.pageX
97 | startPosition.top = event.pageY
98 | $cropper.css({
99 | left : startPosition.left - window.scrollX,
100 | top : startPosition.top - window.scrollY,
101 | width : 0,
102 | height: 0
103 | })
104 | flagMousedown = true
105 | self.fixHtml(true)
106 | $('body').append($cropper)
107 | })
108 |
109 | this.setMousemoveHandler($wrapper, $cropper, startPosition)
110 | this.setMousemoveHandler($cropper, $cropper, startPosition)
111 |
112 | $wrapper.on('mouseup', event => {
113 | const width = event.pageX - startPosition.left
114 | const height = event.pageY - startPosition.top
115 | if (width < 20 || height < 20) return
116 | $cropper.css({ width, height })
117 | flagMousedown = false
118 | $wrapper.remove()
119 | self._setRects(false)
120 | })
121 |
122 | $(window).on('keyup', event => {
123 | if (event.keyCode === ESC) self.clearCropper()
124 | })
125 | }
126 |
127 | clearCropper () {
128 | flagMousedown = false
129 | this.clean()
130 | }
131 |
132 | setMousemoveHandler ($elem, $cropper, startPosition) {
133 | const self = this
134 | $elem.on('mousemove', event => {
135 | if (!flagMousedown) return
136 | $cropper.css({
137 | width : event.pageX - startPosition.left,
138 | height: event.pageY - startPosition.top,
139 | })
140 | self._setRects(true)
141 | })
142 | }
143 |
144 | _setRects (simulate=false, _range=undefined) {
145 | var $cropper = $(`#${APP_PREFIX}-daiz-ss-cropper-main`)
146 | const range = _range || $cropper[0].getBoundingClientRect()
147 | if (range === undefined) return;
148 | this.removeCropper()
149 |
150 | if (simulate) {
151 | this.linkdata = this.setRects(range, simulate)
152 | } else {
153 | this.clean()
154 | // ページから不要なdivが消去されてからスクリーンショットを撮りたいので,
155 | // 1秒待ってから送信する
156 | window.setTimeout(() => {
157 | if (this.existCropUI()) {
158 | console.log('rep')
159 | this._setRects(false, range)
160 | return
161 | }
162 | this.linkdata = this.setRects(range, simulate)
163 | this.capture()
164 | }, 1)
165 | }
166 | }
167 |
168 | // ページ上で選択されている文字列を取得
169 | getSelectedText () {
170 | var self = this;
171 | var selection = window.getSelection();
172 | var text = selection.toString();
173 | return text;
174 | }
175 |
176 | setRects (range, simulate=false) {
177 | // this.fixHtml(true)
178 | let $cropperMain = null
179 | if (!simulate) {
180 | $cropperMain = $(this.removeCropperMain())
181 | }
182 |
183 | const anchorsInArea = new dynamicGazo.AnchorsInArea(document)
184 | anchorsInArea.options.detail = true
185 | anchorsInArea.options.onlyInTopLayer = !simulate
186 | const aTags = anchorsInArea.find(range)
187 |
188 | let imgTags = []
189 | if (!simulate) {
190 | // XXX: 要素が増えたら、共通化
191 | imgTags = this.correctPositions(anchorsInArea.find(range, 'img'), range)
192 | for (const imgTag of imgTags) {
193 | imgTag.css = {}
194 | imgTag.css['border-radius'] = jQuery(imgTag.ref).css('border-radius') || '0px'
195 | }
196 | }
197 |
198 | // リンク以外のテキスト:
199 | var text = this.getSelectedText();
200 | $('#daiz-ss-cropper-main').attr('title', text);
201 |
202 | // リンク: 切り抜かれた形内のみ,aタグを覆えばよい
203 | var aTagRects = [];
204 | for (var i = 0; i < aTags.length; i++) {
205 | var aTag = aTags[i];
206 | var rect = aTag.position
207 | if (rect !== undefined) {
208 | // リンク要素の位置と大きさに合わせて,長方形カバーを被せる
209 | const $cropper = this.$genCropper();
210 | $cropper.css({
211 | width : rect.width,
212 | height: rect.height,
213 | left : rect.left,
214 | top : rect.top
215 | });
216 | var aid = `daiz-ss-a${i}`;
217 | var pos = this.correctPosition(rect, range);
218 | pos.id = aid;
219 | pos.href = aTag.url;
220 | pos.text = aTag.text;
221 | pos.fontSize = $(aTag.ref).css('font-size');
222 | pos.fontFamily = $(aTag.ref).css('font-family');
223 |
224 | $cropper.attr('title', aTag.url);
225 | $cropper.attr('id', aid);
226 | if (simulate) $('body').append($cropper);
227 | aTagRects.push(pos);
228 | }
229 | }
230 |
231 | // 切り取り領域
232 | var pos_cropper = {
233 | x : 0,
234 | y : 0,
235 | orgX : range.left,
236 | orgY : range.top,
237 | width : range.width,
238 | height: range.height
239 | };
240 |
241 | var title = document.title || '';
242 | if (title.length === 0) {
243 | // PDFページの場合,embedタグからファイル名を抽出して
244 | // titleとする
245 | var embeds = $('embed');
246 | if (embeds.length > 0 && embeds[0].type === 'application/pdf') {
247 | var pdfPath = '/' + embeds[0].src;
248 | var toks = pdfPath.split('/');
249 | title = toks[toks.length - 1];
250 | }
251 | }
252 |
253 | var res = {
254 | cropperRect : pos_cropper,
255 | aTagRects : aTagRects,
256 | elementRects: {
257 | img: imgTags
258 | },
259 | text : text,
260 | winW : window.innerWidth,
261 | winH : window.innerHeight,
262 | baseUri : window.location.href,
263 | title : title
264 | };
265 | return res;
266 | }
267 |
268 | // aタグの位置補正
269 | // stageRectの左端,上端を基準とした距離表現に直す
270 | // aTagRect ⊂ stageRect は保証されている
271 | correctPosition (aTagRect, stageRect) {
272 | // XXX: scrollの扱いを詰める必要あり
273 | let res = {}
274 | const x1 = aTagRect.left - stageRect.left
275 | // var x2 = (aTagRect.left + aTagRect.width) - stageRect.left;
276 | const y1 = aTagRect.top - stageRect.top
277 | // var y2 = (aTagRect.top + aTagRect.height) - stageRect.top;
278 | res = {
279 | x : x1,
280 | y : y1,
281 | width : aTagRect.width,
282 | height: aTagRect.height
283 | }
284 | return res
285 | }
286 |
287 | correctPositions (rects, stageRect) {
288 | for (const rect of rects) {
289 | const {x, y} = this.correctPosition(rect.position, stageRect)
290 | rect.x = x
291 | rect.y = y
292 | }
293 | return rects
294 | }
295 |
296 | // 描画されている長方形カバーを全て消去
297 | removeCropper () {
298 | $('.daiz-ss-cropper').remove();
299 | }
300 |
301 | getCropperMain () {
302 | return $(".daiz-ss-cropper-main")[0]
303 | }
304 |
305 | removeCropperMain () {
306 | const $elem = $(".daiz-ss-cropper-main")
307 | if ($elem.length === 0) return null
308 | const copy = $elem[0].cloneNode(true)
309 | $elem.remove();
310 | return copy
311 | }
312 |
313 | capture (mode='capture') {
314 | var self = this
315 | var res = []
316 | window.getSelection().removeAllRanges()
317 |
318 | // MacBook ProのRetinaディスプレイなどの高解像度な
319 | // ディスプレイを使用している場合は1より大きな値となる
320 | var rat = Math.max(window.devicePixelRatio, 1.0);
321 | if (self.linkdata !== null) {
322 | var appName = self.app;
323 | self.app = null;
324 | sendChromeMsg({
325 | command: 'make-screen-shot',
326 | options: {
327 | sitedata: self.linkdata,
328 | mode: mode,
329 | scrapbox_box_id: null,
330 | app: appName,
331 | dpr: rat
332 | }
333 | });
334 | }
335 | }
336 |
337 | clean () {
338 | if (!this.existCropUI()) return
339 | console.log('clean')
340 | this.removeCropperMain();
341 | this.removeCropper();
342 | $('#daiiz-wrapper').remove()
343 | this.fixHtml(false);
344 | }
345 |
346 | existCropUI () {
347 | const wrapperExist = $('#daiiz-wrapper').length > 0
348 | const cropperMainExist = $('.daiz-ss-cropper-main').length > 0
349 | const cropperExist = $('.daiz-ss-cropper').length > 0
350 | return wrapperExist || cropperMainExist || cropperExist
351 | }
352 |
353 | bindEvents () {
354 | var self = this;
355 | var $body = $('body');
356 |
357 | // 画像上での右クリックを追跡
358 | $body.on('contextmenu', 'img', ev => {
359 | var $img = $(ev.target).closest('img');
360 | this.tmp.$contextMenuImg = $img;
361 | });
362 |
363 | $body.on('contextmenu', '.card-thumbnail', ev => {
364 | var $img = $(ev.target).closest('.card-area').find('.card-img');
365 | this.app = 'linkcard';
366 | self.tmp.$contextMenuImg = $img;
367 | });
368 |
369 | // ページでの右クリックを検出
370 | $(window).bind('contextmenu', (e) => {
371 | this.positionLastRclick = [e.clientX, e.clientY];
372 | });
373 |
374 | // コンテキストメニュー(右クリックメニュー)が押された通知をbackgroundページから受け取る
375 | chrome.extension.onRequest.addListener((request, sender, sendResponse) => {
376 | var re = request.event;
377 | if (re === 'capture-whole-page') {
378 | // 撮影領域を選択するやつを表示
379 | const range = {
380 | left: 0,
381 | right: window.innerWidth,
382 | top: 0,
383 | bottom: window.innerHeight,
384 | width: window.innerWidth,
385 | height: window.innerHeight
386 | }
387 | this.linkdata = this.setRects(range, false)
388 | this.capture()
389 | } else if (re === 'capture-range') {
390 | this.renderCropper()
391 | } else if (re === 'cancel-capture-range') {
392 | this.clean()
393 | }
394 | });
395 |
396 | $body.on('click', '.card-close', ev => {
397 | $('.card-area').remove();
398 | });
399 | }
400 | }
401 | var ss = new ScreenShot();
402 |
403 | chrome.extension.onRequest.addListener((request, sender, sendResponse) => {
404 | var mark = "chrome-ext";
405 | if (request.event === 'updated-location-href') {
406 | var $body = $('body');
407 | if ($body.length > 0) {
408 | $body[0].dataset.stat_daiz_svgss = mark;
409 | }
410 | if (ss.inlineViewer === null) {
411 | ss.inlineViewer = new InlineViewer();
412 | }
413 | }
414 | })
415 |
--------------------------------------------------------------------------------
/client/create_svg.js:
--------------------------------------------------------------------------------
1 | const sourceStyle = [
2 | `.source text {
3 | fill: #888888;
4 | font-size: 11px;
5 | font-weight: 400;
6 | text-decoration: none;
7 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
8 | }`,
9 | `.source text:hover {
10 | text-decoration: underline;
11 | fill: #2962FF;
12 | }`
13 | ]
14 |
15 | const createSVGTag = (
16 | {aTagRects, elementRects, text, width, height, baseUri, title, base64Img}) => {
17 | const externals = []
18 |
19 | // imgs
20 | const imgs = elementRects.img
21 | for (const imgRect of imgs) {
22 | const {url, position} = imgRect
23 | // Gyazo以外の画像は無視
24 | if (url.match(/gyazo\.com\//i) === null) continue
25 | // 静止画像の場合は無視
26 | if (url.match(/\.(svg|png|jpe?g|bmp)$/i) !== null) continue
27 | externals.push({
28 | url,
29 | x: imgRect.x,
30 | y: imgRect.y,
31 | width: position.width,
32 | height: position.height,
33 | type: 'img'
34 | })
35 | }
36 |
37 | // ページ内リンク
38 | for (const anchor of aTagRects) {
39 | externals.push({
40 | url: validateUrl(anchor.href),
41 | x: anchor.x,
42 | y: anchor.y,
43 | width: anchor.width,
44 | height: anchor.height,
45 | text: validateTitle(anchor.text)
46 | })
47 | }
48 |
49 | // 出典
50 | externals.push({
51 | url: validateUrl(baseUri),
52 | text: validateTitle(title),
53 | className: 'source',
54 | x: 4,
55 | y: height - 4
56 | })
57 |
58 | const svgTagText = dynamicGazo.svgize.createSvg(base64Img, {
59 | width, height,
60 | className: 'svg-screenshot',
61 | dataset: {
62 | url: validateUrl(baseUri),
63 | title: validateTitle(title)
64 | },
65 | externals,
66 | style: sourceStyle
67 | })
68 |
69 | return {tagText: svgTagText, viewBox: `0 0 ${width} ${height}`}
70 | }
71 |
--------------------------------------------------------------------------------
/client/inline_viewer.js:
--------------------------------------------------------------------------------
1 | /**
2 | * ウェブページ中で DynamicGazo のプレビューを展開する
3 | * 対象画像をホバーしたときにSVGコンテンツを重ねて表示する
4 | */
5 | class InlineViewer {
6 | constructor () {
7 | this.appUrl = (window.dynamicGazo.env === 'production') ?
8 | 'https://svgscreenshot.appspot.com' : 'http://localhost:8080'
9 | this.gyazo = 'https://gyazo.com'
10 | this.svgScreenShotUrlPatterns = [
11 | `${this.appUrl}/c/x/(.+)`,
12 | `${this.appUrl}/thumb/(.+)`
13 | ]
14 | /* 直近で検出した画像のID */
15 | this.latestImageId = null
16 | this.hideAllSVGScreenShots()
17 | this.bindEvents()
18 | }
19 |
20 | detectImageId (src, urlPatterns) {
21 | let imgId = null;
22 | for (let i = 0; i < urlPatterns.length; i++) {
23 | let pattern = urlPatterns[i];
24 | let reg = new RegExp(pattern);
25 | let matched = src.match(reg);
26 | if (matched && matched.length >= 2) {
27 | imgId = matched[1].split('#')[0].split('.')[0]
28 | break;
29 | }
30 | }
31 | if (imgId === null) return null;
32 | if (imgId.indexOf('/') !== -1) return null;
33 | return imgId;
34 | }
35 |
36 | $getCover (cid='', $img) {
37 | // cid is cover-id
38 | var coverId = 'daiz-ss-iv-cover-c-' + cid;
39 | var pageX = window.pageXOffset;
40 | var pageY = window.pageYOffset;
41 |
42 | var $cover = $(`#${coverId}`);
43 | var newCover = false;
44 |
45 | // 存在しない場合は新規作成する
46 | if ($cover.length === 0) {
47 | newCover = true;
48 | var optionClassName = '';
49 | if (window.location.host === 'gyazo.com') {
50 | optionClassName = 'gyazo-com'
51 | }
52 | $cover = $(``);
56 |
57 | $cover.css({
58 | width: $img.width(),
59 | height: $img.height(),
60 | display: 'none'
61 | });
62 | }
63 |
64 | var imgRect = $img[0].getBoundingClientRect();
65 | $cover.css({
66 | left: imgRect.left + pageX,
67 | top: imgRect.top + pageY,
68 | cursor: $img.css('cursor') || 'default'
69 | });
70 |
71 | return [$cover, newCover];
72 | }
73 |
74 | // SVGコンテンツを表示する
75 | renderSVGScreenShot ($cover, cid='') {
76 | var cover = $cover[0]
77 | var coverWidth = $cover.width()
78 | var coverHeight = $cover.height()
79 | var $svgArea = $cover.find('.daiz-ss-iv-svg')
80 | var svgUrl = `${this.appUrl}/d/s/${cid}`
81 |
82 | $.ajax({
83 | url: svgUrl,
84 | method: "POST",
85 | dataType: "json"
86 | }).success(data => {
87 | let svgTag = data.svg_tag;
88 | let appName = data.app_name || null;
89 | if (svgTag.length === 0) return;
90 | var doc = new DOMParser().parseFromString(svgTag, 'application/xml');
91 | $svgArea[0].appendChild(cover.ownerDocument.importNode(doc.documentElement, true));
92 | var svg = cover.querySelector('svg.svg-screenshot');
93 | var orgUrl = data.url;
94 | var title = data.title;
95 | // SVGレイヤーのサイズを設定
96 | // viewBox.width, viewBox.height: SVGのオリジナルサイズ
97 | // coverWidth, coverHeight: サムネイルのサイズ
98 | svg.setAttribute('width', coverWidth);
99 | svg.setAttribute('height', coverHeight);
100 | $cover.show();
101 | });
102 | }
103 |
104 | // SVGコンテンツを最新のサムネイルのサイズに合わせる
105 | updateSVGScreenShotSize ($cover, $img) {
106 | if ($cover.find('.daiz-ss-iv-svg')[0].innerHTML.trim() === '') return;
107 |
108 | var w = $img.width();
109 | var h = $img.height();
110 | $cover.css({
111 | width: w,
112 | height: h
113 | });
114 | var svg = $cover[0].querySelector('svg.svg-screenshot');
115 | if (svg) {
116 | svg.setAttribute('width', w);
117 | svg.setAttribute('height', h);
118 | }
119 | $cover.show();
120 | }
121 |
122 | // 全てのcoverを非表示にする
123 | hideAllSVGScreenShots () {
124 | // 既存の消し忘れカバーを消す
125 | $('.daiz-ss-iv-cover').css('display', 'none');
126 | }
127 |
128 | bindEvents () {
129 | var self = this;
130 | var $body = $('body');
131 |
132 | const showLinkLayer = e => {
133 | const $img = $(e.target).closest('img')
134 |
135 | // 対象画像であるかを確認
136 | const src = decodeURIComponent($img[0].src)
137 | const imageId = self.detectImageId(src, self.svgScreenShotUrlPatterns)
138 |
139 | if (imageId === null) return
140 | if (imageId !== this.latestImageId) {
141 | this.latestImageId = imageId
142 | }
143 |
144 | self.hideAllSVGScreenShots()
145 | var coverInfo = self.$getCover(imageId, $img)
146 | var $cover = coverInfo[0]
147 | if (coverInfo[1]) {
148 | // 新規作成されたカバー
149 | $cover.on('click', event => {
150 | const className = event.target.className.baseVal
151 | if (className !== 'svg-screenshot') return
152 | $cover.hide()
153 | $img.trigger('click')
154 | })
155 |
156 | $body.append($cover)
157 | self.renderSVGScreenShot($cover, imageId)
158 | }else {
159 | self.updateSVGScreenShotSize($cover, $img)
160 | }
161 | }
162 |
163 | // 画像mouseenter時
164 | $body.on('mouseenter', 'img', e => {
165 | showLinkLayer(e)
166 | });
167 |
168 | // 画像mouseleave時
169 | $body.on('mouseleave', '.daiz-ss-iv-cover', e => {
170 | var $cover = $(e.target).closest('.daiz-ss-iv-cover');
171 | $cover.hide();
172 | });
173 | }
174 | }
175 |
--------------------------------------------------------------------------------
/client/options.js:
--------------------------------------------------------------------------------
1 | $(function () {
2 | const getOptions = () => {
3 | return JSON.parse(localStorage.svgscreenshot_settings)
4 | }
5 |
6 | const saveOptions = function (obj) {
7 | localStorage.svgscreenshot_settings = JSON.stringify(obj)
8 | }
9 |
10 | if (!localStorage.svgscreenshot_settings) {
11 | saveOptions({
12 | useGyazo: "no",
13 | gyazoHashtag: "#SVGScreenshot"
14 | })
15 | }else {
16 | const {useGyazo, gyazoHashtag} = getOptions()
17 | $('#use_gyazo')[0].checked = useGyazo === 'yes'
18 | $('#gyazo_hashtag')[0].value = gyazoHashtag
19 | }
20 |
21 | $('#btn_save').on('click', () => {
22 | const useGyazo = ($('#use_gyazo')[0].checked) ? 'yes' : 'no'
23 | const gyazoHashtag = $('#gyazo_hashtag')[0].value
24 | saveOptions({useGyazo, gyazoHashtag})
25 | window.close()
26 | })
27 | })
28 |
--------------------------------------------------------------------------------
/client/popup.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | const replaceToDevUrls = () => {
3 | if (window.dynamicGazo.env === 'production') return
4 | const targets = [
5 | '#open',
6 | '#y-collection',
7 | '#login',
8 | '#collection'
9 | ]
10 | for (const target of targets) {
11 | const url = document.querySelector(target).href
12 | document.querySelector(target).href = url.replace(
13 | /^https\:\/\/svgscreenshot\.appspot\.com/, 'http://localhost:8080')
14 | }
15 | }
16 |
17 | const setCancelEvents = () => {
18 | document.querySelector('#img').addEventListener('click', () => {
19 | closeWindow()
20 | })
21 |
22 | const aTags = document.querySelectorAll('a')
23 | for (const a of aTags) {
24 | a.addEventListener('click', () => { closeWindow() })
25 | }
26 | }
27 |
28 | const setGyazoCollectionLink = () => {
29 | const {useGyazo, gyazoHashtag} = readOptions()
30 | const gayzoCollection = document.querySelector('#gyazo-collection')
31 | if (useGyazo === 'yes') {
32 | if (gyazoHashtag.length === 0) return
33 | gayzoCollection.href = `https://gyazo.com/search/${encodeURIComponent(gyazoHashtag)}`
34 | } else {
35 | gayzoCollection.remove()
36 | }
37 | }
38 |
39 | const itemUrl = (url) => {
40 | if (!url) return ''
41 | if (window.dynamicGazo.env === 'production') return url
42 | return url.replace(/^https\:\/\/svgscreenshot\.appspot\.com/, 'http://localhost:8080')
43 | }
44 |
45 | const openN = () => {
46 | document.querySelector('#n').style.display = 'block';
47 | document.querySelector('#y').style.display = 'none';
48 | }
49 |
50 | const openY = () => {
51 | document.querySelector('#y').style.display = 'block';
52 | document.querySelector('#n').style.display = 'none';
53 | }
54 |
55 | window.addEventListener('load', function () {
56 | document.querySelector('#open').href = itemUrl(localStorage.item_url)
57 | const thumbnail = document.querySelector('#img')
58 | const imgUrl = localStorage.item_img.replace(/\/c\/x\//, '/c/xo/') || ''
59 | thumbnail.setAttribute('src', imgUrl)
60 | thumbnail.closest('div.copy-btn').dataset.clipboardText = itemUrl(imgUrl)
61 | var err = localStorage.is_error || 'ようこそ'
62 | if (err !== 'y') {
63 | // キャプチャ失敗
64 | document.querySelector('#msg').innerText = err
65 | openN()
66 | }else {
67 | new Clipboard('.copy-btn')
68 | openY()
69 | }
70 | replaceToDevUrls()
71 | setGyazoCollectionLink()
72 | setCancelEvents()
73 | }, false)
74 |
75 | document.querySelector('#open').addEventListener('click', function () {
76 | clearBadge();
77 | }, false);
78 |
79 | document.querySelector('#login').addEventListener('click', function () {
80 | clearBadge();
81 | }, false);
82 |
83 | // 範囲選択による撮影モード
84 | chrome.tabs.getSelected(null, function (tab) {
85 | chrome.tabs.sendRequest(tab.id, {
86 | event: 'capture-range'
87 | })
88 | })
89 |
90 | const closeWindow = () => {
91 | chrome.tabs.getSelected(null, function (tab) {
92 | chrome.tabs.sendRequest(tab.id, {
93 | event: 'cancel-capture-range'
94 | })
95 | })
96 | window.close()
97 | }
98 |
99 | // register elements
100 | for (const Elem of dynamicGazo.elements) {
101 | window.customElements.define(Elem.is, Elem)
102 | }
103 | })()
104 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "dynamic-gazo",
3 | "version": "1.1.0",
4 | "main": "index.js",
5 | "engine": {
6 | "node": "8.7.0"
7 | },
8 | "repository": "git@github.com:daiz713/DynamicGazo.git",
9 | "author": "daiiz ",
10 | "license": "MIT",
11 | "scripts": {
12 | "build": "./node_modules/.bin/run-s build:**",
13 | "build:babel": "./node_modules/.bin/babel client/ --out-dir chrome/build/ --minified --source-maps false",
14 | "build:browserify": "NODE_ENV=production ./node_modules/.bin/browserify -t [ babelify ] -t envify src/index.js -o chrome/build/bundle.js -v",
15 | "build:package": "zip -r releases/chrome.zip chrome",
16 | "watch": "./node_modules/.bin/run-p watch:**",
17 | "watch:babel": "npm run build:babel -- --watch",
18 | "watch:browserify": "NODE_ENV=production ./node_modules/.bin/watchify -t [ babelify ] -t envify src/index.js -o chrome/build/bundle.js -v",
19 | "watch-dev": "./node_modules/.bin/run-p watch-dev:**",
20 | "watch-dev:babel": "npm run build:babel -- --watch",
21 | "watch-dev:browserify": "NODE_ENV=development ./node_modules/.bin/watchify -t [ babelify ] -t envify src/index.js -o chrome/build/bundle.js -v"
22 | },
23 | "dependencies": {
24 | "@daiiz/dpi-aware-image": "^2.0.7",
25 | "anchors-in-area": "^1.0.21",
26 | "axios": "^0.18.1",
27 | "img-svgize": "^1.0.0",
28 | "png-dpi-reader-writer": "^0.1.0"
29 | },
30 | "devDependencies": {
31 | "@babel/cli": "^7.7.4",
32 | "@babel/plugin-transform-runtime": "^7.7.4",
33 | "babelify": "^7.3.0",
34 | "browserify": "^14.4.0",
35 | "envify": "^4.1.0",
36 | "npm-run-all": "^4.0.2",
37 | "watchify": "^3.11.1"
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/releases/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/daiiz/svg-screenshot/48ceb573b1a4275abd09762c589abf06e4513b0b/releases/.keep
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | const AnchorsInArea = require('anchors-in-area')
2 | const axios = require('axios')
3 | const {convertToByteArray, convertToDataURI, writePngDpi} = require('png-dpi-reader-writer')
4 | const {DpiAwareImage} = require('@daiiz/dpi-aware-image')
5 | const svgize = require('img-svgize')
6 |
7 | window.dynamicGazo = {
8 | elements: [
9 | DpiAwareImage
10 | ],
11 | env: process.env.NODE_ENV,
12 | appOrigin: (process.env.NODE_ENV === 'production') ?
13 | 'https://svgscreenshot.appspot.com' : 'http://localhost:8080'
14 | }
15 |
16 | dynamicGazo.AnchorsInArea = AnchorsInArea
17 | dynamicGazo.svgize = svgize
18 |
19 | dynamicGazo.addPhysChunk = (dataURI, dpr=1) => {
20 | const orgByteArray = convertToByteArray(dataURI)
21 | try {
22 | const genByteArray = writePngDpi(orgByteArray, dpr * 72)
23 | return convertToDataURI(genByteArray)
24 | } catch (err) {
25 | return orgByteArray
26 | }
27 | }
28 |
29 | // upload to SVGScreenshot
30 | dynamicGazo.uploadToDynamicGazo = async ({svg, title, referer, base64Img, devicePixelRatio}) => {
31 | let res
32 | try {
33 | res = await axios.post(`${dynamicGazo.appOrigin}/api/uploadsvg`, {
34 | svg: svg.tagText,
35 | base64png: base64Img,
36 | orgurl: referer,
37 | title,
38 | viewbox: svg.viewBox,
39 | public: 'yes',
40 | dpr: devicePixelRatio
41 | })
42 | } catch (err) {
43 | console.error(err)
44 | }
45 | return res
46 | }
47 |
48 | // upload to Gyazo
49 | dynamicGazo.uploadToGyazo = async (
50 | {scale, image, referer, title, svgScreenshotImageId, hashTag}) => {
51 | const apiEndpoint = `https://upload.gyazo.com/api/upload/easy_auth`
52 | const clientId = 'a9544994509725a7ecceb7381661274751b5b31f006c7788c1d88517c13d1ebe'
53 | if (dynamicGazo.env !== 'production') return
54 |
55 | const dynamicGazoUrl = `${dynamicGazo.appOrigin}/x/${svgScreenshotImageId}`
56 | const formdata = new window.FormData()
57 | formdata.append('client_id', clientId)
58 | formdata.append('image_url', image)
59 | formdata.append('title', title)
60 | formdata.append('referer_url', referer)
61 | formdata.append('scale', scale)
62 | formdata.append('desc', `\n${dynamicGazoUrl}\n${hashTag}`)
63 |
64 | const response = await window.fetch(apiEndpoint, {
65 | method: 'POST',
66 | body: formdata,
67 | credentials: 'include'
68 | })
69 | const _data = await response.json()
70 |
71 | const data = await window.fetch(_data.get_image_url, {
72 | method: 'GET',
73 | credentials: 'include'
74 | })
75 | const gyazoImageId = data.url.split('gyazo.com/')[1]
76 |
77 | chrome.tabs.create({
78 | url: data.url,
79 | active: false
80 | }, null)
81 | return gyazoImageId
82 | }
83 |
--------------------------------------------------------------------------------