/* * Fuel UX Tree * https://github.com/ExactTarget/fuelux * * Copyright (c) 2014 ExactTarget * Licensed under the BSD New license. */ // -- BEGIN UMD WRAPPER PREFACE -- // For more information on UMD visit: // https://github.com/umdjs/umd/blob/master/jqueryPlugin.js (function (factory) { if (typeof define === 'function' && define.amd) { // if AMD loader is available, register as an anonymous module. define(['jquery'], factory); } else { // OR use browser globals if AMD is not present factory(jQuery); } }(function ($) { // -- END UMD WRAPPER PREFACE -- // -- BEGIN MODULE CODE HERE -- var old = $.fn.tree; // TREE CONSTRUCTOR AND PROTOTYPE var Tree = function (element, options) { this.$element = $(element); this.options = $.extend({}, $.fn.tree.defaults, options); this.$element.on('click.fu.tree', '.tree-item', $.proxy( function(ev) { this.selectItem(ev.currentTarget); } ,this)); //this.$element.on('click.fu.tree', '.tree-branch-name', $.proxy( function(ev) { this.openFolder(ev.currentTarget); }, this)); this.$element.on('click.fu.tree', '.tree-branch-header', $.proxy( function(ev) { this.openFolder(ev.currentTarget); }, this)); //ACE //ACE /** if( this.options.folderSelect ){ this.$element.off('click.fu.tree', '.tree-branch-header'); this.$element.on('click.fu.tree', '.icon-caret', $.proxy( function(ev) { this.openFolder($(ev.currentTarget).parent()); }, this)); this.$element.on('click.fu.tree', '.tree-branch-header', $.proxy( function(ev) { this.selectFolder($(ev.currentTarget)); }, this)); } */ this.render(); }; Tree.prototype = { constructor: Tree, destroy: function() { // any external bindings [none] // empty elements to return to original markup this.$element.find("li:not([data-template])").remove(); this.$element.remove(); // returns string of markup return this.$element[0].outerHTML; }, render: function () { this.populate(this.$element); }, populate: function ($el) { var self = this; var $parent = ($el.hasClass('tree')) ? $el : $el.parent(); var loader = $parent.find('.tree-loader:eq(0)'); var treeData = $parent.data(); loader.removeClass('hide'); this.options.dataSource( treeData ? treeData : {} , function (items) { loader.addClass('hide'); $.each( items.data, function(index, value) { var $entity; if(value.type === 'folder') { $entity = self.$element.find('[data-template=treebranch]:eq(0)').clone().removeClass('hide').removeAttr('data-template'); $entity.data(value); $entity.find('.tree-branch-name > .tree-label').html(value.text || value.name); //ACE var header = $entity.find('.tree-branch-header'); if('icon-class' in value) header.find('i').addClass(value['icon-class']); if('additionalParameters' in value && 'item-selected' in value.additionalParameters && value.additionalParameters['item-selected'] == true) { setTimeout(function(){header.trigger('click')}, 0); } } else if (value.type === 'item') { $entity = self.$element.find('[data-template=treeitem]:eq(0)').clone().removeClass('hide').removeAttr('data-template'); $entity.find('.tree-item-name > .tree-label').html(value.text || value.name); $entity.data(value); //ACE if('additionalParameters' in value && 'item-selected' in value.additionalParameters && value.additionalParameters['item-selected'] == true) { $entity.addClass ('tree-selected'); $entity.find('i').removeClass(self.options['unselected-icon']).addClass(self.options['selected-icon']); //$entity.closest('.tree-folder-content').show(); } } // Decorate $entity with data or other attributes making the // element easily accessable with libraries like jQuery. // // Values are contained within the object returned // for folders and items as attr: // // { // name: "An Item", // type: 'item', // attr = { // 'classes': 'required-item red-text', // 'data-parent': parentId, // 'guid': guid, // 'id': guid // } // }; // // the "name" attribute is also supported but is deprecated for "text" // add attributes to tree-branch or tree-item var attr = value['attr'] || value.dataAttributes || []; $.each(attr, function(key, value) { switch (key) { case 'cssClass': case 'class': case 'className': $entity.addClass(value); break; // allow custom icons case 'data-icon': $entity.find('.icon-item').removeClass().addClass('icon-item ' + value); $entity.attr(key, value); break; // ARIA support case 'id': $entity.attr(key, value); $entity.attr('aria-labelledby', value + '-label'); $entity.find('.tree-branch-name > .tree-label').attr('id', value + '-label'); break; // id, style, data-* default: $entity.attr(key, value); break; } }); // add child nodes if($el.hasClass('tree-branch-header')) { $parent.find('.tree-branch-children:eq(0)').append($entity); } else { $el.append($entity); } }); // return newly populated folder self.$element.trigger('loaded.fu.tree', $parent); }); }, selectItem: function (el) { if(this.options['selectable'] == false) return;//ACE 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('i').removeClass(this.options['selected-icon']).addClass(this.options['unselected-icon']);//ACE data.push(selData); } var eventType = 'selected'; if($el.hasClass('tree-selected')) { eventType = 'deselected'; $el.removeClass('tree-selected'); if($icon.hasClass(this.options['selected-icon']) || $icon.hasClass(this.options['unselected-icon']) ) { $icon.removeClass(this.options['selected-icon']).addClass(this.options['unselected-icon']);//ACE } } else { $el.addClass ('tree-selected'); // add tree dot back in if($icon.hasClass(this.options['selected-icon']) || $icon.hasClass(this.options['unselected-icon']) ) { $icon.removeClass(this.options['unselected-icon']).addClass(this.options['selected-icon']);//ACE } if (this.options.multiSelect) { data.push( selData ); } } this.$element.trigger(eventType + '.fu.tree', {target: selData, selected: data}); // 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 }); }, openFolder: function (el) { var $el = $(el); // tree-branch-name var $branch; var $treeFolderContent; var $treeFolderContentFirstChild; // if item select only //ACE /** if (!this.options.folderSelect) { $el = $(el).parent(); // tree-branch, if tree-branch-name clicked } */ $branch = $el.closest('.tree-branch'); // tree branch $treeFolderContent = $branch.find('.tree-branch-children'); $treeFolderContentFirstChild = $treeFolderContent.eq(0); // manipulate branch/folder var eventType, classToTarget, classToAdd; if ($el.find('.'+$.trim(this.options['close-icon'].replace(/(\s+)/g, '.'))).length) {//ACE eventType = 'opened'; classToTarget = this.options['close-icon'];//ACE classToAdd = this.options['open-icon'];//ACE $branch.addClass('tree-open'); $branch.attr('aria-expanded', 'true'); $treeFolderContentFirstChild.removeClass('hide'); if (!$treeFolderContent.children().length) { this.populate($treeFolderContent); } } else if($el.find('.'+$.trim(this.options['open-icon'].replace(/(\s+)/g, '.')))) { eventType = 'closed'; classToTarget = this.options['open-icon'];//ACE classToAdd = this.options['close-icon'];//ACE $branch.removeClass('tree-open'); $branch.attr('aria-expanded', 'false'); $treeFolderContentFirstChild.addClass('hide'); // remove if no cache if (!this.options.cacheItems) { $treeFolderContentFirstChild.empty(); } } $branch.find('> .tree-branch-header .icon-folder').eq(0) //.removeClass(this.options['close-icon'] + ' ' + this.options['open-icon']) .removeClass(classToTarget)//ACE .addClass(classToAdd); this.$element.trigger(eventType + '.fu.tree', $branch.data()); }, selectFolder: function (clickedElement) { 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'); } 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); } 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 () { var $sel = this.$element.find('.tree-selected'); var data = []; $.each($sel, function (index, value) { data.push($(value).data()); }); return data; }, // collapses open folders collapse: function () { var cacheItems = this.options.cacheItems; // find open folders this.$element.find('.'+$.trim(this.options['open-icon'].replace(/(\s+)/g, '.'))).each(function () {//ACE // update icon class var $this = $(this) .removeClass(this.options['open-icon'] + ' ' + this.options['close-icon']) .addClass(this.options['close-icon']); // "close" or empty folder contents var $parent = $this.parent().parent(); var $folder = $parent.children('.tree-branch-children'); $folder.addClass('hide'); if (!cacheItems) { $folder.empty(); } }); } }; // TREE PLUGIN DEFINITION $.fn.tree = function (option) { var args = Array.prototype.slice.call( arguments, 1 ); var methodReturn; var $set = this.each(function () { var $this = $( this ); var data = $this.data( 'fu.tree' ); var options = typeof option === 'object' && option; if( !data ) $this.data('fu.tree', (data = new Tree( this, options ) ) ); if( typeof option === 'string' ) methodReturn = data[ option ].apply( data, args ); }); return ( methodReturn === undefined ) ? $set : methodReturn; }; $.fn.tree.defaults = { dataSource: function(options, callback){}, multiSelect: false, cacheItems: true, folderSelect: false//ACE }; $.fn.tree.Constructor = Tree; $.fn.tree.noConflict = function () { $.fn.tree = old; return this; }; // NO DATA-API DUE TO NEED OF DATA-SOURCE // -- BEGIN UMD WRAPPER AFTERWORD -- })); // -- END UMD WRAPPER AFTERWORD --