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