Built motion from commit 7767ffc.|0.0.132
[motion.git] / public / bower_components / angular-bootstrap-colorpicker / bootstrap-colorpicker-module.js
1 angular.module('colorpicker.module', [])
2     .factory('Helper', function () {
3       'use strict';
4       return {
5         closestSlider: function (elem) {
6           var matchesSelector = elem.matches || elem.webkitMatchesSelector || elem.mozMatchesSelector || elem.msMatchesSelector;
7           if (matchesSelector.bind(elem)('I')) {
8             return elem.parentNode;
9           }
10           return elem;
11         },
12         getOffset: function (elem, fixedPosition) {
13           var
14             scrollX = 0,
15             scrollY = 0,
16             rect = elem.getBoundingClientRect();
17           while (elem && !isNaN(elem.offsetLeft) && !isNaN(elem.offsetTop)) {
18             if (!fixedPosition && elem.tagName === 'BODY') {
19               scrollX += document.documentElement.scrollLeft || elem.scrollLeft;
20               scrollY += document.documentElement.scrollTop || elem.scrollTop;
21             } else {
22               scrollX += elem.scrollLeft;
23               scrollY += elem.scrollTop;
24             }
25             elem = elem.offsetParent;
26           }
27           return {
28             top: rect.top + window.pageYOffset,
29             left: rect.left + window.pageXOffset,
30             scrollX: scrollX,
31             scrollY: scrollY
32           };
33         },
34         // a set of RE's that can match strings and generate color tuples. https://github.com/jquery/jquery-color/
35         stringParsers: [
36           {
37             re: /rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*(\d+(?:\.\d+)?)\s*)?\)/,
38             parse: function (execResult) {
39               return [
40                 execResult[1],
41                 execResult[2],
42                 execResult[3],
43                 execResult[4]
44               ];
45             }
46           },
47           {
48             re: /rgba?\(\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d+(?:\.\d+)?)\s*)?\)/,
49             parse: function (execResult) {
50               return [
51                 2.55 * execResult[1],
52                 2.55 * execResult[2],
53                 2.55 * execResult[3],
54                 execResult[4]
55               ];
56             }
57           },
58           {
59             re: /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/,
60             parse: function (execResult) {
61               return [
62                 parseInt(execResult[1], 16),
63                 parseInt(execResult[2], 16),
64                 parseInt(execResult[3], 16)
65               ];
66             }
67           },
68           {
69             re: /#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/,
70             parse: function (execResult) {
71               return [
72                 parseInt(execResult[1] + execResult[1], 16),
73                 parseInt(execResult[2] + execResult[2], 16),
74                 parseInt(execResult[3] + execResult[3], 16)
75               ];
76             }
77           }
78         ]
79       };
80     })
81     .factory('Color', ['Helper', function (Helper) {
82       'use strict';
83       return {
84         value: {
85           h: 1,
86           s: 1,
87           b: 1,
88           a: 1
89         },
90         // translate a format from Color object to a string
91         'rgb': function () {
92           var rgb = this.toRGB();
93           return 'rgb(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ')';
94         },
95         'rgba': function () {
96           var rgb = this.toRGB();
97           return 'rgba(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',' + rgb.a + ')';
98         },
99         'hex': function () {
100           return  this.toHex();
101         },
102
103         // HSBtoRGB from RaphaelJS
104         RGBtoHSB: function (r, g, b, a) {
105           r /= 255;
106           g /= 255;
107           b /= 255;
108
109           var H, S, V, C;
110           V = Math.max(r, g, b);
111           C = V - Math.min(r, g, b);
112           H = (C === 0 ? null :
113               V === r ? (g - b) / C :
114                   V === g ? (b - r) / C + 2 :
115                       (r - g) / C + 4
116               );
117           H = ((H + 360) % 6) * 60 / 360;
118           S = C === 0 ? 0 : C / V;
119           return {h: H || 1, s: S, b: V, a: a || 1};
120         },
121
122         //parse a string to HSB
123         setColor: function (val) {
124           val = (val) ? val.toLowerCase() : val;
125           for (var key in Helper.stringParsers) {
126             if (Helper.stringParsers.hasOwnProperty(key)) {
127               var parser = Helper.stringParsers[key];
128               var match = parser.re.exec(val),
129                   values = match && parser.parse(match);
130               if (values) {
131                 this.value = this.RGBtoHSB.apply(null, values);
132                 return false;
133               }
134             }
135           }
136         },
137
138         setHue: function (h) {
139           this.value.h = 1 - h;
140         },
141
142         setSaturation: function (s) {
143           this.value.s = s;
144         },
145
146         setLightness: function (b) {
147           this.value.b = 1 - b;
148         },
149
150         setAlpha: function (a) {
151           this.value.a = parseInt((1 - a) * 100, 10) / 100;
152         },
153
154         // HSBtoRGB from RaphaelJS
155         // https://github.com/DmitryBaranovskiy/raphael/
156         toRGB: function (h, s, b, a) {
157           if (!h) {
158             h = this.value.h;
159             s = this.value.s;
160             b = this.value.b;
161           }
162           h *= 360;
163           var R, G, B, X, C;
164           h = (h % 360) / 60;
165           C = b * s;
166           X = C * (1 - Math.abs(h % 2 - 1));
167           R = G = B = b - C;
168
169           h = ~~h;
170           R += [C, X, 0, 0, X, C][h];
171           G += [X, C, C, X, 0, 0][h];
172           B += [0, 0, X, C, C, X][h];
173           return {
174             r: Math.round(R * 255),
175             g: Math.round(G * 255),
176             b: Math.round(B * 255),
177             a: a || this.value.a
178           };
179         },
180
181         toHex: function (h, s, b, a) {
182           var rgb = this.toRGB(h, s, b, a);
183           return '#' + ((1 << 24) | (parseInt(rgb.r, 10) << 16) | (parseInt(rgb.g, 10) << 8) | parseInt(rgb.b, 10)).toString(16).substr(1);
184         }
185       };
186     }])
187     .factory('Slider', ['Helper', function (Helper) {
188       'use strict';
189       var
190           slider = {
191             maxLeft: 0,
192             maxTop: 0,
193             callLeft: null,
194             callTop: null,
195             knob: {
196               top: 0,
197               left: 0
198             }
199           },
200           pointer = {};
201
202       return {
203         getSlider: function() {
204           return slider;
205         },
206         getLeftPosition: function(event) {
207           return Math.max(0, Math.min(slider.maxLeft, slider.left + ((event.pageX || pointer.left) - pointer.left)));
208         },
209         getTopPosition: function(event) {
210           return Math.max(0, Math.min(slider.maxTop, slider.top + ((event.pageY || pointer.top) - pointer.top)));
211         },
212         setSlider: function (event, fixedPosition) {
213           var
214             target = Helper.closestSlider(event.target),
215             targetOffset = Helper.getOffset(target, fixedPosition),
216             rect = target.getBoundingClientRect(),
217             offsetX = event.clientX - rect.left,
218             offsetY = event.clientY - rect.top;
219
220           slider.knob = target.children[0].style;
221           slider.left = event.pageX - targetOffset.left - window.pageXOffset + targetOffset.scrollX;
222           slider.top = event.pageY - targetOffset.top - window.pageYOffset + targetOffset.scrollY;
223
224           pointer = {
225             left: event.pageX - (offsetX - slider.left),
226             top: event.pageY - (offsetY - slider.top)
227           };
228         },
229         setSaturation: function(event, fixedPosition, componentSize) {
230           slider = {
231             maxLeft: componentSize,
232             maxTop: componentSize,
233             callLeft: 'setSaturation',
234             callTop: 'setLightness'
235           };
236           this.setSlider(event, fixedPosition);
237         },
238         setHue: function(event, fixedPosition, componentSize) {
239           slider = {
240             maxLeft: 0,
241             maxTop: componentSize,
242             callLeft: false,
243             callTop: 'setHue'
244           };
245           this.setSlider(event, fixedPosition);
246         },
247         setAlpha: function(event, fixedPosition, componentSize) {
248           slider = {
249             maxLeft: 0,
250             maxTop: componentSize,
251             callLeft: false,
252             callTop: 'setAlpha'
253           };
254           this.setSlider(event, fixedPosition);
255         },
256         setKnob: function(top, left) {
257           slider.knob.top = top + 'px';
258           slider.knob.left = left + 'px';
259         }
260       };
261     }])
262     .directive('colorpicker', ['$document', '$compile', 'Color', 'Slider', 'Helper', function ($document, $compile, Color, Slider, Helper) {
263       'use strict';
264       return {
265         require: '?ngModel',
266         restrict: 'A',
267         link: function ($scope, elem, attrs, ngModel) {
268           var
269               thisFormat = attrs.colorpicker ? attrs.colorpicker : 'hex',
270               position = angular.isDefined(attrs.colorpickerPosition) ? attrs.colorpickerPosition : 'bottom',
271               inline = angular.isDefined(attrs.colorpickerInline) ? attrs.colorpickerInline : false,
272               fixedPosition = angular.isDefined(attrs.colorpickerFixedPosition) ? attrs.colorpickerFixedPosition : false,
273               target = angular.isDefined(attrs.colorpickerParent) ? elem.parent() : angular.element(document.body),
274               withInput = angular.isDefined(attrs.colorpickerWithInput) ? attrs.colorpickerWithInput : false,
275               componentSize = angular.isDefined(attrs.colorpickerSize) ? attrs.colorpickerSize : 100,
276               componentSizePx = componentSize + 'px',
277               inputTemplate = withInput ? '<input type="text" name="colorpicker-input" spellcheck="false">' : '',
278               closeButton = !inline ? '<button type="button" class="close close-colorpicker">&times;</button>' : '',
279               template =
280                   '<div class="colorpicker dropdown">' +
281                       '<div class="dropdown-menu">' +
282                       '<colorpicker-saturation><i></i></colorpicker-saturation>' +
283                       '<colorpicker-hue><i></i></colorpicker-hue>' +
284                       '<colorpicker-alpha><i></i></colorpicker-alpha>' +
285                       '<colorpicker-preview></colorpicker-preview>' +
286                       inputTemplate +
287                       closeButton +
288                       '</div>' +
289                       '</div>',
290               colorpickerTemplate = angular.element(template),
291               pickerColor = Color,
292               componentSizePx,
293               sliderAlpha,
294               sliderHue = colorpickerTemplate.find('colorpicker-hue'),
295               sliderSaturation = colorpickerTemplate.find('colorpicker-saturation'),
296               colorpickerPreview = colorpickerTemplate.find('colorpicker-preview'),
297               pickerColorPointers = colorpickerTemplate.find('i');
298
299           $compile(colorpickerTemplate)($scope);
300           colorpickerTemplate.css('min-width', parseInt(componentSize) + 29 + 'px');
301           sliderSaturation.css({
302             'width' : componentSizePx,
303             'height' : componentSizePx
304           });
305           sliderHue.css('height', componentSizePx);
306
307           if (withInput) {
308             var pickerColorInput = colorpickerTemplate.find('input');
309             pickerColorInput.css('width', componentSizePx);
310             pickerColorInput
311                 .on('mousedown', function(event) {
312                   event.stopPropagation();
313                 })
314               .on('keyup', function() {
315                 var newColor = this.value;
316                 elem.val(newColor);
317                 if (ngModel && ngModel.$modelValue !== newColor) {
318                   $scope.$apply(ngModel.$setViewValue(newColor));
319                   update(true);
320                 }
321               });
322           }
323
324           function bindMouseEvents() {
325             $document.on('mousemove', mousemove);
326             $document.on('mouseup', mouseup);
327           }
328
329           if (thisFormat === 'rgba') {
330             colorpickerTemplate.addClass('alpha');
331             sliderAlpha = colorpickerTemplate.find('colorpicker-alpha');
332             sliderAlpha.css('height', componentSizePx);
333             sliderAlpha
334                 .on('click', function(event) {
335                   Slider.setAlpha(event, fixedPosition, componentSize);
336                   mousemove(event);
337                 })
338                 .on('mousedown', function(event) {
339                   Slider.setAlpha(event, fixedPosition, componentSize);
340                   bindMouseEvents();
341                 })
342                 .on('mouseup', function(event){
343                   emitEvent('colorpicker-selected-alpha');
344                 });
345           }
346
347           sliderHue
348               .on('click', function(event) {
349                 Slider.setHue(event, fixedPosition, componentSize);
350                 mousemove(event);
351               })
352               .on('mousedown', function(event) {
353                 Slider.setHue(event, fixedPosition, componentSize);
354                 bindMouseEvents();
355               })
356               .on('mouseup', function(event){
357                 emitEvent('colorpicker-selected-hue');
358               });
359
360           sliderSaturation
361               .on('click', function(event) {
362                 Slider.setSaturation(event, fixedPosition, componentSize);
363                 mousemove(event);
364                 if (angular.isDefined(attrs.colorpickerCloseOnSelect)) {
365                   hideColorpickerTemplate();
366                 }
367               })
368               .on('mousedown', function(event) {
369                 Slider.setSaturation(event, fixedPosition, componentSize);
370                 bindMouseEvents();
371               })
372               .on('mouseup', function(event){
373                 emitEvent('colorpicker-selected-saturation');
374               });
375
376           if (fixedPosition) {
377             colorpickerTemplate.addClass('colorpicker-fixed-position');
378           }
379
380           colorpickerTemplate.addClass('colorpicker-position-' + position);
381           if (inline === 'true') {
382             colorpickerTemplate.addClass('colorpicker-inline');
383           }
384
385           target.append(colorpickerTemplate);
386
387           if (ngModel) {
388             ngModel.$render = function () {
389               elem.val(ngModel.$viewValue);
390
391               update();
392             };
393           }
394
395           elem.on('blur keyup change', function() {
396             update();
397           });
398
399           elem.on('$destroy', function() {
400             colorpickerTemplate.remove();
401           });
402
403           function previewColor() {
404             try {
405               colorpickerPreview.css('backgroundColor', pickerColor[thisFormat]());
406             } catch (e) {
407               colorpickerPreview.css('backgroundColor', pickerColor.toHex());
408             }
409             sliderSaturation.css('backgroundColor', pickerColor.toHex(pickerColor.value.h, 1, 1, 1));
410             if (thisFormat === 'rgba') {
411               sliderAlpha.css.backgroundColor = pickerColor.toHex();
412             }
413           }
414
415           function mousemove(event) {
416             var 
417                 left = Slider.getLeftPosition(event),
418                 top = Slider.getTopPosition(event),
419                 slider = Slider.getSlider();
420
421             Slider.setKnob(top, left);
422
423             if (slider.callLeft) {
424               pickerColor[slider.callLeft].call(pickerColor, left / componentSize);
425             }
426             if (slider.callTop) {
427               pickerColor[slider.callTop].call(pickerColor, top / componentSize);
428             }
429             previewColor();
430             var newColor = pickerColor[thisFormat]();
431             elem.val(newColor);
432             if (ngModel) {
433               $scope.$apply(ngModel.$setViewValue(newColor));
434             }
435             if (withInput) {
436               pickerColorInput.val(newColor);
437             }
438             return false;
439           }
440
441           function mouseup() {
442             emitEvent('colorpicker-selected');
443             $document.off('mousemove', mousemove);
444             $document.off('mouseup', mouseup);
445           }
446
447           function update(omitInnerInput) {
448             pickerColor.setColor(elem.val());
449             if (withInput && !omitInnerInput) {
450               pickerColorInput.val(elem.val());
451             }
452             pickerColorPointers.eq(0).css({
453               left: pickerColor.value.s * componentSize + 'px',
454               top: componentSize - pickerColor.value.b * componentSize + 'px'
455             });
456             pickerColorPointers.eq(1).css('top', componentSize * (1 - pickerColor.value.h) + 'px');
457             pickerColorPointers.eq(2).css('top', componentSize * (1 - pickerColor.value.a) + 'px');
458             previewColor();
459           }
460
461           function getColorpickerTemplatePosition() {
462             var
463                 positionValue,
464                 positionOffset = Helper.getOffset(elem[0]);
465
466             if(angular.isDefined(attrs.colorpickerParent)) {
467               positionOffset.left = 0;
468               positionOffset.top = 0;
469             }
470
471             if (position === 'top') {
472               positionValue =  {
473                 'top': positionOffset.top - 147,
474                 'left': positionOffset.left
475               };
476             } else if (position === 'right') {
477               positionValue = {
478                 'top': positionOffset.top,
479                 'left': positionOffset.left + 126
480               };
481             } else if (position === 'bottom') {
482               positionValue = {
483                 'top': positionOffset.top + elem[0].offsetHeight + 2,
484                 'left': positionOffset.left
485               };
486             } else if (position === 'left') {
487               positionValue = {
488                 'top': positionOffset.top,
489                 'left': positionOffset.left - 150
490               };
491             }
492             return {
493               'top': positionValue.top + 'px',
494               'left': positionValue.left + 'px'
495             };
496           }
497
498           function documentMousedownHandler() {
499             hideColorpickerTemplate();
500           }
501
502           function showColorpickerTemplate() {
503
504             if (!colorpickerTemplate.hasClass('colorpicker-visible')) {
505               update();
506               colorpickerTemplate
507                 .addClass('colorpicker-visible')
508                 .css(getColorpickerTemplatePosition());
509               emitEvent('colorpicker-shown');
510
511               if (inline === false) {
512                 // register global mousedown event to hide the colorpicker
513                 $document.on('mousedown', documentMousedownHandler);
514               }
515
516               if (attrs.colorpickerIsOpen) {
517                 $scope[attrs.colorpickerIsOpen] = true;
518                 if (!$scope.$$phase) {
519                   $scope.$digest(); //trigger the watcher to fire
520                 }
521               }
522             }
523           }
524
525           if (inline === false) {
526             elem.on('click', showColorpickerTemplate);
527           } else {
528             showColorpickerTemplate();
529           }
530
531           colorpickerTemplate.on('mousedown', function (event) {
532             event.stopPropagation();
533             event.preventDefault();
534           });
535
536           function emitEvent(name) {
537             if (ngModel) {
538               $scope.$emit(name, {
539                 name: attrs.ngModel,
540                 value: ngModel.$modelValue
541               });
542             }
543           }
544
545           function hideColorpickerTemplate() {
546             if (colorpickerTemplate.hasClass('colorpicker-visible')) {
547               colorpickerTemplate.removeClass('colorpicker-visible');
548               emitEvent('colorpicker-closed');
549               // unregister the global mousedown event
550               $document.off('mousedown', documentMousedownHandler);
551
552               if (attrs.colorpickerIsOpen) {
553                 $scope[attrs.colorpickerIsOpen] = false;
554                 if (!$scope.$$phase) {
555                   $scope.$digest(); //trigger the watcher to fire
556                 }
557               }
558             }
559           }
560
561           colorpickerTemplate.find('button').on('click', function () {
562             hideColorpickerTemplate();
563           });
564
565           if (attrs.colorpickerIsOpen) {
566             $scope.$watch(attrs.colorpickerIsOpen, function(shouldBeOpen) {
567
568               if (shouldBeOpen === true) {
569                 showColorpickerTemplate();
570               } else if (shouldBeOpen === false) {
571                 hideColorpickerTemplate();
572               }
573
574             });
575           }
576         }
577       };
578     }]);