diff --git a/css/settings.css b/css/settings.css index a56bbeb4..9d147179 100644 --- a/css/settings.css +++ b/css/settings.css @@ -28,6 +28,7 @@ width: 280px; border-top-left-radius: 4px; border-bottom-left-radius: 4px; + box-shadow: 0 0 5px 0 black; } .right-sticky-section.expanded .tool-icon { @@ -39,6 +40,7 @@ background-color: #444; margin-right: 0; padding-right: 2px; + border-left : 3px solid gold; } .settings-section { diff --git a/css/spectrum/spectrum-overrides.css b/css/spectrum/spectrum-overrides.css new file mode 100644 index 00000000..1bd874ea --- /dev/null +++ b/css/spectrum/spectrum-overrides.css @@ -0,0 +1,93 @@ +/** + * Piskel specific CSS for spectrum widgets + */ + +/* Widget's main container */ +.sp-container { + border-radius: 4px; + background-color: #2B2B2B; + border: solid 4px #888; + padding: 5px; +} + +.sp-container[data-y-position=bottom] { + border-top-left-radius: 0; +} + +.sp-container[data-y-position=top] { + border-bottom-left-radius: 0; +} + +/* Color square container */ +.sp-color, .sp-hue { + border : 1px solid #222; +} + +/* Remove the padding on the input container */ +.sp-replacer { + padding: 3px; + height: 100%; + border-width: 0; + box-sizing: border-box; + -moz-box-sizing:border-box; + background-color: #444; +} +/* Hide the triangle */ +.sp-dd { + display: none; +} + +/* */ +.sp-preview { + margin-right: 0; + height: 100%; + width: 39px; + border-width: 0; +} + +.sp-palette-row-selection { + max-width: 62px; +} + +.sp-palette .sp-thumb-el { + margin : 0 5px 5px 0; + width: 24px; + height: 24px; + border-color: #444; + +} + +.sp-picker-container, .sp-palette-container { + padding-top: 5px; + padding-right: 5px; + padding-left: 5px; + border-left-width: 0; + border-right-width: 0; +} + +.sp-slider { + height: 5px; + left: -2px; + right: -2px; + border: 2px solid white; + background: rgba(255,255,255,0); + opacity: 1; + border-radius: 2px; + box-shadow: 0 0 1px 1px rgba(0,0,0,0.5), inset 0 0 1px 1px rgba(0,0,0,0.5); +} + +.sp-dragger { + border-radius: 8px; + height: 12px; + width: 12px; + border: 2px solid white; + background: none; + box-sizing:border-box; + -moz-box-sizing:border-box; + box-shadow: 0 0 1px 1px rgba(0,0,0,0.5), inset 0 0 1px 1px rgba(0,0,0,0.5); +} + +.sp-palette .sp-thumb-el.sp-thumb-active { + border-color: gold; + box-shadow: 0 0 0px 1px gold; +} \ No newline at end of file diff --git a/css/spectrum/spectrum.css b/css/spectrum/spectrum.css new file mode 100644 index 00000000..850e1930 --- /dev/null +++ b/css/spectrum/spectrum.css @@ -0,0 +1,477 @@ +/*** +Spectrum Colorpicker v1.1.2 +https://github.com/bgrins/spectrum +Author: Brian Grinstead +License: MIT +***/ + +.sp-container { + position:absolute; + top:0; + left:0; + display:inline-block; + *display: inline; + *zoom: 1; + /* https://github.com/bgrins/spectrum/issues/40 */ + z-index: 9999994; + overflow: hidden; +} +.sp-container.sp-flat { + position: relative; +} + +/* http://ansciath.tumblr.com/post/7347495869/css-aspect-ratio */ +.sp-top { + position:relative; + width: 100%; + display:inline-block; +} +.sp-top-inner { + position:absolute; + top:0; + left:0; + bottom:0; + right:0; +} +.sp-color { + position: absolute; + top:0; + left:0; + bottom:0; + right:20%; +} +.sp-hue { + position: absolute; + top:0; + right:0; + bottom:0; + left:84%; + height: 100%; +} +.sp-fill { + padding-top: 80%; +} +.sp-sat, .sp-val { + position: absolute; + top:0; + left:0; + right:0; + bottom:0; +} + +.sp-alpha-enabled .sp-top { + margin-bottom: 18px; +} +.sp-alpha-enabled .sp-alpha { + display: block; +} +.sp-alpha-handle { + position:absolute; + top:-4px; + bottom: -4px; + width: 6px; + left: 50%; + cursor: pointer; + border: 1px solid black; + background: white; + opacity: .8; +} +.sp-alpha { + display: none; + position: absolute; + bottom: -14px; + right: 0; + left: 0; + height: 8px; +} +.sp-alpha-inner { + border: solid 1px #333; +} + +/* Don't allow text selection */ +.sp-container, .sp-replacer, .sp-preview, .sp-dragger, .sp-slider, .sp-alpha, .sp-alpha-handle, .sp-container.sp-dragging .sp-input, .sp-container button { + -webkit-user-select:none; + -moz-user-select: -moz-none; + -o-user-select:none; + user-select: none; +} + +.sp-container.sp-input-disabled .sp-input-container { + display: none; +} +.sp-container.sp-buttons-disabled .sp-button-container { + display: none; +} +.sp-palette-only .sp-picker-container { + display: none; +} +.sp-palette-disabled .sp-palette-container { + display: none; +} + +.sp-initial-disabled .sp-initial { + display: none; +} + + +/* Gradients for hue, saturation and value instead of images. Not pretty... but it works */ +.sp-sat { + background-image: -webkit-gradient(linear, 0 0, 100% 0, from(#FFF), to(rgba(204, 154, 129, 0))); + background-image: -webkit-linear-gradient(left, #FFF, rgba(204, 154, 129, 0)); + background-image: -moz-linear-gradient(left, #fff, rgba(204, 154, 129, 0)); + background-image: -o-linear-gradient(left, #fff, rgba(204, 154, 129, 0)); + background-image: -ms-linear-gradient(left, #fff, rgba(204, 154, 129, 0)); + background-image: linear-gradient(to right, #fff, rgba(204, 154, 129, 0)); + -ms-filter: "progid:DXImageTransform.Microsoft.gradient(GradientType = 1, startColorstr=#FFFFFFFF, endColorstr=#00CC9A81)"; + filter : progid:DXImageTransform.Microsoft.gradient(GradientType = 1, startColorstr='#FFFFFFFF', endColorstr='#00CC9A81'); +} +.sp-val { + background-image: -webkit-gradient(linear, 0 100%, 0 0, from(#000000), to(rgba(204, 154, 129, 0))); + background-image: -webkit-linear-gradient(bottom, #000000, rgba(204, 154, 129, 0)); + background-image: -moz-linear-gradient(bottom, #000, rgba(204, 154, 129, 0)); + background-image: -o-linear-gradient(bottom, #000, rgba(204, 154, 129, 0)); + background-image: -ms-linear-gradient(bottom, #000, rgba(204, 154, 129, 0)); + background-image: linear-gradient(to top, #000, rgba(204, 154, 129, 0)); + -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=#00CC9A81, endColorstr=#FF000000)"; + filter : progid:DXImageTransform.Microsoft.gradient(startColorstr='#00CC9A81', endColorstr='#FF000000'); +} + +.sp-hue { + background: -moz-linear-gradient(top, #ff0000 0%, #ffff00 17%, #00ff00 33%, #00ffff 50%, #0000ff 67%, #ff00ff 83%, #ff0000 100%); + background: -ms-linear-gradient(top, #ff0000 0%, #ffff00 17%, #00ff00 33%, #00ffff 50%, #0000ff 67%, #ff00ff 83%, #ff0000 100%); + background: -o-linear-gradient(top, #ff0000 0%, #ffff00 17%, #00ff00 33%, #00ffff 50%, #0000ff 67%, #ff00ff 83%, #ff0000 100%); + background: -webkit-gradient(linear, left top, left bottom, from(#ff0000), color-stop(0.17, #ffff00), color-stop(0.33, #00ff00), color-stop(0.5, #00ffff), color-stop(0.67, #0000ff), color-stop(0.83, #ff00ff), to(#ff0000)); + background: -webkit-linear-gradient(top, #ff0000 0%, #ffff00 17%, #00ff00 33%, #00ffff 50%, #0000ff 67%, #ff00ff 83%, #ff0000 100%); +} + +/* IE filters do not support multiple color stops. + Generate 6 divs, line them up, and do two color gradients for each. + Yes, really. + */ +.sp-1 { + height:17%; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0000', endColorstr='#ffff00'); +} +.sp-2 { + height:16%; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffff00', endColorstr='#00ff00'); +} +.sp-3 { + height:17%; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00ff00', endColorstr='#00ffff'); +} +.sp-4 { + height:17%; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00ffff', endColorstr='#0000ff'); +} +.sp-5 { + height:16%; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#0000ff', endColorstr='#ff00ff'); +} +.sp-6 { + height:17%; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff00ff', endColorstr='#ff0000'); +} + +.sp-hidden { + display: none !important; +} + +/* Clearfix hack */ +.sp-cf:before, .sp-cf:after { content: ""; display: table; } +.sp-cf:after { clear: both; } +.sp-cf { *zoom: 1; } + +/* Mobile devices, make hue slider bigger so it is easier to slide */ +@media (max-device-width: 480px) { + .sp-color { right: 40%; } + .sp-hue { left: 63%; } + .sp-fill { padding-top: 60%; } +} +.sp-dragger { + border-radius: 5px; + height: 5px; + width: 5px; + border: 1px solid #fff; + background: #000; + cursor: pointer; + position:absolute; + top:0; + left: 0; +} +.sp-slider { + position: absolute; + top:0; + cursor:pointer; + height: 3px; + left: -1px; + right: -1px; + border: 1px solid #000; + background: white; + opacity: .8; +} + +/* +Theme authors: +Here are the basic themeable display options (colors, fonts, global widths). +See http://bgrins.github.io/spectrum/themes/ for instructions. +*/ + +.sp-container { + border-radius: 0; + background-color: #ECECEC; + border: solid 1px #f0c49B; + padding: 0; +} +.sp-container, .sp-container button, .sp-container input, .sp-color, .sp-hue +{ + font: normal 12px "Lucida Grande", "Lucida Sans Unicode", "Lucida Sans", Geneva, Verdana, sans-serif; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + -ms-box-sizing: border-box; + box-sizing: border-box; +} +.sp-top +{ + margin-bottom: 3px; +} +.sp-color, .sp-hue +{ + border: solid 1px #666; +} + +/* Input */ +.sp-input-container { + float:right; + width: 100px; + margin-bottom: 4px; +} +.sp-initial-disabled .sp-input-container { + width: 100%; +} +.sp-input { + font-size: 12px !important; + border: 1px inset; + padding: 4px 5px; + margin: 0; + width: 100%; + background:transparent; + border-radius: 3px; + color: #222; +} +.sp-input:focus { + border: 1px solid orange; +} +.sp-input.sp-validation-error +{ + border: 1px solid red; + background: #fdd; +} +.sp-picker-container , .sp-palette-container +{ + float:left; + position: relative; + padding: 10px; + padding-bottom: 300px; + margin-bottom: -290px; +} +.sp-picker-container +{ + width: 172px; + border-left: solid 1px #fff; +} + +/* Palettes */ +.sp-palette-container +{ + border-right: solid 1px #ccc; +} + +.sp-palette .sp-thumb-el { + display: block; + position:relative; + float:left; + width: 24px; + height: 15px; + margin: 3px; + cursor: pointer; + border:solid 2px transparent; +} +.sp-palette .sp-thumb-el:hover, .sp-palette .sp-thumb-el.sp-thumb-active { + border-color: orange; +} +.sp-thumb-el +{ + position:relative; +} + +/* Initial */ +.sp-initial +{ + float: left; + border: solid 1px #333; +} +.sp-initial span { + width: 30px; + height: 25px; + border:none; + display:block; + float:left; + margin:0; +} + +/* Buttons */ +.sp-button-container { + float: right; +} + +/* Replacer (the little preview div that shows up instead of the ) */ +.sp-replacer { + margin:0; + overflow:hidden; + cursor:pointer; + padding: 4px; + display:inline-block; + *zoom: 1; + *display: inline; + border: solid 1px #91765d; + background: #eee; + color: #333; + vertical-align: middle; +} +.sp-replacer:hover, .sp-replacer.sp-active { + border-color: #F0C49B; + color: #111; +} +.sp-replacer.sp-disabled { + cursor:default; + border-color: silver; + color: silver; +} +.sp-dd { + padding: 2px 0; + height: 16px; + line-height: 16px; + float:left; + font-size:10px; +} +.sp-preview +{ + position:relative; + width:25px; + height: 20px; + border: solid 1px #222; + margin-right: 5px; + float:left; + z-index: 0; +} + +.sp-palette +{ + *width: 220px; + max-width: 220px; +} +.sp-palette .sp-thumb-el +{ + width:16px; + height: 16px; + margin:2px 1px; + border: solid 1px #d0d0d0; +} + +.sp-container +{ + padding-bottom:0; +} + + +/* Buttons: http://hellohappy.org/css3-buttons/ */ +.sp-container button { + background-color: #eeeeee; + background-image: -webkit-linear-gradient(top, #eeeeee, #cccccc); + background-image: -moz-linear-gradient(top, #eeeeee, #cccccc); + background-image: -ms-linear-gradient(top, #eeeeee, #cccccc); + background-image: -o-linear-gradient(top, #eeeeee, #cccccc); + background-image: linear-gradient(to bottom, #eeeeee, #cccccc); + border: 1px solid #ccc; + border-bottom: 1px solid #bbb; + border-radius: 3px; + color: #333; + font-size: 14px; + line-height: 1; + padding: 5px 4px; + text-align: center; + text-shadow: 0 1px 0 #eee; + vertical-align: middle; +} +.sp-container button:hover { + background-color: #dddddd; + background-image: -webkit-linear-gradient(top, #dddddd, #bbbbbb); + background-image: -moz-linear-gradient(top, #dddddd, #bbbbbb); + background-image: -ms-linear-gradient(top, #dddddd, #bbbbbb); + background-image: -o-linear-gradient(top, #dddddd, #bbbbbb); + background-image: linear-gradient(to bottom, #dddddd, #bbbbbb); + border: 1px solid #bbb; + border-bottom: 1px solid #999; + cursor: pointer; + text-shadow: 0 1px 0 #ddd; +} +.sp-container button:active { + border: 1px solid #aaa; + border-bottom: 1px solid #888; + -webkit-box-shadow: inset 0 0 5px 2px #aaaaaa, 0 1px 0 0 #eeeeee; + -moz-box-shadow: inset 0 0 5px 2px #aaaaaa, 0 1px 0 0 #eeeeee; + -ms-box-shadow: inset 0 0 5px 2px #aaaaaa, 0 1px 0 0 #eeeeee; + -o-box-shadow: inset 0 0 5px 2px #aaaaaa, 0 1px 0 0 #eeeeee; + box-shadow: inset 0 0 5px 2px #aaaaaa, 0 1px 0 0 #eeeeee; +} +.sp-cancel +{ + font-size: 11px; + color: #d93f3f !important; + margin:0; + padding:2px; + margin-right: 5px; + vertical-align: middle; + text-decoration:none; + +} +.sp-cancel:hover +{ + color: #d93f3f !important; + text-decoration: underline; +} + + +.sp-palette span:hover, .sp-palette span.sp-thumb-active +{ + border-color: #000; +} + +.sp-preview, .sp-alpha, .sp-thumb-el +{ + position:relative; + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAIAAADZF8uwAAAAGUlEQVQYV2M4gwH+YwCGIasIUwhT25BVBADtzYNYrHvv4gAAAABJRU5ErkJggg==); +} +.sp-preview-inner, .sp-alpha-inner, .sp-thumb-inner +{ + display:block; + position:absolute; + top:0;left:0;bottom:0;right:0; +} + +.sp-palette .sp-thumb-inner +{ + background-position: 50% 50%; + background-repeat: no-repeat; +} + +.sp-palette .sp-thumb-light.sp-thumb-active .sp-thumb-inner +{ + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAIVJREFUeNpiYBhsgJFMffxAXABlN5JruT4Q3wfi/0DsT64h8UD8HmpIPCWG/KemIfOJCUB+Aoacx6EGBZyHBqI+WsDCwuQ9mhxeg2A210Ntfo8klk9sOMijaURm7yc1UP2RNCMbKE9ODK1HM6iegYLkfx8pligC9lCD7KmRof0ZhjQACDAAceovrtpVBRkAAAAASUVORK5CYII=); +} + +.sp-palette .sp-thumb-dark.sp-thumb-active .sp-thumb-inner +{ + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAMdJREFUOE+tkgsNwzAMRMugEAahEAahEAZhEAqlEAZhEAohEAYh81X2dIm8fKpEspLGvudPOsUYpxE2BIJCroJmEW9qJ+MKaBFhEMNabSy9oIcIPwrB+afvAUFoK4H0tMaQ3XtlrggDhOVVMuT4E5MMG0FBbCEYzjYT7OxLEvIHQLY2zWwQ3D+9luyOQTfKDiFD3iUIfPk8VqrKjgAiSfGFPecrg6HN6m/iBcwiDAo7WiBeawa+Kwh7tZoSCGLMqwlSAzVDhoK+6vH4G0P5wdkAAAAASUVORK5CYII=); +} diff --git a/css/tools.css b/css/tools.css index 664221a5..f0a69e10 100644 --- a/css/tools.css +++ b/css/tools.css @@ -149,6 +149,9 @@ margin-left: 0; } +.palette-wrapper { + margin-top: 10px; +} .palette-color[data-color=TRANSPARENT] { position: relative; diff --git a/img/favicon.png b/img/favicon.png new file mode 100644 index 00000000..f85de46d Binary files /dev/null and b/img/favicon.png differ diff --git a/index.html b/index.html index 0e3e3e56..f7ff5b04 100644 --- a/index.html +++ b/index.html @@ -13,6 +13,8 @@ + + diff --git a/js/Constants.js b/js/Constants.js index e1ab1542..71a12bc9 100644 --- a/js/Constants.js +++ b/js/Constants.js @@ -11,10 +11,12 @@ var Constants = { MAX_HEIGHT : 1024, MAX_WIDTH : 1024, + MINIMUM_ZOOM : 1, + PREVIEW_FILM_SIZE : 120, DEFAULT_PEN_COLOR : '#000000', - TRANSPARENT_COLOR : 'TRANSPARENT', + TRANSPARENT_COLOR : 'rgba(0, 0, 0, 0)', /* * Fake semi-transparent color used to highlight transparent @@ -48,8 +50,9 @@ var Constants = { GRID_STROKE_WIDTH: 1, ZOOMED_OUT_BACKGROUND_COLOR : '#A0A0A0', - LEFT_BUTTON : 'left_button_1', - RIGHT_BUTTON : 'right_button_2', + LEFT_BUTTON : 0, + MIDDLE_BUTTON : 1, + RIGHT_BUTTON : 2, MOUSEMOVE_THROTTLING : 10, ABSTRACT_FUNCTION : function () {throw 'abstract method should be implemented';} diff --git a/js/Events.js b/js/Events.js index 28e1107f..aebb0306 100644 --- a/js/Events.js +++ b/js/Events.js @@ -38,6 +38,5 @@ var Events = { SHOW_NOTIFICATION: "SHOW_NOTIFICATION", HIDE_NOTIFICATION: "HIDE_NOTIFICATION", - // Events triggered by keyboard - SELECT_TOOL : "SELECT_TOOL" + ZOOM_CHANGED : "ZOOM_CHANGED" }; \ No newline at end of file diff --git a/js/app.js b/js/app.js index ca11988c..6ddccc21 100644 --- a/js/app.js +++ b/js/app.js @@ -169,6 +169,7 @@ pskl.utils.serialization.Deserializer.deserialize(res.framesheet, function (piskel) { pskl.app.piskelController.setPiskel(piskel); pskl.app.animationController.setFPS(res.fps); + $.publish(Events.HIDE_NOTIFICATION); }); }; @@ -245,7 +246,7 @@ var firstFrame = this.piskelController.getFrameAt(0); var canvasRenderer = new pskl.rendering.CanvasRenderer(firstFrame, 1); canvasRenderer.drawTransparentAs('rgba(0,0,0,0)'); - var firstFrameCanvas = canvasRenderer.render().canvas; + var firstFrameCanvas = canvasRenderer.render(); return firstFrameCanvas.toDataURL("image/png"); }, diff --git a/js/controller/AnimatedPreviewController.js b/js/controller/AnimatedPreviewController.js index 4fec557a..d3928813 100644 --- a/js/controller/AnimatedPreviewController.js +++ b/js/controller/AnimatedPreviewController.js @@ -18,7 +18,7 @@ }; this.renderer = new pskl.rendering.frame.FrameRenderer(this.container, renderingOptions); - $.subscribe(Events.FRAME_SIZE_CHANGED, this.updateZoom_.bind(this)); + $.subscribe(Events.FRAME_SIZE_CHANGED, this.onFrameSizeChange_.bind(this)); }; ns.AnimatedPreviewController.prototype.init = function () { @@ -67,10 +67,11 @@ return Math.min(hZoom, wZoom); }; - ns.AnimatedPreviewController.prototype.updateZoom_ = function () { + ns.AnimatedPreviewController.prototype.onFrameSizeChange_ = function () { var frame = this.piskelController.getCurrentFrame(); var zoom = this.calculateZoom_(); - this.renderer.setZoom(zoom); this.renderer.setDisplaySize(frame.getWidth() * zoom, frame.getHeight() * zoom); + this.renderer.setZoom(zoom); + this.renderer.setOffset(0, 0); }; })(); \ No newline at end of file diff --git a/js/controller/DrawingController.js b/js/controller/DrawingController.js index 53f7521f..71a7ce31 100644 --- a/js/controller/DrawingController.js +++ b/js/controller/DrawingController.js @@ -1,6 +1,6 @@ (function () { var ns = $.namespace("pskl.controller"); - ns.DrawingController = function (piskelController, paletteController,container) { + ns.DrawingController = function (piskelController, paletteController, container) { /** * @public */ @@ -40,7 +40,6 @@ // State of drawing controller: this.isClicked = false; - this.isRightClicked = false; this.previousMousemoveTime = 0; this.currentToolBehavior = null; }; @@ -52,7 +51,7 @@ this.currentToolBehavior = toolBehavior; this.overlayFrame.clear(); }, this)); - + $(window).resize($.proxy(this.startResizeTimer_, this)); $.subscribe(Events.USER_SETTINGS_CHANGED, $.proxy(this.onUserSettingsChange_, this)); @@ -86,7 +85,14 @@ }, ns.DrawingController.prototype.afterWindowResize_ = function () { + var initialWidth = this.compositeRenderer.getDisplaySize().width; this.compositeRenderer.setDisplaySize(this.getContainerWidth_(), this.getContainerHeight_()); + this.centerColumnWrapperHorizontally_(); + var ratio = this.compositeRenderer.getDisplaySize().width / initialWidth; + var newZoom = ratio * this.compositeRenderer.getZoom(); + this.compositeRenderer.setZoom(newZoom); + + $.publish(Events.ZOOM_CHANGED); }, /** @@ -99,32 +105,37 @@ }, ns.DrawingController.prototype.onFrameSizeChanged_ = function () { - this.compositeRenderer.setZoom(this.calculateZoom_()); this.compositeRenderer.setDisplaySize(this.getContainerWidth_(), this.getContainerHeight_()); + this.compositeRenderer.setZoom(this.calculateZoom_()); + this.compositeRenderer.setOffset(0, 0); + $.publish(Events.ZOOM_CHANGED); }; /** * @private */ ns.DrawingController.prototype.onMousedown_ = function (event) { - this.isClicked = true; - - if(event.button == 2) { // right click - this.isRightClicked = true; - } - + var frame = this.piskelController.getCurrentFrame(); var coords = this.renderer.getCoordinates(event.clientX, event.clientY); - this.currentToolBehavior.applyToolAt( - coords.x, - coords.y, - this.getCurrentColor_(), - this.piskelController.getCurrentFrame(), - this.overlayFrame, - this.wrapEvtInfo_(event) - ); + if (event.button === Constants.MIDDLE_BUTTON) { + if (frame.containsPixel(coords.x, coords.y)) { + $.publish(Events.SELECT_PRIMARY_COLOR, [frame.getPixel(coords.x, coords.y)]); + } + } else { + this.isClicked = true; - $.publish(Events.LOCALSTORAGE_REQUEST); + this.currentToolBehavior.applyToolAt( + coords.x, + coords.y, + this.getCurrentColor_(event), + frame, + this.overlayFrame, + event + ); + + $.publish(Events.LOCALSTORAGE_REQUEST); + } }; /** @@ -133,6 +144,7 @@ ns.DrawingController.prototype.onMousemove_ = function (event) { var currentTime = new Date().getTime(); // Throttling of the mousemove event: + if ((currentTime - this.previousMousemoveTime) > Constants.MOUSEMOVE_THROTTLING ) { var coords = this.renderer.getCoordinates(event.clientX, event.clientY); @@ -141,10 +153,10 @@ this.currentToolBehavior.moveToolAt( coords.x, coords.y, - this.getCurrentColor_(), + this.getCurrentColor_(event), this.piskelController.getCurrentFrame(), this.overlayFrame, - this.wrapEvtInfo_(event) + event ); // TODO(vincz): Find a way to move that to the model instead of being at the interaction level. @@ -156,10 +168,10 @@ this.currentToolBehavior.moveUnactiveToolAt( coords.x, coords.y, - this.getCurrentColor_(), + this.getCurrentColor_(event), this.piskelController.getCurrentFrame(), this.overlayFrame, - this.wrapEvtInfo_(event) + event ); } this.previousMousemoveTime = currentTime; @@ -170,56 +182,44 @@ var event = jQueryEvent.originalEvent; var delta = event.wheelDeltaY || (-2 * event.deltaY); var currentZoom = this.renderer.getZoom(); + + var perfectZoom = this.calculateZoom_(); + var step = perfectZoom / 10; + if (delta > 0) { - this.compositeRenderer.setZoom(currentZoom + 1); + this.compositeRenderer.setZoom(currentZoom + step); } else if (delta < 0) { - this.compositeRenderer.setZoom(currentZoom - 1); + this.compositeRenderer.setZoom(currentZoom - step); } - pskl.app.minimapController.onDrawingControllerMove_(); + $.publish(Events.ZOOM_CHANGED); }; /** * @private */ ns.DrawingController.prototype.onMouseup_ = function (event) { - if(this.isClicked || this.isRightClicked) { + if(this.isClicked) { // 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.renderer.getCoordinates(event.clientX, event.clientY); - //console.log("mousemove: col: " + spriteCoordinate.col + " - row: " + spriteCoordinate.row); this.currentToolBehavior.releaseToolAt( coords.x, coords.y, - this.getCurrentColor_(), + this.getCurrentColor_(event), this.piskelController.getCurrentFrame(), this.overlayFrame, - this.wrapEvtInfo_(event) + 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 */ @@ -230,11 +230,13 @@ /** * @private */ - ns.DrawingController.prototype.getCurrentColor_ = function () { - if(this.isRightClicked) { + ns.DrawingController.prototype.getCurrentColor_ = function (event) { + if(event.button == Constants.RIGHT_BUTTON) { return this.paletteController.getSecondaryColor(); - } else { + } else if(event.button == Constants.LEFT_BUTTON) { return this.paletteController.getPrimaryColor(); + } else { + return Constants.DEFAULT_PEN_COLOR; } }; @@ -279,8 +281,11 @@ ns.DrawingController.prototype.getAvailableWidth_ = function () { var leftSectionWidth = $('.left-column').outerWidth(true), rightSectionWidth = $('.right-column').outerWidth(true), - availableWidth = $('#main-wrapper').width() - leftSectionWidth - rightSectionWidth; - return availableWidth; + toolsContainerWidth = $('#tool-section').outerWidth(true), + settingsContainerWidth = $('#application-action-section').outerWidth(true), + availableWidth = $('#main-wrapper').width() - leftSectionWidth - rightSectionWidth - toolsContainerWidth - settingsContainerWidth; + + return availableWidth-50; }; ns.DrawingController.prototype.getContainerHeight_ = function () { @@ -308,6 +313,6 @@ ns.DrawingController.prototype.setOffset = function (x, y) { this.compositeRenderer.setOffset(x, y); - pskl.app.minimapController.onDrawingControllerMove_(); + $.publish(Events.ZOOM_CHANGED); }; })(); \ No newline at end of file diff --git a/js/controller/MinimapController.js b/js/controller/MinimapController.js index 6b1fc892..5208cecb 100644 --- a/js/controller/MinimapController.js +++ b/js/controller/MinimapController.js @@ -21,9 +21,11 @@ $(this.container).mousedown(this.onMinimapMousedown_.bind(this)); $('body').mousemove(this.onMinimapMousemove_.bind(this)); $('body').mouseup(this.onMinimapMouseup_.bind(this)); + + $.subscribe(Events.ZOOM_CHANGED, $.proxy(this.renderMinimap_, this)); }; - ns.MinimapController.prototype.onDrawingControllerMove_ = function () { + ns.MinimapController.prototype.renderMinimap_ = function () { var zoomRatio = this.getDrawingAreaZoomRatio_(); if (zoomRatio > 1) { this.displayCropFrame_(zoomRatio, this.drawingController.getRenderer().getOffset()); diff --git a/js/controller/PaletteController.js b/js/controller/PaletteController.js index 98013cd1..93f658e7 100644 --- a/js/controller/PaletteController.js +++ b/js/controller/PaletteController.js @@ -19,17 +19,30 @@ pskl.app.shortcutService.addShortcut('X', this.swapColors.bind(this)); pskl.app.shortcutService.addShortcut('D', this.resetColors.bind(this)); + var spectrumCfg = { + showPalette: true, + showButtons: false, + palette: [ + ['rgba(0,0,0,0)'] + ], + clickoutFiresChange : true, + + beforeShow : function(tinycolor) { + tinycolor.setAlpha(1); + } + }; + // Initialize colorpickers: var colorPicker = $('#color-picker'); - colorPicker.val(this.primaryColor); + colorPicker.spectrum($.extend({color: Constants.DEFAULT_PEN_COLOR}, spectrumCfg)); colorPicker.change({isPrimary : true}, $.proxy(this.onPickerChange_, this)); - + this.setTitleOnPicker_(Constants.DEFAULT_PEN_COLOR, colorPicker); var secondaryColorPicker = $('#secondary-color-picker'); - secondaryColorPicker.val(this.secondaryColor); + secondaryColorPicker.spectrum($.extend({color: Constants.TRANSPARENT_COLOR}, spectrumCfg)); secondaryColorPicker.change({isPrimary : false}, $.proxy(this.onPickerChange_, this)); + this.setTitleOnPicker_(Constants.TRANSPARENT_COLOR, secondaryColorPicker); - window.jscolor.install(); }; /** @@ -113,11 +126,17 @@ // 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.spectrum("set", Constants.TRANSPARENT_COLOR); colorPicker.val(Constants.TRANSPARENT_COLOR); } else { - colorPicker[0].color.fromString(color); + colorPicker.spectrum("set", color); } + this.setTitleOnPicker_(color, colorPicker); + }; + + ns.PaletteController.prototype.setTitleOnPicker_ = function (title, colorPicker) { + var spectrumInputSelector = '.sp-replacer'; + colorPicker.next(spectrumInputSelector).attr('title', title); }; })(); diff --git a/js/controller/PiskelController.js b/js/controller/PiskelController.js index 12c570ac..25028ce7 100644 --- a/js/controller/PiskelController.js +++ b/js/controller/PiskelController.js @@ -16,8 +16,8 @@ this.layerIdCounter = 1; - $.publish(Events.PISKEL_RESET); $.publish(Events.FRAME_SIZE_CHANGED); + $.publish(Events.PISKEL_RESET); }; ns.PiskelController.prototype.init = function () { diff --git a/js/drawingtools/BaseTool.js b/js/drawingtools/BaseTool.js index 8b482df2..b3e33e30 100644 --- a/js/drawingtools/BaseTool.js +++ b/js/drawingtools/BaseTool.js @@ -8,11 +8,11 @@ ns.BaseTool = function() {}; - ns.BaseTool.prototype.applyToolAt = function(col, row, color, frame, overlay) {}; + ns.BaseTool.prototype.applyToolAt = function(col, row, color, frame, overlay, event) {}; - ns.BaseTool.prototype.moveToolAt = function(col, row, color, frame, overlay) {}; + ns.BaseTool.prototype.moveToolAt = function(col, row, color, frame, overlay, event) {}; - ns.BaseTool.prototype.moveUnactiveToolAt = function(col, row, color, frame, overlay) { + ns.BaseTool.prototype.moveUnactiveToolAt = function(col, row, color, frame, overlay, event) { if (overlay.containsPixel(col, row)) { if (!isNaN(this.highlightedPixelCol) && !isNaN(this.highlightedPixelRow) && @@ -31,7 +31,7 @@ } }; - ns.BaseTool.prototype.releaseToolAt = function(col, row, color, frame, overlay) {}; + ns.BaseTool.prototype.releaseToolAt = function(col, row, color, frame, overlay, event) {}; /** * Bresenham line algorihtm: Get an array of pixels from diff --git a/js/drawingtools/Circle.js b/js/drawingtools/Circle.js index c05f6f45..ff5e4345 100644 --- a/js/drawingtools/Circle.js +++ b/js/drawingtools/Circle.js @@ -20,7 +20,7 @@ /** * @override */ - ns.Circle.prototype.applyToolAt = function(col, row, color, frame, overlay) { + ns.Circle.prototype.applyToolAt = function(col, row, color, frame, overlay, event) { this.startCol = col; this.startRow = row; @@ -28,7 +28,7 @@ overlay.setPixel(col, row, color); }; - ns.Circle.prototype.moveToolAt = function(col, row, color, frame, overlay) { + ns.Circle.prototype.moveToolAt = function(col, row, color, frame, overlay, event) { overlay.clear(); if(color == Constants.TRANSPARENT_COLOR) { color = Constants.SELECTION_TRANSPARENT_COLOR; @@ -41,7 +41,7 @@ /** * @override */ - ns.Circle.prototype.releaseToolAt = function(col, row, color, frame, overlay) { + ns.Circle.prototype.releaseToolAt = function(col, row, color, frame, overlay, event) { overlay.clear(); if(frame.containsPixel(col, row)) { // cancel if outside of canvas // draw in frame to finalize diff --git a/js/drawingtools/ColorPicker.js b/js/drawingtools/ColorPicker.js index fba7d5a9..bdfd259d 100644 --- a/js/drawingtools/ColorPicker.js +++ b/js/drawingtools/ColorPicker.js @@ -16,12 +16,12 @@ /** * @override */ - ns.ColorPicker.prototype.applyToolAt = function(col, row, color, frame, overlay, context) { + ns.ColorPicker.prototype.applyToolAt = function(col, row, color, frame, overlay, event) { if (frame.containsPixel(col, row)) { var sampledColor = frame.getPixel(col, row); - if (context.button == Constants.LEFT_BUTTON) { + if (event.button == Constants.LEFT_BUTTON) { $.publish(Events.SELECT_PRIMARY_COLOR, [sampledColor]); - } else if (context.button == Constants.RIGHT_BUTTON) { + } else if (event.button == Constants.RIGHT_BUTTON) { $.publish(Events.SELECT_SECONDARY_COLOR, [sampledColor]); } } diff --git a/js/drawingtools/Eraser.js b/js/drawingtools/Eraser.js index c66a4220..57570fe9 100644 --- a/js/drawingtools/Eraser.js +++ b/js/drawingtools/Eraser.js @@ -17,7 +17,7 @@ /** * @override */ - ns.Eraser.prototype.applyToolAt = function(col, row, color, frame, overlay) { - this.superclass.applyToolAt.call(this, col, row, Constants.TRANSPARENT_COLOR, frame, overlay); + ns.Eraser.prototype.applyToolAt = function(col, row, color, frame, overlay, event) { + this.superclass.applyToolAt.call(this, col, row, Constants.TRANSPARENT_COLOR, frame, overlay, event); }; })(); \ No newline at end of file diff --git a/js/drawingtools/Move.js b/js/drawingtools/Move.js index 1155db21..1635e0cf 100644 --- a/js/drawingtools/Move.js +++ b/js/drawingtools/Move.js @@ -20,13 +20,13 @@ /** * @override */ - ns.Move.prototype.applyToolAt = function(col, row, color, frame, overlay) { + ns.Move.prototype.applyToolAt = function(col, row, color, frame, overlay, event) { this.startCol = col; this.startRow = row; this.frameClone = frame.clone(); }; - ns.Move.prototype.moveToolAt = function(col, row, color, frame, overlay) { + ns.Move.prototype.moveToolAt = function(col, row, color, frame, overlay, event) { var colDiff = col - this.startCol, rowDiff = row - this.startRow; this.shiftFrame(colDiff, rowDiff, frame, this.frameClone); }; @@ -48,7 +48,7 @@ /** * @override */ - ns.Move.prototype.releaseToolAt = function(col, row, color, frame, overlay) { + ns.Move.prototype.releaseToolAt = function(col, row, color, frame, overlay, event) { this.moveToolAt(col, row, color, frame, overlay); }; })(); diff --git a/js/drawingtools/PaintBucket.js b/js/drawingtools/PaintBucket.js index cb0ffa66..34fc4714 100644 --- a/js/drawingtools/PaintBucket.js +++ b/js/drawingtools/PaintBucket.js @@ -16,7 +16,7 @@ /** * @override */ - ns.PaintBucket.prototype.applyToolAt = function(col, row, color, frame, overlay) { + ns.PaintBucket.prototype.applyToolAt = function(col, row, color, frame, overlay, event) { pskl.PixelUtils.paintSimilarConnectedPixelsFromFrame(frame, col, row, color); }; diff --git a/js/drawingtools/Rectangle.js b/js/drawingtools/Rectangle.js index dd2a57e5..504d3ef9 100644 --- a/js/drawingtools/Rectangle.js +++ b/js/drawingtools/Rectangle.js @@ -20,7 +20,7 @@ /** * @override */ - ns.Rectangle.prototype.applyToolAt = function(col, row, color, frame, overlay) { + ns.Rectangle.prototype.applyToolAt = function(col, row, color, frame, overlay, event) { this.startCol = col; this.startRow = row; @@ -28,7 +28,7 @@ overlay.setPixel(col, row, color); }; - ns.Rectangle.prototype.moveToolAt = function(col, row, color, frame, overlay) { + ns.Rectangle.prototype.moveToolAt = function(col, row, color, frame, overlay, event) { overlay.clear(); if(color == Constants.TRANSPARENT_COLOR) { color = Constants.SELECTION_TRANSPARENT_COLOR; @@ -41,7 +41,7 @@ /** * @override */ - ns.Rectangle.prototype.releaseToolAt = function(col, row, color, frame, overlay) { + ns.Rectangle.prototype.releaseToolAt = function(col, row, color, frame, overlay, event) { overlay.clear(); if(frame.containsPixel(col, row)) { // cancel if outside of canvas // draw in frame to finalize diff --git a/js/drawingtools/SimplePen.js b/js/drawingtools/SimplePen.js index a5dbbb0e..7ec9840e 100644 --- a/js/drawingtools/SimplePen.js +++ b/js/drawingtools/SimplePen.js @@ -16,11 +16,11 @@ }; pskl.utils.inherit(ns.SimplePen, ns.BaseTool); - + /** * @override */ - ns.SimplePen.prototype.applyToolAt = function(col, row, color, frame, overlay) { + ns.SimplePen.prototype.applyToolAt = function(col, row, color, frame, overlay, event) { if (frame.containsPixel(col, row)) { frame.setPixel(col, row, color); } @@ -28,7 +28,7 @@ this.previousRow = row; }; - ns.SimplePen.prototype.moveToolAt = function(col, row, color, frame, overlay) { + ns.SimplePen.prototype.moveToolAt = function(col, row, color, frame, overlay, event) { 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. diff --git a/js/drawingtools/Stroke.js b/js/drawingtools/Stroke.js index 93173d8b..6462e1ca 100644 --- a/js/drawingtools/Stroke.js +++ b/js/drawingtools/Stroke.js @@ -20,7 +20,7 @@ /** * @override */ - ns.Stroke.prototype.applyToolAt = function(col, row, color, frame, overlay) { + ns.Stroke.prototype.applyToolAt = function(col, row, color, frame, overlay, event) { this.startCol = col; this.startRow = row; @@ -36,7 +36,7 @@ overlay.setPixel(col, row, color); }; - ns.Stroke.prototype.moveToolAt = function(col, row, color, frame, overlay) { + ns.Stroke.prototype.moveToolAt = function(col, row, color, frame, overlay, event) { overlay.clear(); // When the user moussemove (before releasing), we dynamically compute the @@ -62,7 +62,7 @@ /** * @override */ - ns.Stroke.prototype.releaseToolAt = function(col, row, color, frame, overlay) { + ns.Stroke.prototype.releaseToolAt = function(col, row, color, frame, overlay, event) { // 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)) { diff --git a/js/drawingtools/VerticalMirrorPen.js b/js/drawingtools/VerticalMirrorPen.js index 4b1d9d61..435805e8 100644 --- a/js/drawingtools/VerticalMirrorPen.js +++ b/js/drawingtools/VerticalMirrorPen.js @@ -26,7 +26,7 @@ /** * @override */ - ns.VerticalMirrorPen.prototype.applyToolAt = function(col, row, color, frame, overlay) { + ns.VerticalMirrorPen.prototype.applyToolAt = function(col, row, color, frame, overlay, event) { this.superclass.applyToolAt.call(this, col, row, color, frame, overlay); var mirroredCol = this.getSymmetricCol_(col, frame); diff --git a/js/drawingtools/selectiontools/BaseSelect.js b/js/drawingtools/selectiontools/BaseSelect.js index 7d069cc0..19972394 100644 --- a/js/drawingtools/selectiontools/BaseSelect.js +++ b/js/drawingtools/selectiontools/BaseSelect.js @@ -20,7 +20,7 @@ /** * @override */ - ns.BaseSelect.prototype.applyToolAt = function(col, row, color, frame, overlay) { + ns.BaseSelect.prototype.applyToolAt = function(col, row, color, frame, overlay, event) { this.startCol = col; this.startRow = row; @@ -47,7 +47,7 @@ /** * @override */ - ns.BaseSelect.prototype.moveToolAt = function(col, row, color, frame, overlay) { + ns.BaseSelect.prototype.moveToolAt = function(col, row, color, frame, overlay, event) { if(this.mode == "select") { this.onSelect_(col, row, color, frame, overlay); @@ -61,7 +61,7 @@ /** * @override */ - ns.BaseSelect.prototype.releaseToolAt = function(col, row, color, frame, overlay) { + ns.BaseSelect.prototype.releaseToolAt = function(col, row, color, frame, overlay, event) { if(this.mode == "select") { this.onSelectEnd_(col, row, color, frame, overlay); } else if(this.mode == "moveSelection") { @@ -76,7 +76,7 @@ * 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) { + ns.BaseSelect.prototype.moveUnactiveToolAt = function(col, row, color, frame, overlay, event) { if(overlay.getPixel(col, row) != Constants.SELECTION_TRANSPARENT_COLOR) { // We're hovering the selection, show the move tool: diff --git a/js/lib/gif/gif.js b/js/lib/gif/gif.js index ffdc3b54..4bc0bd64 100644 --- a/js/lib/gif/gif.js +++ b/js/lib/gif/gif.js @@ -1,3 +1 @@ -(function(c){function a(b,d){if({}.hasOwnProperty.call(a.cache,b))return a.cache[b];var e=a.resolve(b);if(!e)throw new Error('Failed to resolve module '+b);var c={id:b,require:a,filename:b,exports:{},loaded:!1,parent:d,children:[]};d&&d.children.push(c);var f=b.slice(0,b.lastIndexOf('/')+1);return a.cache[b]=c.exports,e.call(c.exports,c,c.exports,f,b),c.loaded=!0,a.cache[b]=c.exports}a.modules={},a.cache={},a.resolve=function(b){return{}.hasOwnProperty.call(a.modules,b)?a.modules[b]:void 0},a.define=function(b,c){a.modules[b]=c};var b=function(a){return a='/',{title:'browser',version:'v0.10.5',browser:!0,env:{},argv:[],nextTick:c.setImmediate||function(a){setTimeout(a,0)},cwd:function(){return a},chdir:function(b){a=b}}}();a.define('/gif.coffee',function(b,k,j,i){function c(a,b){return{}.hasOwnProperty.call(a,b)}function g(d,b){for(var a=0,c=b.length;athis.frames.length;0<=this.frames.length?++a:--a)b.push(a);return b}.apply(this,arguments).length;athis.frames.length;0<=this.frames.length?++a:--a)b.push(a);return b}.apply(this,arguments)[a],b.push(null);return b}.call(this,[]),a=this.spawnWorkers();for(var b=0,c=function(){var c,c;c=[];for(var b=0;0<=a?ba;0<=a?++b:--b)c.push(b);return c}.apply(this,arguments).length;ba;0<=a?++b:--b)c.push(b);return c}.apply(this,arguments)[b],this.renderNextFrame();return this.emit('start'),this.emit('progress',0)},a.prototype.abort=function(){var a;while(!0){if(a=this.activeWorkers.shift(),!(null!=a))break;console.log('killing active worker'),a.terminate()}return this.running=!1,this.emit('abort')},a.prototype.spawnWorkers=function(){var a,b;return a=Math.min(this.options.workers,this.frames.length),function(){var c;c=[];for(var b=this.freeWorkers.length;this.freeWorkers.length<=a?ba;this.freeWorkers.length<=a?++b:--b)c.push(b);return c}.apply(this,arguments).forEach((b=this,function(d){var a,c;return console.log('spawning worker '+d),c=new Worker(b.options.workerScript),a=b,c.onmessage=function(b){return a.activeWorkers.splice(a.activeWorkers.indexOf(c),1),a.freeWorkers.push(c),a.frameFinished(b.data)},b.freeWorkers.push(c)})),a},a.prototype.frameFinished=function(a){return console.log('frame '+a.index+' finished - '+this.activeWorkers.length+' active'),this.finishedFrames++,this.emit('progress',this.finishedFrames/this.frames.length),this.imageParts[a.index]=a,g(null,this.imageParts)?this.renderNextFrame():this.finishRendering()},a.prototype.finishRendering=function(){var e,a,k,m,b,d,h;b=0;for(var f=0,j=this.imageParts.length;f=this.frames.length?void 0:(c=this.frames[this.nextFrame++],b=this.freeWorkers.shift(),a=this.getTask(c),console.log('starting frame '+(a.index+1)+' of '+this.frames.length),this.activeWorkers.push(b),b.postMessage(a))},a.prototype.getContextData=function(a){return a.getImageData(0,0,this.options.width,this.options.height).data},a.prototype.getImageData=function(b){var a;return null!=this._canvas||(this._canvas=document.createElement('canvas'),this._canvas.width=this.options.width,this._canvas.height=this.options.height),a=this._canvas.getContext('2d'),a.setFill=this.options.background,a.fillRect(0,0,this.options.width,this.options.height),a.drawImage(b,0,0),this.getContextData(a)},a.prototype.getTask=function(a){var c,b;if(c=this.frames.indexOf(a),b={index:c,last:c===this.frames.length-1,delay:a.delay,width:this.options.width,height:this.options.height,quality:this.options.quality,repeat:this.options.repeat,canTransfer:e.name==='chrome'},null!=a.data)b.data=a.data;else if(null!=a.context)b.data=this.getContextData(a.context);else if(null!=a.image)b.data=this.getImageData(a.image);else throw new Error('Invalid frame');return b},a}(d),b.exports=f}),a.define('/browser.coffee',function(f,g,h,i){var a,d,e,c,b;c=navigator.userAgent.toLowerCase(),e=navigator.platform.toLowerCase(),b=c.match(/(opera|ie|firefox|chrome|version)[\s\/:]([\w\d\.]+)?.*?(safari|version[\s\/:]([\w\d\.]+)|$)/)||[null,'unknown',0],d=b[1]==='ie'&&document.documentMode,a={name:b[1]==='version'?b[3]:b[1],version:d||parseFloat(b[1]==='opera'&&b[4]?b[4]:b[2]),platform:{name:c.match(/ip(?:ad|od|hone)/)?'ios':(c.match(/(?:webos|android)/)||e.match(/mac|win|linux/)||['other'])[0]}},a[a.name]=!0,a[a.name+parseInt(a.version,10)]=!0,a.platform[a.platform.name]=!0,f.exports=a}),a.define('events',function(f,e,g,h){b.EventEmitter||(b.EventEmitter=function(){});var a=e.EventEmitter=b.EventEmitter,c=typeof Array.isArray==='function'?Array.isArray:function(a){return Object.prototype.toString.call(a)==='[object Array]'},d=10;a.prototype.setMaxListeners=function(a){this._events||(this._events={}),this._events.maxListeners=a},a.prototype.emit=function(f){if(f==='error'&&(!(this._events&&this._events.error)||c(this._events.error)&&!this._events.error.length))throw arguments[1]instanceof Error?arguments[1]:new Error("Uncaught, unspecified 'error' event.");if(!this._events)return!1;var a=this._events[f];if(!a)return!1;if(!(typeof a=='function'))if(c(a)){var b=Array.prototype.slice.call(arguments,1),e=a.slice();for(var d=0,g=e.length;d0&&this._events[a].length>e&&(this._events[a].warned=!0,console.error('(node) warning: possible EventEmitter memory leak detected. %d listeners added. Use emitter.setMaxListeners() to increase limit.',this._events[a].length),console.trace())}this._events[a].push(b)}else this._events[a]=[this._events[a],b];return this},a.prototype.on=a.prototype.addListener,a.prototype.once=function(b,c){var a=this;return a.on(b,function d(){a.removeListener(b,d),c.apply(this,arguments)}),this},a.prototype.removeListener=function(a,d){if('function'!==typeof d)throw new Error('removeListener only takes instances of Function');if(!(this._events&&this._events[a]))return this;var b=this._events[a];if(c(b)){var e=b.indexOf(d);if(e<0)return this;b.splice(e,1),b.length==0&&delete this._events[a]}else this._events[a]===d&&delete this._events[a];return this},a.prototype.removeAllListeners=function(a){return a&&this._events&&this._events[a]&&(this._events[a]=null),this},a.prototype.listeners=function(a){return this._events||(this._events={}),this._events[a]||(this._events[a]=[]),c(this._events[a])||(this._events[a]=[this._events[a]]),this._events[a]}}),c.GIF=a('/gif.coffee')}.call(this,this)) -//# sourceMappingURL=gif.js.map -// gif.js 0.1.4 - https://github.com/jnordberg/gif.js \ No newline at end of file +(function(c){function a(b,d){if({}.hasOwnProperty.call(a.cache,b))return a.cache[b];var e=a.resolve(b);if(!e)throw new Error('Failed to resolve module '+b);var c={id:b,require:a,filename:b,exports:{},loaded:!1,parent:d,children:[]};d&&d.children.push(c);var f=b.slice(0,b.lastIndexOf('/')+1);return a.cache[b]=c.exports,e.call(c.exports,c,c.exports,f,b),c.loaded=!0,a.cache[b]=c.exports}a.modules={},a.cache={},a.resolve=function(b){return{}.hasOwnProperty.call(a.modules,b)?a.modules[b]:void 0},a.define=function(b,c){a.modules[b]=c};var b=function(a){return a='/',{title:'browser',version:'v0.10.5',browser:!0,env:{},argv:[],nextTick:c.setImmediate||function(a){setTimeout(a,0)},cwd:function(){return a},chdir:function(b){a=b}}}();a.define('/gif.coffee',function(b,k,j,i){function c(a,b){return{}.hasOwnProperty.call(a,b)}function g(d,b){for(var a=0,c=b.length;athis.frames.length;0<=this.frames.length?++a:--a)b.push(a);return b}.apply(this,arguments).length;athis.frames.length;0<=this.frames.length?++a:--a)b.push(a);return b}.apply(this,arguments)[a],b.push(null);return b}.call(this,[]),a=this.spawnWorkers();for(var b=0,c=function(){var c,c;c=[];for(var b=0;0<=a?ba;0<=a?++b:--b)c.push(b);return c}.apply(this,arguments).length;ba;0<=a?++b:--b)c.push(b);return c}.apply(this,arguments)[b],this.renderNextFrame();return this.emit('start'),this.emit('progress',0)},a.prototype.abort=function(){var a;while(!0){if(a=this.activeWorkers.shift(),!(null!=a))break;console.log('killing active worker'),a.terminate()}return this.running=!1,this.emit('abort')},a.prototype.spawnWorkers=function(){var a,b;return a=Math.min(this.options.workers,this.frames.length),function(){var c;c=[];for(var b=this.freeWorkers.length;this.freeWorkers.length<=a?ba;this.freeWorkers.length<=a?++b:--b)c.push(b);return c}.apply(this,arguments).forEach((b=this,function(d){var a,c;return console.log('spawning worker '+d),c=new Worker(b.options.workerScript),a=b,c.onmessage=function(b){return a.activeWorkers.splice(a.activeWorkers.indexOf(c),1),a.freeWorkers.push(c),a.frameFinished(b.data)},b.freeWorkers.push(c)})),a},a.prototype.frameFinished=function(a){return console.log('frame '+a.index+' finished - '+this.activeWorkers.length+' active'),this.finishedFrames++,this.emit('progress',this.finishedFrames/this.frames.length),this.imageParts[a.index]=a,g(null,this.imageParts)?this.renderNextFrame():this.finishRendering()},a.prototype.finishRendering=function(){var e,a,k,m,b,d,h;b=0;for(var f=0,j=this.imageParts.length;f=this.frames.length?void 0:(c=this.frames[this.nextFrame++],b=this.freeWorkers.shift(),a=this.getTask(c),console.log('starting frame '+(a.index+1)+' of '+this.frames.length),this.activeWorkers.push(b),b.postMessage(a))},a.prototype.getContextData=function(a){return a.getImageData(0,0,this.options.width,this.options.height).data},a.prototype.getImageData=function(b){var a;return null!=this._canvas||(this._canvas=document.createElement('canvas'),this._canvas.width=this.options.width,this._canvas.height=this.options.height),a=this._canvas.getContext('2d'),a.setFill=this.options.background,a.fillRect(0,0,this.options.width,this.options.height),a.drawImage(b,0,0),this.getContextData(a)},a.prototype.getTask=function(a){var c,b;if(c=this.frames.indexOf(a),b={index:c,last:c===this.frames.length-1,delay:a.delay,width:this.options.width,height:this.options.height,quality:this.options.quality,repeat:this.options.repeat,canTransfer:e.name==='chrome'},null!=a.data)b.data=a.data;else if(null!=a.context)b.data=this.getContextData(a.context);else if(null!=a.image)b.data=this.getImageData(a.image);else throw new Error('Invalid frame');return b},a}(d),b.exports=f}),a.define('/browser.coffee',function(f,g,h,i){var a,d,e,c,b;c=navigator.userAgent.toLowerCase(),e=navigator.platform.toLowerCase(),b=c.match(/(opera|ie|firefox|chrome|version)[\s\/:]([\w\d\.]+)?.*?(safari|version[\s\/:]([\w\d\.]+)|$)/)||[null,'unknown',0],d=b[1]==='ie'&&document.documentMode,a={name:b[1]==='version'?b[3]:b[1],version:d||parseFloat(b[1]==='opera'&&b[4]?b[4]:b[2]),platform:{name:c.match(/ip(?:ad|od|hone)/)?'ios':(c.match(/(?:webos|android)/)||e.match(/mac|win|linux/)||['other'])[0]}},a[a.name]=!0,a[a.name+parseInt(a.version,10)]=!0,a.platform[a.platform.name]=!0,f.exports=a}),a.define('events',function(f,e,g,h){b.EventEmitter||(b.EventEmitter=function(){});var a=e.EventEmitter=b.EventEmitter,c=typeof Array.isArray==='function'?Array.isArray:function(a){return Object.prototype.toString.call(a)==='[object Array]'},d=10;a.prototype.setMaxListeners=function(a){this._events||(this._events={}),this._events.maxListeners=a},a.prototype.emit=function(f){if(f==='error'&&(!(this._events&&this._events.error)||c(this._events.error)&&!this._events.error.length))throw arguments[1]instanceof Error?arguments[1]:new Error("Uncaught, unspecified 'error' event.");if(!this._events)return!1;var a=this._events[f];if(!a)return!1;if(!(typeof a=='function'))if(c(a)){var b=Array.prototype.slice.call(arguments,1),e=a.slice();for(var d=0,g=e.length;d0&&this._events[a].length>e&&(this._events[a].warned=!0,console.error('(node) warning: possible EventEmitter memory leak detected. %d listeners added. Use emitter.setMaxListeners() to increase limit.',this._events[a].length),console.trace())}this._events[a].push(b)}else this._events[a]=[this._events[a],b];return this},a.prototype.on=a.prototype.addListener,a.prototype.once=function(b,c){var a=this;return a.on(b,function d(){a.removeListener(b,d),c.apply(this,arguments)}),this},a.prototype.removeListener=function(a,d){if('function'!==typeof d)throw new Error('removeListener only takes instances of Function');if(!(this._events&&this._events[a]))return this;var b=this._events[a];if(c(b)){var e=b.indexOf(d);if(e<0)return this;b.splice(e,1),b.length==0&&delete this._events[a]}else this._events[a]===d&&delete this._events[a];return this},a.prototype.removeAllListeners=function(a){return a&&this._events&&this._events[a]&&(this._events[a]=null),this},a.prototype.listeners=function(a){return this._events||(this._events={}),this._events[a]||(this._events[a]=[]),c(this._events[a])||(this._events[a]=[this._events[a]]),this._events[a]}}),c.GIF=a('/gif.coffee')}.call(this,this)); diff --git a/js/lib/jsColor_1_4_0/.DS_Store b/js/lib/jsColor_1_4_0/.DS_Store deleted file mode 100644 index 56a80359..00000000 Binary files a/js/lib/jsColor_1_4_0/.DS_Store and /dev/null differ diff --git a/js/lib/jsColor_1_4_0/arrow.gif b/js/lib/jsColor_1_4_0/arrow.gif deleted file mode 100644 index 246478a8..00000000 Binary files a/js/lib/jsColor_1_4_0/arrow.gif and /dev/null differ diff --git a/js/lib/jsColor_1_4_0/cross.gif b/js/lib/jsColor_1_4_0/cross.gif deleted file mode 100644 index 0ee9c7ac..00000000 Binary files a/js/lib/jsColor_1_4_0/cross.gif and /dev/null differ diff --git a/js/lib/jsColor_1_4_0/demo.html b/js/lib/jsColor_1_4_0/demo.html deleted file mode 100644 index cb860664..00000000 --- a/js/lib/jsColor_1_4_0/demo.html +++ /dev/null @@ -1,12 +0,0 @@ - - - jscolor demo - - - - - - Click here: - - - diff --git a/js/lib/jsColor_1_4_0/hs.png b/js/lib/jsColor_1_4_0/hs.png deleted file mode 100644 index 3d94486c..00000000 Binary files a/js/lib/jsColor_1_4_0/hs.png and /dev/null differ diff --git a/js/lib/jsColor_1_4_0/hv.png b/js/lib/jsColor_1_4_0/hv.png deleted file mode 100644 index 1c5e01f8..00000000 Binary files a/js/lib/jsColor_1_4_0/hv.png and /dev/null differ diff --git a/js/lib/jsColor_1_4_0/jscolor.js b/js/lib/jsColor_1_4_0/jscolor.js deleted file mode 100644 index fab6dc60..00000000 --- a/js/lib/jsColor_1_4_0/jscolor.js +++ /dev/null @@ -1,927 +0,0 @@ -/** - * 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 - 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 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", + "
", + "
", + "" + ].join(''), + markup = (function () { + + // IE does not support gradients with multiple stops, so we need to simulate + // that for the rainbow slider with 8 divs that each have a single gradient + var gradientFix = ""; + if (IE) { + for (var i = 1; i <= 6; i++) { + gradientFix += "
"; + } + } + + return [ + "
", + "
", + "
", + "
", + "
", + "
", + "
", + "
", + "
", + "
", + "
", + "
", + "
", + "
", + "
", + "
", + "
", + gradientFix, + "
", + "
", + "
", + "
", + "
", + "", + "
", + "
", + "
", + "", + "", + "
", + "
", + "
" + ].join(""); + })(); + + function paletteTemplate (p, color, className) { + var html = []; + for (var i = 0; i < p.length; i++) { + var tiny = tinycolor(p[i]); + var c = tiny.toHsl().l < 0.5 ? "sp-thumb-el sp-thumb-dark" : "sp-thumb-el sp-thumb-light"; + c += (tinycolor.equals(color, p[i])) ? " sp-thumb-active" : ""; + + var swatchStyle = rgbaSupport ? ("background-color:" + tiny.toRgbString()) : "filter:" + tiny.toFilter(); + html.push(''); + } + return "
" + html.join('') + "
"; + } + + function hideAll() { + for (var i = 0; i < spectrums.length; i++) { + if (spectrums[i]) { + spectrums[i].hide(); + } + } + } + + function instanceOptions(o, callbackContext) { + var opts = $.extend({}, defaultOpts, o); + opts.callbacks = { + 'move': bind(opts.move, callbackContext), + 'change': bind(opts.change, callbackContext), + 'show': bind(opts.show, callbackContext), + 'hide': bind(opts.hide, callbackContext), + 'beforeShow': bind(opts.beforeShow, callbackContext) + }; + + return opts; + } + + function spectrum(element, o) { + + var opts = instanceOptions(o, element), + flat = opts.flat, + showSelectionPalette = opts.showSelectionPalette, + localStorageKey = opts.localStorageKey, + theme = opts.theme, + callbacks = opts.callbacks, + resize = throttle(reflow, 10), + visible = false, + dragWidth = 0, + dragHeight = 0, + dragHelperHeight = 0, + slideHeight = 0, + slideWidth = 0, + alphaWidth = 0, + alphaSlideHelperWidth = 0, + slideHelperHeight = 0, + currentHue = 0, + currentSaturation = 0, + currentValue = 0, + currentAlpha = 1, + palette = opts.palette.slice(0), + paletteArray = $.isArray(palette[0]) ? palette : [palette], + selectionPalette = opts.selectionPalette.slice(0), + maxSelectionSize = opts.maxSelectionSize, + draggingClass = "sp-dragging", + shiftMovementDirection = null; + + var doc = element.ownerDocument, + body = doc.body, + boundElement = $(element), + disabled = false, + container = $(markup, doc).addClass(theme), + dragger = container.find(".sp-color"), + dragHelper = container.find(".sp-dragger"), + slider = container.find(".sp-hue"), + slideHelper = container.find(".sp-slider"), + alphaSliderInner = container.find(".sp-alpha-inner"), + alphaSlider = container.find(".sp-alpha"), + alphaSlideHelper = container.find(".sp-alpha-handle"), + textInput = container.find(".sp-input"), + paletteContainer = container.find(".sp-palette"), + initialColorContainer = container.find(".sp-initial"), + cancelButton = container.find(".sp-cancel"), + chooseButton = container.find(".sp-choose"), + isInput = boundElement.is("input"), + shouldReplace = isInput && !flat, + replacer = (shouldReplace) ? $(replaceInput).addClass(theme).addClass(opts.className) : $([]), + offsetElement = (shouldReplace) ? replacer : boundElement, + previewElement = replacer.find(".sp-preview-inner"), + initialColor = opts.color || (isInput && boundElement.val()), + colorOnShow = false, + preferredFormat = opts.preferredFormat, + currentPreferredFormat = preferredFormat, + clickoutFiresChange = !opts.showButtons || opts.clickoutFiresChange; + + + function applyOptions() { + + if (opts.showPaletteOnly) { + opts.showPalette = true; + } + + container.toggleClass("sp-flat", flat); + container.toggleClass("sp-input-disabled", !opts.showInput); + container.toggleClass("sp-alpha-enabled", opts.showAlpha); + container.toggleClass("sp-buttons-disabled", !opts.showButtons); + container.toggleClass("sp-palette-disabled", !opts.showPalette); + container.toggleClass("sp-palette-only", opts.showPaletteOnly); + container.toggleClass("sp-initial-disabled", !opts.showInitial); + container.addClass(opts.className); + + reflow(); + } + + function initialize() { + + if (IE) { + container.find("*:not(input)").attr("unselectable", "on"); + } + + applyOptions(); + + if (shouldReplace) { + boundElement.after(replacer).hide(); + } + + if (flat) { + boundElement.after(container).hide(); + } + else { + + var appendTo = opts.appendTo === "parent" ? boundElement.parent() : $(opts.appendTo); + if (appendTo.length !== 1) { + appendTo = $("body"); + } + + appendTo.append(container); + } + + if (localStorageKey && window.localStorage) { + + // Migrate old palettes over to new format. May want to remove this eventually. + try { + var oldPalette = window.localStorage[localStorageKey].split(",#"); + if (oldPalette.length > 1) { + delete window.localStorage[localStorageKey]; + $.each(oldPalette, function(i, c) { + addColorToSelectionPalette(c); + }); + } + } + catch(e) { } + + try { + selectionPalette = window.localStorage[localStorageKey].split(";"); + } + catch (e) { } + } + + offsetElement.bind("click.spectrum touchstart.spectrum", function (e) { + if (!disabled) { + toggle(); + } + + e.stopPropagation(); + + if (!$(e.target).is("input")) { + e.preventDefault(); + } + }); + + if(boundElement.is(":disabled") || (opts.disabled === true)) { + disable(); + } + + // Prevent clicks from bubbling up to document. This would cause it to be hidden. + container.click(stopPropagation); + + // Handle user typed input + textInput.change(setFromTextInput); + textInput.bind("paste", function () { + setTimeout(setFromTextInput, 1); + }); + textInput.keydown(function (e) { if (e.keyCode == 13) { setFromTextInput(); } }); + + cancelButton.text(opts.cancelText); + cancelButton.bind("click.spectrum", function (e) { + e.stopPropagation(); + e.preventDefault(); + hide("cancel"); + }); + + chooseButton.text(opts.chooseText); + chooseButton.bind("click.spectrum", function (e) { + e.stopPropagation(); + e.preventDefault(); + + if (isValid()) { + updateOriginalInput(true); + hide(); + } + }); + + draggable(alphaSlider, function (dragX, dragY, e) { + currentAlpha = (dragX / alphaWidth); + if (e.shiftKey) { + currentAlpha = Math.round(currentAlpha * 10) / 10; + } + + move(); + }); + + draggable(slider, function (dragX, dragY) { + currentHue = parseFloat(dragY / slideHeight); + move(); + }, dragStart, dragStop); + + draggable(dragger, function (dragX, dragY, e) { + + // shift+drag should snap the movement to either the x or y axis. + if (!e.shiftKey) { + shiftMovementDirection = null; + } + else if (!shiftMovementDirection) { + var oldDragX = currentSaturation * dragWidth; + var oldDragY = dragHeight - (currentValue * dragHeight); + var furtherFromX = Math.abs(dragX - oldDragX) > Math.abs(dragY - oldDragY); + + shiftMovementDirection = furtherFromX ? "x" : "y"; + } + + var setSaturation = !shiftMovementDirection || shiftMovementDirection === "x"; + var setValue = !shiftMovementDirection || shiftMovementDirection === "y"; + + if (setSaturation) { + currentSaturation = parseFloat(dragX / dragWidth); + } + if (setValue) { + currentValue = parseFloat((dragHeight - dragY) / dragHeight); + } + + move(); + + }, dragStart, dragStop); + + if (!!initialColor) { + set(initialColor); + + // In case color was black - update the preview UI and set the format + // since the set function will not run (default color is black). + updateUI(); + currentPreferredFormat = preferredFormat || tinycolor(initialColor).format; + + addColorToSelectionPalette(initialColor); + } + else { + updateUI(); + } + + if (flat) { + show(); + } + + function palletElementClick(e) { + if (e.data && e.data.ignore) { + set($(this).data("color")); + move(); + } + else { + set($(this).data("color")); + updateOriginalInput(true); + move(); + hide(); + } + + return false; + } + + var paletteEvent = IE ? "mousedown.spectrum" : "click.spectrum touchstart.spectrum"; + paletteContainer.delegate(".sp-thumb-el", paletteEvent, palletElementClick); + initialColorContainer.delegate(".sp-thumb-el:nth-child(1)", paletteEvent, { ignore: true }, palletElementClick); + } + + function addColorToSelectionPalette(color) { + if (showSelectionPalette) { + var colorRgb = tinycolor(color).toRgbString(); + if ($.inArray(colorRgb, selectionPalette) === -1) { + selectionPalette.push(colorRgb); + while(selectionPalette.length > maxSelectionSize) { + selectionPalette.shift(); + } + } + + if (localStorageKey && window.localStorage) { + try { + window.localStorage[localStorageKey] = selectionPalette.join(";"); + } + catch(e) { } + } + } + } + + function getUniqueSelectionPalette() { + var unique = []; + var p = selectionPalette; + var paletteLookup = {}; + var rgb; + + if (opts.showPalette) { + + for (var i = 0; i < paletteArray.length; i++) { + for (var j = 0; j < paletteArray[i].length; j++) { + rgb = tinycolor(paletteArray[i][j]).toRgbString(); + paletteLookup[rgb] = true; + } + } + + for (i = 0; i < p.length; i++) { + rgb = tinycolor(p[i]).toRgbString(); + + if (!paletteLookup.hasOwnProperty(rgb)) { + unique.push(p[i]); + paletteLookup[rgb] = true; + } + } + } + + return unique.reverse().slice(0, opts.maxSelectionSize); + } + + function drawPalette() { + + var currentColor = get(); + + var html = $.map(paletteArray, function (palette, i) { + return paletteTemplate(palette, currentColor, "sp-palette-row sp-palette-row-" + i); + }); + + if (selectionPalette) { + html.push(paletteTemplate(getUniqueSelectionPalette(), currentColor, "sp-palette-row sp-palette-row-selection")); + } + + paletteContainer.html(html.join("")); + } + + function drawInitial() { + if (opts.showInitial) { + var initial = colorOnShow; + var current = get(); + initialColorContainer.html(paletteTemplate([initial, current], current, "sp-palette-row-initial")); + } + } + + function dragStart() { + if (dragHeight <= 0 || dragWidth <= 0 || slideHeight <= 0) { + reflow(); + } + container.addClass(draggingClass); + shiftMovementDirection = null; + } + + function dragStop() { + container.removeClass(draggingClass); + } + + function setFromTextInput() { + var tiny = tinycolor(textInput.val()); + if (tiny.ok) { + set(tiny); + } + else { + textInput.addClass("sp-validation-error"); + } + } + + function toggle() { + if (visible) { + hide(); + } + else { + show(); + } + } + + function show() { + var event = $.Event('beforeShow.spectrum'); + + if (visible) { + reflow(); + return; + } + + colorOnShow = get(); + boundElement.trigger(event, [ colorOnShow ]); + + if (callbacks.beforeShow(colorOnShow) === false || event.isDefaultPrevented()) { + return; + } + + // if color has changed + set(colorOnShow); + + hideAll(); + visible = true; + + $(doc).bind("mousedown.spectrum", onMousedown); + + // Piskel-specific : change the color as soon as the user does a mouseup + $(doc).bind("mouseup.spectrum", updateColor); + + $(window).bind("resize.spectrum", resize); + replacer.addClass("sp-active"); + container.removeClass("sp-hidden"); + + if (opts.showPalette) { + drawPalette(); + } + reflow(); + updateUI(); + + drawInitial(); + callbacks.show(colorOnShow); + boundElement.trigger('show.spectrum', [ colorOnShow ]); + } + + function onMousedown (e) { + var target = $(e.target); + var parents = target.parents(); + var isClickOutsideWidget = !parents.is(container) && !target.is(container); + + if (isClickOutsideWidget) { + hide(e); + } + } + + // Piskel-specific (code extracted to method) + function updateColor(e) { + var colorHasChanged = !tinycolor.equals(get(), colorOnShow); + + if (colorHasChanged) { + if (clickoutFiresChange && e !== "cancel") { + updateOriginalInput(true); + } + else { + revert(); + } + } + } + + function hide(e) { + + // Return on right click + if (e && e.type == "click" && e.button == 2) { return; } + + // Return if hiding is unnecessary + if (!visible || flat) { return; } + visible = false; + + $(doc).unbind("mousedown.spectrum", onMousedown); + + // Piskel-specific + $(doc).unbind("mouseup.spectrum", updateColor); + + $(window).unbind("resize.spectrum", resize); + + replacer.removeClass("sp-active"); + container.addClass("sp-hidden"); + + updateColor(e); + + // Piskel-specific + addColorToSelectionPalette(get()); + + callbacks.hide(get()); + boundElement.trigger('hide.spectrum', [ get() ]); + } + + function revert() { + set(colorOnShow, true); + } + + function set(color, ignoreFormatChange) { + if (tinycolor.equals(color, get())) { + return; + } + + var newColor = tinycolor(color); + var newHsv = newColor.toHsv(); + + currentHue = (newHsv.h % 360) / 360; + currentSaturation = newHsv.s; + currentValue = newHsv.v; + currentAlpha = newHsv.a; + + updateUI(); + + if (newColor.ok && !ignoreFormatChange) { + currentPreferredFormat = preferredFormat || newColor.format; + } + } + + function get(opts) { + opts = opts || { }; + return tinycolor.fromRatio({ + h: currentHue, + s: currentSaturation, + v: currentValue, + a: Math.round(currentAlpha * 100) / 100 + }, { format: opts.format || currentPreferredFormat }); + } + + function isValid() { + return !textInput.hasClass("sp-validation-error"); + } + + function move() { + updateUI(); + + callbacks.move(get()); + boundElement.trigger('move.spectrum', [ get() ]); + } + + function updateUI() { + + textInput.removeClass("sp-validation-error"); + + updateHelperLocations(); + + // Update dragger background color (gradients take care of saturation and value). + var flatColor = tinycolor.fromRatio({ h: currentHue, s: 1, v: 1 }); + dragger.css("background-color", flatColor.toHexString()); + + // Get a format that alpha will be included in (hex and names ignore alpha) + var format = currentPreferredFormat; + if (currentAlpha < 1) { + if (format === "hex" || format === "hex3" || format === "hex6" || format === "name") { + format = "rgb"; + } + } + + var realColor = get({ format: format }), + realHex = realColor.toHexString(), + realRgb = realColor.toRgbString(); + + // Update the replaced elements background color (with actual selected color) + if (rgbaSupport || realColor.alpha === 1) { + previewElement.css("background-color", realRgb); + } + else { + previewElement.css("background-color", "transparent"); + previewElement.css("filter", realColor.toFilter()); + } + + if (opts.showAlpha) { + var rgb = realColor.toRgb(); + rgb.a = 0; + var realAlpha = tinycolor(rgb).toRgbString(); + var gradient = "linear-gradient(left, " + realAlpha + ", " + realHex + ")"; + + if (IE) { + alphaSliderInner.css("filter", tinycolor(realAlpha).toFilter({ gradientType: 1 }, realHex)); + } + else { + alphaSliderInner.css("background", "-webkit-" + gradient); + alphaSliderInner.css("background", "-moz-" + gradient); + alphaSliderInner.css("background", "-ms-" + gradient); + alphaSliderInner.css("background", gradient); + } + } + + + // Update the text entry input as it changes happen + if (opts.showInput) { + textInput.val(realColor.toString(format)); + } + + if (opts.showPalette) { + drawPalette(); + } + + drawInitial(); + } + + function updateHelperLocations() { + var s = currentSaturation; + var v = currentValue; + + // Where to show the little circle in that displays your current selected color + var dragX = s * dragWidth; + var dragY = (dragHeight) - (v * dragHeight); + dragX = Math.max( + -dragHelperHeight/2, + Math.min(dragWidth - dragHelperHeight/2, dragX - dragHelperHeight/2) + ); + dragY = Math.max( + -dragHelperHeight/2, + Math.min(dragHeight - dragHelperHeight/2, dragY - dragHelperHeight/2) + ); + dragHelper.css({ + "top": dragY, + "left": dragX + }); + + var alphaX = currentAlpha * alphaWidth; + alphaSlideHelper.css({ + "left": alphaX - (alphaSlideHelperWidth / 2) + }); + + // Where to show the bar that displays your current selected hue + var slideY = (currentHue) * slideHeight; + slideHelper.css({ + "top": slideY - (slideHelperHeight/2) + }); + } + + function updateOriginalInput(fireCallback) { + var color = get(); + + if (isInput) { + boundElement.val(color.toString(currentPreferredFormat)); + } + + var hasChanged = !tinycolor.equals(color, colorOnShow); + colorOnShow = color; + + // Update the selection palette with the current color + + // Piskel-specific : commented-out, palette update is done on hide + // addColorToSelectionPalette(color); + + if (fireCallback && hasChanged) { + callbacks.change(color); + boundElement.trigger('change', [ color ]); + } + } + + function reflow() { + dragWidth = dragger.width(); + dragHeight = dragger.height(); + dragHelperHeight = dragHelper.height() + 4; + slideWidth = slider.width(); + slideHeight = slider.height(); + slideHelperHeight = slideHelper.height() + 4; + alphaWidth = alphaSlider.width(); + alphaSlideHelperWidth = alphaSlideHelper.width(); + + if (!flat) { + container.css("position", "absolute"); + container.offset(getOffset(container, offsetElement)); + } + + updateHelperLocations(); + } + + function destroy() { + boundElement.show(); + offsetElement.unbind("click.spectrum touchstart.spectrum"); + container.remove(); + replacer.remove(); + spectrums[spect.id] = null; + } + + function option(optionName, optionValue) { + if (optionName === undefined) { + return $.extend({}, opts); + } + if (optionValue === undefined) { + return opts[optionName]; + } + + opts[optionName] = optionValue; + applyOptions(); + } + + function enable() { + disabled = false; + boundElement.attr("disabled", false); + offsetElement.removeClass("sp-disabled"); + } + + function disable() { + hide(); + disabled = true; + boundElement.attr("disabled", true); + offsetElement.addClass("sp-disabled"); + } + + initialize(); + + var spect = { + show: show, + hide: hide, + toggle: toggle, + reflow: reflow, + option: option, + enable: enable, + disable: disable, + set: function (c) { + set(c); + updateOriginalInput(); + }, + get: get, + destroy: destroy, + container: container + }; + + spect.id = spectrums.push(spect) - 1; + + return spect; + } + + /** + * checkOffset - get the offset below/above and left/right element depending on screen position + * Thanks https://github.com/jquery/jquery-ui/blob/master/ui/jquery.ui.datepicker.js + */ + function getOffset(picker, input) { + var extraY = 0; + var dpWidth = picker.outerWidth(); + var dpHeight = picker.outerHeight(); + var inputHeight = input.outerHeight(); + var doc = picker[0].ownerDocument; + var docElem = doc.documentElement; + var viewWidth = docElem.clientWidth + $(doc).scrollLeft(); + var viewHeight = docElem.clientHeight + $(doc).scrollTop(); + var offset = input.offset(); + offset.top += inputHeight; + + if (Math.min(offset.left, (offset.left + dpWidth > viewWidth && viewWidth > dpWidth))) { + offset.left -= Math.abs(offset.left + dpWidth - viewWidth); + picker.attr('data-x-position','right'); + } else { + offset.left -= 0; + picker.attr('data-x-position','left'); + } + + if (Math.min(offset.top, (offset.top + dpHeight > viewHeight && viewHeight > dpHeight))) { + offset.top -= Math.abs(dpHeight + inputHeight - extraY); + picker.attr('data-y-position','top'); + } else { + offset.top -= extraY; + picker.attr('data-y-position','bottom'); + } + + return offset; + } + + /** + * noop - do nothing + */ + function noop() { + + } + + /** + * stopPropagation - makes the code only doing this a little easier to read in line + */ + function stopPropagation(e) { + e.stopPropagation(); + } + + /** + * Create a function bound to a given object + * Thanks to underscore.js + */ + function bind(func, obj) { + var slice = Array.prototype.slice; + var args = slice.call(arguments, 2); + return function () { + return func.apply(obj, args.concat(slice.call(arguments))); + }; + } + + /** + * Lightweight drag helper. Handles containment within the element, so that + * when dragging, the x is within [0,element.width] and y is within [0,element.height] + */ + function draggable(element, onmove, onstart, onstop) { + onmove = onmove || function () { }; + onstart = onstart || function () { }; + onstop = onstop || function () { }; + var doc = element.ownerDocument || document; + var dragging = false; + var offset = {}; + var maxHeight = 0; + var maxWidth = 0; + var hasTouch = ('ontouchstart' in window); + + var duringDragEvents = {}; + duringDragEvents["selectstart"] = prevent; + duringDragEvents["dragstart"] = prevent; + duringDragEvents["touchmove mousemove"] = move; + duringDragEvents["touchend mouseup"] = stop; + + function prevent(e) { + if (e.stopPropagation) { + e.stopPropagation(); + } + if (e.preventDefault) { + e.preventDefault(); + } + e.returnValue = false; + } + + function move(e) { + if (dragging) { + // Mouseup happened outside of window + if (IE && document.documentMode < 9 && !e.button) { + return stop(); + } + + var touches = e.originalEvent.touches; + var pageX = touches ? touches[0].pageX : e.pageX; + var pageY = touches ? touches[0].pageY : e.pageY; + + var dragX = Math.max(0, Math.min(pageX - offset.left, maxWidth)); + var dragY = Math.max(0, Math.min(pageY - offset.top, maxHeight)); + + if (hasTouch) { + // Stop scrolling in iOS + prevent(e); + } + + onmove.apply(element, [dragX, dragY, e]); + } + } + function start(e) { + var rightclick = (e.which) ? (e.which == 3) : (e.button == 2); + var touches = e.originalEvent.touches; + + if (!rightclick && !dragging) { + if (onstart.apply(element, arguments) !== false) { + dragging = true; + maxHeight = $(element).height(); + maxWidth = $(element).width(); + offset = $(element).offset(); + + $(doc).bind(duringDragEvents); + $(doc.body).addClass("sp-dragging"); + + if (!hasTouch) { + move(e); + } + + prevent(e); + } + } + } + function stop() { + if (dragging) { + $(doc).unbind(duringDragEvents); + $(doc.body).removeClass("sp-dragging"); + onstop.apply(element, arguments); + } + dragging = false; + } + + $(element).bind("touchstart mousedown", start); + } + + function throttle(func, wait, debounce) { + var timeout; + return function () { + var context = this, args = arguments; + var throttler = function () { + timeout = null; + func.apply(context, args); + }; + if (debounce) clearTimeout(timeout); + if (debounce || !timeout) timeout = setTimeout(throttler, wait); + }; + } + + + function log(){/* jshint -W021 */if(window.console){if(Function.prototype.bind)log=Function.prototype.bind.call(console.log,console);else log=function(){Function.prototype.apply.call(console.log,console,arguments);};log.apply(this,arguments);}} + + /** + * Define a jQuery plugin + */ + var dataID = "spectrum.id"; + $.fn.spectrum = function (opts, extra) { + + if (typeof opts == "string") { + + var returnValue = this; + var args = Array.prototype.slice.call( arguments, 1 ); + + this.each(function () { + var spect = spectrums[$(this).data(dataID)]; + if (spect) { + + var method = spect[opts]; + if (!method) { + throw new Error( "Spectrum: no such method: '" + opts + "'" ); + } + + if (opts == "get") { + returnValue = spect.get(); + } + else if (opts == "container") { + returnValue = spect.container; + } + else if (opts == "option") { + returnValue = spect.option.apply(spect, args); + } + else if (opts == "destroy") { + spect.destroy(); + $(this).removeData(dataID); + } + else { + method.apply(spect, args); + } + } + }); + + return returnValue; + } + + // Initializing a new instance of spectrum + return this.spectrum("destroy").each(function () { + var spect = spectrum(this, opts); + $(this).data(dataID, spect.id); + }); + }; + + $.fn.spectrum.load = true; + $.fn.spectrum.loadOpts = {}; + $.fn.spectrum.draggable = draggable; + $.fn.spectrum.defaults = defaultOpts; + + $.spectrum = { }; + $.spectrum.localization = { }; + $.spectrum.palettes = { }; + + $.fn.spectrum.processNativeColorInputs = function () { + var colorInput = $("")[0]; + var supportsColor = colorInput.type === "color" && colorInput.value != "!"; + + if (!supportsColor) { + $("input[type=color]").spectrum({ + preferredFormat: "hex6" + }); + } + }; + + // TinyColor v0.9.16 + // https://github.com/bgrins/TinyColor + // 2013-08-10, Brian Grinstead, MIT License + + (function() { + + var trimLeft = /^[\s,#]+/, + trimRight = /\s+$/, + tinyCounter = 0, + math = Math, + mathRound = math.round, + mathMin = math.min, + mathMax = math.max, + mathRandom = math.random; + + function tinycolor (color, opts) { + + color = (color) ? color : ''; + opts = opts || { }; + + // If input is already a tinycolor, return itself + if (typeof color == "object" && color.hasOwnProperty("_tc_id")) { + return color; + } + + var rgb = inputToRGB(color); + var r = rgb.r, + g = rgb.g, + b = rgb.b, + a = rgb.a, + roundA = mathRound(100*a) / 100, + format = opts.format || rgb.format; + + // Don't let the range of [0,255] come back in [0,1]. + // Potentially lose a little bit of precision here, but will fix issues where + // .5 gets interpreted as half of the total, instead of half of 1 + // If it was supposed to be 128, this was already taken care of by `inputToRgb` + if (r < 1) { r = mathRound(r); } + if (g < 1) { g = mathRound(g); } + if (b < 1) { b = mathRound(b); } + + return { + ok: rgb.ok, + format: format, + _tc_id: tinyCounter++, + alpha: a, + getAlpha: function() { + return a; + }, + setAlpha: function(value) { + a = boundAlpha(value); + roundA = mathRound(100*a) / 100; + }, + toHsv: function() { + var hsv = rgbToHsv(r, g, b); + return { h: hsv.h * 360, s: hsv.s, v: hsv.v, a: a }; + }, + toHsvString: function() { + var hsv = rgbToHsv(r, g, b); + var h = mathRound(hsv.h * 360), s = mathRound(hsv.s * 100), v = mathRound(hsv.v * 100); + return (a == 1) ? + "hsv(" + h + ", " + s + "%, " + v + "%)" : + "hsva(" + h + ", " + s + "%, " + v + "%, "+ roundA + ")"; + }, + toHsl: function() { + var hsl = rgbToHsl(r, g, b); + return { h: hsl.h * 360, s: hsl.s, l: hsl.l, a: a }; + }, + toHslString: function() { + var hsl = rgbToHsl(r, g, b); + var h = mathRound(hsl.h * 360), s = mathRound(hsl.s * 100), l = mathRound(hsl.l * 100); + return (a == 1) ? + "hsl(" + h + ", " + s + "%, " + l + "%)" : + "hsla(" + h + ", " + s + "%, " + l + "%, "+ roundA + ")"; + }, + toHex: function(allow3Char) { + return rgbToHex(r, g, b, allow3Char); + }, + toHexString: function(allow3Char) { + return '#' + rgbToHex(r, g, b, allow3Char); + }, + toRgb: function() { + return { r: mathRound(r), g: mathRound(g), b: mathRound(b), a: a }; + }, + toRgbString: function() { + return (a == 1) ? + "rgb(" + mathRound(r) + ", " + mathRound(g) + ", " + mathRound(b) + ")" : + "rgba(" + mathRound(r) + ", " + mathRound(g) + ", " + mathRound(b) + ", " + roundA + ")"; + }, + toPercentageRgb: function() { + return { r: mathRound(bound01(r, 255) * 100) + "%", g: mathRound(bound01(g, 255) * 100) + "%", b: mathRound(bound01(b, 255) * 100) + "%", a: a }; + }, + toPercentageRgbString: function() { + return (a == 1) ? + "rgb(" + mathRound(bound01(r, 255) * 100) + "%, " + mathRound(bound01(g, 255) * 100) + "%, " + mathRound(bound01(b, 255) * 100) + "%)" : + "rgba(" + mathRound(bound01(r, 255) * 100) + "%, " + mathRound(bound01(g, 255) * 100) + "%, " + mathRound(bound01(b, 255) * 100) + "%, " + roundA + ")"; + }, + toName: function() { + if (a === 0) { + return "transparent"; + } + + return hexNames[rgbToHex(r, g, b, true)] || false; + }, + toFilter: function(secondColor) { + var hex = rgbToHex(r, g, b); + var secondHex = hex; + var alphaHex = Math.round(parseFloat(a) * 255).toString(16); + var secondAlphaHex = alphaHex; + var gradientType = opts && opts.gradientType ? "GradientType = 1, " : ""; + + if (secondColor) { + var s = tinycolor(secondColor); + secondHex = s.toHex(); + secondAlphaHex = Math.round(parseFloat(s.alpha) * 255).toString(16); + } + + return "progid:DXImageTransform.Microsoft.gradient("+gradientType+"startColorstr=#" + pad2(alphaHex) + hex + ",endColorstr=#" + pad2(secondAlphaHex) + secondHex + ")"; + }, + toString: function(format) { + var formatSet = !!format; + format = format || this.format; + + var formattedString = false; + var hasAlphaAndFormatNotSet = !formatSet && a < 1 && a > 0; + var formatWithAlpha = hasAlphaAndFormatNotSet && (format === "hex" || format === "hex6" || format === "hex3" || format === "name"); + + if (format === "rgb") { + formattedString = this.toRgbString(); + } + if (format === "prgb") { + formattedString = this.toPercentageRgbString(); + } + if (format === "hex" || format === "hex6") { + formattedString = this.toHexString(); + } + if (format === "hex3") { + formattedString = this.toHexString(true); + } + if (format === "name") { + formattedString = this.toName(); + } + if (format === "hsl") { + formattedString = this.toHslString(); + } + if (format === "hsv") { + formattedString = this.toHsvString(); + } + + if (formatWithAlpha) { + return this.toRgbString(); + } + + return formattedString || this.toHexString(); + } + }; + } + + // If input is an object, force 1 into "1.0" to handle ratios properly + // String input requires "1.0" as input, so 1 will be treated as 1 + tinycolor.fromRatio = function(color, opts) { + if (typeof color == "object") { + var newColor = {}; + for (var i in color) { + if (color.hasOwnProperty(i)) { + if (i === "a") { + newColor[i] = color[i]; + } + else { + newColor[i] = convertToPercentage(color[i]); + } + } + } + color = newColor; + } + + return tinycolor(color, opts); + }; + + // Given a string or object, convert that input to RGB + // Possible string inputs: + // + // "red" + // "#f00" or "f00" + // "#ff0000" or "ff0000" + // "rgb 255 0 0" or "rgb (255, 0, 0)" + // "rgb 1.0 0 0" or "rgb (1, 0, 0)" + // "rgba (255, 0, 0, 1)" or "rgba 255, 0, 0, 1" + // "rgba (1.0, 0, 0, 1)" or "rgba 1.0, 0, 0, 1" + // "hsl(0, 100%, 50%)" or "hsl 0 100% 50%" + // "hsla(0, 100%, 50%, 1)" or "hsla 0 100% 50%, 1" + // "hsv(0, 100%, 100%)" or "hsv 0 100% 100%" + // + function inputToRGB(color) { + + var rgb = { r: 0, g: 0, b: 0 }; + var a = 1; + var ok = false; + var format = false; + + if (typeof color == "string") { + color = stringInputToObject(color); + } + + if (typeof color == "object") { + if (color.hasOwnProperty("r") && color.hasOwnProperty("g") && color.hasOwnProperty("b")) { + rgb = rgbToRgb(color.r, color.g, color.b); + ok = true; + format = String(color.r).substr(-1) === "%" ? "prgb" : "rgb"; + } + else if (color.hasOwnProperty("h") && color.hasOwnProperty("s") && color.hasOwnProperty("v")) { + color.s = convertToPercentage(color.s); + color.v = convertToPercentage(color.v); + rgb = hsvToRgb(color.h, color.s, color.v); + ok = true; + format = "hsv"; + } + else if (color.hasOwnProperty("h") && color.hasOwnProperty("s") && color.hasOwnProperty("l")) { + color.s = convertToPercentage(color.s); + color.l = convertToPercentage(color.l); + rgb = hslToRgb(color.h, color.s, color.l); + ok = true; + format = "hsl"; + } + + if (color.hasOwnProperty("a")) { + a = color.a; + } + } + + a = boundAlpha(a); + + return { + ok: ok, + format: color.format || format, + r: mathMin(255, mathMax(rgb.r, 0)), + g: mathMin(255, mathMax(rgb.g, 0)), + b: mathMin(255, mathMax(rgb.b, 0)), + a: a + }; + } + + + // Conversion Functions + // -------------------- + + // `rgbToHsl`, `rgbToHsv`, `hslToRgb`, `hsvToRgb` modified from: + // + + // `rgbToRgb` + // Handle bounds / percentage checking to conform to CSS color spec + // + // *Assumes:* r, g, b in [0, 255] or [0, 1] + // *Returns:* { r, g, b } in [0, 255] + function rgbToRgb(r, g, b){ + return { + r: bound01(r, 255) * 255, + g: bound01(g, 255) * 255, + b: bound01(b, 255) * 255 + }; + } + + // `rgbToHsl` + // Converts an RGB color value to HSL. + // *Assumes:* r, g, and b are contained in [0, 255] or [0, 1] + // *Returns:* { h, s, l } in [0,1] + function rgbToHsl(r, g, b) { + + r = bound01(r, 255); + g = bound01(g, 255); + b = bound01(b, 255); + + var max = mathMax(r, g, b), min = mathMin(r, g, b); + var h, s, l = (max + min) / 2; + + if(max == min) { + h = s = 0; // achromatic + } + else { + var d = max - min; + s = l > 0.5 ? d / (2 - max - min) : d / (max + min); + switch(max) { + case r: h = (g - b) / d + (g < b ? 6 : 0); break; + case g: h = (b - r) / d + 2; break; + case b: h = (r - g) / d + 4; break; + } + + h /= 6; + } + + return { h: h, s: s, l: l }; + } + + // `hslToRgb` + // Converts an HSL color value to RGB. + // *Assumes:* h is contained in [0, 1] or [0, 360] and s and l are contained [0, 1] or [0, 100] + // *Returns:* { r, g, b } in the set [0, 255] + function hslToRgb(h, s, l) { + var r, g, b; + + h = bound01(h, 360); + s = bound01(s, 100); + l = bound01(l, 100); + + function hue2rgb(p, q, t) { + if(t < 0) t += 1; + if(t > 1) t -= 1; + if(t < 1/6) return p + (q - p) * 6 * t; + if(t < 1/2) return q; + if(t < 2/3) return p + (q - p) * (2/3 - t) * 6; + return p; + } + + if(s === 0) { + r = g = b = l; // achromatic + } + else { + var q = l < 0.5 ? l * (1 + s) : l + s - l * s; + var p = 2 * l - q; + r = hue2rgb(p, q, h + 1/3); + g = hue2rgb(p, q, h); + b = hue2rgb(p, q, h - 1/3); + } + + return { r: r * 255, g: g * 255, b: b * 255 }; + } + + // `rgbToHsv` + // Converts an RGB color value to HSV + // *Assumes:* r, g, and b are contained in the set [0, 255] or [0, 1] + // *Returns:* { h, s, v } in [0,1] + function rgbToHsv(r, g, b) { + + r = bound01(r, 255); + g = bound01(g, 255); + b = bound01(b, 255); + + var max = mathMax(r, g, b), min = mathMin(r, g, b); + var h, s, v = max; + + var d = max - min; + s = max === 0 ? 0 : d / max; + + if(max == min) { + h = 0; // achromatic + } + else { + switch(max) { + case r: h = (g - b) / d + (g < b ? 6 : 0); break; + case g: h = (b - r) / d + 2; break; + case b: h = (r - g) / d + 4; break; + } + h /= 6; + } + return { h: h, s: s, v: v }; + } + + // `hsvToRgb` + // Converts an HSV color value to RGB. + // *Assumes:* h is contained in [0, 1] or [0, 360] and s and v are contained in [0, 1] or [0, 100] + // *Returns:* { r, g, b } in the set [0, 255] + function hsvToRgb(h, s, v) { + + h = bound01(h, 360) * 6; + s = bound01(s, 100); + v = bound01(v, 100); + + var i = math.floor(h), + f = h - i, + p = v * (1 - s), + q = v * (1 - f * s), + t = v * (1 - (1 - f) * s), + mod = i % 6, + r = [v, q, p, p, t, v][mod], + g = [t, v, v, q, p, p][mod], + b = [p, p, t, v, v, q][mod]; + + return { r: r * 255, g: g * 255, b: b * 255 }; + } + + // `rgbToHex` + // Converts an RGB color to hex + // Assumes r, g, and b are contained in the set [0, 255] + // Returns a 3 or 6 character hex + function rgbToHex(r, g, b, allow3Char) { + + var hex = [ + pad2(mathRound(r).toString(16)), + pad2(mathRound(g).toString(16)), + pad2(mathRound(b).toString(16)) + ]; + + // Return a 3 character hex if possible + if (allow3Char && hex[0].charAt(0) == hex[0].charAt(1) && hex[1].charAt(0) == hex[1].charAt(1) && hex[2].charAt(0) == hex[2].charAt(1)) { + return hex[0].charAt(0) + hex[1].charAt(0) + hex[2].charAt(0); + } + + return hex.join(""); + } + + // `equals` + // Can be called with any tinycolor input + tinycolor.equals = function (color1, color2) { + if (!color1 || !color2) { return false; } + return tinycolor(color1).toRgbString() == tinycolor(color2).toRgbString(); + }; + tinycolor.random = function() { + return tinycolor.fromRatio({ + r: mathRandom(), + g: mathRandom(), + b: mathRandom() + }); + }; + + + // Modification Functions + // ---------------------- + // Thanks to less.js for some of the basics here + // + + tinycolor.desaturate = function (color, amount) { + amount = (amount === 0) ? 0 : (amount || 10); + var hsl = tinycolor(color).toHsl(); + hsl.s -= amount / 100; + hsl.s = clamp01(hsl.s); + return tinycolor(hsl); + }; + tinycolor.saturate = function (color, amount) { + amount = (amount === 0) ? 0 : (amount || 10); + var hsl = tinycolor(color).toHsl(); + hsl.s += amount / 100; + hsl.s = clamp01(hsl.s); + return tinycolor(hsl); + }; + tinycolor.greyscale = function(color) { + return tinycolor.desaturate(color, 100); + }; + tinycolor.lighten = function(color, amount) { + amount = (amount === 0) ? 0 : (amount || 10); + var hsl = tinycolor(color).toHsl(); + hsl.l += amount / 100; + hsl.l = clamp01(hsl.l); + return tinycolor(hsl); + }; + tinycolor.darken = function (color, amount) { + amount = (amount === 0) ? 0 : (amount || 10); + var hsl = tinycolor(color).toHsl(); + hsl.l -= amount / 100; + hsl.l = clamp01(hsl.l); + return tinycolor(hsl); + }; + tinycolor.complement = function(color) { + var hsl = tinycolor(color).toHsl(); + hsl.h = (hsl.h + 180) % 360; + return tinycolor(hsl); + }; + + + // Combination Functions + // --------------------- + // Thanks to jQuery xColor for some of the ideas behind these + // + + tinycolor.triad = function(color) { + var hsl = tinycolor(color).toHsl(); + var h = hsl.h; + return [ + tinycolor(color), + tinycolor({ h: (h + 120) % 360, s: hsl.s, l: hsl.l }), + tinycolor({ h: (h + 240) % 360, s: hsl.s, l: hsl.l }) + ]; + }; + tinycolor.tetrad = function(color) { + var hsl = tinycolor(color).toHsl(); + var h = hsl.h; + return [ + tinycolor(color), + tinycolor({ h: (h + 90) % 360, s: hsl.s, l: hsl.l }), + tinycolor({ h: (h + 180) % 360, s: hsl.s, l: hsl.l }), + tinycolor({ h: (h + 270) % 360, s: hsl.s, l: hsl.l }) + ]; + }; + tinycolor.splitcomplement = function(color) { + var hsl = tinycolor(color).toHsl(); + var h = hsl.h; + return [ + tinycolor(color), + tinycolor({ h: (h + 72) % 360, s: hsl.s, l: hsl.l}), + tinycolor({ h: (h + 216) % 360, s: hsl.s, l: hsl.l}) + ]; + }; + tinycolor.analogous = function(color, results, slices) { + results = results || 6; + slices = slices || 30; + + var hsl = tinycolor(color).toHsl(); + var part = 360 / slices; + var ret = [tinycolor(color)]; + + for (hsl.h = ((hsl.h - (part * results >> 1)) + 720) % 360; --results; ) { + hsl.h = (hsl.h + part) % 360; + ret.push(tinycolor(hsl)); + } + return ret; + }; + tinycolor.monochromatic = function(color, results) { + results = results || 6; + var hsv = tinycolor(color).toHsv(); + var h = hsv.h, s = hsv.s, v = hsv.v; + var ret = []; + var modification = 1 / results; + + while (results--) { + ret.push(tinycolor({ h: h, s: s, v: v})); + v = (v + modification) % 1; + } + + return ret; + }; + + + // Readability Functions + // --------------------- + // + + // `readability` + // Analyze the 2 colors and returns an object with the following properties: + // `brightness`: difference in brightness between the two colors + // `color`: difference in color/hue between the two colors + tinycolor.readability = function(color1, color2) { + var a = tinycolor(color1).toRgb(); + var b = tinycolor(color2).toRgb(); + var brightnessA = (a.r * 299 + a.g * 587 + a.b * 114) / 1000; + var brightnessB = (b.r * 299 + b.g * 587 + b.b * 114) / 1000; + var colorDiff = ( + Math.max(a.r, b.r) - Math.min(a.r, b.r) + + Math.max(a.g, b.g) - Math.min(a.g, b.g) + + Math.max(a.b, b.b) - Math.min(a.b, b.b) + ); + + return { + brightness: Math.abs(brightnessA - brightnessB), + color: colorDiff + }; + }; + + // `readable` + // http://www.w3.org/TR/AERT#color-contrast + // Ensure that foreground and background color combinations provide sufficient contrast. + // *Example* + // tinycolor.readable("#000", "#111") => false + tinycolor.readable = function(color1, color2) { + var readability = tinycolor.readability(color1, color2); + return readability.brightness > 125 && readability.color > 500; + }; + + // `mostReadable` + // Given a base color and a list of possible foreground or background + // colors for that base, returns the most readable color. + // *Example* + // tinycolor.mostReadable("#123", ["#fff", "#000"]) => "#000" + tinycolor.mostReadable = function(baseColor, colorList) { + var bestColor = null; + var bestScore = 0; + var bestIsReadable = false; + for (var i=0; i < colorList.length; i++) { + + // We normalize both around the "acceptable" breaking point, + // but rank brightness constrast higher than hue. + + var readability = tinycolor.readability(baseColor, colorList[i]); + var readable = readability.brightness > 125 && readability.color > 500; + var score = 3 * (readability.brightness / 125) + (readability.color / 500); + + if ((readable && ! bestIsReadable) || + (readable && bestIsReadable && score > bestScore) || + ((! readable) && (! bestIsReadable) && score > bestScore)) { + bestIsReadable = readable; + bestScore = score; + bestColor = tinycolor(colorList[i]); + } + } + return bestColor; + }; + + + // Big List of Colors + // ------------------ + // + var names = tinycolor.names = { + aliceblue: "f0f8ff", + antiquewhite: "faebd7", + aqua: "0ff", + aquamarine: "7fffd4", + azure: "f0ffff", + beige: "f5f5dc", + bisque: "ffe4c4", + black: "000", + blanchedalmond: "ffebcd", + blue: "00f", + blueviolet: "8a2be2", + brown: "a52a2a", + burlywood: "deb887", + burntsienna: "ea7e5d", + cadetblue: "5f9ea0", + chartreuse: "7fff00", + chocolate: "d2691e", + coral: "ff7f50", + cornflowerblue: "6495ed", + cornsilk: "fff8dc", + crimson: "dc143c", + cyan: "0ff", + darkblue: "00008b", + darkcyan: "008b8b", + darkgoldenrod: "b8860b", + darkgray: "a9a9a9", + darkgreen: "006400", + darkgrey: "a9a9a9", + darkkhaki: "bdb76b", + darkmagenta: "8b008b", + darkolivegreen: "556b2f", + darkorange: "ff8c00", + darkorchid: "9932cc", + darkred: "8b0000", + darksalmon: "e9967a", + darkseagreen: "8fbc8f", + darkslateblue: "483d8b", + darkslategray: "2f4f4f", + darkslategrey: "2f4f4f", + darkturquoise: "00ced1", + darkviolet: "9400d3", + deeppink: "ff1493", + deepskyblue: "00bfff", + dimgray: "696969", + dimgrey: "696969", + dodgerblue: "1e90ff", + firebrick: "b22222", + floralwhite: "fffaf0", + forestgreen: "228b22", + fuchsia: "f0f", + gainsboro: "dcdcdc", + ghostwhite: "f8f8ff", + gold: "ffd700", + goldenrod: "daa520", + gray: "808080", + green: "008000", + greenyellow: "adff2f", + grey: "808080", + honeydew: "f0fff0", + hotpink: "ff69b4", + indianred: "cd5c5c", + indigo: "4b0082", + ivory: "fffff0", + khaki: "f0e68c", + lavender: "e6e6fa", + lavenderblush: "fff0f5", + lawngreen: "7cfc00", + lemonchiffon: "fffacd", + lightblue: "add8e6", + lightcoral: "f08080", + lightcyan: "e0ffff", + lightgoldenrodyellow: "fafad2", + lightgray: "d3d3d3", + lightgreen: "90ee90", + lightgrey: "d3d3d3", + lightpink: "ffb6c1", + lightsalmon: "ffa07a", + lightseagreen: "20b2aa", + lightskyblue: "87cefa", + lightslategray: "789", + lightslategrey: "789", + lightsteelblue: "b0c4de", + lightyellow: "ffffe0", + lime: "0f0", + limegreen: "32cd32", + linen: "faf0e6", + magenta: "f0f", + maroon: "800000", + mediumaquamarine: "66cdaa", + mediumblue: "0000cd", + mediumorchid: "ba55d3", + mediumpurple: "9370db", + mediumseagreen: "3cb371", + mediumslateblue: "7b68ee", + mediumspringgreen: "00fa9a", + mediumturquoise: "48d1cc", + mediumvioletred: "c71585", + midnightblue: "191970", + mintcream: "f5fffa", + mistyrose: "ffe4e1", + moccasin: "ffe4b5", + navajowhite: "ffdead", + navy: "000080", + oldlace: "fdf5e6", + olive: "808000", + olivedrab: "6b8e23", + orange: "ffa500", + orangered: "ff4500", + orchid: "da70d6", + palegoldenrod: "eee8aa", + palegreen: "98fb98", + paleturquoise: "afeeee", + palevioletred: "db7093", + papayawhip: "ffefd5", + peachpuff: "ffdab9", + peru: "cd853f", + pink: "ffc0cb", + plum: "dda0dd", + powderblue: "b0e0e6", + purple: "800080", + red: "f00", + rosybrown: "bc8f8f", + royalblue: "4169e1", + saddlebrown: "8b4513", + salmon: "fa8072", + sandybrown: "f4a460", + seagreen: "2e8b57", + seashell: "fff5ee", + sienna: "a0522d", + silver: "c0c0c0", + skyblue: "87ceeb", + slateblue: "6a5acd", + slategray: "708090", + slategrey: "708090", + snow: "fffafa", + springgreen: "00ff7f", + steelblue: "4682b4", + tan: "d2b48c", + teal: "008080", + thistle: "d8bfd8", + tomato: "ff6347", + turquoise: "40e0d0", + violet: "ee82ee", + wheat: "f5deb3", + white: "fff", + whitesmoke: "f5f5f5", + yellow: "ff0", + yellowgreen: "9acd32" + }; + + // Make it easy to access colors via `hexNames[hex]` + var hexNames = tinycolor.hexNames = flip(names); + + + // Utilities + // --------- + + // `{ 'name1': 'val1' }` becomes `{ 'val1': 'name1' }` + function flip(o) { + var flipped = { }; + for (var i in o) { + if (o.hasOwnProperty(i)) { + flipped[o[i]] = i; + } + } + return flipped; + } + + // Return a valid alpha value [0,1] with all invalid values being set to 1 + function boundAlpha(a) { + a = parseFloat(a); + + if (isNaN(a) || a < 0 || a > 1) { + a = 1; + } + + return a; + } + + // Take input from [0, n] and return it as [0, 1] + function bound01(n, max) { + if (isOnePointZero(n)) { n = "100%"; } + + var processPercent = isPercentage(n); + n = mathMin(max, mathMax(0, parseFloat(n))); + + // Automatically convert percentage into number + if (processPercent) { + n = parseInt(n * max, 10) / 100; + } + + // Handle floating point rounding errors + if ((math.abs(n - max) < 0.000001)) { + return 1; + } + + // Convert into [0, 1] range if it isn't already + return (n % max) / parseFloat(max); + } + + // Force a number between 0 and 1 + function clamp01(val) { + return mathMin(1, mathMax(0, val)); + } + + // Parse an integer into hex + function parseHex(val) { + return parseInt(val, 16); + } + + // Need to handle 1.0 as 100%, since once it is a number, there is no difference between it and 1 + // + function isOnePointZero(n) { + return typeof n == "string" && n.indexOf('.') != -1 && parseFloat(n) === 1; + } + + // Check to see if string passed in is a percentage + function isPercentage(n) { + return typeof n === "string" && n.indexOf('%') != -1; + } + + // Force a hex value to have 2 characters + function pad2(c) { + return c.length == 1 ? '0' + c : '' + c; + } + + // Replace a decimal with it's percentage value + function convertToPercentage(n) { + if (n <= 1) { + n = (n * 100) + "%"; + } + + return n; + } + + var matchers = (function() { + + // + var CSS_INTEGER = "[-\\+]?\\d+%?"; + + // + var CSS_NUMBER = "[-\\+]?\\d*\\.\\d+%?"; + + // Allow positive/negative integer/number. Don't capture the either/or, just the entire outcome. + var CSS_UNIT = "(?:" + CSS_NUMBER + ")|(?:" + CSS_INTEGER + ")"; + + // Actual matching. + // Parentheses and commas are optional, but not required. + // Whitespace can take the place of commas or opening paren + var PERMISSIVE_MATCH3 = "[\\s|\\(]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")\\s*\\)?"; + var PERMISSIVE_MATCH4 = "[\\s|\\(]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")\\s*\\)?"; + + return { + rgb: new RegExp("rgb" + PERMISSIVE_MATCH3), + rgba: new RegExp("rgba" + PERMISSIVE_MATCH4), + hsl: new RegExp("hsl" + PERMISSIVE_MATCH3), + hsla: new RegExp("hsla" + PERMISSIVE_MATCH4), + hsv: new RegExp("hsv" + PERMISSIVE_MATCH3), + hex3: /^([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/, + hex6: /^([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/ + }; + })(); + + // `stringInputToObject` + // Permissive string parsing. Take in a number of formats, and output an object + // based on detected format. Returns `{ r, g, b }` or `{ h, s, l }` or `{ h, s, v}` + function stringInputToObject(color) { + + color = color.replace(trimLeft,'').replace(trimRight, '').toLowerCase(); + var named = false; + if (names[color]) { + color = names[color]; + named = true; + } + else if (color == 'transparent') { + return { r: 0, g: 0, b: 0, a: 0, format: "name" }; + } + + // Try to match string input using regular expressions. + // Keep most of the number bounding out of this function - don't worry about [0,1] or [0,100] or [0,360] + // Just return an object and let the conversion functions handle that. + // This way the result will be the same whether the tinycolor is initialized with string or object. + var match; + if ((match = matchers.rgb.exec(color))) { + return { r: match[1], g: match[2], b: match[3] }; + } + if ((match = matchers.rgba.exec(color))) { + return { r: match[1], g: match[2], b: match[3], a: match[4] }; + } + if ((match = matchers.hsl.exec(color))) { + return { h: match[1], s: match[2], l: match[3] }; + } + if ((match = matchers.hsla.exec(color))) { + return { h: match[1], s: match[2], l: match[3], a: match[4] }; + } + if ((match = matchers.hsv.exec(color))) { + return { h: match[1], s: match[2], v: match[3] }; + } + if ((match = matchers.hex6.exec(color))) { + return { + r: parseHex(match[1]), + g: parseHex(match[2]), + b: parseHex(match[3]), + format: named ? "name" : "hex" + }; + } + if ((match = matchers.hex3.exec(color))) { + return { + r: parseHex(match[1] + '' + match[1]), + g: parseHex(match[2] + '' + match[2]), + b: parseHex(match[3] + '' + match[3]), + format: named ? "name" : "hex" + }; + } + + return false; + } + + // Expose tinycolor to window, does not need to run in non-browser context. + window.tinycolor = tinycolor; + + })(); + var tinycolor = window.tinycolor; + + $(function () { + if ($.fn.spectrum.load) { + $.fn.spectrum.processNativeColorInputs(); + } + }); + +})(window, jQuery); diff --git a/js/rendering/PiskelRenderer.js b/js/rendering/PiskelRenderer.js index 33a0f2f4..72872a63 100644 --- a/js/rendering/PiskelRenderer.js +++ b/js/rendering/PiskelRenderer.js @@ -5,7 +5,7 @@ ns.PiskelRenderer = function (piskelController) { var frames = []; for (var i = 0 ; i < piskelController.getFrameCount() ; i++) { - frames.push(this.piskelController.getFrameAt(i)); + frames.push(piskelController.getFrameAt(i)); } ns.FramesheetRenderer.call(this, frames); }; diff --git a/js/rendering/frame/FrameRenderer.js b/js/rendering/frame/FrameRenderer.js index 09476e59..7fe53e46 100644 --- a/js/rendering/frame/FrameRenderer.js +++ b/js/rendering/frame/FrameRenderer.js @@ -77,17 +77,18 @@ }; ns.FrameRenderer.prototype.setZoom = function (zoom) { - // back up center coordinates - var centerX = this.offset.x + (this.displayWidth/(2*this.zoom)); - var centerY = this.offset.y + (this.displayHeight/(2*this.zoom)); + if (zoom > Constants.MINIMUM_ZOOM) { + // back up center coordinates + var centerX = this.offset.x + (this.displayWidth/(2*this.zoom)); + var centerY = this.offset.y + (this.displayHeight/(2*this.zoom)); - this.zoom = Math.max(1, zoom); - - // recenter - this.setOffset( - centerX - (this.displayWidth/(2*this.zoom)), - centerY - (this.displayHeight/(2*this.zoom)) - ); + this.zoom = zoom; + // recenter + this.setOffset( + centerX - (this.displayWidth/(2*this.zoom)), + centerY - (this.displayHeight/(2*this.zoom)) + ); + } }; ns.FrameRenderer.prototype.getZoom = function () { diff --git a/piskel-boot.js b/piskel-boot.js index 1c8da006..204eb7a7 100644 --- a/piskel-boot.js +++ b/piskel-boot.js @@ -31,7 +31,7 @@ script = "build/piskel-packaged-min.js"; } var loaderInterval = window.setInterval(function () { - if (document.querySelectorAll("._ctl").length === 0) { + if (document.querySelectorAll("[data-iframe-loader]").length === 0) { window.clearInterval(loaderInterval); loadScript(script, "pskl.app.init()"); } else { diff --git a/piskel-script-list.js b/piskel-script-list.js index 7ccd1abf..b4e54c12 100644 --- a/piskel-script-list.js +++ b/piskel-script-list.js @@ -6,11 +6,14 @@ exports.scripts = [ // GIF Encoding libraries "js/lib/gif/gif.worker.js", "js/lib/gif/gif.js", + // Spectrum color-picker library + "js/lib/spectrum/spectrum.js", // Application wide configuration "js/Constants.js", "js/Events.js", + // Libraries "js/utils/core.js", "js/utils/UserAgent.js", @@ -27,7 +30,6 @@ exports.scripts = [ "js/utils/serialization/Deserializer.js", "js/utils/serialization/backward/Deserializer_v0.js", "js/utils/serialization/backward/Deserializer_v1.js", - "js/lib/jsColor_1_4_0/jscolor.js", // Application libraries--> "js/rendering/DrawingLoop.js", @@ -93,7 +95,6 @@ exports.scripts = [ "js/drawingtools/selectiontools/RectangleSelect.js", "js/drawingtools/selectiontools/ShapeSelect.js", "js/drawingtools/ColorPicker.js", - // Application controller and initialization "js/app.js" ]; \ No newline at end of file diff --git a/templates/drawing-tools.html b/templates/drawing-tools.html index b1050445..6f93ab45 100644 --- a/templates/drawing-tools.html +++ b/templates/drawing-tools.html @@ -4,13 +4,10 @@
    - - +
    -
    -
    - -
    +
    +