').addClass('ng-bootstrap-contextmenu dropdown clearfix');
+ var $ul = null;
+ //check if we will use a template or the options
+ if ($ctrl.fnTemplateLink) {
+ //link the scope to a clone template
+ $ctrl.fnTemplateLink($scope, function (clone) {
+ $ul = clone;
+ });
+ $ul.find('li').on('click', function (e) {
+ liClickHandler($scope, e, $event, $contextMenu, $ctrl, model, null, false);
+ });
+ } else {
+ //create the ul and build the list
+ $ul = $('
');
+ angular.forEach(options, function (item, i) {
+ if (item === null) {
+ $ul.append($('- ').addClass('divider'));
} else {
- $li.on('click', function ($event) {
- $event.preventDefault();
- });
- $li.addClass('disabled');
+ $ul.append(renderMenuItem($contextMenu, $scope, $event, item, model, $ctrl));
}
- }
- $ul.append($li);
- });
+ });
+ }
+ //format the ul
+ $ul.addClass('dropdown-menu')
+ .attr({ 'role': 'menu' })
+ .css({
+ display: 'block',
+ position: 'absolute',
+ left: $event.pageX + 'px',
+ top: $event.pageY + 'px'
+ });
+ //append to the div
$contextMenu.append($ul);
+ //calculate height
var height = Math.max(
document.body.scrollHeight, document.documentElement.scrollHeight,
document.body.offsetHeight, document.documentElement.offsetHeight,
document.body.clientHeight, document.documentElement.clientHeight
);
+ //format the context menu div
$contextMenu.css({
width: '100%',
height: height + 'px',
@@ -58,32 +246,123 @@ angular.module('ui.bootstrap.contextMenu', [])
left: 0,
zIndex: 9999
});
+ //add to the page
$(document).find('body').append($contextMenu);
+ //context menu control events
$contextMenu.on("mousedown", function (e) {
if ($(e.target).hasClass('dropdown')) {
- $(event.currentTarget).removeClass('context');
+ $target.removeClass('context');
$contextMenu.remove();
+ //call the close function
+ $timeout(function () { $scope.$eval($ctrl.closeExpr); });
}
- }).on('contextmenu', function (event) {
- $(event.currentTarget).removeClass('context');
- event.preventDefault();
+ })
+ .on('contextmenu', function (e) {
+ $(e.currentTarget).removeClass('context');
+ e.preventDefault();
$contextMenu.remove();
+ //call the close function
+ $timeout(function () { $scope.$eval($ctrl.closeExpr); });
});
};
- return function ($scope, element, attrs) {
- element.on('contextmenu', function (event) {
- event.stopPropagation();
+ self.bindContextMenu = function ($scope, $element, $attrs, $controller) {
+ //context menu event
+ $element.on('contextmenu', function ($event) {
+ //only our context must open
+ $event.stopPropagation();
$scope.$apply(function () {
- event.preventDefault();
- var options = $scope.$eval(attrs.contextMenu);
- var model = $scope.$eval(attrs.model);
- if (options instanceof Array) {
- if (options.length === 0) { return; }
- renderContextMenu($scope, event, options, model);
+ $event.preventDefault();
+ //get the scope's options and model
+ var options = $scope.$eval($controller.optionsExpr);
+ var model = $scope.$eval($controller.modelExpr);
+ var useTemplate = $controller.fnTemplateLink;
+ //work the options, if builder
+ if (!useTemplate && angular.isFunction(options._toArray)) {
+ options = options._toArray();
+ }
+ //builder delivers an array
+ if (useTemplate || options instanceof Array) {
+ var open = callMenuItemOpening($scope, $controller);
+ //check if we will open or not
+ if (!useTemplate && (options.length === 0 || !open)) {
+ return;
+ }
+ //render the menu
+ renderContextMenu($scope, $event, options, model, $controller);
+ callMenuItemOpen($scope, $controller);
} else {
- throw '"' + attrs.contextMenu + '" not an array';
+ throw '"' + $controller.optionsExpr + '" is not an array nor a contextMenuBuilder';
}
});
});
};
+}])
+.directive('contextMenu', ["$compile", "$rootElement", "_contextMenuWorker", function ($compile, $rootElement, _contextMenuWorker) {
+ return {
+ restrict: 'A',
+ priority: 1001,//we must run our compile before ngRepeat for this to work
+ controller: function () {
+ var self = this;
+ //hold the template link function
+ self.fnTemplateLink = null;
+ //hold the options expression
+ self.optionsExpr = null;
+ //hold the model expresion
+ self.modelExpr = null;
+ //hold the event expressions
+ self.openingExpr = null;
+ self.openExpr = null;
+ self.closeExpr = null;
+ },
+ compile: function ($element, $attrs) {
+ //get the template view related to our context and remove it
+ var $tmpl = $rootElement.find('[context-menu-template="' + $attrs.contextMenu + '"]').remove();
+ var useTemplate = $tmpl.length > 0;
+ //if a template was provided
+ if (useTemplate) {
+ if (!$tmpl.is("ul")) throw "context-menu-template must be a
";
+ }
+ else if (!$attrs.contextMenu) {
+ throw "context-menu needs a context-menu-template child or its options set";
+ }
+ //remove ourselves
+ $element.removeAttr("context-menu");
+ //add our run directive that will execute on each ngRepeat element
+ $element.attr("context-menu-run", "");
+ //return our link function
+ return function ($scope, $elem, $att, $controller) {
+ //fill our controller properties
+ $controller.optionsExpr = $attrs.contextMenu;
+ $controller.modelExpr = $attrs.model;
+ $controller.openingExpr = $attrs.opening;
+ $controller.openExpr = $attrs.open;
+ $controller.closeExpr = $attrs.close;
+ if (useTemplate) {
+ $controller.fnTemplateLink = $compile($tmpl);
+ } else {
+ //no template, bind now
+ _contextMenuWorker.bindContextMenu($scope, $elem, $att, $controller);
+ }
+ $scope.$on("$destroy", function () {
+ //clear references
+ $controller.optionsExpr = null;
+ $controller.modelExpr = null;
+ $controller.openingExpr = null;
+ $controller.openExpr = null;
+ $controller.fnTemplateLink = null;
+ $controller = null;
+ });
+ }
+ }
+ }
+}])
+.directive('contextMenuRun', ["$timeout", "$q", "_contextMenuWorker", function ($timeout, $q, _contextMenuWorker) {
+ return {
+ restrict: 'A',
+ priority: 999,//to run after the item scope has been created
+ require: 'contextMenu',
+ link: function ($scope, $element, $attrs, $controller) {
+ _contextMenuWorker.bindContextMenu($scope, $element, $attrs, $controller);
+ }
+ }
}]);