(function($) {
/**
* This adds a Chosen style selector for the tree select widget.
*
* This widget requires chosen.css.
*/
$.fn.chosentree = function(params) {
// Setup the default parameters.
params = $.extend({
inputId: 'chosentree-select', /** The input element ID and NAME. */
label: '', /** The label to add to the input. */
description: '', /** The description for the input. */
input_placeholder: 'Select Item', /** The input placeholder text. */
input_type: 'text', /** Define the input type. */
autosearch: false, /** If we would like to autosearch. */
search_text: 'Search', /** The search button text. */
no_results_text: 'No results found', /** Shown when no results. */
min_height: 100, /** The miniumum height. */
more_text: '+%num% more', /** The text to show in the more. */
loaded: null, /** Called when all items are loaded. */
collapsed: true, /** If the tree should be collapsed. */
showtree: false /** To show the tree. */
}, params);
// Iterate through each instance.
return $(this).each(function() {
// Keep track of the treeselect.
var selector = null;
var choices = null;
var search = null;
var input = null;
var search_btn = null;
var label = null;
var description = null;
var treeselect = null;
var treewrapper = null;
var selectedTimer = 0;
var root = null;
// Show or hide the tree.
var showTree = function(show, tween) {
tween = tween || 'fast';
if (show && (!root || root.has_children)) {
treewrapper.addClass('treevisible').show('fast');
}
else {
treewrapper.removeClass('treevisible').hide('fast');
}
};
// Create the selector element.
selector = $(document.createElement('div'));
selector.addClass('chzntree-container');
if (params.input_type == 'search') {
selector.addClass('chzntree-container-single');
search = $(document.createElement('div'));
search.addClass('chzntree-search');
}
else {
selector.addClass('chzntree-container-multi');
choices = $(document.createElement('ul'));
choices.addClass('chzntree-choices chosentree-choices');
search = $(document.createElement('li'));
search.addClass('search-field');
}
// If they wish to have a label.
label = $(document.createElement('label'));
label.attr({
'for': params.inputId
});
label.text(params.label);
// If they wish to have a description.
description = $(document.createElement('div'));
description.attr({
'class': 'description'
});
description.text(params.description);
// Create the input element.
if (params.input_placeholder) {
input = $(document.createElement('input'));
input.attr({
'type': 'text',
'placeholder': params.input_placeholder,
'autocomplete': 'off'
});
if (!params.showtree && params.collapsed) {
input.focus(function(event) {
showTree(true);
});
}
// Add a results item to the input.
if (params.input_type == 'search') {
// Need to make room for the search symbol.
input.addClass('chosentree-search');
// Perform the search.
var doSearch = function(inputValue) {
// We want to make sure we don't try while it is searching...
// And also don't want to search if the input is one character...
if (!input.hasClass('searching') && (inputValue.length !== 1)) {
// Continue if we have a root node.
if (root) {
// Say that we are now searching...
input.addClass('searching');
// Search the tree node.
root.search(inputValue, function(nodes, searchResults) {
// Say we are no longer searching...
input.removeClass('searching');
// Iterate over the nodes and append them to the search.
var count = 0;
root.childlist.children().detach();
// Add a class to distinguish if this is search results.
if (searchResults) {
root.childlist.addClass('chzntree-search-results');
}
else {
root.childlist.removeClass('chzntree-search-results');
}
// Add class if input checkbox is enabled.
if (params.inputName !== '') {
root.childlist.addClass('input-enabled');
}
else {
root.childlist.removeClass('input-enabled');
}
// Iterate through our nodes.
for (var i in nodes) {
count++;
// Use either the search item or the display.
if (searchResults) {
root.childlist.append(nodes[i].searchItem);
}
else {
root.childlist.append(nodes[i].display);
}
}
if (!count) {
var txt = '
' + params.no_results_text + '';
root.childlist.append(txt);
}
});
// A search was performed.
return true;
}
}
// A search was not performed.
return false;
};
// If they wish to autosearch.
if (params.autosearch) {
// Keep track of a search timeout.
var searchTimeout = 0;
// Bind to the input when they type.
input.bind('input', function inputSearch() {
if (!doSearch(input.val())) {
clearTimeout(searchTimeout);
searchTimeout = setTimeout(inputSearch, 1000);
}
});
// Add the autosearch.
search.addClass('autosearch');
}
else {
search_btn = $(document.createElement('input'));
search_btn.attr({
'type': 'button',
'value': params.search_text
});
search_btn.addClass('button chosentree-search-btn');
search_btn.bind('click', function(event) {
event.preventDefault();
doSearch(input.val());
});
// Make sure to do a search.
jQuery(document).bind('keydown', function(event) {
if ((event.keyCode == 13) && input.is(':focus')) {
event.preventDefault();
doSearch(input.val());
}
});
// Add the autosearch.
search.addClass('manualsearch');
}
}
else {
// Add the results class.
input.addClass('chosentree-results');
}
search.append(input);
// Append the search button if it exists.
if (search_btn) {
search.append(search_btn);
}
}
// Creat the chosen selector.
if (choices) {
selector.append(label).append(choices.append(search));
}
else {
selector.append(label).append(search);
}
treewrapper = $(document.createElement('div'));
treewrapper.addClass('treewrapper');
treewrapper.hide();
// Get the tree select.
treeselect = $(document.createElement('div'));
treeselect.addClass('treeselect');
// Setup the keyevents.
$(this).keyup(function(event) {
if (event.which == 27) {
showTree(false);
}
});
// Add the treeselect widget.
treewrapper.append(treeselect);
$(this).append(selector.append(treewrapper));
// Add the description.
$(this).append(description);
// Now declare the treeselect.
var treeparams = params;
// Reset the selected callback.
treeparams.selected = (function(chosentree) {
// Keep track of the selected nodes.
var selectedNodes = {};
// The node callback.
return function(node, direct) {
// If this is a valid node.
if (node.id) {
// Get the existing choices.
var selected_choice = $('li#choice_' + node.id, choices);
// Add the choice if not already added.
if (node.checked) {
// If the choice is already selected, remove it.
if (selected_choice.length !== 0) {
selected_choice.remove();
}
// Add this to the selected nodes.
selectedNodes[node.id] = node;
}
else if (!node.checked) {
// If not selected, then remove the choice.
selected_choice.remove();
}
}
// If we are done selecting.
if (direct) {
// Set the chosentree value.
chosentree.value = {};
// Callback to close the chosen selector.
var closeChosen = function(node) {
return function(event) {
// Prevent the default.
event.preventDefault();
// Get the node data.
node = this.parentNode.nodeData;
// Remove the choice.
$('li#choice_' + node.id, choices).remove();
// Deselect this node.
node.selectChildren(false);
};
};
// Iterate through all the selected nodes.
for (var id in selectedNodes) {
// Set the node.
node = selectedNodes[id];
// Add to the chosen tree value.
chosentree.value[id] = node;
// Get and add a new choice.
var choice = $(document.createElement('li'));
choice.addClass('search-choice');
choice.attr('id', 'choice_' + node.id);
// Add the node data to this choice.
choice.eq(0)[0].nodeData = node;
var span = $(document.createElement('span'));
// If including children below, add text to the title to say so.
if (!params.deepLoad && node.include_children) {
span.text(node.title + ' (All below)');
}
else {
span.text(node.title);
}
// Don't allow them to remove the root element unless it is
// visible and has children.
var close = '';
if (!node.root || (node.showRoot && node.has_children)) {
close = $(document.createElement('a'));
close.addClass('search-choice-close');
close.attr('href', '#');
close.bind('click', closeChosen(node));
}
// Add this to the choices.
if (choices) {
choices.prepend(choice.append(span).append(close));
}
}
if (choices) {
// Only show the choices if they are not visible.
if (!choices.is(':visible')) {
// Show the choices.
choices.show();
}
// Reset the selected nodes.
selectedNodes = {};
// Don't show the default value if the root has not children.
if (input && node.children.length === 0) {
input.attr({'value': ''});
}
// Show more or less.
if (jQuery.fn.moreorless) {
// Get how many nodes there are.
var numNodes = $('li.search-choice', choices).length;
// Add this to the choices.
var more_text = params.more_text.replace('%num%', numNodes);
choices.moreorless(params.min_height, more_text);
if (!choices.div_expanded) {
showTree(true, null);
}
}
}
// If they wish to know when it is loaded.
if (treeparams.loaded) {
// Call our callback with the loaded node.
treeparams.loaded(node);
}
// Trigger an event.
$(chosentree).trigger('treeloaded');
}
};
})(this);
// Now declare our treeselect control.
treeselect.treeselect(treeparams);
root = treeselect.eq(0)[0].treenode;
// Show the tree by default.
if (treeparams.showtree || !treeparams.collapsed) {
showTree(true, null);
}
});
};
})(jQuery);