From 6c11ee29ddefbef5e26d0a1fe13dbb2185612bba Mon Sep 17 00:00:00 2001 From: alecpl <alec@alec.pl> Date: Wed, 14 Apr 2010 09:46:45 -0400 Subject: [PATCH] - use the same iframe hack as in splitter.js --- program/js/list.js | 546 +++++++++++++++++++++++++++++++++++++++++++++--------- 1 files changed, 451 insertions(+), 95 deletions(-) diff --git a/program/js/list.js b/program/js/list.js index dabcecb..ad640fb 100644 --- a/program/js/list.js +++ b/program/js/list.js @@ -37,6 +37,7 @@ this.subject_col = -1; this.shiftkey = false; this.multiselect = false; + this.multiexpand = false; this.multi_selecting = false; this.draggable = false; this.keyboard = false; @@ -76,7 +77,7 @@ for(var r=0; r<this.list.tBodies[0].childNodes.length; r++) { row = this.list.tBodies[0].childNodes[r]; - while (row && (row.nodeType != 1 || row.style.display == 'none')) + while (row && row.nodeType != 1) { row = row.nextSibling; r++; @@ -90,15 +91,15 @@ // set body events if (this.keyboard) { - rcube_event.add_listener({element:document, event:'keyup', object:this, method:'key_press'}); - rcube_event.add_listener({element:document, event:'keydown', object:this, method:'key_down'}); + rcube_event.add_listener({event:bw.opera?'keypress':'keydown', object:this, method:'key_press'}); + rcube_event.add_listener({event:'keydown', object:this, method:'key_down'}); } } }, /** - * + * Init list row and set mouse events on it */ init_row: function(row) { @@ -108,7 +109,7 @@ var p = this; var uid = RegExp.$1; row.uid = uid; - this.rows[uid] = {uid:uid, id:row.id, obj:row, classname:row.className}; + this.rows[uid] = {uid:uid, id:row.id, obj:row}; // set eventhandlers to table row row.onmousedown = function(e){ return p.drag_row(e, this.uid); }; @@ -123,11 +124,11 @@ /** - * + * Remove all list rows */ clear: function(sel) { - var tbody = document.createElement('TBODY'); + var tbody = document.createElement('tbody'); this.list.insertBefore(tbody, this.list.tBodies[0]); this.list.removeChild(this.list.tBodies[1]); this.rows = new Array(); @@ -154,20 +155,21 @@ /** - * + * Add row to the list and initialize it */ insert_row: function(row, attop) { - var tbody = this.list.tBodies[0]; - if (!row.jquery) - row = $(row); + if (this.background) + var tbody = this.background; + else + var tbody = this.list.tBodies[0]; if (attop && tbody.rows.length) - row.prependTo(tbody) + tbody.insertBefore(row, tbody.firstChild); else - row.appendTo(tbody); + tbody.appendChild(row); - this.init_row(row[0]); + this.init_row(row); this.rowcount++; }, @@ -216,7 +218,8 @@ { // 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'))) + var tagname = evtarget.tagName.toLowerCase(); + if (this.dont_select || (evtarget && (tagname == 'input' || tagname == 'img'))) return true; // accept right-clicks @@ -236,38 +239,19 @@ { this.drag_start = true; 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'}); + rcube_event.add_listener({event:'mousemove', object:this, method:'drag_mouse_move'}); + rcube_event.add_listener({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'}); - } - } + // enable dragging over iframes + $('iframe').each(function() { + $('<div class="iframe-dragdrop-fix"></div>') + .css({background: '#fff', + width: this.offsetWidth+'px', height: this.offsetHeight+'px', + position: 'absolute', opacity: '0.001', zIndex: 1000 + }) + .css($(this).offset()) + .appendTo('body'); + }); } return false; @@ -282,8 +266,9 @@ var now = new Date().getTime(); var mod_key = rcube_event.get_modifier(e); var evtarget = rcube_event.get_target(e); + var tagname = evtarget.tagName.toLowerCase(); - if ((evtarget && (evtarget.tagName == 'INPUT' || evtarget.tagName == 'IMG'))) + if ((evtarget && (tagname == 'input' || tagname == 'img'))) return true; // don't do anything (another action processed before) @@ -316,8 +301,211 @@ }, +/* + * Returns thread root ID for specified row ID + */ +find_root: function(uid) +{ + var r = this.rows[uid]; + + if (r && r.parent_uid) + return this.find_root(r.parent_uid); + else + return uid; +}, + + +expand_row: function(e, id) +{ + var row = this.rows[id]; + var evtarget = rcube_event.get_target(e); + var mod_key = rcube_event.get_modifier(e); + + // Don't select this message + this.dont_select = true; + // Don't treat double click on the expando as double click on the message. + row.clicked = 0; + + if (row.expanded) { + evtarget.className = "collapsed"; + if (mod_key == CONTROL_KEY || this.multiexpand) + this.collapse_all(row); + else + this.collapse(row); + } + else { + evtarget.className = "expanded"; + if (mod_key == CONTROL_KEY || this.multiexpand) + this.expand_all(row); + else + this.expand(row); + } +}, + +collapse: function(row) +{ + row.expanded = false; + this.triggerEvent('expandcollapse', { uid:row.uid, expanded:row.expanded }); + var depth = row.depth; + var new_row = row ? row.obj.nextSibling : null; + var r; + + while (new_row) { + if (new_row.nodeType == 1) { + var r = this.rows[new_row.uid]; + if (r && r.depth <= depth) + break; + $(new_row).hide(); + r.expanded = false; + this.triggerEvent('expandcollapse', { uid:r.uid, expanded:r.expanded }); + } + new_row = new_row.nextSibling; + } + + return false; +}, + +expand: function(row) +{ + var depth, new_row; + var last_expanded_parent_depth; + + if (row) { + row.expanded = true; + depth = row.depth; + new_row = row.obj.nextSibling; + this.update_expando(row.uid, true); + this.triggerEvent('expandcollapse', { uid:row.uid, expanded:row.expanded }); + } + else { + var tbody = this.list.tBodies[0]; + new_row = tbody.firstChild; + depth = 0; + last_expanded_parent_depth = 0; + } + + while (new_row) { + if (new_row.nodeType == 1) { + var r = this.rows[new_row.uid]; + if (r) { + if (row && (!r.depth || r.depth <= depth)) + break; + + if (r.parent_uid) { + var p = this.rows[r.parent_uid]; + if (p && p.expanded) { + if ((row && p == row) || last_expanded_parent_depth >= p.depth - 1) { + last_expanded_parent_depth = p.depth; + $(new_row).show(); + r.expanded = true; + this.triggerEvent('expandcollapse', { uid:r.uid, expanded:r.expanded }); + } + } + else + if (row && (! p || p.depth <= depth)) + break; + } + } + } + new_row = new_row.nextSibling; + } + + return false; +}, + + +collapse_all: function(row) +{ + var depth, new_row; + var r; + + if (row) { + row.expanded = false; + depth = row.depth; + new_row = row.obj.nextSibling; + this.update_expando(row.uid); + this.triggerEvent('expandcollapse', { uid:row.uid, expanded:row.expanded }); + + // don't collapse sub-root tree in multiexpand mode + if (depth && this.multiexpand) + return false; + } + else { + var tbody = this.list.tBodies[0]; + new_row = tbody.firstChild; + depth = 0; + } + + while (new_row) { + if (new_row.nodeType == 1) { + var r = this.rows[new_row.uid]; + if (r) { + if (row && (!r.depth || r.depth <= depth)) + break; + + if (row || r.depth) + $(new_row).hide(); + if (r.has_children) { + r.expanded = false; + this.update_expando(r.uid); + this.triggerEvent('expandcollapse', { uid:r.uid, expanded:r.expanded }); + } + } + } + new_row = new_row.nextSibling; + } + + return false; +}, + +expand_all: function(row) +{ + var depth, new_row; + var r; + + if (row) { + row.expanded = true; + depth = row.depth; + new_row = row.obj.nextSibling; + this.update_expando(row.uid, true); + this.triggerEvent('expandcollapse', { uid:row.uid, expanded:row.expanded }); + } + else { + var tbody = this.list.tBodies[0]; + new_row = tbody.firstChild; + depth = 0; + } + + while (new_row) { + if (new_row.nodeType == 1) { + var r = this.rows[new_row.uid]; + if (r) { + if (row && r.depth <= depth) + break; + + $(new_row).show(); + if (r.has_children) { + r.expanded = true; + this.update_expando(r.uid, true); + this.triggerEvent('expandcollapse', { uid:r.uid, expanded:r.expanded }); + } + } + } + new_row = new_row.nextSibling; + } + return false; +}, + +update_expando: function(uid, expanded) +{ + var expando = document.getElementById('rcmexpando' + uid); + if (expando) + expando.className = expanded ? 'expanded' : 'collapsed'; +}, + + /** - * get next/previous/last rows that are not hidden + * get first/next/previous/last rows that are not hidden */ get_next_row: function() { @@ -343,6 +531,20 @@ new_row = new_row.previousSibling; return new_row; +}, + +get_first_row: function() +{ + if (this.rowcount) + { + var rows = this.list.tBodies[0].rows; + + for(var i=0; i<rows.length-1; 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; }, get_last_row: function() @@ -450,6 +652,62 @@ /** + * Select first row + */ +select_first: function(mod_key) +{ + var row = this.get_first_row(); + if (row && mod_key) { + this.shift_select(row, mod_key); + this.triggerEvent('select'); + this.scrollto(row); + } + else if (row) + this.select(row); +}, + + +/** + * Select last row + */ +select_last: function(mod_key) +{ + var row = this.get_last_row(); + if (row && mod_key) { + this.shift_select(row, mod_key); + this.triggerEvent('select'); + this.scrollto(row); + } + else if (row) + this.select(row); +}, + + +/** + * Add all childs of the given row to selection + */ +select_childs: function(uid) +{ + if (!this.rows[uid] || !this.rows[uid].has_children) + return; + + var depth = this.rows[uid].depth; + var row = this.rows[uid].obj.nextSibling; + while (row) { + if (row.nodeType == 1) { + if ((r = this.rows[row.uid])) { + if (!r.depth || r.depth <= depth) + break; + if (!this.in_selection(r.uid)) + this.select_row(r.uid, CONTROL_KEY); + } + } + row = row.nextSibling; + } +}, + + +/** * Perform selection when shift key is pressed */ shift_select: function(id, control) @@ -468,13 +726,15 @@ { if ((this.rows[n].obj.rowIndex >= i) && (this.rows[n].obj.rowIndex <= j)) { - if (!this.in_selection(n)) + if (!this.in_selection(n)) { this.highlight_row(n, true); + } } else { - if (this.in_selection(n) && !control) + if (this.in_selection(n) && !control) { this.highlight_row(n, true); + } } } }, @@ -489,7 +749,7 @@ if (this.selection[n]==id) return true; - return false; + return false; }, @@ -514,10 +774,33 @@ } else if (this.rows[n]) { - this.set_classname(this.rows[n].obj, 'selected', false); - this.set_classname(this.rows[n].obj, 'unfocused', false); + $(this.rows[n].obj).removeClass('selected').removeClass('unfocused'); } } + + // trigger event if selection changed + if (this.selection.join(',') != select_before) + this.triggerEvent('select'); + + this.focus(); + + return true; +}, + + +/** + * Invert selection + */ +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) @@ -605,7 +888,7 @@ } else // unselect row { - var p = find_in_array(id, this.selection); + var p = jQuery.inArray(id, this.selection); 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); @@ -625,6 +908,7 @@ var keyCode = rcube_event.get_keycode(e); var mod_key = rcube_event.get_modifier(e); + switch (keyCode) { case 40: @@ -634,6 +918,22 @@ // Stop propagation so that the browser doesn't scroll rcube_event.cancel(e); return this.use_arrow_key(keyCode, mod_key); + case 61: + case 107: // Plus sign on a numeric keypad (fc11 + firefox 3.5.2) + case 109: + case 32: + // Stop propagation + rcube_event.cancel(e); + var ret = this.use_plusminus_key(keyCode, mod_key); + this.key_pressed = keyCode; + this.triggerEvent('keypress'); + return ret; + case 36: // Home + this.select_first(mod_key); + return rcube_event.cancel(e); + case 35: // End + this.select_last(mod_key); + return rcube_event.cancel(e); default: this.shiftkey = e.shiftKey; this.key_pressed = keyCode; @@ -653,10 +953,18 @@ { switch (rcube_event.get_keycode(e)) { + case 27: + if (this.drag_active) + return this.drag_mouse_up(e); + case 40: case 38: case 63233: case 63232: + case 61: + case 107: + case 109: + case 32: if (!rcube_event.get_modifier(e) && this.focused) return rcube_event.cancel(e); @@ -691,6 +999,36 @@ /** + * Special handling method for +/- keys + */ +use_plusminus_key: function(keyCode, mod_key) +{ + var selected_row = this.rows[this.last_selected]; + if (!selected_row) + return; + + if (keyCode == 32) + keyCode = selected_row.expanded ? 109 : 61; + if (keyCode == 61 || keyCode == 107) + if (mod_key == CONTROL_KEY || this.multiexpand) + this.expand_all(selected_row); + else + this.expand(selected_row); + else + if (mod_key == CONTROL_KEY || this.multiexpand) + this.collapse_all(selected_row); + else + this.collapse(selected_row); + + var expando = document.getElementById('rcmexpando' + selected_row.uid); + if (expando) + expando.className = selected_row.expanded?'expanded':'collapsed'; + + return false; +}, + + +/** * Try to scroll the list to make the specified row visible */ scrollto: function(id) @@ -699,6 +1037,13 @@ if (row && this.frame) { var scroll_to = Number(row.offsetTop); + + // expand thread if target row is hidden (collapsed) + if (!scroll_to && this.rows[id].parent_uid) { + var parent = this.find_root(this.rows[id].uid); + this.expand_all(this.rows[parent]); + scroll_to = Number(row.offsetTop); + } if (scroll_to < Number(this.frame.scrollTop)) this.frame.scrollTop = scroll_to; @@ -723,10 +1068,19 @@ if (!this.draglayer) this.draglayer = $('<div>').attr('id', 'rcmdraglayer').css({ position:'absolute', display:'none', 'z-index':2000 }).appendTo(document.body); - - // get subjects of selectedd messages + + // also select childs of (collapsed) threads for dragging + var selection = $.merge([], this.selection); + var depth, row, uid, r; + for (var n=0; n < selection.length; n++) { + uid = selection[n]; + if (this.rows[uid].has_children && !this.rows[uid].expanded) + this.select_childs(uid); + } + + // get subjects of selected messages var names = ''; - var c, i, node, subject, obj; + var c, i, subject, obj; for(var n=0; n<this.selection.length; n++) { if (n>12) // only show 12 lines @@ -735,21 +1089,29 @@ break; } - if (this.rows[this.selection[n]].obj) + if (obj = this.rows[this.selection[n]].obj) { - obj = this.rows[this.selection[n]].obj; subject = ''; - for(c=0, i=0; i<obj.childNodes.length; i++) + for (c=0, i=0; i<obj.childNodes.length; i++) { - if (obj.childNodes[i].nodeName == 'TD') + if (obj.childNodes[i].nodeName == 'TD') { - 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) - this.drag_start_pos = $(node).offset(); - + if (n == 0) + this.drag_start_pos = $(obj.childNodes[i]).offset(); + + if (this.subject_col < 0 || (this.subject_col >= 0 && this.subject_col == c)) + { + var node, tmp_node, nodes = obj.childNodes[i].childNodes; + // find text node + for (m=0; m<nodes.length; m++) { + if ((tmp_node = obj.childNodes[i].childNodes[m]) && (tmp_node.nodeType==3 || tmp_node.nodeName=='A')) + node = tmp_node; + } + + if (!node) + break; + subject = node.nodeType==3 ? node.data : node.innerHTML; // remove leading spaces subject = subject.replace(/^\s+/i, ''); @@ -774,7 +1136,7 @@ { var pos = rcube_event.get_mouse_pos(e); this.draglayer.css({ left:(pos.x+20)+'px', top:(pos.y-5 + (bw.ie ? document.documentElement.scrollTop : 0))+'px' }); - this.triggerEvent('dragmove', e); + this.triggerEvent('dragmove', e?e:window.event); } this.drag_start = false; @@ -798,36 +1160,30 @@ } this.drag_active = false; + + rcube_event.remove_listener({event:'mousemove', object:this, method:'drag_mouse_move'}); + rcube_event.remove_listener({event:'mouseup', object:this, method:'drag_mouse_up'}); + + // remove temp divs + $('div.iframe-dragdrop-fix').each(function() { this.parentNode.removeChild(this); }); + 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'}); - - 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); +}, + + +/** + * Creating the list in background + */ +set_background_mode: function(flag) +{ + if (flag) { + this.background = document.createElement('tbody'); + } else if (this.background) { + this.list.replaceChild(this.background, this.list.tBodies[0]); + this.background = null; + } } }; -- Gitblit v1.9.1