Built motion from commit 503e72f.|0.0.143
[motion.git] / public / bower_components / angular-bootstrap-contextmenu / contextMenu.js
1 angular.module('ui.bootstrap.contextMenu', [])
2
3 .service('CustomService', function () {
4     "use strict";
5
6     return {
7         initialize: function (item) {
8             console.log("got here", item);
9         }
10     }
11
12 })
13 .directive('contextMenu', ["$parse", "$q", "CustomService", "$sce", function ($parse, $q, custom, $sce) {
14
15     var contextMenus = [];
16     var $currentContextMenu = null;
17     var defaultItemText = "New Item";
18
19     var removeContextMenus = function (level) {
20         /// <summary>Remove context menu.</summary>
21         while (contextMenus.length && (!level || contextMenus.length > level)) {
22             contextMenus.pop().remove();
23         }
24         if (contextMenus.length == 0 && $currentContextMenu) {
25             $currentContextMenu.remove();
26         }
27     };
28
29
30     var processTextItem = function ($scope, item, text, event, model, $promises, nestedMenu, $) {
31         "use strict";
32
33         var $a = $('<a>');
34         $a.css("padding-right", "8px");
35         $a.attr({ tabindex: '-1', href: '#' });
36
37         if (typeof item[0] === 'string') {
38             text = item[0];
39         }
40         else if (typeof item[0] === "function") {
41             item[0].call($scope, $scope, event, model);
42         } else if (typeof item.text !== "undefined") {
43             text = item.text;
44         }
45
46         var $promise = $q.when(text);
47         $promises.push($promise);
48         $promise.then(function (text) {
49             $a.text(text);
50             if (nestedMenu) {
51                 $a.css("cursor", "default");
52                 $a.append($('<strong style="font-family:monospace;font-weight:bold;float:right;">&gt;</strong>'));
53             }
54         });
55
56         return $a;
57
58     };
59
60     var processItem = function ($scope, event, model, item, $ul, $li, $promises, $q, $, level) {
61         /// <summary>Process individual item</summary>
62         "use strict";
63         var nestedMenu = angular.isArray(item[1])
64             ? item[1] : angular.isArray(item[2])
65             ? item[2] : angular.isArray(item[3])
66             ? item[3] : null;
67
68         // if html property is not defined, fallback to text, otherwise use default text
69         // if first item in the item array is a function then invoke .call()
70         // if first item is a string, then text should be the string.
71
72         var text = defaultItemText;
73         if (typeof item[0] === 'string' || typeof item.text !== "undefined") {
74             text = processTextItem($scope, item, text, event, model, $promises, nestedMenu, $);
75         }
76         else if (typeof item.html !== "undefined") {
77             // leave styling open to dev
78             text = item.html
79         }
80
81         $li.append(text);
82
83
84
85
86         // if item is object, and has enabled prop invoke the prop
87         // els if fallback to item[2]
88
89         var isEnabled = function () {
90             if (typeof item.enabled !== "undefined") {
91                 return item.enabled.call($scope, $scope, event, model, text);
92             } else if (typeof item[2] === "function") {
93                 return item[2].call($scope, $scope, event, model, text);
94             } else {
95                 return true;
96             }
97         };
98
99         registerEnabledEvents($scope, isEnabled(), item, $ul, $li, nestedMenu, model, text, event, $, level);
100     };
101
102     var handlePromises = function ($ul, level, event, $promises) {
103         /// <summary>
104         /// calculate if drop down menu would go out of screen at left or bottom
105         /// calculation need to be done after element has been added (and all texts are set; thus thepromises)
106         /// to the DOM the get the actual height
107         /// </summary>
108         "use strict";
109         $q.all($promises).then(function () {
110             if (level === 0) {
111                 var topCoordinate = event.pageY;
112                 var menuHeight = angular.element($ul[0]).prop('offsetHeight');
113                 var winHeight = event.view.innerHeight;
114                 if (topCoordinate > menuHeight && winHeight - topCoordinate < menuHeight) {
115                     topCoordinate = event.pageY - menuHeight;
116                 }
117
118                 var leftCoordinate = event.pageX;
119                 var menuWidth = angular.element($ul[0]).prop('offsetWidth');
120                 var winWidth = event.view.innerWidth;
121                 if (leftCoordinate > menuWidth && winWidth - leftCoordinate < menuWidth) {
122                     leftCoordinate = event.pageX - menuWidth;
123                 }
124
125                 $ul.css({
126                     display: 'block',
127                     position: 'absolute',
128                     left: leftCoordinate + 'px',
129                     top: topCoordinate + 'px'
130                 });
131             }
132         });
133
134     };
135
136     var registerEnabledEvents = function ($scope, enabled, item, $ul, $li, nestedMenu, model, text, event, $, level) {
137         /// <summary>If item is enabled, register various mouse events.</summary>
138         if (enabled) {
139             var openNestedMenu = function ($event) {
140                 removeContextMenus(level + 1);
141                 var ev = {
142                     pageX: event.pageX + $ul[0].offsetWidth - 1,
143                     pageY: $ul[0].offsetTop + $li[0].offsetTop - 3
144                 };
145                 renderContextMenu($scope, ev, nestedMenu, model, level + 1);
146             };
147
148             $li.on('click', function ($event) {
149                 $event.preventDefault();
150                 $scope.$apply(function () {
151                     if (nestedMenu) {
152                         openNestedMenu($event);
153                     } else {
154                         $(event.currentTarget).removeClass('context');
155                         removeContextMenus();
156
157                         if (angular.isFunction(item[1])) {
158                             item[1].call($scope, $scope, event, model, text)
159                         } else {
160                             item.click.call($scope, $scope, event, model, text);
161                         }
162                     }
163                 });
164             });
165
166             $li.on('mouseover', function ($event) {
167                 $scope.$apply(function () {
168                     if (nestedMenu) {
169                         openNestedMenu($event);
170                     }
171                 });
172             });
173         } else {
174             $li.on('click', function ($event) {
175                 $event.preventDefault();
176             });
177             $li.addClass('disabled');
178         }
179
180     };
181
182
183     var renderContextMenu = function ($scope, event, options, model, level) {
184         /// <summary>Render context menu recursively.</summary>
185         if (!level) { level = 0; }
186         if (!$) { var $ = angular.element; }
187         $(event.currentTarget).addClass('context');
188         var $contextMenu = $('<div>');
189         if ($currentContextMenu) {
190             $contextMenu = $currentContextMenu;
191         } else {
192             $currentContextMenu = $contextMenu;
193         }
194         $contextMenu.addClass('dropdown clearfix');
195         var $ul = $('<ul>');
196         $ul.addClass('dropdown-menu');
197         $ul.attr({ 'role': 'menu' });
198         $ul.css({
199             display: 'block',
200             position: 'absolute',
201             left: event.pageX + 'px',
202             top: event.pageY + 'px',
203             "z-index": 10000
204         });
205
206         var $promises = [];
207
208         angular.forEach(options, function (item) {
209
210             var $li = $('<li>');
211             if (item === null) {
212                 $li.addClass('divider');
213             } else if (typeof item[0] === "object") {
214                 custom.initialize($li, item);
215             } else {
216                 processItem($scope, event, model, item, $ul, $li, $promises, $q, $, level);
217             }
218             $ul.append($li);
219         });
220         $contextMenu.append($ul);
221         var height = Math.max(
222             document.body.scrollHeight, document.documentElement.scrollHeight,
223             document.body.offsetHeight, document.documentElement.offsetHeight,
224             document.body.clientHeight, document.documentElement.clientHeight
225         );
226         $contextMenu.css({
227             width: '100%',
228             height: height + 'px',
229             position: 'absolute',
230             top: 0,
231             left: 0,
232             zIndex: 9999
233         });
234         $(document).find('body').append($contextMenu);
235
236         handlePromises($ul, level, event, $promises);
237
238         $contextMenu.on("mousedown", function (e) {
239             if ($(e.target).hasClass('dropdown')) {
240                 $(event.currentTarget).removeClass('context');
241                 removeContextMenus();
242             }
243         }).on('contextmenu', function (event) {
244             $(event.currentTarget).removeClass('context');
245             event.preventDefault();
246             removeContextMenus(level);
247         });
248
249         $scope.$on("$destroy", function () {
250             removeContextMenus();
251         });
252
253         contextMenus.push($ul);
254     };
255     return function ($scope, element, attrs) {
256         element.on('contextmenu', function (event) {
257             event.stopPropagation();
258             $scope.$apply(function () {
259                 event.preventDefault();
260                 var options = $scope.$eval(attrs.contextMenu);
261                 var model = $scope.$eval(attrs.model);
262                 if (options instanceof Array) {
263                     if (options.length === 0) { return; }
264                     renderContextMenu($scope, event, options, model);
265                 } else {
266                     throw '"' + attrs.contextMenu + '" not an array';
267                 }
268             });
269         });
270     };
271 }]);