Thank you Microsoft Outlook.
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
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,
_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] = '<span class="treeImg treeImg' + c + '"></span>';
}
- 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) {
* ourselves. */
ptr.elt = '<span class="' + ptr.c + '" title="' + ptr.l + '" style="background:' + ptr.b + ';color:' + ptr.f + '">' + ptr.l.truncate(10) + '</span>';
}
- row.subjectdata += ptr.elt;
+ r.subjectdata += ptr.elt;
} else {
if (!ptr.elt) {
ptr.elt = '<div class="msgflags ' + ptr.c + '" title="' + ptr.l + '"></div>';
}
- row.status += ptr.elt;
+ r.status += ptr.elt;
- row.bg.push(ptr.c);
+ r.bg.push(ptr.c);
if (ptr.b) {
bg = ptr.b;
// 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, '<span class="qsearchMatch">#{1}</span>');
+ r[h] = r[h].gsub(re, '<span class="qsearchMatch">#{1}</span>');
});
}
// 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');
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() {
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':
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) {
},
// 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();
+ }
}
},
tmp.draft
? DimpCore.compose('resume', { folder: tmp.view, uid: tmp.imapuid })
: this.msgWindow(tmp);
+ e.stop();
}
-
- e.stop();
},
clickHandler: function(parentfunc, e)
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 });
/* 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();
/**
- * 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:
* ======
* -----------------
* 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:
* -----------------
* 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
* 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:
* 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
* 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.
*
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.
// 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();
}
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;
}
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();
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)
_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) {
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;
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)
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.
// 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(),
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';
}
}
* 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/)
* did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
*/
-DimpBase.message_list_template =
-'<div #{vpData} style="#{style}">' +
+DimpBase.msglist_template_horiz =
+'<div class="#{className}" id="#{domid}" style="#{style}">' +
'<div class="msgStatus sep">' +
'<div class="msCheck"></div>' +
'#{status}' +
'<div class="msgDate sep">#{date}</div>' +
'<div class="msgSize">#{size}</div>' +
'</div>';
+
+DimpBase.msglist_template_vert =
+'<div class="#{className}" id="#{domid}" style="#{style}">' +
+ '<div class="msgStatus">' +
+ '<div class="msCheck"></div>' +
+ '#{status}' +
+ '</div>' +
+ '<div>' +
+ '<div class="msgFrom">#{from}</div>' +
+ '<div class="msgDate">#{date}</div>' +
+ '</div>' +
+ '<div>' +
+ '<div class="msgSubject" title="#{subjecttitle}">#{subjectdata}#{subject}</div>' +
+ '</div>' +
+'</div>';
</div>
</div>
- <div class="msgSplitPane">
- <div id="msgList" class="msglist">
- <span class="vpEmpty"><?php echo _("Loading...") ?></span>
- </div>
- <div id="splitBar" style="display:none"></div>
- <div id="previewPane" style="display:none">
- <span id="msgLoading" class="loadingImg" style="display:none"></span>
- <div id="previewInfo" style="display:none">
- <?php echo _("To preview a message, select it from the list above.") ?>
- <br />
+ <div id="msgSplitPane"></div>
+
+ <div id="previewPane" style="display:none">
+ <span id="msgLoading" class="loadingImg" style="display:none"></span>
+ <div id="previewInfo" style="display:none">
+ <?php echo _("To preview a message, select it from the list above.") ?>
+ <br />
<?php if ($browser->isBrowser('opera')): ?>
- <?php echo _("A left click") ?> + <span class="kbd"><?php echo _("Shift") ?></span> + <span class="kbd"><?php echo _("Ctrl") ?></span> <?php echo _("will display available actions.") ?>
+ <?php echo _("A left click") ?> + <span class="kbd"><?php echo _("Shift") ?></span> + <span class="kbd"><?php echo _("Ctrl") ?></span> <?php echo _("will display available actions.") ?>
<?php else: ?>
- <?php echo _("A right-click on a message or a folder will display available actions.") ?>
-<?php endif; ?>
- <br />
- <?php printf(_("Click on a message while holding down the %s key to select multiple messages. To select a range of messages, click the first message of the range, navigate to the last message of the range, and then click on the last message while holding down the %s key."), '<span class="kbd">' . _("Ctrl") . '</span>', '<span class="kbd">' . _("Shift") . '</span>') ?><br /><br />
- <?php echo _("The following keyboard shortcuts are available:") ?><br />
- <span class="iconImg keyupImg"></span> / <span class="iconImg keydownImg"></span> : <?php echo _("Move up/down through the message list.") ?><br />
- <span class="kbd"><?php echo _("PgUp") ?></span> / <span class="kbd"><?php echo _("PgDown") ?></span> : <?php echo _("Move one page up/down through the message list.") ?><br />
- <span class="kbd"><?php echo _("Alt") ?></span> + <span class="kbd"><?php echo _("PgUp") ?></span> / <span class="kbd"><?php echo _("PgDown") ?></span> : <?php echo _("Scroll up/down through the display of the previewed message.") ?><br />
- <span class="kbd"><?php echo _("Home") ?></span> / <span class="kbd"><?php echo _("End") ?></span> : <?php echo _("Move to the beginning/end of the message list.") ?><br />
- <span class="kbd"><?php echo _("Del") ?></span> : <?php echo _("Delete the currently selected message(s).") ?> <?php printf(_("%s will delete the current message and move to the next message if a single message is selected."), '<span class="kbd">' . _("Shift") . '</span> + <span class="kbd">' . _("Del") . '</span>') ?><br />
- <span class="kbd"><?php echo _("Enter") ?></span> : <?php echo _("Open message in a popup window.") ?><br />
- <span class="kbd"><?php echo _("Ctrl") ?></span> + <span class="kbd"><?php echo 'A' ?></span> : <?php echo _("Select all messages in the current mailbox.") ?><br />
- </div>
- <div id="previewMsg" style="display:none">
- <div class="msgHeaders">
- <div id="toggleHeaders">
- <a id="th_expand"><span class="iconImg" title="<?php echo htmlspecialchars(_("Expand Headers")) ?>"></span></a>
- <a id="th_collapse" style="display:none"><span class="iconImg" title="<?php echo htmlspecialchars(_("Collapse Headers")) ?>"></span></a>
- </div>
- <div id="msgHeadersColl">
- <a id="msg_newwin"><span class="iconImg" title="<?php echo htmlspecialchars(_("Open in new window")) ?>"></span></a>
- <span class="date"></span>
- <span class="subject"></span>
- <span class="fromcontainer"><?php echo _("from") ?> <span class="from"></span></span>
- </div>
- <div id="msgHeaders" style="display:none">
- <div class="dimpOptions">
- <div>
- <span id="msg_newwin_options">
- <span class="iconImg"></span>
- <a><?php echo _("Open in new window") ?></a>
- </span>
- </div>
- <div>
- <span id="msg_print">
- <span class="iconImg"></span>
- <a><?php echo _("Print") ?></a>
- </span>
- </div>
+ <?php echo _("A right-click on a message or a folder will display available actions.") ?>
+<?php endif; ?>
+ <br />
+ <?php printf(_("Click on a message while holding down the %s key to select multiple messages. To select a range of messages, click the first message of the range, navigate to the last message of the range, and then click on the last message while holding down the %s key."), '<span class="kbd">' . _("Ctrl") . '</span>', '<span class="kbd">' . _("Shift") . '</span>') ?><br /><br />
+ <?php echo _("The following keyboard shortcuts are available:") ?><br />
+ <span class="iconImg keyupImg"></span> / <span class="iconImg keydownImg"></span> : <?php echo _("Move up/down through the message list.") ?><br />
+ <span class="kbd"><?php echo _("PgUp") ?></span> / <span class="kbd"><?php echo _("PgDown") ?></span> : <?php echo _("Move one page up/down through the message list.") ?><br />
+ <span class="kbd"><?php echo _("Alt") ?></span> + <span class="kbd"><?php echo _("PgUp") ?></span> / <span class="kbd"><?php echo _("PgDown") ?></span> : <?php echo _("Scroll up/down through the display of the previewed message.") ?><br />
+ <span class="kbd"><?php echo _("Home") ?></span> / <span class="kbd"><?php echo _("End") ?></span> : <?php echo _("Move to the beginning/end of the message list.") ?><br />
+ <span class="kbd"><?php echo _("Del") ?></span> : <?php echo _("Delete the currently selected message(s).") ?> <?php printf(_("%s will delete the current message and move to the next message if a single message is selected."), '<span class="kbd">' . _("Shift") . '</span> + <span class="kbd">' . _("Del") . '</span>') ?><br />
+ <span class="kbd"><?php echo _("Enter") ?></span> : <?php echo _("Open message in a popup window.") ?><br />
+ <span class="kbd"><?php echo _("Ctrl") ?></span> + <span class="kbd"><?php echo 'A' ?></span> : <?php echo _("Select all messages in the current mailbox.") ?><br />
+ </div>
+
+ <div id="previewMsg" style="display:none">
+ <div class="msgHeaders">
+ <div id="toggleHeaders">
+ <a id="th_expand"><span class="iconImg" title="<?php echo htmlspecialchars(_("Expand Headers")) ?>"></span></a>
+ <a id="th_collapse" style="display:none"><span class="iconImg" title="<?php echo htmlspecialchars(_("Collapse Headers")) ?>"></span></a>
+ </div>
+ <div id="msgHeadersColl">
+ <a id="msg_newwin"><span class="iconImg" title="<?php echo htmlspecialchars(_("Open in new window")) ?>"></span></a>
+ <span class="date"></span>
+ <span class="subject"></span>
+ <span class="fromcontainer"><?php echo _("from") ?> <span class="from"></span></span>
+ </div>
+ <div id="msgHeaders" style="display:none">
+ <div class="dimpOptions">
+ <div>
+ <span id="msg_newwin_options">
+ <span class="iconImg"></span>
+ <a><?php echo _("Open in new window") ?></a>
+ </span>
+ </div>
+ <div>
+ <span id="msg_print">
+ <span class="iconImg"></span>
+ <a><?php echo _("Print") ?></a>
+ </span>
+ </div>
<?php if (!empty($conf['user']['allow_view_source'])): ?>
- <div>
- <span id="msg_view_source">
- <span class="iconImg"></span>
- <a><?php echo _("View Source") ?></a>
- </span>
- </div>
-<?php endif; ?>
+ <div>
+ <span id="msg_view_source">
+ <span class="iconImg"></span>
+ <a><?php echo _("View Source") ?></a>
+ </span>
</div>
- <div id="msgHeadersContent">
- <table>
- <thead>
- <tr>
- <td class="label"><?php echo _("Subject") ?>:</td>
- <td class="subject"></td>
- </tr>
- <tr id="msgHeaderFrom">
- <td class="label"><?php echo _("From") ?>:</td>
- <td class="from"></td>
- </tr>
- <tr id="msgHeaderDate">
- <td class="label"><?php echo _("Date") ?>:</td>
- <td class="date"></td>
- </tr>
- <tr id="msgHeaderTo">
- <td class="label"><?php echo _("To") ?>:</td>
- <td class="to"></td>
- </tr>
- <tr id="msgHeaderCc">
- <td class="label"><?php echo _("Cc") ?>:</td>
- <td class="cc"></td>
- </tr>
- <tr id="msgAtc" style="display:none">
- <td class="label" id="partlist_toggle">
- <span class="iconImg attachmentImg attachmentImage"></span>
- <span class="iconImg" id="partlist_col"></span>
- <span class="iconImg" id="partlist_exp" style="display:none"></span>
- </td>
- <td>
- <div></div>
- <div id="partlist" style="display:none">
- <table></table>
- </div>
- </td>
- </tr>
- <tr id="msgLogInfo" style="display:none">
- <td class="label" id="msgloglist_toggle">
- <span class="iconImg" id="msgloglist_col"></span>
- <span class="iconImg" id="msgloglist_exp" style="display:none"></span>
- </td>
- <td>
- <div><span class="msgLogLabel"><?php echo _("Message Log") ?></span></div>
- <div id="msgloglist" style="display:none">
- <ul></ul>
- </div>
+<?php endif; ?>
+ </div>
+ <div id="msgHeadersContent">
+ <table>
+ <thead>
+ <tr>
+ <td class="label"><?php echo _("Subject") ?>:</td>
+ <td class="subject"></td>
+ </tr>
+ <tr id="msgHeaderFrom">
+ <td class="label"><?php echo _("From") ?>:</td>
+ <td class="from"></td>
+ </tr>
+ <tr id="msgHeaderDate">
+ <td class="label"><?php echo _("Date") ?>:</td>
+ <td class="date"></td>
+ </tr>
+ <tr id="msgHeaderTo">
+ <td class="label"><?php echo _("To") ?>:</td>
+ <td class="to"></td>
+ </tr>
+ <tr id="msgHeaderCc">
+ <td class="label"><?php echo _("Cc") ?>:</td>
+ <td class="cc"></td>
+ </tr>
+ <tr id="msgAtc" style="display:none">
+ <td class="label" id="partlist_toggle">
+ <span class="iconImg attachmentImg attachmentImage"></span>
+ <span class="iconImg" id="partlist_col"></span>
+ <span class="iconImg" id="partlist_exp" style="display:none"></span>
</td>
- </tr>
- </thead>
- </table>
- </div>
+ <td>
+ <div></div>
+ <div id="partlist" style="display:none">
+ <table></table>
+ </div>
+ </td>
+ </tr>
+ <tr id="msgLogInfo" style="display:none">
+ <td class="label" id="msgloglist_toggle">
+ <span class="iconImg" id="msgloglist_col"></span>
+ <span class="iconImg" id="msgloglist_exp" style="display:none"></span>
+ </td>
+ <td>
+ <div><span class="msgLogLabel"><?php echo _("Message Log") ?></span></div>
+ <div id="msgloglist" style="display:none">
+ <ul></ul>
+ </div>
+ </td>
+ </tr>
+ </thead>
+ </table>
</div>
</div>
- <div id="messageBody" class="messageBody"></div>
</div>
+ <div id="messageBody" class="messageBody"></div>
</div>
</div>
</div>
<div class="context" id="ctx_otheractions" style="display:none">
<a id="oa_preview_hide"><span class="contextImg"></span><?php echo _("Hide Preview") ?></a>
<a id="oa_preview_show"><span class="contextImg"></span><?php echo _("Show Preview") ?></a>
+ <a id="oa_layout_horiz"><span class="contextImg"></span><?php echo _("Horizontal Layout") ?></a>
+ <a id="oa_layout_vert"><span class="contextImg"></span><?php echo _("Vertical Layout") ?></a>
<?php if (!empty($flag_list)): ?>
<div>
<div class="sep"></div>
'name' => $GLOBALS['registry']->get('name', 'imp'),
'popup_height' => 610,
'popup_width' => 820,
- 'preview_pref' => intval($GLOBALS['prefs']->getValue('dimp_show_preview')),
+ 'preview_pref' => $GLOBALS['prefs']->getValue('dimp_show_preview'),
'qsearchid' => IMP_Search::MBOX_PREFIX . IMP_Search::DIMP_QUICKSEARCH,
'qsearchfield' => $GLOBALS['prefs']->getValue('dimp_qsearch_field'),
'refresh_time' => intval($GLOBALS['prefs']->getValue('refresh_time')),
height: auto;
}
-#sidebarPanel .sepfull, #splitBar, .context div.sep {
+#sidebarPanel .sepfull, .splitBar, .context div.sep {
overflow: hidden;
}
overflow-y: auto;
}
-.msgSplitPane {
+#msgSplitPane {
width: 99.7%;
}
}
/* SplitPane styles. */
-.msgSplitPane {
+#msgSplitPane {
border-left: 1px silver solid;
border-right: 1px silver solid;
border-bottom: 1px silver solid;
position: relative;
}
-#splitBar {
- background: #e9e9e9 url("graphics/dragHandle.png") no-repeat scroll center top !important;
+.splitBarHoriz, .splitBarVert {
+ background: #e9e9e9 no-repeat scroll;
border: 1px solid silver;
- cursor: n-resize;
- height: 5px;
z-index: 10;
}
+.splitBarHoriz {
+ background-image: url("graphics/dragHandle.png");
+ background-position: center top;
+ cursor: ns-resize;
+ height: 5px;
+}
+.splitBarVert {
+ background-image: url("graphics/dragHandleVert.png");
+ background-position: center;
+ cursor: ew-resize;
+ height: 300px;
+ margin-bottom: -1px;
+ margin-top: -1px;
+ width: 5px;
+}
/* Message List */
.mboxheader {
position: absolute;
margin: 10px 0 0 10px;
}
-#msgLoading {
- /* Positioned relative to .splitBar, which is 7px high by default. */
- margin-top: 17px;
-}
/* Columns */
div.msgStatus {
font-weight: bold;
overflow: hidden;
border: 1px solid silver;
+ border-top: 0;
background: transparent url("graphics/backhead_orderby.png") repeat-x;
}
#msglistHeader a {
}
/* Message ViewPort */
-.msglist {
- overflow: hidden;
- width: 100%;
-}
.msglist span.vpEmpty, .msglist span.vpError {
font-weight: bold;
}
-o-text-overflow: ellipsis;
height: 16px;
border-bottom: 1px;
- text-indent: 2px;
}
.msglist div.vpRowSelected {
background: #ffa !important;
}
-.msglist div.vpRow div.sep {
+.msglist div.vpRowHoriz div.sep {
border-right: 1px solid #ddd;
}
+.msglist div.vpRowHoriz, .msglist div.vpRowHoriz div {
+ text-indent: 2px;
+}
/* Rows: flags. */
-div.vpRow.flagLowpriority {
+div.flagLowpriority {
font-weight: normal !important;
}
-div.vpRow.flagUnseen {
+div.flagUnseen {
font-weight: bold;
}
-div.vpRow.flagDeleted {
+div.flagDeleted {
text-decoration: line-through;
}
background-image: url("graphics/checkbox_on.png");
}
-/* Scroller */
-.sbdiv {
+/* Rows (vertical mode) */
+.msglist div.vpRowVert {
+ height: auto;
+}
+.msglist div.vpRowVert div.msgStatus {
+ height: 24px;
+ padding-right: 4px;
+ padding-top: 8px;
+ width: 20%;
+}
+.msglist div.vpRowVert div.msgFrom {
+ font-weight: bold;
+ width: auto;
+}
+.msglist div.vpRowVert div.msgDate {
+ float: right;
+ text-indent: 2px;
+ width: auto;
+}
+.msglist div.vpRowVert div.msgSubject {
+ float: none;
+ font-style: italic;
+ width: auto;
+}
+
+/* ViewPort - scroller */
+.vpScroll {
background: url("graphics/scroller_back.png") repeat-y;
- overflow: hidden;
- position: absolute;
- right: 0;
- top: 0;
- width: 14px;
}
-.sbcursor {
+.vpScrollCursor {
background: url("graphics/scroller.png") repeat-y;
border-left: 1px solid silver;
width: 13px;
}
-.sbup, .sbdown {
+.vpScrollDown, .vpScrollUp {
border-left: 1px solid silver;
height: 16px;
width: 13px;
}
-.sbup {
+.vpScrollUp {
background-image: url("graphics/sbcursor_top.png");
}
-.sbdown {
+.vpScrollDown {
background-image: url("graphics/sbcursor_bottom.png");
}
background: #efefef;
padding: 4px 2px;
border: 1px silver solid;
- border-bottom: 0;
background: transparent url("graphics/backhead_s2.png") repeat-x;
height: 16px;
}
cursor: default;
margin: 0;
}
+
#button_compose, #button_checkmail {
display: none;
}
overflow: visible !important;
}
- .msgSplitPane {
+ #msgSplitPane {
border: 0;
}
display: block !important;
}
- #sidebarPanel, #dimpmain_folder_top, #msgList, div.sbdiv, #splitBar, #toggleHeaders, #Growler, #GrowlerLog, #writemsg, .dimpActions, .dimpOptions {
+ #sidebarPanel, #dimpmain_folder_top, #msgSplitPane .msglist, div.sbdiv, .splitBar, #toggleHeaders, #Growler, #GrowlerLog, #writemsg, .dimpActions, .dimpOptions {
display: none !important;
}