Subversion Repositories Integrator Subversion

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
1 espaco 1
/*!
2
 * Bootstrap Context Menu
3
 * Author: @sydcanem
4
 * https://github.com/sydcanem/bootstrap-contextmenu
5
 *
6
 * Inspired by Bootstrap's dropdown plugin.
7
 * Bootstrap (http://getbootstrap.com).
8
 *
9
 * Licensed under MIT
10
 * ========================================================= */
11
 
12
;(function($) {
13
 
14
        'use strict';
15
 
16
        /* CONTEXTMENU CLASS DEFINITION
17
         * ============================ */
18
        var toggle = '[data-toggle="context"]';
19
 
20
        var ContextMenu = function (element, options) {
21
                this.$element = $(element);
22
 
23
                this.before = options.before || this.before;
24
                this.onItem = options.onItem || this.onItem;
25
                this.scopes = options.scopes || null;
26
 
27
                if (options.target) {
28
                        this.$element.data('target', options.target);
29
                }
30
 
31
                this.listen();
32
        };
33
 
34
        ContextMenu.prototype = {
35
 
36
                constructor: ContextMenu
37
                ,show: function(e) {
38
 
39
                        var $menu
40
                                , evt
41
                                , tp
42
                                , items
43
                                , relatedTarget = { relatedTarget: this, target: e.currentTarget };
44
 
45
                        if (this.isDisabled()) return;
46
 
47
                        this.closemenu();
48
 
49
                        if (!this.before.call(this,e,$(e.currentTarget))) return;
50
 
51
                        $menu = this.getMenu();
52
                        $menu.trigger(evt = $.Event('show.bs.context', relatedTarget));
53
 
54
                        tp = this.getPosition(e, $menu);
55
                        items = 'li:not(.divider)';
56
                        $menu.attr('style', '')
57
                                .css(tp)
58
                                .addClass('open')
59
                                .on('click.context.data-api', items, $.proxy(this.onItem, this, $(e.currentTarget)))
60
                                .trigger('shown.bs.context', relatedTarget);
61
 
62
                        // Delegating the `closemenu` only on the currently opened menu.
63
                        // This prevents other opened menus from closing.
64
                        $('html')
65
                                .on('click.context.data-api', $menu.selector, $.proxy(this.closemenu, this));
66
 
67
                        return false;
68
                }
69
 
70
                ,closemenu: function(e) {
71
                        var $menu
72
                                , evt
73
                                , items
74
                                , relatedTarget;
75
 
76
                        $menu = this.getMenu();
77
 
78
                        if(!$menu.hasClass('open')) return;
79
 
80
                        relatedTarget = { relatedTarget: this };
81
                        $menu.trigger(evt = $.Event('hide.bs.context', relatedTarget));
82
 
83
                        items = 'li:not(.divider)';
84
                        $menu.removeClass('open')
85
                                .off('click.context.data-api', items)
86
                                .trigger('hidden.bs.context', relatedTarget);
87
 
88
                        $('html')
89
                                .off('click.context.data-api', $menu.selector);
90
                        // Don't propagate click event so other currently
91
                        // opened menus won't close.
92
                        return false;
93
                }
94
 
95
                ,keydown: function(e) {
96
                        if (e.which == 27) this.closemenu(e);
97
                }
98
 
99
                ,before: function(e) {
100
                        return true;
101
                }
102
 
103
                ,onItem: function(e) {
104
                        return true;
105
                }
106
 
107
                ,listen: function () {
108
                        this.$element.on('contextmenu.context.data-api', this.scopes, $.proxy(this.show, this));
109
                        $('html').on('click.context.data-api', $.proxy(this.closemenu, this));
110
                        $('html').on('keydown.context.data-api', $.proxy(this.keydown, this));
111
                }
112
 
113
                ,destroy: function() {
114
                        this.$element.off('.context.data-api').removeData('context');
115
                        $('html').off('.context.data-api');
116
                }
117
 
118
                ,isDisabled: function() {
119
                        return this.$element.hasClass('disabled') ||
120
                                        this.$element.attr('disabled');
121
                }
122
 
123
                ,getMenu: function () {
124
                        var selector = this.$element.data('target')
125
                                , $menu;
126
 
127
                        if (!selector) {
128
                                selector = this.$element.attr('href');
129
                                selector = selector && selector.replace(/.*(?=#[^\s]*$)/, ''); //strip for ie7
130
                        }
131
 
132
                        $menu = $(selector);
133
 
134
                        return $menu && $menu.length ? $menu : this.$element.find(selector);
135
                }
136
 
137
                ,getPosition: function(e, $menu) {
138
                        var mouseX = e.clientX
139
                                , mouseY = e.clientY
140
                                , boundsX = $(window).width()
141
                                , boundsY = $(window).height()
142
                                , menuWidth = $menu.find('.dropdown-menu').outerWidth()
143
                                , menuHeight = $menu.find('.dropdown-menu').outerHeight()
144
                                , tp = {"position":"absolute","z-index":9999}
145
                                , Y, X, parentOffset;
146
 
147
                        if (mouseY + menuHeight > boundsY) {
148
                                Y = {"top": mouseY - menuHeight + $(window).scrollTop()};
149
                        } else {
150
                                Y = {"top": mouseY + $(window).scrollTop()};
151
                        }
152
 
153
                        if ((mouseX + menuWidth > boundsX) && ((mouseX - menuWidth) > 0)) {
154
                                X = {"left": mouseX - menuWidth + $(window).scrollLeft()};
155
                        } else {
156
                                X = {"left": mouseX + $(window).scrollLeft()};
157
                        }
158
 
159
                        // If context-menu's parent is positioned using absolute or relative positioning,
160
                        // the calculated mouse position will be incorrect.
161
                        // Adjust the position of the menu by its offset parent position.
162
                        parentOffset = $menu.offsetParent().offset();
163
                        X.left = X.left - parentOffset.left;
164
                        Y.top = Y.top - parentOffset.top;
165
 
166
                        return $.extend(tp, Y, X);
167
                }
168
 
169
        };
170
 
171
        /* CONTEXT MENU PLUGIN DEFINITION
172
         * ========================== */
173
 
174
        $.fn.contextmenu = function (option,e) {
175
                return this.each(function () {
176
                        var $this = $(this)
177
                                , data = $this.data('context')
178
                                , options = (typeof option == 'object') && option;
179
 
180
                        if (!data) $this.data('context', (data = new ContextMenu($this, options)));
181
                        if (typeof option == 'string') data[option].call(data, e);
182
                });
183
        };
184
 
185
        $.fn.contextmenu.Constructor = ContextMenu;
186
 
187
        /* APPLY TO STANDARD CONTEXT MENU ELEMENTS
188
         * =================================== */
189
 
190
        $(document)
191
           .on('contextmenu.context.data-api', function() {
192
                        $(toggle).each(function () {
193
                                var data = $(this).data('context');
194
                                if (!data) return;
195
                                data.closemenu();
196
                        });
197
                })
198
                .on('contextmenu.context.data-api', toggle, function(e) {
199
                        $(this).contextmenu('show', e);
200
 
201
                        e.preventDefault();
202
                        e.stopPropagation();
203
                });
204
 
205
}(jQuery));