diff --git a/index.html b/index.html
index 99049f6e4..2e3d3847e 100644
--- a/index.html
+++ b/index.html
@@ -2358,7 +2358,8 @@
-
+
+
diff --git a/index.js b/index.js
index 4c4173dcf..4108254d0 100644
--- a/index.js
+++ b/index.js
@@ -992,6 +992,10 @@ myTreeInit();
myTreeInit();
});
+ $('#btnTreeClearSelected').click(function () {
+ log('Items/folders cleared: ', $('#myTree1').tree('deselectAll') );
+ });
+
$('#btnTreeDiscloseVisible').click(function () {
$('#myTree1').tree('discloseVisible');
});
diff --git a/js/tree.js b/js/tree.js
index 99ccbeb95..d73a7368a 100644
--- a/js/tree.js
+++ b/js/tree.js
@@ -60,6 +60,16 @@
Tree.prototype = {
constructor: Tree,
+ deselectAll: function deselectAll(nodes) {
+ // clear all child tree nodes and style as deselected
+ nodes = nodes || this.$element;
+ var $selectedElements = $(nodes).find('.tree-selected');
+ $selectedElements.each(function (index, element) {
+ styleNodeDeselected( $(element), $(element).find( '.glyphicon' ) );
+ });
+ return $selectedElements;
+ },
+
destroy: function destroy() {
// any external bindings [none]
// empty elements to return to original markup
@@ -159,59 +169,43 @@
});
},
- selectItem: function selectItem(el) {
- if (!this.options.itemSelect) return;
- var $el = $(el);
- var selData = $el.data();
- var $all = this.$element.find('.tree-selected');
- var data = [];
- var $icon = $el.find('.icon-item');
-
- if (this.options.multiSelect) {
- $.each($all, function (index, value) {
- var $val = $(value);
- if ($val[0] !== $el[0]) {
- data.push($(value).data());
- }
- });
- } else if ($all[0] !== $el[0]) {
- $all.removeClass('tree-selected')
- .find('.glyphicon').removeClass('glyphicon-ok').addClass('fueluxicon-bullet');
- data.push(selData);
- }
-
- var eventType = 'selected';
- if ($el.hasClass('tree-selected')) {
- eventType = 'deselected';
- $el.removeClass('tree-selected');
- if ($icon.hasClass('glyphicon-ok') || $icon.hasClass('fueluxicon-bullet')) {
- $icon.removeClass('glyphicon-ok').addClass('fueluxicon-bullet');
- }
-
- } else {
- $el.addClass ('tree-selected');
- // add tree dot back in
- if ($icon.hasClass('glyphicon-ok') || $icon.hasClass('fueluxicon-bullet')) {
- $icon.removeClass('fueluxicon-bullet').addClass('glyphicon-ok');
- }
+ selectTreeNode: function selectItem(clickedElement, nodeType) {
+ var clicked = {}; // object for clicked element
+ clicked.$element = $(clickedElement);
- if (this.options.multiSelect) {
- data.push(selData);
- }
+ var selected = {}; // object for selected elements
+ selected.$elements = this.$element.find('.tree-selected');
+ selected.dataForEvent = [];
+ // determine clicked element and it's icon
+ if (nodeType === 'folder') {
+ // make the clicked.$element the container branch
+ clicked.$element = clicked.$element.closest('.tree-branch');
+ clicked.$icon = clicked.$element.find('.icon-folder');
+ }
+ else {
+ clicked.$icon = clicked.$element.find('.icon-item');
}
+ clicked.elementData = clicked.$element.data();
- this.$element.trigger(eventType + '.fu.tree', {
- target: selData,
- selected: data
+ // the below functions pass objects by copy/reference and use modified object in this function
+ if ( this.options.multiSelect ) {
+ multiSelectSyncNodes(this, clicked, selected);
+ }
+ else {
+ singleSelectSyncNodes(this, clicked, selected);
+ }
+
+ // all done with the DOM, now fire events
+ this.$element.trigger(selected.eventType + '.fu.tree', {
+ target: clicked.elementData,
+ selected: selected.dataForEvent
});
- // Return new list of selected items, the item
- // clicked, and the type of event:
- $el.trigger('updated.fu.tree', {
- selected: data,
- item: $el,
- eventType: eventType
+ clicked.$element.trigger('updated.fu.tree', {
+ selected: selected.dataForEvent,
+ item: clicked.$element,
+ eventType: selected.eventType
});
},
@@ -270,52 +264,16 @@
}
},
- selectFolder: function selectFolder(clickedElement) {
- if (!this.options.folderSelect) return;
- var $clickedElement = $(clickedElement);
- var $clickedBranch = $clickedElement.closest('.tree-branch');
- var $selectedBranch = this.$element.find('.tree-branch.tree-selected');
- var clickedData = $clickedBranch.data();
- var selectedData = [];
- var eventType = 'selected';
-
- // select clicked item
- if ($clickedBranch.hasClass('tree-selected')) {
- eventType = 'deselected';
- $clickedBranch.removeClass('tree-selected');
- } else {
- $clickedBranch.addClass('tree-selected');
+ selectFolder: function selectFolder(el) {
+ if (this.options.folderSelect) {
+ this.selectTreeNode(el, 'folder');
}
+ },
- if (this.options.multiSelect) {
- // get currently selected
- $selectedBranch = this.$element.find('.tree-branch.tree-selected');
-
- $.each($selectedBranch, function (index, value) {
- var $value = $(value);
- if ($value[0] !== $clickedElement[0]) {
- selectedData.push($(value).data());
- }
- });
-
- } else if ($selectedBranch[0] !== $clickedElement[0]) {
- $selectedBranch.removeClass('tree-selected');
-
- selectedData.push(clickedData);
+ selectItem: function selectItem(el) {
+ if (this.options.itemSelect) {
+ this.selectTreeNode(el, 'item');
}
-
- this.$element.trigger(eventType + '.fu.tree', {
- target: clickedData,
- selected: selectedData
- });
-
- // Return new list of selected items, the item
- // clicked, and the type of event:
- $clickedElement.trigger('updated.fu.tree', {
- selected: selectedData,
- item: $clickedElement,
- eventType: eventType
- });
},
selectedItems: function selectedItems() {
@@ -465,11 +423,71 @@
}
};
+
+ // ALIASES
+
//alias for collapse for consistency. "Collapse" is an ambiguous term (collapse what? All? One specific branch?)
Tree.prototype.closeAll = Tree.prototype.collapse;
//alias for backwards compatibility because there's no reason not to.
Tree.prototype.openFolder = Tree.prototype.discloseFolder;
+
+ // PRIVATE FUNCTIONS
+
+ function styleNodeSelected ($element, $icon) {
+ $element.addClass('tree-selected');
+ if ( $element.data('type') === 'item' && $icon.hasClass('fueluxicon-bullet') ) {
+ $icon.removeClass('fueluxicon-bullet').addClass('glyphicon-ok'); // make checkmark
+ }
+ }
+
+ function styleNodeDeselected ($element, $icon) {
+ $element.removeClass('tree-selected');
+ if ( $element.data('type') === 'item' && $icon.hasClass('glyphicon-ok') ) {
+ $icon.removeClass('glyphicon-ok').addClass('fueluxicon-bullet'); // make bullet
+ }
+ }
+
+ function multiSelectSyncNodes (self, clicked, selected) {
+ // search for currently selected and add to selected data list if needed
+ $.each(selected.$elements, function (index, element) {
+ var $element = $(element);
+ if ($element[0] !== clicked.$element[0]) {
+ selected.dataForEvent.push( $($element).data() );
+ }
+ });
+
+ if (clicked.$element.hasClass('tree-selected')) {
+ styleNodeDeselected (clicked.$element, clicked.$icon);
+ // set event data
+ selected.eventType = 'deselected';
+ }
+ else {
+ styleNodeSelected(clicked.$element, clicked.$icon);
+ // set event data
+ selected.eventType = 'selected';
+ selected.dataForEvent.push(clicked.elementData);
+ }
+ }
+
+ function singleSelectSyncNodes(self, clicked, selected) {
+ // element is not currently selected
+ if (selected.$elements[0] !== clicked.$element[0]) {
+ var clearedElements = self.deselectAll(self.$element);
+ styleNodeSelected(clicked.$element, clicked.$icon);
+ // set event data
+ selected.eventType = 'selected';
+ selected.dataForEvent = [clicked.elementData];
+ }
+ else {
+ styleNodeDeselected(clicked.$element, clicked.$icon);
+ // set event data
+ selected.eventType = 'deselected';
+ selected.dataForEvent = [];
+ }
+ }
+
+
// TREE PLUGIN DEFINITION
$.fn.tree = function tree(option) {
diff --git a/test/markup/tree-markup.html b/test/markup/tree-markup.html
index d8330cb3c..cdfaaaf60 100644
--- a/test/markup/tree-markup.html
+++ b/test/markup/tree-markup.html
@@ -66,6 +66,13 @@
Loading...
+
+
+
+
diff --git a/test/tree-test.js b/test/tree-test.js
index bd4d1740e..e51e8c619 100644
--- a/test/tree-test.js
+++ b/test/tree-test.js
@@ -182,12 +182,13 @@ define(function (require) {
$selNode = $tree.find('.tree-branch:eq(1)');
$tree.tree('discloseFolder', $selNode.find('.tree-branch-header'));
- equal($selNode.find('.tree-branch-children > li').length, 4, 'Folder has been populated with sub-folders');
+ equal($selNode.find('.tree-branch-children > li').length, 8, 'Folder has been populated with sub-folders and items');
});
test("Single item/folder selection works as designed", function () {
var $tree = $(html).find('#MyTree');
+ // multiSelect: false is the default
$tree.tree({
dataSource: this.dataSource
});
@@ -204,10 +205,24 @@ define(function (require) {
folderSelect: true
});
- $tree.tree('selectItem', $tree.find('.tree-branch-name:eq(1)'));
- equal($tree.tree('selectedItems').length, 1, 'Return single selected value');
- $tree.tree('selectItem', $tree.find('.tree-branch-name:eq(2)'));
- equal($tree.tree('selectedItems').length, 1, 'Return new single selected value');
+ $tree.tree('selectItem', $tree.find('.tree-item:eq(1)'));
+ equal($tree.tree('selectedItems').length, 1, 'Return single selected item (none previously selected, 1st programatic selection)');
+
+ $tree.tree('selectFolder', $tree.find('.tree-branch-name:eq(1)'));
+ equal($tree.tree('selectedItems').length, 1, 'Return single selected folder (item previously selected, 2nd programatic selection)');
+
+ $tree.tree('selectItem', $tree.find('.tree-item:eq(2)'));
+ equal($tree.tree('selectedItems').length, 1, 'Return single selected item (folder previously selected, 3rd programatic selection)');
+
+ $tree.find('.tree-item:eq(1)').click();
+ equal($tree.tree('selectedItems').length, 1, 'Return single selected item (item previously selected, 1st click selection)');
+
+ $tree.find('.tree-branch-name:eq(1)').click();
+ equal($tree.tree('selectedItems').length, 1, 'Return single selected folder (item previously selected, 2nd click selection)');
+
+ $tree.find('.tree-item:eq(2)').click();
+ equal($tree.tree('selectedItems').length, 1, 'Return single selected item (folder previously selected, 3rd click selection)');
+
});
test("Multiple item/folder selection works as designed", function () {