Thomas Bruederli
2014-06-05 99cdca46b7bcc46fe6affd9e9f9f60a546b2e5b8
program/js/list.js
@@ -51,7 +51,7 @@
  this.rowcount = 0;
  this.colcount = 0;
  this.subject_col = -1;
  this.subject_col = 0;
  this.modkey = 0;
  this.multiselect = false;
  this.multiexpand = false;
@@ -60,6 +60,7 @@
  this.column_movable = false;
  this.keyboard = false;
  this.toggleselect = false;
  this.aria_listbox = false;
  this.drag_active = false;
  this.col_drag_active = false;
@@ -75,6 +76,9 @@
  if (p && typeof p === 'object')
    for (var n in p)
      this[n] = p[n];
  // register this instance
  rcube_list_widget._instances.push(this);
};
@@ -94,6 +98,12 @@
    this.tbody = this.list;
  }
  if ($(this.list).attr('role') == 'listbox') {
    this.aria_listbox = true;
    if (this.multiselect)
      $(this.list).attr('aria-multiselectable', 'true');
  }
  if (this.tbody) {
    this.rows = {};
    this.rowcount = 0;
@@ -111,15 +121,9 @@
    if (this.keyboard) {
      rcube_event.add_listener({event:'keydown', object:this, method:'key_press'});
      // install a link element to receive focus.
      // this helps to maintain the natural tab order when moving focus with keyboard
      this.focus_elem = $('<a>')
        .attr('tabindex', '0')
        .attr('style', 'display:block; width:1px; height:1px; line-height:1px; overflow:hidden; position:fixed; top:-1000px')
        .html($(this.list).attr('summary') || 'Select List')
        .insertAfter(this.list)
        .on('focus', function(e){ me.focus(e); })
        .on('blur', function(e){ me.blur(e); });
      // allow the table element to receive focus.
      $(this.list).attr('tabindex', '0')
        .on('focus', function(e){ me.focus(e); });
    }
  }
@@ -165,6 +169,15 @@
      }, false);
    }
    // label the list row with the subject col as descriptive label
    if (this.aria_listbox) {
      var lbl_id = 'l:' + row.id;
      $(row)
        .attr('role', 'option')
        .attr('aria-labelledby', lbl_id)
        .find(this.col_tagname()).eq(this.subject_col).attr('id', lbl_id);
    }
    if (document.all)
      row.onselectstart = function() { return false; };
@@ -186,7 +199,7 @@
    if (this.fixed_header) {  // copy (modified) fixed header back to the actual table
      $(this.list.tHead).replaceWith($(this.fixed_header).find('thead').clone());
      $(this.list.tHead).find('tr td').attr('style', '').find('a').attr('tabindex', '-1');  // remove fixed widths
      $(this.list.tHead).find('th,td').attr('style', '').find('a').attr('tabindex', '-1');  // remove fixed widths
    }
    else if (!bw.touch && this.list.className.indexOf('fixedheader') >= 0) {
      this.init_fixed_header();
@@ -213,6 +226,7 @@
  if (!this.fixed_header) {
    this.fixed_header = $('<table>')
      .attr('class', this.list.className + ' fixedcopy')
      .attr('role', 'presentation')
      .css({ position:'fixed' })
      .append(clone)
      .append('<tbody></tbody>');
@@ -279,11 +293,10 @@
  this.rows = {};
  this.rowcount = 0;
  this.last_selected = 0;
  if (sel)
    this.clear_selection();
  else
    this.last_selected = 0;
  // reset scroll position (in Opera)
  if (this.frame)
@@ -332,7 +345,7 @@
    if (row.id) domrow.id = row.id;
    if (row.className) domrow.className = row.className;
    if (row.style) $.extend(domrow.style, row.style);
    if (row.uid) $(domrow).data('uid', row.uid);
    if (row.uid) $(domrow).data('uid', String(row.uid)); // #1489906
    for (var e, domcell, col, i=0; row.cols && i < row.cols.length; i++) {
      col = row.cols[i];
@@ -392,23 +405,21 @@
  if (this.focused)
    return;
  var n, id;
  this.focused = true;
  for (n in this.selection) {
    id = this.selection[n];
    if (this.rows[id] && this.rows[id].obj) {
      $(this.rows[id].obj).addClass('selected').removeClass('unfocused');
    }
  }
  if (e)
    rcube_event.cancel(e);
  var focus_elem = null;
  if (this.last_selected && this.rows[this.last_selected]) {
    focus_elem = $(this.rows[this.last_selected].obj).find(this.col_tagname()).eq(this.subject_col).attr('tabindex', '0');
  }
  // Un-focus already focused elements (#1487123, #1487316, #1488600, #1488620)
  if (this.focus_elem) {
  if (focus_elem && focus_elem.length) {
    // We now fix this by explicitly assigning focus to a dedicated link element
    this.focus_noscroll(this.focus_elem);
    this.focus_noscroll(focus_elem);
  }
  else {
    // It looks that window.focus() does the job for all browsers, but not Firefox (#1489058)
@@ -416,7 +427,7 @@
    window.focus();
  }
  $(this.list).addClass('focus');
  $(this.list).addClass('focus').removeAttr('tabindex');
  // set internal focus pointer to first row
  if (!this.last_selected)
@@ -429,16 +440,18 @@
 */
blur: function(e)
{
  var n, id;
  this.focused = false;
  for (n in this.selection) {
    id = this.selection[n];
    if (this.rows[id] && this.rows[id].obj) {
      $(this.rows[id].obj).removeClass('selected focused').addClass('unfocused');
    }
  // avoid the table getting focus right again
  var me = this;
  setTimeout(function(){
    $(me.list).removeClass('focus').attr('tabindex', '0');
  }, 20);
  if (this.last_selected && this.rows[this.last_selected]) {
    $(this.rows[this.last_selected].obj)
      .find(this.col_tagname()).eq(this.subject_col).removeAttr('tabindex');
  }
  $(this.list).removeClass('focus');
},
/**
@@ -842,9 +855,9 @@
get_first_row: function()
{
  if (this.rowcount) {
    var i, len, uid, rows = this.tbody.childNodes;
    var i, uid, rows = this.tbody.childNodes;
    for (i=0, len=rows.length-1; i<len; i++)
    for (i=0; i<rows.length; i++)
      if (rows[i].id && (uid = this.get_row_uid(rows[i])))
        return uid;
  }
@@ -887,9 +900,10 @@
 */
select_row: function(id, mod_key, with_mouse)
{
  var select_before = this.selection.join(',');
  var select_before = this.selection.join(','),
    in_selection_before = this.in_selection(id);
  if (!this.multiselect)
  if (!this.multiselect && with_mouse)
    mod_key = 0;
  if (!this.shift_start)
@@ -925,20 +939,26 @@
    this.multi_selecting = true;
  }
  // trigger event if selection changed
  if (this.selection.join(',') != select_before)
    this.triggerEvent('select');
  if (this.last_selected != 0 && this.rows[this.last_selected])
    $(this.rows[this.last_selected].obj).removeClass('focused');
  if (this.last_selected != 0 && this.rows[this.last_selected]) {
    $(this.rows[this.last_selected].obj).removeClass('focused')
      .find(this.col_tagname()).eq(this.subject_col).removeAttr('tabindex');
  }
  // unselect if toggleselect is active and the same row was clicked again
  if (this.toggleselect && this.last_selected == id) {
  if (this.toggleselect && in_selection_before) {
    this.clear_selection();
    id = null;
  }
  else
  // trigger event if selection changed
  else if (this.selection.join(',') != select_before) {
    this.triggerEvent('select');
  }
  if (this.rows[id]) {
    $(this.rows[id].obj).addClass('focused');
    // set cursor focus to link inside selected row
    if (this.focused)
      this.focus_noscroll($(this.rows[id].obj).find(this.col_tagname()).eq(this.subject_col).attr('tabindex', '0'));
  }
  if (!this.selection.length)
    this.shift_start = null;
@@ -1086,7 +1106,7 @@
      this.highlight_row(n, true, true);
    }
    else {
      $(this.rows[n].obj).removeClass('selected').removeClass('unfocused').removeAttr('aria-selected');
      $(this.rows[n].obj).removeClass('selected').removeAttr('aria-selected');
    }
  }
@@ -1143,7 +1163,7 @@
  else {
    for (n in this.selection)
      if (this.rows[this.selection[n]]) {
        $(this.rows[this.selection[n]].obj).removeClass('selected').removeClass('unfocused').removeAttr('aria-selected');
        $(this.rows[this.selection[n]].obj).removeClass('selected').removeAttr('aria-selected');
      }
    this.selection = [];
@@ -1222,7 +1242,7 @@
        a_post = this.selection.slice(p+1, this.selection.length);
      this.selection = a_pre.concat(a_post);
      $(this.rows[id].obj).removeClass('selected').removeClass('unfocused').removeAttr('aria-selected');
      $(this.rows[id].obj).removeClass('selected').removeAttr('aria-selected');
      if (!norecur && !this.rows[id].expanded)
        this.highlight_children(id, false);
    }
@@ -1301,6 +1321,14 @@
      }
      return rcube_event.cancel(e);
    case 9: // Tab
      this.blur();
      break;
    case 13: // Enter
      if (!this.selection.length)
        this.select_row(this.last_selected, mod_key, false);
    default:
      this.key_pressed = keyCode;
@@ -1766,3 +1794,6 @@
rcube_list_widget.prototype.addEventListener = rcube_event_engine.prototype.addEventListener;
rcube_list_widget.prototype.removeEventListener = rcube_event_engine.prototype.removeEventListener;
rcube_list_widget.prototype.triggerEvent = rcube_event_engine.prototype.triggerEvent;
// static
rcube_list_widget._instances = [];