| | |
| | | scroll_delay: 500, |
| | | scroll_step: 5, |
| | | scroll_speed: 20, |
| | | save_state: false, |
| | | check_droptarget: function(node){ return !node.virtual } |
| | | }, p || {}); |
| | | |
| | |
| | | indexbyid = {}, |
| | | selection = null, |
| | | drag_active = false, |
| | | search_active = false, |
| | | last_search = '', |
| | | box_coords = {}, |
| | | item_coords = [], |
| | | autoexpand_timer, |
| | |
| | | body_scroll_top = 0, |
| | | list_scroll_top = 0, |
| | | scroll_timer, |
| | | searchfield, |
| | | tree_state, |
| | | list_id = (container.attr('id') || id_prefix || '0'), |
| | | me = this; |
| | | |
| | | |
| | |
| | | } |
| | | }); |
| | | |
| | | // 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 |
| | | |
| | |
| | | } |
| | | |
| | | me.triggerEvent(node.collapsed ? 'collapse' : 'expand', node); |
| | | save_state(id, node.collapsed); |
| | | } |
| | | } |
| | | |
| | |
| | | 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); |
| | | |
| | |
| | | else { |
| | | // 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 |
| | |
| | | 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); |
| | | } |
| | | |
| | | /** |
| | |
| | | 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) |
| | |
| | | { |
| | | 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) |
| | | } |
| | | |
| | |
| | | } |
| | | 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); |
| | | } |
| | |
| | | */ |
| | | 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; |
| | | } |
| | | |
| | |
| | | */ |
| | | 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); |
| | | } |
| | | |
| | | /** |
| | |
| | | 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 |
| | | |
| | | /** |