comparison plugins/contextmenu/contextmenu.js @ 0:1e000243b222

vanilla 1.3.3 distro, I hope
author Charlie Root
date Thu, 04 Jan 2018 15:50:29 -0500
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:1e000243b222
1 /**
2 * ContextMenu plugin script
3 *
4 * @licstart The following is the entire license notice for the
5 * JavaScript code in this file.
6 *
7 * Copyright (C) 2009-2014 Philip Weir
8 *
9 * The JavaScript code in this page is free software: you can redistribute it
10 * and/or modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation, either version 3 of
12 * the License, or (at your option) any later version.
13 *
14 * @licend The above is the entire license notice
15 * for the JavaScript code in this file.
16 */
17
18 rcube_webmail.prototype.context_menu_skip_commands = new Array('mail-checkmail', 'mail-compose', 'addressbook-add', 'addressbook-import', 'addressbook-advanced-search', 'addressbook-search-create');
19 rcube_webmail.prototype.context_menu_overload_commands = new Array('move', 'copy');
20 rcube_webmail.prototype.context_menu_commands = new Array();
21 rcube_webmail.prototype.context_menu_popup_menus = new Array();
22 rcube_webmail.prototype.context_menu_popup_commands = {};
23
24 rcube_webmail.prototype.context_menu_command_pattern = /rcmail\.command\(\'([^\']+)\',\s?\'((?:\\\'|[^\'])*)\'/;
25
26 function rcm_listmenu_init(row, props, events) {
27 if (!events)
28 events = {};
29
30 var menu = rcm_callbackmenu_init(props, $.extend({
31 'beforeactivate': function(p) {
32 rcmail.env.contextmenu_selection = p.ref.list_selection(true);
33 },
34 'afteractivate': function(p) {
35 p.ref.menu_selection = p.ref.list_object.get_selection();
36 p.ref.list_selection(false, rcmail.env.contextmenu_selection);
37 }
38 }, events));
39
40 var list_object = props.list_object ? props.list_object : rcmail.message_list;
41 $("#" + row).on("contextmenu", function(e) {
42 if (uid = list_object.get_row_uid(this)) {
43 rcm_show_menu(e, this, uid, menu);
44 }
45 });
46 }
47
48 function rcm_foldermenu_init(el, props, events) {
49 if (!events)
50 events = {};
51
52 var menu = rcm_callbackmenu_init($.extend({'menu_name': 'folderlist', 'list_object': null}, props), $.extend({
53 'beforeactivate': function(p) {
54 if (rcmail.env.contextmenu_messagecount_request) {
55 rcmail.env.contextmenu_messagecount_request.abort();
56 }
57 rcmail.env.contextmenu_messagecount_request = null;
58 },
59 'activate': function(p) {
60 if ($.inArray(p.command, Array('expunge', 'purge', 'mark-all-read')) >= 0) {
61 // disable the commands by default
62 $(p.el).addClass('disabled').removeClass('active');
63
64 // if menu is opened on current folder (or special mark-all-read command) then enable the commands same as in UI
65 if ((rcmail.env.context_menu_source_id == rcmail.env.mailbox || p.command == 'mark-all-read') && rcm_check_button_state(p.btn, true)) {
66 $(p.el).addClass('active').removeClass('disabled');
67 }
68 // if menu is opened on difference folder then get message count for the folder
69 else if (rcmail.env.context_menu_source_id != rcmail.env.mailbox && !rcmail.env.contextmenu_messagecount_request) {
70 // folder check called async to prevent slowdown on menu load
71 rcmail.env.contextmenu_messagecount_request = $.ajax({
72 type: 'POST', url: rcmail.url('plugin.contextmenu.messagecount'), data: {'_mbox': rcmail.env.context_menu_source_id}, dataType: 'json', async: true,
73 success: function(data) {
74 if (data.messagecount > 0 && $('#rcm_folderlist').is(':visible')) {
75 // override the environment to check if commands should be abled
76 var temp_exists = rcmail.env.exists;
77 var temp_mailbox = rcmail.env.mailbox;
78 rcmail.env.exists = data.messagecount;
79 rcmail.env.mailbox = rcmail.env.context_menu_source_id;
80
81 $('#rcm_folderlist').find('a.cmd_expunge').addClass('active').removeClass('disabled');
82 if (rcmail.purge_mailbox_test()) {
83 $('#rcm_folderlist').find('a.cmd_purge').addClass('active').removeClass('disabled');
84 }
85
86 rcmail.env.exists = temp_exists;
87 rcmail.env.mailbox = temp_mailbox;
88 }
89 }
90 });
91 }
92 }
93 },
94 'beforecommand': function(p) {
95 if (rcmail.env.context_menu_source_id != rcmail.env.mailbox && $.inArray(p.command, Array('expunge', 'purge')) >= 0) {
96 var result = rcmail[p.command + '_mailbox'](rcmail.env.context_menu_source_id);
97
98 // update the unread count and trash icon
99 if (p.command == 'purge' && result !== false) {
100 rcmail.set_unread_count(rcmail.env.context_menu_source_id, 0, false);
101
102 if (rcmail.env.context_menu_source_id == rcmail.env.trash_mailbox)
103 rcmail.set_trash_count(0);
104 }
105
106 return {'abort': true, 'result': true};
107 }
108 else if (rcmail.env.context_menu_source_id != rcmail.env.mailbox && p.command == 'mark-all-read') {
109 rcmail.mark_all_read(rcmail.env.context_menu_source_id);
110 return {'abort': true, 'result': true};
111 }
112 }
113 }, events));
114
115 $(el).click(function(e) {
116 // hide menu when changing folder
117 menu.hide(e);
118 })
119 .on("contextmenu", function(e) {
120 var source = $(this).children('a');
121
122 // remove focus (and keyboard nav highlighting) from A
123 source.blur();
124
125 if (source.attr('rel') && source.attr('onclick') && source.attr('onclick').match(rcmail.context_menu_command_pattern)) {
126 rcm_show_menu(e, this, source.attr('rel'), menu);
127 }
128 });
129 }
130
131 function rcm_abookmenu_init(el, props, events) {
132 if (!events)
133 events = {};
134
135 var menu = rcm_callbackmenu_init($.extend({'menu_name': 'abooklist'}, props), $.extend({
136 'beforeactivate': function(p) {
137 p.ref.container.find('li.submenu').remove();
138 },
139 'activate': function(p) {
140 var ids = rcmail.env.context_menu_source_id.split(':', 2);
141 cur_source = ids[0];
142
143 if (p.command == 'group-create') {
144 // addressbook
145 if ($(p.source).hasClass('addressbook') && rcmail.env.address_sources[cur_source].groups && !rcmail.env.address_sources[cur_source].readonly) {
146 $(p.el).addClass('active').removeClass('disabled');
147 }
148 else {
149 $(p.el).addClass('disabled').removeClass('active');
150 }
151 }
152 else if (p.command == 'group-rename' || p.command == 'group-delete') {
153 // group
154 if ($(p.source).hasClass('contactgroup') && !rcmail.env.address_sources[cur_source].readonly) {
155 $(p.el).addClass('active').removeClass('disabled');
156 }
157 else {
158 $(p.el).addClass('disabled').removeClass('active');
159 }
160 }
161 else if (p.command == 'search-delete') {
162 // saved search
163 if ($(p.source).hasClass('contactsearch')) {
164 $(p.el).addClass('active').removeClass('disabled');
165 }
166 else {
167 $(p.el).addClass('disabled').removeClass('active');
168 }
169 }
170 },
171 'command': function(p) {
172 if (!$(p.el).hasClass('active'))
173 return;
174
175 var prev_source = rcmail.env.source;
176 var prev_group = rcmail.env.group;
177 var result = false;
178
179 var ids = rcmail.env.context_menu_source_id.split(':', 2);
180 cur_source = ids[0];
181 cur_id = ids[1];
182
183 rcmail.env.source = cur_source;
184 rcmail.env.group = cur_id;
185
186 // enable the required command
187 var prev_command = rcmail.commands[p.command];
188 rcmail.enable_command(p.command, true);
189
190 switch (p.command) {
191 case 'search-delete':
192 var result = false;
193
194 if ($(p.ref.selected_object).children('a').attr('rel')) {
195 var prev_search_id = rcmail.env.search_id;
196 var prev_search_request = rcmail.env.search_request;
197 rcmail.env.search_request = true;
198 rcmail.env.search_id = $(p.ref.selected_object).children('a').attr('rel').replace('S', '');
199
200 result = rcmail.command(p.command, p.args, p.el, p.evt);
201
202 rcmail.env.search_request = prev_search_request;
203 rcmail.env.search_id = prev_search_id;
204 }
205 break;
206 default:
207 result = rcmail.command(p.command, p.args, p.el, p.evt);
208 break;
209 }
210
211 rcmail.enable_command(p.command, prev_command);
212 rcmail.env.source = prev_source;
213 rcmail.env.group = prev_group;
214
215 return result;
216 }
217 }, events));
218
219 $(el).click(function(e) {
220 // hide menu when changing address book
221 menu.hide(e);
222 })
223 .on("contextmenu",function(e) {
224 var source = $(this).children('a');
225
226 // remove focus (and keyboard nav highlighting) from A
227 source.blur();
228
229 if (source.attr('rel') && source.attr('rel').match(/([A-Z0-9\-_]+(:[A-Z0-9\-_]+)?)/i)) {
230 rcm_show_menu(e, this, RegExp.$1, menu);
231 }
232 });
233 }
234
235 function rcm_callbackmenu_init(props, events) {
236 var std_events = {
237 'command': function(p) {
238 if (!$(p.el).hasClass('active'))
239 return;
240
241 if (p.ref.list_object) {
242 var prev_display_next = rcmail.env.display_next;
243
244 if (!(p.ref.list_object.selection.length == 1 && p.ref.list_object.in_selection(rcmail.env.context_menu_source_id)))
245 rcmail.env.display_next = false;
246
247 var prev_sel = p.ref.list_selection(true);
248 }
249
250 // enable the required command
251 var prev_command = rcmail.commands[p.command];
252 rcmail.enable_command(p.command, true);
253 var result = rcmail.command(p.command, p.args, p.el, p.evt);
254 rcmail.enable_command(p.command, prev_command);
255
256 if (p.ref.list_object) {
257 p.ref.list_selection(false, prev_sel);
258 rcmail.env.display_next = prev_display_next;
259 }
260
261 if ($.inArray(p.command, rcmail.context_menu_overload_commands) >= 0) {
262 rcmail.context_menu_commands[p.command] = rcmail.commands[p.command];
263 rcmail.enable_command(p.command, true);
264 }
265
266 return result;
267 },
268 'activate': function(p) {
269 $(p.el).addClass(p.enabled ? 'active' : 'disabled');
270 }
271 }
272
273 if (events)
274 $.extend(std_events, events);
275
276 if (!rcmail.env.contextmenus[props.menu_name]) {
277 var menu = new rcube_context_menu(props);
278 $.each(std_events, function(trigger, func) {
279 menu.addEventListener(trigger, function(p) { return func(p); });
280 });
281 menu.init();
282 rcmail.env.contextmenus[props.menu_name] = menu;
283 }
284 else {
285 var menu = rcmail.env.contextmenus[props.menu_name];
286 }
287
288 return menu;
289 }
290
291 function rcm_show_menu(e, obj, id, menu) {
292 // if contextmenus have been disabled then show browser context menu as normal
293 if (!rcmail.env.contextmenu)
294 return true;
295
296 e.preventDefault();
297 e.cancelBubble = true;
298 if (e.stopPropagation)
299 e.stopPropagation();
300
301 // hide any other open menus
302 for (var i = 0; i < rcmail.menu_stack.length; i++) {
303 rcmail.hide_menu(rcmail.menu_stack[i], e);
304 }
305
306 rcmail.env.context_menu_source_id = id;
307 menu.show(obj, e);
308 }
309
310 function rcm_hide_menu(e, sub_only) {
311 $.each($(sub_only ? '.rcmsubmenu' : 'div.contextmenu'), function() {
312 if ($(this).is(':visible')) {
313 $(this).hide();
314 rcmail.triggerEvent('menu-close', { name: $(this).attr('id'), props:{ menu: $(this).attr('id') }, originalEvent: e });
315 }
316 });
317
318 // close popup menus opened by the contextmenu
319 for (var i = rcmail.context_menu_popup_menus.length - 1; i >= 0; i--) {
320 rcmail.hide_menu(rcmail.context_menu_popup_menus[i], e);
321 rcmail.context_menu_popup_menus.pop();
322 }
323 }
324
325 function rcube_context_menu(p) {
326 this.menu_name = null;
327 this.menu_source = null;
328 this.list_object = rcmail.message_list;
329 this.source_class = 'contextRow';
330 this.mouseover_timeout = 400;
331
332 this.is_submenu = false;
333 this.parent_menu = this;
334 this.parent_object = null;
335 this.selected_object = null
336 this.container = null;
337 this.original_selection = new Array();
338 this.menu_selection = new Array();
339 this.submenus = new Array();
340 this.timers = new Array();
341
342 // overwrite default parameters
343 if (p && typeof p === 'object')
344 for (var n in p)
345 this[n] = p[n];
346
347 var ref = this;
348
349 this.init = function() {
350 if (!this.container) {
351 rcmail.triggerEvent('contextmenu_init', this);
352
353 this.container = $('<div id="rcm_'+ this.menu_name +'" style="display: none;"></div>');
354 this.container.addClass('contextmenu popupmenu');
355 this.container.addClass(this.is_submenu ? 'rcmsubmenu' : 'rcmmainmenu');
356
357 var rows = [], ul = $('<ul role="menu">'),
358 li = $('<li>'), link = $('<a>'), span = $('<span>');
359
360 ul.addClass('toolbarmenu iconized');
361 li.attr('role', 'menuitem');
362
363 link.attr('href', '#');
364 link.addClass('icon active');
365 link.attr('role', 'button');
366 link.attr('tabindex', '-1');
367 link.attr('aria-disabled', 'true');
368
369 span.addClass('icon');
370
371 // loop over possible menu elements and build settings object
372 sources = typeof this.menu_source == 'string' ? [this.menu_source] : this.menu_source;
373 this.menu_source = {}
374 $.each(sources, function(i) {
375 var source_elements;
376 if (typeof sources[i] == 'string') {
377 ref.menu_source[sources[i]] = {
378 'toggle': !$(sources[i]).is(':visible')
379 };
380 source_elements = $(sources[i]).children();
381 }
382 else {
383 ref.menu_source[i] = {
384 'toggle': false
385 };
386 source_elements = $(sources[i]);
387 }
388
389 ul.attr('aria-labelledby', $(sources[i]).attr('aria-labelledby'));
390
391 $.each(source_elements, function() {
392 var elem, command, args;
393
394 if ($(this).is('a')) {
395 elem = $(this).clone();
396 }
397 else if ($(this).is('span') && $(this).children().length == 2) {
398 elem = $(this).children(':first').clone();
399
400 if ($(this).children(':last').attr('onclick').match(rcmail.context_menu_popup_pattern)) {
401 $(elem).attr('onclick', $(this).children(':last').attr('onclick'));
402 }
403 }
404 else if ($(this).is('li') && $(this).children('a').length == 1) {
405 elem = $(this).children('a').clone();
406
407 if (!elem.attr('onclick') || !elem.attr('onclick').match(rcmail.context_menu_command_pattern))
408 return;
409 }
410 else if ($(this).parent().is('a')) {
411 elem = $(this).parent().clone();
412 }
413 else if (this.command && this.label) {
414 elem = $('<a>').attr('href', '#')
415 .attr('id', 'rcmjs')
416 .attr('onclick', "return rcmail.command('"+ this.command +"','"+ this.props +"',this,event)")
417 .addClass(this.classes)
418 .html(this.label);
419 }
420 else {
421 return;
422 }
423
424 // skip any element that does not look like a Roundcube button
425 if (!elem.attr('onclick')) {
426 return;
427 }
428
429 if (elem.attr('onclick').match(rcmail.context_menu_command_pattern)) {
430 command = RegExp.$1;
431 args = RegExp.$2;
432 }
433
434 // skip elements we don't need
435 if ($.inArray(rcmail.env.task + '-' + command, rcmail.context_menu_skip_commands) > -1 || elem.hasClass('rcm_ignore')) {
436 return;
437 }
438
439 var a = link.clone(), row = li.clone();
440
441 // add command name element
442 tmp = span.clone();
443 tmp.text($.trim(elem.text()).length > 0 ? $.trim(elem.text()) : elem.attr('title'));
444 tmp.addClass(elem.children('span').attr('class'));
445 a.append(tmp);
446 a.addClass(elem.attr('class'));
447 a.removeClass('button').removeClass('disabled');
448 a.addClass('rcm_elem_' + elem.attr('id'));
449
450 if (elem.attr('onclick').match(rcmail.context_menu_popup_pattern)) {
451 a.data('command', RegExp.$1);
452 a.append($('<span>').addClass('right-arrow'));
453 row.addClass('submenu');
454 a.click(function(e) {
455 if (!$(this).hasClass('active'))
456 return;
457
458 ref.submenu(a, e);
459 return false;
460 });
461
462 if (ref.mouseover_timeout > -1) {
463 a.mouseover(function(e) {
464 if (!$(this).hasClass('active'))
465 return;
466
467 ref.timers['submenu_show'] = window.setTimeout(function(a, e) {
468 ref.submenu(a, e);
469 }, ref.mouseover_timeout, a, e);
470 });
471
472 a.mouseout(function(e) {
473 if (!$(this).hasClass('active'))
474 return;
475
476 $(this).blur(); clearTimeout(ref.timers['submenu_show']);
477 });
478 }
479 }
480 else {
481 a.addClass('cmd_' + command);
482 a.data('command', command);
483 if (elem.attr('target'))
484 a.attr('target', elem.attr('target'));
485
486 a.click(function(e) {
487 if ($(this).parents('.rcmsubmenu').length == 0) {
488 rcm_hide_menu(e, true);
489 clearTimeout(ref.timers['submenu_hide']);
490 }
491
492 var cur_popups = rcmail.menu_stack.length;
493 var result;
494
495 var callback = ref.parent_menu.triggerEvent('beforecommand', {ref: ref, el: this, command: command, args: args});
496 if (!callback || !callback.abort) {
497 result = ref.parent_menu.triggerEvent('command', {ref: ref, el: this, command: command, args: args, evt: e});
498 }
499 else {
500 result = callback.result;
501 }
502
503 if (!callback || !callback.skipaftercommand)
504 ref.parent_menu.triggerEvent('aftercommand', {ref: ref, el: this, command: command, args: args});
505
506 if (rcmail.menu_stack.length > cur_popups) {
507 var popup_name = rcmail.menu_stack[rcmail.menu_stack.length - 1];
508 rcmail.context_menu_popup_menus.push(popup_name);
509
510 // make sure enabled commands match context menu message selection
511 $.each(rcmail.context_menu_popup_commands[popup_name], function(cmd, state) {
512 rcmail.enable_command(cmd, state);
513 });
514 }
515
516 // ensure menu is always hidden after action (for Safari)
517 ref.hide(e);
518
519 return result;
520 });
521
522 if (ref.mouseover_timeout > -1 && !ref.is_submenu) {
523 a.mouseover(function(e) {
524 ref.timers['submenu_hide'] = window.setTimeout(function(e) {
525 rcm_hide_menu(e, true);
526 }, ref.mouseover_timeout, e);
527 });
528
529 a.mouseout(function(e) { clearTimeout(ref.timers['submenu_hide']); });
530 }
531 }
532
533 row.append(a);
534 ref.parent_menu.triggerEvent('insertitem', {item: row});
535 rows.push(row);
536 });
537 });
538
539 ul.append(rows).appendTo(this.container);
540 this.parent_menu.triggerEvent('init', {ref: this});
541 this.container.appendTo($('body'));
542 }
543 };
544
545 this.show = function(obj, e) {
546 if (obj) {
547 this.hide(e);
548 }
549
550 var callback = this.parent_menu.triggerEvent('beforeactivate', {ref: this, source: obj});
551 if (!callback || !callback.abort) {
552 if (obj) {
553 $(obj).addClass(this.source_class);
554 }
555
556 $.each(ref.menu_source, function(id, props) {
557 if (props.toggle) {
558 $(id).parent().show();
559 }
560 });
561
562 $.each(this.container.find('a'), function() {
563 if ($(this).hasClass('rcm_active')) {
564 $(this).addClass('active');
565 }
566 else if (btn = $(this).attr('class').match(/rcm_elem_([a-z0-9]+)/)) {
567 $(this).parent('li')[(btn[1] == 'rcmjs' || $('#' + btn[1]).is(':visible')) ? 'show' : 'hide']();
568 $(this).removeClass('active').removeClass('disabled');
569
570 var enabled = false;
571 if (!rcm_check_button_state(btn[1], false) && (!ref.is_submenu || rcm_check_button_state(btn[1], true))) {
572 enabled = true;
573 }
574
575 var ret = ref.parent_menu.triggerEvent('activate', {el: this, btn: btn[1], source: obj, command: $(this).data('command'), enabled: enabled});
576 if (ret === true) {
577 $(this).addClass('active').removeClass('disabled');
578 }
579 else if (ret === false) {
580 $(this).addClass('disabled').removeClass('active');
581 }
582 }
583 });
584
585 $.each(ref.menu_source, function(id, props) {
586 if (props.toggle) {
587 $(id).parent().hide();
588 }
589 });
590
591 this.parent_menu.triggerEvent('afteractivate', {ref: this, source: obj});
592 }
593
594 // position menu on the screen
595 if (this.is_submenu) {
596 rcmail.element_position(this.container, this.parent_object);
597 }
598 else {
599 this.position(e, this.container);
600 }
601
602 if (!callback || callback.show !== false) {
603 this.selected_object = obj;
604 this.container.show();
605 rcmail.triggerEvent('menu-open', { name: this.container.attr('id'), props:{ menu: this.container.attr('id') }, originalEvent: e });
606 }
607 };
608
609 this.hide = function(e) {
610 // use window.event when e is not defined (legacy support for IE8)
611 var target = e ? e.target : window.event.srcElement;
612
613 if ($('div.contextmenu').is(':visible') && (rcmail.context_menu_popup_menus.length == 0 || $(target).parents('div.contextmenu').length == 0)) {
614 this.selected_object = null;
615 $('.' + this.source_class).removeClass(this.source_class);
616 rcm_hide_menu(e);
617
618 for (var i in rcmail.context_menu_commands) {
619 if (!rcmail.context_menu_commands[i]) {
620 rcmail.enable_command(i, false);
621 }
622 }
623
624 rcmail.context_menu_commands = new Array();
625 }
626 };
627
628 this.submenu = function(link, e) {
629 // use window.event when e is not defined (legacy support for IE8)
630 if (!e)
631 e = window.event;
632
633 if (e) {
634 e.cancelBubble = true;
635 if (e.stopPropagation)
636 e.stopPropagation();
637 }
638
639 rcm_hide_menu(e, true);
640
641 var id = rcmail.gui_containers[$(link).data('command')] ? rcmail.gui_containers[$(link).data('command')].attr('id') : $(link).data('command');
642 if (!this.submenus[id]) {
643 var elem = !$('#' + id).is('ul') ? '#' + id + ' ul' : '#' + id; // check if the container returned is a ul else there should be one directly beneath it
644 this.submenus[id] = new rcube_context_menu({'menu_name': id, 'menu_source': elem, 'parent_menu': this, 'parent_object': link, 'is_submenu': true, 'list_object': this.list_object});
645 this.submenus[id].init();
646 }
647
648 this.submenus[id].show(null, e);
649 };
650
651 this.position = function(e, menu) {
652 // temporarily show element to calculate its size
653 menu.css({left: '-1000px', top: '-1000px'}).show();
654
655 var win = $(window),
656 win_height = win.height(),
657 elem_height = $(menu).height(),
658 elem_width = $(menu).width(),
659 top = e.pageY,
660 left = e.pageX;
661
662 if (top + elem_height > win_height) {
663 top -= elem_height;
664
665 if (top < 0)
666 top = Math.max(0, (win_height - elem_height) / 2);
667 }
668
669 if (left + elem_width > win.width())
670 left -= elem_width;
671
672 menu.hide();
673 menu.css({left: left + 'px', top: top + 'px'});
674 };
675
676 this.list_selection = function(show, prev_sel) {
677 // make the system think no preview pane exists while we do some fake message selects
678 // to enable/disable relevent commands for current selection
679 var prev_contentframe = rcmail.env.contentframe;
680 rcmail.env.contentframe = null;
681
682 if (show) {
683 if (this.list_object.selection.length == 0 || !this.list_object.in_selection(rcmail.env.context_menu_source_id)) {
684 prev_sel = this.list_object.get_selection();
685 this.list_object.highlight_row(rcmail.env.context_menu_source_id, true);
686
687 for (var i in prev_sel)
688 this.list_object.highlight_row(prev_sel[i], true);
689
690 this.list_object.triggerEvent('select');
691 }
692 else {
693 // trigger a select event to update active commands
694 // use case: select multiple message, open contextmenu; open contextmenu on a message not in selection; open contextmenu on selection
695 this.list_object.triggerEvent('select');
696 }
697 }
698 else if (prev_sel) {
699 for (var i in prev_sel)
700 this.list_object.highlight_row(prev_sel[i], true);
701
702 this.list_object.highlight_row(rcmail.env.context_menu_source_id, true);
703 this.list_object.triggerEvent('select');
704 }
705
706 rcmail.env.contentframe = prev_contentframe;
707
708 return prev_sel;
709 };
710
711 this.addEventListener = rcube_event_engine.prototype.addEventListener;
712 this.removeEventListener = rcube_event_engine.prototype.removeEventListener;
713 this.triggerEvent = rcube_event_engine.prototype.triggerEvent;
714 };
715
716 function rcm_override_mailbox_command(menu, props, before) {
717 if ($('div.contextmenu').is(':visible') && $.inArray(props.action, rcmail.context_menu_overload_commands) >= 0) {
718 if (before) {
719 rcmail.env.context_menu_prev_display_next = rcmail.env.display_next;
720 if (!(menu.list_object.selection.length == 1 && menu.list_object.in_selection(rcmail.env.context_menu_source_id)))
721 rcmail.env.display_next = false;
722
723 rcmail.env.context_menu_prev_sel = menu.list_selection(true);
724 }
725 else if (rcmail.env.context_menu_prev_sel) {
726 menu.list_selection(false, rcmail.env.context_menu_prev_sel);
727 rcmail.env.display_next = rcmail.env.context_menu_prev_display_next;
728 }
729 }
730 }
731
732 function rcm_check_button_state(btn, active) {
733 var classes = active ? rcmail.context_menu_button_active_class : rcmail.context_menu_button_disabled_class;
734 var found = false;
735
736 $.each(classes, function(i) {
737 if ($('#' + btn).hasClass(classes[i])) {
738 found = true;
739
740 // stop processing
741 return false;
742 }
743 });
744
745 return found;
746 }
747
748 function rcm_addressbook_selector(event, command, callback) {
749 var container = rcmail.rcm_addressbook_selector_element;
750
751 if (!container) {
752 var rows = [],
753 ul = $('<ul class="toolbarmenu">');
754
755 container = $('<div id="addressbook-selector" class="popupmenu"></div>');
756
757 // loop over address books
758 $.each(rcmail.env.address_sources, function() {
759 if (!this.readonly) {
760 rows.push(rcm_addressbook_selector_item(this));
761
762 if (this.groups) {
763 var ref = this;
764 $.each(rcmail.env.contactgroups, function() {
765 rows.push(rcm_addressbook_selector_item(this, ref.id));
766 });
767 }
768 }
769 });
770
771 ul.append(rows).appendTo(container);
772
773 // temporarily show element to calculate its size
774 container.css({left: '-1000px', top: '-1000px'})
775 .appendTo($('body')).show();
776
777 // set max-height if the list is long
778 if (rows.length > 10)
779 container.css('max-height', $('li', container)[0].offsetHeight * 10 + 9);
780
781 // register delegate event handler for folder item clicks
782 container.on('click', 'a.active', function(e) {
783 container.data('callback')(this, container.data('command'), e);
784 return false;
785 });
786
787 rcmail.rcm_addressbook_selector_element = container;
788 }
789
790 container.data('command', command);
791 container.data('callback', callback);
792
793 // customize menu for move or copy
794 container.find('li').show();
795
796 // search result may contain contacts from many sources, but if there is only one...
797 var source = rcmail.env.source;
798 if (source == '' && rcmail.env.selection_sources.length == 1)
799 source = rcmail.env.selection_sources[0];
800
801 // hide currently open address book from menu
802 if (source) {
803 $.each(container.find('a'), function() {
804 if (($(this).data('source') && $(this).data('source') == source) || $(this).data('id') == source)
805 $(this).parent('li').hide();
806 });
807 }
808
809 // position menu on the screen
810 rcmail.show_menu('addressbook-selector', true, event);
811 }
812
813 function rcm_group_selector(event, command, callback) {
814 var container = rcmail.rcm_addressgroup_selector_element;
815
816 if (!container) {
817 var rows = [],
818 ul = $('<ul class="toolbarmenu">');
819
820 container = $('<div id="addressgroup-selector" class="popupmenu"></div>');
821
822 // loop over address books
823 $.each(rcmail.env.address_sources, function() {
824 if (this.id === rcmail.env.source) {
825 var ref = this;
826 $.each(rcmail.env.contactgroups, function() {
827 rows.push(rcm_addressbook_selector_item(this, ref.id));
828 });
829 }
830 });
831
832 ul.append(rows).appendTo(container);
833
834 // remove indent added by rcm_addressbook_selector_item()
835 $(ul).find('a').removeAttr('style');
836
837 // temporarily show element to calculate its size
838 container.css({left: '-1000px', top: '-1000px'})
839 .appendTo($('body')).show();
840
841 // set max-height if the list is long
842 if (rows.length > 10)
843 container.css('max-height', $('li', container)[0].offsetHeight * 10 + 9);
844
845 // register delegate event handler for folder item clicks
846 container.on('click', 'a.active', {cmd: command}, function(e) {
847 container.data('callback')(this, e);
848 return false;
849 });
850
851 rcmail.rcm_addressgroup_selector_element = container;
852 }
853
854 container.data('callback', callback);
855
856 // customize menu for move or copy
857 container.find('li').show();
858
859 // hide currently open group from menu
860 if (rcmail.env.group) {
861 $.each(container.find('a'), function() {
862 if ($(this).data('id') == rcmail.env.group)
863 $(this).parent('li').hide();
864 });
865 }
866
867 // position menu on the screen
868 rcmail.show_menu('addressgroup-selector', true, event);
869 }
870
871 function rcm_addressbook_selector_item(obj, abook_id) {
872 if (abook_id && abook_id === obj.source || !abook_id) {
873 var a = $('<a>').attr('href', '#').addClass('icon'),
874 row = $('<li>');
875
876 if (obj.type == 'group') {
877 a.addClass('active contactgroup')
878 a.data('source', obj.source);
879 a.data('id', obj.id);
880 a.css('padding-left', '16px');
881 }
882 else {
883 a.addClass('addressbook active').data('id', obj.id);
884 }
885
886 // add address book name element
887 a.append($('<span>').text(obj.name));
888
889 return row.append(a);
890 }
891 }
892
893 $(document).ready(function() {
894 if (window.rcmail) {
895 rcmail.env.contextmenus = {};
896
897 rcmail.addEventListener('init', function() {
898 // no need to reattach events inside iframe
899 if (rcmail.is_framed())
900 return;
901
902 var body_mouseup = function(e) { $.each(rcmail.env.contextmenus, function() { this.hide(e); }); };
903 $(document.body).on('click contextmenu', body_mouseup);
904
905 // Hide menu after clicks in iframes (eg. preview pane)
906 $('iframe').on('load', function(e) {
907 try { $(this.contentDocument || this.contentWindow).on('mouseup', body_mouseup) }
908 catch (e) { /* catch possible "Permission denied" error in IE */ }
909 })
910 .contents().on('mouseup', body_mouseup);
911 });
912
913 if ((rcmail.env.task == 'mail' || rcmail.env.task == 'addressbook') && rcmail.env.action == '') {
914 // special handeling for move/copy functions (folder/address book selector)
915 rcmail.addEventListener('actionbefore', function(props) {
916 var menu = rcmail.env.task == 'addressbook' ? rcmail.env.contextmenus['contactlist'] : rcmail.env.contextmenus['messagelist'];
917 rcm_override_mailbox_command(menu, props, true);
918 });
919
920 rcmail.addEventListener('actionafter', function(props) {
921 var menu = rcmail.env.task == 'addressbook' ? rcmail.env.contextmenus['contactlist'] : rcmail.env.contextmenus['messagelist'];
922 rcm_override_mailbox_command(menu, props, false);
923 });
924 }
925
926 if (rcmail.env.task == 'mail' && rcmail.env.action == '') {
927 rcmail.register_command('plugin.contextmenu.collapseall', function(props, obj) {
928 $("#mailboxlist div.expanded").each(function() { $(this).click(); });
929 }, false);
930
931 rcmail.register_command('plugin.contextmenu.expandall', function(props, obj) {
932 $("#mailboxlist div.collapsed").each(function() { $(this).click(); });
933 }, false);
934
935 rcmail.register_command('plugin.contextmenu.openfolder', function(props, obj) {
936 var button_id = rcmail.buttons['plugin.contextmenu.openfolder'][0].id;
937
938 rcube_find_object(button_id).href = '?_task=mail&_mbox='+urlencode(rcmail.env.context_menu_source_id);
939 rcmail.sourcewin = window.open(rcube_find_object(button_id).href);
940 if (rcmail.sourcewin)
941 window.setTimeout(function() { rcmail.sourcewin.focus(); }, 20);
942
943 rcube_find_object(button_id).href = '#';
944 }, false);
945 }
946
947 if (rcmail.env.task == 'addressbook' && rcmail.env.action == '') {
948 // address book selector
949 rcmail.addEventListener('actionbefore', function(props) {
950 if ((props.action == 'move' || props.action == 'copy') && props.props == '') {
951 rcm_addressbook_selector(props.originalEvent, props.action, function(obj, cmd, evt) {
952 // search result may contain contacts from many sources, but if there is only one...
953 var source = rcmail.env.source;
954 if (source == '' && rcmail.env.selection_sources.length == 1)
955 source = rcmail.env.selection_sources[0];
956
957 if ($(obj).data('source')) {
958 rcmail.command(cmd, rcmail.env.contactgroups['G' + $(obj).data('source') + $(obj).data('id')], evt);
959 }
960 else {
961 rcmail.command(cmd, rcmail.env.address_sources[$(obj).data('id')], evt);
962 }
963 });
964
965 return false;
966 }
967 });
968
969 // address book group selector
970 rcmail.register_command('plugin.contextmenu.assigngroup', function(props, obj, event) {
971 rcm_group_selector(event, props, function(obj, evt) {
972 // search result may contain contacts from many sources, but if there is only one...
973 rcm_override_mailbox_command(rcmail.env.contextmenus['contactlist'], { action: 'copy' } , true);
974 rcmail.group_member_change('add', rcmail.contact_list.get_selection().join(','), rcmail.env.source, $(obj).data('id'));
975 rcm_override_mailbox_command(rcmail.env.contextmenus['contactlist'], { action: 'copy' } , false);
976 });
977 }, false);
978
979 // reset address book selector when groups change
980 rcmail.addEventListener('group_insert', function() { $("#addressbook-selector").remove(); $("#addressgroup-selector").remove(); rcmail.rcm_addressbook_selector_element = undefined; rcmail.rcm_addressgroup_selector_element = undefined; } );
981 rcmail.addEventListener('group_update', function() { $("#addressbook-selector").remove(); $("#addressgroup-selector").remove(); rcmail.rcm_addressbook_selector_element = undefined; rcmail.rcm_addressgroup_selector_element = undefined; } );
982 rcmail.addEventListener('group_delete', function() { $("#addressbook-selector").remove(); $("#addressgroup-selector").remove(); rcmail.rcm_addressbook_selector_element = undefined; rcmail.rcm_addressgroup_selector_element = undefined; } );
983 }
984
985 // special event listeners for intreacting with plugins which open popup menus (eg: zipdownload)
986 rcmail.addEventListener('menu-open', function(p) {
987 // check for popupmenus that arent part of contextmenu
988 if ($('div.contextmenu').is(':visible') && p.name.indexOf('rcm_') != 0) {
989 rcmail.context_menu_popup_commands[p.name] = {};
990 $('#' + p.name).find('a').each(function() {
991 if ($(this).attr('onclick') && $(this).attr('onclick').match(rcmail.context_menu_command_pattern)) {
992 var cmd = RegExp.$1;
993 rcmail.context_menu_popup_commands[p.name][cmd] = rcmail.commands[cmd];
994 }
995 });
996 }
997 });
998
999 rcmail.addEventListener('menu-close', function(p) {
1000 // check required args are present, other plugins trigger this event too
1001 if (!p.originalEvent) {
1002 return;
1003 }
1004
1005 // check for popupmenus that arent part of contextmenu
1006 var e = p.originalEvent.currentTarget ? p.originalEvent.currentTarget : p.originalEvent.srcElement;
1007 if ($('div.contextmenu').is(':visible') && p.name.indexOf('rcm_') != 0 && $(e).attr('class').indexOf('rcm_elem_') == -1) {
1008 rcm_hide_menu(p.originalEvent);
1009 }
1010 });
1011
1012 rcmail.addEventListener('get_single_uid', function() {
1013 if ($('#rcm_messagelist').is(':visible') && rcmail.env.contextmenus['messagelist'].menu_selection.length == 1) {
1014 return rcmail.env.contextmenus['messagelist'].menu_selection[0];
1015 }
1016 });
1017
1018 rcmail.addEventListener('get_single_cid', function() {
1019 if ($('#rcm_contactlist').is(':visible') && rcmail.env.contextmenus['contactlist'].menu_selection.length == 1) {
1020 return rcmail.env.contextmenus['contactlist'].menu_selection[0];
1021 }
1022 else if ($('#rcm_composeto').is(':visible') && rcmail.env.contextmenus['composeto'].menu_selection.length == 1) {
1023 return rcmail.env.contextmenus['composeto'].menu_selection[0];
1024 }
1025 });
1026 }
1027 });