pushing preview for import

This commit is contained in:
jdescottes 2013-10-23 23:43:37 +02:00
parent d98ddef0b9
commit 16594369d8
136 changed files with 18765 additions and 0 deletions

3
.gitignore vendored
View File

@ -4,6 +4,9 @@
# nodejs local installs
node_modules
#build artifacts
build
# sublime text stuff (the -project should actually be shared, but then we'd have to share the same disk location)
*.sublime-project
*.sublime-workspace

189
closure_compiled_binary.js Normal file
View File

@ -0,0 +1,189 @@
var Constants={DEFAULT:{HEIGHT:32,WIDTH:32,FPS:12},MODEL_VERSION:1,MAX_HEIGHT:128,MAX_WIDTH:128,PREVIEW_FILM_SIZE:120,DEFAULT_PEN_COLOR:"#000000",TRANSPARENT_COLOR:"TRANSPARENT",SELECTION_TRANSPARENT_COLOR:"rgba(255, 255, 255, 0.6)",TOOL_TARGET_HIGHLIGHT_COLOR:"rgba(255, 255, 255, 0.2)",STATIC:{URL:{SAVE:"http://3.piskel-app.appspot.com/store",GET:"http://3.piskel-app.appspot.com/get"}},APPENGINE:{URL:{SAVE:"save"}},IMAGE_SERVICE_UPLOAD_URL:"http://screenletstore.appspot.com/__/upload",IMAGE_SERVICE_GET_URL:"http://screenletstore.appspot.com/img/",
GRID_STROKE_WIDTH:1,LEFT_BUTTON:"left_button_1",RIGHT_BUTTON:"right_button_2"};var Events={TOOL_SELECTED:"TOOL_SELECTED",TOOL_RELEASED:"TOOL_RELEASED",PRIMARY_COLOR_SELECTED:"PRIMARY_COLOR_SELECTED",PRIMARY_COLOR_UPDATED:"PRIMARY_COLOR_UPDATED",SECONDARY_COLOR_SELECTED:"SECONDARY_COLOR_SELECTED",SECONDARY_COLOR_UPDATED:"SECONDARY_COLOR_UPDATED",LOCALSTORAGE_REQUEST:"LOCALSTORAGE_REQUEST",CANVAS_RIGHT_CLICKED:"CANVAS_RIGHT_CLICKED",REFRESH:"REFRESH",REDRAW_PREVIEWFILM:"REDRAW_PREVIEWFILM",USER_SETTINGS_CHANGED:"USER_SETTINGS_CHANGED",CLOSE_SETTINGS_DRAWER:"CLOSE_SETTINGS_DRAWER",
PISKEL_RESET:"PISKEL_RESET",FRAME_SIZE_CHANGED:"FRAME_SIZE_CHANGED",CURRENT_FRAME_SET:"CURRENT_FRAME_SET",SELECTION_CREATED:"SELECTION_CREATED",SELECTION_MOVE_REQUEST:"SELECTION_MOVE_REQUEST",SELECTION_DISMISSED:"SELECTION_DISMISSED",SHOW_NOTIFICATION:"SHOW_NOTIFICATION",HIDE_NOTIFICATION:"HIDE_NOTIFICATION",UNDO:"UNDO",REDO:"REDO",CUT:"CUT",COPY:"COPY",PASTE:"PASTE"};(function(){$.namespace("pskl").app={init:function(){var a=this.readSizeFromURL_(),d=new pskl.model.Piskel(a.width,a.height),b=new pskl.model.Layer("Layer 1"),a=new pskl.model.Frame(a.width,a.height);b.addFrame(a);d.addLayer(b);this.piskelController=new pskl.controller.PiskelController(d);this.drawingController=new pskl.controller.DrawingController(this.piskelController,$("#drawing-canvas-container"));this.drawingController.init();this.animationController=new pskl.controller.AnimatedPreviewController(this.piskelController,
$("#preview-canvas-container"));this.animationController.init();this.previewsController=new pskl.controller.PreviewFilmController(this.piskelController,$("#preview-list"));this.previewsController.init();this.layersListController=new pskl.controller.LayersListController(this.piskelController);this.layersListController.init();this.settingsController=new pskl.controller.settings.SettingsController(this.piskelController);this.settingsController.init();this.selectionManager=new pskl.selection.SelectionManager(this.piskelController);
this.selectionManager.init();this.historyService=new pskl.service.HistoryService(this.piskelController);this.historyService.init();this.keyboardEventService=new pskl.service.KeyboardEventService;this.keyboardEventService.init();this.notificationController=new pskl.controller.NotificationController;this.notificationController.init();this.localStorageService=new pskl.service.LocalStorageService(this.piskelController);this.localStorageService.init();this.imageUploadService=new pskl.service.ImageUploadService;
this.imageUploadService.init();this.toolController=new pskl.controller.ToolController;this.toolController.init();this.paletteController=new pskl.controller.PaletteController;this.paletteController.init();d=new pskl.rendering.DrawingLoop;d.addCallback(this.render,this);d.start();this.initBootstrapTooltips_();(this.isStaticVersion=!pskl.appEngineToken_)?this.finishInitStatic_():this.finishInitAppEngine_()},finishInitStatic_:function(){var a=this.readFramesheetIdFromURL_();a?($.publish(Events.SHOW_NOTIFICATION,
[{content:"Loading animation with id : ["+a+"]"}]),this.loadFramesheetFromService(a)):this.localStorageService.displayRestoreNotification()},finishInitAppEngine_:function(){if(pskl.framesheetData_&&pskl.framesheetData_.content){var a=pskl.utils.Serializer.createPiskel(pskl.framesheetData_.content);pskl.app.piskelController.setPiskel(a);pskl.app.animationController.setFPS(pskl.framesheetData_.fps)}},initBootstrapTooltips_:function(){$("body").tooltip({selector:"[rel=tooltip]"})},render:function(a){this.drawingController.render(a);
this.animationController.render(a);this.previewsController.render(a)},readSizeFromURL_:function(){var a,d=this.readUrlParameter_("size").split("x");!d||2!=d.length||isNaN(d[0])||isNaN(d[1])?a={height:Constants.DEFAULT.HEIGHT,width:Constants.DEFAULT.WIDTH}:(a=parseInt(d[0],10),d=parseInt(d[1],10),a={height:Math.min(d,Constants.MAX_HEIGHT),width:Math.min(a,Constants.MAX_WIDTH)});return a},readFramesheetIdFromURL_:function(){return this.readUrlParameter_("frameId")},readUrlParameter_:function(a){var d,
b,c=window.location.search.substring(1).split("&");for(d=0;d<c.length;d++)if(b=c[d].split("="),b[0]==a)return window.unescape(b[1]);return""},loadFramesheetFromService:function(a){var d=new XMLHttpRequest;d.open("GET",Constants.STATIC.URL.GET+"?l="+a,!0);d.responseType="text";d.onload=function(d){d=JSON.parse(this.responseText);var a=pskl.utils.Serializer.createPiskel(d.framesheet);pskl.app.piskelController.setPiskel(a);pskl.app.animationController.setFPS(d.fps);$.publish(Events.HIDE_NOTIFICATION)};
d.onerror=function(){$.publish(Events.HIDE_NOTIFICATION)};d.send()},storeSheet:function(a){this.isStaticVersion?this.storeSheetStatic_():this.storeSheetAppEngine_();a&&(a.stopPropagation(),a.preventDefault());return!1},storeSheetStatic_:function(){var a=new XMLHttpRequest,d=new FormData;d.append("framesheet_content",this.piskelController.serialize());d.append("fps_speed",$("#preview-fps").val());a.open("POST",Constants.STATIC.URL.SAVE,!0);a.onload=function(d){if(200==this.status)d=window.location.href.replace(window.location.search,
""),window.location.href=d+"?frameId="+this.responseText;else this.onerror(d)};a.onerror=function(d){$.publish(Events.SHOW_NOTIFICATION,[{content:"Saving failed ("+this.status+")"}])};a.send(d)},storeSheetAppEngine_:function(){var a=new XMLHttpRequest,d=new FormData;d.append("framesheet_content",this.piskelController.serialize());d.append("fps_speed",$("#preview-fps").val());d.append("name",$("#piskel-name").val());d.append("frames",this.piskelController.getFrameCount());d.append("preview",this.getFirstFrameAsPng());
d.append("framesheet",this.getFramesheetAsPng());a.open("POST",Constants.APPENGINE.URL.SAVE,!0);a.onload=function(d){if(200==this.status)$.publish(Events.SHOW_NOTIFICATION,[{content:"Successfully saved !"}]);else this.onerror(d)};a.onerror=function(d){$.publish(Events.SHOW_NOTIFICATION,[{content:"Saving failed ("+this.status+")"}])};a.send(d)},getFirstFrameAsPng:function(){var a=this.piskelController.getFrameAt(0),a=new pskl.rendering.CanvasRenderer(a,1);a.drawTransparentAs("rgba(0,0,0,0)");return a.render().canvas.toDataURL("image/png")},
getFramesheetAsPng:function(){return(new pskl.rendering.SpritesheetRenderer(this.piskelController)).render().toDataURL("image/png")},uploadAsSpritesheetPNG:function(){var a=this.getFramesheetAsPng();this.imageUploadService.upload(a,this.openWindow.bind(this))},openWindow:function(a){var d=["dialog=yes,scrollbars=no,status=no","width="+this.piskelController.getWidth()*this.piskelController.getFrameCount(),"height="+this.piskelController.getHeight()].join();window.open(a,"piskel-export",d)}}})();(function(){var a=$.namespace("pskl.controller");a.AnimatedPreviewController=function(d,a,c){this.piskelController=d;this.container=a;this.currentIndex=this.elapsedTime=0;this.setFPS(Constants.DEFAULT.FPS);d={dpi:this.calculateDPI_()};this.renderer=new pskl.rendering.FrameRenderer(this.container,d);$.subscribe(Events.FRAME_SIZE_CHANGED,this.updateDPI_.bind(this))};a.AnimatedPreviewController.prototype.init=function(){$("#preview-fps")[0].addEventListener("change",this.onFPSSliderChange.bind(this))};
a.AnimatedPreviewController.prototype.onFPSSliderChange=function(d){this.setFPS(parseInt($("#preview-fps")[0].value,10))};a.AnimatedPreviewController.prototype.setFPS=function(d){this.fps=d;$("#preview-fps").val(this.fps);$("#display-fps").html(this.fps+" FPS")};a.AnimatedPreviewController.prototype.getFPS=function(){return this.fps};a.AnimatedPreviewController.prototype.render=function(d){this.elapsedTime+=d;d=Math.floor(this.elapsedTime/(1E3/this.fps));d!=this.currentIndex&&(this.currentIndex=d,
this.piskelController.hasFrameAt(this.currentIndex)||(this.elapsedTime=this.currentIndex=0),this.renderer.render(this.piskelController.getFrameAt(this.currentIndex)))};a.AnimatedPreviewController.prototype.calculateDPI_=function(){var d=this.piskelController.getCurrentFrame().getHeight(),a=this.piskelController.getCurrentFrame().getWidth();return pskl.PixelUtils.calculateDPI(200,200,d,a)};a.AnimatedPreviewController.prototype.updateDPI_=function(){this.dpi=this.calculateDPI_();this.renderer.setDPI(this.dpi)}})();(function(){var a=$.namespace("pskl.controller");a.DrawingController=function(d,a){this.piskelController=d;this.overlayFrame=pskl.model.Frame.createEmptyFromFrame(d.getCurrentFrame());this.container=a;this.dpi=this.calculateDPI_();var c={dpi:this.dpi,supportGridRendering:!0};this.overlayRenderer=new pskl.rendering.FrameRenderer(this.container,c,["canvas-overlay"]);this.renderer=new pskl.rendering.FrameRenderer(this.container,c,["drawing-canvas"]);this.layersBelowRenderer=new pskl.rendering.FrameRenderer(this.container,
c,["layers-canvas","layers-below-canvas"]);this.layersAboveRenderer=new pskl.rendering.FrameRenderer(this.container,c,["layers-canvas","layers-above-canvas"]);this.isRightClicked=this.isClicked=!1;this.previousMousemoveTime=0;this.currentToolBehavior=null;this.primaryColor=Constants.DEFAULT_PEN_COLOR;this.secondaryColor=Constants.TRANSPARENT_COLOR};a.DrawingController.prototype.init=function(){this.initMouseBehavior();$.subscribe(Events.TOOL_SELECTED,$.proxy(function(d,a){console.log("Tool selected: ",
a);this.currentToolBehavior=a;this.overlayFrame.clear()},this));$.subscribe(Events.PRIMARY_COLOR_SELECTED,$.proxy(function(d,a){console.log("Primary color selected: ",a);this.primaryColor=a;$.publish(Events.PRIMARY_COLOR_UPDATED,[a])},this));$.subscribe(Events.SECONDARY_COLOR_SELECTED,$.proxy(function(d,a){console.log("Secondary color selected: ",a);this.secondaryColor=a;$.publish(Events.SECONDARY_COLOR_UPDATED,[a])},this));$(window).resize($.proxy(this.startDPIUpdateTimer_,this));$.subscribe(Events.USER_SETTINGS_CHANGED,
$.proxy(this.onUserSettingsChange_,this));$.subscribe(Events.FRAME_SIZE_CHANGED,$.proxy(this.updateDPI_,this));this.updateDPI_()};a.DrawingController.prototype.initMouseBehavior=function(){var d=$("body");this.container.mousedown($.proxy(this.onMousedown_,this));this.container.mousemove($.proxy(this.onMousemove_,this));d.mouseup($.proxy(this.onMouseup_,this));d.contextmenu(this.onCanvasContextMenu_)};a.DrawingController.prototype.startDPIUpdateTimer_=function(){this.dpiUpdateTimer&&window.clearInterval(this.dpiUpdateTimer);
this.dpiUpdateTimer=window.setTimeout($.proxy(this.updateDPI_,this),200)};a.DrawingController.prototype.onUserSettingsChange_=function(d,a,c){a==pskl.UserSettings.SHOW_GRID&&this.updateDPI_()};a.DrawingController.prototype.onMousedown_=function(d){this.isClicked=!0;2==d.button&&(this.isRightClicked=!0,$.publish(Events.CANVAS_RIGHT_CLICKED));var a=this.getSpriteCoordinates(d);this.currentToolBehavior.applyToolAt(a.col,a.row,this.getCurrentColor_(),this.piskelController.getCurrentFrame(),this.overlayFrame,
this.wrapEvtInfo_(d));$.publish(Events.LOCALSTORAGE_REQUEST)};a.DrawingController.prototype.onMousemove_=function(d){var a=(new Date).getTime();if(40<a-this.previousMousemoveTime){var c=this.getSpriteCoordinates(d);this.isClicked?(this.currentToolBehavior.moveToolAt(c.col,c.row,this.getCurrentColor_(),this.piskelController.getCurrentFrame(),this.overlayFrame,this.wrapEvtInfo_(d)),$.publish(Events.LOCALSTORAGE_REQUEST)):this.currentToolBehavior.moveUnactiveToolAt(c.col,c.row,this.getCurrentColor_(),
this.piskelController.getCurrentFrame(),this.overlayFrame,this.wrapEvtInfo_(d));this.previousMousemoveTime=a}};a.DrawingController.prototype.onMouseup_=function(d){if(this.isClicked||this.isRightClicked){this.isRightClicked=this.isClicked=!1;var a=this.getSpriteCoordinates(d);this.currentToolBehavior.releaseToolAt(a.col,a.row,this.getCurrentColor_(),this.piskelController.getCurrentFrame(),this.overlayFrame,this.wrapEvtInfo_(d));$.publish(Events.TOOL_RELEASED)}};a.DrawingController.prototype.wrapEvtInfo_=
function(d){var a={};0===d.button?a.button=Constants.LEFT_BUTTON:2==d.button&&(a.button=Constants.RIGHT_BUTTON);return a};a.DrawingController.prototype.getRelativeCoordinates=function(d,a){var c=this.container.offset();return{x:d-c.left,y:a-c.top}};a.DrawingController.prototype.getSpriteCoordinates=function(d){d=this.getRelativeCoordinates(d.clientX,d.clientY);return this.renderer.convertPixelCoordinatesIntoSpriteCoordinate(d)};a.DrawingController.prototype.getCurrentColor_=function(){return this.isRightClicked?
this.secondaryColor:this.primaryColor};a.DrawingController.prototype.onCanvasContextMenu_=function(d){if($(d.target).closest("#drawing-canvas-container").length)return d.preventDefault(),d.stopPropagation(),d.cancelBubble=!0,!1};a.DrawingController.prototype.render=function(){this.renderLayers();this.renderFrame();this.renderOverlay()};a.DrawingController.prototype.renderFrame=function(){var d=this.piskelController.getCurrentFrame(),a=this.dpi+"-"+d.serialize();this.serializedFrame!=a&&(d.isSameSize(this.overlayFrame)||
(this.overlayFrame=pskl.model.Frame.createEmptyFromFrame(d)),this.serializedFrame=a,this.renderer.render(d))};a.DrawingController.prototype.renderOverlay=function(){var d=this.dpi+"-"+this.overlayFrame.serialize();this.serializedOverlay!=d&&(this.serializedOverlay=d,this.overlayRenderer.render(this.overlayFrame))};a.DrawingController.prototype.renderLayers=function(){var d=this.piskelController.getLayers(),a=this.piskelController.currentFrameIndex,c=this.piskelController.currentLayerIndex,f=[this.dpi,
a,c,d.length].join("-");if(this.serializedLayerFrame!=f){this.layersAboveRenderer.clear();this.layersBelowRenderer.clear();var e=d.slice(0,c),e=this.getFrameForLayersAt_(a,e);this.layersBelowRenderer.render(e);c+1<d.length&&(d=d.slice(c+1,d.length),a=this.getFrameForLayersAt_(a,d),this.layersAboveRenderer.render(a));this.serializedLayerFrame=f}};a.DrawingController.prototype.getFrameForLayersAt_=function(a,b){var c=b.map(function(c){return c.getFrameAt(a)});return pskl.utils.FrameUtils.merge(c)};
a.DrawingController.prototype.calculateDPI_=function(){var a=$("#main-wrapper").height(),b=$(".left-column").outerWidth(!0),c=$(".right-column").outerWidth(!0),b=$("#main-wrapper").width()-b-c,c=this.piskelController.getCurrentFrame().getHeight(),f=this.piskelController.getCurrentFrame().getWidth();pskl.UserSettings.get(pskl.UserSettings.SHOW_GRID)&&(b-=f*Constants.GRID_STROKE_WIDTH,a-=c*Constants.GRID_STROKE_WIDTH);return pskl.PixelUtils.calculateDPI(a,b,c,f)};a.DrawingController.prototype.updateDPI_=
function(){this.dpi=this.calculateDPI_();this.overlayRenderer.setDPI(this.dpi);this.renderer.setDPI(this.dpi);this.layersAboveRenderer.setDPI(this.dpi);this.layersBelowRenderer.setDPI(this.dpi);var a=this.piskelController.getCurrentFrame().getHeight(),b=a*this.dpi;pskl.UserSettings.get(pskl.UserSettings.SHOW_GRID)&&(b+=Constants.GRID_STROKE_WIDTH*a);a=Math.floor(($("#main-wrapper").height()-b)/2);$("#column-wrapper").css({top:a+"px",height:b+"px"})}})();(function(){var a=$.namespace("pskl.controller");a.LayersListController=function(a){this.piskelController=a};a.LayersListController.prototype.init=function(){this.layerItemTemplate_=pskl.utils.Template.get("layer-item-template");this.rootEl=document.querySelectorAll(".layers-list-container")[0];this.layersListEl=document.querySelectorAll(".layers-list")[0];this.rootEl.addEventListener("click",this.onClick_.bind(this));$.subscribe(Events.PISKEL_RESET,this.renderLayerList_.bind(this));this.renderLayerList_()};
a.LayersListController.prototype.renderLayerList_=function(){this.layersListEl.innerHTML="";this.piskelController.getLayers().forEach(this.addLayerItem.bind(this))};a.LayersListController.prototype.addLayerItem=function(a){var b=pskl.utils.Template.replace(this.layerItemTemplate_,{layername:a.getName()}),b=pskl.utils.Template.createFromHTML(b);this.piskelController.getCurrentLayer()===a&&b.classList.add("current-layer-item");this.layersListEl.insertBefore(b,this.layersListEl.firstChild)};a.LayersListController.prototype.onClick_=
function(a){a=a.target||a.srcElement;if("BUTTON"==a.nodeName)this.onButtonClick_(a);else"LI"==a.nodeName&&(a=a.getAttribute("data-layer-name"),this.piskelController.selectLayerByName(a))};a.LayersListController.prototype.onButtonClick_=function(a){a=a.getAttribute("data-action");"up"==a?this.piskelController.moveLayerUp():"down"==a?this.piskelController.moveLayerDown():"add"==a?this.piskelController.createLayer():"delete"==a&&this.piskelController.removeCurrentLayer()}})();(function(){var a=$.namespace("pskl.controller");a.NotificationController=function(){};a.NotificationController.prototype.init=function(){$.subscribe(Events.SHOW_NOTIFICATION,$.proxy(this.displayMessage_,this));$.subscribe(Events.HIDE_NOTIFICATION,$.proxy(this.removeMessage_,this))};a.NotificationController.prototype.displayMessage_=function(a,b){var c=document.createElement("div");c.id="user-message";c.className="user-message";c.innerHTML=b.content;c.innerHTML+="<div title='Close message' class='close'>x</div>";
document.body.appendChild(c);$(c).find(".close").click($.proxy(this.removeMessage_,this));b.behavior&&b.behavior(c)};a.NotificationController.prototype.removeMessage_=function(a){a=$("#user-message");a.length&&a.remove()}})();(function(){var a=$.namespace("pskl.controller");a.PaletteController=function(){};a.PaletteController.prototype.init=function(){$(".palette-color[data-color=TRANSPARENT]").mouseup($.proxy(this.onPaletteColorClick_,this));$.subscribe(Events.PRIMARY_COLOR_UPDATED,$.proxy(function(a,d){this.updateColorPicker_(d,$("#color-picker"))},this));$.subscribe(Events.SECONDARY_COLOR_UPDATED,$.proxy(function(a,d){this.updateColorPicker_(d,$("#secondary-color-picker"))},this));var a=$("#color-picker");a.val(Constants.DEFAULT_PEN_COLOR);
a.change({isPrimary:!0},$.proxy(this.onPickerChange_,this));a=$("#secondary-color-picker");a.val(Constants.TRANSPARENT_COLOR);a.change({isPrimary:!1},$.proxy(this.onPickerChange_,this));window.jscolor.install()};a.PaletteController.prototype.onPickerChange_=function(a,b){var c=$(a.target);a.data.isPrimary?$.publish(Events.PRIMARY_COLOR_SELECTED,[c.val()]):$.publish(Events.SECONDARY_COLOR_SELECTED,[c.val()])};a.PaletteController.prototype.onPaletteColorClick_=function(a){var b=$(a.target).data("color"),
c=3==a.which;1==a.which?$.publish(Events.PRIMARY_COLOR_SELECTED,[b]):c&&$.publish(Events.SECONDARY_COLOR_SELECTED,[b])};a.PaletteController.prototype.updateColorPicker_=function(a,b){a==Constants.TRANSPARENT_COLOR?(b[0].color.fromString("#fff"),b.val(Constants.TRANSPARENT_COLOR)):b[0].color.fromString(a)}})();(function(){var a=$.namespace("pskl.controller");a.PiskelController=function(a){if(a)this.setPiskel(a);else throw"A piskel instance is mandatory for instanciating PiskelController";};a.PiskelController.prototype.setPiskel=function(a){this.piskel=a;this.currentFrameIndex=this.currentLayerIndex=0;this.layerIdCounter=1;$.publish(Events.PISKEL_RESET);$.publish(Events.FRAME_SIZE_CHANGED)};a.PiskelController.prototype.getHeight=function(){return this.piskel.getHeight()};a.PiskelController.prototype.getWidth=
function(){return this.piskel.getWidth()};a.PiskelController.prototype.getFPS=function(){return pskl.app.animationController.getFPS()};a.PiskelController.prototype.getLayers=function(){return this.piskel.getLayers()};a.PiskelController.prototype.getCurrentLayer=function(){return this.piskel.getLayerAt(this.currentLayerIndex)};a.PiskelController.prototype.getCurrentFrame=function(){return this.getCurrentLayer().getFrameAt(this.currentFrameIndex)};a.PiskelController.prototype.getFrameAt=function(a){var b=
this.getLayers().map(function(c){return c.getFrameAt(a)});return pskl.utils.FrameUtils.merge(b)};a.PiskelController.prototype.hasFrameAt=function(a){return!!this.getCurrentLayer().getFrameAt(a)};a.PiskelController.prototype.getFrameByIndex=a.PiskelController.prototype.getMergedFrameAt;a.PiskelController.prototype.addEmptyFrame=function(){this.getLayers().forEach(function(a){a.addFrame(this.createEmptyFrame_())}.bind(this))};a.PiskelController.prototype.createEmptyFrame_=function(){var a=this.piskel.getWidth(),
b=this.piskel.getHeight();return new pskl.model.Frame(a,b)};a.PiskelController.prototype.removeFrameAt=function(a){this.getLayers().forEach(function(b){b.removeFrameAt(a)});this.currentFrameIndex>=a&&this.setCurrentFrameIndex(this.currentFrameIndex-1);$.publish(Events.PISKEL_RESET)};a.PiskelController.prototype.duplicateFrameAt=function(a){this.getLayers().forEach(function(b){b.duplicateFrameAt(a)})};a.PiskelController.prototype.moveFrame=function(a,b){this.getLayers().forEach(function(c){c.moveFrame(a,
b)})};a.PiskelController.prototype.getFrameCount=function(){return this.piskel.getLayerAt(0).length()};a.PiskelController.prototype.setCurrentFrameIndex=function(a){this.currentFrameIndex=a;$.publish(Events.PISKEL_RESET)};a.PiskelController.prototype.setCurrentLayerIndex=function(a){this.currentLayerIndex=a;$.publish(Events.PISKEL_RESET)};a.PiskelController.prototype.selectLayer=function(a){a=this.getLayers().indexOf(a);-1!=a&&this.setCurrentLayerIndex(a)};a.PiskelController.prototype.selectLayerByName=
function(a){this.hasLayerForName_(a)&&(a=this.piskel.getLayersByName(a)[0],this.selectLayer(a))};a.PiskelController.prototype.generateLayerName_=function(){for(var a="Layer "+this.layerIdCounter;this.hasLayerForName_(a);)this.layerIdCounter++,a="Layer "+this.layerIdCounter;return a};a.PiskelController.prototype.createLayer=function(a){a||(a=this.generateLayerName_());if(this.hasLayerForName_(a))throw"Layer name should be unique";a=new pskl.model.Layer(a);for(var b=0;b<this.getFrameCount();b++)a.addFrame(this.createEmptyFrame_());
this.piskel.addLayer(a);this.setCurrentLayerIndex(this.piskel.getLayers().length-1)};a.PiskelController.prototype.hasLayerForName_=function(a){return 0<this.piskel.getLayersByName(a).length};a.PiskelController.prototype.moveLayerUp=function(){var a=this.getCurrentLayer();this.piskel.moveLayerUp(a);this.selectLayer(a)};a.PiskelController.prototype.moveLayerDown=function(){var a=this.getCurrentLayer();this.piskel.moveLayerDown(a);this.selectLayer(a)};a.PiskelController.prototype.removeCurrentLayer=
function(){if(1<this.getLayers().length){var a=this.getCurrentLayer();this.piskel.removeLayer(a);this.setCurrentLayerIndex(0)}};a.PiskelController.prototype.serialize=function(){return pskl.utils.Serializer.serializePiskel(this.piskel)};a.PiskelController.prototype.load=function(a){this.deserialize(JSON.stringify(a))}})();(function(){var a=$.namespace("pskl.controller");a.PreviewFilmController=function(a,b,c){this.piskelController=a;this.container=b;this.dpi=this.calculateDPI_();this.redrawFlag=!0};a.PreviewFilmController.prototype.init=function(){$.subscribe(Events.TOOL_RELEASED,this.flagForRedraw_.bind(this));$.subscribe(Events.PISKEL_RESET,this.flagForRedraw_.bind(this));$.subscribe(Events.PISKEL_RESET,this.refreshDPI_.bind(this));$("#preview-list-scroller").scroll(this.updateScrollerOverflows.bind(this));this.updateScrollerOverflows()};
a.PreviewFilmController.prototype.addFrame=function(){this.piskelController.addEmptyFrame();this.piskelController.setCurrentFrameIndex(this.piskelController.getFrameCount()-1);this.updateScrollerOverflows()};a.PreviewFilmController.prototype.flagForRedraw_=function(){this.redrawFlag=!0};a.PreviewFilmController.prototype.refreshDPI_=function(){this.dpi=this.calculateDPI_()};a.PreviewFilmController.prototype.render=function(){this.redrawFlag&&(this.createPreviews_(),this.redrawFlag=!1)};a.PreviewFilmController.prototype.updateScrollerOverflows=
function(){var a=$("#preview-list-scroller"),b=a.height(),c=a.scrollTop(),f=$("#preview-list").height(),e=$(".top-overflow").height(),g=a=!1;b<f&&(c>e&&(a=!0),f-c-b>e&&(g=!0));b=$("#preview-list-wrapper");b.toggleClass("top-overflow-visible",a);b.toggleClass("bottom-overflow-visible",g)};a.PreviewFilmController.prototype.createPreviews_=function(){this.container.html("");$(".tooltip").remove();for(var a=this.piskelController.getFrameCount(),b=0;b<a;b++)this.container.append(this.createPreviewTile_(b));
b=document.createElement("div");b.id="add-frame-action";b.className="add-frame-action";b.innerHTML="<p class='label'>Add new frame</p>";this.container.append(b);$(b).click(this.addFrame.bind(this));1<a&&this.initDragndropBehavior_();this.updateScrollerOverflows()};a.PreviewFilmController.prototype.initDragndropBehavior_=function(){$("#preview-list").sortable({placeholder:"preview-tile-drop-proxy",update:$.proxy(this.onUpdate_,this),items:".preview-tile"});$("#preview-list").disableSelection()};a.PreviewFilmController.prototype.onUpdate_=
function(a,b){var c=parseInt(b.item.data("tile-number"),10),f=$(".preview-tile").index(b.item);this.piskelController.moveFrame(c,f);this.piskelController.setCurrentFrameIndex(f);$.publish(Events.LOCALSTORAGE_REQUEST)};a.PreviewFilmController.prototype.createPreviewTile_=function(a){var b=this.piskelController.getCurrentLayer().getFrameAt(a),c=document.createElement("li"),f="preview-tile";c.setAttribute("data-tile-number",a);this.piskelController.getCurrentFrame()==b&&(f+=" selected");c.className=
f;f=document.createElement("div");f.className="canvas-container";var e=document.createElement("div");e.className="canvas-background";f.appendChild(e);c.addEventListener("click",this.onPreviewClick_.bind(this,a));e=document.createElement("button");e.setAttribute("rel","tooltip");e.setAttribute("data-placement","right");e.setAttribute("title","Duplicate this frame");e.className="tile-overlay duplicate-frame-action";c.appendChild(e);e.addEventListener("click",this.onAddButtonClick_.bind(this,a));e={dpi:this.dpi};
(new pskl.rendering.FrameRenderer($(f),e,["tile-view"])).render(b);c.appendChild(f);if(0<a||1<this.piskelController.getFrameCount())b=document.createElement("button"),b.setAttribute("rel","tooltip"),b.setAttribute("data-placement","right"),b.setAttribute("title","Delete this frame"),b.className="tile-overlay delete-frame-action",b.addEventListener("click",this.onDeleteButtonClick_.bind(this,a)),c.appendChild(b),b=document.createElement("div"),b.className="tile-overlay dnd-action",c.appendChild(b);
b=document.createElement("div");b.className="tile-overlay tile-count";b.innerHTML=a;c.appendChild(b);return c};a.PreviewFilmController.prototype.onPreviewClick_=function(a,b){b.target.classList.contains("tile-overlay")||this.piskelController.setCurrentFrameIndex(a)};a.PreviewFilmController.prototype.onDeleteButtonClick_=function(a,b){this.piskelController.removeFrameAt(a);$.publish(Events.LOCALSTORAGE_REQUEST);this.updateScrollerOverflows()};a.PreviewFilmController.prototype.onAddButtonClick_=function(a,
b){this.piskelController.duplicateFrameAt(a);$.publish(Events.LOCALSTORAGE_REQUEST);this.piskelController.setCurrentFrameIndex(a+1);this.updateScrollerOverflows()};a.PreviewFilmController.prototype.calculateDPI_=function(){var a=this.piskelController.getCurrentFrame(),b=a.getHeight(),a=a.getWidth(),c=Math.max(a,b);return pskl.PixelUtils.calculateDPI(Constants.PREVIEW_FILM_SIZE*b/c,Constants.PREVIEW_FILM_SIZE*a/c,b,a)||1}})();(function(){var a=$.namespace("pskl.controller");a.ToolController=function(){this.toolInstances={simplePen:new pskl.drawingtools.SimplePen,verticalMirrorPen:new pskl.drawingtools.VerticalMirrorPen,eraser:new pskl.drawingtools.Eraser,paintBucket:new pskl.drawingtools.PaintBucket,stroke:new pskl.drawingtools.Stroke,rectangle:new pskl.drawingtools.Rectangle,circle:new pskl.drawingtools.Circle,move:new pskl.drawingtools.Move,rectangleSelect:new pskl.drawingtools.RectangleSelect,shapeSelect:new pskl.drawingtools.ShapeSelect,
colorPicker:new pskl.drawingtools.ColorPicker};this.previousSelectedTool=this.currentSelectedTool=this.toolInstances.simplePen};a.ToolController.prototype.init=function(){this.createToolMarkup_();this.selectTool_(this.toolInstances.simplePen);$("#tool-section").click($.proxy(this.onToolIconClicked_,this))};a.ToolController.prototype.activateToolOnStage_=function(a){var b=$("body"),c=b.data("selected-tool-class");c&&b.removeClass(c);b.addClass(a.toolId);b.data("selected-tool-class",a.toolId)};a.ToolController.prototype.selectTool_=
function(a){console.log("Selecting Tool:",this.currentSelectedTool);this.currentSelectedTool=a;this.activateToolOnStage_(this.currentSelectedTool);$.publish(Events.TOOL_SELECTED,[a])};a.ToolController.prototype.onToolIconClicked_=function(a){a=$(a.target).closest(".tool-icon");if(a.length){var b=a.data().toolId;if(b=this.getToolById_(b))this.selectTool_(b),$("#tool-section .tool-icon.selected").removeClass("selected"),a.addClass("selected")}};a.ToolController.prototype.getToolById_=function(a){for(var b in this.toolInstances)if(this.toolInstances[b].toolId==
a)return this.toolInstances[b];return null};a.ToolController.prototype.createToolMarkup_=function(){var a,b="",c,f;for(f in this.toolInstances)a=this.toolInstances[f],c=a.toolId,this.currentSelectedTool==a&&(c+=" selected"),b+='<li rel="tooltip" data-placement="right" class="tool-icon '+c+'" data-tool-id="'+a.toolId+'" title="'+a.helpText+'"></li>';$("#tools-container").html(b)}})();(function(){var a=$.namespace("pskl.controller.settings");a.ApplicationSettingsController=function(){};a.ApplicationSettingsController.prototype.init=function(){var a=pskl.UserSettings.get(pskl.UserSettings.CANVAS_BACKGROUND);$("#background-picker-wrapper").find(".background-picker[data-background-class="+a+"]").addClass("selected");a=pskl.UserSettings.get(pskl.UserSettings.SHOW_GRID);$("#show-grid").prop("checked",a);$("#show-grid").change($.proxy(function(a){a=$("#show-grid").prop("checked");pskl.UserSettings.set(pskl.UserSettings.SHOW_GRID,
a)},this));$("#background-picker-wrapper").click(function(a){a=$(a.target).closest(".background-picker");if(a.length){var d=a.data("background-class");pskl.UserSettings.set(pskl.UserSettings.CANVAS_BACKGROUND,d);$(".background-picker").removeClass("selected");a.addClass("selected")}})}})();(function(){var a=$.namespace("pskl.controller.settings");a.GifExportController=function(a){this.piskelController=a};a.GifExportController.RESOLUTIONS=[{dpi:1},{dpi:5},{dpi:10,"default":!0},{dpi:20}];a.GifExportController.prototype.init=function(){this.radioTemplate_=pskl.utils.Template.get("gif-export-radio-template");this.previewContainerEl=document.querySelectorAll(".gif-export-preview")[0];this.radioGroupEl=document.querySelectorAll(".gif-export-radio-group")[0];this.uploadForm=$("[name=gif-export-upload-form]");
this.uploadForm.submit(this.onUploadFormSubmit_.bind(this));this.createRadioElements_()};a.GifExportController.prototype.onUploadFormSubmit_=function(a){a.originalEvent.preventDefault();a=this.getSelectedDpi_();var b=this.piskelController.getFPS();this.renderAsImageDataAnimatedGIF(a,b,this.onGifRenderingCompleted_.bind(this))};a.GifExportController.prototype.onGifRenderingCompleted_=function(a){this.updatePreview_(a);this.previewContainerEl.classList.add("preview-upload-ongoing");pskl.app.imageUploadService.upload(a,
this.onImageUploadCompleted_.bind(this))};a.GifExportController.prototype.onImageUploadCompleted_=function(a){this.updatePreview_(a);this.previewContainerEl.classList.remove("preview-upload-ongoing")};a.GifExportController.prototype.updatePreview_=function(a){this.previewContainerEl.innerHTML="<div><img style='max-width:240px;' src='"+a+"'/></div>"};a.GifExportController.prototype.getSelectedDpi_=function(){var a=this.uploadForm.get(0).querySelectorAll("[name=gif-dpi]"),a=Array.prototype.slice.call(a,
0).filter(function(a){return!!a.checked});if(1==a.length)return a[0].value;throw"Unexpected error when retrieving selected dpi";};a.GifExportController.prototype.createRadioElements_=function(){for(var d=a.GifExportController.RESOLUTIONS,b=0;b<d.length;b++){var c=this.createRadioForResolution_(d[b]);this.radioGroupEl.appendChild(c)}};a.GifExportController.prototype.createRadioForResolution_=function(a){var b=a.dpi,c=b*this.piskelController.getWidth()+"x"+b*this.piskelController.getHeight(),b=pskl.utils.Template.replace(this.radioTemplate_,
{value:b,label:c}),b=pskl.utils.Template.createFromHTML(b);a["default"]&&b.getElementsByTagName("input")[0].setAttribute("checked","checked");return b};a.GifExportController.prototype.blobToBase64_=function(a,b){var c=new FileReader;c.onload=function(){b(c.result)};c.readAsDataURL(a)};a.GifExportController.prototype.renderAsImageDataAnimatedGIF=function(a,b,c){for(var f=new window.GIF({workers:2,quality:10,width:this.piskelController.getWidth()*a,height:this.piskelController.getHeight()*a}),e=0;e<
this.piskelController.getFrameCount();e++){var g=this.piskelController.getFrameAt(e),g=new pskl.rendering.CanvasRenderer(g,a);f.addFrame(g.render(),{delay:1E3/b})}f.on("finished",function(a){this.blobToBase64_(a,c)}.bind(this));f.render()}})();(function(){var a=$.namespace("pskl.controller.settings");a.ImportController=function(a){this.piskelController=a;this.importedImage_=null};a.ImportController.prototype.init=function(){this.importForm=$("[name=import-form]");this.hiddenFileInput=$("[name=file-upload-input]");this.fileInputButton=$(".file-input-button");this.fileInputStatus=$(".file-input-status");this.fileInputStatus.html("No file selected ...");this.importPreview=$(".import-section-preview");this.resizeWidth=$("[name=resize-width]");
this.resizeHeight=$("[name=resize-height]");this.smoothResize=$("[name=smooth-resize-checkbox]");this.submitButton=$("[name=import-submit]");this.importForm.submit(this.onImportFormSubmit_.bind(this));this.hiddenFileInput.change(this.onFileUploadChange_.bind(this));this.fileInputButton.click(this.onFileInputClick_.bind(this));this.resizeWidth.keyup(this.onResizeInputKeyUp_.bind(this,"width"));this.resizeHeight.keyup(this.onResizeInputKeyUp_.bind(this,"height"))};a.ImportController.prototype.reset_=
function(){this.importForm.get(0).reset();this.fileInputStatus.html("No file selected ...");$.publish(Events.CLOSE_SETTINGS_DRAWER)};a.ImportController.prototype.onResizeInputKeyUp_=function(a,b){this.importedImage_&&this.synchronizeResizeFields_(b.target.value,a)};a.ImportController.prototype.synchronizeResizeFields_=function(a,b){a=parseInt(a,10);isNaN(a)&&(a=0);var c=this.importedImage_.height,f=this.importedImage_.width;"width"===b?this.resizeHeight.val(Math.round(a*c/f)):this.resizeWidth.val(Math.round(a*
f/c))};a.ImportController.prototype.onImportFormSubmit_=function(a){a.originalEvent.preventDefault();this.importImageToPiskel_()};a.ImportController.prototype.onFileUploadChange_=function(a){this.importFromFile_()};a.ImportController.prototype.onFileInputClick_=function(a){this.hiddenFileInput.click()};a.ImportController.prototype.importFromFile_=function(){var a=this.hiddenFileInput.get(0).files;if(1==a.length)if(a=a[0],this.isImage_(a))this.readImageFile_(a),this.enableDisabledSections_();else throw this.reset_(),
"File is not an image : "+a.type;};a.ImportController.prototype.enableDisabledSections_=function(){this.resizeWidth.removeAttr("disabled");this.resizeHeight.removeAttr("disabled");this.smoothResize.removeAttr("disabled");this.submitButton.removeAttr("disabled");this.fileInputButton.removeClass("button-primary");this.fileInputButton.blur();$(".import-section-disabled").removeClass("import-section-disabled")};a.ImportController.prototype.readImageFile_=function(a){pskl.utils.FileUtils.readFile(a,this.processImageSource_.bind(this))};
a.ImportController.prototype.processImageSource_=function(a){this.importedImage_=new Image;this.importedImage_.onload=this.onImageLoaded_.bind(this);this.importedImage_.src=a};a.ImportController.prototype.onImageLoaded_=function(a){a=this.importedImage_.width;var b=this.importedImage_.height,c=this.hiddenFileInput.val(),c=this.extractFileNameFromPath_(c);this.fileInputStatus.html(c);this.resizeWidth.val(a);this.resizeHeight.val(b);this.importPreview.width("auto");this.importPreview.append(this.createImagePreview_())};
a.ImportController.prototype.createImagePreview_=function(){var a=document.createElement("IMG");a.src=this.importedImage_.src;a.setAttribute("height",60);return a};a.ImportController.prototype.extractFileNameFromPath_=function(a){var b=[],b=-1!==a.indexOf("/")?a.split("/"):-1!==a.indexOf("\\")?a.split("\\"):[a];return b[b.length-1]};a.ImportController.prototype.importImageToPiskel_=function(){if(this.importedImage_&&window.confirm("You are about to create a new Piskel, unsaved changes will be lost.")){var a=
this.resizeWidth.val(),b=this.resizeHeight.val(),c=!!this.smoothResize.prop("checked"),a=pskl.utils.ImageResizer.resize(this.importedImage_,a,b,c),a=pskl.utils.FrameUtils.createFromImage(a),a=pskl.utils.Serializer.createPiskel([a]);pskl.app.piskelController.setPiskel(a);pskl.app.animationController.setFPS(Constants.DEFAULT.FPS);this.reset_()}};a.ImportController.prototype.isImage_=function(a){return 0===a.type.indexOf("image")}})();(function(){var a=$.namespace("pskl.controller.settings"),d={user:{template:"templates/settings/application.html",controller:a.ApplicationSettingsController},gif:{template:"templates/settings/export-gif.html",controller:a.GifExportController},"import":{template:"templates/settings/import.html",controller:a.ImportController}};a.SettingsController=function(a){this.piskelController=a;this.drawerContainer=document.getElementById("drawer-container");this.settingsContainer=$("[data-pskl-controller=settings]");
this.expanded=!1;this.currentSetting=null};a.SettingsController.prototype.init=function(){$("[data-setting]").click(function(a){a=a.originalEvent.currentTarget.getAttribute("data-setting");this.currentSetting!=a?this.loadSetting(a):this.closeDrawer()}.bind(this));$("body").click(function(a){a=$.contains(this.settingsContainer.get(0),a.target);this.expanded&&!a&&this.closeDrawer()}.bind(this));$.subscribe(Events.CLOSE_SETTINGS_DRAWER,this.closeDrawer.bind(this))};a.SettingsController.prototype.loadSetting=
function(a){this.drawerContainer.innerHTML=pskl.utils.Template.get(d[a].template);(new d[a].controller(this.piskelController)).init();this.settingsContainer.addClass("expanded");$(".has-expanded-drawer").removeClass("has-expanded-drawer");$("[data-setting="+a+"]").addClass("has-expanded-drawer");this.expanded=!0;this.currentSetting=a};a.SettingsController.prototype.closeDrawer=function(){this.settingsContainer.removeClass("expanded");$(".has-expanded-drawer").removeClass("has-expanded-drawer");this.expanded=
!1;this.currentSetting=null}})();(function(){var a=$.namespace("pskl.drawingtools");a.BaseTool=function(){};a.BaseTool.prototype.applyToolAt=function(a,b,c,f,e){};a.BaseTool.prototype.moveToolAt=function(a,b,c,f,e){};a.BaseTool.prototype.moveUnactiveToolAt=function(a,b,c,f,e){e.containsPixel(a,b)&&(isNaN(this.highlightedPixelCol)||isNaN(this.highlightedPixelRow)||this.highlightedPixelRow==b&&this.highlightedPixelCol==a||e.clear(),e.setPixel(a,b,Constants.TOOL_TARGET_HIGHLIGHT_COLOR),this.highlightedPixelCol=a,this.highlightedPixelRow=
b)};a.BaseTool.prototype.releaseToolAt=function(a,b,c,f,e){};a.BaseTool.prototype.getLinePixels_=function(a,b,c,f){for(var e=[],g=Math.abs(b-a),h=Math.abs(f-c),k=a<b?1:-1,l=c<f?1:-1,m=g-h;;){e.push({col:a,row:c});if(a==b&&c==f)break;var n=2*m;n>-h&&(m-=h,a+=k);n<g&&(m+=g,c+=l)}return e}})();(function(){var a=$.namespace("pskl.drawingtools");a.Circle=function(){this.toolId="tool-circle";this.helpText="Circle tool";this.startRow=this.startCol=null};pskl.utils.inherit(a.Circle,a.BaseTool);a.Circle.prototype.applyToolAt=function(a,b,c,f,e){this.startCol=a;this.startRow=b;e.setPixel(a,b,c)};a.Circle.prototype.moveToolAt=function(a,b,c,f,e){e.clear();c==Constants.TRANSPARENT_COLOR&&(c=Constants.SELECTION_TRANSPARENT_COLOR);this.drawCircle_(a,b,c,e)};a.Circle.prototype.releaseToolAt=function(a,
b,c,f,e){e.clear();f.containsPixel(a,b)&&this.drawCircle_(a,b,c,f)};a.Circle.prototype.drawCircle_=function(a,b,c,f){a=this.getCirclePixels_(this.startCol,this.startRow,a,b);for(b=0;b<a.length;b++)f.setPixel(a[b].col,a[b].row,c)};a.Circle.prototype.getCirclePixels_=function(a,b,c,f){a=pskl.PixelUtils.getOrderedRectangleCoordinates(a,b,c,f);b=(a.x0+a.x1)/2;c=(a.y0+a.y1)/2;f=a.x1-b;var e=a.y1-c,g=[],h,k,l;for(h=a.x0;h<a.x1;h++)l=Math.acos((h-b)/f),k=Math.round(e*Math.sin(l)+c),g.push({col:h,row:k}),
g.push({col:2*b-h,row:2*c-k});for(k=a.y0;k<a.y1;k++)l=Math.asin((k-c)/e),h=Math.round(f*Math.cos(l)+b),g.push({col:h,row:k}),g.push({col:2*b-h,row:2*c-k});return g}})();(function(){var a=$.namespace("pskl.drawingtools");a.ColorPicker=function(){this.toolId="tool-colorpicker";this.helpText="Color picker"};pskl.utils.inherit(a.ColorPicker,a.BaseTool);a.ColorPicker.prototype.applyToolAt=function(a,b,c,f,e,g){f.containsPixel(a,b)&&(a=f.getPixel(a,b),g.button==Constants.LEFT_BUTTON?$.publish(Events.PRIMARY_COLOR_SELECTED,[a]):g.button==Constants.RIGHT_BUTTON&&$.publish(Events.SECONDARY_COLOR_SELECTED,[a]))}})();(function(){var a=$.namespace("pskl.drawingtools");a.Eraser=function(){this.toolId="tool-eraser";this.helpText="Eraser tool"};pskl.utils.inherit(a.Eraser,a.SimplePen);a.Eraser.prototype.applyToolAt=function(a,b,c,f,e){this.superclass.applyToolAt.call(this,a,b,Constants.TRANSPARENT_COLOR,f,e)}})();(function(){var a=$.namespace("pskl.drawingtools");a.Move=function(){this.toolId="tool-move";this.helpText="Move tool";this.startRow=this.startCol=null};pskl.utils.inherit(a.Move,a.BaseTool);a.Move.prototype.applyToolAt=function(a,b,c,f,e){this.startCol=a;this.startRow=b;this.frameClone=f.clone()};a.Move.prototype.moveToolAt=function(a,b,c,f,e){this.shiftFrame(a-this.startCol,b-this.startRow,f,this.frameClone)};a.Move.prototype.shiftFrame=function(a,b,c,f){for(var e,g=0;g<c.getWidth();g++)for(var h=
0;h<c.getHeight();h++)e=f.containsPixel(g-a,h-b)?f.getPixel(g-a,h-b):Constants.TRANSPARENT_COLOR,c.setPixel(g,h,e)};a.Move.prototype.releaseToolAt=function(a,b,c,f,e){this.moveToolAt(a,b,c,f,e)}})();(function(){var a=$.namespace("pskl.drawingtools");a.PaintBucket=function(){this.toolId="tool-paint-bucket";this.helpText="Paint bucket tool"};pskl.utils.inherit(a.PaintBucket,a.BaseTool);a.PaintBucket.prototype.applyToolAt=function(a,b,c,f,e){pskl.PixelUtils.paintSimilarConnectedPixelsFromFrame(f,a,b,c)}})();(function(){var a=$.namespace("pskl.drawingtools");a.Rectangle=function(){this.toolId="tool-rectangle";this.helpText="Rectangle tool";this.startRow=this.startCol=null};pskl.utils.inherit(a.Rectangle,a.BaseTool);a.Rectangle.prototype.applyToolAt=function(a,b,c,f,e){this.startCol=a;this.startRow=b;e.setPixel(a,b,c)};a.Rectangle.prototype.moveToolAt=function(a,b,c,f,e){e.clear();c==Constants.TRANSPARENT_COLOR&&(c=Constants.SELECTION_TRANSPARENT_COLOR);this.drawRectangle_(a,b,c,e)};a.Rectangle.prototype.releaseToolAt=
function(a,b,c,f,e){e.clear();f.containsPixel(a,b)&&this.drawRectangle_(a,b,c,f)};a.Rectangle.prototype.drawRectangle_=function(a,b,c,f){a=pskl.PixelUtils.getBoundRectanglePixels(this.startCol,this.startRow,a,b);for(b=0;b<a.length;b++)f.setPixel(a[b].col,a[b].row,c)}})();(function(){var a=$.namespace("pskl.drawingtools");a.SimplePen=function(){this.toolId="tool-pen";this.helpText="Pen tool";this.previousRow=this.previousCol=null};pskl.utils.inherit(a.SimplePen,a.BaseTool);a.SimplePen.prototype.applyToolAt=function(a,b,c,f,e){f.containsPixel(a,b)&&f.setPixel(a,b,c);this.previousCol=a;this.previousRow=b};a.SimplePen.prototype.moveToolAt=function(a,b,c,f,e){if(1<Math.abs(a-this.previousCol)||1<Math.abs(b-this.previousRow))for(var g=this.getLinePixels_(a,this.previousCol,
b,this.previousRow),h=0,k=g.length;h<k;h++){var l=g[h];this.applyToolAt(l.col,l.row,c,f,e)}else this.applyToolAt(a,b,c,f,e);this.previousCol=a;this.previousRow=b}})();(function(){var a=$.namespace("pskl.drawingtools");a.Stroke=function(){this.toolId="tool-stroke";this.helpText="Stroke tool";this.startRow=this.startCol=null};pskl.utils.inherit(a.Stroke,a.BaseTool);a.Stroke.prototype.applyToolAt=function(a,b,c,f,e){this.startCol=a;this.startRow=b;e.setPixel(a,b,c)};a.Stroke.prototype.moveToolAt=function(a,b,c,f,e){e.clear();a=this.getLinePixels_(this.startCol,a,this.startRow,b);for(b=0;b<a.length;b++)c==Constants.TRANSPARENT_COLOR&&(c=Constants.SELECTION_TRANSPARENT_COLOR),
e.setPixel(a[b].col,a[b].row,c)};a.Stroke.prototype.releaseToolAt=function(a,b,c,f,e){if(f.containsPixel(a,b))for(a=this.getLinePixels_(this.startCol,a,this.startRow,b),b=0;b<a.length;b++)f.setPixel(a[b].col,a[b].row,c);e.clear()}})();(function(){var a=$.namespace("pskl.drawingtools");a.VerticalMirrorPen=function(){this.toolId="tool-vertical-mirror-pen";this.helpText="vertical mirror pen tool";this.mirroredPreviousRow=this.mirroredPreviousCol=this.swap=null};pskl.utils.inherit(a.VerticalMirrorPen,a.SimplePen);a.VerticalMirrorPen.prototype.setMirrorContext=function(){this.swap=this.previousCol;this.previousCol=this.mirroredPreviousCol};a.VerticalMirrorPen.prototype.unsetMirrorContext=function(){this.mirroredPreviousCol=this.previousCol;
this.previousCol=this.swap};a.VerticalMirrorPen.prototype.applyToolAt=function(a,b,c,f,e){this.superclass.applyToolAt.call(this,a,b,c,f,e);this.mirroredPreviousCol=a=this.getSymmetricCol_(a,f);this.setMirrorContext();this.superclass.applyToolAt.call(this,a,b,c,f,e);this.unsetMirrorContext()};a.VerticalMirrorPen.prototype.getSymmetricCol_=function(a,b){return b.getWidth()-a-1}})();(function(){var a=$.namespace("pskl.drawingtools");a.BaseSelect=function(){this.secondaryToolId="tool-move";this.BodyRoot=$("body");this.startRow=this.startCol=null};pskl.utils.inherit(a.BaseSelect,a.BaseTool);a.BaseSelect.prototype.applyToolAt=function(a,b,c,f,e){this.startCol=a;this.startRow=b;this.lastCol=a;this.lastRow=b;e.getPixel(a,b)!=Constants.SELECTION_TRANSPARENT_COLOR?(this.mode="select",this.onSelectStart_(a,b,c,f,e)):(this.mode="moveSelection",this.onSelectionDragStart_(a,b,c,f,e))};
a.BaseSelect.prototype.moveToolAt=function(a,b,c,f,e){if("select"==this.mode)this.onSelect_(a,b,c,f,e);else if("moveSelection"==this.mode)this.onSelectionDrag_(a,b,c,f,e)};a.BaseSelect.prototype.releaseToolAt=function(a,b,c,f,e){if("select"==this.mode)this.onSelectEnd_(a,b,c,f,e);else if("moveSelection"==this.mode)this.onSelectionDragEnd_(a,b,c,f,e)};a.BaseSelect.prototype.moveUnactiveToolAt=function(a,b,c,f,e){e.getPixel(a,b)!=Constants.SELECTION_TRANSPARENT_COLOR?(this.BodyRoot.addClass(this.toolId),
this.BodyRoot.removeClass(this.secondaryToolId)):(this.BodyRoot.addClass(this.secondaryToolId),this.BodyRoot.removeClass(this.toolId))};a.BaseSelect.prototype.drawSelectionOnOverlay_=function(a,b){for(var c=a.pixels,f=0,e=c.length;f<e;f++)b.setPixel(c[f].col,c[f].row,Constants.SELECTION_TRANSPARENT_COLOR)};a.BaseSelect.prototype.shiftOverlayFrame_=function(a,b,c,f){for(var e,g=0;g<c.getWidth();g++)for(var h=0;h<c.getHeight();h++)e=f.containsPixel(g-a,h-b)?f.getPixel(g-a,h-b):Constants.TRANSPARENT_COLOR,
c.setPixel(g,h,e)};a.BaseSelect.prototype.onSelectStart_=function(a,b,c,f,e){};a.BaseSelect.prototype.onSelect_=function(a,b,c,f,e){};a.BaseSelect.prototype.onSelectEnd_=function(a,b,c,f,e){};a.BaseSelect.prototype.onSelectionDragStart_=function(a,b,c,f,e){this.overlayFrameReference=e.clone()};a.BaseSelect.prototype.onSelectionDrag_=function(a,b,c,f,e){c=a-this.lastCol;f=b-this.lastRow;this.shiftOverlayFrame_(a-this.startCol,b-this.startRow,e,this.overlayFrameReference);$.publish(Events.SELECTION_MOVE_REQUEST,
[c,f]);this.lastCol=a;this.lastRow=b};a.BaseSelect.prototype.onSelectionDragEnd_=function(a,b,c,f,e){this.onSelectionDrag_(a,b,c,f,e)}})();(function(){var a=$.namespace("pskl.drawingtools");a.RectangleSelect=function(){this.toolId="tool-rectangle-select";this.helpText="Rectangle selection tool";a.BaseSelect.call(this)};pskl.utils.inherit(a.RectangleSelect,a.BaseSelect);a.RectangleSelect.prototype.onSelectStart_=function(a,b,c,f,e){e.setPixel(a,b,c)};a.RectangleSelect.prototype.onSelect_=function(a,b,c,f,e){e.clear();this.startCol==a&&this.startRow==b?$.publish(Events.SELECTION_DISMISSED):(a=new pskl.selection.RectangularSelection(this.startCol,
this.startRow,a,b),$.publish(Events.SELECTION_CREATED,[a]),this.drawSelectionOnOverlay_(a,e))};a.RectangleSelect.prototype.onSelectEnd_=function(a,b,c,f,e){this.onSelect_(a,b,c,f,e)}})();(function(){var a=$.namespace("pskl.drawingtools");a.ShapeSelect=function(){this.toolId="tool-shape-select";this.helpText="Shape selection tool";a.BaseSelect.call(this)};pskl.utils.inherit(a.ShapeSelect,a.BaseSelect);a.ShapeSelect.prototype.onSelectStart_=function(a,b,c,f,e){$.publish(Events.SELECTION_DISMISSED);e.clear();a=pskl.PixelUtils.getSimilarConnectedPixelsFromFrame(f,a,b);a=new pskl.selection.ShapeSelection(a);$.publish(Events.SELECTION_CREATED,[a]);this.drawSelectionOnOverlay_(a,e)}})();(function(){window.iframeloader={onLoad:function(a){a=a.target||a.srcElement;var d=a.getAttribute("data-iframe-loader");"display"===d?(d=document.createElement("div"),d.innerHTML=a.contentWindow.document.body.innerHTML,1==d.children.length&&(d=d.children[0]),a.parentNode.replaceChild(d,a)):"store"===d?(d=document.createElement("script"),d.setAttribute("type","text/html"),d.setAttribute("id",a.getAttribute("src")),d.innerHTML=a.contentWindow.document.body.innerHTML,a.parentNode.removeChild(a),document.body.appendChild(d)):
console.error("iframeLoader invalid type : "+d)}}})();(function(a,d){function b(b,d){var e,f;e=b.nodeName.toLowerCase();if("area"===e){e=b.parentNode;f=e.name;if(!b.href||!f||"map"!==e.nodeName.toLowerCase())return!1;e=a("img[usemap=#"+f+"]")[0];return!!e&&c(e)}return(/input|select|textarea|button|object/.test(e)?!b.disabled:"a"===e?b.href||d:d)&&c(b)}function c(c){return a.expr.filters.visible(c)&&!a(c).parents().addBack().filter(function(){return"hidden"===a.css(this,"visibility")}).length}var f=0,e=/^ui-id-\d+$/;a.ui=a.ui||{};a.extend(a.ui,{version:"1.10.3",
keyCode:{BACKSPACE:8,COMMA:188,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,LEFT:37,NUMPAD_ADD:107,NUMPAD_DECIMAL:110,NUMPAD_DIVIDE:111,NUMPAD_ENTER:108,NUMPAD_MULTIPLY:106,NUMPAD_SUBTRACT:109,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SPACE:32,TAB:9,UP:38}});a.fn.extend({focus:function(c){return function(b,d){return"number"===typeof b?this.each(function(){var c=this;setTimeout(function(){a(c).focus();d&&d.call(c)},b)}):c.apply(this,arguments)}}(a.fn.focus),scrollParent:function(){var c;c=
a.ui.ie&&/(static|relative)/.test(this.css("position"))||/absolute/.test(this.css("position"))?this.parents().filter(function(){return/(relative|absolute|fixed)/.test(a.css(this,"position"))&&/(auto|scroll)/.test(a.css(this,"overflow")+a.css(this,"overflow-y")+a.css(this,"overflow-x"))}).eq(0):this.parents().filter(function(){return/(auto|scroll)/.test(a.css(this,"overflow")+a.css(this,"overflow-y")+a.css(this,"overflow-x"))}).eq(0);return/fixed/.test(this.css("position"))||!c.length?a(document):
c},zIndex:function(c){if(c!==d)return this.css("zIndex",c);if(this.length){c=a(this[0]);for(var b;c.length&&c[0]!==document;){b=c.css("position");if("absolute"===b||"relative"===b||"fixed"===b)if(b=parseInt(c.css("zIndex"),10),!isNaN(b)&&0!==b)return b;c=c.parent()}}return 0},uniqueId:function(){return this.each(function(){this.id||(this.id="ui-id-"+ ++f)})},removeUniqueId:function(){return this.each(function(){e.test(this.id)&&a(this).removeAttr("id")})}});a.extend(a.expr[":"],{data:a.expr.createPseudo?
a.expr.createPseudo(function(c){return function(b){return!!a.data(b,c)}}):function(c,b,d){return!!a.data(c,d[3])},focusable:function(c){return b(c,!isNaN(a.attr(c,"tabindex")))},tabbable:function(c){var d=a.attr(c,"tabindex"),e=isNaN(d);return(e||0<=d)&&b(c,!e)}});a("<a>").outerWidth(1).jquery||a.each(["Width","Height"],function(c,b){function e(c,b,d,g){a.each(f,function(){b-=parseFloat(a.css(c,"padding"+this))||0;d&&(b-=parseFloat(a.css(c,"border"+this+"Width"))||0);g&&(b-=parseFloat(a.css(c,"margin"+
this))||0)});return b}var f="Width"===b?["Left","Right"]:["Top","Bottom"],m=b.toLowerCase(),n={innerWidth:a.fn.innerWidth,innerHeight:a.fn.innerHeight,outerWidth:a.fn.outerWidth,outerHeight:a.fn.outerHeight};a.fn["inner"+b]=function(c){return c===d?n["inner"+b].call(this):this.each(function(){a(this).css(m,e(this,c)+"px")})};a.fn["outer"+b]=function(c,d){return"number"!==typeof c?n["outer"+b].call(this,c):this.each(function(){a(this).css(m,e(this,c,!0,d)+"px")})}});a.fn.addBack||(a.fn.addBack=function(a){return this.add(null==
a?this.prevObject:this.prevObject.filter(a))});a("<a>").data("a-b","a").removeData("a-b").data("a-b")&&(a.fn.removeData=function(c){return function(b){return arguments.length?c.call(this,a.camelCase(b)):c.call(this)}}(a.fn.removeData));a.ui.ie=!!/msie [\w.]+/.exec(navigator.userAgent.toLowerCase());a.support.selectstart="onselectstart"in document.createElement("div");a.fn.extend({disableSelection:function(){return this.bind((a.support.selectstart?"selectstart":"mousedown")+".ui-disableSelection",
function(a){a.preventDefault()})},enableSelection:function(){return this.unbind(".ui-disableSelection")}});a.extend(a.ui,{plugin:{add:function(c,b,d){var e;c=a.ui[c].prototype;for(e in d)c.plugins[e]=c.plugins[e]||[],c.plugins[e].push([b,d[e]])},call:function(a,c,b){var d=a.plugins[c];if(d&&a.element[0].parentNode&&11!==a.element[0].parentNode.nodeType)for(c=0;c<d.length;c++)a.options[d[c][0]]&&d[c][1].apply(a.element,b)}},hasScroll:function(c,b){if("hidden"===a(c).css("overflow"))return!1;var d=
b&&"left"===b?"scrollLeft":"scrollTop",e=!1;if(0<c[d])return!0;c[d]=1;e=0<c[d];c[d]=0;return e}})})(jQuery);
(function(a,d){var b=0,c=Array.prototype.slice,f=a.cleanData;a.cleanData=function(c){for(var b=0,d;null!=(d=c[b]);b++)try{a(d).triggerHandler("remove")}catch(k){}f(c)};a.widget=function(c,b,d){var f,l,m,n,q={},p=c.split(".")[0];c=c.split(".")[1];f=p+"-"+c;d||(d=b,b=a.Widget);a.expr[":"][f.toLowerCase()]=function(c){return!!a.data(c,f)};a[p]=a[p]||{};l=a[p][c];m=a[p][c]=function(a,c){if(!this._createWidget)return new m(a,c);arguments.length&&this._createWidget(a,c)};a.extend(m,l,{version:d.version,
_proto:a.extend({},d),_childConstructors:[]});n=new b;n.options=a.widget.extend({},n.options);a.each(d,function(c,d){a.isFunction(d)?q[c]=function(){var a=function(){return b.prototype[c].apply(this,arguments)},e=function(a){return b.prototype[c].apply(this,a)};return function(){var c=this._super,b=this._superApply,f;this._super=a;this._superApply=e;f=d.apply(this,arguments);this._super=c;this._superApply=b;return f}}():q[c]=d});m.prototype=a.widget.extend(n,{widgetEventPrefix:l?n.widgetEventPrefix:
c},q,{constructor:m,namespace:p,widgetName:c,widgetFullName:f});l?(a.each(l._childConstructors,function(c,b){var d=b.prototype;a.widget(d.namespace+"."+d.widgetName,m,b._proto)}),delete l._childConstructors):b._childConstructors.push(m);a.widget.bridge(c,m)};a.widget.extend=function(b){for(var f=c.call(arguments,1),h=0,k=f.length,l,m;h<k;h++)for(l in f[h])m=f[h][l],f[h].hasOwnProperty(l)&&m!==d&&(a.isPlainObject(m)?b[l]=a.isPlainObject(b[l])?a.widget.extend({},b[l],m):a.widget.extend({},m):b[l]=m);
return b};a.widget.bridge=function(b,f){var h=f.prototype.widgetFullName||b;a.fn[b]=function(k){var l="string"===typeof k,m=c.call(arguments,1),n=this;k=!l&&m.length?a.widget.extend.apply(null,[k].concat(m)):k;l?this.each(function(){var c,f=a.data(this,h);if(!f)return a.error("cannot call methods on "+b+" prior to initialization; attempted to call method '"+k+"'");if(!a.isFunction(f[k])||"_"===k.charAt(0))return a.error("no such method '"+k+"' for "+b+" widget instance");c=f[k].apply(f,m);if(c!==
f&&c!==d)return n=c&&c.jquery?n.pushStack(c.get()):c,!1}):this.each(function(){var c=a.data(this,h);c?c.option(k||{})._init():a.data(this,h,new f(k,this))});return n}};a.Widget=function(){};a.Widget._childConstructors=[];a.Widget.prototype={widgetName:"widget",widgetEventPrefix:"",defaultElement:"<div>",options:{disabled:!1,create:null},_createWidget:function(c,d){d=a(d||this.defaultElement||this)[0];this.element=a(d);this.uuid=b++;this.eventNamespace="."+this.widgetName+this.uuid;this.options=a.widget.extend({},
this.options,this._getCreateOptions(),c);this.bindings=a();this.hoverable=a();this.focusable=a();d!==this&&(a.data(d,this.widgetFullName,this),this._on(!0,this.element,{remove:function(a){a.target===d&&this.destroy()}}),this.document=a(d.style?d.ownerDocument:d.document||d),this.window=a(this.document[0].defaultView||this.document[0].parentWindow));this._create();this._trigger("create",null,this._getCreateEventData());this._init()},_getCreateOptions:a.noop,_getCreateEventData:a.noop,_create:a.noop,
_init:a.noop,destroy:function(){this._destroy();this.element.unbind(this.eventNamespace).removeData(this.widgetName).removeData(this.widgetFullName).removeData(a.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:a.noop,widget:function(){return this.element},
option:function(c,b){var f=c,k,l,m;if(0===arguments.length)return a.widget.extend({},this.options);if("string"===typeof c)if(f={},k=c.split("."),c=k.shift(),k.length){l=f[c]=a.widget.extend({},this.options[c]);for(m=0;m<k.length-1;m++)l[k[m]]=l[k[m]]||{},l=l[k[m]];c=k.pop();if(b===d)return l[c]===d?null:l[c];l[c]=b}else{if(b===d)return this.options[c]===d?null:this.options[c];f[c]=b}this._setOptions(f);return this},_setOptions:function(a){for(var c in a)this._setOption(c,a[c]);return this},_setOption:function(a,
c){this.options[a]=c;"disabled"===a&&(this.widget().toggleClass(this.widgetFullName+"-disabled ui-state-disabled",!!c).attr("aria-disabled",c),this.hoverable.removeClass("ui-state-hover"),this.focusable.removeClass("ui-state-focus"));return this},enable:function(){return this._setOption("disabled",!1)},disable:function(){return this._setOption("disabled",!0)},_on:function(c,b,d){var f,l=this;"boolean"!==typeof c&&(d=b,b=c,c=!1);d?(b=f=a(b),this.bindings=this.bindings.add(b)):(d=b,b=this.element,f=
this.widget());a.each(d,function(d,h){function q(){if(c||!0!==l.options.disabled&&!a(this).hasClass("ui-state-disabled"))return("string"===typeof h?l[h]:h).apply(l,arguments)}"string"!==typeof h&&(q.guid=h.guid=h.guid||q.guid||a.guid++);var p=d.match(/^(\w+)\s*(.*)$/),r=p[1]+l.eventNamespace;(p=p[2])?f.delegate(p,r,q):b.bind(r,q)})},_off:function(a,c){c=(c||"").split(" ").join(this.eventNamespace+" ")+this.eventNamespace;a.unbind(c).undelegate(c)},_delay:function(a,c){var b=this;return setTimeout(function(){return("string"===
typeof a?b[a]:a).apply(b,arguments)},c||0)},_hoverable:function(c){this.hoverable=this.hoverable.add(c);this._on(c,{mouseenter:function(c){a(c.currentTarget).addClass("ui-state-hover")},mouseleave:function(c){a(c.currentTarget).removeClass("ui-state-hover")}})},_focusable:function(c){this.focusable=this.focusable.add(c);this._on(c,{focusin:function(c){a(c.currentTarget).addClass("ui-state-focus")},focusout:function(c){a(c.currentTarget).removeClass("ui-state-focus")}})},_trigger:function(c,b,d){var f,
l=this.options[c];d=d||{};b=a.Event(b);b.type=(c===this.widgetEventPrefix?c:this.widgetEventPrefix+c).toLowerCase();b.target=this.element[0];if(c=b.originalEvent)for(f in c)f in b||(b[f]=c[f]);this.element.trigger(b,d);return!(a.isFunction(l)&&!1===l.apply(this.element[0],[b].concat(d))||b.isDefaultPrevented())}};a.each({show:"fadeIn",hide:"fadeOut"},function(c,b){a.Widget.prototype["_"+c]=function(d,f,l){"string"===typeof f&&(f={effect:f});var m,n=f?!0===f||"number"===typeof f?b:f.effect||b:c;f=
f||{};"number"===typeof f&&(f={duration:f});m=!a.isEmptyObject(f);f.complete=l;f.delay&&d.delay(f.delay);if(m&&a.effects&&a.effects.effect[n])d[c](f);else if(n!==c&&d[n])d[n](f.duration,f.easing,l);else d.queue(function(b){a(this)[c]();l&&l.call(d[0]);b()})}})})(jQuery);
(function(a,d){var b=!1;a(document).mouseup(function(){b=!1});a.widget("ui.mouse",{version:"1.10.3",options:{cancel:"input,textarea,button,select,option",distance:1,delay:0},_mouseInit:function(){var c=this;this.element.bind("mousedown."+this.widgetName,function(a){return c._mouseDown(a)}).bind("click."+this.widgetName,function(b){if(!0===a.data(b.target,c.widgetName+".preventClickEvent"))return a.removeData(b.target,c.widgetName+".preventClickEvent"),b.stopImmediatePropagation(),!1});this.started=
!1},_mouseDestroy:function(){this.element.unbind("."+this.widgetName);this._mouseMoveDelegate&&a(document).unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate)},_mouseDown:function(c){if(!b){this._mouseStarted&&this._mouseUp(c);this._mouseDownEvent=c;var d=this,e=1===c.which,g="string"===typeof this.options.cancel&&c.target.nodeName?a(c.target).closest(this.options.cancel).length:!1;if(!e||g||!this._mouseCapture(c))return!0;this.mouseDelayMet=
!this.options.delay;this.mouseDelayMet||(this._mouseDelayTimer=setTimeout(function(){d.mouseDelayMet=!0},this.options.delay));if(this._mouseDistanceMet(c)&&this._mouseDelayMet(c)&&(this._mouseStarted=!1!==this._mouseStart(c),!this._mouseStarted))return c.preventDefault(),!0;!0===a.data(c.target,this.widgetName+".preventClickEvent")&&a.removeData(c.target,this.widgetName+".preventClickEvent");this._mouseMoveDelegate=function(a){return d._mouseMove(a)};this._mouseUpDelegate=function(a){return d._mouseUp(a)};
a(document).bind("mousemove."+this.widgetName,this._mouseMoveDelegate).bind("mouseup."+this.widgetName,this._mouseUpDelegate);c.preventDefault();return b=!0}},_mouseMove:function(c){if(a.ui.ie&&(!document.documentMode||9>document.documentMode)&&!c.button)return this._mouseUp(c);if(this._mouseStarted)return this._mouseDrag(c),c.preventDefault();this._mouseDistanceMet(c)&&this._mouseDelayMet(c)&&((this._mouseStarted=!1!==this._mouseStart(this._mouseDownEvent,c))?this._mouseDrag(c):this._mouseUp(c));
return!this._mouseStarted},_mouseUp:function(c){a(document).unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate);this._mouseStarted&&(this._mouseStarted=!1,c.target===this._mouseDownEvent.target&&a.data(c.target,this.widgetName+".preventClickEvent",!0),this._mouseStop(c));return!1},_mouseDistanceMet:function(a){return Math.max(Math.abs(this._mouseDownEvent.pageX-a.pageX),Math.abs(this._mouseDownEvent.pageY-a.pageY))>=this.options.distance},
_mouseDelayMet:function(){return this.mouseDelayMet},_mouseStart:function(){},_mouseDrag:function(){},_mouseStop:function(){},_mouseCapture:function(){return!0}})})(jQuery);
(function(a,d){function b(a){return/left|right/.test(a.css("float"))||/inline|table-cell/.test(a.css("display"))}a.widget("ui.sortable",a.ui.mouse,{version:"1.10.3",widgetEventPrefix:"sort",ready:!1,options:{appendTo:"parent",axis:!1,connectWith:!1,containment:!1,cursor:"auto",cursorAt:!1,dropOnEmpty:!0,forcePlaceholderSize:!1,forceHelperSize:!1,grid:!1,handle:!1,helper:"original",items:"> *",opacity:!1,placeholder:!1,revert:!1,scroll:!0,scrollSensitivity:20,scrollSpeed:20,scope:"default",tolerance:"intersect",
zIndex:1E3,activate:null,beforeStop:null,change:null,deactivate:null,out:null,over:null,receive:null,remove:null,sort:null,start:null,stop:null,update:null},_create:function(){var a=this.options;this.containerCache={};this.element.addClass("ui-sortable");this.refresh();this.floating=this.items.length?"x"===a.axis||b(this.items[0].item):!1;this.offset=this.element.offset();this._mouseInit();this.ready=!0},_destroy:function(){this.element.removeClass("ui-sortable ui-sortable-disabled");this._mouseDestroy();
for(var a=this.items.length-1;0<=a;a--)this.items[a].item.removeData(this.widgetName+"-item");return this},_setOption:function(c,b){"disabled"===c?(this.options[c]=b,this.widget().toggleClass("ui-sortable-disabled",!!b)):a.Widget.prototype._setOption.apply(this,arguments)},_mouseCapture:function(c,b){var d=null,g=!1,h=this;if(this.reverting||this.options.disabled||"static"===this.options.type)return!1;this._refreshItems(c);a(c.target).parents().each(function(){if(a.data(this,h.widgetName+"-item")===
h)return d=a(this),!1});a.data(c.target,h.widgetName+"-item")===h&&(d=a(c.target));if(!d||this.options.handle&&!b&&(a(this.options.handle,d).find("*").addBack().each(function(){this===c.target&&(g=!0)}),!g))return!1;this.currentItem=d;this._removeCurrentsFromItems();return!0},_mouseStart:function(c,b,d){var g;b=this.options;this.currentContainer=this;this.refreshPositions();this.helper=this._createHelper(c);this._cacheHelperProportions();this._cacheMargins();this.scrollParent=this.helper.scrollParent();
this.offset=this.currentItem.offset();this.offset={top:this.offset.top-this.margins.top,left:this.offset.left-this.margins.left};a.extend(this.offset,{click:{left:c.pageX-this.offset.left,top:c.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()});this.helper.css("position","absolute");this.cssPosition=this.helper.css("position");this.originalPosition=this._generatePosition(c);this.originalPageX=c.pageX;this.originalPageY=c.pageY;b.cursorAt&&this._adjustOffsetFromHelper(b.cursorAt);
this.domPosition={prev:this.currentItem.prev()[0],parent:this.currentItem.parent()[0]};this.helper[0]!==this.currentItem[0]&&this.currentItem.hide();this._createPlaceholder();b.containment&&this._setContainment();b.cursor&&"auto"!==b.cursor&&(g=this.document.find("body"),this.storedCursor=g.css("cursor"),g.css("cursor",b.cursor),this.storedStylesheet=a("<style>*{ cursor: "+b.cursor+" !important; }</style>").appendTo(g));b.opacity&&(this.helper.css("opacity")&&(this._storedOpacity=this.helper.css("opacity")),
this.helper.css("opacity",b.opacity));b.zIndex&&(this.helper.css("zIndex")&&(this._storedZIndex=this.helper.css("zIndex")),this.helper.css("zIndex",b.zIndex));this.scrollParent[0]!==document&&"HTML"!==this.scrollParent[0].tagName&&(this.overflowOffset=this.scrollParent.offset());this._trigger("start",c,this._uiHash());this._preserveHelperProportions||this._cacheHelperProportions();if(!d)for(d=this.containers.length-1;0<=d;d--)this.containers[d]._trigger("activate",c,this._uiHash(this));a.ui.ddmanager&&
(a.ui.ddmanager.current=this);a.ui.ddmanager&&!b.dropBehaviour&&a.ui.ddmanager.prepareOffsets(this,c);this.dragging=!0;this.helper.addClass("ui-sortable-helper");this._mouseDrag(c);return!0},_mouseDrag:function(c){var b,d,g,h;b=this.options;d=!1;this.position=this._generatePosition(c);this.positionAbs=this._convertPositionTo("absolute");this.lastPositionAbs||(this.lastPositionAbs=this.positionAbs);this.options.scroll&&(this.scrollParent[0]!==document&&"HTML"!==this.scrollParent[0].tagName?(this.overflowOffset.top+
this.scrollParent[0].offsetHeight-c.pageY<b.scrollSensitivity?this.scrollParent[0].scrollTop=d=this.scrollParent[0].scrollTop+b.scrollSpeed:c.pageY-this.overflowOffset.top<b.scrollSensitivity&&(this.scrollParent[0].scrollTop=d=this.scrollParent[0].scrollTop-b.scrollSpeed),this.overflowOffset.left+this.scrollParent[0].offsetWidth-c.pageX<b.scrollSensitivity?this.scrollParent[0].scrollLeft=d=this.scrollParent[0].scrollLeft+b.scrollSpeed:c.pageX-this.overflowOffset.left<b.scrollSensitivity&&(this.scrollParent[0].scrollLeft=
d=this.scrollParent[0].scrollLeft-b.scrollSpeed)):(c.pageY-a(document).scrollTop()<b.scrollSensitivity?d=a(document).scrollTop(a(document).scrollTop()-b.scrollSpeed):a(window).height()-(c.pageY-a(document).scrollTop())<b.scrollSensitivity&&(d=a(document).scrollTop(a(document).scrollTop()+b.scrollSpeed)),c.pageX-a(document).scrollLeft()<b.scrollSensitivity?d=a(document).scrollLeft(a(document).scrollLeft()-b.scrollSpeed):a(window).width()-(c.pageX-a(document).scrollLeft())<b.scrollSensitivity&&(d=a(document).scrollLeft(a(document).scrollLeft()+
b.scrollSpeed))),!1!==d&&a.ui.ddmanager&&!b.dropBehaviour&&a.ui.ddmanager.prepareOffsets(this,c));this.positionAbs=this._convertPositionTo("absolute");this.options.axis&&"y"===this.options.axis||(this.helper[0].style.left=this.position.left+"px");this.options.axis&&"x"===this.options.axis||(this.helper[0].style.top=this.position.top+"px");for(b=this.items.length-1;0<=b;b--)if(d=this.items[b],g=d.item[0],(h=this._intersectsWithPointer(d))&&d.instance===this.currentContainer&&g!==this.currentItem[0]&&
this.placeholder[1===h?"next":"prev"]()[0]!==g&&!a.contains(this.placeholder[0],g)&&("semi-dynamic"===this.options.type?!a.contains(this.element[0],g):1)){this.direction=1===h?"down":"up";if("pointer"===this.options.tolerance||this._intersectsWithSides(d))this._rearrange(c,d);else break;this._trigger("change",c,this._uiHash());break}this._contactContainers(c);a.ui.ddmanager&&a.ui.ddmanager.drag(this,c);this._trigger("sort",c,this._uiHash());this.lastPositionAbs=this.positionAbs;return!1},_mouseStop:function(c,
b){if(c){a.ui.ddmanager&&!this.options.dropBehaviour&&a.ui.ddmanager.drop(this,c);if(this.options.revert){var d=this,g=this.placeholder.offset(),h=this.options.axis,k={};h&&"x"!==h||(k.left=g.left-this.offset.parent.left-this.margins.left+(this.offsetParent[0]===document.body?0:this.offsetParent[0].scrollLeft));h&&"y"!==h||(k.top=g.top-this.offset.parent.top-this.margins.top+(this.offsetParent[0]===document.body?0:this.offsetParent[0].scrollTop));this.reverting=!0;a(this.helper).animate(k,parseInt(this.options.revert,
10)||500,function(){d._clear(c)})}else this._clear(c,b);return!1}},cancel:function(){if(this.dragging){this._mouseUp({target:null});"original"===this.options.helper?this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper"):this.currentItem.show();for(var c=this.containers.length-1;0<=c;c--)this.containers[c]._trigger("deactivate",null,this._uiHash(this)),this.containers[c].containerCache.over&&(this.containers[c]._trigger("out",null,this._uiHash(this)),this.containers[c].containerCache.over=
0)}this.placeholder&&(this.placeholder[0].parentNode&&this.placeholder[0].parentNode.removeChild(this.placeholder[0]),"original"!==this.options.helper&&this.helper&&this.helper[0].parentNode&&this.helper.remove(),a.extend(this,{helper:null,dragging:!1,reverting:!1,_noFinalSort:null}),this.domPosition.prev?a(this.domPosition.prev).after(this.currentItem):a(this.domPosition.parent).prepend(this.currentItem));return this},serialize:function(c){var b=this._getItemsAsjQuery(c&&c.connected),d=[];c=c||{};
a(b).each(function(){var b=(a(c.item||this).attr(c.attribute||"id")||"").match(c.expression||/(.+)[\-=_](.+)/);b&&d.push((c.key||b[1]+"[]")+"="+(c.key&&c.expression?b[1]:b[2]))});!d.length&&c.key&&d.push(c.key+"=");return d.join("&")},toArray:function(c){var b=this._getItemsAsjQuery(c&&c.connected),d=[];c=c||{};b.each(function(){d.push(a(c.item||this).attr(c.attribute||"id")||"")});return d},_intersectsWith:function(a){var b=this.positionAbs.left,d=b+this.helperProportions.width,g=this.positionAbs.top,
h=g+this.helperProportions.height,k=a.left,l=k+a.width,m=a.top,n=m+a.height,q=this.offset.click.top,p=this.offset.click.left,p="y"===this.options.axis||b+p>k&&b+p<l,q=("x"===this.options.axis||g+q>m&&g+q<n)&&p;return"pointer"===this.options.tolerance||this.options.forcePointerForContainers||"pointer"!==this.options.tolerance&&this.helperProportions[this.floating?"width":"height"]>a[this.floating?"width":"height"]?q:k<b+this.helperProportions.width/2&&d-this.helperProportions.width/2<l&&m<g+this.helperProportions.height/
2&&h-this.helperProportions.height/2<n},_intersectsWithPointer:function(a){var b="y"===this.options.axis||this.positionAbs.left+this.offset.click.left>a.left&&this.positionAbs.left+this.offset.click.left<a.left+a.width;a=("x"===this.options.axis||this.positionAbs.top+this.offset.click.top>a.top&&this.positionAbs.top+this.offset.click.top<a.top+a.height)&&b;var b=this._getDragVerticalDirection(),d=this._getDragHorizontalDirection();return a?this.floating?d&&"right"===d||"down"===b?2:1:b&&("down"===
b?2:1):!1},_intersectsWithSides:function(a){var b=this.positionAbs.top+this.offset.click.top>a.top+a.height/2&&this.positionAbs.top+this.offset.click.top<a.top+a.height/2+a.height;a=this.positionAbs.left+this.offset.click.left>a.left+a.width/2&&this.positionAbs.left+this.offset.click.left<a.left+a.width/2+a.width;var d=this._getDragVerticalDirection(),g=this._getDragHorizontalDirection();return this.floating&&g?"right"===g&&a||"left"===g&&!a:d&&("down"===d&&b||"up"===d&&!b)},_getDragVerticalDirection:function(){var a=
this.positionAbs.top-this.lastPositionAbs.top;return 0!==a&&(0<a?"down":"up")},_getDragHorizontalDirection:function(){var a=this.positionAbs.left-this.lastPositionAbs.left;return 0!==a&&(0<a?"right":"left")},refresh:function(a){this._refreshItems(a);this.refreshPositions();return this},_connectWith:function(){var a=this.options;return a.connectWith.constructor===String?[a.connectWith]:a.connectWith},_getItemsAsjQuery:function(c){var b,d,g,h=[],k=[],l=this._connectWith();if(l&&c)for(c=l.length-1;0<=
c;c--)for(d=a(l[c]),b=d.length-1;0<=b;b--)(g=a.data(d[b],this.widgetFullName))&&g!==this&&!g.options.disabled&&k.push([a.isFunction(g.options.items)?g.options.items.call(g.element):a(g.options.items,g.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),g]);k.push([a.isFunction(this.options.items)?this.options.items.call(this.element,null,{options:this.options,item:this.currentItem}):a(this.options.items,this.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),this]);
for(c=k.length-1;0<=c;c--)k[c][0].each(function(){h.push(this)});return a(h)},_removeCurrentsFromItems:function(){var c=this.currentItem.find(":data("+this.widgetName+"-item)");this.items=a.grep(this.items,function(a){for(var b=0;b<c.length;b++)if(c[b]===a.item[0])return!1;return!0})},_refreshItems:function(c){this.items=[];this.containers=[this];var b,d,g,h,k,l=this.items,m=[[a.isFunction(this.options.items)?this.options.items.call(this.element[0],c,{item:this.currentItem}):a(this.options.items,
this.element),this]];if((k=this._connectWith())&&this.ready)for(b=k.length-1;0<=b;b--)for(g=a(k[b]),d=g.length-1;0<=d;d--)(h=a.data(g[d],this.widgetFullName))&&h!==this&&!h.options.disabled&&(m.push([a.isFunction(h.options.items)?h.options.items.call(h.element[0],c,{item:this.currentItem}):a(h.options.items,h.element),h]),this.containers.push(h));for(b=m.length-1;0<=b;b--)for(c=m[b][1],g=m[b][0],d=0,k=g.length;d<k;d++)h=a(g[d]),h.data(this.widgetName+"-item",c),l.push({item:h,instance:c,width:0,height:0,
left:0,top:0})},refreshPositions:function(b){this.offsetParent&&this.helper&&(this.offset.parent=this._getParentOffset());var d,e,g;for(d=this.items.length-1;0<=d;d--)e=this.items[d],e.instance!==this.currentContainer&&this.currentContainer&&e.item[0]!==this.currentItem[0]||(g=this.options.toleranceElement?a(this.options.toleranceElement,e.item):e.item,b||(e.width=g.outerWidth(),e.height=g.outerHeight()),g=g.offset(),e.left=g.left,e.top=g.top);if(this.options.custom&&this.options.custom.refreshContainers)this.options.custom.refreshContainers.call(this);
else for(d=this.containers.length-1;0<=d;d--)g=this.containers[d].element.offset(),this.containers[d].containerCache.left=g.left,this.containers[d].containerCache.top=g.top,this.containers[d].containerCache.width=this.containers[d].element.outerWidth(),this.containers[d].containerCache.height=this.containers[d].element.outerHeight();return this},_createPlaceholder:function(b){b=b||this;var d,e=b.options;e.placeholder&&e.placeholder.constructor!==String||(d=e.placeholder,e.placeholder={element:function(){var e=
b.currentItem[0].nodeName.toLowerCase(),h=a("<"+e+">",b.document[0]).addClass(d||b.currentItem[0].className+" ui-sortable-placeholder").removeClass("ui-sortable-helper");"tr"===e?b.currentItem.children().each(function(){a("<td>&#160;</td>",b.document[0]).attr("colspan",a(this).attr("colspan")||1).appendTo(h)}):"img"===e&&h.attr("src",b.currentItem.attr("src"));d||h.css("visibility","hidden");return h},update:function(a,h){if(!d||e.forcePlaceholderSize)h.height()||h.height(b.currentItem.innerHeight()-
parseInt(b.currentItem.css("paddingTop")||0,10)-parseInt(b.currentItem.css("paddingBottom")||0,10)),h.width()||h.width(b.currentItem.innerWidth()-parseInt(b.currentItem.css("paddingLeft")||0,10)-parseInt(b.currentItem.css("paddingRight")||0,10))}});b.placeholder=a(e.placeholder.element.call(b.element,b.currentItem));b.currentItem.after(b.placeholder);e.placeholder.update(b,b.placeholder)},_contactContainers:function(c){var d,e,g,h,k,l,m,n,q,p=e=null;for(d=this.containers.length-1;0<=d;d--)a.contains(this.currentItem[0],
this.containers[d].element[0])||(this._intersectsWith(this.containers[d].containerCache)?e&&a.contains(this.containers[d].element[0],e.element[0])||(e=this.containers[d],p=d):this.containers[d].containerCache.over&&(this.containers[d]._trigger("out",c,this._uiHash(this)),this.containers[d].containerCache.over=0));if(e)if(1===this.containers.length)this.containers[p].containerCache.over||(this.containers[p]._trigger("over",c,this._uiHash(this)),this.containers[p].containerCache.over=1);else{d=1E4;
g=null;h=(q=e.floating||b(this.currentItem))?"left":"top";k=q?"width":"height";l=this.positionAbs[h]+this.offset.click[h];for(e=this.items.length-1;0<=e;e--)a.contains(this.containers[p].element[0],this.items[e].item[0])&&this.items[e].item[0]!==this.currentItem[0]&&(!q||this.positionAbs.top+this.offset.click.top>this.items[e].top&&this.positionAbs.top+this.offset.click.top<this.items[e].top+this.items[e].height)&&(m=this.items[e].item.offset()[h],n=!1,Math.abs(m-l)>Math.abs(m+this.items[e][k]-l)&&
(n=!0,m+=this.items[e][k]),Math.abs(m-l)<d&&(d=Math.abs(m-l),g=this.items[e],this.direction=n?"up":"down"));(g||this.options.dropOnEmpty)&&this.currentContainer!==this.containers[p]&&(g?this._rearrange(c,g,null,!0):this._rearrange(c,null,this.containers[p].element,!0),this._trigger("change",c,this._uiHash()),this.containers[p]._trigger("change",c,this._uiHash(this)),this.currentContainer=this.containers[p],this.options.placeholder.update(this.currentContainer,this.placeholder),this.containers[p]._trigger("over",
c,this._uiHash(this)),this.containers[p].containerCache.over=1)}},_createHelper:function(b){var d=this.options;b=a.isFunction(d.helper)?a(d.helper.apply(this.element[0],[b,this.currentItem])):"clone"===d.helper?this.currentItem.clone():this.currentItem;b.parents("body").length||a("parent"!==d.appendTo?d.appendTo:this.currentItem[0].parentNode)[0].appendChild(b[0]);b[0]===this.currentItem[0]&&(this._storedCSS={width:this.currentItem[0].style.width,height:this.currentItem[0].style.height,position:this.currentItem.css("position"),
top:this.currentItem.css("top"),left:this.currentItem.css("left")});b[0].style.width&&!d.forceHelperSize||b.width(this.currentItem.width());b[0].style.height&&!d.forceHelperSize||b.height(this.currentItem.height());return b},_adjustOffsetFromHelper:function(b){"string"===typeof b&&(b=b.split(" "));a.isArray(b)&&(b={left:+b[0],top:+b[1]||0});"left"in b&&(this.offset.click.left=b.left+this.margins.left);"right"in b&&(this.offset.click.left=this.helperProportions.width-b.right+this.margins.left);"top"in
b&&(this.offset.click.top=b.top+this.margins.top);"bottom"in b&&(this.offset.click.top=this.helperProportions.height-b.bottom+this.margins.top)},_getParentOffset:function(){this.offsetParent=this.helper.offsetParent();var b=this.offsetParent.offset();"absolute"===this.cssPosition&&this.scrollParent[0]!==document&&a.contains(this.scrollParent[0],this.offsetParent[0])&&(b.left+=this.scrollParent.scrollLeft(),b.top+=this.scrollParent.scrollTop());if(this.offsetParent[0]===document.body||this.offsetParent[0].tagName&&
"html"===this.offsetParent[0].tagName.toLowerCase()&&a.ui.ie)b={top:0,left:0};return{top:b.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:b.left+(parseInt(this.offsetParent.css("borderLeftWidth"),10)||0)}},_getRelativeOffset:function(){if("relative"===this.cssPosition){var a=this.currentItem.position();return{top:a.top-(parseInt(this.helper.css("top"),10)||0)+this.scrollParent.scrollTop(),left:a.left-(parseInt(this.helper.css("left"),10)||0)+this.scrollParent.scrollLeft()}}return{top:0,
left:0}},_cacheMargins:function(){this.margins={left:parseInt(this.currentItem.css("marginLeft"),10)||0,top:parseInt(this.currentItem.css("marginTop"),10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var b,d,e;d=this.options;"parent"===d.containment&&(d.containment=this.helper[0].parentNode);if("document"===d.containment||"window"===d.containment)this.containment=[0-this.offset.relative.left-
this.offset.parent.left,0-this.offset.relative.top-this.offset.parent.top,a("document"===d.containment?document:window).width()-this.helperProportions.width-this.margins.left,(a("document"===d.containment?document:window).height()||document.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top];/^(document|window|parent)$/.test(d.containment)||(b=a(d.containment)[0],d=a(d.containment).offset(),e="hidden"!==a(b).css("overflow"),this.containment=[d.left+(parseInt(a(b).css("borderLeftWidth"),
10)||0)+(parseInt(a(b).css("paddingLeft"),10)||0)-this.margins.left,d.top+(parseInt(a(b).css("borderTopWidth"),10)||0)+(parseInt(a(b).css("paddingTop"),10)||0)-this.margins.top,d.left+(e?Math.max(b.scrollWidth,b.offsetWidth):b.offsetWidth)-(parseInt(a(b).css("borderLeftWidth"),10)||0)-(parseInt(a(b).css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left,d.top+(e?Math.max(b.scrollHeight,b.offsetHeight):b.offsetHeight)-(parseInt(a(b).css("borderTopWidth"),10)||0)-(parseInt(a(b).css("paddingBottom"),
10)||0)-this.helperProportions.height-this.margins.top])},_convertPositionTo:function(b,d){d||(d=this.position);var e="absolute"===b?1:-1,g="absolute"!==this.cssPosition||this.scrollParent[0]!==document&&a.contains(this.scrollParent[0],this.offsetParent[0])?this.scrollParent:this.offsetParent,h=/(html|body)/i.test(g[0].tagName);return{top:d.top+this.offset.relative.top*e+this.offset.parent.top*e-("fixed"===this.cssPosition?-this.scrollParent.scrollTop():h?0:g.scrollTop())*e,left:d.left+this.offset.relative.left*
e+this.offset.parent.left*e-("fixed"===this.cssPosition?-this.scrollParent.scrollLeft():h?0:g.scrollLeft())*e}},_generatePosition:function(b){var d,e,g=this.options;e=b.pageX;d=b.pageY;var h="absolute"!==this.cssPosition||this.scrollParent[0]!==document&&a.contains(this.scrollParent[0],this.offsetParent[0])?this.scrollParent:this.offsetParent,k=/(html|body)/i.test(h[0].tagName);"relative"!==this.cssPosition||this.scrollParent[0]!==document&&this.scrollParent[0]!==this.offsetParent[0]||(this.offset.relative=
this._getRelativeOffset());this.originalPosition&&(this.containment&&(b.pageX-this.offset.click.left<this.containment[0]&&(e=this.containment[0]+this.offset.click.left),b.pageY-this.offset.click.top<this.containment[1]&&(d=this.containment[1]+this.offset.click.top),b.pageX-this.offset.click.left>this.containment[2]&&(e=this.containment[2]+this.offset.click.left),b.pageY-this.offset.click.top>this.containment[3]&&(d=this.containment[3]+this.offset.click.top)),g.grid&&(d=this.originalPageY+Math.round((d-
this.originalPageY)/g.grid[1])*g.grid[1],d=this.containment?d-this.offset.click.top>=this.containment[1]&&d-this.offset.click.top<=this.containment[3]?d:d-this.offset.click.top>=this.containment[1]?d-g.grid[1]:d+g.grid[1]:d,e=this.originalPageX+Math.round((e-this.originalPageX)/g.grid[0])*g.grid[0],e=this.containment?e-this.offset.click.left>=this.containment[0]&&e-this.offset.click.left<=this.containment[2]?e:e-this.offset.click.left>=this.containment[0]?e-g.grid[0]:e+g.grid[0]:e));return{top:d-
this.offset.click.top-this.offset.relative.top-this.offset.parent.top+("fixed"===this.cssPosition?-this.scrollParent.scrollTop():k?0:h.scrollTop()),left:e-this.offset.click.left-this.offset.relative.left-this.offset.parent.left+("fixed"===this.cssPosition?-this.scrollParent.scrollLeft():k?0:h.scrollLeft())}},_rearrange:function(a,b,d,g){d?d[0].appendChild(this.placeholder[0]):b.item[0].parentNode.insertBefore(this.placeholder[0],"down"===this.direction?b.item[0]:b.item[0].nextSibling);var h=this.counter=
this.counter?++this.counter:1;this._delay(function(){h===this.counter&&this.refreshPositions(!g)})},_clear:function(a,b){this.reverting=!1;var d,g=[];!this._noFinalSort&&this.currentItem.parent().length&&this.placeholder.before(this.currentItem);this._noFinalSort=null;if(this.helper[0]===this.currentItem[0]){for(d in this._storedCSS)if("auto"===this._storedCSS[d]||"static"===this._storedCSS[d])this._storedCSS[d]="";this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper")}else this.currentItem.show();
this.fromOutside&&!b&&g.push(function(a){this._trigger("receive",a,this._uiHash(this.fromOutside))});!this.fromOutside&&this.domPosition.prev===this.currentItem.prev().not(".ui-sortable-helper")[0]&&this.domPosition.parent===this.currentItem.parent()[0]||b||g.push(function(a){this._trigger("update",a,this._uiHash())});this===this.currentContainer||b||(g.push(function(a){this._trigger("remove",a,this._uiHash())}),g.push(function(a){return function(b){a._trigger("receive",b,this._uiHash(this))}}.call(this,
this.currentContainer)),g.push(function(a){return function(b){a._trigger("update",b,this._uiHash(this))}}.call(this,this.currentContainer)));for(d=this.containers.length-1;0<=d;d--)b||g.push(function(a){return function(b){a._trigger("deactivate",b,this._uiHash(this))}}.call(this,this.containers[d])),this.containers[d].containerCache.over&&(g.push(function(a){return function(b){a._trigger("out",b,this._uiHash(this))}}.call(this,this.containers[d])),this.containers[d].containerCache.over=0);this.storedCursor&&
(this.document.find("body").css("cursor",this.storedCursor),this.storedStylesheet.remove());this._storedOpacity&&this.helper.css("opacity",this._storedOpacity);this._storedZIndex&&this.helper.css("zIndex","auto"===this._storedZIndex?"":this._storedZIndex);this.dragging=!1;if(this.cancelHelperRemoval){if(!b){this._trigger("beforeStop",a,this._uiHash());for(d=0;d<g.length;d++)g[d].call(this,a);this._trigger("stop",a,this._uiHash())}return this.fromOutside=!1}b||this._trigger("beforeStop",a,this._uiHash());
this.placeholder[0].parentNode.removeChild(this.placeholder[0]);this.helper[0]!==this.currentItem[0]&&this.helper.remove();this.helper=null;if(!b){for(d=0;d<g.length;d++)g[d].call(this,a);this._trigger("stop",a,this._uiHash())}this.fromOutside=!1;return!0},_trigger:function(){!1===a.Widget.prototype._trigger.apply(this,arguments)&&this.cancel()},_uiHash:function(b){var d=b||this;return{helper:d.helper,placeholder:d.placeholder||a([]),position:d.position,originalPosition:d.originalPosition,offset:d.positionAbs,
item:d.currentItem,sender:b?b.element:null}}})})(jQuery);(function(a){var d=a({});a.subscribe=function(){d.on.apply(d,arguments)};a.unsubscribe=function(){d.off.apply(d,arguments)};a.publish=function(){d.trigger.apply(d,arguments)}})(jQuery);(function(){var a=$.namespace("pskl.model");a.Frame=function(d,b){if(d&&b)this.width=d,this.height=b,this.pixels=a.Frame.createEmptyPixelGrid_(d,b),this.previousStates=[this.getPixels()],this.stateIndex=0;else throw"Bad arguments in pskl.model.Frame constructor : "+d+", "+b;};a.Frame.fromPixelGrid=function(a){if(a.length&&a[0].length){var b=new pskl.model.Frame(a.length,a[0].length);b.setPixels(a);return b}throw"Bad arguments in pskl.model.Frame.fromPixelGrid : "+a;};a.Frame.createEmptyPixelGrid_=
function(a,b){for(var c=[],f=0;f<a;f++){for(var e=[],g=0;g<b;g++)e.push(Constants.TRANSPARENT_COLOR);c[f]=e}return c};a.Frame.createEmptyFromFrame=function(d){return new a.Frame(d.getWidth(),d.getHeight())};a.Frame.prototype.clone=function(){var d=new a.Frame(this.width,this.height);d.setPixels(this.getPixels());return d};a.Frame.prototype.getPixels=function(){return this.clonePixels_(this.pixels)};a.Frame.prototype.setPixels=function(a){this.pixels=this.clonePixels_(a)};a.Frame.prototype.clear=function(){var d=
a.Frame.createEmptyPixelGrid_(this.getWidth(),this.getHeight());this.setPixels(d)};a.Frame.prototype.clonePixels_=function(a){for(var b=[],c=0;c<a.length;c++)b[c]=a[c].slice(0,a[c].length);return b};a.Frame.prototype.serialize=function(){return JSON.stringify(this.pixels)};a.Frame.prototype.setPixel=function(a,b,c){this.pixels[a][b]=c};a.Frame.prototype.getPixel=function(a,b){return this.pixels[a][b]};a.Frame.prototype.forEachPixel=function(a){for(var b=0;b<this.getWidth();b++)for(var c=0;c<this.getHeight();c++)a(this.getPixel(b,
c),b,c)};a.Frame.prototype.getWidth=function(){return this.width};a.Frame.prototype.getHeight=function(){return this.height};a.Frame.prototype.containsPixel=function(a,b){return 0<=a&&0<=b&&a<this.pixels.length&&b<this.pixels[0].length};a.Frame.prototype.saveState=function(){this.previousStates.length=this.stateIndex+1;this.previousStates.push(this.getPixels());this.stateIndex=this.previousStates.length-1};a.Frame.prototype.loadPreviousState=function(){0<this.stateIndex&&(this.stateIndex--,this.setPixels(this.previousStates[this.stateIndex]))};
a.Frame.prototype.loadNextState=function(){this.stateIndex<this.previousStates.length-1&&(this.stateIndex++,this.setPixels(this.previousStates[this.stateIndex]))};a.Frame.prototype.isSameSize=function(a){return this.getHeight()==a.getHeight()&&this.getWidth()==a.getWidth()}})();(function(){var a=$.namespace("pskl.model");a.Layer=function(a){if(a)this.name=a,this.frames=[];else throw"Invalid arguments in Layer constructor : 'name' is mandatory";};a.Layer.prototype.getName=function(){return this.name};a.Layer.prototype.getFrames=function(){return this.frames};a.Layer.prototype.getFrameAt=function(a){return this.frames[a]};a.Layer.prototype.addFrame=function(a){this.frames.push(a)};a.Layer.prototype.addFrameAt=function(a,b){this.frames.splice(b,0,a)};a.Layer.prototype.removeFrame=
function(a){a=this.frames.indexOf(a);this.removeFrameAt(a)};a.Layer.prototype.removeFrameAt=function(a){if(this.frames[a])this.frames.splice(a,1);else throw"Invalid index in removeFrameAt : "+a+" (size : "+this.length()+")";};a.Layer.prototype.moveFrame=function(a,b){var c=this.frames.splice(a,1)[0];this.frames.splice(b,0,c)};a.Layer.prototype.swapFramesAt=function(a,b){var c=this.frames[a],f=this.frames[b];if(c&&f)this.frames[b]=c,this.frames[a]=f;else throw console.log("frames",this.frames),console.log("fromIndex",
a,"toIndex",b),"Frame not found in moveFrameAt";};a.Layer.prototype.duplicateFrame=function(a){this.frames.indexOf(a);this.duplicateFrameAt()};a.Layer.prototype.duplicateFrameAt=function(a){var b=this.frames[a];if(b)b=b.clone(),this.addFrameAt(b,a);else throw"Frame not found in duplicateFrameAt";};a.Layer.prototype.length=function(){return this.frames.length}})();(function(){var a=$.namespace("pskl.model");a.Piskel=function(a,b){if(a&&b)this.layers=[],this.width=a,this.height=b;else throw"Missing arguments in Piskel constructor : "+Array.prototype.join.call(arguments,",");};a.Piskel.prototype.getLayers=function(){return this.layers};a.Piskel.prototype.getHeight=function(){return this.height};a.Piskel.prototype.getWidth=function(){return this.width};a.Piskel.prototype.getLayers=function(){return this.layers};a.Piskel.prototype.getLayerAt=function(a){return this.layers[a]};
a.Piskel.prototype.getLayersByName=function(a){return this.layers.filter(function(b){return b.getName()==a})};a.Piskel.prototype.addLayer=function(a){this.layers.push(a)};a.Piskel.prototype.moveLayerUp=function(a){var b=this.layers.indexOf(a);-1<b&&b<this.layers.length-1&&(this.layers[b]=this.layers[b+1],this.layers[b+1]=a)};a.Piskel.prototype.moveLayerDown=function(a){var b=this.layers.indexOf(a);0<b&&(this.layers[b]=this.layers[b-1],this.layers[b-1]=a)};a.Piskel.prototype.removeLayer=function(a){a=
this.layers.indexOf(a);-1!=a&&this.layers.splice(a,1)};a.Piskel.prototype.removeLayerAt=function(a){this.layers.splice(a,1)}})();(function(){var a=$.namespace("pskl.rendering");a.CanvasRenderer=function(a,b){this.frame=a;this.dpi=b;this.transparentColor_="white"};a.CanvasRenderer.prototype.drawTransparentAs=function(a){this.transparentColor_=a};a.CanvasRenderer.prototype.render=function(){for(var a=this.createCanvas_().getContext("2d"),b=0,c=this.frame.getWidth();b<c;b++)for(var f=0,e=this.frame.getHeight();f<e;f++){var g=this.frame.getPixel(b,f);this.renderPixel_(g,b,f,a)}return a};a.CanvasRenderer.prototype.renderPixel_=
function(a,b,c,f){a==Constants.TRANSPARENT_COLOR&&(a=this.transparentColor_);f.fillStyle=a;f.fillRect(b*this.dpi,c*this.dpi,this.dpi,this.dpi)};a.CanvasRenderer.prototype.createCanvas_=function(){var a=this.frame.getWidth()*this.dpi,b=this.frame.getHeight()*this.dpi;return pskl.CanvasUtils.createCanvas(a,b)}})();(function(){var a=$.namespace("pskl.rendering");a.DrawingLoop=function(){this.requestAnimationFrame=this.getRequestAnimationFrameShim_();this.isRunning=!1;this.previousTime=0;this.callbacks=[]};a.DrawingLoop.prototype.addCallback=function(a,b,c){a={fn:a,scope:b,args:c};this.callbacks.push(a);return a};a.DrawingLoop.prototype.removeCallback=function(a){a=this.callbacks.indexOf(a);-1!=a&&this.callbacks.splice(a,1)};a.DrawingLoop.prototype.start=function(){this.isRunning=!0;this.loop_()};a.DrawingLoop.prototype.loop_=
function(){var a=Date.now();this.executeCallbacks_(a-this.previousTime);this.previousTime=a;this.requestAnimationFrame.call(window,this.loop_.bind(this))};a.DrawingLoop.prototype.executeCallbacks_=function(a){for(var b=0;b<this.callbacks.length;b++){var c=this.callbacks[b];c.fn.call(c.scope,a,c.args)}};a.DrawingLoop.prototype.stop=function(){this.isRunning=!1};a.DrawingLoop.prototype.getRequestAnimationFrameShim_=function(){return window.requestAnimationFrame||window.mozRequestAnimationFrame||window.webkitRequestAnimationFrame||
window.msRequestAnimationFrame||function(a){window.setTimeout(a,1E3/60)}}})();(function(){var a=$.namespace("pskl.rendering");a.FrameRenderer=function(a,b,c){this.defaultRenderingOptions={supportGridRendering:!1};b=$.extend(!0,{},this.defaultRenderingOptions,b);if(void 0===a)throw"Bad FrameRenderer initialization. <container> undefined.";if(isNaN(b.dpi))throw"Bad FrameRenderer initialization. <dpi> not well defined.";this.container=a;this.dpi=b.dpi;this.supportGridRendering=b.supportGridRendering;this.classes=c||[];this.classes.push("canvas");this.canvas=null;this.enableGrid(pskl.UserSettings.get(pskl.UserSettings.SHOW_GRID));
this.canvasConfigDirty=!0;this.updateBackgroundClass_(pskl.UserSettings.get(pskl.UserSettings.CANVAS_BACKGROUND));$.subscribe(Events.USER_SETTINGS_CHANGED,$.proxy(this.onUserSettingsChange_,this))};a.FrameRenderer.prototype.setDPI=function(a){this.dpi=a;this.canvasConfigDirty=!0};a.FrameRenderer.prototype.onUserSettingsChange_=function(a,b,c){b==pskl.UserSettings.SHOW_GRID?this.enableGrid(c):b==pskl.UserSettings.CANVAS_BACKGROUND&&this.updateBackgroundClass_(c)};a.FrameRenderer.prototype.updateBackgroundClass_=
function(a){var b=this.container.data("current-background-class");b&&this.container.removeClass(b);this.container.addClass(a);this.container.data("current-background-class",a)};a.FrameRenderer.prototype.enableGrid=function(a){this.gridStrokeWidth=a&&this.supportGridRendering?Constants.GRID_STROKE_WIDTH:0;this.canvasConfigDirty=!0};a.FrameRenderer.prototype.render=function(a){if(a){this.clear();for(var b=this.getCanvas_(a).getContext("2d"),c=0,f=a.getWidth();c<f;c++)for(var e=0,g=a.getHeight();e<g;e++){var h=
a.getPixel(c,e);this.renderPixel_(h,c,e,b)}this.lastRenderedFrame=a}};a.FrameRenderer.prototype.renderPixel_=function(a,b,c,f){a!=Constants.TRANSPARENT_COLOR&&(f.fillStyle=a,f.fillRect(this.getFramePos_(b),this.getFramePos_(c),this.dpi,this.dpi))};a.FrameRenderer.prototype.clear=function(){this.canvas&&this.canvas.getContext("2d").clearRect(0,0,this.canvas.width,this.canvas.height)};a.FrameRenderer.prototype.convertPixelCoordinatesIntoSpriteCoordinate=function(a){var b=this.dpi+this.gridStrokeWidth;
return{col:(a.x-a.x%b)/b,row:(a.y-a.y%b)/b}};a.FrameRenderer.prototype.getFramePos_=function(a){return a*this.dpi+(a-1)*this.gridStrokeWidth};a.FrameRenderer.prototype.getCanvas_=function(a){if(this.canvasConfigDirty){$(this.canvas).remove();var b=a.getWidth();a=a.getHeight();b=pskl.CanvasUtils.createCanvas(b*this.dpi+this.gridStrokeWidth*(b-1),a*this.dpi+this.gridStrokeWidth*(a-1),this.classes);this.container.append(b);this.canvas=b;this.canvasConfigDirty=!1}return this.canvas}})();(function(){var a=$.namespace("pskl.rendering");a.SpritesheetRenderer=function(a){this.piskelController=a};a.SpritesheetRenderer.prototype.render=function(){for(var a=this.createCanvas_(),b=0;b<this.piskelController.getFrameCount();b++){var c=this.piskelController.getFrameAt(b);this.drawFrameInCanvas_(c,a,b*this.piskelController.getWidth(),0)}return a};a.SpritesheetRenderer.prototype.drawFrameInCanvas_=function(a,b,c,f){b=b.getContext("2d");for(var e=0,g=a.getWidth();e<g;e++)for(var h=0,k=a.getHeight();h<
k;h++){var l=a.getPixel(e,h);l!=Constants.TRANSPARENT_COLOR&&(b.fillStyle=l,b.fillRect(e+c,h+f,1,1))}};a.SpritesheetRenderer.prototype.createCanvas_=function(){var a=this.piskelController.getFrameCount();if(0<a){var a=a*this.piskelController.getWidth(),b=this.piskelController.getHeight();return pskl.CanvasUtils.createCanvas(a,b)}throw"Cannot render empty Spritesheet";}})();(function(){var a=$.namespace("pskl.selection");a.BaseSelection=function(){this.reset()};a.BaseSelection.prototype.reset=function(){this.pixels=[];this.hasPastedContent=!1};a.BaseSelection.prototype.move=function(a,b){for(var c,f=[],e=0,g=this.pixels.length;e<g;e++)c=this.pixels[e],c.col+=a,c.row+=b,f.push(c);this.pixels=f};a.BaseSelection.prototype.fillSelectionFromFrame=function(a){for(var b,c=0,f=this.pixels.length;c<f;c++)b=this.pixels[c],b.copiedColor=a.getPixel(b.col,b.row);this.hasPastedContent=
!0}})();(function(){var a=$.namespace("pskl.selection");a.RectangularSelection=function(a,b,c,f){this.pixels=pskl.PixelUtils.getRectanglePixels(a,b,c,f)};pskl.utils.inherit(a.RectangularSelection,a.BaseSelection)})();(function(){var a=$.namespace("pskl.selection");a.SelectionManager=function(a){this.piskelController=a;this.currentSelection=null};a.SelectionManager.prototype.init=function(){$.subscribe(Events.SELECTION_CREATED,$.proxy(this.onSelectionCreated_,this));$.subscribe(Events.SELECTION_DISMISSED,$.proxy(this.onSelectionDismissed_,this));$.subscribe(Events.SELECTION_MOVE_REQUEST,$.proxy(this.onSelectionMoved_,this));$.subscribe(Events.PASTE,$.proxy(this.onPaste_,this));$.subscribe(Events.COPY,$.proxy(this.onCopy_,
this));$.subscribe(Events.CUT,$.proxy(this.onCut_,this));$.subscribe(Events.TOOL_SELECTED,$.proxy(this.onToolSelected_,this))};a.SelectionManager.prototype.cleanSelection_=function(){this.currentSelection&&this.currentSelection.reset()};a.SelectionManager.prototype.onToolSelected_=function(a,b){b instanceof pskl.drawingtools.BaseSelect||this.cleanSelection_()};a.SelectionManager.prototype.onSelectionDismissed_=function(a){this.cleanSelection_()};a.SelectionManager.prototype.onCut_=function(a){if(this.currentSelection){this.currentSelection.fillSelectionFromFrame(this.piskelController.getCurrentFrame());
a=this.currentSelection.pixels;for(var b=this.piskelController.getCurrentFrame(),c=0,f=a.length;c<f;c++)try{b.setPixel(a[c].col,a[c].row,Constants.TRANSPARENT_COLOR)}catch(e){}}else throw"Bad state for CUT callback in SelectionManager";};a.SelectionManager.prototype.onPaste_=function(a){if(this.currentSelection&&this.currentSelection.hasPastedContent){a=this.currentSelection.pixels;for(var b=this.piskelController.getCurrentFrame(),c=0,f=a.length;c<f;c++)try{b.setPixel(a[c].col,a[c].row,a[c].copiedColor)}catch(e){}}};
a.SelectionManager.prototype.onCopy_=function(a){if(this.currentSelection&&this.piskelController.getCurrentFrame())this.currentSelection.fillSelectionFromFrame(this.piskelController.getCurrentFrame());else throw"Bad state for CUT callback in SelectionManager";};a.SelectionManager.prototype.onSelectionCreated_=function(a,b){if(b)this.currentSelection=b;else throw"No selection set in SelectionManager";};a.SelectionManager.prototype.onSelectionMoved_=function(a,b,c){if(this.currentSelection)this.currentSelection.move(b,
c);else throw"Bad state: No currentSelection set when trying to move it in SelectionManager";}})();(function(){var a=$.namespace("pskl.selection");a.ShapeSelection=function(a){this.pixels=a};pskl.utils.inherit(a.ShapeSelection,a.BaseSelection)})();(function(){var a=$.namespace("pskl.service");a.HistoryService=function(a){this.piskelController=a};a.HistoryService.prototype.init=function(){$.subscribe(Events.TOOL_RELEASED,this.saveState.bind(this));$.subscribe(Events.UNDO,this.undo.bind(this));$.subscribe(Events.REDO,this.redo.bind(this))};a.HistoryService.prototype.saveState=function(){this.piskelController.getCurrentFrame().saveState()};a.HistoryService.prototype.undo=function(){this.piskelController.getCurrentFrame().loadPreviousState();$.publish(Events.PISKEL_RESET)};
a.HistoryService.prototype.redo=function(){this.piskelController.getCurrentFrame().loadNextState();$.publish(Events.PISKEL_RESET)}})();(function(){var a=$.namespace("pskl.service");a.ImageUploadService=function(){};a.ImageUploadService.prototype.init=function(){};a.ImageUploadService.prototype.upload=function(a,b,c){var f=new XMLHttpRequest,e=new FormData;e.append("data",a);f.open("POST",Constants.IMAGE_SERVICE_UPLOAD_URL,!0);f.onload=function(a){200==this.status?b(Constants.IMAGE_SERVICE_GET_URL+this.responseText):c()};f.send(e)}})();(function(){var a=$.namespace("pskl.service");a.KeyboardEventService=function(){};a.KeyboardEventService.prototype.KeyboardActions_={ctrl:{z:Events.UNDO,y:Events.REDO,x:Events.CUT,c:Events.COPY,v:Events.PASTE}};a.KeyboardEventService.prototype.CharCodeToKeyCodeMap_={90:"z",89:"y",88:"x",67:"c",86:"v"};a.KeyboardEventService.prototype.onKeyUp_=function(a){var b=!1;-1!=navigator.appVersion.indexOf("Mac")&&(b=!0);if(b?a.metaKey:a.ctrlKey)if(b=this.CharCodeToKeyCodeMap_[a.which])if(b=this.KeyboardActions_.ctrl[b])return $.publish(b),
a.preventDefault(),!1};a.KeyboardEventService.prototype.init=function(){$(document.body).keydown($.proxy(this.onKeyUp_,this))}})();(function(){var a=$.namespace("pskl.service");a.LocalStorageService=function(a){if(void 0===a)throw"Bad LocalStorageService initialization: <undefined piskelController>";this.piskelController=a;this.localStorageThrottler_=null};a.LocalStorageService.prototype.init=function(a){$.subscribe(Events.LOCALSTORAGE_REQUEST,$.proxy(this.persistToLocalStorageRequest_,this))};a.LocalStorageService.prototype.persistToLocalStorageRequest_=function(){null!==this.localStorageThrottler_&&window.clearTimeout(this.localStorageThrottler_);
this.localStorageThrottler_=window.setTimeout($.proxy(function(){this.persistToLocalStorage_();this.localStorageThrottler_=null},this),1E3)};a.LocalStorageService.prototype.persistToLocalStorage_=function(){console.log("[LocalStorage service]: Snapshot stored");window.localStorage.snapShot=this.piskelController.serialize()};a.LocalStorageService.prototype.restoreFromLocalStorage_=function(){this.piskelController.deserialize(window.localStorage.snapShot);this.piskelController.setCurrentFrameIndex(0)};
a.LocalStorageService.prototype.cleanLocalStorage_=function(){console.log("[LocalStorage service]: Snapshot removed");delete window.localStorage.snapShot};a.LocalStorageService.prototype.displayRestoreNotification=function(){window.localStorage&&window.localStorage.snapShot&&$.publish(Events.SHOW_NOTIFICATION,[{content:"Non saved version found. <a href='#' class='localstorage-restore onclick='pskl.app.restoreFromLocalStorage()'>reload</a> or <a href='#' class='localstorage-discard' onclick='pskl.app.cleanLocalStorage()'>discard</a>",
behavior:$.proxy(function(a){a=$(a);a.click($.proxy(function(a){a=$(a.target);a.hasClass("localstorage-restore")?this.restoreFromLocalStorage_():a.hasClass("localstorage-discard")&&this.cleanLocalStorage_();$.publish(Events.HIDE_NOTIFICATION)},this))},this)}])}})();(function(){$.namespace("pskl").CanvasUtils={createCanvas:function(a,d,b){var c=document.createElement("canvas");c.setAttribute("width",a);c.setAttribute("height",d);"string"==typeof b&&(b=[b]);if(Array.isArray(b))for(a=0;a<b.length;a++)c.classList.add(b[a]);return c}}})();(function(){$.namespace("pskl.utils").FileUtils={readFile:function(a,d){var b=new FileReader;b.onload=function(a){d(a.target.result)};b.readAsDataURL(a)}}})();(function(){$.namespace("pskl.utils").FrameUtils={merge:function(a){var d=null;if(a.length){d=a[0].clone();d.getWidth();d.getHeight();for(var b=1;b<a.length;b++)pskl.utils.FrameUtils.mergeFrames_(d,a[b])}return d},mergeFrames_:function(a,d){d.forEachPixel(function(b,c,d){b!=Constants.TRANSPARENT_COLOR&&a.setPixel(c,d,b)})},createFromImage:function(a){var d=a.width,b=a.height,c=pskl.CanvasUtils.createCanvas(d,b).getContext("2d");c.drawImage(a,0,0,d,b,0,0,d,b);d=c.getImageData(0,0,d,b).data;b=[];for(c=
0;c<a.width;++c){b[c]=[];for(var f=0;f<a.height;++f){var e=4*(f*a.width+c),g=d[e],h=d[e+1],k=d[e+2];b[c][f]=125>d[e+3]?"TRANSPARENT":this.rgbToHex(g,h,k)}}return b},rgbToHex:function(a,d,b){return"#"+this.componentToHex(a)+this.componentToHex(d)+this.componentToHex(b)},componentToHex:function(a){a=a.toString(16);return 1==a.length?"0"+a:a}}})();(function(){$.namespace("pskl.utils").ImageResizer={resize:function(a,d,b,c){var f=pskl.CanvasUtils.createCanvas(d,b),e=f.getContext("2d");e.save();c||this.disableSmoothingOnContext(e);e.translate(f.width/2,f.height/2);e.scale(d/a.width,b/a.height);e.drawImage(a,-a.width/2,-a.height/2);e.restore();return f},disableSmoothingOnContext:function(a){a.imageSmoothingEnabled=!1;a.mozImageSmoothingEnabled=!1;a.oImageSmoothingEnabled=!1;a.webkitImageSmoothingEnabled=!1;a.msImageSmoothingEnabled=!1}}})();(function(){$.namespace("pskl").PixelUtils={getRectanglePixels:function(a,d,b,c){a=this.getOrderedRectangleCoordinates(a,d,b,c);d=[];for(b=a.x0;b<=a.x1;b++)for(c=a.y0;c<=a.y1;c++)d.push({col:b,row:c});return d},getBoundRectanglePixels:function(a,d,b,c){a=this.getOrderedRectangleCoordinates(a,d,b,c);d=[];for(b=a.x0;b<=a.x1;b++)d.push({col:b,row:a.y0}),d.push({col:b,row:a.y1});for(b=a.y0;b<=a.y1;b++)d.push({col:a.x0,row:b}),d.push({col:a.x1,row:b});return d},getOrderedRectangleCoordinates:function(a,
d,b,c){return{x0:Math.min(a,b),y0:Math.min(d,c),x1:Math.max(a,b),y1:Math.max(d,c)}},getSimilarConnectedPixelsFromFrame:function(a,d,b){a=a.clone();return this.paintSimilarConnectedPixelsFromFrame(a,d,b,"sdfsdfsdf")},paintSimilarConnectedPixelsFromFrame:function(a,d,b,c){var f=[],e=[],g=[-1,0,1,0],h=[0,1,0,-1],k;try{k=a.getPixel(d,b)}catch(l){}if(k!=c){e.push({col:d,row:b});d=0;for(b=a.getWidth()*a.getHeight();0<e.length;){d++;var m=e.pop();a.setPixel(m.col,m.row,c);f.push({col:m.col,row:m.row});for(var n=
0;4>n;n++){var q=m.col+h[n],p=m.row+g[n];try{a.containsPixel(q,p)&&a.getPixel(q,p)==k&&e.push({col:q,row:p})}catch(r){}}if(d>10*b){console.log("loop breaker called");break}}return f}},calculateDPIForContainer:function(a,d,b){return this.calculateDPI(a.height(),a.width(),d,b)},calculateDPI:function(a,d,b,c){return Math.min(Math.floor(a/b),Math.floor(d/c))}}})();(function(){$.namespace("pskl.utils").Serializer={serializePiskel:function(a){var d=a.getLayers().map(function(a){return pskl.utils.Serializer.serializeLayer(a)});return JSON.stringify({modelVersion:Constants.MODEL_VERSION,piskel:{height:a.getHeight(),width:a.getWidth(),layers:d}})},serializeLayer:function(a){var d=a.getFrames().map(function(a){return a.serialize()});return JSON.stringify({name:a.getName(),frames:d})},deserializePiskel:function(a){a=JSON.parse(a);return this.createPiskelFromData(a)},
createPiskel:function(a){var d=null;a.modelVersion==Constants.MODEL_VERSION?(a=a.piskel,d=new pskl.model.Piskel(a.width,a.height),a.layers.forEach(function(a){a=pskl.utils.Serializer.deserializeLayer(a);d.addLayer(a)})):d=pskl.utils.Serializer.backwardDeserializer_(a);return d},deserializeLayer:function(a){a=JSON.parse(a);var d=new pskl.model.Layer(a.name);a.frames.forEach(function(a){a=pskl.utils.Serializer.deserializeFrame(a);d.addFrame(a)});return d},deserializeFrame:function(a){a=JSON.parse(a);
return pskl.model.Frame.fromPixelGrid(a)},backwardDeserializer_:function(a){var d=new pskl.model.Layer("Layer 1");a.forEach(function(a){d.addFrame(pskl.model.Frame.fromPixelGrid(a))});a=d.getFrameAt(0).getWidth();var b=d.getFrameAt(0).getHeight();a=new pskl.model.Piskel(a,b);a.addLayer(d);return a}}})();(function(){$.namespace("pskl.utils").Template={get:function(a){var d=document.getElementById(a);if(d)return d.innerHTML;console.error("Could not find template for id :",a)},createFromHTML:function(a){var d=document.createElement("div");d.innerHTML=a;return d.children[0]},replace:function(a,d){for(var b in d)d.hasOwnProperty(b)&&(a=a.replace(RegExp("\\{\\{"+b+"\\}\\}","g"),d[b]));return a}}})();(function(){$.namespace("pskl").UserSettings={SHOW_GRID:"SHOW_GRID",CANVAS_BACKGROUND:"CANVAS_BACKGROUND",KEY_TO_DEFAULT_VALUE_MAP_:{SHOW_GRID:!1,CANVAS_BACKGROUND:"medium-canvas-background"},cache_:{},get:function(a){this.checkKeyValidity_(a);a in this.cache_||(this.cache_[a]=this.readFromLocalStorage_(a)||this.readFromDefaults_(a));return this.cache_[a]},set:function(a,d){this.checkKeyValidity_(a);this.cache_[a]=d;this.writeToLocalStorage_(a,d);$.publish(Events.USER_SETTINGS_CHANGED,[a,d])},readFromLocalStorage_:function(a){a=
window.localStorage[a];"undefined"!=typeof a&&(a=JSON.parse(a));return a},writeToLocalStorage_:function(a,d){window.localStorage[a]=JSON.stringify(d)},readFromDefaults_:function(a){return this.KEY_TO_DEFAULT_VALUE_MAP_[a]},checkKeyValidity_:function(a){a in this.KEY_TO_DEFAULT_VALUE_MAP_||console.log("UserSettings key <"+a+"> not find in supported keys.")}}})();jQuery.namespace=function(){var a=arguments,d=null,b,c,f;for(b=0;b<a.length;b+=1)for(f=a[b].split("."),d=window,c=0;c<f.length;c+=1)d[f[c]]=d[f[c]]||{},d=d[f[c]];return d};"function"!==typeof Function.prototype.bind&&(Function.prototype.bind=function(a){var d=this;return function(){return d.apply(a,arguments)}});
(function(){var a=$.namespace("pskl.utils");a.rgbToHex=function(a,b,c){if(255<a||255<b||255<c)throw"Invalid color component";return(a<<16|b<<8|c).toString(16)};a.inherit=function(a,b){a.prototype=Object.create(b.prototype);a.prototype.constructor=a;a.prototype.superclass=b.prototype}})();(function(){var a=function(a,b){var c=window.document.createElement("script");c.setAttribute("src",a);c.setAttribute("onload",b);window.document.body.appendChild(c)};if(-1!=window.location.href.indexOf("debug")){window.exports={};var d=0;window.loadNextScript=function(){d==window.exports.scripts.length?(pskl.app.init(),delete window.exports,delete window.loadDebugScripts,delete window.done):(a(window.exports.scripts[d],"loadNextScript()"),d++)};a("piskel-script-list.js","loadNextScript()")}else{var b;
b=-1!=window.location.href.indexOf("pack")?"build/piskel-packaged.js":"build/piskel-packaged-min.js";var c=window.setInterval(function(){0===document.querySelectorAll("._ctl").length?(window.clearInterval(c),a(b,"pskl.app.init()")):console.log("waiting for templates to load ....")},100)}})();exports.scripts="js/lib/jquery-1.8.0.js js/lib/jquery-ui-1.10.3.custom.js js/lib/pubsub.js js/lib/bootstrap/bootstrap.js js/lib/gif/gif.worker.js js/lib/gif/gif.js js/Constants.js js/Events.js js/utils/core.js js/utils/CanvasUtils.js js/utils/FileUtils.js js/utils/FrameUtils.js js/utils/ImageResizer.js js/utils/PixelUtils.js js/utils/Serializer.js js/utils/Template.js js/utils/UserSettings.js js/lib/jsColor_1_4_0/jscolor.js js/rendering/DrawingLoop.js js/model/Frame.js js/model/Layer.js js/model/Piskel.js js/selection/SelectionManager.js js/selection/BaseSelection.js js/selection/RectangularSelection.js js/selection/ShapeSelection.js js/rendering/CanvasRenderer.js js/rendering/FrameRenderer.js js/rendering/SpritesheetRenderer.js js/controller/PiskelController.js js/controller/DrawingController.js js/controller/PreviewFilmController.js js/controller/LayersListController.js js/controller/AnimatedPreviewController.js js/controller/ToolController.js js/controller/PaletteController.js js/controller/NotificationController.js js/controller/settings/ApplicationSettingsController.js js/controller/settings/GifExportController.js js/controller/settings/ImportController.js js/controller/settings/SettingsController.js js/service/LocalStorageService.js js/service/HistoryService.js js/service/KeyboardEventService.js js/service/ImageUploadService.js js/drawingtools/BaseTool.js js/drawingtools/SimplePen.js js/drawingtools/VerticalMirrorPen.js js/drawingtools/Eraser.js js/drawingtools/Stroke.js js/drawingtools/PaintBucket.js js/drawingtools/Rectangle.js js/drawingtools/Circle.js js/drawingtools/Move.js js/drawingtools/selectiontools/BaseSelect.js js/drawingtools/selectiontools/RectangleSelect.js js/drawingtools/selectiontools/ShapeSelect.js js/drawingtools/ColorPicker.js js/app.js".split(" ");

View File

@ -0,0 +1,8 @@
.tooltip.in {
opacity: 0.95;
filter: alpha(opacity=95);
}
.tooltip {
line-height: 20px;
}

112
preview/css/bootstrap/bootstrap.css vendored Normal file
View File

@ -0,0 +1,112 @@
/*!
* Bootstrap v2.1.1
*
* Copyright 2012 Twitter, Inc
* Licensed under the Apache License v2.0
* http://www.apache.org/licenses/LICENSE-2.0
*
* Designed and built with all the love in the world @twitter by @mdo and @fat.
*/
.clearfix {
*zoom: 1;
}
.clearfix:before,
.clearfix:after {
display: table;
content: "";
line-height: 0;
}
.clearfix:after {
clear: both;
}
.hide-text {
font: 0/0 a;
color: transparent;
text-shadow: none;
background-color: transparent;
border: 0;
}
.input-block-level {
display: block;
width: 100%;
min-height: 30px;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
.tooltip {
position: absolute;
z-index: 1030;
display: block;
visibility: visible;
padding: 5px;
font-size: 11px;
opacity: 0;
filter: alpha(opacity=0);
}
.tooltip.in {
opacity: 0.8;
filter: alpha(opacity=80);
}
.tooltip.top {
margin-top: -3px;
}
.tooltip.right {
margin-left: 3px;
}
.tooltip.bottom {
margin-top: 3px;
}
.tooltip.left {
margin-left: -3px;
}
.tooltip-inner {
max-width: 200px;
padding: 3px 8px;
color: #ffffff;
text-align: center;
text-decoration: none;
background-color: #000000;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
}
.tooltip-arrow {
position: absolute;
width: 0;
height: 0;
border-color: transparent;
border-style: solid;
}
.tooltip.top .tooltip-arrow {
bottom: 0;
left: 50%;
margin-left: -5px;
border-width: 5px 5px 0;
border-top-color: #000000;
}
.tooltip.right .tooltip-arrow {
top: 50%;
left: 0;
margin-top: -5px;
border-width: 5px 5px 5px 0;
border-right-color: #000000;
}
.tooltip.left .tooltip-arrow {
top: 50%;
right: 0;
margin-top: -5px;
border-width: 5px 0 5px 5px;
border-left-color: #000000;
}
.tooltip.bottom .tooltip-arrow {
top: 0;
left: 50%;
margin-left: -5px;
border-width: 0 5px 5px;
border-bottom-color: #000000;
}
.tooltip {
line-height: 20px;
}

View File

@ -0,0 +1 @@
Bootstrap custom build containing only the tooltip component

69
preview/css/forms.css Normal file
View File

@ -0,0 +1,69 @@
.textfield {
background : black;
border : 1px solid #888;
border-radius : 2px;
padding : 3px 10px;
color : white;
}
.textfield[disabled=disabled] {
background : #3a3a3a;
}
.textfield-small {
width : 50px;
}
.button {
height: 24px;
box-sizing: border-box;
background-color: #3f3f3f;
border: 1px solid #333;
border-top-color: #666;
border-bottom-color: #222;
cursor: pointer;
text-decoration: none;
color: white;
text-shadow: 0px -1px 0 black;
font-weight: bold;
font-size: 1rem;
text-align: center;
transition: background-color 0.2s linear;
}
.button:hover {
text-decoration: none;
background-color: #484848;
color: gold;
}
.button-primary {
background-color: rgb(255,215,0); /* gold */
border-color: rgb(179, 164, 0);
border-top-color: white;
border-bottom-color: rgb(151, 133, 0);
color: black;
text-shadow: 0px 1px 0 #fff;
}
.button-primary:hover {
background-color: rgb(255,235,20);
color: #333;
}
.button[disabled=disabled],
.button[disabled=disabled]:hover {
cursor:default;
background-color: #aaa;
color: #777;
text-shadow: 0px 1px 0 #bbb;
border-color: #666;
border-top-color: #999;
border-bottom-color: #555;
}

View File

@ -0,0 +1,185 @@
.preview-list-wrapper {
position: relative;
height: 100%;
overflow: hidden;
}
.preview-list-scroller {
overflow-y: scroll;
overflow-x: hidden;
height: 100%;
}
.top-overflow,
.bottom-overflow {
height: 20px;
position: absolute;
left: 0;
right: 12px;
-webkit-transition: all 500ms ease-out;
-moz-transition: all 500ms ease-out;
-ms-transition: all 500ms ease-out;
-o-transition: all 500ms ease-out;
transition: all 500ms ease-out;
background-image: linear-gradient(45deg, rgba(0,0,0, 0.8) 25%, transparent 25%, transparent 75%, rgba(0,0,0, 0.8) 75%, rgba(0,0,0, 0.8)),
linear-gradient(-45deg, rgba(0,0,0, 0.8) 25%, transparent 25%, transparent 75%, rgba(0,0,0, 0.8) 75%, rgba(0,0,0, 0.8));
background-size: 29px 45px;
background-repeat: repeat-x;
background-position-x: 3px;
z-index: 10;
}
.top-overflow {
top: -20px;
}
.bottom-overflow {
bottom: -20px;
background-position-x: 0;
background-position-y: -23px;
}
.top-overflow-visible .top-overflow {
top: 0;
}
.bottom-overflow-visible .bottom-overflow {
bottom: 0;
}
.preview-list {
list-style-type: none;
padding-right: 7px;
}
.add-frame-action {
border: #888 solid 4px;
font-size: 13px;
color: #888;
cursor: pointer;
padding: 6px 0;
border-radius: 4px;
margin-top: 8px;
background-image: url(../img/plus.png);
background-repeat: no-repeat;
background-position: 3px 7px;
background-size: 26px 26px;
text-indent: 18px;
background-color: #222;
}
.add-frame-action .label {
width: 80px;
margin: 0 auto;
}
.add-frame-action:hover {
border-color: gold;
}
.preview-tile {
position: relative;
border: #444 3px solid;
border-radius: 3px;
margin: 5px 0;
}
.preview-tile:first-child {
margin-top: 0;
}
.preview-tile:hover {
border: #999 3px solid;
}
.preview-tile .tile-overlay {
z-index: 10;
position: absolute;
background-color: rgba(100, 100, 100, 0.6);
opacity: 0;
height: 30px;
width: 30px;
border: none;
}
.preview-tile:hover .tile-overlay {
opacity: 1.0;
}
.preview-tile .tile-overlay.tile-count {
display: block;
opacity: 1.0;
border-bottom-right-radius: 3px;
top: 0;
left: 0;
font-size: 14px;
line-height: 30px;
color: white;
font-weight: bold;
text-align: center;
cursor: default;
}
.preview-tile .tile-overlay.delete-frame-action {
background-image: url(../img/garbage.png);
background-repeat: no-repeat;
top: 0;
right: 0;
border-bottom-left-radius: 3px;
height: 30px;
width: 30px;
background-position: 5px 5px;
background-size: 20px;
cursor: pointer;
}
.preview-tile .tile-overlay.duplicate-frame-action {
bottom: 0;
right: 0;
border-bottom-left-radius: 3px;
background-image: url(../img/duplicate.png);
background-repeat: no-repeat;
background-position: 5px 5px;
background-size: 20px;
cursor: pointer;
}
.preview-tile .tile-overlay.dnd-action {
bottom: 0;
left: 0;
border-top-right-radius: 3px;
background-image: url(../img/dragndrop.png);
background-repeat: no-repeat;
background-position: 5px 5px;
background-size: 20px;
cursor: move;
}
.preview-tile.selected {
border-color: gold;
}
.preview-tile.selected:after {
content: " ";
position: absolute;
top: 38px;
right: -15px;
border: transparent 4px solid;
border-left-color: gold;
border-width: 6px;
border-style: solid;
}
/**
* Drag n drop styles.
*/
.preview-tile-drop-proxy {
border: 3px dashed gold;
height: 90px;
border-radius: 9px;
background-color: rgba(255, 215,0, 0.2);
}

36
preview/css/reset.css Normal file
View File

@ -0,0 +1,36 @@
html, body {
height : 100%; width: 100%;
margin : 0;
overflow: hidden;
cursor : default;
font-family: arial;
font-size: 11px;
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
ul, li {
margin : 0;
padding : 0;
list-style-type: none;
}
/* Force apparition of scrollbars on leopard */
::-webkit-scrollbar {
-webkit-appearance: none;
width: 6px;
}
::-webkit-scrollbar-thumb {
border-radius: 2px;
background-color: #666;
}
::-webkit-scrollbar-track {
background-color: rgba(50, 50, 50, 0.4);
}

167
preview/css/settings.css Normal file
View File

@ -0,0 +1,167 @@
/** Righty sticky drawer expanded state. */
.right-sticky-section.sticky-section {
right: 0;
width: 47px;
-webkit-transition: all 200ms ease-out;
-moz-transition: all 200ms ease-out;
-ms-transition: all 200ms ease-out;
transition: all 200ms ease-out;
}
.right-sticky-section.expanded {
right: 280px;
}
.right-sticky-section .tool-icon {
float: right;
margin-right: 0;
}
.drawer-content {
overflow: hidden;
background-color: #444;
height: 550px;
max-height: 100%;
width: 280px;
border-top-left-radius: 4px;
border-bottom-left-radius: 4px;
}
.right-sticky-section.expanded .tool-icon {
padding-right: 1px;
}
.right-sticky-section .tool-icon.has-expanded-drawer {
position: relative;
background-color: #444;
margin-right: 0;
padding-right: 2px;
}
.settings-section {
margin: 10px 20px;
font-size: 12px;
font-weight: bold;
color: #ccc;
text-shadow: 1px 1px #000;
}
.settings-title {
margin-top: 20px;
margin-bottom: 10px;
text-transform: uppercase;
border-bottom: 1px #aaa solid;
padding-bottom: 5px;
}
.settings-item {}
.background-picker-wrapper {
overflow: hidden;
padding: 10px 5px 20px 5px;
}
.background-picker {
cursor: pointer;
float: left;
height: 35px;
width: 35px;
background-color: transparent;
margin-right: 15px;
padding: 1px;
position: relative;
}
.background-picker:after {
content: " ";
position: absolute;
top: -2px;
right: -2px;
bottom: -2px;
left: -2px;
}
.background-picker:hover:after {
border: #eee 1px solid;
}
.background-picker.selected:after {
border: gold 1px solid;
}
/* Gif Export Setting panel*/
.gif-upload-button,
.gif-render-button {
/*float : right;*/
margin-top : 10px;
margin-right : 10px;
}
.gif-export-radio-group {
margin:10px 0;
}
.gif-export-preview {
margin-top:20px;
max-width:240px;
position:relative;
}
.preview-upload-ongoing:before{
content: "Upload ongoing ...";
position: absolute;
display: block;
height: 100%;
width: 100%;
text-align: center;
padding-top: 45%;
box-sizing:border-box;
-moz-box-sizing:border-box;
background: rgba(0,0,0,0.5);
color: white;
}
/* Import panel */
.import-section {
margin: 15px 0;
}
.import-section-disabled {
color : #888;
}
.import-section-preview {
display : inline-block;
height : 60px;
width: 60px;
border : 1px dashed #999;
border-radius: 3px;
margin-left : 10px;
}
.import-resize-field {
width: 30px;
margin: 0px 8px;
text-align: right;
}
.file-input-button {
margin: 0px 8px;
border-radius: 2px;
}
.file-input-status {
display: inline-block;
width: 130px;
overflow: hidden;
height: 1.5rem;
word-break : break-all;
vertical-align: middle;
font-style: italic;
font-weight: normal;
text-shadow: none;
}

294
preview/css/style.css Normal file
View File

@ -0,0 +1,294 @@
body {
background: radial-gradient(circle, #000, #373737);
/* 16/06/2013 : -webkit still needed for
safari, safari mobile and android browser and chrome for android
cf http://caniuse.com/css-gradients */
background: -webkit-radial-gradient(circle, #000, #373737);
}
/**
* Application layout
*/
.main-wrapper {
position: absolute;
top: 5px;
right: 0;
bottom: 5px;
left: 0;
}
.column-wrapper {
text-align: center;
font-size: 0;
position: absolute;
left: 100px; /* Reserve room for tools on the left edge of the screen. */
top: 0;
right: 50px; /* Reserve room for actions on the right edge of the screen. */
bottom: 0;
}
.piskel-name-container {
overflow:hidden;
position:fixed;
top:10px;
left:10px;
color:white;
font-family:Tahoma;
z-index: 10000;
}
.piskel-name-container #piskel-name {
border :none;
color : lightgrey;
background: transparent;
font-size:16pt;
}
.column {
display: inline-block;
}
.left-column {
vertical-align: top;
height: 100%;
margin-right: 7px;
}
.main-column {
height: 100%;
position: relative;
}
.right-column {
vertical-align: top;
margin-left: 10px;
}
.drawing-canvas-container {
font-size: 0;
}
.sticky-section {
position: fixed;
top: 0;
bottom: 0;
z-index: 1000;
}
.sticky-section .sticky-section-wrap {
display: table;
height: 100%;
}
.sticky-section .vertical-centerer {
display: table-cell;
vertical-align: middle;
}
.left-sticky-section.sticky-section {
left: 0;
max-width: 100px;
}
.left-sticky-section .tool-icon {
float: left;
}
/**
* Canvases layout
*/
.canvas {
position: relative;
z-index: 1;
}
.canvas-container {
position: relative;
display: block;
}
.canvas-container .canvas-background {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
}
.light-picker-background,
.light-canvas-background .canvas-background {
background: url(../img/canvas_background/light_canvas_background.png) repeat;
}
.medium-picker-background,
.medium-canvas-background .canvas-background {
background: url(../img/canvas_background/medium_canvas_background.png) repeat;
}
.lowcont-medium-picker-background,
.lowcont-medium-canvas-background .canvas-background {
background: url(../img/canvas_background/lowcont_medium_canvas_background.png) repeat;
}
.lowcont-dark-picker-background,
.lowcont-dark-canvas-background .canvas-background {
background: url(../img/canvas_background/lowcont_dark_canvas_background.png) repeat;
}
.layers-canvas {
opacity: 0.2;
}
.canvas.canvas-overlay,
.canvas.layers-canvas {
position: absolute;
top: 0;
left: 0;
}
/**
* Z-indexes should match the drawing area canvas superposition order :
* - 1 : draw layers below current layer
* - 2 : draw current layer
* - 3 : draw layers above current layer
* - 4 : draw the tools overlay
*/
.canvas.layers-below-canvas {z-index: 7;}
.canvas.drawing-canvas {z-index: 8;}
.canvas.layers-above-canvas {z-index: 9;}
.canvas.canvas-overlay {z-index: 10;}
/**
* Animated preview styles.
*/
.preview-container {
border : 0px Solid black;
border-radius:5px 0px 0px 5px;
box-shadow : 0px 0px 2px rgba(0,0,0,0.2);
font-size: 0;
}
.preview-container canvas {
border : 0px Solid transparent;
}
.display-fps {
float: left;
color: #aaa;
font-size: 12px;
min-width: 55px;
vertical-align: bottom;
line-height: 24px;
}
.range-fps {
overflow: hidden;
}
/**
* Layers container
*/
.layers-list-container {
border: 4px solid #888;
font-size: medium;
color: white;
text-align: left;
border-radius: 4px;
margin-top: 10px;
}
.layers-title {
padding: 8px;
margin: 0;
font-size: 15px;
background: #222;
background-image: url('../img/layers.svg');
background-size: 22px;
background-repeat: no-repeat;
background-position: 97%;
}
.layers-list {
font-size : 12px;
}
.layer-item {
height:24px;
line-height: 24px;
padding : 0 10px;
border-top: 1px solid #444;
cursor : pointer;
}
.layer-item:hover {
background : #222;
}
.current-layer-item,
.current-layer-item:hover {
background : #333;
color: gold;
}
.layers-button-container {
overflow : hidden;
}
.layers-button-arrow {
font-family : 'Lucida Grande', Calibri;
padding : 2px 6px 0 6px;
}
.layers-button {
margin: 0;
width: 25%;
float : left;
}
/* @override */
.button.layers-button {
border-left-width: 0;
}
.layers-button:last-child {
border-right-width: 0;
}
/**
* User messages
*/
.user-message {
position: absolute;
right: 0;
bottom: 0;
background-color: #F9EDBE;
padding: 10px 47px;
border-top-left-radius: 7px;
color: #222;
border: #F0C36D 1px solid;
border-right: 0;
border-bottom: 0;
font-weight: bold;
font-size: 13px;
z-index: 10000;
max-width: 300px;
}
.user-message .close {
position: absolute;
top: 6px;
right: 17px;
color: gray;
font-weight: bold;
cursor: pointer;
font-size: 18px;
}
.user-message .close:hover {
color: black;
}

229
preview/css/tools.css Normal file
View File

@ -0,0 +1,229 @@
.tools-wrapper,
.options-wrapper,
.palette-wrapper {
float: left;
}
.tool-icon {
cursor : pointer;
width: 46px;
height: 46px;
margin: 1px;
background-color: #3a3a3a;
background-repeat: no-repeat;
background-position: 12px 12px;
background-size: 24px 24px;
}
.tool-icon.selected {
cursor: default;
background-color: #444;
border: 1px gold solid;
margin: 0;
}
.tool-icon:hover {
background-color: #444;
}
/*
* Tool icons:
*/
.tool-icon.tool-pen {
background-image: url(../img/tools/pen.png);
}
.tool-icon.tool-vertical-mirror-pen {
background-image: url(../img/tools/mirror.png);
background-position: 0px 10px;
background-size: 38px 27px;
}
.tool-icon.tool-paint-bucket {
background-image: url(../img/tools/paintbucket.png);
}
.tool-icon.tool-eraser {
background-image: url(../img/tools/eraser.png);
}
.tool-icon.tool-stroke {
background-image: url(../img/tools/stroke.png);
}
.tool-icon.tool-rectangle {
background-image: url(../img/tools/rectangle.png);
background-position: 12px 14px;
background-size: 24px 20px;
}
.tool-icon.tool-circle {
background-image: url(../img/tools/circle.png);
}
.tool-icon.tool-move {
background-image: url(../img/tools/hand.png);
background-position: 12px 12px;
background-size: 24px 24px;
}
.tool-icon.tool-rectangle-select {
background-image: url(../img/tools/rectangle_selection.png);
background-position: 12px 14px;
background-size: 24px 20px;
}
.tool-icon.tool-shape-select {
background-image: url(../img/tools/magicwand.png);
}
.tool-icon.tool-colorpicker {
background-image: url(../img/tools/eyedropper.png);
background-position: 12px 12px;
background-size: 23px 23px;
}
/*
* Tool cursors:
*/
.tool-paint-bucket .drawing-canvas-container:hover {
cursor: url(../img/icons/paint-bucket.png) 12 12, pointer;
}
.tool-vertical-mirror-pen .drawing-canvas-container:hover {
cursor: url(../img/icons/vertical-mirror-pen.png) 5 15, pointer;
}
.tool-pen .drawing-canvas-container:hover {
cursor: url(../img/icons/pen.png) 0 15, pointer;
}
.tool-eraser .drawing-canvas-container:hover {
cursor: url(../img/icons/eraser.png) 0 15, pointer;
}
.tool-stroke .drawing-canvas-container:hover {
cursor: url(../img/icons/pen.png) 0 15, pointer;
}
.tool-rectangle .drawing-canvas-container:hover {
cursor: url(../img/icons/rectangle.png) 0 15, pointer;
}
.tool-circle .drawing-canvas-container:hover {
cursor: url(../img/icons/circle.png) 2 15, pointer;
}
.tool-move .drawing-canvas-container:hover {
cursor: url(../img/icons/hand.png) 15 15, pointer;
}
.tool-rectangle-select .drawing-canvas-container:hover {
cursor: url(../img/icons/select.png) 15 15, pointer;
}
.tool-shape-select .drawing-canvas-container:hover {
cursor: url(../img/icons/wand.png) 15 15, pointer;
}
.tool-colorpicker .drawing-canvas-container:hover {
cursor: url(../img/icons/dropper.png) 0 15, pointer;
}
.tool-color-picker input {
width: 16px;
height: 16px;
text-indent: -10000px;
border: 1px solid black;
background: white;
cursor: pointer;
position : relative;
top: 10px;
margin-left: 2px;
}
.tool-color-picker .secondary-color-picker {
top : 18px;
margin-left: 0;
}
.palette-color[data-color=TRANSPARENT] {
position: relative;
top: 10px;
left: 10px;
background-color: white;
height : 16px;
width : 16px;
border: 2px solid #000;
background-size: 16px 16px;
background-position: 0 0;
background-image: -webkit-gradient(
linear,
left top,
right bottom,
color-stop(0, #fff),
color-stop(0.45, #fff),
color-stop(0.5, #ff0000),
color-stop(0.55, #fff),
color-stop(1, #fff)
);
background-image: -moz-linear-gradient(
left top,
#fff 0%,
#fff 45%,
#f00 50%,
#fff 55%,
#fff 100%
);
}
/*
* Framesheet level actions:
*/
.tool-icon.gallery-icon {
background-image: url(../img/gallery.png);
background-position: 3px 3px;
background-size: 39px 39px;
}
.tool-icon.save-icon {
background-image: url(../img/save.png);
background-position: 6px 6px;
background-size: 36px 36px;
}
.tool-icon.gear-icon {
background-image: url(../img/gear.png);
background-position: 6px 7px;
background-size: 32px 32px;
}
.tool-icon.upload-cloud-icon {
background-image: url(../img/cloud_export.png);
background-position: 4px 0px;
background-size: 36px 36px;
position: relative;
}
.tool-icon.import-icon {
background-image: url(../img/import-icon.png);
background-position: 10px 5px;
background-size: 26px;
position: relative;
}
.upload-cloud-icon .label {
position: absolute;
left: 0;
bottom: 4px;
right: 0;
font-size: 11px;
text-transform: uppercase;
color: #fff;
text-align: center;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 223 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 431 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 278 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 418 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 594 B

BIN
preview/img/dragndrop.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 532 B

BIN
preview/img/duplicate.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 634 B

BIN
preview/img/gallery.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 305 B

BIN
preview/img/garbage.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

BIN
preview/img/gear.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 789 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 761 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 209 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 543 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 656 B

BIN
preview/img/icons/hand.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 672 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 557 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 707 B

BIN
preview/img/icons/pen.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 450 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 660 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 450 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 603 B

BIN
preview/img/icons/wand.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 570 B

BIN
preview/img/import-icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 720 B

62
preview/img/layers.svg Normal file
View File

@ -0,0 +1,62 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
version="1.1"
width="90"
height="87.886002"
id="svg3024">
<defs
id="defs3026" />
<metadata
id="metadata3029">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
transform="translate(-266.63206,-597.79217)"
id="layer1">
<g
transform="translate(262.14286,465.21932)"
id="Captions" />
<g
id="g3825" />
<g
transform="translate(-0.51079959,126.51585)"
id="layer1-9"
style="fill:#ffffff;fill-opacity:1">
<g
transform="translate(262.14286,465.21932)"
id="Captions-4"
style="fill:#ffffff;fill-opacity:1" />
<g
id="g3825-8"
style="fill:#ffffff;fill-opacity:1">
<path
d="m 357.14286,556.72132 c 0,1.346 -1.097,2.441 -2.442,2.441 h -60.239 c -1.347,0 -2.441,-1.096 -2.441,-2.441 v -4.555 h 50.241 c 4.346,0 7.884,-3.539 7.884,-7.885 v -48.128 h 4.555 c 1.346,0 2.442,1.096 2.442,2.441 v 58.127 z"
id="path2989-8"
style="fill:#ffffff;fill-opacity:1" />
<path
d="m 279.58086,544.28132 v -4.555 h 6.997 2.722 2.721 37.803 c 4.346,0 7.884,-3.537 7.884,-7.883 v -35.69 -2.721 -2.721 -6.998 h 4.555 c 1.346,0 2.442,1.096 2.442,2.443 v 4.555 2.721 2.721 48.129 c 0,1.346 -1.097,2.443 -2.442,2.443 h -50.242 -2.721 -2.722 -4.555 c -1.347,0 -2.442,-1.098 -2.442,-2.444 z"
id="path2991-2"
style="fill:#ffffff;fill-opacity:1" />
<path
d="m 329.82386,471.27632 h -60.239 c -1.347,0 -2.442,1.093 -2.442,2.441 v 58.127 c 0,1.344 1.095,2.441 2.442,2.441 h 4.555 2.721 2.721 6.997 2.722 2.721 37.803 c 1.345,0 2.441,-1.098 2.441,-2.441 v -35.691 -2.721 -2.721 -6.998 -2.721 -2.721 -4.554 c -10e-4,-1.348 -1.098,-2.441 -2.442,-2.441 z"
id="path2993-4"
style="fill:#ffffff;fill-opacity:1" />
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

BIN
preview/img/plus.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 271 B

BIN
preview/img/save.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 395 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 909 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1010 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
preview/img/tools/hand.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
preview/img/tools/pen.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 253 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 245 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 376 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 372 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

61
preview/index.html Normal file
View File

@ -0,0 +1,61 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>Piskel</title>
<meta name="description" content="">
<meta name="author" content="Julian Descottes">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" type="text/css" href="css/reset.css">
<link rel="stylesheet" type="text/css" href="css/style.css">
<link rel="stylesheet" type="text/css" href="css/forms.css">
<link rel="stylesheet" type="text/css" href="css/settings.css">
<link rel="stylesheet" type="text/css" href="css/tools.css">
<link rel="stylesheet" type="text/css" href="css/bootstrap/bootstrap.css">
<link rel="stylesheet" type="text/css" href="css/bootstrap/bootstrap-tooltip-custom.css">
<link rel="stylesheet" type="text/css" href="css/preview-film-section.css">
</head>
<body>
<script type="text/javascript" src="js/lib/iframeLoader.js"></script>
<div class="piskel-name-container">
<input readonly id="piskel-name" type="text" value=""/>
</div>
<div id="main-wrapper" class="main-wrapper">
<iframe src="templates/drawing-tools.html" onload="iframeloader.onLoad(event)" data-iframe-loader="display"></iframe>
<div id="column-wrapper" class="column-wrapper">
<div class='column left-column'>
<iframe src="templates/frames-list.html" onload="iframeloader.onLoad(event)" data-iframe-loader="display"></iframe>
</div>
<div class='column main-column'>
<div id="drawing-canvas-container" class="drawing-canvas-container canvas-container">
<div class="canvas-background"></div>
</div>
</div>
<div class="column right-column">
<iframe src="templates/preview.html" onload="iframeloader.onLoad(event)" data-iframe-loader="display"></iframe>
<iframe src="templates/layers-list.html" onload="iframeloader.onLoad(event)" data-iframe-loader="display"></iframe>
</div>
</div>
<div id="application-action-section" data-pskl-controller="settings" class="sticky-section right-sticky-section">
<div class="sticky-section-wrap">
<iframe src="templates/settings.html" onload="iframeloader.onLoad(event)" data-iframe-loader="display"></iframe>
<div class="drawer vertical-centerer">
<div class="drawer-content" id="drawer-container">
<iframe src="templates/settings/application.html" onload="iframeloader.onLoad(event)" data-iframe-loader="store"></iframe>
<iframe src="templates/settings/export-gif.html" onload="iframeloader.onLoad(event)" data-iframe-loader="store"></iframe>
<iframe src="templates/settings/import.html" onload="iframeloader.onLoad(event)" data-iframe-loader="store"></iframe>
</div>
</div>
</div>
</div>
</div>
<script type="text/javascript" src="piskel-boot.js"></script>
</body>
</html>

52
preview/js/Constants.js Normal file
View File

@ -0,0 +1,52 @@
// TODO(grosbouddha): put under pskl namespace.
var Constants = {
DEFAULT : {
HEIGHT : 32,
WIDTH : 32,
FPS : 12
},
MODEL_VERSION : 1,
MAX_HEIGHT : 128,
MAX_WIDTH : 128,
PREVIEW_FILM_SIZE : 120,
DEFAULT_PEN_COLOR : '#000000',
TRANSPARENT_COLOR : 'TRANSPARENT',
/*
* Fake semi-transparent color used to highlight transparent
* strokes and rectangles:
*/
SELECTION_TRANSPARENT_COLOR: 'rgba(255, 255, 255, 0.6)',
/*
* When a tool is hovering the drawing canvas, we highlight the eventual
* pixel target with this color:
*/
TOOL_TARGET_HIGHLIGHT_COLOR: 'rgba(255, 255, 255, 0.2)',
/*
* Default entry point for piskel web service:
*/
STATIC : {
URL : {
SAVE : 'http://3.piskel-app.appspot.com/store',
GET : 'http://3.piskel-app.appspot.com/get'
}
},
APPENGINE : {
URL : {
SAVE : 'save'
}
},
IMAGE_SERVICE_UPLOAD_URL : 'http://screenletstore.appspot.com/__/upload',
IMAGE_SERVICE_GET_URL : 'http://screenletstore.appspot.com/img/',
GRID_STROKE_WIDTH: 1,
LEFT_BUTTON : 'left_button_1',
RIGHT_BUTTON : 'right_button_2'
};

67
preview/js/Events.js Normal file
View File

@ -0,0 +1,67 @@
// TODO(grosbouddha): put under pskl namespace.
var Events = {
TOOL_SELECTED : "TOOL_SELECTED",
TOOL_RELEASED : "TOOL_RELEASED",
PRIMARY_COLOR_SELECTED: "PRIMARY_COLOR_SELECTED",
PRIMARY_COLOR_UPDATED: "PRIMARY_COLOR_UPDATED",
SECONDARY_COLOR_SELECTED: "SECONDARY_COLOR_SELECTED",
SECONDARY_COLOR_UPDATED: "SECONDARY_COLOR_UPDATED",
/**
* When this event is emitted, a request is sent to the localstorage
* Service to save the current framesheet. The storage service
* may not immediately store data (internal throttling of requests).
*/
LOCALSTORAGE_REQUEST: "LOCALSTORAGE_REQUEST",
CANVAS_RIGHT_CLICKED: "CANVAS_RIGHT_CLICKED",
/**
* Event to request a refresh of the display.
* A bit overkill but, it's just workaround in our current drawing system.
* TODO: Remove or rework when redraw system is refactored.
*/
REFRESH: "REFRESH",
/**
* Temporary event to bind the redraw of right preview film to the canvas.
* This redraw should be driven by model updates.
* TODO(vincz): Remove.
*/
REDRAW_PREVIEWFILM: "REDRAW_PREVIEWFILM",
/**
* Fired each time a user setting change.
* The payload will be:
* 1st argument: Name of the settings
* 2nd argument: New value
*/
USER_SETTINGS_CHANGED: "USER_SETTINGS_CHANGED",
/* Listened to by SettingsController */
CLOSE_SETTINGS_DRAWER : "CLOSE_SETTINGS_DRAWER",
/**
* The framesheet was reseted and is now probably drastically different.
* Number of frames, content of frames, color used for the palette may have changed.
*/
PISKEL_RESET: "PISKEL_RESET",
FRAME_SIZE_CHANGED : "FRAME_SIZE_CHANGED",
CURRENT_FRAME_SET: "CURRENT_FRAME_SET",
SELECTION_CREATED: "SELECTION_CREATED",
SELECTION_MOVE_REQUEST: "SELECTION_MOVE_REQUEST",
SELECTION_DISMISSED: "SELECTION_DISMISSED",
SHOW_NOTIFICATION: "SHOW_NOTIFICATION",
HIDE_NOTIFICATION: "HIDE_NOTIFICATION",
UNDO: "UNDO",
REDO: "REDO",
CUT: "CUT",
COPY: "COPY",
PASTE: "PASTE"
};

262
preview/js/app.js Normal file
View File

@ -0,0 +1,262 @@
/**
* @require Constants
* @require Events
*/
(function () {
var ns = $.namespace("pskl");
/**
* Main application controller
*/
ns.app = {
init : function () {
var size = this.readSizeFromURL_();
var piskel = new pskl.model.Piskel(size.width, size.height);
var layer = new pskl.model.Layer("Layer 1");
var frame = new pskl.model.Frame(size.width, size.height);
layer.addFrame(frame);
piskel.addLayer(layer);
this.piskelController = new pskl.controller.PiskelController(piskel);
this.drawingController = new pskl.controller.DrawingController(this.piskelController, $('#drawing-canvas-container'));
this.drawingController.init();
this.animationController = new pskl.controller.AnimatedPreviewController(this.piskelController, $('#preview-canvas-container'));
this.animationController.init();
this.previewsController = new pskl.controller.PreviewFilmController(this.piskelController, $('#preview-list'));
this.previewsController.init();
this.layersListController = new pskl.controller.LayersListController(this.piskelController);
this.layersListController.init();
this.settingsController = new pskl.controller.settings.SettingsController(this.piskelController);
this.settingsController.init();
this.selectionManager = new pskl.selection.SelectionManager(this.piskelController);
this.selectionManager.init();
this.historyService = new pskl.service.HistoryService(this.piskelController);
this.historyService.init();
this.keyboardEventService = new pskl.service.KeyboardEventService();
this.keyboardEventService.init();
this.notificationController = new pskl.controller.NotificationController();
this.notificationController.init();
this.localStorageService = new pskl.service.LocalStorageService(this.piskelController);
this.localStorageService.init();
this.imageUploadService = new pskl.service.ImageUploadService();
this.imageUploadService.init();
this.toolController = new pskl.controller.ToolController();
this.toolController.init();
this.paletteController = new pskl.controller.PaletteController();
this.paletteController.init();
var drawingLoop = new pskl.rendering.DrawingLoop();
drawingLoop.addCallback(this.render, this);
drawingLoop.start();
this.initBootstrapTooltips_();
/**
* True when piskel is running in static mode (no back end needed).
* When started from APP Engine, appEngineToken_ (Boolean) should be set on window.pskl
*/
this.isStaticVersion = !pskl.appEngineToken_;
if (this.isStaticVersion) {
this.finishInitStatic_();
} else {
this.finishInitAppEngine_();
}
},
finishInitStatic_ : function () {
var framesheetId = this.readFramesheetIdFromURL_();
if (framesheetId) {
$.publish(Events.SHOW_NOTIFICATION, [{
"content" : "Loading animation with id : [" + framesheetId + "]"
}]);
this.loadFramesheetFromService(framesheetId);
} else {
this.localStorageService.displayRestoreNotification();
}
},
finishInitAppEngine_ : function () {
if (pskl.framesheetData_ && pskl.framesheetData_.content) {
var piskel = pskl.utils.Serializer.createPiskel(pskl.framesheetData_.content);
pskl.app.piskelController.setPiskel(piskel);
pskl.app.animationController.setFPS(pskl.framesheetData_.fps);
}
},
initBootstrapTooltips_ : function () {
$('body').tooltip({
selector: '[rel=tooltip]'
});
},
render : function (delta) {
this.drawingController.render(delta);
this.animationController.render(delta);
this.previewsController.render(delta);
},
readSizeFromURL_ : function () {
var sizeParam = this.readUrlParameter_("size"),
size;
// parameter expected as size=64x48 => size=widthxheight
var parts = sizeParam.split("x");
if (parts && parts.length == 2 && !isNaN(parts[0]) && !isNaN(parts[1])) {
var width = parseInt(parts[0], 10),
height = parseInt(parts[1], 10);
size = {
height : Math.min(height, Constants.MAX_HEIGHT),
width : Math.min(width, Constants.MAX_WIDTH)
};
} else {
size = {
height : Constants.DEFAULT.HEIGHT,
width : Constants.DEFAULT.WIDTH
};
}
return size;
},
readFramesheetIdFromURL_ : function () {
return this.readUrlParameter_("frameId");
},
readUrlParameter_ : function (paramName) {
var searchString = window.location.search.substring(1),
i, val, params = searchString.split("&");
for (i = 0; i < params.length; i++) {
val = params[i].split("=");
if (val[0] == paramName) {
return window.unescape(val[1]);
}
}
return "";
},
loadFramesheetFromService : function (frameId) {
var xhr = new XMLHttpRequest();
xhr.open('GET', Constants.STATIC.URL.GET + '?l=' + frameId, true);
xhr.responseType = 'text';
xhr.onload = function (e) {
var res = JSON.parse(this.responseText);
var piskel = pskl.utils.Serializer.createPiskel(res.framesheet);
pskl.app.piskelController.setPiskel(piskel);
pskl.app.animationController.setFPS(res.fps);
$.publish(Events.HIDE_NOTIFICATION);
};
xhr.onerror = function () {
$.publish(Events.HIDE_NOTIFICATION);
};
xhr.send();
},
storeSheet : function (event) {
if (this.isStaticVersion) {
this.storeSheetStatic_();
} else {
this.storeSheetAppEngine_();
}
if(event) {
event.stopPropagation();
event.preventDefault();
}
return false;
},
storeSheetStatic_ : function () {
var xhr = new XMLHttpRequest();
var formData = new FormData();
formData.append('framesheet_content', this.piskelController.serialize());
formData.append('fps_speed', $('#preview-fps').val());
xhr.open('POST', Constants.STATIC.URL.SAVE, true);
xhr.onload = function(e) {
if (this.status == 200) {
var baseUrl = window.location.href.replace(window.location.search, "");
window.location.href = baseUrl + "?frameId=" + this.responseText;
} else {
this.onerror(e);
}
};
xhr.onerror = function(e) {
$.publish(Events.SHOW_NOTIFICATION, [{"content": "Saving failed ("+this.status+")"}]);
};
xhr.send(formData);
},
storeSheetAppEngine_ : function () {
var xhr = new XMLHttpRequest();
var formData = new FormData();
formData.append('framesheet_content', this.piskelController.serialize());
formData.append('fps_speed', $('#preview-fps').val());
formData.append('name', $('#piskel-name').val());
formData.append('frames', this.piskelController.getFrameCount());
formData.append('preview', this.getFirstFrameAsPng());
formData.append('framesheet', this.getFramesheetAsPng());
xhr.open('POST', Constants.APPENGINE.URL.SAVE, true);
xhr.onload = function(e) {
if (this.status == 200) {
$.publish(Events.SHOW_NOTIFICATION, [{"content": "Successfully saved !"}]);
} else {
this.onerror(e);
}
};
xhr.onerror = function(e) {
$.publish(Events.SHOW_NOTIFICATION, [{"content": "Saving failed ("+this.status+")"}]);
};
xhr.send(formData);
},
getFirstFrameAsPng : function () {
var firstFrame = this.piskelController.getFrameAt(0);
var frameRenderer = new pskl.rendering.CanvasRenderer(firstFrame, 1);
frameRenderer.drawTransparentAs('rgba(0,0,0,0)');
var firstFrameCanvas = frameRenderer.render().canvas;
return firstFrameCanvas.toDataURL("image/png");
},
getFramesheetAsPng : function () {
var renderer = new pskl.rendering.SpritesheetRenderer(this.piskelController);
var framesheetCanvas = renderer.render();
return framesheetCanvas.toDataURL("image/png");
},
uploadAsSpritesheetPNG : function () {
var imageData = this.getFramesheetAsPng();
this.imageUploadService.upload(imageData, this.openWindow.bind(this));
},
openWindow : function (url) {
var options = [
"dialog=yes", "scrollbars=no", "status=no",
"width=" + this.piskelController.getWidth() * this.piskelController.getFrameCount(),
"height=" + this.piskelController.getHeight()
].join(",");
window.open(url, "piskel-export", options);
}
};
})();

View File

@ -0,0 +1,71 @@
(function () {
var ns = $.namespace("pskl.controller");
ns.AnimatedPreviewController = function (piskelController, container, dpi) {
this.piskelController = piskelController;
this.container = container;
this.elapsedTime = 0;
this.currentIndex = 0;
this.setFPS(Constants.DEFAULT.FPS);
var renderingOptions = {
"dpi": this.calculateDPI_()
};
this.renderer = new pskl.rendering.FrameRenderer(this.container, renderingOptions);
$.subscribe(Events.FRAME_SIZE_CHANGED, this.updateDPI_.bind(this));
};
ns.AnimatedPreviewController.prototype.init = function () {
// the oninput event won't work on IE10 unfortunately, but at least will provide a
// consistent behavior across all other browsers that support the input type range
// see https://bugzilla.mozilla.org/show_bug.cgi?id=853670
$("#preview-fps")[0].addEventListener('change', this.onFPSSliderChange.bind(this));
};
ns.AnimatedPreviewController.prototype.onFPSSliderChange = function (evt) {
this.setFPS(parseInt($("#preview-fps")[0].value, 10));
};
ns.AnimatedPreviewController.prototype.setFPS = function (fps) {
this.fps = fps;
$("#preview-fps").val(this.fps);
$("#display-fps").html(this.fps + " FPS");
};
ns.AnimatedPreviewController.prototype.getFPS = function () {
return this.fps;
};
ns.AnimatedPreviewController.prototype.render = function (delta) {
this.elapsedTime += delta;
var index = Math.floor(this.elapsedTime / (1000/this.fps));
if (index != this.currentIndex) {
this.currentIndex = index;
if (!this.piskelController.hasFrameAt(this.currentIndex)) {
this.currentIndex = 0;
this.elapsedTime = 0;
}
this.renderer.render(this.piskelController.getFrameAt(this.currentIndex));
}
};
/**
* Calculate the preview DPI depending on the framesheet size
*/
ns.AnimatedPreviewController.prototype.calculateDPI_ = function () {
var previewSize = 200,
framePixelHeight = this.piskelController.getCurrentFrame().getHeight(),
framePixelWidth = this.piskelController.getCurrentFrame().getWidth();
// TODO (julz) : should have a utility to get a Size from framesheet easily (what about empty framesheets though ?)
//return pskl.PixelUtils.calculateDPIForContainer($(".preview-container"), framePixelHeight, framePixelWidth);
return pskl.PixelUtils.calculateDPI(previewSize, previewSize, framePixelHeight, framePixelWidth);
};
ns.AnimatedPreviewController.prototype.updateDPI_ = function () {
this.dpi = this.calculateDPI_();
this.renderer.setDPI(this.dpi);
};
})();

View File

@ -0,0 +1,353 @@
(function () {
var ns = $.namespace("pskl.controller");
ns.DrawingController = function (piskelController, container) {
/**
* @public
*/
this.piskelController = piskelController;
/**
* @public
*/
this.overlayFrame = pskl.model.Frame.createEmptyFromFrame(piskelController.getCurrentFrame());
/**
* @private
*/
this.container = container;
this.dpi = this.calculateDPI_();
// TODO(vincz): Store user prefs in a localstorage string ?
var renderingOptions = {
"dpi": this.dpi,
"supportGridRendering" : true
};
this.overlayRenderer = new pskl.rendering.FrameRenderer(this.container, renderingOptions, ["canvas-overlay"]);
this.renderer = new pskl.rendering.FrameRenderer(this.container, renderingOptions, ["drawing-canvas"]);
this.layersBelowRenderer = new pskl.rendering.FrameRenderer(this.container, renderingOptions, ["layers-canvas", "layers-below-canvas"]);
this.layersAboveRenderer = new pskl.rendering.FrameRenderer(this.container, renderingOptions, ["layers-canvas", "layers-above-canvas"]);
// State of drawing controller:
this.isClicked = false;
this.isRightClicked = false;
this.previousMousemoveTime = 0;
this.currentToolBehavior = null;
this.primaryColor = Constants.DEFAULT_PEN_COLOR;
this.secondaryColor = Constants.TRANSPARENT_COLOR;
};
ns.DrawingController.prototype.init = function () {
this.initMouseBehavior();
$.subscribe(Events.TOOL_SELECTED, $.proxy(function(evt, toolBehavior) {
console.log("Tool selected: ", toolBehavior);
this.currentToolBehavior = toolBehavior;
this.overlayFrame.clear();
}, this));
/**
* TODO(grosbouddha): Primary/secondary color state are kept in this general controller.
* Find a better place to store that. Perhaps PaletteController?
*/
$.subscribe(Events.PRIMARY_COLOR_SELECTED, $.proxy(function(evt, color) {
console.log("Primary color selected: ", color);
this.primaryColor = color;
$.publish(Events.PRIMARY_COLOR_UPDATED, [color]);
}, this));
$.subscribe(Events.SECONDARY_COLOR_SELECTED, $.proxy(function(evt, color) {
console.log("Secondary color selected: ", color);
this.secondaryColor = color;
$.publish(Events.SECONDARY_COLOR_UPDATED, [color]);
}, this));
$(window).resize($.proxy(this.startDPIUpdateTimer_, this));
$.subscribe(Events.USER_SETTINGS_CHANGED, $.proxy(this.onUserSettingsChange_, this));
$.subscribe(Events.FRAME_SIZE_CHANGED, $.proxy(this.updateDPI_, this));
this.updateDPI_();
};
ns.DrawingController.prototype.initMouseBehavior = function() {
var body = $('body');
this.container.mousedown($.proxy(this.onMousedown_, this));
this.container.mousemove($.proxy(this.onMousemove_, this));
body.mouseup($.proxy(this.onMouseup_, this));
// Deactivate right click:
body.contextmenu(this.onCanvasContextMenu_);
};
ns.DrawingController.prototype.startDPIUpdateTimer_ = function () {
if (this.dpiUpdateTimer) {
window.clearInterval(this.dpiUpdateTimer);
}
this.dpiUpdateTimer = window.setTimeout($.proxy(this.updateDPI_, this), 200);
},
/**
* @private
*/
ns.DrawingController.prototype.onUserSettingsChange_ = function (evt, settingsName, settingsValue) {
if(settingsName == pskl.UserSettings.SHOW_GRID) {
this.updateDPI_();
}
},
/**
* @private
*/
ns.DrawingController.prototype.onMousedown_ = function (event) {
this.isClicked = true;
if(event.button == 2) { // right click
this.isRightClicked = true;
$.publish(Events.CANVAS_RIGHT_CLICKED);
}
var coords = this.getSpriteCoordinates(event);
this.currentToolBehavior.applyToolAt(
coords.col, coords.row,
this.getCurrentColor_(),
this.piskelController.getCurrentFrame(),
this.overlayFrame,
this.wrapEvtInfo_(event)
);
$.publish(Events.LOCALSTORAGE_REQUEST);
};
/**
* @private
*/
ns.DrawingController.prototype.onMousemove_ = function (event) {
var currentTime = new Date().getTime();
// Throttling of the mousemove event:
if ((currentTime - this.previousMousemoveTime) > 40 ) {
var coords = this.getSpriteCoordinates(event);
if (this.isClicked) {
this.currentToolBehavior.moveToolAt(
coords.col, coords.row,
this.getCurrentColor_(),
this.piskelController.getCurrentFrame(),
this.overlayFrame,
this.wrapEvtInfo_(event)
);
// TODO(vincz): Find a way to move that to the model instead of being at the interaction level.
// Eg when drawing, it may make sense to have it here. However for a non drawing tool,
// you don't need to draw anything when mousemoving and you request useless localStorage.
$.publish(Events.LOCALSTORAGE_REQUEST);
} else {
this.currentToolBehavior.moveUnactiveToolAt(
coords.col, coords.row,
this.getCurrentColor_(),
this.piskelController.getCurrentFrame(),
this.overlayFrame,
this.wrapEvtInfo_(event)
);
}
this.previousMousemoveTime = currentTime;
}
};
/**
* @private
*/
ns.DrawingController.prototype.onMouseup_ = function (event) {
if(this.isClicked || this.isRightClicked) {
// A mouse button was clicked on the drawing canvas before this mouseup event,
// the user was probably drawing on the canvas.
// Note: The mousemove movement (and the mouseup) may end up outside
// of the drawing canvas.
this.isClicked = false;
this.isRightClicked = false;
var coords = this.getSpriteCoordinates(event);
//console.log("mousemove: col: " + spriteCoordinate.col + " - row: " + spriteCoordinate.row);
this.currentToolBehavior.releaseToolAt(
coords.col, coords.row,
this.getCurrentColor_(),
this.piskelController.getCurrentFrame(),
this.overlayFrame,
this.wrapEvtInfo_(event)
);
$.publish(Events.TOOL_RELEASED);
}
};
/**
* @private
*/
ns.DrawingController.prototype.wrapEvtInfo_ = function (event) {
var evtInfo = {};
if (event.button === 0) {
evtInfo.button = Constants.LEFT_BUTTON;
} else if (event.button == 2) {
evtInfo.button = Constants.RIGHT_BUTTON;
}
return evtInfo;
};
/**
* @private
*/
ns.DrawingController.prototype.getRelativeCoordinates = function (clientX, clientY) {
var canvasPageOffset = this.container.offset();
return {
x : clientX - canvasPageOffset.left,
y : clientY - canvasPageOffset.top
};
};
/**
* @private
*/
ns.DrawingController.prototype.getSpriteCoordinates = function(event) {
var coords = this.getRelativeCoordinates(event.clientX, event.clientY);
return this.renderer.convertPixelCoordinatesIntoSpriteCoordinate(coords);
};
/**
* @private
*/
ns.DrawingController.prototype.getCurrentColor_ = function () {
if(this.isRightClicked) {
return this.secondaryColor;
} else {
return this.primaryColor;
}
};
/**
* @private
*/
ns.DrawingController.prototype.onCanvasContextMenu_ = function (event) {
if ($(event.target).closest('#drawing-canvas-container').length) {
// Deactivate right click on drawing canvas only.
event.preventDefault();
event.stopPropagation();
event.cancelBubble = true;
return false;
}
};
ns.DrawingController.prototype.render = function () {
this.renderLayers();
this.renderFrame();
this.renderOverlay();
};
ns.DrawingController.prototype.renderFrame = function () {
var frame = this.piskelController.getCurrentFrame();
var serializedFrame = this.dpi + "-" + frame.serialize();
if (this.serializedFrame != serializedFrame) {
if (!frame.isSameSize(this.overlayFrame)) {
this.overlayFrame = pskl.model.Frame.createEmptyFromFrame(frame);
}
this.serializedFrame = serializedFrame;
this.renderer.render(frame);
}
};
ns.DrawingController.prototype.renderOverlay = function () {
var serializedOverlay = this.dpi + "-" + this.overlayFrame.serialize();
if (this.serializedOverlay != serializedOverlay) {
this.serializedOverlay = serializedOverlay;
this.overlayRenderer.render(this.overlayFrame);
}
};
ns.DrawingController.prototype.renderLayers = function () {
var layers = this.piskelController.getLayers();
var currentFrameIndex = this.piskelController.currentFrameIndex;
var currentLayerIndex = this.piskelController.currentLayerIndex;
var serializedLayerFrame = [
this.dpi,
currentFrameIndex,
currentLayerIndex,
layers.length
].join("-");
if (this.serializedLayerFrame != serializedLayerFrame) {
this.layersAboveRenderer.clear();
this.layersBelowRenderer.clear();
var downLayers = layers.slice(0, currentLayerIndex);
var downFrame = this.getFrameForLayersAt_(currentFrameIndex, downLayers);
this.layersBelowRenderer.render(downFrame);
if (currentLayerIndex + 1 < layers.length) {
var upLayers = layers.slice(currentLayerIndex + 1, layers.length);
var upFrame = this.getFrameForLayersAt_(currentFrameIndex, upLayers);
this.layersAboveRenderer.render(upFrame);
}
this.serializedLayerFrame = serializedLayerFrame;
}
};
ns.DrawingController.prototype.getFrameForLayersAt_ = function (frameIndex, layers) {
var frames = layers.map(function (l) {
return l.getFrameAt(frameIndex);
});
return pskl.utils.FrameUtils.merge(frames);
};
/**
* @private
*/
ns.DrawingController.prototype.calculateDPI_ = function() {
var availableViewportHeight = $('#main-wrapper').height(),
leftSectionWidth = $('.left-column').outerWidth(true),
rightSectionWidth = $('.right-column').outerWidth(true),
availableViewportWidth = $('#main-wrapper').width() - leftSectionWidth - rightSectionWidth,
framePixelHeight = this.piskelController.getCurrentFrame().getHeight(),
framePixelWidth = this.piskelController.getCurrentFrame().getWidth();
if (pskl.UserSettings.get(pskl.UserSettings.SHOW_GRID)) {
availableViewportWidth = availableViewportWidth - (framePixelWidth * Constants.GRID_STROKE_WIDTH);
availableViewportHeight = availableViewportHeight - (framePixelHeight * Constants.GRID_STROKE_WIDTH);
}
var dpi = pskl.PixelUtils.calculateDPI(
availableViewportHeight, availableViewportWidth, framePixelHeight, framePixelWidth);
return dpi;
};
/**
* @private
*/
ns.DrawingController.prototype.updateDPI_ = function() {
this.dpi = this.calculateDPI_();
this.overlayRenderer.setDPI(this.dpi);
this.renderer.setDPI(this.dpi);
this.layersAboveRenderer.setDPI(this.dpi);
this.layersBelowRenderer.setDPI(this.dpi);
var currentFrameHeight = this.piskelController.getCurrentFrame().getHeight();
var canvasHeight = currentFrameHeight * this.dpi;
if (pskl.UserSettings.get(pskl.UserSettings.SHOW_GRID)) {
canvasHeight += Constants.GRID_STROKE_WIDTH * currentFrameHeight;
}
var verticalGapInPixel = Math.floor(($('#main-wrapper').height() - canvasHeight) / 2);
$('#column-wrapper').css({
'top': verticalGapInPixel + 'px',
'height': canvasHeight + 'px'
});
};
})();

View File

@ -0,0 +1,59 @@
(function () {
var ns = $.namespace('pskl.controller');
ns.LayersListController = function (piskelController) {
this.piskelController = piskelController;
};
ns.LayersListController.prototype.init = function () {
this.layerItemTemplate_ = pskl.utils.Template.get('layer-item-template');
this.rootEl = document.querySelectorAll('.layers-list-container')[0];
this.layersListEl = document.querySelectorAll('.layers-list')[0];
this.rootEl.addEventListener('click', this.onClick_.bind(this));
$.subscribe(Events.PISKEL_RESET, this.renderLayerList_.bind(this));
this.renderLayerList_();
};
ns.LayersListController.prototype.renderLayerList_ = function () {
this.layersListEl.innerHTML = '';
var layers = this.piskelController.getLayers();
layers.forEach(this.addLayerItem.bind(this));
};
ns.LayersListController.prototype.addLayerItem = function (layer) {
var layerItemHtml = pskl.utils.Template.replace(this.layerItemTemplate_, {
layername : layer.getName()
});
var layerItem = pskl.utils.Template.createFromHTML(layerItemHtml);
if (this.piskelController.getCurrentLayer() === layer) {
layerItem.classList.add('current-layer-item');
}
this.layersListEl.insertBefore(layerItem, this.layersListEl.firstChild);
};
ns.LayersListController.prototype.onClick_ = function (evt) {
var el = evt.target || evt.srcElement;
if (el.nodeName == 'BUTTON') {
this.onButtonClick_(el);
} else if (el.nodeName == 'LI') {
var layerName = el.getAttribute('data-layer-name');
this.piskelController.selectLayerByName(layerName);
}
};
ns.LayersListController.prototype.onButtonClick_ = function (button) {
var action = button.getAttribute('data-action');
if (action == 'up') {
this.piskelController.moveLayerUp();
} else if (action == 'down') {
this.piskelController.moveLayerDown();
} else if (action == 'add') {
this.piskelController.createLayer();
} else if (action == 'delete') {
this.piskelController.removeCurrentLayer();
}
};
})();

View File

@ -0,0 +1,39 @@
(function () {
var ns = $.namespace("pskl.controller");
ns.NotificationController = function () {};
/**
* @public
*/
ns.NotificationController.prototype.init = function() {
$.subscribe(Events.SHOW_NOTIFICATION, $.proxy(this.displayMessage_, this));
$.subscribe(Events.HIDE_NOTIFICATION, $.proxy(this.removeMessage_, this));
};
/**
* @private
*/
ns.NotificationController.prototype.displayMessage_ = function (evt, messageInfo) {
var message = document.createElement('div');
message.id = "user-message";
message.className = "user-message";
message.innerHTML = messageInfo.content;
message.innerHTML = message.innerHTML + "<div title='Close message' class='close'>x</div>";
document.body.appendChild(message);
$(message).find(".close").click($.proxy(this.removeMessage_, this));
if(messageInfo.behavior) {
messageInfo.behavior(message);
}
};
/**
* @private
*/
ns.NotificationController.prototype.removeMessage_ = function (evt) {
var message = $("#user-message");
if (message.length) {
message.remove();
}
};
})();

View File

@ -0,0 +1,83 @@
(function () {
var ns = $.namespace("pskl.controller");
ns.PaletteController = function () {};
/**
* @public
*/
ns.PaletteController.prototype.init = function() {
var transparentColorPalette = $(".palette-color[data-color=TRANSPARENT]");
transparentColorPalette.mouseup($.proxy(this.onPaletteColorClick_, this));
$.subscribe(Events.PRIMARY_COLOR_UPDATED, $.proxy(function(evt, color) {
this.updateColorPicker_(color, $('#color-picker'));
}, this));
$.subscribe(Events.SECONDARY_COLOR_UPDATED, $.proxy(function(evt, color) {
this.updateColorPicker_(color, $('#secondary-color-picker'));
}, this));
// Initialize colorpickers:
var colorPicker = $('#color-picker');
colorPicker.val(Constants.DEFAULT_PEN_COLOR);
colorPicker.change({isPrimary : true}, $.proxy(this.onPickerChange_, this));
var secondaryColorPicker = $('#secondary-color-picker');
secondaryColorPicker.val(Constants.TRANSPARENT_COLOR);
secondaryColorPicker.change({isPrimary : false}, $.proxy(this.onPickerChange_, this));
window.jscolor.install();
};
/**
* @private
*/
ns.PaletteController.prototype.onPickerChange_ = function(evt, isPrimary) {
var inputPicker = $(evt.target);
if(evt.data.isPrimary) {
$.publish(Events.PRIMARY_COLOR_SELECTED, [inputPicker.val()]);
} else {
$.publish(Events.SECONDARY_COLOR_SELECTED, [inputPicker.val()]);
}
};
/**
* @private
*/
ns.PaletteController.prototype.onPaletteColorClick_ = function (event) {
var selectedColor = $(event.target).data("color");
var isLeftClick = (event.which == 1);
var isRightClick = (event.which == 3);
if (isLeftClick) {
$.publish(Events.PRIMARY_COLOR_SELECTED, [selectedColor]);
} else if (isRightClick) {
$.publish(Events.SECONDARY_COLOR_SELECTED, [selectedColor]);
}
};
/**
* @private
*/
ns.PaletteController.prototype.updateColorPicker_ = function (color, colorPicker) {
if (color == Constants.TRANSPARENT_COLOR) {
// We can set the current palette color to transparent.
// You can then combine this transparent color with an advanced
// tool for customized deletions.
// Eg: bucket + transparent: Delete a colored area
// Stroke + transparent: hollow out the equivalent of a stroke
// The colorpicker can't be set to a transparent state.
// We set its background to white and insert the
// string "TRANSPARENT" to mimic this state:
colorPicker[0].color.fromString("#fff");
colorPicker.val(Constants.TRANSPARENT_COLOR);
} else {
colorPicker[0].color.fromString(color);
}
};
})();

View File

@ -0,0 +1,194 @@
(function () {
var ns = $.namespace('pskl.controller');
ns.PiskelController = function (piskel) {
if (piskel) {
this.setPiskel(piskel);
} else {
throw 'A piskel instance is mandatory for instanciating PiskelController';
}
};
ns.PiskelController.prototype.setPiskel = function (piskel) {
this.piskel = piskel;
this.currentLayerIndex = 0;
this.currentFrameIndex = 0;
this.layerIdCounter = 1;
$.publish(Events.PISKEL_RESET);
$.publish(Events.FRAME_SIZE_CHANGED);
};
ns.PiskelController.prototype.getHeight = function () {
return this.piskel.getHeight();
};
ns.PiskelController.prototype.getWidth = function () {
return this.piskel.getWidth();
};
/**
* TODO : this should be removed
* FPS should be stored in the Piskel model and not in the
* animationController
* Then piskelController should be able to return this information
* @return {Number} Frames per second for the current animation
*/
ns.PiskelController.prototype.getFPS = function () {
return pskl.app.animationController.getFPS();
};
ns.PiskelController.prototype.getLayers = function () {
return this.piskel.getLayers();
};
ns.PiskelController.prototype.getCurrentLayer = function () {
return this.piskel.getLayerAt(this.currentLayerIndex);
};
ns.PiskelController.prototype.getCurrentFrame = function () {
var layer = this.getCurrentLayer();
return layer.getFrameAt(this.currentFrameIndex);
};
ns.PiskelController.prototype.getFrameAt = function (index) {
var frames = this.getLayers().map(function (l) {
return l.getFrameAt(index);
});
return pskl.utils.FrameUtils.merge(frames);
};
ns.PiskelController.prototype.hasFrameAt = function (index) {
return !!this.getCurrentLayer().getFrameAt(index);
};
// backward from framesheet
ns.PiskelController.prototype.getFrameByIndex =
ns.PiskelController.prototype.getMergedFrameAt;
ns.PiskelController.prototype.addEmptyFrame = function () {
var layers = this.getLayers();
layers.forEach(function (l) {
l.addFrame(this.createEmptyFrame_());
}.bind(this));
};
ns.PiskelController.prototype.createEmptyFrame_ = function () {
var w = this.piskel.getWidth(), h = this.piskel.getHeight();
return new pskl.model.Frame(w, h);
};
ns.PiskelController.prototype.removeFrameAt = function (index) {
var layers = this.getLayers();
layers.forEach(function (l) {
l.removeFrameAt(index);
});
// Current frame index is impacted if the removed frame was before the current frame
if (this.currentFrameIndex >= index) {
this.setCurrentFrameIndex(this.currentFrameIndex - 1);
}
$.publish(Events.PISKEL_RESET);
};
ns.PiskelController.prototype.duplicateFrameAt = function (index) {
var layers = this.getLayers();
layers.forEach(function (l) {
l.duplicateFrameAt(index);
});
};
ns.PiskelController.prototype.moveFrame = function (fromIndex, toIndex) {
var layers = this.getLayers();
layers.forEach(function (l) {
l.moveFrame(fromIndex, toIndex);
});
};
ns.PiskelController.prototype.getFrameCount = function () {
var layer = this.piskel.getLayerAt(0);
return layer.length();
};
ns.PiskelController.prototype.setCurrentFrameIndex = function (index) {
this.currentFrameIndex = index;
$.publish(Events.PISKEL_RESET);
};
ns.PiskelController.prototype.setCurrentLayerIndex = function (index) {
this.currentLayerIndex = index;
$.publish(Events.PISKEL_RESET);
};
ns.PiskelController.prototype.selectLayer = function (layer) {
var index = this.getLayers().indexOf(layer);
if (index != -1) {
this.setCurrentLayerIndex(index);
}
};
ns.PiskelController.prototype.selectLayerByName = function (name) {
if (this.hasLayerForName_(name)) {
var layer = this.piskel.getLayersByName(name)[0];
this.selectLayer(layer);
}
};
ns.PiskelController.prototype.generateLayerName_ = function () {
var name = "Layer " + this.layerIdCounter;
while (this.hasLayerForName_(name)) {
this.layerIdCounter++;
name = "Layer " + this.layerIdCounter;
}
return name;
};
ns.PiskelController.prototype.createLayer = function (name) {
if (!name) {
name = this.generateLayerName_();
}
if (!this.hasLayerForName_(name)) {
var layer = new pskl.model.Layer(name);
for (var i = 0 ; i < this.getFrameCount() ; i++) {
layer.addFrame(this.createEmptyFrame_());
}
this.piskel.addLayer(layer);
this.setCurrentLayerIndex(this.piskel.getLayers().length - 1);
} else {
throw 'Layer name should be unique';
}
};
ns.PiskelController.prototype.hasLayerForName_ = function (name) {
return this.piskel.getLayersByName(name).length > 0;
};
ns.PiskelController.prototype.moveLayerUp = function () {
var layer = this.getCurrentLayer();
this.piskel.moveLayerUp(layer);
this.selectLayer(layer);
};
ns.PiskelController.prototype.moveLayerDown = function () {
var layer = this.getCurrentLayer();
this.piskel.moveLayerDown(layer);
this.selectLayer(layer);
};
ns.PiskelController.prototype.removeCurrentLayer = function () {
if (this.getLayers().length > 1) {
var layer = this.getCurrentLayer();
this.piskel.removeLayer(layer);
this.setCurrentLayerIndex(0);
}
};
ns.PiskelController.prototype.serialize = function () {
return pskl.utils.Serializer.serializePiskel(this.piskel);
};
ns.PiskelController.prototype.load = function (data) {
this.deserialize(JSON.stringify(data));
};
})();

View File

@ -0,0 +1,220 @@
(function () {
var ns = $.namespace("pskl.controller");
ns.PreviewFilmController = function (piskelController, container, dpi) {
this.piskelController = piskelController;
this.container = container;
this.dpi = this.calculateDPI_();
this.redrawFlag = true;
};
ns.PreviewFilmController.prototype.init = function() {
$.subscribe(Events.TOOL_RELEASED, this.flagForRedraw_.bind(this));
$.subscribe(Events.PISKEL_RESET, this.flagForRedraw_.bind(this));
$.subscribe(Events.PISKEL_RESET, this.refreshDPI_.bind(this));
$('#preview-list-scroller').scroll(this.updateScrollerOverflows.bind(this));
this.updateScrollerOverflows();
};
ns.PreviewFilmController.prototype.addFrame = function () {
this.piskelController.addEmptyFrame();
this.piskelController.setCurrentFrameIndex(this.piskelController.getFrameCount() - 1);
this.updateScrollerOverflows();
};
ns.PreviewFilmController.prototype.flagForRedraw_ = function () {
this.redrawFlag = true;
};
ns.PreviewFilmController.prototype.refreshDPI_ = function () {
this.dpi = this.calculateDPI_();
};
ns.PreviewFilmController.prototype.render = function () {
if (this.redrawFlag) {
// TODO(vincz): Full redraw on any drawing modification, optimize.
this.createPreviews_();
this.redrawFlag = false;
}
};
ns.PreviewFilmController.prototype.updateScrollerOverflows = function () {
var scroller = $('#preview-list-scroller');
var scrollerHeight = scroller.height();
var scrollTop = scroller.scrollTop();
var scrollerContentHeight = $('#preview-list').height();
var treshold = $('.top-overflow').height();
var overflowTop = false,
overflowBottom = false;
if (scrollerHeight < scrollerContentHeight) {
if (scrollTop > treshold) {
overflowTop = true;
}
var scrollBottom = (scrollerContentHeight - scrollTop) - scrollerHeight;
if (scrollBottom > treshold) {
overflowBottom = true;
}
}
var wrapper = $('#preview-list-wrapper');
wrapper.toggleClass('top-overflow-visible', overflowTop);
wrapper.toggleClass('bottom-overflow-visible', overflowBottom);
};
ns.PreviewFilmController.prototype.createPreviews_ = function () {
this.container.html("");
// Manually remove tooltips since mouseout events were shortcut by the DOM refresh:
$(".tooltip").remove();
var frameCount = this.piskelController.getFrameCount();
for (var i = 0, l = frameCount; i < l ; i++) {
this.container.append(this.createPreviewTile_(i));
}
// Append 'new empty frame' button
var newFrameButton = document.createElement("div");
newFrameButton.id = "add-frame-action";
newFrameButton.className = "add-frame-action";
newFrameButton.innerHTML = "<p class='label'>Add new frame</p>";
this.container.append(newFrameButton);
$(newFrameButton).click(this.addFrame.bind(this));
var needDragndropBehavior = (frameCount > 1);
if(needDragndropBehavior) {
this.initDragndropBehavior_();
}
this.updateScrollerOverflows();
};
/**
* @private
*/
ns.PreviewFilmController.prototype.initDragndropBehavior_ = function () {
$("#preview-list").sortable({
placeholder: "preview-tile-drop-proxy",
update: $.proxy(this.onUpdate_, this),
items: ".preview-tile"
});
$("#preview-list").disableSelection();
};
/**
* @private
*/
ns.PreviewFilmController.prototype.onUpdate_ = function( event, ui ) {
var originFrameId = parseInt(ui.item.data("tile-number"), 10);
var targetInsertionId = $('.preview-tile').index(ui.item);
this.piskelController.moveFrame(originFrameId, targetInsertionId);
this.piskelController.setCurrentFrameIndex(targetInsertionId);
// TODO(grosbouddha): move localstorage request to the model layer?
$.publish(Events.LOCALSTORAGE_REQUEST);
};
/**
* @private
* TODO(vincz): clean this giant rendering function & remove listeners.
*/
ns.PreviewFilmController.prototype.createPreviewTile_ = function(tileNumber) {
var currentFrame = this.piskelController.getCurrentLayer().getFrameAt(tileNumber);
var previewTileRoot = document.createElement("li");
var classname = "preview-tile";
previewTileRoot.setAttribute("data-tile-number", tileNumber);
if (this.piskelController.getCurrentFrame() == currentFrame) {
classname += " selected";
}
previewTileRoot.className = classname;
var canvasContainer = document.createElement("div");
canvasContainer.className = "canvas-container";
var canvasBackground = document.createElement("div");
canvasBackground.className = "canvas-background";
canvasContainer.appendChild(canvasBackground);
previewTileRoot.addEventListener('click', this.onPreviewClick_.bind(this, tileNumber));
var cloneFrameButton = document.createElement("button");
cloneFrameButton.setAttribute('rel', 'tooltip');
cloneFrameButton.setAttribute('data-placement', 'right');
cloneFrameButton.setAttribute('title', 'Duplicate this frame');
cloneFrameButton.className = "tile-overlay duplicate-frame-action";
previewTileRoot.appendChild(cloneFrameButton);
cloneFrameButton.addEventListener('click', this.onAddButtonClick_.bind(this, tileNumber));
// TODO(vincz): Eventually optimize this part by not recreating a FrameRenderer. Note that the real optim
// is to make this update function (#createPreviewTile) less aggressive.
var renderingOptions = {"dpi": this.dpi };
var currentFrameRenderer = new pskl.rendering.FrameRenderer($(canvasContainer), renderingOptions, ["tile-view"]);
currentFrameRenderer.render(currentFrame);
previewTileRoot.appendChild(canvasContainer);
if(tileNumber > 0 || this.piskelController.getFrameCount() > 1) {
// Add 'remove frame' button.
var deleteButton = document.createElement("button");
deleteButton.setAttribute('rel', 'tooltip');
deleteButton.setAttribute('data-placement', 'right');
deleteButton.setAttribute('title', 'Delete this frame');
deleteButton.className = "tile-overlay delete-frame-action";
deleteButton.addEventListener('click', this.onDeleteButtonClick_.bind(this, tileNumber));
previewTileRoot.appendChild(deleteButton);
// Add 'dragndrop handle'.
var dndHandle = document.createElement("div");
dndHandle.className = "tile-overlay dnd-action";
previewTileRoot.appendChild(dndHandle);
}
var tileCount = document.createElement("div");
tileCount.className = "tile-overlay tile-count";
tileCount.innerHTML = tileNumber;
previewTileRoot.appendChild(tileCount);
return previewTileRoot;
};
ns.PreviewFilmController.prototype.onPreviewClick_ = function (index, evt) {
// has not class tile-action:
if(!evt.target.classList.contains('tile-overlay')) {
this.piskelController.setCurrentFrameIndex(index);
}
};
ns.PreviewFilmController.prototype.onDeleteButtonClick_ = function (index, evt) {
this.piskelController.removeFrameAt(index);
$.publish(Events.LOCALSTORAGE_REQUEST); // Should come from model
this.updateScrollerOverflows();
};
ns.PreviewFilmController.prototype.onAddButtonClick_ = function (index, evt) {
this.piskelController.duplicateFrameAt(index);
$.publish(Events.LOCALSTORAGE_REQUEST); // Should come from model
this.piskelController.setCurrentFrameIndex(index + 1);
this.updateScrollerOverflows();
};
/**
* Calculate the preview DPI depending on the piskel size
*/
ns.PreviewFilmController.prototype.calculateDPI_ = function () {
var curFrame = this.piskelController.getCurrentFrame(),
frameHeight = curFrame.getHeight(),
frameWidth = curFrame.getWidth(),
maxFrameDim = Math.max(frameWidth, frameHeight);
var previewHeight = Constants.PREVIEW_FILM_SIZE * frameHeight / maxFrameDim;
var previewWidth = Constants.PREVIEW_FILM_SIZE * frameWidth / maxFrameDim;
return pskl.PixelUtils.calculateDPI(previewHeight, previewWidth, frameHeight, frameWidth) || 1;
};
})();

View File

@ -0,0 +1,107 @@
(function () {
var ns = $.namespace("pskl.controller");
ns.ToolController = function () {
this.toolInstances = {
"simplePen" : new pskl.drawingtools.SimplePen(),
"verticalMirrorPen" : new pskl.drawingtools.VerticalMirrorPen(),
"eraser" : new pskl.drawingtools.Eraser(),
"paintBucket" : new pskl.drawingtools.PaintBucket(),
"stroke" : new pskl.drawingtools.Stroke(),
"rectangle" : new pskl.drawingtools.Rectangle(),
"circle" : new pskl.drawingtools.Circle(),
"move" : new pskl.drawingtools.Move(),
"rectangleSelect" : new pskl.drawingtools.RectangleSelect(),
"shapeSelect" : new pskl.drawingtools.ShapeSelect(),
"colorPicker" : new pskl.drawingtools.ColorPicker()
};
this.currentSelectedTool = this.toolInstances.simplePen;
this.previousSelectedTool = this.toolInstances.simplePen;
};
/**
* @public
*/
ns.ToolController.prototype.init = function() {
this.createToolMarkup_();
// Initialize tool:
// Set SimplePen as default selected tool:
this.selectTool_(this.toolInstances.simplePen);
// Activate listener on tool panel:
$("#tool-section").click($.proxy(this.onToolIconClicked_, this));
};
/**
* @private
*/
ns.ToolController.prototype.activateToolOnStage_ = function(tool) {
var stage = $("body");
var previousSelectedToolClass = stage.data("selected-tool-class");
if(previousSelectedToolClass) {
stage.removeClass(previousSelectedToolClass);
}
stage.addClass(tool.toolId);
stage.data("selected-tool-class", tool.toolId);
};
/**
* @private
*/
ns.ToolController.prototype.selectTool_ = function(tool) {
console.log("Selecting Tool:" , this.currentSelectedTool);
this.currentSelectedTool = tool;
this.activateToolOnStage_(this.currentSelectedTool);
$.publish(Events.TOOL_SELECTED, [tool]);
};
/**
* @private
*/
ns.ToolController.prototype.onToolIconClicked_ = function(evt) {
var target = $(evt.target);
var clickedTool = target.closest(".tool-icon");
if(clickedTool.length) {
var toolId = clickedTool.data().toolId;
var tool = this.getToolById_(toolId);
if (tool) {
this.selectTool_(tool);
// Show tool as selected:
$('#tool-section .tool-icon.selected').removeClass('selected');
clickedTool.addClass('selected');
}
}
};
ns.ToolController.prototype.getToolById_ = function (toolId) {
for(var key in this.toolInstances) {
if (this.toolInstances[key].toolId == toolId) {
return this.toolInstances[key];
}
}
return null;
};
/**
* @private
*/
ns.ToolController.prototype.createToolMarkup_ = function() {
var currentTool, toolMarkup = '', extraClass;
// TODO(vincz): Tools rendering order is not enforced by the data stucture (this.toolInstances), fix that.
for (var toolKey in this.toolInstances) {
currentTool = this.toolInstances[toolKey];
extraClass = currentTool.toolId;
if (this.currentSelectedTool == currentTool) {
extraClass = extraClass + " selected";
}
toolMarkup += '<li rel="tooltip" data-placement="right" class="tool-icon ' + extraClass + '" data-tool-id="' + currentTool.toolId +
'" title="' + currentTool.helpText + '"></li>';
}
$('#tools-container').html(toolMarkup);
};
})();

View File

@ -0,0 +1,38 @@
(function () {
var ns = $.namespace("pskl.controller.settings");
ns.ApplicationSettingsController = function () {};
/**
* @public
*/
ns.ApplicationSettingsController.prototype.init = function() {
// Highlight selected background picker:
var backgroundClass = pskl.UserSettings.get(pskl.UserSettings.CANVAS_BACKGROUND);
$('#background-picker-wrapper')
.find('.background-picker[data-background-class=' + backgroundClass + ']')
.addClass('selected');
// Initial state for grid display:
var show_grid = pskl.UserSettings.get(pskl.UserSettings.SHOW_GRID);
$('#show-grid').prop('checked', show_grid);
// Handle grid display changes:
$('#show-grid').change($.proxy(function(evt) {
var checked = $('#show-grid').prop('checked');
pskl.UserSettings.set(pskl.UserSettings.SHOW_GRID, checked);
}, this));
// Handle canvas background changes:
$('#background-picker-wrapper').click(function(evt) {
var target = $(evt.target).closest('.background-picker');
if (target.length) {
var backgroundClass = target.data('background-class');
pskl.UserSettings.set(pskl.UserSettings.CANVAS_BACKGROUND, backgroundClass);
$('.background-picker').removeClass('selected');
target.addClass('selected');
}
});
};
})();

View File

@ -0,0 +1,129 @@
(function () {
var ns = $.namespace("pskl.controller.settings");
ns.GifExportController = function (piskelController) {
this.piskelController = piskelController;
};
/**
* List of Resolutions applicable for Gif export
* @static
* @type {Array} array of Objects {dpi:{Number}, default:{Boolean}}
*/
ns.GifExportController.RESOLUTIONS = [
{
'dpi' : 1
},{
'dpi' : 5
},{
'dpi' : 10,
'default' : true
},{
'dpi' : 20
}
];
ns.GifExportController.prototype.init = function () {
this.radioTemplate_ = pskl.utils.Template.get("gif-export-radio-template");
this.previewContainerEl = document.querySelectorAll(".gif-export-preview")[0];
this.radioGroupEl = document.querySelectorAll(".gif-export-radio-group")[0];
this.uploadForm = $("[name=gif-export-upload-form]");
this.uploadForm.submit(this.onUploadFormSubmit_.bind(this));
this.createRadioElements_();
};
ns.GifExportController.prototype.onUploadFormSubmit_ = function (evt) {
evt.originalEvent.preventDefault();
var selectedDpi = this.getSelectedDpi_(),
fps = this.piskelController.getFPS(),
dpi = selectedDpi;
this.renderAsImageDataAnimatedGIF(dpi, fps, this.onGifRenderingCompleted_.bind(this));
};
ns.GifExportController.prototype.onGifRenderingCompleted_ = function (imageData) {
this.updatePreview_(imageData);
this.previewContainerEl.classList.add("preview-upload-ongoing");
pskl.app.imageUploadService.upload(imageData, this.onImageUploadCompleted_.bind(this));
};
ns.GifExportController.prototype.onImageUploadCompleted_ = function (imageUrl) {
this.updatePreview_(imageUrl);
this.previewContainerEl.classList.remove("preview-upload-ongoing");
};
ns.GifExportController.prototype.updatePreview_ = function (src) {
this.previewContainerEl.innerHTML = "<div><img style='max-width:240px;' src='"+src+"'/></div>";
};
ns.GifExportController.prototype.getSelectedDpi_ = function () {
var radiosColl = this.uploadForm.get(0).querySelectorAll("[name=gif-dpi]"),
radios = Array.prototype.slice.call(radiosColl,0);
var selectedRadios = radios.filter(function(radio) {return !!radio.checked;});
if (selectedRadios.length == 1) {
return selectedRadios[0].value;
} else {
throw "Unexpected error when retrieving selected dpi";
}
};
ns.GifExportController.prototype.createRadioElements_ = function () {
var resolutions = ns.GifExportController.RESOLUTIONS;
for (var i = 0 ; i < resolutions.length ; i++) {
var radio = this.createRadioForResolution_(resolutions[i]);
this.radioGroupEl.appendChild(radio);
}
};
ns.GifExportController.prototype.createRadioForResolution_ = function (resolution) {
var dpi = resolution.dpi;
var label = dpi*this.piskelController.getWidth() + "x" + dpi*this.piskelController.getHeight();
var value = dpi;
var radioHTML = pskl.utils.Template.replace(this.radioTemplate_, {value : value, label : label});
var radioEl = pskl.utils.Template.createFromHTML(radioHTML);
if (resolution['default']) {
var input = radioEl.getElementsByTagName("input")[0];
input.setAttribute("checked", "checked");
}
return radioEl;
};
ns.GifExportController.prototype.blobToBase64_ = function(blob, cb) {
var reader = new FileReader();
reader.onload = function() {
var dataUrl = reader.result;
cb(dataUrl);
};
reader.readAsDataURL(blob);
};
ns.GifExportController.prototype.renderAsImageDataAnimatedGIF = function(dpi, fps, cb) {
var gif = new window.GIF({
workers: 2,
quality: 10,
width: this.piskelController.getWidth()*dpi,
height: this.piskelController.getHeight()*dpi
});
for (var i = 0; i < this.piskelController.getFrameCount(); i++) {
var frame = this.piskelController.getFrameAt(i);
var renderer = new pskl.rendering.CanvasRenderer(frame, dpi);
gif.addFrame(renderer.render(), {
delay: 1000 / fps
});
}
gif.on('finished', function(blob) {
this.blobToBase64_(blob, cb);
}.bind(this));
gif.render();
};
})();

View File

@ -0,0 +1,168 @@
(function () {
var ns = $.namespace('pskl.controller.settings');
var DEFAULT_FILE_STATUS = 'No file selected ...';
var PREVIEW_HEIGHT = 60;
ns.ImportController = function (piskelController) {
this.piskelController = piskelController;
this.importedImage_ = null;
};
ns.ImportController.prototype.init = function () {
this.importForm = $("[name=import-form]");
this.hiddenFileInput = $("[name=file-upload-input]");
this.fileInputButton = $(".file-input-button");
this.fileInputStatus = $(".file-input-status");
this.fileInputStatus.html(DEFAULT_FILE_STATUS);
this.importPreview = $(".import-section-preview");
this.resizeWidth = $("[name=resize-width]");
this.resizeHeight = $("[name=resize-height]");
this.smoothResize = $("[name=smooth-resize-checkbox]");
this.submitButton = $("[name=import-submit]");
this.importForm.submit(this.onImportFormSubmit_.bind(this));
this.hiddenFileInput.change(this.onFileUploadChange_.bind(this));
this.fileInputButton.click(this.onFileInputClick_.bind(this));
this.resizeWidth.keyup(this.onResizeInputKeyUp_.bind(this, 'width'));
this.resizeHeight.keyup(this.onResizeInputKeyUp_.bind(this, 'height'));
};
ns.ImportController.prototype.reset_ = function () {
this.importForm.get(0).reset();
this.fileInputStatus.html(DEFAULT_FILE_STATUS);
$.publish(Events.CLOSE_SETTINGS_DRAWER);
};
ns.ImportController.prototype.onResizeInputKeyUp_ = function (from, evt) {
if (this.importedImage_) {
this.synchronizeResizeFields_(evt.target.value, from);
}
};
ns.ImportController.prototype.synchronizeResizeFields_ = function (value, from) {
value = parseInt(value, 10);
if (isNaN(value)) {
value = 0;
}
var height = this.importedImage_.height, width = this.importedImage_.width;
if (from === 'width') {
this.resizeHeight.val(Math.round(value * height / width));
} else {
this.resizeWidth.val(Math.round(value * width / height));
}
};
ns.ImportController.prototype.onImportFormSubmit_ = function (evt) {
evt.originalEvent.preventDefault();
this.importImageToPiskel_();
};
ns.ImportController.prototype.onFileUploadChange_ = function (evt) {
this.importFromFile_();
};
ns.ImportController.prototype.onFileInputClick_ = function (evt) {
this.hiddenFileInput.click();
};
ns.ImportController.prototype.importFromFile_ = function () {
var files = this.hiddenFileInput.get(0).files;
if (files.length == 1) {
var file = files[0];
if (this.isImage_(file)) {
this.readImageFile_(file);
this.enableDisabledSections_();
} else {
this.reset_();
throw "File is not an image : " + file.type;
}
}
};
ns.ImportController.prototype.enableDisabledSections_ = function () {
this.resizeWidth.removeAttr('disabled');
this.resizeHeight.removeAttr('disabled');
this.smoothResize.removeAttr('disabled');
this.submitButton.removeAttr('disabled');
this.fileInputButton.removeClass('button-primary');
this.fileInputButton.blur();
$('.import-section-disabled').removeClass('import-section-disabled');
};
ns.ImportController.prototype.readImageFile_ = function (imageFile) {
pskl.utils.FileUtils.readFile(imageFile, this.processImageSource_.bind(this));
};
/**
* Create an image from the given source (url or data-url), and onload forward to onImageLoaded
* TODO : should be a generic utility method, should take a callback
* @param {String} imageSource url or data-url, will be used as src for the image
*/
ns.ImportController.prototype.processImageSource_ = function (imageSource) {
this.importedImage_ = new Image();
this.importedImage_.onload = this.onImageLoaded_.bind(this);
this.importedImage_.src = imageSource;
};
ns.ImportController.prototype.onImageLoaded_ = function (evt) {
var w = this.importedImage_.width,
h = this.importedImage_.height;
var filePath = this.hiddenFileInput.val();
var fileName = this.extractFileNameFromPath_(filePath);
this.fileInputStatus.html(fileName);
this.resizeWidth.val(w);
this.resizeHeight.val(h);
this.importPreview.width("auto");
this.importPreview.append(this.createImagePreview_());
};
ns.ImportController.prototype.createImagePreview_ = function () {
var image = document.createElement('IMG');
image.src = this.importedImage_.src;
image.setAttribute('height', PREVIEW_HEIGHT);
return image;
};
ns.ImportController.prototype.extractFileNameFromPath_ = function (path) {
var parts = [];
if (path.indexOf('/') !== -1) {
parts = path.split('/');
} else if (path.indexOf('\\') !== -1) {
parts = path.split('\\');
} else {
parts = [path];
}
return parts[parts.length-1];
};
ns.ImportController.prototype.importImageToPiskel_ = function () {
if (this.importedImage_) {
if (window.confirm("You are about to create a new Piskel, unsaved changes will be lost.")) {
var w = this.resizeWidth.val(),
h = this.resizeHeight.val(),
smoothing = !!this.smoothResize.prop('checked');
var image = pskl.utils.ImageResizer.resize(this.importedImage_, w, h, smoothing);
var frame = pskl.utils.FrameUtils.createFromImage(image);
var piskel = pskl.utils.Serializer.createPiskel([frame]);
pskl.app.piskelController.setPiskel(piskel);
pskl.app.animationController.setFPS(Constants.DEFAULT.FPS);
this.reset_();
}
}
};
ns.ImportController.prototype.isImage_ = function (file) {
return file.type.indexOf('image') === 0;
};
})();

View File

@ -0,0 +1,76 @@
(function () {
var ns = $.namespace("pskl.controller.settings");
var settings = {
'user' : {
template : 'templates/settings/application.html',
controller : ns.ApplicationSettingsController
},
'gif' : {
template : 'templates/settings/export-gif.html',
controller : ns.GifExportController
},
'import' : {
template : 'templates/settings/import.html',
controller : ns.ImportController
}
};
var SEL_SETTING_CLS = 'has-expanded-drawer';
var EXP_DRAWER_CLS = 'expanded';
ns.SettingsController = function (piskelController) {
this.piskelController = piskelController;
this.drawerContainer = document.getElementById("drawer-container");
this.settingsContainer = $('[data-pskl-controller=settings]');
this.expanded = false;
this.currentSetting = null;
};
/**
* @public
*/
ns.SettingsController.prototype.init = function() {
// Expand drawer when clicking 'Settings' tab.
$('[data-setting]').click(function(evt) {
var el = evt.originalEvent.currentTarget;
var setting = el.getAttribute("data-setting");
if (this.currentSetting != setting) {
this.loadSetting(setting);
} else {
this.closeDrawer();
}
}.bind(this));
$('body').click(function (evt) {
var isInSettingsContainer = $.contains(this.settingsContainer.get(0), evt.target);
if (this.expanded && !isInSettingsContainer) {
this.closeDrawer();
}
}.bind(this));
$.subscribe(Events.CLOSE_SETTINGS_DRAWER, this.closeDrawer.bind(this));
};
ns.SettingsController.prototype.loadSetting = function (setting) {
this.drawerContainer.innerHTML = pskl.utils.Template.get(settings[setting].template);
(new settings[setting].controller(this.piskelController)).init();
this.settingsContainer.addClass(EXP_DRAWER_CLS);
$('.' + SEL_SETTING_CLS).removeClass(SEL_SETTING_CLS);
$('[data-setting='+setting+']').addClass(SEL_SETTING_CLS);
this.expanded = true;
this.currentSetting = setting;
};
ns.SettingsController.prototype.closeDrawer = function () {
this.settingsContainer.removeClass(EXP_DRAWER_CLS);
$('.' + SEL_SETTING_CLS).removeClass(SEL_SETTING_CLS);
this.expanded = false;
this.currentSetting = null;
};
})();

View File

@ -0,0 +1,75 @@
/**
* @provide pskl.drawingtools.BaseTool
*
* @require pskl.utils
*/
(function() {
var ns = $.namespace("pskl.drawingtools");
ns.BaseTool = function() {};
ns.BaseTool.prototype.applyToolAt = function(col, row, color, frame, overlay) {};
ns.BaseTool.prototype.moveToolAt = function(col, row, color, frame, overlay) {};
ns.BaseTool.prototype.moveUnactiveToolAt = function(col, row, color, frame, overlay) {
if (overlay.containsPixel(col, row)) {
if (!isNaN(this.highlightedPixelCol) &&
!isNaN(this.highlightedPixelRow) &&
(this.highlightedPixelRow != row ||
this.highlightedPixelCol != col)) {
// Clean the previously highlighted pixel:
overlay.clear();
}
// Show the current pixel targeted by the tool:
overlay.setPixel(col, row, Constants.TOOL_TARGET_HIGHLIGHT_COLOR);
this.highlightedPixelCol = col;
this.highlightedPixelRow = row;
}
};
ns.BaseTool.prototype.releaseToolAt = function(col, row, color, frame, overlay) {};
/**
* Bresenham line algorihtm: Get an array of pixels from
* start and end coordinates.
*
* http://en.wikipedia.org/wiki/Bresenham's_line_algorithm
* http://stackoverflow.com/questions/4672279/bresenham-algorithm-in-javascript
*
* @private
*/
ns.BaseTool.prototype.getLinePixels_ = function(x0, x1, y0, y1) {
var pixels = [];
var dx = Math.abs(x1-x0);
var dy = Math.abs(y1-y0);
var sx = (x0 < x1) ? 1 : -1;
var sy = (y0 < y1) ? 1 : -1;
var err = dx-dy;
while(true){
// Do what you need to for this
pixels.push({"col": x0, "row": y0});
if ((x0==x1) && (y0==y1)) {
break;
}
var e2 = 2*err;
if (e2>-dy){
err -= dy;
x0 += sx;
}
if (e2 < dx) {
err += dx;
y0 += sy;
}
}
return pixels;
};
})();

View File

@ -0,0 +1,85 @@
/**
* @provide pskl.drawingtools.Circle
*
* @require pskl.utils
*/
(function() {
var ns = $.namespace("pskl.drawingtools");
ns.Circle = function() {
this.toolId = "tool-circle";
this.helpText = "Circle tool";
// Circle's first point coordinates (set in applyToolAt)
this.startCol = null;
this.startRow = null;
};
pskl.utils.inherit(ns.Circle, ns.BaseTool);
/**
* @override
*/
ns.Circle.prototype.applyToolAt = function(col, row, color, frame, overlay) {
this.startCol = col;
this.startRow = row;
// Drawing the first point of the rectangle in the fake overlay canvas:
overlay.setPixel(col, row, color);
};
ns.Circle.prototype.moveToolAt = function(col, row, color, frame, overlay) {
overlay.clear();
if(color == Constants.TRANSPARENT_COLOR) {
color = Constants.SELECTION_TRANSPARENT_COLOR;
}
// draw in overlay
this.drawCircle_(col, row, color, overlay);
};
/**
* @override
*/
ns.Circle.prototype.releaseToolAt = function(col, row, color, frame, overlay) {
overlay.clear();
if(frame.containsPixel(col, row)) { // cancel if outside of canvas
// draw in frame to finalize
this.drawCircle_(col, row, color, frame);
}
};
ns.Circle.prototype.drawCircle_ = function (col, row, color, targetFrame) {
var circlePoints = this.getCirclePixels_(this.startCol, this.startRow, col, row);
for(var i = 0; i< circlePoints.length; i++) {
// Change model:
targetFrame.setPixel(circlePoints[i].col, circlePoints[i].row, color);
}
};
ns.Circle.prototype.getCirclePixels_ = function (x0, y0, x1, y1) {
var coords = pskl.PixelUtils.getOrderedRectangleCoordinates(x0, y0, x1, y1);
var xC = (coords.x0 + coords.x1)/2;
var yC = (coords.y0 + coords.y1)/2;
var rX = coords.x1 - xC;
var rY = coords.y1 - yC;
var pixels = [];
var x, y, angle;
for (x = coords.x0 ; x < coords.x1 ; x++) {
angle = Math.acos((x - xC)/rX);
y = Math.round(rY * Math.sin(angle) + yC);
pixels.push({"col": x, "row": y});
pixels.push({"col": 2*xC - x, "row": 2*yC - y});
}
for (y = coords.y0 ; y < coords.y1 ; y++) {
angle = Math.asin((y - yC)/rY);
x = Math.round(rX * Math.cos(angle) + xC);
pixels.push({"col": x, "row": y});
pixels.push({"col": 2*xC - x, "row": 2*yC - y});
}
return pixels;
};
})();

View File

@ -0,0 +1,29 @@
/**
* @provide pskl.drawingtools.ColorPicker
*
* @require pskl.utils
*/
(function() {
var ns = $.namespace("pskl.drawingtools");
ns.ColorPicker = function() {
this.toolId = "tool-colorpicker";
this.helpText = "Color picker";
};
pskl.utils.inherit(ns.ColorPicker, ns.BaseTool);
/**
* @override
*/
ns.ColorPicker.prototype.applyToolAt = function(col, row, color, frame, overlay, context) {
if (frame.containsPixel(col, row)) {
var sampledColor = frame.getPixel(col, row);
if (context.button == Constants.LEFT_BUTTON) {
$.publish(Events.PRIMARY_COLOR_SELECTED, [sampledColor]);
} else if (context.button == Constants.RIGHT_BUTTON) {
$.publish(Events.SECONDARY_COLOR_SELECTED, [sampledColor]);
}
}
};
})();

View File

@ -0,0 +1,23 @@
/**
* @provide pskl.drawingtools.Eraser
*
* @require Constants
* @require pskl.utils
*/
(function() {
var ns = $.namespace("pskl.drawingtools");
ns.Eraser = function() {
this.toolId = "tool-eraser";
this.helpText = "Eraser tool";
};
pskl.utils.inherit(ns.Eraser, ns.SimplePen);
/**
* @override
*/
ns.Eraser.prototype.applyToolAt = function(col, row, color, frame, overlay) {
this.superclass.applyToolAt.call(this, col, row, Constants.TRANSPARENT_COLOR, frame, overlay);
};
})();

View File

@ -0,0 +1,54 @@
/**
* @provide pskl.drawingtools.Move
*
* @require pskl.utils
*/
(function() {
var ns = $.namespace("pskl.drawingtools");
ns.Move = function() {
this.toolId = "tool-move";
this.helpText = "Move tool";
// Stroke's first point coordinates (set in applyToolAt)
this.startCol = null;
this.startRow = null;
};
pskl.utils.inherit(ns.Move, ns.BaseTool);
/**
* @override
*/
ns.Move.prototype.applyToolAt = function(col, row, color, frame, overlay) {
this.startCol = col;
this.startRow = row;
this.frameClone = frame.clone();
};
ns.Move.prototype.moveToolAt = function(col, row, color, frame, overlay) {
var colDiff = col - this.startCol, rowDiff = row - this.startRow;
this.shiftFrame(colDiff, rowDiff, frame, this.frameClone);
};
ns.Move.prototype.shiftFrame = function (colDiff, rowDiff, frame, reference) {
var color;
for (var col = 0 ; col < frame.getWidth() ; col++) {
for (var row = 0 ; row < frame.getHeight() ; row++) {
if (reference.containsPixel(col - colDiff, row - rowDiff)) {
color = reference.getPixel(col - colDiff, row - rowDiff);
} else {
color = Constants.TRANSPARENT_COLOR;
}
frame.setPixel(col, row, color);
}
}
};
/**
* @override
*/
ns.Move.prototype.releaseToolAt = function(col, row, color, frame, overlay) {
this.moveToolAt(col, row, color, frame, overlay);
};
})();

View File

@ -0,0 +1,36 @@
/**
* @provide pskl.drawingtools.PaintBucket
*
* @require pskl.utils
*/
(function() {
var ns = $.namespace("pskl.drawingtools");
ns.PaintBucket = function() {
this.toolId = "tool-paint-bucket";
this.helpText = "Paint bucket tool";
};
pskl.utils.inherit(ns.PaintBucket, ns.BaseTool);
/**
* @override
*/
ns.PaintBucket.prototype.applyToolAt = function(col, row, color, frame, overlay) {
pskl.PixelUtils.paintSimilarConnectedPixelsFromFrame(frame, col, row, color);
};
})();

View File

@ -0,0 +1,59 @@
/**
* @provide pskl.drawingtools.Rectangle
*
* @require pskl.utils
*/
(function() {
var ns = $.namespace("pskl.drawingtools");
ns.Rectangle = function() {
this.toolId = "tool-rectangle";
this.helpText = "Rectangle tool";
// Rectangle's first point coordinates (set in applyToolAt)
this.startCol = null;
this.startRow = null;
};
pskl.utils.inherit(ns.Rectangle, ns.BaseTool);
/**
* @override
*/
ns.Rectangle.prototype.applyToolAt = function(col, row, color, frame, overlay) {
this.startCol = col;
this.startRow = row;
// Drawing the first point of the rectangle in the fake overlay canvas:
overlay.setPixel(col, row, color);
};
ns.Rectangle.prototype.moveToolAt = function(col, row, color, frame, overlay) {
overlay.clear();
if(color == Constants.TRANSPARENT_COLOR) {
color = Constants.SELECTION_TRANSPARENT_COLOR;
}
// draw in overlay
this.drawRectangle_(col, row, color, overlay);
};
/**
* @override
*/
ns.Rectangle.prototype.releaseToolAt = function(col, row, color, frame, overlay) {
overlay.clear();
if(frame.containsPixel(col, row)) { // cancel if outside of canvas
// draw in frame to finalize
this.drawRectangle_(col, row, color, frame);
}
};
ns.Rectangle.prototype.drawRectangle_ = function (col, row, color, targetFrame) {
var strokePoints = pskl.PixelUtils.getBoundRectanglePixels(this.startCol, this.startRow, col, row);
for(var i = 0; i< strokePoints.length; i++) {
// Change model:
targetFrame.setPixel(strokePoints[i].col, strokePoints[i].row, color);
}
};
})();

View File

@ -0,0 +1,49 @@
/**
* @provide pskl.drawingtools.SimplePen
*
* @require pskl.utils
*/
(function() {
var ns = $.namespace("pskl.drawingtools");
ns.SimplePen = function() {
this.toolId = "tool-pen";
this.helpText = "Pen tool";
this.previousCol = null;
this.previousRow = null;
};
pskl.utils.inherit(ns.SimplePen, ns.BaseTool);
/**
* @override
*/
ns.SimplePen.prototype.applyToolAt = function(col, row, color, frame, overlay) {
if (frame.containsPixel(col, row)) {
frame.setPixel(col, row, color);
}
this.previousCol = col;
this.previousRow = row;
};
ns.SimplePen.prototype.moveToolAt = function(col, row, color, frame, overlay) {
if((Math.abs(col - this.previousCol) > 1) || (Math.abs(row - this.previousRow) > 1)) {
// The pen movement is too fast for the mousemove frequency, there is a gap between the
// current point and the previously drawn one.
// We fill the gap by calculating missing dots (simple linear interpolation) and draw them.
var interpolatedPixels = this.getLinePixels_(col, this.previousCol, row, this.previousRow);
for(var i=0, l=interpolatedPixels.length; i<l; i++) {
var coords = interpolatedPixels[i];
this.applyToolAt(coords.col, coords.row, color, frame, overlay);
}
}
else {
this.applyToolAt(col, row, color, frame, overlay);
}
this.previousCol = col;
this.previousRow = row;
};
})();

View File

@ -0,0 +1,80 @@
/**
* @provide pskl.drawingtools.Stroke
*
* @require pskl.utils
*/
(function() {
var ns = $.namespace("pskl.drawingtools");
ns.Stroke = function() {
this.toolId = "tool-stroke";
this.helpText = "Stroke tool";
// Stroke's first point coordinates (set in applyToolAt)
this.startCol = null;
this.startRow = null;
};
pskl.utils.inherit(ns.Stroke, ns.BaseTool);
/**
* @override
*/
ns.Stroke.prototype.applyToolAt = function(col, row, color, frame, overlay) {
this.startCol = col;
this.startRow = row;
// When drawing a stroke we don't change the model instantly, since the
// user can move his cursor to change the stroke direction and length
// dynamically. Instead we draw the (preview) stroke in a fake canvas that
// overlay the drawing canvas.
// We wait for the releaseToolAt callback to impact both the
// frame model and canvas rendering.
// The fake canvas where we will draw the preview of the stroke:
// Drawing the first point of the stroke in the fake overlay canvas:
overlay.setPixel(col, row, color);
};
ns.Stroke.prototype.moveToolAt = function(col, row, color, frame, overlay) {
overlay.clear();
// When the user moussemove (before releasing), we dynamically compute the
// pixel to draw the line and draw this line in the overlay canvas:
var strokePoints = this.getLinePixels_(this.startCol, col, this.startRow, row);
// Drawing current stroke:
for(var i = 0; i< strokePoints.length; i++) {
if(color == Constants.TRANSPARENT_COLOR) {
// When mousemoving the stroke tool, we draw in the canvas overlay above the drawing canvas.
// If the stroke color is transparent, we won't be
// able to see it during the movement.
// We set it to a semi-opaque white during the tool mousemove allowing to see colors below the stroke.
// When the stroke tool will be released, It will draw a transparent stroke,
// eg deleting the equivalent of a stroke.
color = Constants.SELECTION_TRANSPARENT_COLOR;
}
overlay.setPixel(strokePoints[i].col, strokePoints[i].row, color);
}
};
/**
* @override
*/
ns.Stroke.prototype.releaseToolAt = function(col, row, color, frame, overlay) {
// If the stroke tool is released outside of the canvas, we cancel the stroke:
// TODO: Mutualize this check in common method
if(frame.containsPixel(col, row)) {
// The user released the tool to draw a line. We will compute the pixel coordinate, impact
// the model and draw them in the drawing canvas (not the fake overlay anymore)
var strokePoints = this.getLinePixels_(this.startCol, col, this.startRow, row);
for(var i = 0; i< strokePoints.length; i++) {
// Change model:
frame.setPixel(strokePoints[i].col, strokePoints[i].row, color);
}
}
// For now, we are done with the stroke tool and don't need an overlay anymore:
overlay.clear();
};
})();

View File

@ -0,0 +1,46 @@
(function() {
var ns = $.namespace("pskl.drawingtools");
ns.VerticalMirrorPen = function() {
this.toolId = "tool-vertical-mirror-pen";
this.helpText = "vertical mirror pen tool";
this.swap = null;
this.mirroredPreviousCol = null;
this.mirroredPreviousRow = null;
};
pskl.utils.inherit(ns.VerticalMirrorPen, ns.SimplePen);
ns.VerticalMirrorPen.prototype.setMirrorContext = function() {
this.swap = this.previousCol;
this.previousCol = this.mirroredPreviousCol;
};
ns.VerticalMirrorPen.prototype.unsetMirrorContext = function() {
this.mirroredPreviousCol = this.previousCol;
this.previousCol = this.swap;
};
/**
* @override
*/
ns.VerticalMirrorPen.prototype.applyToolAt = function(col, row, color, frame, overlay) {
this.superclass.applyToolAt.call(this, col, row, color, frame, overlay);
var mirroredCol = this.getSymmetricCol_(col, frame);
this.mirroredPreviousCol = mirroredCol;
this.setMirrorContext();
this.superclass.applyToolAt.call(this, mirroredCol, row, color, frame, overlay);
this.unsetMirrorContext();
};
/**
* @private
*/
ns.VerticalMirrorPen.prototype.getSymmetricCol_ = function(col, frame) {
return frame.getWidth() - col - 1;
};
})();

View File

@ -0,0 +1,160 @@
/**
* @provide pskl.drawingtools.BaseSelect
*
* @require pskl.utils
*/
(function() {
var ns = $.namespace("pskl.drawingtools");
ns.BaseSelect = function() {
this.secondaryToolId = "tool-move";
this.BodyRoot = $('body');
// Select's first point coordinates (set in applyToolAt)
this.startCol = null;
this.startRow = null;
};
pskl.utils.inherit(ns.BaseSelect, ns.BaseTool);
/**
* @override
*/
ns.BaseSelect.prototype.applyToolAt = function(col, row, color, frame, overlay) {
this.startCol = col;
this.startRow = row;
this.lastCol = col;
this.lastRow = row;
// The select tool can be in two different state.
// If the inital click of the tool is not on a selection, we go in "select"
// mode to create a selection.
// If the initial click is on a previous selection, we go in "moveSelection"
// mode to allow to move the selection by drag'n dropping it.
if(overlay.getPixel(col, row) != Constants.SELECTION_TRANSPARENT_COLOR) {
this.mode = "select";
this.onSelectStart_(col, row, color, frame, overlay);
}
else {
this.mode = "moveSelection";
this.onSelectionDragStart_(col, row, color, frame, overlay);
}
};
/**
* @override
*/
ns.BaseSelect.prototype.moveToolAt = function(col, row, color, frame, overlay) {
if(this.mode == "select") {
this.onSelect_(col, row, color, frame, overlay);
}
else if(this.mode == "moveSelection") {
this.onSelectionDrag_(col, row, color, frame, overlay);
}
};
/**
* @override
*/
ns.BaseSelect.prototype.releaseToolAt = function(col, row, color, frame, overlay) {
if(this.mode == "select") {
this.onSelectEnd_(col, row, color, frame, overlay);
} else if(this.mode == "moveSelection") {
this.onSelectionDragEnd_(col, row, color, frame, overlay);
}
};
/**
* If we mouseover the selection draw inside the overlay frame, show the 'move' cursor
* instead of the 'select' one. It indicates that we can move the selection by dragndroping it.
* @override
*/
ns.BaseSelect.prototype.moveUnactiveToolAt = function(col, row, color, frame, overlay) {
if(overlay.getPixel(col, row) != Constants.SELECTION_TRANSPARENT_COLOR) {
// We're hovering the selection, show the move tool:
this.BodyRoot.addClass(this.toolId);
this.BodyRoot.removeClass(this.secondaryToolId);
} else {
// We're not hovering the selection, show create selection tool:
this.BodyRoot.addClass(this.secondaryToolId);
this.BodyRoot.removeClass(this.toolId);
}
};
/**
* For each pixel in the selection draw it in white transparent on the tool overlay
* @protected
*/
ns.BaseSelect.prototype.drawSelectionOnOverlay_ = function (selection, overlay) {
var pixels = selection.pixels;
for(var i=0, l=pixels.length; i<l; i++) {
overlay.setPixel(pixels[i].col, pixels[i].row, Constants.SELECTION_TRANSPARENT_COLOR);
}
};
/**
* Move the overlay frame filled with semi-transparent pixels that represent the selection.
* @private
*/
ns.BaseSelect.prototype.shiftOverlayFrame_ = function (colDiff, rowDiff, overlayFrame, reference) {
var color;
for (var col = 0 ; col < overlayFrame.getWidth() ; col++) {
for (var row = 0 ; row < overlayFrame.getHeight() ; row++) {
if (reference.containsPixel(col - colDiff, row - rowDiff)) {
color = reference.getPixel(col - colDiff, row - rowDiff);
} else {
color = Constants.TRANSPARENT_COLOR;
}
overlayFrame.setPixel(col, row, color);
}
}
};
// The list of callbacks to implement by specialized tools to implement the selection creation behavior.
/** @protected */
ns.BaseSelect.prototype.onSelectStart_ = function (col, row, color, frame, overlay) {};
/** @protected */
ns.BaseSelect.prototype.onSelect_ = function (col, row, color, frame, overlay) {};
/** @protected */
ns.BaseSelect.prototype.onSelectEnd_ = function (col, row, color, frame, overlay) {};
// The list of callbacks that define the drag'n drop behavior of the selection.
/** @private */
ns.BaseSelect.prototype.onSelectionDragStart_ = function (col, row, color, frame, overlay) {
// Since we will move the overlayFrame in which the current selection is rendered,
// we clone it to have a reference for the later shifting process.
this.overlayFrameReference = overlay.clone();
};
/** @private */
ns.BaseSelect.prototype.onSelectionDrag_ = function (col, row, color, frame, overlay) {
var deltaCol = col - this.lastCol;
var deltaRow = row - this.lastRow;
var colDiff = col - this.startCol, rowDiff = row - this.startRow;
// Shifting selection on overlay frame:
this.shiftOverlayFrame_(colDiff, rowDiff, overlay, this.overlayFrameReference);
// Update selection model:
$.publish(Events.SELECTION_MOVE_REQUEST, [deltaCol, deltaRow]);
this.lastCol = col;
this.lastRow = row;
};
/** @private */
ns.BaseSelect.prototype.onSelectionDragEnd_ = function (col, row, color, frame, overlay) {
this.onSelectionDrag_(col, row, color, frame, overlay);
};
})();

View File

@ -0,0 +1,52 @@
/**
* @provide pskl.drawingtools.RectangleSelect
*
* @require pskl.utils
*/
(function() {
var ns = $.namespace("pskl.drawingtools");
ns.RectangleSelect = function() {
this.toolId = "tool-rectangle-select";
this.helpText = "Rectangle selection tool";
ns.BaseSelect.call(this);
};
pskl.utils.inherit(ns.RectangleSelect, ns.BaseSelect);
/**
* @override
*/
ns.RectangleSelect.prototype.onSelectStart_ = function (col, row, color, frame, overlay) {
// Drawing the first point of the rectangle in the fake overlay canvas:
overlay.setPixel(col, row, color);
};
/**
* When creating the rectangle selection, we clear the current overlayFrame and
* redraw the current rectangle based on the orgin coordinate and
* the current mouse coordiinate in sprite.
* @override
*/
ns.RectangleSelect.prototype.onSelect_ = function (col, row, color, frame, overlay) {
overlay.clear();
if(this.startCol == col &&this.startRow == row) {
$.publish(Events.SELECTION_DISMISSED);
} else {
var selection = new pskl.selection.RectangularSelection(
this.startCol, this.startRow, col, row);
$.publish(Events.SELECTION_CREATED, [selection]);
this.drawSelectionOnOverlay_(selection, overlay);
}
};
/**
* @override
*/
ns.RectangleSelect.prototype.onSelectEnd_ = function (col, row, color, frame, overlay) {
this.onSelect_(col, row, color, frame, overlay);
};
})();

View File

@ -0,0 +1,36 @@
/**
* @provide pskl.drawingtools.ShapeSelect
*
* @require pskl.utils
*/
(function() {
var ns = $.namespace("pskl.drawingtools");
ns.ShapeSelect = function() {
this.toolId = "tool-shape-select";
this.helpText = "Shape selection tool";
ns.BaseSelect.call(this);
};
pskl.utils.inherit(ns.ShapeSelect, ns.BaseSelect);
/**
* For the shape select tool, you just need to click one time to create a selection.
* So we jsut need to implement onSelectStart_ (no need for onSelect_ & onSelectEnd_)
* @override
*/
ns.ShapeSelect.prototype.onSelectStart_ = function (col, row, color, frame, overlay) {
// Clean previous selection:
$.publish(Events.SELECTION_DISMISSED);
overlay.clear();
// From the pixel cliked, get shape using an algorithm similar to the paintbucket one:
var pixels = pskl.PixelUtils.getSimilarConnectedPixelsFromFrame(frame, col, row);
var selection = new pskl.selection.ShapeSelection(pixels);
$.publish(Events.SELECTION_CREATED, [selection]);
this.drawSelectionOnOverlay_(selection, overlay);
};
})();

275
preview/js/lib/bootstrap/bootstrap.js vendored Normal file
View File

@ -0,0 +1,275 @@
/* ===========================================================
* bootstrap-tooltip.js v2.1.1
* http://twitter.github.com/bootstrap/javascript.html#tooltips
* Inspired by the original jQuery.tipsy by Jason Frame
* ===========================================================
* Copyright 2012 Twitter, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ========================================================== */
!function ($) {
"use strict"; // jshint ;_;
/* TOOLTIP PUBLIC CLASS DEFINITION
* =============================== */
var Tooltip = function (element, options) {
this.init('tooltip', element, options)
}
Tooltip.prototype = {
constructor: Tooltip
, init: function (type, element, options) {
var eventIn
, eventOut
this.type = type
this.$element = $(element)
this.options = this.getOptions(options)
this.enabled = true
if (this.options.trigger == 'click') {
this.$element.on('click.' + this.type, this.options.selector, $.proxy(this.toggle, this))
} else if (this.options.trigger != 'manual') {
eventIn = this.options.trigger == 'hover' ? 'mouseenter' : 'focus'
eventOut = this.options.trigger == 'hover' ? 'mouseleave' : 'blur'
this.$element.on(eventIn + '.' + this.type, this.options.selector, $.proxy(this.enter, this))
this.$element.on(eventOut + '.' + this.type, this.options.selector, $.proxy(this.leave, this))
}
this.options.selector ?
(this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) :
this.fixTitle()
}
, getOptions: function (options) {
options = $.extend({}, $.fn[this.type].defaults, options, this.$element.data())
if (options.delay && typeof options.delay == 'number') {
options.delay = {
show: options.delay
, hide: options.delay
}
}
return options
}
, enter: function (e) {
var self = $(e.currentTarget)[this.type](this._options).data(this.type)
if (!self.options.delay || !self.options.delay.show) return self.show()
clearTimeout(this.timeout)
self.hoverState = 'in'
this.timeout = setTimeout(function() {
if (self.hoverState == 'in') self.show()
}, self.options.delay.show)
}
, leave: function (e) {
var self = $(e.currentTarget)[this.type](this._options).data(this.type)
if (this.timeout) clearTimeout(this.timeout)
if (!self.options.delay || !self.options.delay.hide) return self.hide()
self.hoverState = 'out'
this.timeout = setTimeout(function() {
if (self.hoverState == 'out') self.hide()
}, self.options.delay.hide)
}
, show: function () {
var $tip
, inside
, pos
, actualWidth
, actualHeight
, placement
, tp
if (this.hasContent() && this.enabled) {
$tip = this.tip()
this.setContent()
if (this.options.animation) {
$tip.addClass('fade')
}
placement = typeof this.options.placement == 'function' ?
this.options.placement.call(this, $tip[0], this.$element[0]) :
this.options.placement
inside = /in/.test(placement)
$tip
.remove()
.css({ top: 0, left: 0, display: 'block' })
.appendTo(inside ? this.$element : document.body)
pos = this.getPosition(inside)
actualWidth = $tip[0].offsetWidth
actualHeight = $tip[0].offsetHeight
switch (inside ? placement.split(' ')[1] : placement) {
case 'bottom':
tp = {top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2}
break
case 'top':
tp = {top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2}
break
case 'left':
tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth}
break
case 'right':
tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width}
break
}
$tip
.css(tp)
.addClass(placement)
.addClass('in')
}
}
, setContent: function () {
var $tip = this.tip()
, title = this.getTitle()
$tip.find('.tooltip-inner')[this.options.html ? 'html' : 'text'](title)
$tip.removeClass('fade in top bottom left right')
}
, hide: function () {
var that = this
, $tip = this.tip()
$tip.removeClass('in')
function removeWithAnimation() {
var timeout = setTimeout(function () {
$tip.off($.support.transition.end).remove()
}, 500)
$tip.one($.support.transition.end, function () {
clearTimeout(timeout)
$tip.remove()
})
}
$.support.transition && this.$tip.hasClass('fade') ?
removeWithAnimation() :
$tip.remove()
return this
}
, fixTitle: function () {
var $e = this.$element
if ($e.attr('title') || typeof($e.attr('data-original-title')) != 'string') {
$e.attr('data-original-title', $e.attr('title') || '').removeAttr('title')
}
}
, hasContent: function () {
return this.getTitle()
}
, getPosition: function (inside) {
return $.extend({}, (inside ? {top: 0, left: 0} : this.$element.offset()), {
width: this.$element[0].offsetWidth
, height: this.$element[0].offsetHeight
})
}
, getTitle: function () {
var title
, $e = this.$element
, o = this.options
title = $e.attr('data-original-title')
|| (typeof o.title == 'function' ? o.title.call($e[0]) : o.title)
return title
}
, tip: function () {
return this.$tip = this.$tip || $(this.options.template)
}
, validate: function () {
if (!this.$element[0].parentNode) {
this.hide()
this.$element = null
this.options = null
}
}
, enable: function () {
this.enabled = true
}
, disable: function () {
this.enabled = false
}
, toggleEnabled: function () {
this.enabled = !this.enabled
}
, toggle: function () {
this[this.tip().hasClass('in') ? 'hide' : 'show']()
}
, destroy: function () {
this.hide().$element.off('.' + this.type).removeData(this.type)
}
}
/* TOOLTIP PLUGIN DEFINITION
* ========================= */
$.fn.tooltip = function ( option ) {
return this.each(function () {
var $this = $(this)
, data = $this.data('tooltip')
, options = typeof option == 'object' && option
if (!data) $this.data('tooltip', (data = new Tooltip(this, options)))
if (typeof option == 'string') data[option]()
})
}
$.fn.tooltip.Constructor = Tooltip
$.fn.tooltip.defaults = {
animation: true
, placement: 'top'
, selector: false
, template: '<div class="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>'
, trigger: 'hover'
, title: ''
, delay: 0
, html: true
}
}(window.jQuery);

View File

@ -0,0 +1 @@
Bootstrap custom build containing only the tooltip component

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,52 @@
(function () {
/**
* Depending on the value of the data-iframe-loader attribute, display or store the content of the iframe
* @param {HTMLElement} iframe
*/
var processFrame = function (iframe) {
var type = iframe.getAttribute('data-iframe-loader');
if (type === "display") {
displayFrame(iframe);
} else if (type === "store") {
storeFrame(iframe);
} else {
console.error('iframeLoader invalid type : ' + type);
}
};
/**
* Replace the existing iframe with the content of the iframe
* If the iframe has a single root element, it will directly replace the iframe.
* If there are several roots, a wrapping div will be created and will replace the iframe
* @param {HTMLElement} iframe
*/
var displayFrame = function (iframe) {
var div=document.createElement("div");
div.innerHTML = iframe.contentWindow.document.body.innerHTML;
if (div.children.length == 1) {
div = div.children[0];
}
iframe.parentNode.replaceChild(div, iframe);
};
/**
* Load the iframe content as a <script type="text/html" id={iframe-src}>{iframe-content}</script>
* The content can later be accessed by getting the script (through getElementById for instance) and reading innerHTML
* @param {HTMLElement} iframe
*/
var storeFrame = function (iframe) {
var script=document.createElement("script");
script.setAttribute("type", "text/html");
script.setAttribute("id", iframe.getAttribute("src"));
script.innerHTML = iframe.contentWindow.document.body.innerHTML;
iframe.parentNode.removeChild(iframe);
document.body.appendChild(script);
};
window.iframeloader = {
onLoad : function (event) {
var iframe = event.target || event.srcElement;
processFrame(iframe);
}
}
})();

9227
preview/js/lib/jquery-1.8.0.js vendored Normal file

File diff suppressed because it is too large Load Diff

2252
preview/js/lib/jquery-ui-1.10.3.custom.js vendored Normal file

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 B

View File

@ -0,0 +1,12 @@
<html>
<head>
<title>jscolor demo</title>
</head>
<body>
<script type="text/javascript" src="jscolor.js"></script>
Click here: <input class="color" value="66ff00">
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@ -0,0 +1,927 @@
/**
* jscolor, JavaScript Color Picker
*
* @version 1.4.0
* @license GNU Lesser General Public License, http://www.gnu.org/copyleft/lesser.html
* @author Jan Odvarko, http://odvarko.cz
* @created 2008-06-15
* @updated 2012-07-06
* @link http://jscolor.com
*/
var jscolor = {
dir : '', // location of jscolor directory (leave empty to autodetect)
bindClass : 'color', // class name
binding : true, // automatic binding via <input class="...">
preloading : true, // use image preloading?
install : function() {
if (document.readyState === "complete") {
jscolor.init();
} else {
jscolor.addEvent(window, 'load', jscolor.init);
}
},
init : function() {
if(jscolor.binding) {
jscolor.bind();
}
if(jscolor.preloading) {
jscolor.preload();
}
},
getDir : function() {
return "/" + window.location.pathname.split("/")[1] + "/js/lib/jsColor_1_4_0/";
},
bind : function() {
var matchClass = new RegExp('(^|\\s)('+jscolor.bindClass+')\\s*(\\{[^}]*\\})?', 'i');
var e = document.getElementsByTagName('input');
for(var i=0; i<e.length; i+=1) {
var m;
if(!e[i].color && e[i].className && (m = e[i].className.match(matchClass))) {
var prop = {};
if(m[3]) {
try {
prop = (new Function ('return (' + m[3] + ')'))();
} catch(eInvalidProp) {}
}
e[i].color = new jscolor.color(e[i], prop);
}
}
},
preload : function() {
for(var fn in jscolor.imgRequire) {
if(jscolor.imgRequire.hasOwnProperty(fn)) {
jscolor.loadImage(fn);
}
}
},
images : {
pad : [ 181, 101 ],
sld : [ 16, 101 ],
cross : [ 15, 15 ],
arrow : [ 7, 11 ]
},
imgRequire : {},
imgLoaded : {},
requireImage : function(filename) {
jscolor.imgRequire[filename] = true;
},
loadImage : function(filename) {
if(!jscolor.imgLoaded[filename]) {
jscolor.imgLoaded[filename] = new Image();
jscolor.imgLoaded[filename].src = jscolor.getDir()+filename;
}
},
fetchElement : function(mixed) {
return typeof mixed === 'string' ? document.getElementById(mixed) : mixed;
},
addEvent : function(el, evnt, func) {
if(el.addEventListener) {
el.addEventListener(evnt, func, false);
} else if(el.attachEvent) {
el.attachEvent('on'+evnt, func);
}
},
fireEvent : function(el, evnt) {
if(!el) {
return;
}
if(document.createEvent) {
var ev = document.createEvent('HTMLEvents');
ev.initEvent(evnt, true, true);
el.dispatchEvent(ev);
} else if(document.createEventObject) {
var ev = document.createEventObject();
el.fireEvent('on'+evnt, ev);
} else if(el['on'+evnt]) { // alternatively use the traditional event model (IE5)
el['on'+evnt]();
}
},
getElementPos : function(e) {
var e1=e, e2=e;
var x=0, y=0;
if(e1.offsetParent) {
do {
x += e1.offsetLeft;
y += e1.offsetTop;
} while(e1 = e1.offsetParent);
}
while((e2 = e2.parentNode) && e2.nodeName.toUpperCase() !== 'BODY') {
x -= e2.scrollLeft;
y -= e2.scrollTop;
}
return [x, y];
},
getElementSize : function(e) {
return [e.offsetWidth, e.offsetHeight];
},
getRelMousePos : function(e) {
var x = 0, y = 0;
if (!e) { e = window.event; }
if (typeof e.offsetX === 'number') {
x = e.offsetX;
y = e.offsetY;
} else if (typeof e.layerX === 'number') {
x = e.layerX;
y = e.layerY;
}
return { x: x, y: y };
},
getViewPos : function() {
if(typeof window.pageYOffset === 'number') {
return [window.pageXOffset, window.pageYOffset];
} else if(document.body && (document.body.scrollLeft || document.body.scrollTop)) {
return [document.body.scrollLeft, document.body.scrollTop];
} else if(document.documentElement && (document.documentElement.scrollLeft || document.documentElement.scrollTop)) {
return [document.documentElement.scrollLeft, document.documentElement.scrollTop];
} else {
return [0, 0];
}
},
getViewSize : function() {
if(typeof window.innerWidth === 'number') {
return [window.innerWidth, window.innerHeight];
} else if(document.body && (document.body.clientWidth || document.body.clientHeight)) {
return [document.body.clientWidth, document.body.clientHeight];
} else if(document.documentElement && (document.documentElement.clientWidth || document.documentElement.clientHeight)) {
return [document.documentElement.clientWidth, document.documentElement.clientHeight];
} else {
return [0, 0];
}
},
URI : function(uri) { // See RFC3986
this.scheme = null;
this.authority = null;
this.path = '';
this.query = null;
this.fragment = null;
this.parse = function(uri) {
var m = uri.match(/^(([A-Za-z][0-9A-Za-z+.-]*)(:))?((\/\/)([^\/?#]*))?([^?#]*)((\?)([^#]*))?((#)(.*))?/);
this.scheme = m[3] ? m[2] : null;
this.authority = m[5] ? m[6] : null;
this.path = m[7];
this.query = m[9] ? m[10] : null;
this.fragment = m[12] ? m[13] : null;
return this;
};
this.toString = function() {
var result = '';
if(this.scheme !== null) { result = result + this.scheme + ':'; }
if(this.authority !== null) { result = result + '//' + this.authority; }
if(this.path !== null) { result = result + this.path; }
if(this.query !== null) { result = result + '?' + this.query; }
if(this.fragment !== null) { result = result + '#' + this.fragment; }
return result;
};
this.toAbsolute = function(base) {
var base = new jscolor.URI(base);
var r = this;
var t = new jscolor.URI;
if(base.scheme === null) { return false; }
if(r.scheme !== null && r.scheme.toLowerCase() === base.scheme.toLowerCase()) {
r.scheme = null;
}
if(r.scheme !== null) {
t.scheme = r.scheme;
t.authority = r.authority;
t.path = removeDotSegments(r.path);
t.query = r.query;
} else {
if(r.authority !== null) {
t.authority = r.authority;
t.path = removeDotSegments(r.path);
t.query = r.query;
} else {
if(r.path === '') {
t.path = base.path;
if(r.query !== null) {
t.query = r.query;
} else {
t.query = base.query;
}
} else {
if(r.path.substr(0,1) === '/') {
t.path = removeDotSegments(r.path);
} else {
if(base.authority !== null && base.path === '') {
t.path = '/'+r.path;
} else {
t.path = base.path.replace(/[^\/]+$/,'')+r.path;
}
t.path = removeDotSegments(t.path);
}
t.query = r.query;
}
t.authority = base.authority;
}
t.scheme = base.scheme;
}
t.fragment = r.fragment;
return t;
};
function removeDotSegments(path) {
var out = '';
while(path) {
if(path.substr(0,3)==='../' || path.substr(0,2)==='./') {
path = path.replace(/^\.+/,'').substr(1);
} else if(path.substr(0,3)==='/./' || path==='/.') {
path = '/'+path.substr(3);
} else if(path.substr(0,4)==='/../' || path==='/..') {
path = '/'+path.substr(4);
out = out.replace(/\/?[^\/]*$/, '');
} else if(path==='.' || path==='..') {
path = '';
} else {
var rm = path.match(/^\/?[^\/]*/)[0];
path = path.substr(rm.length);
out = out + rm;
}
}
return out;
}
if(uri) {
this.parse(uri);
}
},
/*
* Usage example:
* var myColor = new jscolor.color(myInputElement)
*/
color : function(target, prop) {
this.required = true; // refuse empty values?
this.adjust = true; // adjust value to uniform notation?
this.hash = false; // prefix color with # symbol?
this.caps = true; // uppercase?
this.slider = true; // show the value/saturation slider?
this.valueElement = target; // value holder
this.styleElement = target; // where to reflect current color
this.onImmediateChange = null; // onchange callback (can be either string or function)
this.hsv = [0, 0, 1]; // read-only 0-6, 0-1, 0-1
this.rgb = [1, 1, 1]; // read-only 0-1, 0-1, 0-1
this.minH = 0; // read-only 0-6
this.maxH = 6; // read-only 0-6
this.minS = 0; // read-only 0-1
this.maxS = 1; // read-only 0-1
this.minV = 0; // read-only 0-1
this.maxV = 1; // read-only 0-1
this.pickerOnfocus = true; // display picker on focus?
this.pickerMode = 'HSV'; // HSV | HVS
this.pickerPosition = 'bottom'; // left | right | top | bottom
this.pickerSmartPosition = true; // automatically adjust picker position when necessary
this.pickerButtonHeight = 20; // px
this.pickerClosable = false;
this.pickerCloseText = 'Close';
this.pickerButtonColor = 'ButtonText'; // px
this.pickerFace = 10; // px
this.pickerFaceColor = 'ThreeDFace'; // CSS color
this.pickerBorder = 1; // px
this.pickerBorderColor = 'ThreeDHighlight ThreeDShadow ThreeDShadow ThreeDHighlight'; // CSS color
this.pickerInset = 1; // px
this.pickerInsetColor = 'ThreeDShadow ThreeDHighlight ThreeDHighlight ThreeDShadow'; // CSS color
this.pickerZIndex = 10000;
for(var p in prop) {
if(prop.hasOwnProperty(p)) {
this[p] = prop[p];
}
}
this.hidePicker = function() {
if(isPickerOwner()) {
removePicker();
}
};
this.showPicker = function() {
if(!isPickerOwner()) {
var tp = jscolor.getElementPos(target); // target pos
var ts = jscolor.getElementSize(target); // target size
var vp = jscolor.getViewPos(); // view pos
var vs = jscolor.getViewSize(); // view size
var ps = getPickerDims(this); // picker size
var a, b, c;
switch(this.pickerPosition.toLowerCase()) {
case 'left': a=1; b=0; c=-1; break;
case 'right':a=1; b=0; c=1; break;
case 'top': a=0; b=1; c=-1; break;
default: a=0; b=1; c=1; break;
}
var l = (ts[b]+ps[b])/2;
// picker pos
if (!this.pickerSmartPosition) {
var pp = [
tp[a],
tp[b]+ts[b]-l+l*c
];
} else {
var pp = [
-vp[a]+tp[a]+ps[a] > vs[a] ?
(-vp[a]+tp[a]+ts[a]/2 > vs[a]/2 && tp[a]+ts[a]-ps[a] >= 0 ? tp[a]+ts[a]-ps[a] : tp[a]) :
tp[a],
-vp[b]+tp[b]+ts[b]+ps[b]-l+l*c > vs[b] ?
(-vp[b]+tp[b]+ts[b]/2 > vs[b]/2 && tp[b]+ts[b]-l-l*c >= 0 ? tp[b]+ts[b]-l-l*c : tp[b]+ts[b]-l+l*c) :
(tp[b]+ts[b]-l+l*c >= 0 ? tp[b]+ts[b]-l+l*c : tp[b]+ts[b]-l-l*c)
];
}
drawPicker(pp[a], pp[b]);
}
};
this.importColor = function() {
if(!valueElement) {
this.exportColor();
} else {
if(!this.adjust) {
if(!this.fromString(valueElement.value, leaveValue)) {
styleElement.style.backgroundImage = styleElement.jscStyle.backgroundImage;
styleElement.style.backgroundColor = styleElement.jscStyle.backgroundColor;
styleElement.style.color = styleElement.jscStyle.color;
this.exportColor(leaveValue | leaveStyle);
}
} else if(!this.required && /^\s*$/.test(valueElement.value)) {
valueElement.value = '';
styleElement.style.backgroundImage = styleElement.jscStyle.backgroundImage;
styleElement.style.backgroundColor = styleElement.jscStyle.backgroundColor;
styleElement.style.color = styleElement.jscStyle.color;
this.exportColor(leaveValue | leaveStyle);
} else if(this.fromString(valueElement.value)) {
// OK
} else {
this.exportColor();
}
}
};
this.exportColor = function(flags) {
if(!(flags & leaveValue) && valueElement) {
var value = this.toString();
if(this.caps) { value = value.toUpperCase(); }
if(this.hash) { value = '#'+value; }
valueElement.value = value;
}
if(!(flags & leaveStyle) && styleElement) {
styleElement.style.backgroundImage = "none";
styleElement.style.backgroundColor =
'#'+this.toString();
styleElement.style.color =
0.213 * this.rgb[0] +
0.715 * this.rgb[1] +
0.072 * this.rgb[2]
< 0.5 ? '#FFF' : '#000';
}
if(!(flags & leavePad) && isPickerOwner()) {
redrawPad();
}
if(!(flags & leaveSld) && isPickerOwner()) {
redrawSld();
}
};
this.fromHSV = function(h, s, v, flags) { // null = don't change
if(h !== null) { h = Math.max(0.0, this.minH, Math.min(6.0, this.maxH, h)); }
if(s !== null) { s = Math.max(0.0, this.minS, Math.min(1.0, this.maxS, s)); }
if(v !== null) { v = Math.max(0.0, this.minV, Math.min(1.0, this.maxV, v)); }
this.rgb = HSV_RGB(
h===null ? this.hsv[0] : (this.hsv[0]=h),
s===null ? this.hsv[1] : (this.hsv[1]=s),
v===null ? this.hsv[2] : (this.hsv[2]=v)
);
this.exportColor(flags);
};
this.fromRGB = function(r, g, b, flags) { // null = don't change
if(r !== null) { r = Math.max(0.0, Math.min(1.0, r)); }
if(g !== null) { g = Math.max(0.0, Math.min(1.0, g)); }
if(b !== null) { b = Math.max(0.0, Math.min(1.0, b)); }
var hsv = RGB_HSV(
r===null ? this.rgb[0] : r,
g===null ? this.rgb[1] : g,
b===null ? this.rgb[2] : b
);
if(hsv[0] !== null) {
this.hsv[0] = Math.max(0.0, this.minH, Math.min(6.0, this.maxH, hsv[0]));
}
if(hsv[2] !== 0) {
this.hsv[1] = hsv[1]===null ? null : Math.max(0.0, this.minS, Math.min(1.0, this.maxS, hsv[1]));
}
this.hsv[2] = hsv[2]===null ? null : Math.max(0.0, this.minV, Math.min(1.0, this.maxV, hsv[2]));
// update RGB according to final HSV, as some values might be trimmed
var rgb = HSV_RGB(this.hsv[0], this.hsv[1], this.hsv[2]);
this.rgb[0] = rgb[0];
this.rgb[1] = rgb[1];
this.rgb[2] = rgb[2];
this.exportColor(flags);
};
this.fromString = function(hex, flags) {
var m = hex.match(/^\W*([0-9A-F]{3}([0-9A-F]{3})?)\W*$/i);
if(!m) {
return false;
} else {
if(m[1].length === 6) { // 6-char notation
this.fromRGB(
parseInt(m[1].substr(0,2),16) / 255,
parseInt(m[1].substr(2,2),16) / 255,
parseInt(m[1].substr(4,2),16) / 255,
flags
);
} else { // 3-char notation
this.fromRGB(
parseInt(m[1].charAt(0)+m[1].charAt(0),16) / 255,
parseInt(m[1].charAt(1)+m[1].charAt(1),16) / 255,
parseInt(m[1].charAt(2)+m[1].charAt(2),16) / 255,
flags
);
}
return true;
}
};
this.toString = function() {
return (
(0x100 | Math.round(255*this.rgb[0])).toString(16).substr(1) +
(0x100 | Math.round(255*this.rgb[1])).toString(16).substr(1) +
(0x100 | Math.round(255*this.rgb[2])).toString(16).substr(1)
);
};
function RGB_HSV(r, g, b) {
var n = Math.min(Math.min(r,g),b);
var v = Math.max(Math.max(r,g),b);
var m = v - n;
if(m === 0) { return [ null, 0, v ]; }
var h = r===n ? 3+(b-g)/m : (g===n ? 5+(r-b)/m : 1+(g-r)/m);
return [ h===6?0:h, m/v, v ];
}
function HSV_RGB(h, s, v) {
if(h === null) { return [ v, v, v ]; }
var i = Math.floor(h);
var f = i%2 ? h-i : 1-(h-i);
var m = v * (1 - s);
var n = v * (1 - s*f);
switch(i) {
case 6:
case 0: return [v,n,m];
case 1: return [n,v,m];
case 2: return [m,v,n];
case 3: return [m,n,v];
case 4: return [n,m,v];
case 5: return [v,m,n];
}
}
function removePicker() {
delete jscolor.picker.owner;
document.getElementsByTagName('body')[0].removeChild(jscolor.picker.boxB);
}
function drawPicker(x, y) {
if(!jscolor.picker) {
jscolor.picker = {
box : document.createElement('div'),
boxB : document.createElement('div'),
pad : document.createElement('div'),
padB : document.createElement('div'),
padM : document.createElement('div'),
sld : document.createElement('div'),
sldB : document.createElement('div'),
sldM : document.createElement('div'),
btn : document.createElement('div'),
btnS : document.createElement('span'),
btnT : document.createTextNode(THIS.pickerCloseText)
};
for(var i=0,segSize=4; i<jscolor.images.sld[1]; i+=segSize) {
var seg = document.createElement('div');
seg.style.height = segSize+'px';
seg.style.fontSize = '1px';
seg.style.lineHeight = '0';
jscolor.picker.sld.appendChild(seg);
}
jscolor.picker.sldB.appendChild(jscolor.picker.sld);
jscolor.picker.box.appendChild(jscolor.picker.sldB);
jscolor.picker.box.appendChild(jscolor.picker.sldM);
jscolor.picker.padB.appendChild(jscolor.picker.pad);
jscolor.picker.box.appendChild(jscolor.picker.padB);
jscolor.picker.box.appendChild(jscolor.picker.padM);
jscolor.picker.btnS.appendChild(jscolor.picker.btnT);
jscolor.picker.btn.appendChild(jscolor.picker.btnS);
jscolor.picker.box.appendChild(jscolor.picker.btn);
jscolor.picker.boxB.appendChild(jscolor.picker.box);
}
var p = jscolor.picker;
// controls interaction
p.box.onmouseup =
p.box.onmouseout = function() { target.focus(); };
p.box.onmousedown = function() { abortBlur=true; };
p.box.onmousemove = function(e) {
if (holdPad || holdSld) {
holdPad && setPad(e);
holdSld && setSld(e);
if (document.selection) {
document.selection.empty();
} else if (window.getSelection) {
window.getSelection().removeAllRanges();
}
dispatchImmediateChange();
}
};
p.padM.onmouseup =
p.padM.onmouseout = function() { if(holdPad) { holdPad=false; jscolor.fireEvent(valueElement,'change'); } };
p.padM.onmousedown = function(e) {
// if the slider is at the bottom, move it up
switch(modeID) {
case 0: if (THIS.hsv[2] === 0) { THIS.fromHSV(null, null, 1.0); }; break;
case 1: if (THIS.hsv[1] === 0) { THIS.fromHSV(null, 1.0, null); }; break;
}
holdPad=true;
setPad(e);
dispatchImmediateChange();
};
p.sldM.onmouseup =
p.sldM.onmouseout = function() { if(holdSld) { holdSld=false; jscolor.fireEvent(valueElement,'change'); } };
p.sldM.onmousedown = function(e) {
holdSld=true;
setSld(e);
dispatchImmediateChange();
};
// picker
var dims = getPickerDims(THIS);
p.box.style.width = dims[0] + 'px';
p.box.style.height = dims[1] + 'px';
// picker border
p.boxB.style.position = 'absolute';
p.boxB.style.clear = 'both';
p.boxB.style.left = x+'px';
p.boxB.style.top = y+'px';
p.boxB.style.zIndex = THIS.pickerZIndex;
p.boxB.style.border = THIS.pickerBorder+'px solid';
p.boxB.style.borderColor = THIS.pickerBorderColor;
p.boxB.style.background = THIS.pickerFaceColor;
// pad image
p.pad.style.width = jscolor.images.pad[0]+'px';
p.pad.style.height = jscolor.images.pad[1]+'px';
// pad border
p.padB.style.position = 'absolute';
p.padB.style.left = THIS.pickerFace+'px';
p.padB.style.top = THIS.pickerFace+'px';
p.padB.style.border = THIS.pickerInset+'px solid';
p.padB.style.borderColor = THIS.pickerInsetColor;
// pad mouse area
p.padM.style.position = 'absolute';
p.padM.style.left = '0';
p.padM.style.top = '0';
p.padM.style.width = THIS.pickerFace + 2*THIS.pickerInset + jscolor.images.pad[0] + jscolor.images.arrow[0] + 'px';
p.padM.style.height = p.box.style.height;
p.padM.style.cursor = 'crosshair';
// slider image
p.sld.style.overflow = 'hidden';
p.sld.style.width = jscolor.images.sld[0]+'px';
p.sld.style.height = jscolor.images.sld[1]+'px';
// slider border
p.sldB.style.display = THIS.slider ? 'block' : 'none';
p.sldB.style.position = 'absolute';
p.sldB.style.right = THIS.pickerFace+'px';
p.sldB.style.top = THIS.pickerFace+'px';
p.sldB.style.border = THIS.pickerInset+'px solid';
p.sldB.style.borderColor = THIS.pickerInsetColor;
// slider mouse area
p.sldM.style.display = THIS.slider ? 'block' : 'none';
p.sldM.style.position = 'absolute';
p.sldM.style.right = '0';
p.sldM.style.top = '0';
p.sldM.style.width = jscolor.images.sld[0] + jscolor.images.arrow[0] + THIS.pickerFace + 2*THIS.pickerInset + 'px';
p.sldM.style.height = p.box.style.height;
try {
p.sldM.style.cursor = 'pointer';
} catch(eOldIE) {
p.sldM.style.cursor = 'hand';
}
// "close" button
function setBtnBorder() {
var insetColors = THIS.pickerInsetColor.split(/\s+/);
var pickerOutsetColor = insetColors.length < 2 ? insetColors[0] : insetColors[1] + ' ' + insetColors[0] + ' ' + insetColors[0] + ' ' + insetColors[1];
p.btn.style.borderColor = pickerOutsetColor;
}
p.btn.style.display = THIS.pickerClosable ? 'block' : 'none';
p.btn.style.position = 'absolute';
p.btn.style.left = THIS.pickerFace + 'px';
p.btn.style.bottom = THIS.pickerFace + 'px';
p.btn.style.padding = '0 15px';
p.btn.style.height = '18px';
p.btn.style.border = THIS.pickerInset + 'px solid';
setBtnBorder();
p.btn.style.color = THIS.pickerButtonColor;
p.btn.style.font = '12px sans-serif';
p.btn.style.textAlign = 'center';
try {
p.btn.style.cursor = 'pointer';
} catch(eOldIE) {
p.btn.style.cursor = 'hand';
}
p.btn.onmousedown = function () {
THIS.hidePicker();
};
p.btnS.style.lineHeight = p.btn.style.height;
// load images in optimal order
switch(modeID) {
case 0: var padImg = 'hs.png'; break;
case 1: var padImg = 'hv.png'; break;
}
p.padM.style.backgroundImage = "url('"+jscolor.getDir()+"cross.gif')";
p.padM.style.backgroundRepeat = "no-repeat";
p.sldM.style.backgroundImage = "url('"+jscolor.getDir()+"arrow.gif')";
p.sldM.style.backgroundRepeat = "no-repeat";
p.pad.style.backgroundImage = "url('"+jscolor.getDir()+padImg+"')";
p.pad.style.backgroundRepeat = "no-repeat";
p.pad.style.backgroundPosition = "0 0";
// place pointers
redrawPad();
redrawSld();
jscolor.picker.owner = THIS;
document.getElementsByTagName('body')[0].appendChild(p.boxB);
}
function getPickerDims(o) {
var dims = [
2*o.pickerInset + 2*o.pickerFace + jscolor.images.pad[0] +
(o.slider ? 2*o.pickerInset + 2*jscolor.images.arrow[0] + jscolor.images.sld[0] : 0),
o.pickerClosable ?
4*o.pickerInset + 3*o.pickerFace + jscolor.images.pad[1] + o.pickerButtonHeight :
2*o.pickerInset + 2*o.pickerFace + jscolor.images.pad[1]
];
return dims;
}
function redrawPad() {
// redraw the pad pointer
switch(modeID) {
case 0: var yComponent = 1; break;
case 1: var yComponent = 2; break;
}
var x = Math.round((THIS.hsv[0]/6) * (jscolor.images.pad[0]-1));
var y = Math.round((1-THIS.hsv[yComponent]) * (jscolor.images.pad[1]-1));
jscolor.picker.padM.style.backgroundPosition =
(THIS.pickerFace+THIS.pickerInset+x - Math.floor(jscolor.images.cross[0]/2)) + 'px ' +
(THIS.pickerFace+THIS.pickerInset+y - Math.floor(jscolor.images.cross[1]/2)) + 'px';
// redraw the slider image
var seg = jscolor.picker.sld.childNodes;
switch(modeID) {
case 0:
var rgb = HSV_RGB(THIS.hsv[0], THIS.hsv[1], 1);
for(var i=0; i<seg.length; i+=1) {
seg[i].style.backgroundColor = 'rgb('+
(rgb[0]*(1-i/seg.length)*100)+'%,'+
(rgb[1]*(1-i/seg.length)*100)+'%,'+
(rgb[2]*(1-i/seg.length)*100)+'%)';
}
break;
case 1:
var rgb, s, c = [ THIS.hsv[2], 0, 0 ];
var i = Math.floor(THIS.hsv[0]);
var f = i%2 ? THIS.hsv[0]-i : 1-(THIS.hsv[0]-i);
switch(i) {
case 6:
case 0: rgb=[0,1,2]; break;
case 1: rgb=[1,0,2]; break;
case 2: rgb=[2,0,1]; break;
case 3: rgb=[2,1,0]; break;
case 4: rgb=[1,2,0]; break;
case 5: rgb=[0,2,1]; break;
}
for(var i=0; i<seg.length; i+=1) {
s = 1 - 1/(seg.length-1)*i;
c[1] = c[0] * (1 - s*f);
c[2] = c[0] * (1 - s);
seg[i].style.backgroundColor = 'rgb('+
(c[rgb[0]]*100)+'%,'+
(c[rgb[1]]*100)+'%,'+
(c[rgb[2]]*100)+'%)';
}
break;
}
}
function redrawSld() {
// redraw the slider pointer
switch(modeID) {
case 0: var yComponent = 2; break;
case 1: var yComponent = 1; break;
}
var y = Math.round((1-THIS.hsv[yComponent]) * (jscolor.images.sld[1]-1));
jscolor.picker.sldM.style.backgroundPosition =
'0 ' + (THIS.pickerFace+THIS.pickerInset+y - Math.floor(jscolor.images.arrow[1]/2)) + 'px';
}
function isPickerOwner() {
return jscolor.picker && jscolor.picker.owner === THIS;
}
function blurTarget() {
if(valueElement === target) {
THIS.importColor();
}
if(THIS.pickerOnfocus) {
THIS.hidePicker();
}
}
function blurValue() {
if(valueElement !== target) {
THIS.importColor();
}
}
function setPad(e) {
var mpos = jscolor.getRelMousePos(e);
var x = mpos.x - THIS.pickerFace - THIS.pickerInset;
var y = mpos.y - THIS.pickerFace - THIS.pickerInset;
switch(modeID) {
case 0: THIS.fromHSV(x*(6/(jscolor.images.pad[0]-1)), 1 - y/(jscolor.images.pad[1]-1), null, leaveSld); break;
case 1: THIS.fromHSV(x*(6/(jscolor.images.pad[0]-1)), null, 1 - y/(jscolor.images.pad[1]-1), leaveSld); break;
}
}
function setSld(e) {
var mpos = jscolor.getRelMousePos(e);
var y = mpos.y - THIS.pickerFace - THIS.pickerInset;
switch(modeID) {
case 0: THIS.fromHSV(null, null, 1 - y/(jscolor.images.sld[1]-1), leavePad); break;
case 1: THIS.fromHSV(null, 1 - y/(jscolor.images.sld[1]-1), null, leavePad); break;
}
}
function dispatchImmediateChange() {
if (THIS.onImmediateChange) {
var callback;
if (typeof THIS.onImmediateChange === 'string') {
callback = new Function (THIS.onImmediateChange);
} else {
callback = THIS.onImmediateChange;
}
callback.call(THIS);
}
}
var THIS = this;
var modeID = this.pickerMode.toLowerCase()==='hvs' ? 1 : 0;
var abortBlur = false;
var
valueElement = jscolor.fetchElement(this.valueElement),
styleElement = jscolor.fetchElement(this.styleElement);
var
holdPad = false,
holdSld = false;
var
leaveValue = 1<<0,
leaveStyle = 1<<1,
leavePad = 1<<2,
leaveSld = 1<<3;
// target
jscolor.addEvent(target, 'focus', function() {
if(THIS.pickerOnfocus) { THIS.showPicker(); }
});
jscolor.addEvent(target, 'blur', function() {
if(!abortBlur) {
window.setTimeout(function(){ abortBlur || blurTarget(); abortBlur=false; }, 0);
} else {
abortBlur = false;
}
});
// valueElement
if(valueElement) {
var updateField = function() {
THIS.fromString(valueElement.value, leaveValue);
dispatchImmediateChange();
};
jscolor.addEvent(valueElement, 'keyup', updateField);
jscolor.addEvent(valueElement, 'input', updateField);
jscolor.addEvent(valueElement, 'blur', blurValue);
valueElement.setAttribute('autocomplete', 'off');
}
// styleElement
if(styleElement) {
styleElement.jscStyle = {
backgroundImage : styleElement.style.backgroundImage,
backgroundColor : styleElement.style.backgroundColor,
color : styleElement.style.color
};
}
// require images
switch(modeID) {
case 0: jscolor.requireImage('hs.png'); break;
case 1: jscolor.requireImage('hv.png'); break;
}
jscolor.requireImage('cross.gif');
jscolor.requireImage('arrow.gif');
this.importColor();
}
};

Some files were not shown because too many files have changed in this diff Show More