Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F224328
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
27 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/program/js/treelist.js b/program/js/treelist.js
index fec2d7f21..5913f44b4 100644
--- a/program/js/treelist.js
+++ b/program/js/treelist.js
@@ -1,577 +1,575 @@
/*
+-----------------------------------------------------------------------+
| Roundcube Treelist widget |
| |
| This file is part of the Roundcube Webmail client |
| Copyright (C) 2013, The Roundcube Dev Team |
| |
| Licensed under the GNU General Public License version 3 or |
| any later version with exceptions for skins & plugins. |
| See the README file for a full license statement. |
| |
+-----------------------------------------------------------------------+
| Authors: Thomas Bruederli <roundcube@gmail.com> |
+-----------------------------------------------------------------------+
| Requires: common.js |
+-----------------------------------------------------------------------+
*/
/**
* Roundcube Treelist widget class
* @contructor
*/
function rcube_treelist_widget(node, p)
{
- // apply some defaults to p
- p = $.extend({
- id_prefix: '',
- autoexpand: 1000,
- selectable: false,
- check_droptarget: function(node){ return !node.virtual }
- }, p || {});
-
- var container = $(node);
- var data = p.data || [];
- var indexbyid = {};
- var selection = null;
- var drag_active = false;
- var box_coords = {};
- var item_coords = [];
- var autoexpand_timer;
- var autoexpand_item;
- var body_scroll_top = 0;
- var list_scroll_top = 0;
- var me = this;
-
-
- /////// export public members and methods
-
- this.container = container;
- this.expand = expand;
- this.collapse = collapse;
- this.select = select;
- this.render = render;
- this.drag_start = drag_start;
- this.drag_end = drag_end;
- this.intersects = intersects;
- this.update = update_node;
- this.insert = insert;
- this.remove = remove;
- this.get_item = get_item;
- this.get_selection = get_selection;
-
-
- /////// startup code (constructor)
-
- // abort if node not found
- if (!container.length)
- return;
-
- if (p.data) {
- index_data({ children:data });
- }
- // load data from DOM
- else {
- update_data();
- }
-
- // register click handlers on list
- container.on('click', 'div.treetoggle', function(e){
- toggle(dom2id($(this).parent()));
- });
-
- container.on('click', 'li', function(e){
- var node = p.selectable ? indexbyid[dom2id($(this))] : null;
- if (node && !node.virtual) {
- select(node.id);
- e.stopPropagation();
- }
- });
-
-
- /////// private methods
-
- /**
- * Collaps a the node with the given ID
- */
- function collapse(id, recursive, set)
- {
- var node;
- if (node = indexbyid[id]) {
- node.collapsed = typeof set == 'undefined' || set;
- update_dom(node);
-
- // Work around a bug in IE6 and IE7, see #1485309
- if (window.bw && (bw.ie6 || bw.ie7) && node.collapsed) {
- id2dom(node.id).next().children('ul:visible').hide().show();
- }
-
- if (recursive && node.children) {
- for (var i=0; i < node.children.length; i++) {
- collapse(node.children[i].id, recursive, set);
- }
- }
-
- me.triggerEvent(node.collapsed ? 'collapse' : 'expand', node);
- }
- }
-
- /**
- * Expand a the node with the given ID
- */
- function expand(id, recursive)
- {
- collapse(id, recursive, false);
- }
-
- /**
- * Toggle collapsed state of a list node
- */
- function toggle(id, recursive)
- {
- var node;
- if (node = indexbyid[id]) {
- collapse(id, recursive, !node.collapsed);
- }
- }
-
- /**
- * Select a tree node by it's ID
- */
- function select(id)
- {
- if (selection) {
- id2dom(selection).removeClass('selected');
- selection = null;
- }
-
- var li = id2dom(id);
- if (li.length) {
- li.addClass('selected');
- selection = id;
- // TODO: expand all parent nodes if collapsed
- scroll_to_node(li);
- }
-
- me.triggerEvent('select', indexbyid[id]);
- }
-
- /**
- * Getter for the currently selected node ID
- */
- function get_selection()
- {
- return selection;
- }
-
- /**
- * Return the DOM element of the list item with the given ID
- */
- function get_item(id)
- {
- return id2dom(id).get(0);
- }
-
- /**
- * Insert the given node
- */
- function insert(node, parent_id, sort)
- {
- var li, parent_li,
- parent_node = parent_id ? indexbyid[parent_id] : null;
-
- // insert as child of an existing node
- if (parent_node) {
- if (!parent_node.children)
- parent_node.children = [];
-
- parent_node.children.push(node);
- parent_li = id2dom(parent_id);
-
- // re-render the entire subtree
- if (parent_node.children.length == 1) {
- render_node(parent_node, parent_li.parent(), parent_li);
- li = id2dom(node.id);
- }
- else {
- // append new node to parent's child list
- li = render_node(node, parent_li.children('ul').first());
- }
- }
- // insert at top level
- else {
- data.push(node);
- li = render_node(node, container);
- }
-
- indexbyid[node.id] = node;
-
- if (sort) {
- resort_node(li, typeof sort == 'string' ? '[class~="' + sort + '"]' : '');
- }
- }
-
- /**
- * Update properties of an existing node
- */
- function update_node(id, updates, sort)
- {
- var li, node = indexbyid[id];
- if (node) {
- li = id2dom(id);
-
- if (updates.id || updates.html || updates.children || updates.classes) {
- $.extend(node, updates);
- render_node(node, li.parent(), li);
- }
-
- if (node.id != id) {
- delete indexbyid[id];
- indexbyid[node.id] = node;
- }
-
- if (sort) {
- resort_node(li, typeof sort == 'string' ? '[class~="' + sort + '"]' : '');
- }
- }
- }
-
- /**
- * Helper method to sort the list of the given item
- */
- function resort_node(li, filter)
- {
- var first, sibling,
- myid = li.get(0).id,
- sortname = li.children().first().text().toUpperCase();
-
- li.parent().children('li' + filter).each(function(i, elem) {
- if (i == 0)
- first = elem;
- if (elem.id == myid) {
- // skip
- }
- else if (elem.id != myid && sortname >= $(elem).children().first().text().toUpperCase()) {
- sibling = elem;
- }
- else {
- return false;
- }
- });
-
- if (sibling) {
- li.insertAfter(sibling);
- }
- else if (first.id != myid) {
- li.insertBefore(first);
- }
-
- // reload data from dom
- update_data();
- }
-
- /**
- * Remove the item with the given ID
- */
- function remove(id)
- {
- var node, li;
- if (node = indexbyid[id]) {
- li = id2dom(id);
- li.remove();
-
- node.deleted = true;
- delete indexbyid[id];
-
- return true;
- }
-
- return false;
- }
-
- /**
- * (Re-)read tree data from DOM
- */
- function update_data()
- {
- data = walk_list(container);
- }
-
- /**
- * Apply the 'collapsed' status of the data node to the corresponding DOM element(s)
- */
- function update_dom(node)
- {
- var li = id2dom(node.id);
- li.children('ul').first()[(node.collapsed ? 'hide' : 'show')]();
- li.children('div.treetoggle').removeClass('collapsed expanded').addClass(node.collapsed ? 'collapsed' : 'expanded');
- me.triggerEvent('toggle', node);
- }
-
- /**
- * Render the tree list from the internal data structure
- */
- function render()
- {
- if (me.triggerEvent('renderBefore', data) === false)
- return;
-
- // remove all child nodes
- container.html('');
-
- // render child nodes
- for (var i=0; i < data.length; i++) {
- render_node(data[i], container);
- }
-
- me.triggerEvent('renderAfter', container);
- }
-
- /**
- * Render a specific node into the DOM list
- */
- function render_node(node, parent, replace)
- {
- if (node.deleted)
- return;
-
- var li = $('<li>')
- .attr('id', p.id_prefix + (p.id_encode ? p.id_encode(node.id) : node.id))
- .addClass((node.classes || []).join(' '));
-
- if (replace)
- replace.replaceWith(li);
- else
- li.appendTo(parent);
-
- if (typeof node.html == 'string') {
- li.html(node.html);
- }
- else if (typeof node.html == 'object') {
- li.append(node.html);
- }
-
- if (node.virtual)
- li.addClass('virtual');
- if (node.id == selection)
- li.addClass('selected');
-
- // add child list and toggle icon
- if (node.children && node.children.length) {
- $('<div class="treetoggle '+(node.collapsed ? 'collapsed' : 'expanded') + '"> </div>').appendTo(li);
- var ul = $('<ul>').appendTo(li).attr('class', node.childlistclass);
- if (node.collapsed)
- ul.hide();
-
- for (var i=0; i < node.children.length; i++) {
- render_node(node.children[i], ul);
- }
- }
-
- return li;
- }
-
- /**
- * Recursively walk the DOM tree and build an internal data structure
- * representing the skeleton of this tree list.
- */
- function walk_list(ul)
- {
- var result = [];
- ul.children('li').each(function(i,e){
- var li = $(e), sublist = li.children('ul');
- var node = {
- id: dom2id(li),
- classes: li.attr('class').split(' '),
- virtual: li.hasClass('virtual'),
- html: li.children().first().get(0).outerHTML,
- children: walk_list(sublist)
- }
-
- if (sublist.length) {
- node.childlistclass = sublist.attr('class');
- }
- if (node.children.length) {
- node.collapsed = sublist.css('display') == 'none';
- }
- if (li.hasClass('selected')) {
- selection = node.id;
- }
-
- result.push(node);
- indexbyid[node.id] = node;
- })
-
- return result;
- }
-
- /**
- * Recursively walk the data tree and index nodes by their ID
- */
- function index_data(node)
- {
- if (node.id) {
- indexbyid[node.id] = node;
- }
- for (var c=0; node.children && c < node.children.length; c++) {
- index_data(node.children[c]);
- }
- }
-
- /**
- * Get the (stripped) node ID from the given DOM element
- */
- function dom2id(li)
- {
- var domid = li.attr('id').replace(new RegExp('^' + (p.id_prefix) || '%'), '');
- return p.id_decode ? p.id_decode(domid) : domid;
- }
-
- /**
- * Get the <li> element for the given node ID
- */
- function id2dom(id)
- {
- var domid = p.id_encode ? p.id_encode(id) : id;
- return $('#' + p.id_prefix + domid);
- }
-
- /**
- * Scroll the parent container to make the given list item visible
- */
- function scroll_to_node(li)
- {
- var scroller = container.parent(),
- current_offset = scroller.scrollTop(),
- rel_offset = li.offset().top - scroller.offset().top;
-
- if (rel_offset < 0 || rel_offset + li.height() > scroller.height())
- scroller.scrollTop(rel_offset + current_offset);
- }
-
- ///// drag & drop support
-
- /**
- * When dragging starts, compute absolute bounding boxes of the list and it's items
- * for faster comparisons while mouse is moving
- */
- function drag_start()
- {
- var li, item, height,
- pos = container.offset();
-
- body_scroll_top = bw.ie ? 0 : window.pageYOffset;
- list_scroll_top = container.parent().scrollTop();
-
- drag_active = true;
- box_coords = {
- x1: pos.left,
- y1: pos.top,
- x2: pos.left + container.width(),
- y2: pos.top + container.height()
- };
-
- item_coords = [];
- for (var id in indexbyid) {
- li = id2dom(id);
- item = li.children().first().get(0);
- if (height = item.offsetHeight) {
- pos = $(item).offset();
- item_coords[id] = {
- x1: pos.left,
- y1: pos.top,
- x2: pos.left + item.offsetWidth,
- y2: pos.top + height,
- on: id == autoexpand_item
- };
- }
- }
- }
-
- /**
- * Signal that dragging has stopped
- */
- function drag_end()
- {
- drag_active = false;
-
- if (autoexpand_timer) {
- clearTimeout(autoexpand_timer);
- autoexpand_timer = null;
- autoexpand_item = null;
- }
-
- $('li.droptarget', container).removeClass('droptarget');
- }
-
- /**
- * Determine if the given mouse coords intersect the list and one if its items
- */
- function intersects(mouse, highlight)
- {
- // offsets to compensate for scrolling while dragging a message
- var boffset = bw.ie ? -document.documentElement.scrollTop : body_scroll_top,
- moffset = list_scroll_top - container.parent().scrollTop(),
- result = null;
-
- mouse.top = mouse.y + -moffset - boffset;
-
- // no intersection with list bounding box
- if (mouse.x < box_coords.x1 || mouse.x >= box_coords.x2 || mouse.top < box_coords.y1 || mouse.top >= box_coords.y2) {
- // TODO: optimize performance for this operation
- $('li.droptarget', container).removeClass('droptarget');
- return result;
- }
-
- // check intersection with visible list items
- var pos, node;
- for (var id in item_coords) {
- pos = item_coords[id];
- if (mouse.x >= pos.x1 && mouse.x < pos.x2 && mouse.top >= pos.y1 && mouse.top < pos.y2) {
- node = indexbyid[id];
-
- // if the folder is collapsed, expand it after the configured time
- if (node.children && node.children.length && node.collapsed && p.autoexpand && autoexpand_item != id) {
- if (autoexpand_timer)
- clearTimeout(autoexpand_timer);
-
- autoexpand_item = id;
- autoexpand_timer = setTimeout(function() {
- expand(autoexpand_item);
- drag_start(); // re-calculate item coords
- autoexpand_item = null;
- }, p.autoexpand);
- }
- else if (autoexpand_timer && autoexpand_item != id) {
- clearTimeout(autoexpand_timer);
- autoexpand_item = null;
- autoexpand_timer = null;
- }
-
- // check if this item is accepted as drop target
- if (p.check_droptarget(node)) {
- if (highlight) {
- id2dom(id).addClass('droptarget');
- pos.on = true;
- }
- result = id;
- }
- else {
- result = null;
- }
- }
- else if (pos.on) {
- id2dom(id).removeClass('droptarget');
- pos.on = false;
- }
- }
-
- return result;
- }
+ // apply some defaults to p
+ p = $.extend({
+ id_prefix: '',
+ autoexpand: 1000,
+ selectable: false,
+ check_droptarget: function(node){ return !node.virtual }
+ }, p || {});
+
+ var container = $(node),
+ data = p.data || [],
+ indexbyid = {},
+ selection = null,
+ drag_active = false,
+ box_coords = {},
+ item_coords = [],
+ autoexpand_timer,
+ autoexpand_item,
+ body_scroll_top = 0,
+ list_scroll_top = 0,
+ me = this;
+
+
+ /////// export public members and methods
+
+ this.container = container;
+ this.expand = expand;
+ this.collapse = collapse;
+ this.select = select;
+ this.render = render;
+ this.drag_start = drag_start;
+ this.drag_end = drag_end;
+ this.intersects = intersects;
+ this.update = update_node;
+ this.insert = insert;
+ this.remove = remove;
+ this.get_item = get_item;
+ this.get_selection = get_selection;
+
+ /////// startup code (constructor)
+
+ // abort if node not found
+ if (!container.length)
+ return;
+
+ if (p.data)
+ index_data({ children:data });
+ // load data from DOM
+ else
+ update_data();
+
+ // register click handlers on list
+ container.on('click', 'div.treetoggle', function(e){
+ toggle(dom2id($(this).parent()));
+ });
+
+ container.on('click', 'li', function(e){
+ var node = p.selectable ? indexbyid[dom2id($(this))] : null;
+ if (node && !node.virtual) {
+ select(node.id);
+ e.stopPropagation();
+ }
+ });
+
+
+ /////// private methods
+
+ /**
+ * Collaps a the node with the given ID
+ */
+ function collapse(id, recursive, set)
+ {
+ var node;
+
+ if (node = indexbyid[id]) {
+ node.collapsed = typeof set == 'undefined' || set;
+ update_dom(node);
+
+ // Work around a bug in IE6 and IE7, see #1485309
+ if (window.bw && (bw.ie6 || bw.ie7) && node.collapsed) {
+ id2dom(node.id).next().children('ul:visible').hide().show();
+ }
+
+ if (recursive && node.children) {
+ for (var i=0; i < node.children.length; i++) {
+ collapse(node.children[i].id, recursive, set);
+ }
+ }
+
+ me.triggerEvent(node.collapsed ? 'collapse' : 'expand', node);
+ }
+ }
+
+ /**
+ * Expand a the node with the given ID
+ */
+ function expand(id, recursive)
+ {
+ collapse(id, recursive, false);
+ }
+
+ /**
+ * Toggle collapsed state of a list node
+ */
+ function toggle(id, recursive)
+ {
+ var node;
+ if (node = indexbyid[id]) {
+ collapse(id, recursive, !node.collapsed);
+ }
+ }
+
+ /**
+ * Select a tree node by it's ID
+ */
+ function select(id)
+ {
+ if (selection) {
+ id2dom(selection).removeClass('selected');
+ selection = null;
+ }
+
+ var li = id2dom(id);
+ if (li.length) {
+ li.addClass('selected');
+ selection = id;
+ // TODO: expand all parent nodes if collapsed
+ scroll_to_node(li);
+ }
+
+ me.triggerEvent('select', indexbyid[id]);
+ }
+
+ /**
+ * Getter for the currently selected node ID
+ */
+ function get_selection()
+ {
+ return selection;
+ }
+
+ /**
+ * Return the DOM element of the list item with the given ID
+ */
+ function get_item(id)
+ {
+ return id2dom(id).get(0);
+ }
+
+ /**
+ * Insert the given node
+ */
+ function insert(node, parent_id, sort)
+ {
+ var li, parent_li,
+ parent_node = parent_id ? indexbyid[parent_id] : null;
+
+ // insert as child of an existing node
+ if (parent_node) {
+ if (!parent_node.children)
+ parent_node.children = [];
+
+ parent_node.children.push(node);
+ parent_li = id2dom(parent_id);
+
+ // re-render the entire subtree
+ if (parent_node.children.length == 1) {
+ render_node(parent_node, parent_li.parent(), parent_li);
+ li = id2dom(node.id);
+ }
+ else {
+ // append new node to parent's child list
+ li = render_node(node, parent_li.children('ul').first());
+ }
+ }
+ // insert at top level
+ else {
+ data.push(node);
+ li = render_node(node, container);
+ }
+
+ indexbyid[node.id] = node;
+
+ if (sort) {
+ resort_node(li, typeof sort == 'string' ? '[class~="' + sort + '"]' : '');
+ }
+ }
+
+ /**
+ * Update properties of an existing node
+ */
+ function update_node(id, updates, sort)
+ {
+ var li, node = indexbyid[id];
+
+ if (node) {
+ li = id2dom(id);
+
+ if (updates.id || updates.html || updates.children || updates.classes) {
+ $.extend(node, updates);
+ render_node(node, li.parent(), li);
+ }
+
+ if (node.id != id) {
+ delete indexbyid[id];
+ indexbyid[node.id] = node;
+ }
+
+ if (sort) {
+ resort_node(li, typeof sort == 'string' ? '[class~="' + sort + '"]' : '');
+ }
+ }
+ }
+
+ /**
+ * Helper method to sort the list of the given item
+ */
+ function resort_node(li, filter)
+ {
+ var first, sibling,
+ myid = li.get(0).id,
+ sortname = li.children().first().text().toUpperCase();
+
+ li.parent().children('li' + filter).each(function(i, elem) {
+ if (i == 0)
+ first = elem;
+ if (elem.id == myid) {
+ // skip
+ }
+ else if (elem.id != myid && sortname >= $(elem).children().first().text().toUpperCase()) {
+ sibling = elem;
+ }
+ else {
+ return false;
+ }
+ });
+
+ if (sibling) {
+ li.insertAfter(sibling);
+ }
+ else if (first.id != myid) {
+ li.insertBefore(first);
+ }
+
+ // reload data from dom
+ update_data();
+ }
+
+ /**
+ * Remove the item with the given ID
+ */
+ function remove(id)
+ {
+ var node, li;
+
+ if (node = indexbyid[id]) {
+ li = id2dom(id);
+ li.remove();
+
+ node.deleted = true;
+ delete indexbyid[id];
+
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * (Re-)read tree data from DOM
+ */
+ function update_data()
+ {
+ data = walk_list(container);
+ }
+
+ /**
+ * Apply the 'collapsed' status of the data node to the corresponding DOM element(s)
+ */
+ function update_dom(node)
+ {
+ var li = id2dom(node.id);
+ li.children('ul').first()[(node.collapsed ? 'hide' : 'show')]();
+ li.children('div.treetoggle').removeClass('collapsed expanded').addClass(node.collapsed ? 'collapsed' : 'expanded');
+ me.triggerEvent('toggle', node);
+ }
+
+ /**
+ * Render the tree list from the internal data structure
+ */
+ function render()
+ {
+ if (me.triggerEvent('renderBefore', data) === false)
+ return;
+
+ // remove all child nodes
+ container.html('');
+
+ // render child nodes
+ for (var i=0; i < data.length; i++) {
+ render_node(data[i], container);
+ }
+
+ me.triggerEvent('renderAfter', container);
+ }
+
+ /**
+ * Render a specific node into the DOM list
+ */
+ function render_node(node, parent, replace)
+ {
+ if (node.deleted)
+ return;
+
+ var li = $('<li>')
+ .attr('id', p.id_prefix + (p.id_encode ? p.id_encode(node.id) : node.id))
+ .addClass((node.classes || []).join(' '));
+
+ if (replace)
+ replace.replaceWith(li);
+ else
+ li.appendTo(parent);
+
+ if (typeof node.html == 'string')
+ li.html(node.html);
+ else if (typeof node.html == 'object')
+ li.append(node.html);
+
+ if (node.virtual)
+ li.addClass('virtual');
+ if (node.id == selection)
+ li.addClass('selected');
+
+ // add child list and toggle icon
+ if (node.children && node.children.length) {
+ $('<div class="treetoggle '+(node.collapsed ? 'collapsed' : 'expanded') + '"> </div>').appendTo(li);
+ var ul = $('<ul>').appendTo(li).attr('class', node.childlistclass);
+ if (node.collapsed)
+ ul.hide();
+
+ for (var i=0; i < node.children.length; i++) {
+ render_node(node.children[i], ul);
+ }
+ }
+
+ return li;
+ }
+
+ /**
+ * Recursively walk the DOM tree and build an internal data structure
+ * representing the skeleton of this tree list.
+ */
+ function walk_list(ul)
+ {
+ var result = [];
+ ul.children('li').each(function(i,e){
+ var li = $(e), sublist = li.children('ul');
+ var node = {
+ id: dom2id(li),
+ classes: li.attr('class').split(' '),
+ virtual: li.hasClass('virtual'),
+ html: li.children().first().get(0).outerHTML,
+ children: walk_list(sublist)
+ }
+
+ if (sublist.length) {
+ node.childlistclass = sublist.attr('class');
+ }
+ if (node.children.length) {
+ node.collapsed = sublist.css('display') == 'none';
+ }
+ if (li.hasClass('selected')) {
+ selection = node.id;
+ }
+
+ result.push(node);
+ indexbyid[node.id] = node;
+ })
+
+ return result;
+ }
+
+ /**
+ * Recursively walk the data tree and index nodes by their ID
+ */
+ function index_data(node)
+ {
+ if (node.id) {
+ indexbyid[node.id] = node;
+ }
+ for (var c=0; node.children && c < node.children.length; c++) {
+ index_data(node.children[c]);
+ }
+ }
+
+ /**
+ * Get the (stripped) node ID from the given DOM element
+ */
+ function dom2id(li)
+ {
+ var domid = li.attr('id').replace(new RegExp('^' + (p.id_prefix) || '%'), '');
+ return p.id_decode ? p.id_decode(domid) : domid;
+ }
+
+ /**
+ * Get the <li> element for the given node ID
+ */
+ function id2dom(id)
+ {
+ var domid = p.id_encode ? p.id_encode(id) : id;
+ return $('#' + p.id_prefix + domid);
+ }
+
+ /**
+ * Scroll the parent container to make the given list item visible
+ */
+ function scroll_to_node(li)
+ {
+ var scroller = container.parent(),
+ current_offset = scroller.scrollTop(),
+ rel_offset = li.offset().top - scroller.offset().top;
+
+ if (rel_offset < 0 || rel_offset + li.height() > scroller.height())
+ scroller.scrollTop(rel_offset + current_offset);
+ }
+
+ ///// drag & drop support
+
+ /**
+ * When dragging starts, compute absolute bounding boxes of the list and it's items
+ * for faster comparisons while mouse is moving
+ */
+ function drag_start()
+ {
+ var li, item, height,
+ pos = container.offset();
+
+ body_scroll_top = bw.ie ? 0 : window.pageYOffset;
+ list_scroll_top = container.parent().scrollTop();
+
+ drag_active = true;
+ box_coords = {
+ x1: pos.left,
+ y1: pos.top,
+ x2: pos.left + container.width(),
+ y2: pos.top + container.height()
+ };
+
+ item_coords = [];
+ for (var id in indexbyid) {
+ li = id2dom(id);
+ item = li.children().first().get(0);
+ if (height = item.offsetHeight) {
+ pos = $(item).offset();
+ item_coords[id] = {
+ x1: pos.left,
+ y1: pos.top,
+ x2: pos.left + item.offsetWidth,
+ y2: pos.top + height,
+ on: id == autoexpand_item
+ };
+ }
+ }
+ }
+
+ /**
+ * Signal that dragging has stopped
+ */
+ function drag_end()
+ {
+ drag_active = false;
+
+ if (autoexpand_timer) {
+ clearTimeout(autoexpand_timer);
+ autoexpand_timer = null;
+ autoexpand_item = null;
+ }
+
+ $('li.droptarget', container).removeClass('droptarget');
+ }
+
+ /**
+ * Determine if the given mouse coords intersect the list and one if its items
+ */
+ function intersects(mouse, highlight)
+ {
+ // offsets to compensate for scrolling while dragging a message
+ var boffset = bw.ie ? -document.documentElement.scrollTop : body_scroll_top,
+ moffset = list_scroll_top - container.parent().scrollTop(),
+ result = null;
+
+ mouse.top = mouse.y + -moffset - boffset;
+
+ // no intersection with list bounding box
+ if (mouse.x < box_coords.x1 || mouse.x >= box_coords.x2 || mouse.top < box_coords.y1 || mouse.top >= box_coords.y2) {
+ // TODO: optimize performance for this operation
+ $('li.droptarget', container).removeClass('droptarget');
+ return result;
+ }
+
+ // check intersection with visible list items
+ var pos, node;
+ for (var id in item_coords) {
+ pos = item_coords[id];
+ if (mouse.x >= pos.x1 && mouse.x < pos.x2 && mouse.top >= pos.y1 && mouse.top < pos.y2) {
+ node = indexbyid[id];
+
+ // if the folder is collapsed, expand it after the configured time
+ if (node.children && node.children.length && node.collapsed && p.autoexpand && autoexpand_item != id) {
+ if (autoexpand_timer)
+ clearTimeout(autoexpand_timer);
+
+ autoexpand_item = id;
+ autoexpand_timer = setTimeout(function() {
+ expand(autoexpand_item);
+ drag_start(); // re-calculate item coords
+ autoexpand_item = null;
+ }, p.autoexpand);
+ }
+ else if (autoexpand_timer && autoexpand_item != id) {
+ clearTimeout(autoexpand_timer);
+ autoexpand_item = null;
+ autoexpand_timer = null;
+ }
+
+ // check if this item is accepted as drop target
+ if (p.check_droptarget(node)) {
+ if (highlight) {
+ id2dom(id).addClass('droptarget');
+ pos.on = true;
+ }
+ result = id;
+ }
+ else {
+ result = null;
+ }
+ }
+ else if (pos.on) {
+ id2dom(id).removeClass('droptarget');
+ pos.on = false;
+ }
+ }
+
+ return result;
+ }
}
// use event processing functions from Roundcube's rcube_event_engine
rcube_treelist_widget.prototype.addEventListener = rcube_event_engine.prototype.addEventListener;
rcube_treelist_widget.prototype.removeEventListener = rcube_event_engine.prototype.removeEventListener;
rcube_treelist_widget.prototype.triggerEvent = rcube_event_engine.prototype.triggerEvent;
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sat, Mar 1, 9:19 AM (1 d, 16 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
166515
Default Alt Text
(27 KB)
Attached To
Mode
R3 roundcubemail
Attached
Detach File
Event Timeline
Log In to Comment