summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Bruederli <thomas@roundcube.net>2013-05-03 11:38:52 +0200
committerThomas Bruederli <thomas@roundcube.net>2013-05-03 11:38:52 +0200
commit517dae3e74e39aceabaf2c5da330849a21e82d81 (patch)
tree6fe7a07764fb7523bc0bd01551f59d159e0e5d5d
parentb92ec5c86d2f85c43705ada6fbcb8912f48907f0 (diff)
Experimental: refactored list.js and html_table class to render lists with different html structures (e.g. table, ul, div). This show provide more flexibility to skin designers and improve mobile device support
-rw-r--r--program/include/rcmail.php2
-rw-r--r--program/js/app.js55
-rw-r--r--program/js/list.js173
-rw-r--r--program/lib/Roundcube/html.php34
-rw-r--r--program/steps/mail/func.inc16
5 files changed, 183 insertions, 97 deletions
diff --git a/program/include/rcmail.php b/program/include/rcmail.php
index 7acb3490d..6257f4925 100644
--- a/program/include/rcmail.php
+++ b/program/include/rcmail.php
@@ -1167,7 +1167,7 @@ class rcmail extends rcube
*/
public function table_output($attrib, $table_data, $a_show_cols, $id_col)
{
- $table = new html_table(/*array('cols' => count($a_show_cols))*/);
+ $table = new html_table($attrib);
// add table header
if (!$attrib['noheader']) {
diff --git a/program/js/app.js b/program/js/app.js
index 962651d17..a652b8985 100644
--- a/program/js/app.js
+++ b/program/js/app.js
@@ -1579,7 +1579,7 @@ function rcube_webmail()
this.msglist_set_coltypes = function(list)
{
- var i, found, name, cols = list.list.tHead.rows[0].cells;
+ var i, found, name, cols = list.thead.rows[0].cells;
this.env.coltypes = [];
@@ -1733,10 +1733,7 @@ function rcube_webmail()
+ (flags.flagged ? ' flagged' : '')
+ (flags.unread_children && flags.seen && !this.env.autoexpand_threads ? ' unroot' : '')
+ (message.selected ? ' selected' : ''),
- // for performance use DOM instead of jQuery here
- row = document.createElement('tr');
-
- row.id = 'rcmrow'+uid;
+ row = { cols:[], style:{}, id:'rcmrow'+uid };
// message status icons
css_class = 'msgicon';
@@ -1800,8 +1797,7 @@ function rcube_webmail()
// add each submitted col
for (n in this.env.coltypes) {
c = this.env.coltypes[n];
- col = document.createElement('td');
- col.className = String(c).toLowerCase();
+ col = { className: String(c).toLowerCase() };
if (c == 'flag') {
css_class = (flags.flagged ? 'flagged' : 'unflagged');
@@ -1846,8 +1842,7 @@ function rcube_webmail()
html = cols[c];
col.innerHTML = html;
-
- row.appendChild(col);
+ row.cols.push(col);
}
list.insert_row(row, attop);
@@ -2210,7 +2205,7 @@ function rcube_webmail()
if (root)
row = rows[root] ? rows[root].obj : null;
else
- row = this.message_list.list.tBodies[0].firstChild;
+ row = this.message_list.tbody.firstChild;
while (row) {
if (row.nodeType == 1 && (r = rows[row.uid])) {
@@ -2386,7 +2381,7 @@ function rcube_webmail()
this.delete_excessive_thread_rows = function()
{
var rows = this.message_list.rows,
- tbody = this.message_list.list.tBodies[0],
+ tbody = this.message_list.tbody,
row = tbody.firstChild,
cnt = this.env.pagesize + 1;
@@ -4329,21 +4324,7 @@ function rcube_webmail()
newcid = newcid+'-'+source;
}
- if (list.rows[cid] && (row = list.rows[cid].obj)) {
- for (c=0; c<cols_arr.length; c++)
- if (row.cells[c])
- $(row.cells[c]).html(cols_arr[c]);
-
- // cid change
- if (newcid) {
- newcid = this.html_identifier(newcid);
- row.id = 'rcmrow' + newcid;
- list.remove_row(cid);
- list.init_row(row);
- list.selection[0] = newcid;
- row.style.display = '';
- }
- }
+ list.update_row(cid, cols_arr, newcid, true);
};
// add row to contacts list
@@ -4353,7 +4334,7 @@ function rcube_webmail()
return false;
var c, col, list = this.contact_list,
- row = document.createElement('tr');
+ row = { cols:[] };
row.id = 'rcmrow'+this.html_identifier(cid);
row.className = 'contact ' + (classes || '');
@@ -4363,10 +4344,10 @@ function rcube_webmail()
// add each submitted col
for (c in cols) {
- col = document.createElement('td');
+ col = {};
col.className = String(c).toLowerCase();
col.innerHTML = cols[c];
- row.appendChild(col);
+ row.cols.push(col);
}
list.insert_row(row);
@@ -4965,18 +4946,16 @@ function rcube_webmail()
this.update_identity_row = function(id, name, add)
{
- var row, col, list = this.identity_list,
+ var list = this.identity_list,
rid = this.html_identifier(id);
- if (list.rows[rid] && (row = list.rows[rid].obj)) {
- $(row.cells[0]).html(name);
- }
- else if (add) {
- row = $('<tr>').attr('id', 'rcmrow'+rid).get(0);
- col = $('<td>').addClass('mail').html(name).appendTo(row);
- list.insert_row(row);
+ if (add) {
+ list.insert_row({ id:'rcmrow'+rid, cols:[ { className:'mail', innerHTML:name } ] });
list.select(rid);
}
+ else {
+ list.update_row(rid, [ name ]);
+ }
};
@@ -5751,7 +5730,7 @@ function rcube_webmail()
this.set_message_coltypes = function(coltypes, repl, smart_col)
{
var list = this.message_list,
- thead = list ? list.list.tHead : null,
+ thead = list ? list.thead : null,
cell, col, n, len, th, tr;
this.env.coltypes = coltypes;
diff --git a/program/js/list.js b/program/js/list.js
index b3b619073..dd99fd2f2 100644
--- a/program/js/list.js
+++ b/program/js/list.js
@@ -30,6 +30,9 @@ function rcube_list_widget(list, p)
this.BACKSPACE_KEY = 8;
this.list = list ? list : null;
+ this.tagname = this.list ? this.list.nodeName.toLowerCase() : 'table';
+ this.thead;
+ this.tbody;
this.frame = null;
this.rows = [];
this.selection = [];
@@ -56,7 +59,7 @@ function rcube_list_widget(list, p)
this.focused = false;
this.drag_mouse_start = null;
this.dblclick_time = 600;
- this.row_init = function(){};
+ this.row_init = function(){}; // @deprecated; use list.addEventListener('initrow') instead
// overwrite default paramaters
if (p && typeof p === 'object')
@@ -73,11 +76,19 @@ rcube_list_widget.prototype = {
*/
init: function()
{
- if (this.list && this.list.tBodies[0]) {
+ if (this.tagname == 'table' && this.list && this.list.tBodies[0]) {
+ this.thead = this.list.tHead;
+ this.tbody = this.list.tBodies[0];
+ }
+ else if (this.tagname != 'table' && this.list) {
+ this.tbody = this.list;
+ }
+
+ if (this.tbody) {
this.rows = [];
this.rowcount = 0;
- var r, len, rows = this.list.tBodies[0].rows;
+ var r, len, rows = this.tbody.childNodes;
for (r=0, len=rows.length; r<len; r++) {
this.init_row(rows[r]);
@@ -127,7 +138,8 @@ init_row: function(row)
if (document.all)
row.onselectstart = function() { return false; };
- this.row_init(this.rows[uid]);
+ this.row_init(this.rows[uid]); // legacy support
+ this.triggerEvent('initrow', this.rows[uid]);
}
},
@@ -137,16 +149,16 @@ init_row: function(row)
*/
init_header: function()
{
- if (this.list && this.list.tHead) {
+ if (this.thead) {
this.colcount = 0;
var col, r, p = this;
// add events for list columns moving
- if (this.column_movable && this.list.tHead && this.list.tHead.rows) {
- for (r=0; r<this.list.tHead.rows[0].cells.length; r++) {
+ if (this.column_movable && this.thead && this.thead.rows) {
+ for (r=0; r<this.thead.rows[0].cells.length; r++) {
if (this.column_fixed == r)
continue;
- col = this.list.tHead.rows[0].cells[r];
+ col = this.thead.rows[0].cells[r];
col.onmousedown = function(e){ return p.drag_column(e, this); };
this.colcount++;
}
@@ -160,10 +172,16 @@ init_header: function()
*/
clear: function(sel)
{
- var tbody = document.createElement('tbody');
+ if (this.tagname == 'table') {
+ var tbody = document.createElement('tbody');
+ this.list.insertBefore(tbody, this.tbody);
+ this.list.removeChild(this.list.tBodies[1]);
+ this.tbody = tbody;
+ }
+ else {
+ $(this.row_tagname() + ':not(.thead)', this.tbody).remove();
+ }
- this.list.insertBefore(tbody, this.list.tBodies[0]);
- this.list.removeChild(this.list.tBodies[1]);
this.rows = [];
this.rowcount = 0;
@@ -181,12 +199,12 @@ clear: function(sel)
*/
remove_row: function(uid, sel_next)
{
- var obj = this.rows[uid] ? this.rows[uid].obj : null;
+ var node = this.rows[uid] ? this.rows[uid].obj : null;
- if (!obj)
+ if (!node)
return;
- obj.style.display = 'none';
+ node.style.display = 'none';
if (sel_next)
this.select_next();
@@ -201,9 +219,28 @@ remove_row: function(uid, sel_next)
*/
insert_row: function(row, attop)
{
- var tbody = this.list.tBodies[0];
+ var tbody = this.tbody;
+
+ // create a real dom node first
+ if (row.nodeName === undefined) {
+ // for performance reasons use DOM instead of jQuery here
+ var domrow = document.createElement(this.row_tagname());
+ if (row.id) domrow.id = row.id;
+ if (row.className) domrow.className = row.className;
+ if (row.style) $.extend(domrow.style, row.style);
+
+ for (var domcell, col, i=0; row.cols && i < row.cols.length; i++) {
+ col = row.cols[i];
+ domcell = document.createElement(this.col_tagname());
+ if (col.className) domcell.className = col.className;
+ if (col.innerHTML) domcell.innerHTML = col.innerHTML;
+ domrow.appendChild(domcell);
+ }
+
+ row = domrow;
+ }
- if (attop && tbody.rows.length)
+ if (attop && tbody.childNodes.length)
tbody.insertBefore(row, tbody.firstChild);
else
tbody.appendChild(row);
@@ -212,6 +249,28 @@ insert_row: function(row, attop)
this.rowcount++;
},
+/**
+ *
+ */
+update_row: function(id, cols, newid, select)
+{
+ var row = this.rows[id];
+ if (!row) return false;
+
+ var domrow = row.obj;
+ for (var domcell, col, i=0; cols && i < cols.length; i++) {
+ this.get_cell(domrow, i).html(cols[i]);
+ }
+
+ if (newid) {
+ delete this.rows[id];
+ domrow.id = 'rcmrow' + newid;
+ this.init_row(domrow);
+
+ if (select)
+ this.selection[0] = newid;
+ }
+},
/**
@@ -269,8 +328,8 @@ drag_column: function(e, col)
this.add_dragfix();
// find selected column number
- for (var i=0; i<this.list.tHead.rows[0].cells.length; i++) {
- if (col == this.list.tHead.rows[0].cells[i]) {
+ for (var i=0; i<this.thead.rows[0].cells.length; i++) {
+ if (col == this.thead.rows[0].cells[i]) {
this.selected_column = i;
break;
}
@@ -449,7 +508,7 @@ expand: function(row)
this.triggerEvent('expandcollapse', { uid:row.uid, expanded:row.expanded, obj:row.obj });
}
else {
- var tbody = this.list.tBodies[0];
+ var tbody = this.tbody;
new_row = tbody.firstChild;
depth = 0;
last_expanded_parent_depth = 0;
@@ -502,7 +561,7 @@ collapse_all: function(row)
return false;
}
else {
- new_row = this.list.tBodies[0].firstChild;
+ new_row = this.tbody.firstChild;
depth = 0;
}
@@ -541,7 +600,7 @@ expand_all: function(row)
this.triggerEvent('expandcollapse', { uid:row.uid, expanded:row.expanded, obj:row.obj });
}
else {
- new_row = this.list.tBodies[0].firstChild;
+ new_row = this.tbody.firstChild;
depth = 0;
}
@@ -609,7 +668,7 @@ get_prev_row: function()
get_first_row: function()
{
if (this.rowcount) {
- var i, len, rows = this.list.tBodies[0].rows;
+ var i, len, rows = this.tbody.childNodes;
for (i=0, len=rows.length-1; i<len; i++)
if (rows[i].id && String(rows[i].id).match(/^rcmrow([a-z0-9\-_=\+\/]+)/i) && this.rows[RegExp.$1] != null)
@@ -622,7 +681,7 @@ get_first_row: function()
get_last_row: function()
{
if (this.rowcount) {
- var i, rows = this.list.tBodies[0].rows;
+ var i, rows = this.tbody.childNodes;
for (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)
@@ -632,6 +691,22 @@ get_last_row: function()
return null;
},
+row_tagname: function()
+{
+ var row_tagnames = { table:'tr', ul:'li', '*':'div' };
+ return row_tagnames[this.tagname] || row_tagnames['*'];
+},
+
+col_tagname: function()
+{
+ var col_tagnames = { table:'td', '*':'span' };
+ return col_tagnames[this.tagname] || col_tagnames['*'];
+},
+
+get_cell: function(row, index)
+{
+ return $(this.col_tagname(), row).eq(index);
+},
/**
* selects or unselects the proper row depending on the modifier key pressed
@@ -779,19 +854,19 @@ shift_select: function(id, control)
this.shift_start = id;
var n, i, j, to_row = this.rows[id],
- from_rowIndex = this.rows[this.shift_start].obj.rowIndex,
- to_rowIndex = to_row.obj.rowIndex;
+ from_rowIndex = this._rowIndex(this.rows[this.shift_start].obj),
+ to_rowIndex = this._rowIndex(to_row.obj);
if (!to_row.expanded && to_row.has_children)
if (to_row = this.rows[(this.row_children(id)).pop()])
- to_rowIndex = to_row.obj.rowIndex;
+ to_rowIndex = this._rowIndex(to_row.obj);
i = ((from_rowIndex < to_rowIndex) ? from_rowIndex : to_rowIndex),
j = ((from_rowIndex > to_rowIndex) ? from_rowIndex : to_rowIndex);
// iterate through the entire message list
for (n in this.rows) {
- if (this.rows[n].obj.rowIndex >= i && this.rows[n].obj.rowIndex <= j) {
+ if (this._rowIndex(this.rows[n].obj) >= i && this._rowIndex(this.rows[n].obj) <= j) {
if (!this.in_selection(n)) {
this.highlight_row(n, true);
}
@@ -804,6 +879,13 @@ shift_select: function(id, control)
}
},
+/**
+ * Helper method to emulate the rowIndex property of non-tr elements
+ */
+_rowIndex: function(obj)
+{
+ return (obj.rowIndex !== undefined) ? obj.rowIndex : $(obj).prevAll().length;
+},
/**
* Check if given id is part of the current selection
@@ -1148,7 +1230,7 @@ drag_mouse_move: function(e)
this.draglayer.html('');
// get subjects of selected messages
- var i, n, obj;
+ var i, n, obj, me;
for (n=0; n<this.selection.length; n++) {
// only show 12 lines
if (n>12) {
@@ -1156,29 +1238,28 @@ drag_mouse_move: function(e)
break;
}
+ me = this;
if (obj = this.rows[this.selection[n]].obj) {
- for (i=0; i<obj.childNodes.length; i++) {
- if (obj.childNodes[i].nodeName == 'TD') {
- if (n == 0)
- this.drag_start_pos = $(obj.childNodes[i]).offset();
+ $('> '+this.col_tagname(), obj).each(function(i,elem){
+ if (n == 0)
+ me.drag_start_pos = $(elem).offset();
- if (this.subject_col < 0 || (this.subject_col >= 0 && this.subject_col == i)) {
- var subject = $(obj.childNodes[i]).text();
-
- if (!subject)
- break;
+ if (me.subject_col < 0 || (me.subject_col >= 0 && me.subject_col == i)) {
+ var subject = $(elem).text();
+ if (subject) {
// remove leading spaces
subject = $.trim(subject);
// truncate line to 50 characters
subject = (subject.length > 50 ? subject.substring(0, 50) + '...' : subject);
var entry = $('<div>').text(subject);
- this.draglayer.append(entry);
- break;
+ me.draglayer.append(entry);
}
+
+ return false; // break
}
- }
+ });
}
}
@@ -1253,7 +1334,7 @@ column_drag_mouse_move: function(e)
if (!this.col_draglayer) {
var lpos = $(this.list).offset(),
- cells = this.list.tHead.rows[0].cells;
+ cells = this.thead.rows[0].cells;
// create dragging layer
this.col_draglayer = $('<div>').attr('id', 'rcmcoldraglayer')
@@ -1409,7 +1490,11 @@ del_dragfix: function()
*/
column_replace: function(from, to)
{
- var len, cells = this.list.tHead.rows[0].cells,
+ // only supported for <table> lists
+ if (!this.thead || !this.thead.rows)
+ return;
+
+ var len, cells = this.thead.rows[0].cells,
elem = cells[from],
before = cells[to],
td = document.createElement('td');
@@ -1422,8 +1507,8 @@ column_replace: function(from, to)
cells[0].parentNode.replaceChild(elem, td);
// replace list cells
- for (r=0, len=this.list.tBodies[0].rows.length; r<len; r++) {
- row = this.list.tBodies[0].rows[r];
+ for (r=0, len=this.tbody.rows.length; r<len; r++) {
+ row = this.tbody.rows[r];
elem = row.cells[from];
before = row.cells[to];
diff --git a/program/lib/Roundcube/html.php b/program/lib/Roundcube/html.php
index dbc9ca51f..830ada9c2 100644
--- a/program/lib/Roundcube/html.php
+++ b/program/lib/Roundcube/html.php
@@ -678,6 +678,11 @@ class html_table extends html
{
$default_attrib = self::$doctype == 'xhtml' ? array('summary' => '', 'border' => 0) : array();
$this->attrib = array_merge($attrib, $default_attrib);
+
+ if (!empty($attrib['tagname']) && $attrib['tagname'] != 'table') {
+ $this->tagname = $attrib['tagname'];
+ $this->allowed = self::$common_attrib;
+ }
}
/**
@@ -816,19 +821,20 @@ class html_table extends html
if (!empty($this->header)) {
$rowcontent = '';
foreach ($this->header as $c => $col) {
- $rowcontent .= self::tag('td', $col->attrib, $col->content);
+ $rowcontent .= self::tag($this->_col_tagname(), $col->attrib, $col->content);
}
- $thead = self::tag('thead', null, self::tag('tr', null, $rowcontent, parent::$common_attrib));
+ $thead = $this->tagname == 'table' ? self::tag('thead', null, self::tag('tr', null, $rowcontent, parent::$common_attrib)) :
+ self::tag($this->_row_tagname(), array('class' => 'thead'), $rowcontent, parent::$common_attrib);
}
foreach ($this->rows as $r => $row) {
$rowcontent = '';
foreach ($row->cells as $c => $col) {
- $rowcontent .= self::tag('td', $col->attrib, $col->content);
+ $rowcontent .= self::tag($this->_col_tagname(), $col->attrib, $col->content);
}
if ($r < $this->rowindex || count($row->cells)) {
- $tbody .= self::tag('tr', $row->attrib, $rowcontent, parent::$common_attrib);
+ $tbody .= self::tag($this->_row_tagname(), $row->attrib, $rowcontent, parent::$common_attrib);
}
}
@@ -837,7 +843,7 @@ class html_table extends html
}
// add <tbody>
- $this->content = $thead . self::tag('tbody', null, $tbody);
+ $this->content = $thead . ($this->tagname == 'table' ? self::tag('tbody', null, $tbody) : $tbody);
unset($this->attrib['cols'], $this->attrib['rowsonly']);
return parent::show();
@@ -862,4 +868,22 @@ class html_table extends html
$this->rowindex = 0;
}
+ /**
+ * Getter for the corresponding tag name for table row elements
+ */
+ private function _row_tagname()
+ {
+ static $row_tagnames = array('table' => 'tr', 'ul' => 'li', '*' => 'div');
+ return $row_tagnames[$this->tagname] ?: $row_tagnames['*'];
+ }
+
+ /**
+ * Getter for the corresponding tag name for table cell elements
+ */
+ private function _col_tagname()
+ {
+ static $col_tagnames = array('table' => 'td', '*' => 'span');
+ return $col_tagnames[$this->tagname] ?: $col_tagnames['*'];
+ }
+
}
diff --git a/program/steps/mail/func.inc b/program/steps/mail/func.inc
index f00813ea2..37f37d08f 100644
--- a/program/steps/mail/func.inc
+++ b/program/steps/mail/func.inc
@@ -236,15 +236,13 @@ function rcmail_message_list($attrib)
$OUTPUT->include_script('list.js');
- $thead = '';
- foreach (rcmail_message_list_head($attrib, $a_show_cols) as $cell)
- $thead .= html::tag('td', array('class' => $cell['className'], 'id' => $cell['id']), $cell['html']);
-
- return html::tag('table',
- $attrib,
- html::tag('thead', null, html::tag('tr', null, $thead)) .
- html::tag('tbody', null, ''),
- array('style', 'class', 'id', 'cellpadding', 'cellspacing', 'border', 'summary'));
+ $table = new html_table($attrib);
+ if (!$attrib['noheader']) {
+ foreach (rcmail_message_list_head($attrib, $a_show_cols) as $cell)
+ $table->add_header(array('class' => $cell['className'], 'id' => $cell['id']), $cell['html']);
+ }
+
+ return $table->show();
}