Built motion from commit 76eb00b9e.|1.0.24
[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               colorpickerValue = {
293                 h: 1,
294                 s: 0,
295                 b: 1,
296                 a: 1
297               },
298               sliderAlpha,
299               sliderHue = colorpickerTemplate.find('colorpicker-hue'),
300               sliderSaturation = colorpickerTemplate.find('colorpicker-saturation'),
301               colorpickerPreview = colorpickerTemplate.find('colorpicker-preview'),
302               pickerColorPointers = colorpickerTemplate.find('i');
303
304           $compile(colorpickerTemplate)($scope);
305           colorpickerTemplate.css('min-width', parseInt(componentSize) + 29 + 'px');
306           sliderSaturation.css({
307             'width' : componentSizePx,
308             'height' : componentSizePx
309           });
310           sliderHue.css('height', componentSizePx);
311
312           if (withInput) {
313             var pickerColorInput = colorpickerTemplate.find('input');
314             pickerColorInput.css('width', componentSizePx);
315             pickerColorInput
316                 .on('mousedown', function(event) {
317                   event.stopPropagation();
318                 })
319               .on('keyup', function() {
320                 var newColor = this.value;
321                 elem.val(newColor);
322                 if (ngModel && ngModel.$modelValue !== newColor) {
323                   $scope.$apply(ngModel.$setViewValue(newColor));
324                   update(true);
325                 }
326               });
327           }
328
329           function bindMouseEvents() {
330             $document.on('mousemove', mousemove);
331             $document.on('mouseup', mouseup);
332           }
333
334           if (thisFormat === 'rgba') {
335             colorpickerTemplate.addClass('alpha');
336             sliderAlpha = colorpickerTemplate.find('colorpicker-alpha');
337             sliderAlpha.css('height', componentSizePx);
338             sliderAlpha
339                 .on('click', function(event) {
340                   Slider.setAlpha(event, fixedPosition, componentSize);
341                   mousemove(event);
342                 })
343                 .on('mousedown', function(event) {
344                   Slider.setAlpha(event, fixedPosition, componentSize);
345                   bindMouseEvents();
346                 })
347                 .on('mouseup', function(event){
348                   emitEvent('colorpicker-selected-alpha');
349                 });
350           }
351
352           sliderHue
353               .on('click', function(event) {
354                 Slider.setHue(event, fixedPosition, componentSize);
355                 mousemove(event);
356               })
357               .on('mousedown', function(event) {
358                 Slider.setHue(event, fixedPosition, componentSize);
359                 bindMouseEvents();
360               })
361               .on('mouseup', function(event){
362                 emitEvent('colorpicker-selected-hue');
363               });
364
365           sliderSaturation
366               .on('click', function(event) {
367                 Slider.setSaturation(event, fixedPosition, componentSize);
368                 mousemove(event);
369                 if (angular.isDefined(attrs.colorpickerCloseOnSelect)) {
370                   hideColorpickerTemplate();
371                 }
372               })
373               .on('mousedown', function(event) {
374                 Slider.setSaturation(event, fixedPosition, componentSize);
375                 bindMouseEvents();
376               })
377               .on('mouseup', function(event){
378                 emitEvent('colorpicker-selected-saturation');
379               });
380
381           if (fixedPosition) {
382             colorpickerTemplate.addClass('colorpicker-fixed-position');
383           }
384
385           colorpickerTemplate.addClass('colorpicker-position-' + position);
386           if (inline === 'true') {
387             colorpickerTemplate.addClass('colorpicker-inline');
388           }
389
390           target.append(colorpickerTemplate);
391
392           if (ngModel) {
393             ngModel.$render = function () {
394               elem.val(ngModel.$viewValue);
395
396               update();
397             };
398           }
399
400           elem.on('blur keyup change', function() {
401             update();
402           });
403
404           elem.on('$destroy', function() {
405             colorpickerTemplate.remove();
406           });
407
408           function previewColor() {
409             try {
410               colorpickerPreview.css('backgroundColor', pickerColor[thisFormat]());
411             } catch (e) {
412               colorpickerPreview.css('backgroundColor', pickerColor.toHex());
413             }
414             sliderSaturation.css('backgroundColor', pickerColor.toHex(pickerColor.value.h, 1, 1, 1));
415             if (thisFormat === 'rgba') {
416               sliderAlpha.css.backgroundColor = pickerColor.toHex();
417             }
418           }
419
420           function mousemove(event) {
421             var
422                 left = Slider.getLeftPosition(event),
423                 top = Slider.getTopPosition(event),
424                 slider = Slider.getSlider();
425
426             Slider.setKnob(top, left);
427
428             if (slider.callLeft) {
429               pickerColor[slider.callLeft].call(pickerColor, left / componentSize);
430             }
431             if (slider.callTop) {
432               pickerColor[slider.callTop].call(pickerColor, top / componentSize);
433             }
434             previewColor();
435             var newColor = pickerColor[thisFormat]();
436             elem.val(newColor);
437             if (ngModel) {
438               $scope.$apply(ngModel.$setViewValue(newColor));
439             }
440             if (withInput) {
441               pickerColorInput.val(newColor);
442             }
443             return false;
444           }
445
446           function mouseup() {
447             emitEvent('colorpicker-selected');
448             $document.off('mousemove', mousemove);
449             $document.off('mouseup', mouseup);
450           }
451
452           function update(omitInnerInput) {
453             pickerColor.value = colorpickerValue;
454             pickerColor.setColor(elem.val());
455             if (withInput && !omitInnerInput) {
456               pickerColorInput.val(elem.val());
457             }
458             pickerColorPointers.eq(0).css({
459               left: pickerColor.value.s * componentSize + 'px',
460               top: componentSize - pickerColor.value.b * componentSize + 'px'
461             });
462             pickerColorPointers.eq(1).css('top', componentSize * (1 - pickerColor.value.h) + 'px');
463             pickerColorPointers.eq(2).css('top', componentSize * (1 - pickerColor.value.a) + 'px');
464             colorpickerValue = pickerColor.value;
465             previewColor();
466           }
467
468           function getColorpickerTemplatePosition() {
469             var
470                 positionValue,
471                 positionOffset = Helper.getOffset(elem[0]);
472
473             if(angular.isDefined(attrs.colorpickerParent)) {
474               positionOffset.left = 0;
475               positionOffset.top = 0;
476             }
477
478             if (position === 'top') {
479               positionValue =  {
480                 'top': positionOffset.top - 147,
481                 'left': positionOffset.left
482               };
483             } else if (position === 'right') {
484               positionValue = {
485                 'top': positionOffset.top,
486                 'left': positionOffset.left + 126
487               };
488             } else if (position === 'bottom') {
489               positionValue = {
490                 'top': positionOffset.top + elem[0].offsetHeight + 2,
491                 'left': positionOffset.left
492               };
493             } else if (position === 'left') {
494               positionValue = {
495                 'top': positionOffset.top,
496                 'left': positionOffset.left - 150
497               };
498             }
499             return {
500               'top': positionValue.top + 'px',
501               'left': positionValue.left + 'px'
502             };
503           }
504
505           function documentMousedownHandler() {
506             hideColorpickerTemplate();
507           }
508
509           function showColorpickerTemplate() {
510
511             if (!colorpickerTemplate.hasClass('colorpicker-visible')) {
512               update();
513               colorpickerTemplate
514                 .addClass('colorpicker-visible')
515                 .css(getColorpickerTemplatePosition());
516               emitEvent('colorpicker-shown');
517
518               if (inline === false) {
519                 // register global mousedown event to hide the colorpicker
520                 $document.on('mousedown', documentMousedownHandler);
521               }
522
523               if (attrs.colorpickerIsOpen) {
524                 $scope[attrs.colorpickerIsOpen] = true;
525                 if (!$scope.$$phase || !$scope.$root.$$phase) {
526                   $scope.$digest(); //trigger the watcher to fire
527                 }
528               }
529             }
530           }
531
532           if (inline === false) {
533             elem.on('click', showColorpickerTemplate);
534           } else {
535             showColorpickerTemplate();
536           }
537
538           colorpickerTemplate.on('mousedown', function (event) {
539             event.stopPropagation();
540             event.preventDefault();
541           });
542
543           function emitEvent(name) {
544             if (ngModel) {
545               $scope.$emit(name, {
546                 name: attrs.ngModel,
547                 value: ngModel.$modelValue
548               });
549             }
550           }
551
552           function hideColorpickerTemplate() {
553             if (colorpickerTemplate.hasClass('colorpicker-visible')) {
554               colorpickerTemplate.removeClass('colorpicker-visible');
555               emitEvent('colorpicker-closed');
556               // unregister the global mousedown event
557               $document.off('mousedown', documentMousedownHandler);
558
559               if (attrs.colorpickerIsOpen) {
560                 $scope[attrs.colorpickerIsOpen] = false;
561                 if (!$scope.$$phase || !$scope.$root.$$phase) {
562                   $scope.$digest(); //trigger the watcher to fire
563                 }
564               }
565             }
566           }
567
568           colorpickerTemplate.find('button').on('click', function () {
569             hideColorpickerTemplate();
570           });
571
572           if (attrs.colorpickerIsOpen) {
573             $scope.$watch(attrs.colorpickerIsOpen, function(shouldBeOpen) {
574
575               if (shouldBeOpen === true) {
576                 showColorpickerTemplate();
577               } else if (shouldBeOpen === false) {
578                 hideColorpickerTemplate();
579               }
580
581             });
582           }
583         }
584       };
585     }]);