diff options
Diffstat (limited to 'program/js')
| -rw-r--r-- | program/js/treelist.js | 196 | 
1 files changed, 191 insertions, 5 deletions
diff --git a/program/js/treelist.js b/program/js/treelist.js index b5629a658..a861b4987 100644 --- a/program/js/treelist.js +++ b/program/js/treelist.js @@ -44,6 +44,7 @@ function rcube_treelist_widget(node, p)      scroll_delay: 500,      scroll_step: 5,      scroll_speed: 20, +    save_state: false,      check_droptarget: function(node){ return !node.virtual }    }, p || {}); @@ -52,6 +53,8 @@ function rcube_treelist_widget(node, p)      indexbyid = {},      selection = null,      drag_active = false, +    search_active = false, +    last_search = '',      box_coords = {},      item_coords = [],      autoexpand_timer, @@ -59,6 +62,9 @@ function rcube_treelist_widget(node, p)      body_scroll_top = 0,      list_scroll_top = 0,      scroll_timer, +    searchfield, +    tree_state, +    list_id = (container.attr('id') || id_prefix || '0'),      me = this; @@ -105,6 +111,43 @@ function rcube_treelist_widget(node, p)      }    }); +  // activate search function +  if (p.searchbox) { +    searchfield = $(p.searchbox).on('keyup', function(e) { +      var key = rcube_event.get_keycode(e), +        mod = rcube_event.get_modifier(e); + +      switch (key) { +        case 9:   // tab +          break; + +        case 13:  // enter +          search(this.value, true); +          return rcube_event.cancel(e); + +        case 27:  // escape +          reset_search(); +          break; + +        case 38:  // arrow up +        case 37:  // left +        case 39:  // right +        case 40:  // arrow down +          return;  // ignore arrow keys + +        default: +          search(this.value, false); +          break; +      } +    }).attr('autocomplete', 'off'); + +    // find the reset button for this search field +    searchfield.parent().find('a.reset').click(function(e) { +      reset_search(); +      return false; +    }) +  } +    /////// private methods @@ -126,6 +169,7 @@ function rcube_treelist_widget(node, p)        }        me.triggerEvent(node.collapsed ? 'collapse' : 'expand', node); +      save_state(id, node.collapsed);      }    } @@ -191,13 +235,21 @@ function rcube_treelist_widget(node, p)    function insert(node, parent_id, sort)    {      var li, parent_li, -      parent_node = parent_id ? indexbyid[parent_id] : null; +      parent_node = parent_id ? indexbyid[parent_id] : null +      search_ = search_active; + +    // apply saved state +    state = get_state(node.id, node.collapsed); +    if (state !== undefined) { +      node.collapsed = state; +    }      // insert as child of an existing node      if (parent_node) {        if (!parent_node.children)          parent_node.children = []; +      search_active = false;        parent_node.children.push(node);        parent_li = id2dom(parent_id); @@ -210,6 +262,19 @@ function rcube_treelist_widget(node, p)          // append new node to parent's child list          li = render_node(node, parent_li.children('ul').first());        } + +      // list is in search mode +      if (search_) { +        search_active = search_; + +        // add clone to current search results (top level) +        $('<li>') +          .attr('id', li.attr('id') + '--xsR') +          .attr('class', li.attr('class')) +          .addClass('searchresult__') +          .append(li.children().first().clone(true, true)) +          .appendTo(container); +      }      }      // insert at top level      else { @@ -335,6 +400,80 @@ function rcube_treelist_widget(node, p)      drag_active = false;      container.html(''); + +    reset_search(); +  } + +  /** +   *  +   */ +  function search(q, enter) +  { +    q = String(q).toLowerCase(); + +    if (!q.length) +      return reset_search(); +    else if (q == last_search && !enter) +      return 0; + +    var hits = []; +    var search_tree = function(items) { +      $.each(items, function(i, node) { +        var li, sli; +        if (!node.virtual && !node.deleted && String(node.text).toLowerCase().indexOf(q) >= 0) { +          li = id2dom(node.id); +          sli = $('<li>') +            .attr('id', li.attr('id') + '--xsR') +            .attr('class', li.attr('class')) +            .addClass('searchresult__') +            .append(li.children().first().clone(true, true)) +            .appendTo(container); +            hits.push(node.id); +        } + +        if (node.children && node.children.length) { +          search_tree(node.children); +        } +      }); +    }; + +    // reset old search results +    if (search_active) { +      $(container).children('li.searchresult__').remove(); +      search_active = false; +    } + +    // hide all list items +    $(container).children('li').hide().removeClass('selected'); + +    // search recursively in tree (to keep sorting order) +    search_tree(data); +    search_active = true; +    last_search = q; + +    me.triggerEvent('search', { query: q, last: last_search, count: hits.length, ids: hits, execute: enter||false }); + +    return hits.count; +  } + +  /** +   *  +   */ +  function reset_search() +  { +    if (searchfield) +      searchfield.val(''); + +    $(container).children('li.searchresult__').remove(); +    $(container).children('li').show(); + +    search_active = false; + +    me.triggerEvent('search', { query: false, last: last_search }); +    last_search = ''; + +    if (selection) +      select(selection);    }    /** @@ -378,6 +517,9 @@ function rcube_treelist_widget(node, p)      else if (typeof node.html == 'object')        li.append(node.html); +    if (!node.text) +      node.text = li.children().first().text(); +      if (node.virtual)        li.addClass('virtual');      if (node.id == selection) @@ -406,12 +548,13 @@ function rcube_treelist_widget(node, p)    {      var result = [];      ul.children('li').each(function(i,e){ -      var li = $(e), sublist = li.children('ul'); +      var state, li = $(e), sublist = li.children('ul');        var node = {          id: dom2id(li),          classes: String(li.attr('class')).split(' '),          virtual: li.hasClass('virtual'),          html: li.children().first().get(0).outerHTML, +        text: li.children().first().text(),          children: walk_list(sublist)        } @@ -420,6 +563,14 @@ function rcube_treelist_widget(node, p)        }        if (node.children.length) {          node.collapsed = sublist.css('display') == 'none'; + +        // apply saved state +        state = get_state(node.id, node.collapsed); +        if (state !== undefined) { +          node.collapsed = state; +          sublist[(state?'hide':'show')](); +        } +          if (!li.children('div.treetoggle').length)            $('<div class="treetoggle '+(node.collapsed ? 'collapsed' : 'expanded') + '"> </div>').appendTo(li);        } @@ -452,7 +603,7 @@ function rcube_treelist_widget(node, p)     */    function dom2id(li)    { -    var domid = li.attr('id').replace(new RegExp('^' + (p.id_prefix) || '%'), ''); +    var domid = li.attr('id').replace(new RegExp('^' + (p.id_prefix) || '%'), '').replace(/--xsR$/, '');      return p.id_decode ? p.id_decode(domid) : domid;    } @@ -461,8 +612,9 @@ function rcube_treelist_widget(node, p)     */    function id2dom(id)    { -    var domid = p.id_encode ? p.id_encode(id) : id; -    return $('#' + p.id_prefix + domid); +    var domid = p.id_encode ? p.id_encode(id) : id, +      suffix = search_active ? '--xsR' : ''; +    return $('#' + p.id_prefix + domid + suffix);    }    /** @@ -478,6 +630,40 @@ function rcube_treelist_widget(node, p)        scroller.scrollTop(rel_offset + current_offset);    } +  /** +   * Save node collapse state to localStorage +   */ +  function save_state(id, collapsed) +  { +    if (p.save_state && window.rcmail) { +      var key = 'treelist-' + list_id; +      if (!tree_state) { +        tree_state = rcmail.local_storage_get_item(key, {}); +      } + +      if (tree_state[id] != collapsed) { +        tree_state[id] = collapsed; +        rcmail.local_storage_set_item(key, tree_state); +      } +    } +  } + +  /** +   * Read node collapse state from localStorage +   */ +  function get_state(id) +  { +    if (p.save_state && window.rcmail) { +      if (!tree_state) { +        tree_state = rcmail.local_storage_get_item('treelist-' + list_id, {}); +      } +      return tree_state[id]; +    } + +    return undefined; +  } + +    ///// drag & drop support    /**  | 
