pax_global_header 0000666 0000000 0000000 00000000064 12514377576 0014533 g ustar 00root root 0000000 0000000 52 comment=08b5a39d37dfa4d390697b006a2ca9d4bd7e9979 Roundcube-Plugin-Context-Menu-2.1.2/ 0000775 0000000 0000000 00000000000 12514377576 0017243 5 ustar 00root root 0000000 0000000 Roundcube-Plugin-Context-Menu-2.1.2/.gitignore 0000664 0000000 0000000 00000000017 12514377576 0021231 0 ustar 00root root 0000000 0000000 config.inc.php Roundcube-Plugin-Context-Menu-2.1.2/CHANGELOG 0000664 0000000 0000000 00000010256 12514377576 0020461 0 ustar 00root root 0000000 0000000 Roundcube Webmail ContextMenu ============================= Version 2.1.2 (2015-04-18, rc-1.1) ================================================= * Fix #67 address book copy/move action not updated Version 2.1.1 (2015-03-18, rc-1.1) ================================================= * Update submenu detection after 619891c Version 2.1 (2015-01-06, rc-1.1) ================================================= * Add specific menu for adding contact to group * Update after dev-accessibility merged to RC master * Support multi-folder search results (66536974fe) * Fix possible CSRF in readfolder action * Add option to move/copy contacts in address book Version 2.0 (2014-04-30, rc-1.0) ================================================= * Remove IE6 support * Remove rcm_contextmenu_register_command, use commands from toolbars * Remove specially created manus, generate context menu dynamically from UI * Much better menu generation, improve loading times Version 1.13 (2014-02-01, rc-1.0) ================================================= * Add menu to contact list on compose screen (requires b3c034c) Version 1.12 (2013-12-01, rc-1.0) ================================================= * Update depreciated moveto command to move * Update config file var names to match core Version 1.11 (2013-05-19, rc-1.0) ================================================= * Add export option to address book menu * Add contextmenu_show JS event **** code branching/tagging no longer sync'd to roundcube versions **** Version 1.10 (2013-03-03, rc-0.9) ================================================= * Add remove selected contacts from group option * Rename default skin to classic (c40419bdfe) * rcube_ui > rcube_utils (r6091) * Update for Roundcube framework Version 1.9 (2012-04-14, rc-0.8) ================================================= * Update after r5781 * Add inital support for Larry Version 1.8 (2011-05-14, rc-0.8) ================================================= * Fix readfolder method after r5557 * Use new labels for group delete/rename (r4864) * Add forward-attachment function (r4761) Version 1.7 (2011-10-01, rc-0.5) ================================================= * Add reply-list function (r4032) Version 1.6 (2010-08-01, rc-0.4) ================================================= * Fix error in deleteing after moving using context menu * Update hooks (r3840) Version 1.5 (2010-05-29, rc-0.4) ================================================= * Hide menu after click in iframes * Update contact copying after r3694 * Update hooks after r3685 Version 1.4 (2010-03-30, rc-0.4) ================================================= * Update after r3614 * Update jquery.ContextMenu to 1.01 * Use new imap->search_once function (r3446) * Add initial support for contact groups in address book * Fix mark all as read function * Update to r3393 * Fix folder count with nested folders Version 1.3 (2010-02-07, rc-0.4) ================================================= * Update after r3258 * CSS update after r3141 * Use local_skin_path() (rev 3076) * Added address book context menu (requires r3023) Version 1.2 (2009-09-18, rc-0.3) ================================================= * Fix folder menu for UTF8 folders * Use rcmail_send_unread_count (r2960) * Add 'move to' to message list menu Version 1.1 (2009-09-11, rc-0.3) ================================================= * Added folder list context menu * Moved menu generation from JS to PHP * Added support for sub menus * Added new "edit" action * Update insertrow event listener following changes in core * Add fallback to default skin * Better init restrictions * Correct style after r2541 * Only init when on mailbox screen (while _uid is empty and user is logged in) * Added hook for other plugins to add items to the menu (see README) * Added support for new actions: show source, save .eml, open in new window * Added support for plugin template system * Added highlighting of selected row * Fixed menu in IE * Fixed delete in Trash folder * Added ability to mark or delete multiple messages * Fixed delete function * Fixed working with "Select" functions * Added event hooks when message list is updated * Created plugin Roundcube-Plugin-Context-Menu-2.1.2/MANUAL.md 0000664 0000000 0000000 00000027366 12514377576 0020560 0 ustar 00root root 0000000 0000000 # Contextmenu manual This file provides information for plugin and skin developers. The Contextmenu plugin can be extended by other plugins; new menus can be created and existing menus manipulated. For basic installation information please see the [README](./README.md). - [Creating a new Contextmenu](#creating-a-new-contextmenu) - [Working with an existing Contextmenu](#working-with-an-existing-contextmenu) - [Events](#events) - [Contextmenu and skins](#contextmenu-and-skins) ## Creating a new Contextmenu By default Contextmenu is added to the `mail` and `addressbook` tasks in Roundcube. It can be added to other tasks by calling the PHP function `include_plugin()` like this `$this->include_plugin('contextmenu');` from inside your plugin. This function checks if the Contextmenu plugin is available and loads it if possible. The JavaScript function `rcm_callbackmenu_init()` creates the Contextmenu object. If the Contextmenu functions are enabled in the UI then the `rcmail.env.contextmenu` variable JavaScript will be set to true. Setting this variable to false will disable all context menus. ```js var menu = rcm_callbackmenu_init(props, events); ``` The functions takes 2 parameters: `props` (required) JSON object: * menu_name - (string) required - A friendly name for the Contextmenu, it is also used as the ID for the Contextmenu element. * menu_source - (string or array) required - See [Menu sources](#menu-sources) for details. * list_object - (object) optional - If Contextmenu is used on a Roundcube list object then that list object should be set here (e.g. `rcmail.message_list`), set to `null` if using Contextmenu on another element. It is set to `rcmail.message_list` by default. * source_class - (string) optional - The CSS class applied to the triggering element, `contextRow` by default. * mouseover_timeout - (int) optional - The delay for displaying submenus on mouseover, set to -1 to disable mouseover. `400` by default. `events` (optional) JSON object. Contextmenu triggers a number of events during execution, for example `command` is tiggered when the user clicks on an item in the menu. Full details of all the events can be found in the [Events](#events) section of this file. This parameters allows a plugin author to attach their own functions to the Contextmenu events, overriding the defaults. Creating a simple Contextmenu looks like this: ```js var menu = rcm_callbackmenu_init( {'menu_name': 'messagelist', 'menu_source': '#messagetoolbar'}, {'beforeactivate': function(p) { rcmail.env.contextmenu_selection = p.ref.list_selection(true); }, 'afteractivate': function(p) { p.ref.list_selection(false, rcmail.env.contextmenu_selection); }}); ``` The Contextmenu must then be attached to the element(s) in the UI. For example: ```js $(el).bind("contextmenu", function(e) { rcm_show_menu(e, obj, source_id, menu); }); ``` The `rcm_show_menu` displays a Contextmenu on the screen. It has the following parameters: * e - (event) The JS event object * obj - (object) The object the Contextmenu is active on (typically `this`) * source_id - (string) The object ID used by core function. When using the Contextmenu on a Roundcube list object then the ID can be retrieved from the object, like this: ```js if (uid = list_object.get_row_uid(this)) { rcm_show_menu(e, this, uid, menu); } ``` The ID can also be extracted from the originial function call, like this: ```js if (source.attr('onclick') && source.attr('onclick').match(rcmail.context_menu_command_pattern)) { rcm_show_menu(e, this, RegExp.$2, menu); } ``` * menu - (object) The menu object as created by `rcm_callbackmenu_init` ## Menu sources The menu_source parameter can be a string (for building the Contextmenu from a single source) or an array of jQuery selectors. To add custom elements to the Contextmenu a menu element must first be added to the IU, for example: ```php $li = ''; $li .= html::tag('li', null, $this->api->output->button(array('command' => 'plugin.myplugin.command1', 'type' => 'link', 'class' => 'myclass1', 'label' => 'myplugin.command1'))); $li .= html::tag('li', null, $this->api->output->button(array('command' => 'plugin.myplugin.command2', 'type' => 'link', 'class' => 'myclass2', 'label' => 'myplugin.command2'))); $li .= html::tag('li', null, $this->api->output->button(array('command' => 'plugin.myplugin.command3', 'type' => 'link', 'class' => 'myclass3', 'label' => 'myplugin.command3'))); $out = html::tag('ul', array('id' => 'mymenu'), $li); $this->api->output->add_footer(html::div(array('style' => 'display: none;'), $out)); ``` The Contextmenu can then be invoked like this: ```js var menu = rcm_callbackmenu_init({menu_name: 'mymenu', menu_source: '#mymenu'}); ``` A JSON object can also be used instead of an element selector to add simple elements to the Contextmenu. For example: ```js var menu = rcm_callbackmenu_init({menu_name: 'mymenu', menu_source: ['#mymenu', {lable: 'extra item', command: 'plugin.myplugin.command', props: '', class: 'myclass'}]}); ``` The JSON object can have: * `label` (string) required - text for the menu element * `command` (string) required - the Roundcube command to execute on click * `props` (string) optional - arguments to pass to the Roundcube command * `classes` (string) optional - classes to apply to the menu element ## Working with an existing Contextmenu A global event `contextmenu_init` is triggered when a new Contextmenu is initialised so other plugins can interact with it. ```js rcmail.addEventListener('contextmenu_init', function(menu) { // identify the folder list context menu if (menu.menu_name == 'folderlist') { // add a shortcut to the folder management screen to the end of the menu menu.menu_source.push({label: 'Manage folders', command: 'folders', props: '', classes: 'managefolders'}); // make sure this new shortcut is always active menu.addEventListener('activate', function(p) { if (p.command == 'folders') { return true; } }); } }); ``` The Contextmnu object is passed to the function allowing properities to be manipulated and/or new events to be attached. By default the following menus are created: On the mail screen: * messagelist - attached to rows in the message list * folderlist - attached to entries in the folder list On the message composing screen: * composeto - attached to contacts in the contacts search widget On the address book screen: * contactlist - attached to rows in the contacts list * abooklist - attached to addressbooks and groups To prevent an element from appearing in a Contextmenu give it the class `rcm_ignore`. To make sure an element in the Contextmenu is always active give it the class `rcm_active`. The environmental variable `rcmail.env.context_menu_source_id` contains the ID of the specific element that the Contextmenu was triggered on, this is the `source_id` passed to `rcm_show_menu` ## Events The following events are triggered by Contextmenu: `init` - Triggered once the Contextmenu object has been initalized * ref - The Contextmenu object `beforecommand` - Triggered when an element in the menu is clicked * ref - The Contextmenu object * el - The HTML object being clicked * command - The Roundcube command to run * args - The arguments being passed to the Roundcube command This function can return the following in a JSON object: * abort - Boolean, abort the default command execution, other events like `command` and `aftercommand` will not be executed * result - Result of the command, if abort if true this is returned to the client `command` - Triggered when an element in the menu is clicked * ref - The Contextmenu object * el - The HTML object being clicked * command - The Roundcube command to run * args - The arguments being passed to the Roundcube command * evt - The JS event object This function can return the result of the command to pass back to the client By default the following function is used: ```js function(p) { if (!$(p.el).hasClass('active')) return false; if (p.ref.list_object) { var prev_display_next = rcmail.env.display_next; if (!(p.ref.list_object.selection.length == 1 && p.ref.list_object.in_selection(rcmail.env.context_menu_source_id))) rcmail.env.display_next = false; var prev_sel = p.ref.list_selection(true); } // enable the required command var prev_command = rcmail.commands[p.command]; rcmail.enable_command(p.command, true); var result = rcmail.command(p.command, p.args, p.el, p.evt); rcmail.enable_command(p.command, prev_command); if (p.ref.list_object) { p.ref.list_selection(false, prev_sel); rcmail.env.display_next = prev_display_next; } if ($.inArray(p.command, rcmail.context_menu_overload_commands) >= 0) { rcmail.context_menu_commands[p.command] = rcmail.commands[p.command]; rcmail.enable_command(p.command, true); } return result; } ``` The Contextmenu works by faking a message selection and calling the normal Roundcube command before putting everything back to normal. `aftercommand` - Triggered when an element in the menu is clicked * ref - The Contextmenu object * el - The HTML object being clicked * command - The Roundcube command to run * args - The arguments being passed to the Roundcube command `beforeactivate` - Triggered when a Contextmenu is displayed * ref - The Contextmenu object * source - The element the Contextmenu has been triggered on This function can return the following in a JSON object: * abort - Boolean, abort the default activation process, other events like `activate` and `afteractivat` will not be executed * show - Boolean, show the menu or not `activate` - Triggered when a Contextmenu is displayed, a separate event is triggered for each menu item * el - The menu element being activated * btn - The ID of the button in the UI on which the menu element is based * source - The element the Contextmenu has been triggered on * command - The command the menu element executes * enabled - Boolean, if the menu element is active or not This function can return a boolean value: true to activate the element, false to disable it `afteractivate` - Triggered when a Contextmenu is displayed * ref - The Contextmenu object * source - The element the Contextmenu has been triggered on `insertitem` - Triggered each time an item is added to a Contextmenu * item - The HTML object to be added to the menu For example permanently deactivating the delete option on the message list Contextmenu could be done like this: ```js rcmail.addEventListener('contextmenu_init', function(menu) { if (menu.menu_name == 'messagelist') { menu.addEventListener('activate', function(p) { var is_delete = false; $.each(rcmail.buttons['delete'], function() { if (this.id == p.btn) { is_delete = true; return false; } }); return is_delete ? false : null; }); } }); ``` ## Contextmenu and skins In the plugin folder there is a skins folder, and inside that there is a folder for each skin. Two files are needed for each skin: contextmenu.css - CSS for the menu, and functions.js containing the JavaScript to create Contextmenus in the skin. This plugin provides some helper functions for adding the default menus to the UI, they are: `rcm_listmenu_init()` for attaching a Contextmenu to a Roundcube list object, `rcm_foldermenu_init()` for attaching a Contextmenu to the folder list on the mail screen, and `rcm_abookmenu_init()` for attaching a Contextmenu to the address book and groups list on the address book screen. Each function expects the same 3 parameters: * The HTML object or jQuery selector of the element to attach to. * A props object, see [Creating a new Contextmenu](#creating-a-new-contextmenu) * An events object, see [Events](#events) Contextmenus must be defined separately for each skin because they are built from the toolbar elements in the UI which may have different IDs as well as different construction on each skin. Roundcube-Plugin-Context-Menu-2.1.2/README.md 0000664 0000000 0000000 00000002030 12514377576 0020515 0 ustar 00root root 0000000 0000000 Roundcube Webmail ContextMenu ============================= This plugin creates contextmenus for various parts of Roundcube using commands from the toolbars. License ------- This plugin is released under the [GNU General Public License Version 3+][gpl]. Even if skins might contain some programming work, they are not considered as a linked part of the plugin and therefore skins DO NOT fall under the provisions of the GPL license. See the README file located in the core skins folder for details on the skin license. Install ------- * Place this plugin folder into plugins directory of Roundcube * Add contextmenu to $config['plugins'] in your Roundcube config **NB:** When downloading the plugin from GitHub you will need to create a directory called contextmenu and place the files in there, ignoring the root directory in the downloaded archive. [rcplugrepo]: http://plugins.roundcube.net/packages/johndoh/contextmenu [releases]: http://github.com/JohnDoh/Roundcube-Plugin-Context-Menu/releases [gpl]: http://www.gnu.org/licenses/gpl.html Roundcube-Plugin-Context-Menu-2.1.2/composer.json 0000664 0000000 0000000 00000001321 12514377576 0021762 0 ustar 00root root 0000000 0000000 { "name": "johndoh/contextmenu", "description": "Adds context menus with common tasks to various parts of Roundcube", "keywords": ["context","menu","right-click"], "homepage": "http://github.com/JohnDoh/Roundcube-Plugin-Context-Menu/", "license": "GPL-3.0", "type": "roundcube-plugin", "version": "2.1.2", "authors": [ { "name": "Philip Weir", "email": "roundcube@tehinterweb.co.uk", "role": "Developer" } ], "repositories": [ { "type": "composer", "url": "http://plugins.roundcube.net" } ], "require": { "php": ">=5.2.1", "roundcube/plugin-installer": ">=0.1.2" }, "extra": { "roundcube": { "min-version": "1.1-beta" } } } Roundcube-Plugin-Context-Menu-2.1.2/contextmenu.js 0000664 0000000 0000000 00000101275 12514377576 0022160 0 ustar 00root root 0000000 0000000 /** * ContextMenu plugin script * * @licstart The following is the entire license notice for the * JavaScript code in this file. * * Copyright (C) 2009-2014 Philip Weir * * The JavaScript code in this page is free software: you can redistribute it * and/or modify it under the terms of the GNU General Public License * as published by the Free Software Foundation, either version 3 of * the License, or (at your option) any later version. * * @licend The above is the entire license notice * for the JavaScript code in this file. */ rcube_webmail.prototype.context_menu_skip_commands = new Array('mail-checkmail', 'mail-compose', 'addressbook-add', 'addressbook-import', 'addressbook-advanced-search', 'addressbook-search-create'); rcube_webmail.prototype.context_menu_overload_commands = new Array('move', 'copy'); rcube_webmail.prototype.context_menu_commands = new Array(); rcube_webmail.prototype.context_menu_popup_menus = new Array(); rcube_webmail.prototype.context_menu_popup_commands = {}; rcube_webmail.prototype.context_menu_command_pattern = /rcmail\.command\(\'([^\']+)\',\s?\'([^\']*)\'/; function rcm_listmenu_init(row, props, events) { if (!events) events = {}; var menu = rcm_callbackmenu_init(props, $.extend({ 'beforeactivate': function(p) { rcmail.env.contextmenu_selection = p.ref.list_selection(true); }, 'afteractivate': function(p) { p.ref.menu_selection = p.ref.list_object.get_selection(); p.ref.list_selection(false, rcmail.env.contextmenu_selection); } }, events)); var list_object = props.list_object ? props.list_object : rcmail.message_list; $("#" + row).bind("contextmenu", function(e) { if (uid = list_object.get_row_uid(this)) { rcm_show_menu(e, this, uid, menu); } }); } function rcm_foldermenu_init(el, props, events) { if (!events) events = {}; var menu = rcm_callbackmenu_init($.extend({'menu_name': 'folderlist', 'list_object': null}, props), $.extend({ 'activate': function(p) { if ($.inArray(p.command, Array('expunge', 'purge')) >= 0) { if (rcmail.env.context_menu_source_id == rcmail.env.mailbox && rcm_check_button_state(p.btn, true)) { $(p.el).addClass('active').removeClass('disabled'); } else { $(p.el).addClass('disabled').removeClass('active'); } } if (p.command == 'plugin.contextmenu.readfolder') { if ($(p.source).children('a:first').has('span.unreadcount').length > 0) { $(p.el).addClass('active').removeClass('disabled'); } else { $(p.el).addClass('disabled').removeClass('active'); } } } }, events)); $(el).bind("click",function(e) { // hide menu when changing folder menu.hide(e); }) .bind("contextmenu", function(e) { var source = $(this).children('a'); // remove focus (and keyboard nav highlighting) from A source.blur(); if (source.attr('onclick') && source.attr('onclick').match(rcmail.context_menu_command_pattern)) { rcm_show_menu(e, this, RegExp.$2, menu); } }); } function rcm_abookmenu_init(el, props, events) { if (!events) events = {}; var menu = rcm_callbackmenu_init($.extend({'menu_name': 'abooklist'}, props), $.extend({ 'beforeactivate': function(p) { p.ref.container.find('li.submenu').remove(); }, 'activate': function(p) { var ids = rcmail.env.context_menu_source_id.split(':', 2); cur_source = ids[0]; if (p.command == 'group-create') { // addressbook if ($(p.source).hasClass('addressbook') && rcmail.env.address_sources[cur_source].groups && !rcmail.env.address_sources[cur_source].readonly) { $(p.el).addClass('active').removeClass('disabled'); } else { $(p.el).addClass('disabled').removeClass('active'); } } else if (p.command == 'group-rename' || p.command == 'group-delete') { // group if ($(p.source).hasClass('contactgroup') && !rcmail.env.address_sources[cur_source].readonly) { $(p.el).addClass('active').removeClass('disabled'); } else { $(p.el).addClass('disabled').removeClass('active'); } } else if (p.command == 'search-delete') { // saved search if ($(p.source).hasClass('contactsearch')) { $(p.el).addClass('active').removeClass('disabled'); } else { $(p.el).addClass('disabled').removeClass('active'); } } }, 'beforecommand': function(p) { if (!$(p.el).hasClass('active')) return; var ids = rcmail.env.context_menu_source_id.split(':', 2); cur_source = ids[0]; rcmail.env.source = cur_source; }, 'command': function(p) { if (!$(p.el).hasClass('active')) return; var prev_source = rcmail.env.source; var prev_group = rcmail.env.group; var result = false; var ids = rcmail.env.context_menu_source_id.split(':', 2); cur_source = ids[0]; cur_id = ids[1]; rcmail.env.source = cur_source; rcmail.env.group = cur_id; // enable the required command var prev_command = rcmail.commands[p.command]; rcmail.enable_command(p.command, true); switch (p.command) { case 'group-rename': result = rcmail.command(p.command, p.args, p.el); // callback requires target is selected rcmail.enable_command('listgroup', true); rcmail.env.source = prev_source rcmail.env.group = prev_group; prev_source = cur_source; prev_group = cur_id; rcmail.command('listgroup', {'source': prev_source, 'id': prev_group}, p.el, p.evt); rcmail.enable_command('listgroup', false); break; case 'group-delete': result = rcmail.command(p.command, p.args, p.el); break; case 'search-delete': var result = false; if ($(p.ref.selected_object).children('a').attr('rel')) { var prev_search_id = rcmail.env.search_id; var prev_search_request = rcmail.env.search_request; var prev_command = rcmail.commands[p.command]; rcmail.env.search_request = true; rcmail.env.search_id = $(p.ref.selected_object).children('a').attr('rel').replace('S', ''); rcmail.enable_command(p.command, true); result = rcmail.command(p.command, p.args, p.el, p.evt); rcmail.enable_command(p.command, prev_command); rcmail.env.search_request = prev_search_request; rcmail.env.search_id = prev_search_id; } break; default: // enable the required command var prev_command = rcmail.commands[p.command]; rcmail.enable_command(p.command, true); var result = rcmail.command(p.command, p.args, p.el, p.evt); rcmail.enable_command(p.command, prev_command); break; } rcmail.enable_command(p.command, prev_command); rcmail.env.source = prev_source; rcmail.env.group = prev_group; return result; }, 'aftercommand': function(p) { var ids = rcmail.env.context_menu_source_id.split(':', 2); cur_source = ids[0]; if (rcmail.env.source = cur_source) rcmail.command('list', cur_source, p.el); } }, events)); $(el).bind("click",function(e) { // hide menu when changing address book menu.hide(e); }) .bind("contextmenu",function(e) { var source = $(this).children('a'); // remove focus (and keyboard nav highlighting) from A source.blur(); if (source.attr('rel') && source.attr('rel').match(/([A-Z0-9\-_]+(:[A-Z0-9\-_]+)?)/i)) { rcm_show_menu(e, this, RegExp.$1, menu); } }); } function rcm_callbackmenu_init(props, events) { var std_events = { 'command': function(p) { if (!$(p.el).hasClass('active')) return; if (p.ref.list_object) { var prev_display_next = rcmail.env.display_next; if (!(p.ref.list_object.selection.length == 1 && p.ref.list_object.in_selection(rcmail.env.context_menu_source_id))) rcmail.env.display_next = false; var prev_sel = p.ref.list_selection(true); } // enable the required command var prev_command = rcmail.commands[p.command]; rcmail.enable_command(p.command, true); var result = rcmail.command(p.command, p.args, p.el, p.evt); rcmail.enable_command(p.command, prev_command); if (p.ref.list_object) { p.ref.list_selection(false, prev_sel); rcmail.env.display_next = prev_display_next; } if ($.inArray(p.command, rcmail.context_menu_overload_commands) >= 0) { rcmail.context_menu_commands[p.command] = rcmail.commands[p.command]; rcmail.enable_command(p.command, true); } return result; }, 'activate': function(p) { $(p.el).addClass(p.enabled ? 'active' : 'disabled'); } } if (events) $.extend(std_events, events); if (!rcmail.env.contextmenus[props.menu_name]) { var menu = new rcube_context_menu(props); $.each(std_events, function(trigger, func) { menu.addEventListener(trigger, function(p) { return func(p); }); }); menu.init(); rcmail.env.contextmenus[props.menu_name] = menu; } else { var menu = rcmail.env.contextmenus[props.menu_name]; } return menu; } function rcm_show_menu(e, obj, id, menu) { // if contextmenus have been disabled then show browser context menu as normal if (!rcmail.env.contextmenu) return true; e.preventDefault(); e.cancelBubble = true; if (e.stopPropagation) e.stopPropagation(); // hide any other open menus for (var i = 0; i < rcmail.menu_stack.length; i++) { rcmail.hide_menu(rcmail.menu_stack[i], e); } rcmail.env.context_menu_source_id = id; menu.show(obj, e); } function rcm_hide_menu(e, sub_only) { $.each($(sub_only ? '.rcmsubmenu' : 'div.contextmenu'), function() { if ($(this).is(':visible')) { $(this).hide(); rcmail.triggerEvent('menu-close', { name: $(this).attr('id'), props:{ menu: $(this).attr('id') }, originalEvent: e }); } }); // close popup menus opened by the contextmenu for (var i = rcmail.context_menu_popup_menus.length - 1; i >= 0; i--) { rcmail.hide_menu(rcmail.context_menu_popup_menus[i], e); rcmail.context_menu_popup_menus.pop(); } } function rcube_context_menu(p) { this.menu_name = null; this.menu_source = null; this.list_object = rcmail.message_list; this.source_class = 'contextRow'; this.mouseover_timeout = 400; this.is_submenu = false; this.parent_menu = this; this.parent_object = null; this.selected_object = null this.container = null; this.original_selection = new Array(); this.menu_selection = new Array(); this.submenus = new Array(); this.timers = new Array(); // overwrite default parameters if (p && typeof p === 'object') for (var n in p) this[n] = p[n]; var ref = this; this.init = function() { if (!this.container) { rcmail.triggerEvent('contextmenu_init', this); this.container = $('
'); this.container.addClass('contextmenu popupmenu'); this.container.addClass(this.is_submenu ? 'rcmsubmenu' : 'rcmmainmenu'); var rows = [], ul = $('