File indexing completed on 2024-05-19 06:00:15

0001 'use strict';
0002 
0003 angular.module('colorpicker.module', [])
0004     .factory('Helper', function () {
0005       return {
0006         closestSlider: function (elem) {
0007           var matchesSelector = elem.matches || elem.webkitMatchesSelector || elem.mozMatchesSelector || elem.msMatchesSelector;
0008           if (matchesSelector.bind(elem)('I')) {
0009             return elem.parentNode;
0010           }
0011           return elem;
0012         },
0013         getOffset: function (elem, fixedPosition) {
0014           var
0015               x = 0,
0016               y = 0,
0017               scrollX = 0,
0018               scrollY = 0;
0019           while (elem && !isNaN(elem.offsetLeft) && !isNaN(elem.offsetTop)) {
0020             x += elem.offsetLeft;
0021             y += elem.offsetTop;
0022             if (!fixedPosition && elem.tagName === 'BODY') {
0023               scrollX += document.documentElement.scrollLeft || elem.scrollLeft;
0024               scrollY += document.documentElement.scrollTop || elem.scrollTop;
0025             } else {
0026               scrollX += elem.scrollLeft;
0027               scrollY += elem.scrollTop;
0028             }
0029             elem = elem.offsetParent;
0030           }
0031           return {
0032             top: y,
0033             left: x,
0034             scrollX: scrollX,
0035             scrollY: scrollY
0036           };
0037         },
0038         // a set of RE's that can match strings and generate color tuples. https://github.com/jquery/jquery-color/
0039         stringParsers: [
0040           {
0041             re: /rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*(\d+(?:\.\d+)?)\s*)?\)/,
0042             parse: function (execResult) {
0043               return [
0044                 execResult[1],
0045                 execResult[2],
0046                 execResult[3],
0047                 execResult[4]
0048               ];
0049             }
0050           },
0051           {
0052             re: /rgba?\(\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d+(?:\.\d+)?)\s*)?\)/,
0053             parse: function (execResult) {
0054               return [
0055                 2.55 * execResult[1],
0056                 2.55 * execResult[2],
0057                 2.55 * execResult[3],
0058                 execResult[4]
0059               ];
0060             }
0061           },
0062           {
0063             re: /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/,
0064             parse: function (execResult) {
0065               return [
0066                 parseInt(execResult[1], 16),
0067                 parseInt(execResult[2], 16),
0068                 parseInt(execResult[3], 16)
0069               ];
0070             }
0071           },
0072           {
0073             re: /#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/,
0074             parse: function (execResult) {
0075               return [
0076                 parseInt(execResult[1] + execResult[1], 16),
0077                 parseInt(execResult[2] + execResult[2], 16),
0078                 parseInt(execResult[3] + execResult[3], 16)
0079               ];
0080             }
0081           }
0082         ]
0083       };
0084     })
0085     .factory('Color', ['Helper', function (Helper) {
0086       return {
0087         value: {
0088           h: 1,
0089           s: 1,
0090           b: 1,
0091           a: 1
0092         },
0093         // translate a format from Color object to a string
0094         'rgb': function () {
0095           var rgb = this.toRGB();
0096           return 'rgb(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ')';
0097         },
0098         'rgba': function () {
0099           var rgb = this.toRGB();
0100           return 'rgba(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',' + rgb.a + ')';
0101         },
0102         'hex': function () {
0103           return  this.toHex();
0104         },
0105 
0106         // HSBtoRGB from RaphaelJS
0107         RGBtoHSB: function (r, g, b, a) {
0108           r /= 255;
0109           g /= 255;
0110           b /= 255;
0111 
0112           var H, S, V, C;
0113           V = Math.max(r, g, b);
0114           C = V - Math.min(r, g, b);
0115           H = (C === 0 ? null :
0116               V === r ? (g - b) / C :
0117                   V === g ? (b - r) / C + 2 :
0118                       (r - g) / C + 4
0119               );
0120           H = ((H + 360) % 6) * 60 / 360;
0121           S = C === 0 ? 0 : C / V;
0122           return {h: H || 1, s: S, b: V, a: a || 1};
0123         },
0124 
0125         //parse a string to HSB
0126         setColor: function (val) {
0127           val = val.toLowerCase();
0128           for (var key in Helper.stringParsers) {
0129             if (Helper.stringParsers.hasOwnProperty(key)) {
0130               var parser = Helper.stringParsers[key];
0131               var match = parser.re.exec(val),
0132                   values = match && parser.parse(match);
0133               if (values) {
0134                 this.value = this.RGBtoHSB.apply(null, values);
0135                 return false;
0136               }
0137             }
0138           }
0139         },
0140 
0141         setHue: function (h) {
0142           this.value.h = 1 - h;
0143         },
0144 
0145         setSaturation: function (s) {
0146           this.value.s = s;
0147         },
0148 
0149         setLightness: function (b) {
0150           this.value.b = 1 - b;
0151         },
0152 
0153         setAlpha: function (a) {
0154           this.value.a = parseInt((1 - a) * 100, 10) / 100;
0155         },
0156 
0157         // HSBtoRGB from RaphaelJS
0158         // https://github.com/DmitryBaranovskiy/raphael/
0159         toRGB: function (h, s, b, a) {
0160           if (!h) {
0161             h = this.value.h;
0162             s = this.value.s;
0163             b = this.value.b;
0164           }
0165           h *= 360;
0166           var R, G, B, X, C;
0167           h = (h % 360) / 60;
0168           C = b * s;
0169           X = C * (1 - Math.abs(h % 2 - 1));
0170           R = G = B = b - C;
0171 
0172           h = ~~h;
0173           R += [C, X, 0, 0, X, C][h];
0174           G += [X, C, C, X, 0, 0][h];
0175           B += [0, 0, X, C, C, X][h];
0176           return {
0177             r: Math.round(R * 255),
0178             g: Math.round(G * 255),
0179             b: Math.round(B * 255),
0180             a: a || this.value.a
0181           };
0182         },
0183 
0184         toHex: function (h, s, b, a) {
0185           var rgb = this.toRGB(h, s, b, a);
0186           return '#' + ((1 << 24) | (parseInt(rgb.r, 10) << 16) | (parseInt(rgb.g, 10) << 8) | parseInt(rgb.b, 10)).toString(16).substr(1);
0187         }
0188       };
0189     }])
0190     .factory('Slider', ['Helper', function (Helper) {
0191       var
0192           slider = {
0193             maxLeft: 0,
0194             maxTop: 0,
0195             callLeft: null,
0196             callTop: null,
0197             knob: {
0198               top: 0,
0199               left: 0
0200             }
0201           },
0202           pointer = {};
0203 
0204       return {
0205         getSlider: function() {
0206           return slider;
0207         },
0208         getLeftPosition: function(event) {
0209           return Math.max(0, Math.min(slider.maxLeft, slider.left + ((event.pageX || pointer.left) - pointer.left)));
0210         },
0211         getTopPosition: function(event) {
0212           return Math.max(0, Math.min(slider.maxTop, slider.top + ((event.pageY || pointer.top) - pointer.top)));
0213         },
0214         setSlider: function (event, fixedPosition) {
0215           var
0216             target = Helper.closestSlider(event.target),
0217             targetOffset = Helper.getOffset(target, fixedPosition);
0218           slider.knob = target.children[0].style;
0219           slider.left = event.pageX - targetOffset.left - window.pageXOffset + targetOffset.scrollX;
0220           slider.top = event.pageY - targetOffset.top - window.pageYOffset + targetOffset.scrollY;
0221 
0222           pointer = {
0223             left: event.pageX,
0224             top: event.pageY
0225           };
0226         },
0227         setSaturation: function(event, fixedPosition) {
0228           slider = {
0229             maxLeft: 100,
0230             maxTop: 100,
0231             callLeft: 'setSaturation',
0232             callTop: 'setLightness'
0233           };
0234           this.setSlider(event, fixedPosition);
0235         },
0236         setHue: function(event, fixedPosition) {
0237           slider = {
0238             maxLeft: 0,
0239             maxTop: 100,
0240             callLeft: false,
0241             callTop: 'setHue'
0242           };
0243           this.setSlider(event, fixedPosition);
0244         },
0245         setAlpha: function(event, fixedPosition) {
0246           slider = {
0247             maxLeft: 0,
0248             maxTop: 100,
0249             callLeft: false,
0250             callTop: 'setAlpha'
0251           };
0252           this.setSlider(event, fixedPosition);
0253         },
0254         setKnob: function(top, left) {
0255           slider.knob.top = top + 'px';
0256           slider.knob.left = left + 'px';
0257         }
0258       };
0259     }])
0260     .directive('colorpicker', ['$document', '$compile', 'Color', 'Slider', 'Helper', function ($document, $compile, Color, Slider, Helper) {
0261       return {
0262         require: '?ngModel',
0263         restrict: 'A',
0264         link: function ($scope, elem, attrs, ngModel) {
0265           var
0266               thisFormat = attrs.colorpicker ? attrs.colorpicker : 'hex',
0267               position = angular.isDefined(attrs.colorpickerPosition) ? attrs.colorpickerPosition : 'bottom',
0268               fixedPosition = angular.isDefined(attrs.colorpickerFixedPosition) ? attrs.colorpickerFixedPosition : false,
0269               target = angular.isDefined(attrs.colorpickerParent) ? elem.parent() : angular.element(document.body),
0270               withInput = angular.isDefined(attrs.colorpickerWithInput) ? attrs.colorpickerWithInput : false,
0271               inputTemplate = withInput ? '<input type="text" name="colorpicker-input">' : '',
0272               template =
0273                   '<div class="colorpicker dropdown">' +
0274                       '<div class="dropdown-menu">' +
0275                       '<colorpicker-saturation><i></i></colorpicker-saturation>' +
0276                       '<colorpicker-hue><i></i></colorpicker-hue>' +
0277                       '<colorpicker-alpha><i></i></colorpicker-alpha>' +
0278                       '<colorpicker-preview></colorpicker-preview>' +
0279                       inputTemplate +
0280                       '<button class="close close-colorpicker">&times;</button>' +
0281                       '</div>' +
0282                       '</div>',
0283               colorpickerTemplate = angular.element(template),
0284               pickerColor = Color,
0285               sliderAlpha,
0286               sliderHue = colorpickerTemplate.find('colorpicker-hue'),
0287               sliderSaturation = colorpickerTemplate.find('colorpicker-saturation'),
0288               colorpickerPreview = colorpickerTemplate.find('colorpicker-preview'),
0289               pickerColorPointers = colorpickerTemplate.find('i');
0290 
0291           $compile(colorpickerTemplate)($scope);
0292 
0293           if (withInput) {
0294             var pickerColorInput = colorpickerTemplate.find('input');
0295             pickerColorInput
0296                 .on('mousedown', function(event) {
0297                   event.stopPropagation();
0298                 })
0299                 .on('keyup', function(event) {
0300                   var newColor = this.value;
0301                   elem.val(newColor);
0302                   if(ngModel) {
0303                     $scope.$apply(ngModel.$setViewValue(newColor));
0304                   }
0305                   event.stopPropagation();
0306                   event.preventDefault();
0307                 });
0308             elem.on('keyup', function() {
0309               pickerColorInput.val(elem.val());
0310             });
0311           }
0312 
0313           var bindMouseEvents = function() {
0314             $document.on('mousemove', mousemove);
0315             $document.on('mouseup', mouseup);
0316           };
0317 
0318           if (thisFormat === 'rgba') {
0319             colorpickerTemplate.addClass('alpha');
0320             sliderAlpha = colorpickerTemplate.find('colorpicker-alpha');
0321             sliderAlpha
0322                 .on('click', function(event) {
0323                   Slider.setAlpha(event, fixedPosition);
0324                   mousemove(event);
0325                 })
0326                 .on('mousedown', function(event) {
0327                   Slider.setAlpha(event, fixedPosition);
0328                   bindMouseEvents();
0329                 });
0330           }
0331 
0332           sliderHue
0333               .on('click', function(event) {
0334                 Slider.setHue(event, fixedPosition);
0335                 mousemove(event);
0336               })
0337               .on('mousedown', function(event) {
0338                 Slider.setHue(event, fixedPosition);
0339                 bindMouseEvents();
0340               });
0341 
0342           sliderSaturation
0343               .on('click', function(event) {
0344                 Slider.setSaturation(event, fixedPosition);
0345                 mousemove(event);
0346               })
0347               .on('mousedown', function(event) {
0348                 Slider.setSaturation(event, fixedPosition);
0349                 bindMouseEvents();
0350               });
0351 
0352           if (fixedPosition) {
0353             colorpickerTemplate.addClass('colorpicker-fixed-position');
0354           }
0355 
0356           colorpickerTemplate.addClass('colorpicker-position-' + position);
0357 
0358           target.append(colorpickerTemplate);
0359 
0360           if(ngModel) {
0361             ngModel.$render = function () {
0362               elem.val(ngModel.$viewValue);
0363             };
0364             $scope.$watch(attrs.ngModel, function() {
0365               update();
0366             });
0367           }
0368 
0369           elem.on('$destroy', function() {
0370             colorpickerTemplate.remove();
0371           });
0372 
0373           var previewColor = function () {
0374             try {
0375               colorpickerPreview.css('backgroundColor', pickerColor[thisFormat]());
0376             } catch (e) {
0377               colorpickerPreview.css('backgroundColor', pickerColor.toHex());
0378             }
0379             sliderSaturation.css('backgroundColor', pickerColor.toHex(pickerColor.value.h, 1, 1, 1));
0380             if (thisFormat === 'rgba') {
0381               sliderAlpha.css.backgroundColor = pickerColor.toHex();
0382             }
0383           };
0384 
0385           var mousemove = function (event) {
0386             var
0387                 left = Slider.getLeftPosition(event),
0388                 top = Slider.getTopPosition(event),
0389                 slider = Slider.getSlider();
0390 
0391             Slider.setKnob(top, left);
0392 
0393             if (slider.callLeft) {
0394               pickerColor[slider.callLeft].call(pickerColor, left / 100);
0395             }
0396             if (slider.callTop) {
0397               pickerColor[slider.callTop].call(pickerColor, top / 100);
0398             }
0399             previewColor();
0400             var newColor = pickerColor[thisFormat]();
0401             elem.val(newColor);
0402             if(ngModel) {
0403               $scope.$apply(ngModel.$setViewValue(newColor));
0404             }
0405             if (withInput) {
0406               pickerColorInput.val(newColor);
0407             }
0408             return false;
0409           };
0410 
0411           var mouseup = function () {
0412             $document.off('mousemove', mousemove);
0413             $document.off('mouseup', mouseup);
0414           };
0415 
0416           var update = function () {
0417             pickerColor.setColor(elem.val());
0418             pickerColorPointers.eq(0).css({
0419               left: pickerColor.value.s * 100 + 'px',
0420               top: 100 - pickerColor.value.b * 100 + 'px'
0421             });
0422             pickerColorPointers.eq(1).css('top', 100 * (1 - pickerColor.value.h) + 'px');
0423             pickerColorPointers.eq(2).css('top', 100 * (1 - pickerColor.value.a) + 'px');
0424             previewColor();
0425           };
0426 
0427           var getColorpickerTemplatePosition = function() {
0428             var
0429                 positionValue,
0430                 positionOffset = Helper.getOffset(elem[0]);
0431 
0432             if(angular.isDefined(attrs.colorpickerParent)) {
0433               positionOffset.left = 0;
0434               positionOffset.top = 0;
0435             }
0436 
0437             if (position === 'top') {
0438               positionValue =  {
0439                 'top': positionOffset.top - 147,
0440                 'left': positionOffset.left
0441               };
0442             } else if (position === 'right') {
0443               positionValue = {
0444                 'top': positionOffset.top,
0445                 'left': positionOffset.left + 126
0446               };
0447             } else if (position === 'bottom') {
0448               positionValue = {
0449                 'top': positionOffset.top + elem[0].offsetHeight + 2,
0450                 'left': positionOffset.left
0451               };
0452             } else if (position === 'left') {
0453               positionValue = {
0454                 'top': positionOffset.top,
0455                 'left': positionOffset.left - 150
0456               };
0457             }
0458             return {
0459               'top': positionValue.top + 'px',
0460               'left': positionValue.left + 'px'
0461             };
0462           };
0463 
0464           var documentMousedownHandler = function() {
0465             hideColorpickerTemplate();
0466           };
0467 
0468           elem.on('click', function () {
0469             update();
0470             colorpickerTemplate
0471                 .addClass('colorpicker-visible')
0472                 .css(getColorpickerTemplatePosition());
0473 
0474             // register global mousedown event to hide the colorpicker
0475             $document.on('mousedown', documentMousedownHandler);
0476           });
0477 
0478           colorpickerTemplate.on('mousedown', function (event) {
0479             event.stopPropagation();
0480             event.preventDefault();
0481           });
0482 
0483           var hideColorpickerTemplate = function() {
0484             if (colorpickerTemplate.hasClass('colorpicker-visible')) {
0485               colorpickerTemplate.removeClass('colorpicker-visible');
0486 
0487               // unregister the global mousedown event
0488               $document.off('mousedown', documentMousedownHandler);
0489             }
0490           };
0491 
0492           colorpickerTemplate.find('button').on('click', function () {
0493             hideColorpickerTemplate();
0494           });
0495         }
0496       };
0497     }]);