From dc5a8295cef78e444355c0eed26bed2ebfd37cbf Mon Sep 17 00:00:00 2001 From: Michael M Slusarz Date: Mon, 16 Nov 2009 20:48:11 -0700 Subject: [PATCH] Add vertical-pane preview layout to DIMP. Thank you Microsoft Outlook. --- imp/docs/CHANGES | 1 + imp/js/DimpBase.js | 228 ++++++++++++--------- imp/js/ViewPort.js | 360 +++++++++++++++++++++------------ imp/js/mailbox-dimp.js | 26 ++- imp/templates/index/index-dimp.inc | 215 ++++++++++---------- imp/templates/javascript_defs_dimp.php | 2 +- imp/themes/graphics/dragHandleVert.png | Bin 0 -> 132 bytes imp/themes/ie6_or_less-dimp.css | 2 +- imp/themes/ie7-dimp.css | 2 +- imp/themes/screen-dimp.css | 92 ++++++--- 10 files changed, 556 insertions(+), 372 deletions(-) create mode 100644 imp/themes/graphics/dragHandleVert.png diff --git a/imp/docs/CHANGES b/imp/docs/CHANGES index 3cbc70446..31fd1e9da 100644 --- a/imp/docs/CHANGES +++ b/imp/docs/CHANGES @@ -2,6 +2,7 @@ v5.0-git -------- +[mms] Add vertical-pane preview layout to DIMP. [mms] Wrap content-related MIME parts in a border when viewing inline to indicate their relationship. [mms] For drafts, save the original message index for forwards/replies so when diff --git a/imp/js/DimpBase.js b/imp/js/DimpBase.js index 5fdba6c64..b17e42ab6 100644 --- a/imp/js/DimpBase.js +++ b/imp/js/DimpBase.js @@ -10,8 +10,9 @@ var DimpBase = { // Vars used and defaulting to null/false: // cfolderaction, folder, folderswitch, offset, pollPE, pp, search, - // uid, viewport - // message_list_template set via js/mailbox-dimp.js + // template, uid, viewport + // msglist_template_horiz and msglist_template_vert set via + // js/mailbox-dimp.js cacheids: {}, lastrow: -1, pivotrow: -1, @@ -390,86 +391,42 @@ var DimpBase = { _createViewPort: function() { + [ $('msglistHeader') ].invoke(DIMP.conf.preview_pref == 'vert' ? 'hide' : 'show'); + + this.template = { + horiz: new Template(this.msglist_template_horiz), + vert: new Template(this.msglist_template_vert) + }; + this.viewport = new ViewPort({ // Mandatory config ajax_url: DIMP.conf.URI_AJAX + '/ViewPort', - content: 'msgList', - template: this.message_list_template, - - // Optional config - ajax_opts: DimpCore.doActionOpts, - buffer_pages: DIMP.conf.buffer_pages, - content_class: 'msglist', - empty_msg: DIMP.text.vp_empty, - page_size: DIMP.conf.splitbar_pos, - show_split_pane: DIMP.conf.preview_pref, - split_bar: 'splitBar', - split_pane: 'previewPane', - wait: DIMP.conf.viewport_wait, - - // Callbacks - onAjaxRequest: function(id) { - var p = $H(); - if (this.folderswitch && this.isSearch(id, true)) { - p.set('qsearchmbox', this.search.mbox); - if (this.search.flag) { - p.update({ qsearchflag: this.search.flag, qsearchflagnot: Number(this.convertFlag(this.search.flag, this.search.not)) }); - } else { - p.set('qsearch', $F('qsearch_input')); - } - } - return DimpCore.addRequestParams(p); - }.bind(this), - onAjaxResponse: DimpCore.doActionComplete.bind(DimpCore), - onCachedList: function(id) { - var tmp, vs; - if (!this.cacheids[id]) { - vs = this.viewport.getSelection(id); - if (!vs.size()) { - return ''; - } - - if (vs.getBuffer().getMetaData('search')) { - this.cacheids[id] = vs.get('uid').toJSON(); - } else { - tmp = {}; - tmp[id] = vs.get('uid').clone(); - this.cacheids[id] = DimpCore.toRangeString(tmp); - } - } - return this.cacheids[id]; - }.bind(this), - onCacheUpdate: function(id) { - delete this.cacheids[id]; - }.bind(this), - onClear: function(r) { - r.each(this._removeMouseEvents.bind(this)); - }.bind(this), - onContent: function(row) { + container: 'msgSplitPane', + onContent: function(r, mode) { var bg, re, u, thread = $H(this.viewport.getMetaData('thread')), tsort = (this.viewport.getMetaData('sortby') == $H(DIMP.conf.sort).get('thread').v); - row.subjectdata = row.status = ''; - row.subjecttitle = row.subject; + r.subjectdata = r.status = ''; + r.subjecttitle = r.subject; // Add thread graphics if (tsort) { - u = thread.get(row.imapuid); + u = thread.get(r.imapuid); if (u) { $R(0, u.length, true).each(function(i) { var c = u.charAt(i); if (!this.tcache[c]) { this.tcache[c] = ''; } - row.subjectdata += this.tcache[c]; + r.subjectdata += this.tcache[c]; }, this); } } /* Generate the status flags. */ - if (row.flag) { - row.flag.each(function(a) { + if (r.flag) { + r.flag.each(function(a) { var ptr = DIMP.conf.flags[a]; if (ptr.p) { if (!ptr.elt) { @@ -478,14 +435,14 @@ var DimpBase = { * ourselves. */ ptr.elt = '' + ptr.l.truncate(10) + ''; } - row.subjectdata += ptr.elt; + r.subjectdata += ptr.elt; } else { if (!ptr.elt) { ptr.elt = '
'; } - row.status += ptr.elt; + r.status += ptr.elt; - row.bg.push(ptr.c); + r.bg.push(ptr.c); if (ptr.b) { bg = ptr.b; @@ -496,26 +453,90 @@ var DimpBase = { // Set bg if (bg) { - row.style = 'background:' + bg; + r.style = 'background:' + bg; } // Check for search strings if (this.isSearch(null, true)) { re = new RegExp("(" + $F('qsearch_input') + ")", "i"); [ 'from', 'subject' ].each(function(h) { - row[h] = row[h].gsub(re, '#{1}'); + r[h] = r[h].gsub(re, '#{1}'); }); } // If these fields are null, invalid string was scrubbed by // JSON encode. - if (row.from === null) { - row.from = '[' + DIMP.text.badaddr + ']'; + if (r.from === null) { + r.from = '[' + DIMP.text.badaddr + ']'; } - if (row.subject === null) { - row.subject = row.subjecttitle = '[' + DIMP.text.badsubject + ']'; + if (r.subject === null) { + r.subject = r.subjecttitle = '[' + DIMP.text.badsubject + ']'; + } + + r.bg.push('vpRow'); + + switch (mode) { + case 'vert': + r.bg.unshift('vpRowVert'); + r.className = r.bg.join(' '); + return this.template.vert.evaluate(r); + + default: + r.bg.unshift('vpRowHoriz'); + r.className = r.bg.join(' '); + return this.template.horiz.evaluate(r); } }.bind(this), + + // Optional config + ajax_opts: DimpCore.doActionOpts, + buffer_pages: DIMP.conf.buffer_pages, + empty_msg: DIMP.text.vp_empty, + list_class: 'msglist', + page_size: DIMP.conf.splitbar_pos, + pane_data: 'previewPane', + pane_mode: DIMP.conf.preview_pref, + split_bar_class: { horiz: 'splitBarHoriz', vert: 'splitBarVert' }, + wait: DIMP.conf.viewport_wait, + + // Callbacks + onAjaxRequest: function(id) { + var p = $H(); + if (this.folderswitch && this.isSearch(id, true)) { + p.set('qsearchmbox', this.search.mbox); + if (this.search.flag) { + p.update({ qsearchflag: this.search.flag, qsearchflagnot: Number(this.convertFlag(this.search.flag, this.search.not)) }); + } else { + p.set('qsearch', $F('qsearch_input')); + } + } + return DimpCore.addRequestParams(p); + }.bind(this), + onAjaxResponse: DimpCore.doActionComplete.bind(DimpCore), + onCachedList: function(id) { + var tmp, vs; + if (!this.cacheids[id]) { + vs = this.viewport.getSelection(id); + if (!vs.size()) { + return ''; + } + + if (vs.getBuffer().getMetaData('search')) { + this.cacheids[id] = vs.get('uid').toJSON(); + } else { + tmp = {}; + tmp[id] = vs.get('uid').clone(); + this.cacheids[id] = DimpCore.toRangeString(tmp); + } + } + return this.cacheids[id]; + }.bind(this), + onCacheUpdate: function(id) { + delete this.cacheids[id]; + }.bind(this), + onClear: function(r) { + r.each(this._removeMouseEvents.bind(this)); + }.bind(this), onContentComplete: function(rows) { var row, ssc, tmp, l = this.viewport.getMetaData('label'); @@ -615,13 +636,15 @@ var DimpBase = { onFetch: this.loadingImg.bind(this, 'viewport', true), onSelect: this._select.bind(this), onSlide: this.setMessageListTitle.bind(this), - onSplitBarChange: function() { - this._updatePrefs('dimp_splitbar', this.viewport.getPageSize()); + onSplitBarChange: function(mode) { + if (mode = 'horiz') { + this._updatePrefs('dimp_splitbar', this.viewport.getPageSize()); + } }.bind(this), - onSplitBarEnd: function() { + onSplitBarEnd: function(mode) { $('messageBodyCover').hide(); }, - onSplitBarStart: function() { + onSplitBarStart: function(mode) { $('messageBodyCover').clonePosition('messageBody').show(); }, onWait: function() { @@ -765,8 +788,17 @@ var DimpBase = { break; case 'oa_preview_hide': + DIMP.conf.preview_pref_old = DIMP.conf.preview_pref; + this.togglePreviewPane(''); + break; + case 'oa_preview_show': - this.togglePreviewPane(); + this.togglePreviewPane(DIMP.conf.preview_pref_old || 'horiz'); + break; + + case 'oa_layout_horiz': + case 'oa_layout_vert': + this.togglePreviewPane(id.substring(10)); break; case 'oa_blacklist': @@ -873,12 +905,21 @@ var DimpBase = { break; case 'ctx_otheractions': - if (DIMP.conf.preview_pref) { - $('oa_preview_hide').show(); - $('oa_preview_show').hide(); - } else { - $('oa_preview_hide').hide(); + switch (DIMP.conf.preview_pref) { + case 'vert': + $('oa_preview_hide', 'oa_layout_horiz').invoke('show'); + $('oa_preview_show', 'oa_layout_vert').invoke('hide'); + break; + + case 'horiz': + $('oa_preview_hide', 'oa_layout_vert').invoke('show'); + $('oa_preview_show', 'oa_layout_horiz').invoke('hide'); + break; + + default: + $('oa_preview_hide', 'oa_layout_horiz', 'oa_layout_vert').invoke('hide'); $('oa_preview_show').show(); + break; } tmp = [ $('oa_undeleted') ]; $('oa_blacklist', 'oa_whitelist').each(function(o) { @@ -1022,14 +1063,18 @@ var DimpBase = { }, // Preview pane functions - togglePreviewPane: function() + // mode = (string) Either 'horiz', 'vert', or empty + togglePreviewPane: function(mode) { - var p = DIMP.conf.preview_pref = !DIMP.conf.preview_pref; - $('oa_preview_hide', 'oa_preview_show').invoke('toggle'); - this._updatePrefs('show_preview', Number(p)); - this.viewport.showSplitPane(p); - if (p) { - this.initPreviewPane(); + var old = DIMP.conf.preview_pref; + if (mode != DIMP.conf.preview_pref) { + DIMP.conf.preview_pref = mode; + this._updatePrefs('dimp_show_preview', mode); + [ $('msglistHeader') ].invoke(mode == 'vert' ? 'hide' : 'show'); + this.viewport.showSplitPane(mode); + if (!old) { + this.initPreviewPane(); + } } }, @@ -1664,9 +1709,8 @@ var DimpBase = { tmp.draft ? DimpCore.compose('resume', { folder: tmp.view, uid: tmp.imapuid }) : this.msgWindow(tmp); + e.stop(); } - - e.stop(); }, clickHandler: function(parentfunc, e) @@ -2545,7 +2589,7 @@ var DimpBase = { var c; if (show) { - $(id + 'Loading').clonePosition(id == 'viewport' ? 'msgList' : 'splitBar', { setLeft: false, setTop: true, setHeight: false, setWidth: false }).show(); + $(id + 'Loading').clonePosition(id == 'viewport' ? 'msgSplitPane' : 'previewPane', { setLeft: false, setTop: true, setHeight: false, setWidth: false }).show(); c = 'progress'; } else { $(id + 'Loading').fade({ duration: 0.2 }); @@ -2578,13 +2622,11 @@ var DimpBase = { /* Register global handlers now. */ document.observe('keydown', this.keydownHandler.bindAsEventListener(this)); document.observe('change', this.changeHandler.bindAsEventListener(this)); + document.observe('dblclick', this.dblclickHandler.bindAsEventListener(this)); /* Limit to folders sidebar only. */ $('foldersSidebar').observe('mouseover', this.mouseoverHandler.bindAsEventListener(this)); - /* Limit to msgList only. */ - $('msgList').observe('dblclick', this.dblclickHandler.bindAsEventListener(this)); - /* Show page now. */ $('sidebarPanel').setStyle({ width: DIMP.conf.sidebar_width }); $('dimpLoading').hide(); diff --git a/imp/js/ViewPort.js b/imp/js/ViewPort.js index 55f378436..c45450dcc 100644 --- a/imp/js/ViewPort.js +++ b/imp/js/ViewPort.js @@ -1,5 +1,6 @@ /** - * ViewPort.js - Code to create a viewport window in a web browser. + * ViewPort.js - Code to create a viewport window, with optional split pane + * functionality. * * Usage: * ====== @@ -9,11 +10,21 @@ * ----------------- * ajax_url: (string) The URL to send the viewport requests to. * This URL should return its response in an object named - * 'ViewPort' (other information can be returned in the response). - * content: (Element/string) A DOM element/ID of the container to hold the - * viewport rows. - * template: (string) TODO DIV with 'vpData' - * Class: 'vpRow' 'vpRowSelected' + * 'ViewPort' (other information can be returned in the response and + * will be ignored by the viewport object). + * container: (Element/string) A DOM element/ID of the container that holds + * the viewport. This element should be empty and have no children. + * onContent: (function) A function that takes 2 arguments - the data object + * for the row and a string indicating the current pane_mode. + * + * This function MUST return the HTML representation of the row. + * + * This representation MUST include both the DOM ID (stored in + * the domid field) and the CSS class name (stored as an array in + * the bg field) in the outermost element. + * + * Selected rows will contain the classname 'vpRowSelected'. + * * * Optional options: * ----------------- @@ -27,18 +38,23 @@ * within this percentage of the end of the current cached * viewport, send a background request to the server to retrieve * the next slice. + * list_class: (string) The CSS class to use for the list container. * lookbehind: (integer) What percentage of the received buffer should be - * used to download messages before the given row number? - * page_size: (integer) Default page size to view on load. - * show_split_pane: (boolean) Show the split pane on load? - * split_bar: (Element/string) A DOM element/ID of the element used to display - * the split bar. - * split_pane: (Element/string) A DOM element/ID of the container to hold - * the split pane info. + * used to download rows before the given row number? + * page_size: (integer) Default page size to view on load. Only used if + * pane_mode is 'horiz'. + * pane_data: (Element/string) A DOM element/ID of the container to hold + * the split pane data. This element will be moved inside of the + * container element. + * pane_mode: (string) The split pane mode to show on load? Either empty, + * 'horiz', or 'vert'. + * split_bar_class: (object) The CSS class(es) to use for the split bar. + * Takes two properties: 'horiz' and 'vert'. * wait: (integer) How long, in seconds, to wait before displaying an - * informational message to users that the message list is still being + * informational message to users that the list is still being * built. * + * * Callbacks: * ---------- * onAjaxRequest @@ -53,11 +69,15 @@ * onFetch * onSelect * onSlide + * * onSplitBarChange * onSplitBarEnd * onSplitBarStart + * + Passed the current pane mode (either 'horiz' or 'vert'). + * * onWait * + * * Outgoing AJAX request has the following params: * ----------------------------------------------- * For ALL requests: @@ -83,6 +103,7 @@ * slice: (string) The list of rows to retrieve from the server. * In the format: [first_row]:[last_row] * + * * Incoming AJAX response has the following params: * ------------------------------------------------ * cacheid: (string) A unique string that changes whenever the viewport @@ -101,6 +122,15 @@ * overwriting it. * view: (string) The view ID of the request. * + * + * Scroll bars use ars styled using these CSS class names: + * ------------------------------------------------------- + * vpScroll - The scroll bar container. + * vpScrollUp - The UP arrow. + * vpScrollCursor - The cursor used to slide within the bounds. + * vpScrollDown - The DOWN arrow. + * + * * Requires prototypejs 1.6+, DimpSlider.js, scriptaculous 1.8+ (effects.js * only), and Horde's dragdrop2.js. * @@ -120,21 +150,30 @@ var ViewPort = Class.create({ this.opts = Object.extend({ buffer_pages: 10, limit_factor: 35, - lookbehind: 40 + lookbehind: 40, + split_bar_class: {} }, opts); - this.opts.content = $(opts.content); - this.opts.split_pane = $(opts.split_pane); + this.opts.container = $(opts.container); + this.opts.pane_data = $(opts.pane_data); + + this.opts.content = new Element('DIV', { className: opts.list_class }).setStyle({ overflow: 'hidden' }); + this.opts.container.insert(this.opts.content); this.scroller = new ViewPort_Scroller(this); - this.template = new Template(opts.template); + this.split_pane = { + curr: null, + horiz: { + loc: opts.page_size, + }, + vert: {} + }; this.views = {}; - this.split_bar_loc = opts.page_size; - this.show_split_pane = opts.show_split_pane; + this.pane_mode = opts.pane_mode; - this.isbusy = this.line_height = this.page_size = this.split_bar = null; + this.isbusy = this.page_size = null; this.request_num = 1; // Init empty string now. @@ -158,7 +197,7 @@ var ViewPort = Class.create({ // Need a page size before we can continue - this is what determines // the slice size to request from the server. if (this.page_size === null) { - ps = this.getPageSize(this.show_split_pane ? 'default' : 'max'); + ps = this.getPageSize(this.pane_mode ? 'default' : 'max'); if (isNaN(ps)) { return this.loadView.bind(this, view, search, background).defer(); } @@ -336,8 +375,9 @@ var ViewPort = Class.create({ this.isbusy = false; }, - // nowait = (boolean) TODO - onResize: function(nowait) + // nowait = (boolean) If true, don't delay before resizing. + // size = (integer) The page size to use instead of auto-determining. + onResize: function(nowait, size) { if (!this.opts.content.visible()) { return; @@ -348,55 +388,82 @@ var ViewPort = Class.create({ } if (nowait) { - this._onResize(); + this._onResize(size); } else { - this.resizefunc = this._onResize.bind(this).delay(0.1); + this.resizefunc = this._onResize.bind(this, size).delay(0.1); } }, - _onResize: function() + // size = (integer) The page size to use instead of auto-determining. + _onResize: function(size) { // This is needed for IE 6 - or else horizontal scrolling can occur. if (!this.opts.content.offsetHeight) { - return this._onResize.bind(this).defer(); + return this._onResize.bind(this, size).defer(); } - var h, setpane, - c = $(this.opts.content), + var h, + c = this.opts.content, de = document.documentElement, lh = this._getLineHeight(); + if (size) { + this.page_size = size; + } + // Get split pane dimensions - if (this.opts.split_pane) { - if (this.show_split_pane) { - if (!this.opts.split_pane.visible()) { - this._initSplitBar(); - if (this.split_bar_loc && - this.split_bar_loc > 0) { - this.split_bar_loc = this.page_size = Math.min(this.split_bar_loc, this.getPageSize('splitmax')); - } else { - this.page_size = this.getPageSize('default'); - } + switch (this.pane_mode) { + case 'horiz': + this._initSplitBar(); + + if (!size) { + this.page_size = (this.split_pane.horiz.loc && this.split_pane.horiz.loc > 0) + ? Math.min(this.split_pane.horiz.loc, this.getPageSize('splitmax')) + : this.getPageSize('default'); + } + this.split_pane.horiz.loc = this.page_size; + + h = lh * this.page_size; + c.setStyle({ float: 'none', height: h + 'px', width: '100%' }); + this.split_pane.currbar.show(); + this.opts.pane_data.setStyle({ height: (this._getMaxHeight() - h - lh) + 'px' }).show(); + break; + + case 'vert': + this._initSplitBar(); + + if (!size) { + this.page_size = this.getPageSize('max'); + } + + h = lh * this.page_size; + // TODO: Configurable default size? Currently it is 35% of + // container size. + c.setStyle({ float: 'left', height: h + 'px', width: parseInt(this.opts.container.getWidth() * 0.35, 10) + 'px' }); + this.split_pane.currbar.setStyle({ height: h + 'px' }).show(); + this.opts.pane_data.setStyle({ height: h + 'px' }).show(); + break; + + default: + if (this.split_pane.curr) { + if (this.pane_mode == 'horiz') { + this.split_pane.horiz.loc = this.page_size; } - setpane = true; - } else if (this.opts.split_pane.visible()) { - this.split_bar_loc = this.page_size; - [ this.opts.split_pane, this.split_bar ].invoke('hide'); + [ this.opts.pane_data, this.split_pane.currbar ].invoke('hide'); + this.split_pane.curr = this.split_pane.currbar = null; + } + + if (!size) { + this.page_size = this.getPageSize('max'); } - } - if (!setpane) { - this.page_size = this.getPageSize('max'); + c.setStyle({ float: 'none', height: (lh * this.page_size) + 'px', width: '100%' }); + break; } // Do some magic to ensure we never cause a horizontal scroll. - h = lh * this.page_size; - c.setStyle({ height: h + 'px' }); - if (setpane) { - this.opts.split_pane.setStyle({ height: (this._getMaxHeight() - h - lh) + 'px' }).show(); - this.split_bar.show(); - } else if (de.scrollHeight - de.clientHeight) { - c.setStyle({ height: (lh * (this.page_size - 1)) + 'px' }); + if (de.scrollHeight - de.clientHeight) { + c.setStyle({ height: (parseInt(c.getStyle('height'), 10) - lh) + 'px' }); } this.scroller.onResize(); @@ -771,16 +838,7 @@ var ViewPort = Class.create({ r.bg.push('vpRowSelected'); } - r.bg.unshift('vpRow'); - - if (this.opts.onContent) { - this.opts.onContent(r); - } - - // Mandatory DOM ID and class information. - r.vpData = 'id="' + r.domid + '" class="' + r.bg.join(' ') + '"'; - - return this.template.evaluate(r); + return this.opts.onContent(r, this.pane_mode); }, updateRow: function(row) @@ -855,19 +913,22 @@ var ViewPort = Class.create({ _getLineHeight: function() { - if (!this.line_height) { + var mode = this.pane_mode || 'horiz'; + + if (!this.split_pane[mode].lh) { // To avoid hardcoding the line height, create a temporary row to // figure out what the CSS says. - var d = new Element('DIV', { className: this.opts.content_class }).insert(new Element('DIV', { className: 'vpRow' })).hide(); + var d = new Element('DIV', { className: this.opts.list_class }).insert(this.prepareRow({ domid: null }, mode)).hide(); $(document.body).insert(d); - this.line_height = d.getHeight(); + this.split_pane[mode].lh = d.getHeight(); d.remove(); } - return this.line_height; + return this.split_pane[mode].lh; }, // (type) = (string) [null (DEFAULT), 'current', 'default', 'max'] + // return: (integer) Number of rows in current view. getPageSize: function(type) { switch (type) { @@ -875,11 +936,13 @@ var ViewPort = Class.create({ return Math.min(this.page_size, this.getMetaData('total_rows')); case 'default': - return Math.max(parseInt(this.getPageSize('max') * 0.45), 5); + return (this.pane_mode == 'vert') + ? this.getPageSize('max') + : Math.max(parseInt(this.getPageSize('max') * 0.45), 5); case 'max': case 'splitmax': - return parseInt((this._getMaxHeight() - (type == 'max' ? 0 : 100)) / this._getLineHeight()); + return parseInt(this._getMaxHeight() / this._getLineHeight()) - (type == 'max' ? 0 : 1); default: return this.page_size; @@ -902,68 +965,109 @@ var ViewPort = Class.create({ return Math.round(this.bufferSize() * (this.opts.limit_factor / 100)); }, - showSplitPane: function(show) + // mode = (string) Either 'horiz', 'vert', or empty. + showSplitPane: function(mode) { - this.show_split_pane = show; + this.pane_mode = mode; this.onResize(true); }, _initSplitBar: function() { - if (this.split_bar) { + if (this.split_pane.currbar) { + this.split_pane.currbar.hide(); + } + + this.split_pane.curr = this.pane_mode; + + if (this.split_pane[this.pane_mode].bar) { + this.split_pane.currbar = this.split_pane[this.pane_mode].bar.show(); return; } - this.split_bar = $(this.opts.split_bar); - new Drag(this.split_bar, { - constraint: 'vertical', - ghosting: true, - nodrop: true, - onStart: function() { - // Cache these values since we will be using them multiple - // times in snap(). - this.sp = { - lh: this._getLineHeight(), - lines: this.page_size, - max: this.getPageSize('splitmax'), - orig: this.page_size, - pos: $(this.opts.content).positionedOffset()[1] - }; - if (this.opts.onSplitBarStart) { - this.opts.onSplitBarStart(); - } - }.bind(this), - snap: function(x, y, elt) { - var l = parseInt((y - this.sp.pos) / this.sp.lh); - if (l < 1) { - l = 1; - } else if (l > this.sp.max) { - l = this.sp.max; - } - this.sp.lines = l; - return [ x, this.sp.pos + (l * this.sp.lh) ]; - }.bind(this), - onEnd: function() { - this.page_size = this.sp.lines; - this.onResize(true); + this.split_pane.currbar = this.split_pane[this.pane_mode].bar = new Element('DIV', { className: this.opts.split_bar_class[this.pane_mode] }); + + if (!this.opts.pane_data.descendantOf(this.opts.container)) { + this.opts.container.insert({ bottom: this.opts.pane_data.remove() }); + } + + this.opts.pane_data.insert({ before: this.split_pane.currbar }); + + switch (this.pane_mode) { + case 'horiz': + new Drag(this.split_pane.currbar, { + constraint: 'vertical', + ghosting: true, + nodrop: true, + onStart: function() { + // Cache these values since we will be using them multiple + // times in snap(). + this.sp = { + lh: this._getLineHeight(), + lines: this.page_size, + max: this.getPageSize('splitmax'), + orig: this.page_size, + pos: this.opts.content.positionedOffset()[1] + }; + if (this.opts.onSplitBarStart) { + this.opts.onSplitBarStart('horiz'); + } + }.bind(this), + snap: function(x, y, elt) { + var l = parseInt((y - this.sp.pos) / this.sp.lh); + if (l < 1) { + l = 1; + } else if (l > this.sp.max) { + l = this.sp.max; + } + this.sp.lines = l; + return [ x, this.sp.pos + (l * this.sp.lh) ]; + }.bind(this), + onEnd: function() { + this.onResize(true, this.sp.lines); + if (this.opts.onSplitBarChange && + this.sp.orig != this.sp.lines) { + this.opts.onSplitBarChange('horiz'); + } + if (this.opts.onSplitBarEnd) { + this.opts.onSplitBarEnd('horiz'); + } + }.bind(this) + }); + + this.split_pane.currbar.observe('dblclick', function() { + var old_size = this.page_size; + this.onResize(true, this.getPageSize('default')); if (this.opts.onSplitBarChange && - this.sp.orig != this.sp.lines) { - this.opts.onSplitBarChange(); - } - if (this.opts.onSplitBarEnd) { - this.opts.onSplitBarEnd(); + old_size != this.page_size) { + this.opts.onSplitBarChange('horiz'); } - }.bind(this) - }); - this.split_bar.observe('dblclick', function() { - var old_size = this.page_size; - this.page_size = this.getPageSize('default'); - this._onResize(true); - if (this.opts.onSplitBarChange && - old_size != this.page_size) { - this.opts.onSplitBarChange(); - } - }.bind(this)); + }.bind(this)); + break; + + case 'vert': + new Drag(this.split_pane.currbar.setStyle({ float: 'left' }), { + constraint: 'horizontal', + ghosting: true, + nodrop: true, + snapToParent: true, + onStart: function(drag) { + if (this.opts.onSplitBarStart) { + this.opts.onSplitBarStart('vert'); + } + }.bind(this), + onEnd: function(drag) { + this.opts.content.setStyle({ width: drag.lastCoord[0] + 'px' }); + if (this.opts.onSplitBarChange && drag.wasDragged) { + this.opts.onSplitBarChange('vert'); + } + if (this.opts.onSplitBarEnd) { + this.opts.onSplitBarEnd('vert'); + } + }.bind(this) + }); + break; + } }, createSelection: function(format, data, view) @@ -1055,7 +1159,7 @@ ViewPort_Scroller = Class.create({ var c = this.vp.opts.content; // Create the outer div. - this.scrollDiv = new Element('DIV', { className: 'sbdiv' }).setStyle({ height: c.getHeight() + 'px' }).hide(); + this.scrollDiv = new Element('DIV', { className: 'vpScroll' }).setStyle({ height: c.getHeight() + 'px', overflow: 'hidden', position: 'absolute', right: 0, top: 0 }).hide(); // Add scrollbar to parent viewport and give our parent a right // margin just big enough to accomodate the scrollbar. @@ -1063,8 +1167,8 @@ ViewPort_Scroller = Class.create({ // Create scrollbar object. this.scrollbar = new DimpSlider(this.scrollDiv, { - buttonclass: { up: 'sbup', down: 'sbdown' }, - cursorclass: 'sbcursor', + buttonclass: { up: 'vpScrollUp', down: 'vpScrollDown' }, + cursorclass: 'vpScrollCursor', onChange: this._onScroll.bind(this), onSlide: this.vp.opts.onSlide ? this.vp.opts.onSlide : null, pagesize: this.vp.getPageSize(), @@ -1216,10 +1320,10 @@ ViewPort_Buffer = Class.create({ this._rangeCheck($A($R(Math.max(offset + 1 - this.vp.limitTolerance(), 1), offset)))) { return 'top'; } else if (this._rangeCheck($A($R(offset + 1, Math.min(offset + this.vp.limitTolerance() + this.vp.getPageSize() - 1, this.getMetaData('total_rows')))).reverse())) { - // Search for missing messages in reverse order since in - // normal usage (sequential scrolling through the message - // list) messages are more likely to be missing at furthest - // from the current view. + // Search for missing rows in reverse order since in normal + // usage (sequential scrolling through the row list) rows are + // more likely to be missing at furthest from the current + // view. return 'bottom'; } } diff --git a/imp/js/mailbox-dimp.js b/imp/js/mailbox-dimp.js index 4206c907c..a50ebc02b 100644 --- a/imp/js/mailbox-dimp.js +++ b/imp/js/mailbox-dimp.js @@ -2,12 +2,7 @@ * mailbox-dimp.js - Template used to format the rows in the message list * display. * - * 'vpData' is a mandatory entry for ID/class information automatically - * generated by ViewPort.js. - * 'status', 'style', and 'subjectdata' are entries that are internally - * created by DimpBase.js. - * - * See the documentation of prototypejs - Template for the template format: + * See the documentation of prototypejs::Template for the template format: * http://www.prototypejs.org/api/template * * Copyright 2005-2009 The Horde Project (http://www.horde.org/) @@ -16,8 +11,8 @@ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html. */ -DimpBase.message_list_template = -'
' + +DimpBase.msglist_template_horiz = +'
' + '
' + '
' + '#{status}' + @@ -27,3 +22,18 @@ DimpBase.message_list_template = '
#{date}
' + '
#{size}
' + '
'; + +DimpBase.msglist_template_vert = +'
' + + '
' + + '
' + + '#{status}' + + '
' + + '
' + + '
#{from}
' + + '
#{date}
' + + '
' + + '
' + + '
#{subjectdata}#{subject}
' + + '
' + +'
'; diff --git a/imp/templates/index/index-dimp.inc b/imp/templates/index/index-dimp.inc index a29c95685..fb67f6144 100644 --- a/imp/templates/index/index-dimp.inc +++ b/imp/templates/index/index-dimp.inc @@ -191,122 +191,119 @@ function _simpleButton($id, $text, $image)
-
-
- -
- -