From 6c347ff82668f6369adf16d174bd4016ed5646bd Mon Sep 17 00:00:00 2001 From: jendib Date: Thu, 8 Aug 2013 01:38:30 +0200 Subject: [PATCH] Tag colors (client) --- .../resources/db/update/dbupdate-002-0.sql | 4 +- docs-parent/TODO | 1 - .../com/sismics/rest/util/ValidationUtil.java | 15 +- .../docs/rest/resource/TagResource.java | 4 +- docs-web/src/main/webapp/img/alpha.png | Bin 0 -> 1739 bytes docs-web/src/main/webapp/img/hue.png | Bin 0 -> 506 bytes docs-web/src/main/webapp/img/saturation.png | Bin 0 -> 8045 bytes docs-web/src/main/webapp/index.html | 3 + docs-web/src/main/webapp/js/app.js | 2 +- docs-web/src/main/webapp/js/controller/Tag.js | 11 +- .../main/webapp/lib/angular.colorpicker.js | 97 ++++ docs-web/src/main/webapp/lib/colorpicker.js | 543 ++++++++++++++++++ .../webapp/partial/directive.selecttag.html | 2 +- .../src/main/webapp/partial/document.html | 2 +- .../main/webapp/partial/document.view.html | 2 +- docs-web/src/main/webapp/partial/tag.html | 8 +- .../src/main/webapp/style/colorpicker.css | 112 ++++ docs-web/src/main/webapp/style/main.less | 7 + .../docs/rest/TestDocumentResource.java | 6 +- .../sismics/docs/rest/TestTagResource.java | 10 +- 20 files changed, 802 insertions(+), 27 deletions(-) create mode 100644 docs-web/src/main/webapp/img/alpha.png create mode 100644 docs-web/src/main/webapp/img/hue.png create mode 100644 docs-web/src/main/webapp/img/saturation.png create mode 100644 docs-web/src/main/webapp/lib/angular.colorpicker.js create mode 100644 docs-web/src/main/webapp/lib/colorpicker.js create mode 100644 docs-web/src/main/webapp/style/colorpicker.css diff --git a/docs-core/src/main/resources/db/update/dbupdate-002-0.sql b/docs-core/src/main/resources/db/update/dbupdate-002-0.sql index 79ff42c0..6dc22cba 100644 --- a/docs-core/src/main/resources/db/update/dbupdate-002-0.sql +++ b/docs-core/src/main/resources/db/update/dbupdate-002-0.sql @@ -1,3 +1,3 @@ -alter table T_TAG add column TAG_COLOR_C varchar(6) not null; -update T_TAG set TAG_COLOR_C = '3a87ad'; +alter table T_TAG add column TAG_COLOR_C varchar(7) not null; +update T_TAG set TAG_COLOR_C = '#3a87ad'; update T_CONFIG set CFG_VALUE_C='2' where CFG_ID_C='DB_VERSION'; diff --git a/docs-parent/TODO b/docs-parent/TODO index 38913018..8dbc8e35 100644 --- a/docs-parent/TODO +++ b/docs-parent/TODO @@ -1,2 +1 @@ - Users administration (client) -- Tag color (client) diff --git a/docs-web-common/src/main/java/com/sismics/rest/util/ValidationUtil.java b/docs-web-common/src/main/java/com/sismics/rest/util/ValidationUtil.java index e46416ba..fb7a6b10 100644 --- a/docs-web-common/src/main/java/com/sismics/rest/util/ValidationUtil.java +++ b/docs-web-common/src/main/java/com/sismics/rest/util/ValidationUtil.java @@ -63,7 +63,7 @@ public class ValidationUtil { throw new ClientException("ValidationError", MessageFormat.format("{0} must be more than {1} characters", name, lengthMin)); } if (lengthMax != null && s.length() > lengthMax) { - throw new ClientException("ValidationError", MessageFormat.format("{0} must be more than {1} characters", name, lengthMax)); + throw new ClientException("ValidationError", MessageFormat.format("{0} must be less than {1} characters", name, lengthMax)); } return s; } @@ -94,6 +94,19 @@ public class ValidationUtil { return validateLength(s, name, 1, null, false); } + /** + * Checks if the string is a hexadecimal color. + * + * @param s String to validate + * @param name Name of the parameter + * @param nullable True if the string can be empty or null + * @throws JSONException + */ + public static void validateHexColor(String s, String name, boolean nullable) throws JSONException { + // TODO Do a real check + ValidationUtil.validateLength(s, "name", 7, 7, nullable); + } + /** * Checks if the string is an email. * diff --git a/docs-web/src/main/java/com/sismics/docs/rest/resource/TagResource.java b/docs-web/src/main/java/com/sismics/docs/rest/resource/TagResource.java index 67593266..1270e618 100644 --- a/docs-web/src/main/java/com/sismics/docs/rest/resource/TagResource.java +++ b/docs-web/src/main/java/com/sismics/docs/rest/resource/TagResource.java @@ -110,7 +110,7 @@ public class TagResource extends BaseResource { // Validate input data name = ValidationUtil.validateLength(name, "name", 1, 36, false); - color = ValidationUtil.validateLength(color, "color", 6, 6, false); + ValidationUtil.validateHexColor(color, "color", true); // Get the tag TagDao tagDao = new TagDao(); @@ -151,7 +151,7 @@ public class TagResource extends BaseResource { // Validate input data name = ValidationUtil.validateLength(name, "name", 1, 36, true); - color = ValidationUtil.validateLength(color, "color", 6, 6, true); + ValidationUtil.validateHexColor(color, "color", true); // Get the tag TagDao tagDao = new TagDao(); diff --git a/docs-web/src/main/webapp/img/alpha.png b/docs-web/src/main/webapp/img/alpha.png new file mode 100644 index 0000000000000000000000000000000000000000..eaddb40b364056ecb78d73db5bfc7398c0940721 GIT binary patch literal 1739 zcmeAS@N?(olHy`uVBq!ia0vp^Aa)7|Gmz{M3S7#-z|0Wf6XFWwUIv5z|NjGNAbIcJ zy;rYZ0oflveuRiXq_DElq~L}C6+-}~0yH&n1uz{T8dD8W1X&Qmg@_=lhl|4%BdbA{ zB}N?LB#17EEof|HtKn>jE=(>&9AYn~?f7*;OoP~tRWC#e%~E7Rh%QXMa1mtZ!5sr< zplL@I2TEZDKs7*dG!nvvs7I568v+!ENMVXV#KH0?h9iq3%R}62HaVZ!OcD&8-W12`%ht@F|2xzB zGw0F0zZNahKKvz3GuNH{?%|(*>}9W&JxW%(@%z(hOB=&0CQi2x{3}=%U?#@t63+c( z#kJL3=Oo|mUBk0O;IeS+iCwQRJ~|!vbZgkT;7FzqJ)g|ezOa@3aXk@rpz`%C9i{h@ zcU6{3t4OQ=Sg~%)bD{NO8#nO^K4)!anJ}$+LPI|L4^8>|elN6cE9j}q7d^PC`tkwa zg9>2;`8TWI z8}Z+K+*6{t#L=t#j;7Yqxouv{L$su(e7mqL@%`EVm5(3)uKs+u{?#95+fEmMf4%A6 zPYMc)iwh?U`1r>9_}&)Yv)9{u?{R@&m%R>2-SW8Mb#CLvjpsUg)^x2YD(ma+K3rB+ z5!5ZYI+HbANs?*igrzf*!n#}T-y7A=JjVDrE`3dRi=^5$y`T5EHo>%5R5SMxL zTwF?O)-0~o3EJALU$%63JKbYtvj`0s;iY#ICg(w2E*Y&1OBv_5QX%Re)~O@^rVhblxWSs9EjqY0Flx)_xTn z?8Kerl;Nk}&%NWPOUdh%9HuKJ74O>Ky}Q?Np`xkMm8DfJ^R9Fo%xDv^ix-(DbZz3| z#gC)ia~ATlZ2i?Awf4`#5UoPrA1|MLxqXt8-E--$r9lf9DH$3TN++7%tyepgCgl;~ z^8Or$g!^F*L&H?VRA!FdC;lwnxzqC7MV&*5w_I*G2L`sb2HtS|%<($YD3R^1s*Z~H z=>-cEZv47^>sFs?OZ)HN?f0t{4~5+Fj__vZ2!FvQe(Uxkcb2tb)qdG?rlibaS-HhU z%T(!!#M37dkraQ(XZi{rMghdJdvraxG``t{@B-{o5AE{3&YLO3->7a)pIyWDRC>&BE_`wQJzaH+n_ndRjJ(r)$V$Rp7jh#4voR5g?AK*Ip37i9o#W$b_ zbb$`A9<+jmU?!Lh8bGb2%yO-PP2hVWVR(e55)%ql_{pQFlSmOI#9QDiDQ)16;5Hn@ zXK8!^rh*fM4yT0P;T3WH#09p?YXh(nV-jBh&xiD<=@rpN1aQcG;ybZMd?aQi3ipVh z9?yuU#6#kq&}vi>1;jNXi8x1S2xN+(BP{43-%8yI!@xRu127PFaoxecJ$W)dm&ur2 z`ap?TdVKf)G^f2Y_CWf~X3HOHixw9SY^&3Q#pB_K>;CkwYyGOT$K7YrYJcH1E7!(NgnaDDA|P(69UIK)uS>99>~ce4Qh|fGO8(oXoL3`UjO!=wbi> literal 0 HcmV?d00001 diff --git a/docs-web/src/main/webapp/img/saturation.png b/docs-web/src/main/webapp/img/saturation.png new file mode 100644 index 0000000000000000000000000000000000000000..82a4e3d117e93d524054394791c10b6c74c6e1fe GIT binary patch literal 8045 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 literal 0 HcmV?d00001 diff --git a/docs-web/src/main/webapp/index.html b/docs-web/src/main/webapp/index.html index 80fbc1bb..80036a4c 100644 --- a/docs-web/src/main/webapp/index.html +++ b/docs-web/src/main/webapp/index.html @@ -6,6 +6,7 @@ + + @@ -27,6 +29,7 @@ + diff --git a/docs-web/src/main/webapp/js/app.js b/docs-web/src/main/webapp/js/app.js index 4c322ef8..e8414734 100644 --- a/docs-web/src/main/webapp/js/app.js +++ b/docs-web/src/main/webapp/js/app.js @@ -3,7 +3,7 @@ /** * Trackino application. */ -var App = angular.module('docs', ['ui.state', 'ui.bootstrap', 'ui.route', 'ui.keypress', 'ui.validate', 'ui.sortable', 'restangular', 'ngSanitize']) +var App = angular.module('docs', ['ui.state', 'ui.bootstrap', 'ui.route', 'ui.keypress', 'ui.validate', 'ui.sortable', 'restangular', 'ngSanitize', 'colorpicker.module']) /** * Configuring modules. diff --git a/docs-web/src/main/webapp/js/controller/Tag.js b/docs-web/src/main/webapp/js/controller/Tag.js index 052b0801..7d9d88f4 100644 --- a/docs-web/src/main/webapp/js/controller/Tag.js +++ b/docs-web/src/main/webapp/js/controller/Tag.js @@ -4,6 +4,8 @@ * Tag controller. */ App.controller('Tag', function($scope, $dialog, $state, Tag, Restangular) { + $scope.tag = { name: '', color: '#3a87ad' }; + // Retrieve tags Tag.tags().then(function(data) { $scope.tags = data.tags; @@ -27,11 +29,10 @@ App.controller('Tag', function($scope, $dialog, $state, Tag, Restangular) { * Add a tag. */ $scope.addTag = function() { - var name = $scope.tag.name; - $scope.tag.name = ''; // TODO Check if the tag don't already exists - Restangular.one('tag').put({ name: name }).then(function(data) { - $scope.tags.push({ id: data.id, name: name }); + Restangular.one('tag').put($scope.tag).then(function(data) { + $scope.tags.push({ id: data.id, name: $scope.tag.name, color: $scope.tag.color }); + $scope.tag = { name: '', color: '#3a87ad' }; }); }; @@ -57,7 +58,7 @@ App.controller('Tag', function($scope, $dialog, $state, Tag, Restangular) { }; /** - * Update a tag name. + * Update a tag. */ $scope.updateTag = function(tag) { Restangular.one('tag', tag.id).post('', tag); diff --git a/docs-web/src/main/webapp/lib/angular.colorpicker.js b/docs-web/src/main/webapp/lib/angular.colorpicker.js new file mode 100644 index 00000000..d483cab9 --- /dev/null +++ b/docs-web/src/main/webapp/lib/angular.colorpicker.js @@ -0,0 +1,97 @@ +'use strict'; + +angular.module('colorpicker.module', []) + .factory('helper', function () { + return { + prepareValues: function(format) { + var thisFormat = 'hex'; + if (format) { + thisFormat = format; + } + return { + name: thisFormat, + transform: 'to' + (thisFormat === 'hex' ? thisFormat.charAt(0).toUpperCase() + thisFormat.slice(1) : thisFormat.length > 3 ? thisFormat.toUpperCase().slice(0, -1) : thisFormat.toUpperCase()) + }; + }, + updateView: function(element, value) { + if (!value) { + value = ''; + } + element.val(value); + element.data('color', value); + element.data('colorpicker').update(); + } + } + }) + .directive('colorpicker', ['helper', function(helper) { + return { + require: '?ngModel', + restrict: 'A', + link: function(scope, element, attrs, ngModel) { + + var thisFormat = helper.prepareValues(attrs.colorpicker); + + element.colorpicker({format: thisFormat.name}); + + element.on('$destroy', function() { + element.data('colorpicker').picker.remove(); + }); + + if(!ngModel) return; + + element.colorpicker().on('changeColor', function(event) { + element.val(element.data('colorpicker').format(event.color[thisFormat.transform]())); + scope.$apply(ngModel.$setViewValue(element.data('colorpicker').format(event.color[thisFormat.transform]()))); + }); + + element.colorpicker().on('hide', function(){ + scope.$apply(attrs.onHide); + }); + + element.colorpicker().on('show', function(){ + scope.$apply(attrs.onShow); + }); + + ngModel.$render = function() { + helper.updateView(element, ngModel.$viewValue) ; + } + } + }; + }]) + .directive('colorpicker', ['helper', function(helper) { + return { + require: '?ngModel', + restrict: 'E', + replace: true, + transclude: false, + scope: { + componentPicker: '=ngModel', + inputName: '@inputName', + inputClass: '@inputClass', + colorFormat: '@colorFormat' + }, + template: '
' + + '' + + '' + + '
', + + link: function(scope, element, attrs, ngModel) { + + var thisFormat = helper.prepareValues(attrs.colorFormat); + + element.colorpicker(); + if(!ngModel) return; + + var elementInput = angular.element(element.children()[0]); + + element.colorpicker().on('changeColor', function(event) { + elementInput.val(element.data('colorpicker').format(event.color[thisFormat.transform]())); + scope.$parent.$apply(ngModel.$setViewValue(element.data('colorpicker').format(event.color[thisFormat.transform]()))); + }); + + ngModel.$render = function() { + helper.updateView(element, ngModel.$viewValue) ; + } + } + }; + }]); diff --git a/docs-web/src/main/webapp/lib/colorpicker.js b/docs-web/src/main/webapp/lib/colorpicker.js new file mode 100644 index 00000000..1f512d59 --- /dev/null +++ b/docs-web/src/main/webapp/lib/colorpicker.js @@ -0,0 +1,543 @@ +/* ========================================================= + * bootstrap-colorpicker.js + * http://www.eyecon.ro/bootstrap-colorpicker + * ========================================================= + * Copyright 2012 Stefan Petre + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ========================================================= */ + +!function( $ ) { + + // Color object + + var Color = function(val) { + this.value = { + h: 1, + s: 1, + b: 1, + a: 1 + }; + this.setColor(val); + }; + + Color.prototype = { + constructor: Color, + + //parse a string to HSB + setColor: function(val){ + val = val.toLowerCase(); + var that = this; + $.each( CPGlobal.stringParsers, function( i, parser ) { + var match = parser.re.exec( val ), + values = match && parser.parse( match ), + space = parser.space||'rgba'; + if ( values ) { + if (space === 'hsla') { + that.value = CPGlobal.RGBtoHSB.apply(null, CPGlobal.HSLtoRGB.apply(null, values)); + } else { + that.value = CPGlobal.RGBtoHSB.apply(null, values); + } + return false; + } + }); + }, + + setHue: function(h) { + this.value.h = 1- h; + }, + + setSaturation: function(s) { + this.value.s = s; + }, + + setLightness: function(b) { + this.value.b = 1- b; + }, + + setAlpha: function(a) { + this.value.a = parseInt((1 - a)*100, 10)/100; + }, + + // HSBtoRGB from RaphaelJS + // https://github.com/DmitryBaranovskiy/raphael/ + toRGB: function(h, s, b, a) { + if (!h) { + h = this.value.h; + s = this.value.s; + b = this.value.b; + } + h *= 360; + var R, G, B, X, C; + h = (h % 360) / 60; + C = b * s; + X = C * (1 - Math.abs(h % 2 - 1)); + R = G = B = b - C; + + h = ~~h; + R += [C, X, 0, 0, X, C][h]; + G += [X, C, C, X, 0, 0][h]; + B += [0, 0, X, C, C, X][h]; + return { + r: Math.round(R*255), + g: Math.round(G*255), + b: Math.round(B*255), + a: a||this.value.a + }; + }, + + toHex: function(h, s, b, a){ + var rgb = this.toRGB(h, s, b, a); + return '#'+((1 << 24) | (parseInt(rgb.r) << 16) | (parseInt(rgb.g) << 8) | parseInt(rgb.b)).toString(16).substr(1); + }, + + toHSL: function(h, s, b, a){ + if (!h) { + h = this.value.h; + s = this.value.s; + b = this.value.b; + } + var H = h, + L = (2 - s) * b, + S = s * b; + if (L > 0 && L <= 1) { + S /= L; + } else { + S /= 2 - L; + } + L /= 2; + if (S > 1) { + S = 1; + } + return { + h: H, + s: S, + l: L, + a: a||this.value.a + }; + } + }; + + // Picker object + + var Colorpicker = function(element, options){ + this.element = $(element); + var format = options.format||this.element.data('color-format')||'hex'; + this.format = CPGlobal.translateFormats[format]; + this.isInput = this.element.is('input'); + this.component = this.element.is('.color') ? this.element.find('.add-on') : false; + + this.picker = $(CPGlobal.template) + .appendTo('body') + .on('mousedown', $.proxy(this.mousedown, this)); + + if (this.isInput) { + this.element.on({ + 'focus': $.proxy(this.show, this), + 'keyup': $.proxy(this.update, this) + }); + } else if (this.component){ + this.component.on({ + 'click': $.proxy(this.show, this) + }); + } else { + this.element.on({ + 'click': $.proxy(this.show, this) + }); + } + if (format === 'rgba' || format === 'hsla') { + this.picker.addClass('alpha'); + this.alpha = this.picker.find('.colorpicker-alpha')[0].style; + } + + if (this.component){ + this.picker.find('.colorpicker-color').hide(); + this.preview = this.element.find('i')[0].style; + } else { + this.preview = this.picker.find('div:last')[0].style; + } + + this.base = this.picker.find('div:first')[0].style; + this.update(); + }; + + Colorpicker.prototype = { + constructor: Colorpicker, + + show: function(e) { + this.update(); + this.picker.show(); + this.height = this.component ? this.component.outerHeight() : this.element.outerHeight(); + this.place(); + $(window).on('resize', $.proxy(this.place, this)); + if (!this.isInput) { + if (e) { + e.stopPropagation(); + e.preventDefault(); + } + } + $(document).on({ + 'mousedown': $.proxy(this.hide, this) + }); + + this.element.trigger({ + type: 'show', + color: this.color + }); + }, + + update: function(){ + this.color = new Color(this.isInput ? this.element.prop('value') : this.element.data('color')); + this.picker.find('i') + .eq(0).css({left: this.color.value.s*100, top: 100 - this.color.value.b*100}).end() + .eq(1).css('top', 100 * (1 - this.color.value.h)).end() + .eq(2).css('top', 100 * (1 - this.color.value.a)); + this.previewColor(); + }, + + setValue: function(newColor) { + this.color = new Color(newColor); + this.picker.find('i') + .eq(0).css({left: this.color.value.s*100, top: 100 - this.color.value.b*100}).end() + .eq(1).css('top', 100 * (1 - this.color.value.h)).end() + .eq(2).css('top', 100 * (1 - this.color.value.a)); + this.previewColor(); + this.element.trigger({ + type: 'changeColor', + color: this.color + }); + }, + + hide: function(){ + this.picker.hide(); + $(window).off('resize', this.place); + if (!this.isInput) { + $(document).off({ + 'mousedown': this.hide + }); + if (this.component){ + this.element.find('input').prop('value', this.format.call(this)); + } + this.element.data('color', this.format.call(this)); + } else { + this.element.prop('value', this.format.call(this)); + } + this.element.trigger({ + type: 'hide', + color: this.color + }); + }, + + place: function(){ + var offset = this.component ? this.component.offset() : this.element.offset(); + this.picker.css({ + top: offset.top + this.height, + left: offset.left + }); + }, + + //preview color change + previewColor: function(){ + try { + this.preview.backgroundColor = this.format.call(this); + } catch(e) { + this.preview.backgroundColor = this.color.toHex(); + } + //set the color for brightness/saturation slider + this.base.backgroundColor = this.color.toHex(this.color.value.h, 1, 1, 1); + //set te color for alpha slider + if (this.alpha) { + this.alpha.backgroundColor = this.color.toHex(); + } + }, + + pointer: null, + + slider: null, + + mousedown: function(e){ + e.stopPropagation(); + e.preventDefault(); + + var target = $(e.target); + + //detect the slider and set the limits and callbacks + var zone = target.closest('div'); + if (!zone.is('.colorpicker')) { + if (zone.is('.colorpicker-saturation')) { + this.slider = $.extend({}, CPGlobal.sliders.saturation); + } + else if (zone.is('.colorpicker-hue')) { + this.slider = $.extend({}, CPGlobal.sliders.hue); + } + else if (zone.is('.colorpicker-alpha')) { + this.slider = $.extend({}, CPGlobal.sliders.alpha); + } else { + return false; + } + var offset = zone.offset(); + //reference to knob's style + this.slider.knob = zone.find('i')[0].style; + this.slider.left = e.pageX - offset.left; + this.slider.top = e.pageY - offset.top; + this.pointer = { + left: e.pageX, + top: e.pageY + }; + //trigger mousemove to move the knob to the current position + $(document).on({ + mousemove: $.proxy(this.mousemove, this), + mouseup: $.proxy(this.mouseup, this) + }).trigger('mousemove'); + } + return false; + }, + + mousemove: function(e){ + e.stopPropagation(); + e.preventDefault(); + var left = Math.max( + 0, + Math.min( + this.slider.maxLeft, + this.slider.left + ((e.pageX||this.pointer.left) - this.pointer.left) + ) + ); + var top = Math.max( + 0, + Math.min( + this.slider.maxTop, + this.slider.top + ((e.pageY||this.pointer.top) - this.pointer.top) + ) + ); + this.slider.knob.left = left + 'px'; + this.slider.knob.top = top + 'px'; + if (this.slider.callLeft) { + this.color[this.slider.callLeft].call(this.color, left/100); + } + if (this.slider.callTop) { + this.color[this.slider.callTop].call(this.color, top/100); + } + this.previewColor(); + this.element.trigger({ + type: 'changeColor', + color: this.color + }); + return false; + }, + + mouseup: function(e){ + e.stopPropagation(); + e.preventDefault(); + $(document).off({ + mousemove: this.mousemove, + mouseup: this.mouseup + }); + return false; + } + } + + $.fn.colorpicker = function ( option, val ) { + return this.each(function () { + var $this = $(this), + data = $this.data('colorpicker'), + options = typeof option === 'object' && option; + if (!data) { + $this.data('colorpicker', (data = new Colorpicker(this, $.extend({}, $.fn.colorpicker.defaults,options)))); + } + + if (typeof option === 'string') data[option](val); + }); + }; + + $.fn.colorpicker.defaults = { + }; + + $.fn.colorpicker.Constructor = Colorpicker; + + var CPGlobal = { + + // translate a format from Color object to a string + translateFormats: { + 'rgb': function(){ + var rgb = this.color.toRGB(); + return 'rgb('+rgb.r+','+rgb.g+','+rgb.b+')'; + }, + + 'rgba': function(){ + var rgb = this.color.toRGB(); + return 'rgba('+rgb.r+','+rgb.g+','+rgb.b+','+rgb.a+')'; + }, + + 'hsl': function(){ + var hsl = this.color.toHSL(); + return 'hsl('+Math.round(hsl.h*360)+','+Math.round(hsl.s*100)+'%,'+Math.round(hsl.l*100)+'%)'; + }, + + 'hsla': function(){ + var hsl = this.color.toHSL(); + return 'hsla('+Math.round(hsl.h*360)+','+Math.round(hsl.s*100)+'%,'+Math.round(hsl.l*100)+'%,'+hsl.a+')'; + }, + + 'hex': function(){ + return this.color.toHex(); + } + }, + + sliders: { + saturation: { + maxLeft: 100, + maxTop: 100, + callLeft: 'setSaturation', + callTop: 'setLightness' + }, + + hue: { + maxLeft: 0, + maxTop: 100, + callLeft: false, + callTop: 'setHue' + }, + + alpha: { + maxLeft: 0, + maxTop: 100, + callLeft: false, + callTop: 'setAlpha' + } + }, + + // HSBtoRGB from RaphaelJS + // https://github.com/DmitryBaranovskiy/raphael/ + RGBtoHSB: function (r, g, b, a){ + r /= 255; + g /= 255; + b /= 255; + + var H, S, V, C; + V = Math.max(r, g, b); + C = V - Math.min(r, g, b); + H = (C === 0 ? null : + V == r ? (g - b) / C : + V == g ? (b - r) / C + 2 : + (r - g) / C + 4 + ); + H = ((H + 360) % 6) * 60 / 360; + S = C === 0 ? 0 : C / V; + return {h: H||1, s: S, b: V, a: a||1}; + }, + + HueToRGB: function (p, q, h) { + if (h < 0) + h += 1; + else if (h > 1) + h -= 1; + + if ((h * 6) < 1) + return p + (q - p) * h * 6; + else if ((h * 2) < 1) + return q; + else if ((h * 3) < 2) + return p + (q - p) * ((2 / 3) - h) * 6; + else + return p; + }, + + HSLtoRGB: function (h, s, l, a) + { + if (s < 0) { + s = 0; + } + var q; + if (l <= 0.5) { + q = l * (1 + s); + } else { + q = l + s - (l * s); + } + + var p = 2 * l - q; + + var tr = h + (1 / 3); + var tg = h; + var tb = h - (1 / 3); + + var r = Math.round(CPGlobal.HueToRGB(p, q, tr) * 255); + var g = Math.round(CPGlobal.HueToRGB(p, q, tg) * 255); + var b = Math.round(CPGlobal.HueToRGB(p, q, tb) * 255); + return [r, g, b, a||1]; + }, + + // a set of RE's that can match strings and generate color tuples. + // from John Resig color plugin + // https://github.com/jquery/jquery-color/ + stringParsers: [ + { + re: /rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*(\d+(?:\.\d+)?)\s*)?\)/, + parse: function( execResult ) { + return [ + execResult[ 1 ], + execResult[ 2 ], + execResult[ 3 ], + execResult[ 4 ] + ]; + } + }, { + re: /rgba?\(\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d+(?:\.\d+)?)\s*)?\)/, + parse: function( execResult ) { + return [ + 2.55 * execResult[1], + 2.55 * execResult[2], + 2.55 * execResult[3], + execResult[ 4 ] + ]; + } + }, { + re: /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/, + parse: function( execResult ) { + return [ + parseInt( execResult[ 1 ], 16 ), + parseInt( execResult[ 2 ], 16 ), + parseInt( execResult[ 3 ], 16 ) + ]; + } + }, { + re: /#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/, + parse: function( execResult ) { + return [ + parseInt( execResult[ 1 ] + execResult[ 1 ], 16 ), + parseInt( execResult[ 2 ] + execResult[ 2 ], 16 ), + parseInt( execResult[ 3 ] + execResult[ 3 ], 16 ) + ]; + } + }, { + re: /hsla?\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d+(?:\.\d+)?)\s*)?\)/, + space: 'hsla', + parse: function( execResult ) { + return [ + execResult[1]/360, + execResult[2] / 100, + execResult[3] / 100, + execResult[4] + ]; + } + } + ], + template: '