From 9489adc5936fd516f3f6df91cfce3697b36e2cf1 Mon Sep 17 00:00:00 2001 From: thomascube <thomas@roundcube.net> Date: Tue, 02 Jun 2009 11:46:14 -0400 Subject: [PATCH] Fix list events used in drag&drop functionality (#1485890) --- program/js/list.js | 307 ++++++++++++++++++++++++++++++++------------------- 1 files changed, 193 insertions(+), 114 deletions(-) diff --git a/program/js/list.js b/program/js/list.js index 926d98a..aa34546 100644 --- a/program/js/list.js +++ b/program/js/list.js @@ -3,7 +3,7 @@ | RoundCube List Widget | | | | This file is part of the RoundCube Webmail client | - | Copyright (C) 2006-2008, RoundCube Dev, - Switzerland | + | Copyright (C) 2006-2009, RoundCube Dev, - Switzerland | | Licensed under the GNU GPL | | | +-----------------------------------------------------------------------+ @@ -13,7 +13,7 @@ | Requires: common.js | +-----------------------------------------------------------------------+ - $Id: list.js 344 2006-09-18 03:49:28Z thomasb $ + $Id$ */ @@ -51,7 +51,6 @@ this.drag_mouse_start = null; this.dblclick_time = 600; this.row_init = function(){}; - this.events = { click:[], dblclick:[], select:[], keypress:[], dragstart:[], dragmove:[], dragend:[] }; // overwrite default paramaters if (p && typeof(p)=='object') @@ -99,12 +98,12 @@ /** - * + * Init list row and set mouse events on it */ init_row: function(row) { // make references in internal array and set event handlers - if (row && String(row.id).match(/rcmrow([a-z0-9\-_=]+)/i)) + if (row && String(row.id).match(/rcmrow([a-z0-9\-_=\+\/]+)/i)) { var p = this; var uid = RegExp.$1; @@ -124,7 +123,7 @@ /** - * + * Remove all list rows */ clear: function(sel) { @@ -155,11 +154,14 @@ /** - * + * Add row to the list and initialize it */ insert_row: function(row, attop) { - var tbody = this.list.tBodies[0]; + if (this.background) + var tbody = this.background; + else + var tbody = this.list.tBodies[0]; if (attop && tbody.rows.length) tbody.insertBefore(row, tbody.firstChild); @@ -173,7 +175,7 @@ /** - * Set focur to the list + * Set focus to the list */ focus: function(e) { @@ -181,10 +183,8 @@ for (var n=0; n<this.selection.length; n++) { id = this.selection[n]; - if (this.rows[id] && this.rows[id].obj) - { - this.set_classname(this.rows[id].obj, 'selected', true); - this.set_classname(this.rows[id].obj, 'unfocused', false); + if (this.rows[id] && this.rows[id].obj) { + $(this.rows[id].obj).addClass('selected').removeClass('unfocused'); } } @@ -203,10 +203,8 @@ for (var n=0; n<this.selection.length; n++) { id = this.selection[n]; - if (this.rows[id] && this.rows[id].obj) - { - this.set_classname(this.rows[id].obj, 'selected', false); - this.set_classname(this.rows[id].obj, 'unfocused', true); + if (this.rows[id] && this.rows[id].obj) { + $(this.rows[id].obj).removeClass('selected').addClass('unfocused'); } } }, @@ -220,7 +218,7 @@ // don't do anything (another action processed before) var evtarget = rcube_event.get_target(e); if (this.dont_select || (evtarget && (evtarget.tagName == 'INPUT' || evtarget.tagName == 'IMG'))) - return false; + return true; // accept right-clicks if (rcube_event.get_button(e) == 2) @@ -241,6 +239,36 @@ this.drag_mouse_start = rcube_event.get_mouse_pos(e); rcube_event.add_listener({element:document, event:'mousemove', object:this, method:'drag_mouse_move'}); rcube_event.add_listener({element:document, event:'mouseup', object:this, method:'drag_mouse_up'}); + + // add listener for iframes + var iframes = document.getElementsByTagName('IFRAME'); + this.iframe_events = Object(); + for (var n in iframes) + { + var iframedoc = null; + if (iframes[n].contentDocument) + iframedoc = iframes[n].contentDocument; + else if (iframes[n].contentWindow) + iframedoc = iframes[n].contentWindow.document; + else if (iframes[n].document) + iframedoc = iframes[n].document; + + if (iframedoc) + { + var list = this; + var pos = $('#'+iframes[n].id).offset(); + this.iframe_events[n] = function(e) { e._offset = pos; return list.drag_mouse_move(e); } + + if (iframedoc.addEventListener) + iframedoc.addEventListener('mousemove', this.iframe_events[n], false); + else if (iframes[n].attachEvent) + iframedoc.attachEvent('onmousemove', this.iframe_events[n]); + else + iframedoc['onmousemove'] = this.iframe_events[n]; + + rcube_event.add_listener({element:iframedoc, event:'mouseup', object:this, method:'drag_mouse_up'}); + } + } } return false; @@ -255,10 +283,10 @@ var now = new Date().getTime(); var mod_key = rcube_event.get_modifier(e); var evtarget = rcube_event.get_target(e); - + if ((evtarget && (evtarget.tagName == 'INPUT' || evtarget.tagName == 'IMG'))) - return false; - + return true; + // don't do anything (another action processed before) if (this.dont_select) { @@ -277,9 +305,9 @@ // row was double clicked if (this.rows && dblclicked && this.in_selection(id)) - this.trigger_event('dblclick'); + this.triggerEvent('dblclick'); else - this.trigger_event('click'); + this.triggerEvent('click'); if (!this.drag_active) rcube_event.cancel(e); @@ -290,7 +318,7 @@ /** - * get next and previous rows that are not hidden + * get next/previous/last rows that are not hidden */ get_next_row: function() { @@ -318,8 +346,24 @@ return new_row; }, +get_last_row: function() +{ + if (this.rowcount) + { + var rows = this.list.tBodies[0].rows; -// selects or unselects the proper row depending on the modifier key pressed + for(var i=rows.length-1; i>=0; i--) + if(rows[i].id && String(rows[i].id).match(/rcmrow([a-z0-9\-_=\+\/]+)/i) && this.rows[RegExp.$1] != null) + return RegExp.$1; + } + + return null; +}, + + +/** + * selects or unselects the proper row depending on the modifier key pressed + */ select_row: function(id, mod_key, with_mouse) { var select_before = this.selection.join(','); @@ -361,10 +405,10 @@ // trigger event if selection changed if (this.selection.join(',') != select_before) - this.trigger_event('select'); + this.triggerEvent('select'); if (this.last_selected != 0 && this.rows[this.last_selected]) - this.set_classname(this.rows[this.last_selected].obj, 'focused', false); + $(this.rows[this.last_selected].obj).removeClass('focused'); // unselect if toggleselect is active and the same row was clicked again if (this.toggleselect && this.last_selected == id) @@ -373,7 +417,7 @@ id = null; } else - this.set_classname(this.rows[id].obj, 'focused', true); + $(this.rows[id].obj).addClass('focused'); if (!this.selection.length) this.shift_start = null; @@ -460,20 +504,24 @@ // reset but remember selection first var select_before = this.selection.join(','); - this.clear_selection(); - + this.selection = new Array(); + for (var n in this.rows) { - if (!filter || this.rows[n][filter]==true) + if (!filter || (this.rows[n] && this.rows[n][filter] == true)) { this.last_selected = n; this.highlight_row(n, true); + } + else if (this.rows[n]) + { + $(this.rows[n].obj).removeClass('selected').removeClass('unfocused'); } } // trigger event if selection changed if (this.selection.join(',') != select_before) - this.trigger_event('select'); + this.triggerEvent('select'); this.focus(); @@ -482,22 +530,58 @@ /** - * Unselect all selected rows + * Invert selection */ -clear_selection: function() +invert_selection: function() +{ + if (!this.rows || !this.rows.length) + return false; + + // remember old selection + var select_before = this.selection.join(','); + + for (var n in this.rows) + this.highlight_row(n, true); + + // trigger event if selection changed + if (this.selection.join(',') != select_before) + this.triggerEvent('select'); + + this.focus(); + + return true; +}, + + +/** + * Unselect selected row(s) + */ +clear_selection: function(id) { var num_select = this.selection.length; - for (var n=0; n<this.selection.length; n++) - if (this.rows[this.selection[n]]) + + // one row + if (id) { - this.set_classname(this.rows[this.selection[n]].obj, 'selected', false); - this.set_classname(this.rows[this.selection[n]].obj, 'unfocused', false); + for (var n=0; n<this.selection.length; n++) + if (this.selection[n] == id) { + this.selection.splice(n,1); + break; + } + } + // all rows + else + { + for (var n=0; n<this.selection.length; n++) + if (this.rows[this.selection[n]]) { + $(this.rows[this.selection[n]].obj).removeClass('selected').removeClass('unfocused'); + } + + this.selection = new Array(); } - this.selection = new Array(); - - if (num_select) - this.trigger_event('select'); + if (num_select && !this.selection.length) + this.triggerEvent('select'); }, @@ -533,7 +617,7 @@ { this.clear_selection(); this.selection[0] = id; - this.set_classname(this.rows[id].obj, 'selected', true); + $(this.rows[id].obj).addClass('selected'); } } else if (this.rows[id]) @@ -541,7 +625,7 @@ if (!this.in_selection(id)) // select row { this.selection[this.selection.length] = id; - this.set_classname(this.rows[id].obj, 'selected', true); + $(this.rows[id].obj).addClass('selected'); } else // unselect row { @@ -549,8 +633,7 @@ var a_pre = this.selection.slice(0, p); var a_post = this.selection.slice(p+1, this.selection.length); this.selection = a_pre.concat(a_post); - this.set_classname(this.rows[id].obj, 'selected', false); - this.set_classname(this.rows[id].obj, 'unfocused', false); + $(this.rows[id].obj).removeClass('selected').removeClass('unfocused'); } } }, @@ -566,6 +649,7 @@ var keyCode = rcube_event.get_keycode(e); var mod_key = rcube_event.get_modifier(e); + switch (keyCode) { case 40: @@ -578,7 +662,7 @@ default: this.shiftkey = e.shiftKey; this.key_pressed = keyCode; - this.trigger_event('keypress'); + this.triggerEvent('keypress'); if (this.key_pressed == this.BACKSPACE_KEY) return rcube_event.cancel(e); @@ -594,6 +678,9 @@ { switch (rcube_event.get_keycode(e)) { + case 27: + if (this.drag_active) + this.drag_mouse_up(e); case 40: case 38: case 63233: @@ -658,12 +745,13 @@ { // check mouse movement, of less than 3 pixels, don't start dragging var m = rcube_event.get_mouse_pos(e); + if (!this.drag_mouse_start || (Math.abs(m.x - this.drag_mouse_start.x) < 3 && Math.abs(m.y - this.drag_mouse_start.y) < 3)) return false; if (!this.draglayer) - this.draglayer = new rcube_layer('rcmdraglayer', {x:0, y:0, width:300, vis:0, zindex:2000}); - + this.draglayer = $('<div>').attr('id', 'rcmdraglayer').css({ position:'absolute', display:'none', 'z-index':2000 }).appendTo(document.body); + // get subjects of selectedd messages var names = ''; var c, i, node, subject, obj; @@ -687,8 +775,17 @@ if (((node = obj.childNodes[i].firstChild) && (node.nodeType==3 || node.nodeName=='A')) && (this.subject_col < 0 || (this.subject_col >= 0 && this.subject_col == c))) { + if (n == 0) { + if (node.nodeType == 3) + this.drag_start_pos = $(obj.childNodes[i]).offset(); + else + this.drag_start_pos = $(node).offset(); + } subject = node.nodeType==3 ? node.data : node.innerHTML; - names += (subject.length > 50 ? subject.substring(0, 50)+'...' : subject) + '<br />'; + // remove leading spaces + subject = subject.replace(/^\s+/i, ''); + // truncate line to 50 characters + names += (subject.length > 50 ? subject.substring(0, 50)+'...' : subject) + '<br />'; break; } c++; @@ -697,18 +794,18 @@ } } - this.draglayer.write(names); - this.draglayer.show(1); + this.draglayer.html(names); + this.draglayer.show(); this.drag_active = true; - this.trigger_event('dragstart'); + this.triggerEvent('dragstart'); } if (this.drag_active && this.draglayer) { var pos = rcube_event.get_mouse_pos(e); - this.draglayer.move(pos.x+20, pos.y-5); - this.trigger_event('dragmove', e); + this.draglayer.css({ left:(pos.x+20)+'px', top:(pos.y-5 + (bw.ie ? document.documentElement.scrollTop : 0))+'px' }); + this.triggerEvent('dragmove', e?e:window.event); } this.drag_start = false; @@ -724,80 +821,62 @@ { document.onmousemove = null; - if (this.draglayer && this.draglayer.visible) - this.draglayer.show(0); + if (this.draglayer && this.draglayer.is(':visible')) { + if (this.drag_start_pos) + this.draglayer.animate(this.drag_start_pos, 300, 'swing').hide(20); + else + this.draglayer.hide(); + } this.drag_active = false; - this.trigger_event('dragend'); + this.triggerEvent('dragend'); rcube_event.remove_listener({element:document, event:'mousemove', object:this, method:'drag_mouse_move'}); rcube_event.remove_listener({element:document, event:'mouseup', object:this, method:'drag_mouse_up'}); - this.focus(); - + var iframes = document.getElementsByTagName('IFRAME'); + for (var n in iframes) { + var iframedoc; + + if (iframes[n].contentDocument) + iframedoc = iframes[n].contentDocument; + else if (iframes[n].contentWindow) + iframedoc = iframes[n].contentWindow.document; + else if (iframes[n].document) + iframedoc = iframes[n].document; + + if (iframedoc) { + if (this.iframe_events[n]) { + if (iframedoc.removeEventListener) + iframedoc.removeEventListener('mousemove', this.iframe_events[n], false); + else if (iframedoc.detachEvent) + iframedoc.detachEvent('onmousemove', this.iframe_events[n]); + else + iframedoc['onmousemove'] = null; + } + rcube_event.remove_listener({element:iframedoc, event:'mouseup', object:this, method:'drag_mouse_up'}); + } + } + return rcube_event.cancel(e); }, - /** - * set/unset a specific class name + * Creating the list in background */ -set_classname: function(obj, classname, set) +set_background_mode: function(flag) { - var reg = new RegExp('\s*'+classname, 'i'); - if (!set && obj.className.match(reg)) - obj.className = obj.className.replace(reg, ''); - else if (set && !obj.className.match(reg)) - obj.className += ' '+classname; -}, - - -/** - * Setter for object event handlers - * - * @param {String} Event name - * @param {Function} Handler function - * @return Listener ID (used to remove this handler later on) - */ -addEventListener: function(evt, handler) -{ - if (this.events[evt]) { - var handle = this.events[evt].length; - this.events[evt][handle] = handler; - return handle; - } - else - return false; -}, - - -/** - * Removes a specific event listener - * - * @param {String} Event name - * @param {Int} Listener ID to remove - */ -removeEventListener: function(evt, handle) -{ - if (this.events[evt] && this.events[evt][handle]) - this.events[evt][handle] = null; -}, - - -/** - * This will execute all registered event handlers - * @private - */ -trigger_event: function(evt, p) -{ - if (this.events[evt] && this.events[evt].length) { - for (var i=0; i<this.events[evt].length; i++) - if (typeof(this.events[evt][i]) == 'function') - this.events[evt][i](this, p); + if (flag) { + this.background = document.createElement('TBODY'); + } else if (this.background) { + this.list.replaceChild(this.background, this.list.tBodies[0]); + this.background = null; } } - }; +rcube_list_widget.prototype.addEventListener = rcube_event_engine.prototype.addEventListener; +rcube_list_widget.prototype.removeEventListener = rcube_event_engine.prototype.removeEventListener; +rcube_list_widget.prototype.triggerEvent = rcube_event_engine.prototype.triggerEvent; -- Gitblit v1.9.1