Advertisement
lordjackson

bootstrap-treeview

Mar 1st, 2018
122
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. /* =========================================================
  2.  * bootstrap-treeview.js v1.2.0
  3.  * =========================================================
  4.  * Copyright 2013 Jonathan Miles
  5.  * Project URL : http://www.jondmiles.com/bootstrap-treeview
  6.  *
  7.  * Licensed under the Apache License, Version 2.0 (the "License");
  8.  * you may not use this file except in compliance with the License.
  9.  * You may obtain a copy of the License at
  10.  *
  11.  * http://www.apache.org/licenses/LICENSE-2.0
  12.  *
  13.  * Unless required by applicable law or agreed to in writing, software
  14.  * distributed under the License is distributed on an "AS IS" BASIS,
  15.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  16.  * See the License for the specific language governing permissions and
  17.  * limitations under the License.
  18.  * ========================================================= */
  19.  
  20. ;(function ($, window, document, undefined) {
  21.  
  22.     console.log('teste');
  23.  
  24.     /*global jQuery, console*/
  25.  
  26.     'use strict';
  27.  
  28.     var pluginName = 'treeview';
  29.  
  30.     var _default = {};
  31.  
  32.     _default.settings = {
  33.  
  34.         injectStyle: true,
  35.  
  36.         levels: 2,
  37.  
  38.         expandIcon: 'glyphicon glyphicon-plus',
  39.         collapseIcon: 'glyphicon glyphicon-minus',
  40.         emptyIcon: 'glyphicon',
  41.         nodeIcon: '',
  42.         selectedIcon: '',
  43.         checkedIcon: 'glyphicon glyphicon-check',
  44.         uncheckedIcon: 'glyphicon glyphicon-unchecked',
  45.  
  46.         color: undefined, // '#000000',
  47.         backColor: undefined, // '#FFFFFF',
  48.         borderColor: undefined, // '#dddddd',
  49.         onhoverColor: '#F5F5F5',
  50.         selectedColor: '#FFFFFF',
  51.         selectedBackColor: '#428bca',
  52.         searchResultColor: '#D9534F',
  53.         searchResultBackColor: undefined, //'#FFFFFF',
  54.  
  55.         enableLinks: false,
  56.         highlightSelected: true,
  57.         highlightSearchResults: true,
  58.         showBorder: true,
  59.         showIcon: true,
  60.         showCheckbox: false,
  61.         showTags: false,
  62.         multiSelect: false,
  63.  
  64.         // Event handlers
  65.         onNodeChecked: undefined,
  66.         onNodeCollapsed: undefined,
  67.         onNodeDisabled: undefined,
  68.         onNodeEnabled: undefined,
  69.         onNodeExpanded: undefined,
  70.         onNodeSelected: undefined,
  71.         onNodeUnchecked: undefined,
  72.         onNodeUnselected: undefined,
  73.         onSearchComplete: undefined,
  74.         onSearchCleared: undefined
  75.     };
  76.  
  77.     _default.options = {
  78.         silent: false,
  79.         ignoreChildren: false
  80.     };
  81.  
  82.     _default.searchOptions = {
  83.         ignoreCase: true,
  84.         exactMatch: false,
  85.         revealResults: true
  86.     };
  87.  
  88.     var Tree = function (element, options) {
  89.  
  90.         this.$element = $(element);
  91.         this.elementId = element.id;
  92.         this.styleId = this.elementId + '-style';
  93.  
  94.         this.init(options);
  95.  
  96.         return {
  97.  
  98.             // Options (public access)
  99.             options: this.options,
  100.  
  101.             // Initialize / destroy methods
  102.             init: $.proxy(this.init, this),
  103.             remove: $.proxy(this.remove, this),
  104.  
  105.             // Get methods
  106.             getNode: $.proxy(this.getNode, this),
  107.             getParent: $.proxy(this.getParent, this),
  108.             getSiblings: $.proxy(this.getSiblings, this),
  109.             getSelected: $.proxy(this.getSelected, this),
  110.             getUnselected: $.proxy(this.getUnselected, this),
  111.             getExpanded: $.proxy(this.getExpanded, this),
  112.             getCollapsed: $.proxy(this.getCollapsed, this),
  113.             getChecked: $.proxy(this.getChecked, this),
  114.             getUnchecked: $.proxy(this.getUnchecked, this),
  115.             getDisabled: $.proxy(this.getDisabled, this),
  116.             getEnabled: $.proxy(this.getEnabled, this),
  117.  
  118.             // Select methods
  119.             selectNode: $.proxy(this.selectNode, this),
  120.             unselectNode: $.proxy(this.unselectNode, this),
  121.             toggleNodeSelected: $.proxy(this.toggleNodeSelected, this),
  122.  
  123.             // Expand / collapse methods
  124.             collapseAll: $.proxy(this.collapseAll, this),
  125.             collapseNode: $.proxy(this.collapseNode, this),
  126.             expandAll: $.proxy(this.expandAll, this),
  127.             expandNode: $.proxy(this.expandNode, this),
  128.             toggleNodeExpanded: $.proxy(this.toggleNodeExpanded, this),
  129.             revealNode: $.proxy(this.revealNode, this),
  130.  
  131.             // Expand / collapse methods
  132.             checkAll: $.proxy(this.checkAll, this),
  133.             checkNode: $.proxy(this.checkNode, this),
  134.             uncheckAll: $.proxy(this.uncheckAll, this),
  135.             uncheckNode: $.proxy(this.uncheckNode, this),
  136.             toggleNodeChecked: $.proxy(this.toggleNodeChecked, this),
  137.  
  138.             // Disable / enable methods
  139.             disableAll: $.proxy(this.disableAll, this),
  140.             disableNode: $.proxy(this.disableNode, this),
  141.             enableAll: $.proxy(this.enableAll, this),
  142.             enableNode: $.proxy(this.enableNode, this),
  143.             toggleNodeDisabled: $.proxy(this.toggleNodeDisabled, this),
  144.  
  145.             // Search methods
  146.             search: $.proxy(this.search, this),
  147.             clearSearch: $.proxy(this.clearSearch, this)
  148.         };
  149.     };
  150.  
  151.     Tree.prototype.init = function (options) {
  152.  
  153.         this.tree = [];
  154.         this.nodes = [];
  155.  
  156.         if (options.data) {
  157.             if (typeof options.data === 'string') {
  158.                 options.data = $.parseJSON(options.data);
  159.             }
  160.             this.tree = $.extend(true, [], options.data);
  161.             delete options.data;
  162.         }
  163.         this.options = $.extend({}, _default.settings, options);
  164.  
  165.         this.destroy();
  166.         this.subscribeEvents();
  167.         this.setInitialStates({ nodes: this.tree }, 0);
  168.         this.render();
  169.     };
  170.  
  171.     Tree.prototype.remove = function () {
  172.         this.destroy();
  173.         $.removeData(this, pluginName);
  174.         $('#' + this.styleId).remove();
  175.     };
  176.  
  177.     Tree.prototype.destroy = function () {
  178.  
  179.         if (!this.initialized) return;
  180.  
  181.         this.$wrapper.remove();
  182.         this.$wrapper = null;
  183.  
  184.         // Switch off events
  185.         this.unsubscribeEvents();
  186.  
  187.         // Reset this.initialized flag
  188.         this.initialized = false;
  189.     };
  190.  
  191.     Tree.prototype.unsubscribeEvents = function () {
  192.  
  193.         this.$element.off('click');
  194.         this.$element.off('nodeChecked');
  195.         this.$element.off('nodeCollapsed');
  196.         this.$element.off('nodeDisabled');
  197.         this.$element.off('nodeEnabled');
  198.         this.$element.off('nodeExpanded');
  199.         this.$element.off('nodeSelected');
  200.         this.$element.off('nodeUnchecked');
  201.         this.$element.off('nodeUnselected');
  202.         this.$element.off('searchComplete');
  203.         this.$element.off('searchCleared');
  204.     };
  205.  
  206.     Tree.prototype.subscribeEvents = function () {
  207.  
  208.         this.unsubscribeEvents();
  209.  
  210.         this.$element.on('click', $.proxy(this.clickHandler, this));
  211.  
  212.         if (typeof (this.options.onNodeChecked) === 'function') {
  213.             this.$element.on('nodeChecked', this.options.onNodeChecked);
  214.         }
  215.  
  216.         if (typeof (this.options.onNodeCollapsed) === 'function') {
  217.             this.$element.on('nodeCollapsed', this.options.onNodeCollapsed);
  218.         }
  219.  
  220.         if (typeof (this.options.onNodeDisabled) === 'function') {
  221.             this.$element.on('nodeDisabled', this.options.onNodeDisabled);
  222.         }
  223.  
  224.         if (typeof (this.options.onNodeEnabled) === 'function') {
  225.             this.$element.on('nodeEnabled', this.options.onNodeEnabled);
  226.         }
  227.  
  228.         if (typeof (this.options.onNodeExpanded) === 'function') {
  229.             this.$element.on('nodeExpanded', this.options.onNodeExpanded);
  230.         }
  231.  
  232.         if (typeof (this.options.onNodeSelected) === 'function') {
  233.             this.$element.on('nodeSelected', this.options.onNodeSelected);
  234.         }
  235.  
  236.         if (typeof (this.options.onNodeUnchecked) === 'function') {
  237.             this.$element.on('nodeUnchecked', this.options.onNodeUnchecked);
  238.         }
  239.  
  240.         if (typeof (this.options.onNodeUnselected) === 'function') {
  241.             this.$element.on('nodeUnselected', this.options.onNodeUnselected);
  242.         }
  243.  
  244.         if (typeof (this.options.onSearchComplete) === 'function') {
  245.             this.$element.on('searchComplete', this.options.onSearchComplete);
  246.         }
  247.  
  248.         if (typeof (this.options.onSearchCleared) === 'function') {
  249.             this.$element.on('searchCleared', this.options.onSearchCleared);
  250.         }
  251.     };
  252.  
  253.     /*
  254.         Recurse the tree structure and ensure all nodes have
  255.         valid initial states.  User defined states will be preserved.
  256.         For performance we also take this opportunity to
  257.         index nodes in a flattened structure
  258.     */
  259.     Tree.prototype.setInitialStates = function (node, level) {
  260.  
  261.         if (!node.nodes) return;
  262.         level += 1;
  263.  
  264.  
  265.  
  266.         var parent = node;
  267.         var _this = this;
  268.         $.each(node.nodes, function checkStates(index, node) {
  269.  
  270.             // nodeId : unique, incremental identifier
  271.             node.nodeId = _this.nodes.length;
  272.  
  273.             // parentId : transversing up the tree
  274.             node.parentId = parent.nodeId;
  275.  
  276.             // if not provided set selectable default value
  277.             if (!node.hasOwnProperty('selectable')) {
  278.                 node.selectable = true;
  279.             }
  280.  
  281.             // where provided we should preserve states
  282.             node.state = node.state || {};
  283.  
  284.             // set checked state; unless set always false
  285.             if (!node.state.hasOwnProperty('checked')) {
  286.                 node.state.checked = false;
  287.             }
  288.  
  289.             // set enabled state; unless set always false
  290.             if (!node.state.hasOwnProperty('disabled')) {
  291.                 node.state.disabled = false;
  292.             }
  293.  
  294.             // set expanded state; if not provided based on levels
  295.             if (!node.state.hasOwnProperty('expanded')) {
  296.                 if (!node.state.disabled &&
  297.                         (level < _this.options.levels) &&
  298.                         (node.nodes && node.nodes.length > 0)) {
  299.                     node.state.expanded = true;
  300.                 }
  301.                 else {
  302.                     node.state.expanded = false;
  303.                 }
  304.             }
  305.  
  306.             // set selected state; unless set always false
  307.             if (!node.state.hasOwnProperty('selected')) {
  308.                 node.state.selected = false;
  309.             }
  310.  
  311.             // index nodes in a flattened structure for use later
  312.             _this.nodes.push(node);
  313.  
  314.             // recurse child nodes and transverse the tree
  315.             if (node.nodes) {
  316.                 _this.setInitialStates(node, level);
  317.             }
  318.         });
  319.     };
  320.  
  321.     Tree.prototype.clickHandler = function (event) {
  322.  
  323.         if (!this.options.enableLinks) event.preventDefault();
  324.  
  325.         var target = $(event.target);
  326.         var node = this.findNode(target);
  327.         if (!node || node.state.disabled) return;
  328.        
  329.         var classList = target.attr('class') ? target.attr('class').split(' ') : [];
  330.         if ((classList.indexOf('expand-icon') !== -1)) {
  331.  
  332.             this.toggleExpandedState(node, _default.options);
  333.             this.render();
  334.         }
  335.         else if ((classList.indexOf('check-icon') !== -1)) {
  336.            
  337.             this.toggleCheckedState(node, _default.options);
  338.             this.render();
  339.         }
  340.         else {
  341.            
  342.             if (node.selectable) {
  343.                 this.toggleSelectedState(node, _default.options);
  344.             } else {
  345.                 this.toggleExpandedState(node, _default.options);
  346.             }
  347.  
  348.             this.render();
  349.         }
  350.     };
  351.  
  352.     // Looks up the DOM for the closest parent list item to retrieve the
  353.     // data attribute nodeid, which is used to lookup the node in the flattened structure.
  354.     Tree.prototype.findNode = function (target) {
  355.  
  356.         var nodeId = target.closest('li.list-group-item').attr('data-nodeid');
  357.         var node = this.nodes[nodeId];
  358.  
  359.         if (!node) {
  360.             console.log('Error: node does not exist');
  361.         }
  362.         return node;
  363.     };
  364.  
  365.     Tree.prototype.toggleExpandedState = function (node, options) {
  366.         if (!node) return;
  367.         this.setExpandedState(node, !node.state.expanded, options);
  368.     };
  369.  
  370.     Tree.prototype.setExpandedState = function (node, state, options) {
  371.  
  372.         if (state === node.state.expanded) return;
  373.  
  374.         if (state && node.nodes) {
  375.  
  376.             // Expand a node
  377.             node.state.expanded = true;
  378.             if (!options.silent) {
  379.                 this.$element.trigger('nodeExpanded', $.extend(true, {}, node));
  380.             }
  381.         }
  382.         else if (!state) {
  383.  
  384.             // Collapse a node
  385.             node.state.expanded = false;
  386.             if (!options.silent) {
  387.                 this.$element.trigger('nodeCollapsed', $.extend(true, {}, node));
  388.             }
  389.  
  390.             // Collapse child nodes
  391.             if (node.nodes && !options.ignoreChildren) {
  392.                 $.each(node.nodes, $.proxy(function (index, node) {
  393.                     this.setExpandedState(node, false, options);
  394.                 }, this));
  395.             }
  396.         }
  397.     };
  398.  
  399.     Tree.prototype.toggleSelectedState = function (node, options) {
  400.         if (!node) return;
  401.         this.setSelectedState(node, !node.state.selected, options);
  402.     };
  403.  
  404.     Tree.prototype.setSelectedState = function (node, state, options) {
  405.  
  406.         if (state === node.state.selected) return;
  407.  
  408.         if (state) {
  409.  
  410.             // If multiSelect false, unselect previously selected
  411.             if (!this.options.multiSelect) {
  412.                 $.each(this.findNodes('true', 'g', 'state.selected'), $.proxy(function (index, node) {
  413.                     this.setSelectedState(node, false, options);
  414.                 }, this));
  415.             }
  416.  
  417.             // Continue selecting node
  418.             node.state.selected = true;
  419.             if (!options.silent) {
  420.                 this.$element.trigger('nodeSelected', $.extend(true, {}, node));
  421.             }
  422.         }
  423.         else {
  424.  
  425.             // Unselect node
  426.             node.state.selected = false;
  427.             if (!options.silent) {
  428.                 this.$element.trigger('nodeUnselected', $.extend(true, {}, node));
  429.             }
  430.         }
  431.     };
  432.  
  433.     Tree.prototype.toggleCheckedState = function (node, options) {
  434.         if (!node) return;
  435.         this.setCheckedState(node, !node.state.checked, options);
  436.     };
  437.  
  438.     Tree.prototype.setCheckedState = function (node, state, options) {
  439.  
  440.         if (state === node.state.checked) return;
  441.  
  442.         if (state) {
  443.  
  444.             // Check node
  445.             node.state.checked = true;
  446.  
  447.             if (!options.silent) {
  448.                 this.$element.trigger('nodeChecked', $.extend(true, {}, node));
  449.             }
  450.         }
  451.         else {
  452.  
  453.             // Uncheck node
  454.             node.state.checked = false;
  455.             if (!options.silent) {
  456.                 this.$element.trigger('nodeUnchecked', $.extend(true, {}, node));
  457.             }
  458.         }
  459.     };
  460.  
  461.     Tree.prototype.setDisabledState = function (node, state, options) {
  462.  
  463.         if (state === node.state.disabled) return;
  464.  
  465.         if (state) {
  466.  
  467.             // Disable node
  468.             node.state.disabled = true;
  469.  
  470.             // Disable all other states
  471.             this.setExpandedState(node, false, options);
  472.             this.setSelectedState(node, false, options);
  473.             this.setCheckedState(node, false, options);
  474.  
  475.             if (!options.silent) {
  476.                 this.$element.trigger('nodeDisabled', $.extend(true, {}, node));
  477.             }
  478.         }
  479.         else {
  480.  
  481.             // Enabled node
  482.             node.state.disabled = false;
  483.             if (!options.silent) {
  484.                 this.$element.trigger('nodeEnabled', $.extend(true, {}, node));
  485.             }
  486.         }
  487.     };
  488.  
  489.     Tree.prototype.render = function () {
  490.  
  491.         if (!this.initialized) {
  492.  
  493.             // Setup first time only components
  494.             this.$element.addClass(pluginName);
  495.             this.$wrapper = $(this.template.list);
  496.  
  497.             this.injectStyle();
  498.  
  499.             this.initialized = true;
  500.         }
  501.  
  502.         this.$element.empty().append(this.$wrapper.empty());
  503.  
  504.         // Build tree
  505.         this.buildTree(this.tree, 0);
  506.     };
  507.  
  508.     // Starting from the root node, and recursing down the
  509.     // structure we build the tree one node at a time
  510.     Tree.prototype.buildTree = function (nodes, level) {
  511.  
  512.         if (!nodes) return;
  513.         level += 1;
  514.  
  515.         var _this = this;
  516.         $.each(nodes, function addNodes(id, node) {
  517.  
  518.             var treeItem = $(_this.template.item)
  519.                 .addClass('node-' + _this.elementId)
  520.                 .addClass(node.state.checked ? 'node-checked' : '')
  521.                 .addClass(node.state.disabled ? 'node-disabled': '')
  522.                 .addClass(node.state.selected ? 'node-selected' : '')
  523.                 .addClass(node.searchResult ? 'search-result' : '')
  524.                 .attr('data-nodeid', node.nodeId)
  525.                 .attr('id', node.id )
  526.                 .attr('style', _this.buildStyleOverride(node));
  527.  
  528.             // Add indent/spacer to mimic tree structure
  529.             for (var i = 0; i < (level - 1); i++) {
  530.                 treeItem.append(_this.template.indent);
  531.             }
  532.  
  533.             // Add expand, collapse or empty spacer icons
  534.             var classList = [];
  535.             if (node.nodes) {
  536.                 classList.push('expand-icon');
  537.                 if (node.state.expanded) {
  538.                     classList.push(_this.options.collapseIcon);
  539.                 }
  540.                 else {
  541.                     classList.push(_this.options.expandIcon);
  542.                 }
  543.             }
  544.             else {
  545.                 classList.push(_this.options.emptyIcon);
  546.             }
  547.  
  548.             treeItem
  549.                 .append($(_this.template.icon)
  550.                     .addClass(classList.join(' '))
  551.                 );
  552.  
  553.  
  554.             // Add node icon
  555.             if (_this.options.showIcon) {
  556.                
  557.                 var classList = ['node-icon'];
  558.  
  559.                 classList.push(node.icon || _this.options.nodeIcon);
  560.                 if (node.state.selected) {
  561.                     classList.pop();
  562.                     classList.push(node.selectedIcon || _this.options.selectedIcon ||
  563.                                     node.icon || _this.options.nodeIcon);
  564.                 }
  565.  
  566.                 treeItem
  567.                     .append($(_this.template.icon)
  568.                         .addClass(classList.join(' '))
  569.                     );
  570.             }
  571.  
  572.             // Add check / unchecked icon
  573.             if (_this.options.showCheckbox) {
  574.  
  575.                 var classList = ['check-icon'];
  576.                 if (node.state.checked) {
  577.                     classList.push(_this.options.checkedIcon);
  578.                 }
  579.                 else {
  580.                     classList.push(_this.options.uncheckedIcon);
  581.                 }
  582.  
  583.                 treeItem
  584.                     .append($(_this.template.icon)
  585.                         .addClass(classList.join(' '))
  586.                     );
  587.             }
  588.  
  589.             // Add text
  590.             if (_this.options.enableLinks) {
  591.                 // Add hyperlink
  592.                 treeItem
  593.                     .append($(_this.template.link)
  594.                         .attr('href', node.href)
  595.                         .append(node.text)
  596.                     );
  597.             }
  598.             else {
  599.                 // otherwise just text
  600.                 treeItem
  601.                     .append(node.text);
  602.             }
  603.  
  604.             // Add tags as badges
  605.             if (_this.options.showTags && node.tags) {
  606.                 $.each(node.tags, function addTag(id, tag) {
  607.                     treeItem
  608.                         .append($(_this.template.badge)
  609.                             .append(tag)
  610.                         );
  611.                 });
  612.             }
  613.  
  614.             // Add item to the tree
  615.             _this.$wrapper.append(treeItem);
  616.  
  617.             // Recursively add child ndoes
  618.             if (node.nodes && node.state.expanded && !node.state.disabled) {
  619.                 return _this.buildTree(node.nodes, level);
  620.             }
  621.         });
  622.     };
  623.  
  624.     // Define any node level style override for
  625.     // 1. selectedNode
  626.     // 2. node|data assigned color overrides
  627.     Tree.prototype.buildStyleOverride = function (node) {
  628.  
  629.         if (node.state.disabled) return '';
  630.  
  631.         var color = node.color;
  632.         var backColor = node.backColor;
  633.  
  634.         if (this.options.highlightSelected && node.state.selected) {
  635.             if (this.options.selectedColor) {
  636.                 color = this.options.selectedColor + '; font-weight: bold; ';
  637.             }
  638.             if (this.options.selectedBackColor) {
  639.                 backColor = this.options.selectedBackColor + '; font-weight: bold; ';
  640.             }
  641.         }
  642.  
  643.         if (this.options.highlightSearchResults && node.searchResult && !node.state.disabled) {
  644.             if (this.options.searchResultColor) {
  645.                 color = this.options.searchResultColor;
  646.             }
  647.             if (this.options.searchResultBackColor) {
  648.                 backColor = this.options.searchResultBackColor;
  649.             }
  650.         }
  651.  
  652.         return 'color:' + color +
  653.             ';background-color:' + backColor + ';';
  654.     };
  655.  
  656.     // Add inline style into head
  657.     Tree.prototype.injectStyle = function () {
  658.  
  659.         if (this.options.injectStyle && !document.getElementById(this.styleId)) {
  660.             $('<style type="text/css" id="' + this.styleId + '"> ' + this.buildStyle() + ' </style>').appendTo('head');
  661.         }
  662.     };
  663.  
  664.     // Construct trees style based on user options
  665.     Tree.prototype.buildStyle = function () {
  666.  
  667.         var style = '.node-' + this.elementId + '{';
  668.  
  669.         if (this.options.color) {
  670.             style += 'color:' + this.options.color + ';';
  671.         }
  672.  
  673.         if (this.options.backColor) {
  674.             style += 'background-color:' + this.options.backColor + ';';
  675.         }
  676.  
  677.         if (!this.options.showBorder) {
  678.             style += 'border:none;';
  679.         }
  680.         else if (this.options.borderColor) {
  681.             style += 'border:1px solid ' + this.options.borderColor + ';';
  682.         }
  683.         style += '}';
  684.  
  685.         if (this.options.onhoverColor) {
  686.             style += '.node-' + this.elementId + ':not(.node-disabled):hover{' +
  687.                 'background-color:' + this.options.onhoverColor + ';' +
  688.             '}';
  689.         }
  690.  
  691.         return this.css + style;
  692.     };
  693.  
  694.     Tree.prototype.template = {
  695.         list: '<ul class="list-group"></ul>',
  696.         item: '<li class="list-group-item"></li>',
  697.         indent: '<span class="indent"></span>',
  698.         icon: '<span class="icon"></span>',
  699.         link: '<a href="#" style="color:inherit;"></a>',
  700.         badge: '<span class="badge"></span>'
  701.     };
  702.  
  703.     Tree.prototype.css = '.treeview .list-group-item{cursor:pointer}.treeview span.indent{margin-left:10px;margin-right:10px}.treeview span.icon{width:12px;margin-right:5px}.treeview .node-disabled{color:silver;cursor:not-allowed}'
  704.  
  705.  
  706.     /**
  707.         Returns a single node object that matches the given node id.
  708.         @param {Number} nodeId - A node's unique identifier
  709.         @return {Object} node - Matching node
  710.     */
  711.     Tree.prototype.getNode = function (nodeId) {
  712.         return this.nodes[nodeId];
  713.     };
  714.  
  715.     /**
  716.         Returns the parent node of a given node, if valid otherwise returns undefined.
  717.         @param {Object|Number} identifier - A valid node or node id
  718.         @returns {Object} node - The parent node
  719.     */
  720.     Tree.prototype.getParent = function (identifier) {
  721.         var node = this.identifyNode(identifier);
  722.         return this.nodes[node.parentId];
  723.     };
  724.  
  725.     /**
  726.         Returns an array of sibling nodes for a given node, if valid otherwise returns undefined.
  727.         @param {Object|Number} identifier - A valid node or node id
  728.         @returns {Array} nodes - Sibling nodes
  729.     */
  730.     Tree.prototype.getSiblings = function (identifier) {
  731.         var node = this.identifyNode(identifier);
  732.         var parent = this.getParent(node);
  733.         var nodes = parent ? parent.nodes : this.tree;
  734.         return nodes.filter(function (obj) {
  735.                 return obj.nodeId !== node.nodeId;
  736.             });
  737.     };
  738.  
  739.     /**
  740.         Returns an array of selected nodes.
  741.         @returns {Array} nodes - Selected nodes
  742.     */
  743.     Tree.prototype.getSelected = function () {
  744.         return this.findNodes('true', 'g', 'state.selected');
  745.     };
  746.  
  747.     /**
  748.         Returns an array of unselected nodes.
  749.         @returns {Array} nodes - Unselected nodes
  750.     */
  751.     Tree.prototype.getUnselected = function () {
  752.         return this.findNodes('false', 'g', 'state.selected');
  753.     };
  754.  
  755.     /**
  756.         Returns an array of expanded nodes.
  757.         @returns {Array} nodes - Expanded nodes
  758.     */
  759.     Tree.prototype.getExpanded = function () {
  760.         return this.findNodes('true', 'g', 'state.expanded');
  761.     };
  762.  
  763.     /**
  764.         Returns an array of collapsed nodes.
  765.         @returns {Array} nodes - Collapsed nodes
  766.     */
  767.     Tree.prototype.getCollapsed = function () {
  768.         return this.findNodes('false', 'g', 'state.expanded');
  769.     };
  770.  
  771.     /**
  772.         Returns an array of checked nodes.
  773.         @returns {Array} nodes - Checked nodes
  774.     */
  775.     Tree.prototype.getChecked = function () {
  776.         return this.findNodes('true', 'g', 'state.checked');
  777.     };
  778.  
  779.     /**
  780.         Returns an array of unchecked nodes.
  781.         @returns {Array} nodes - Unchecked nodes
  782.     */
  783.     Tree.prototype.getUnchecked = function () {
  784.         return this.findNodes('false', 'g', 'state.checked');
  785.     };
  786.  
  787.     /**
  788.         Returns an array of disabled nodes.
  789.         @returns {Array} nodes - Disabled nodes
  790.     */
  791.     Tree.prototype.getDisabled = function () {
  792.         return this.findNodes('true', 'g', 'state.disabled');
  793.     };
  794.  
  795.     /**
  796.         Returns an array of enabled nodes.
  797.         @returns {Array} nodes - Enabled nodes
  798.     */
  799.     Tree.prototype.getEnabled = function () {
  800.         return this.findNodes('false', 'g', 'state.disabled');
  801.     };
  802.  
  803.  
  804.     /**
  805.         Set a node state to selected
  806.         @param {Object|Number} identifiers - A valid node, node id or array of node identifiers
  807.         @param {optional Object} options
  808.     */
  809.     Tree.prototype.selectNode = function (identifiers, options) {
  810.         this.forEachIdentifier(identifiers, options, $.proxy(function (node, options) {
  811.             this.setSelectedState(node, true, options);
  812.         }, this));
  813.  
  814.         this.render();
  815.     };
  816.  
  817.     /**
  818.         Set a node state to unselected
  819.         @param {Object|Number} identifiers - A valid node, node id or array of node identifiers
  820.         @param {optional Object} options
  821.     */
  822.     Tree.prototype.unselectNode = function (identifiers, options) {
  823.         this.forEachIdentifier(identifiers, options, $.proxy(function (node, options) {
  824.             this.setSelectedState(node, false, options);
  825.         }, this));
  826.  
  827.         this.render();
  828.     };
  829.  
  830.     /**
  831.         Toggles a node selected state; selecting if unselected, unselecting if selected.
  832.         @param {Object|Number} identifiers - A valid node, node id or array of node identifiers
  833.         @param {optional Object} options
  834.     */
  835.     Tree.prototype.toggleNodeSelected = function (identifiers, options) {
  836.         this.forEachIdentifier(identifiers, options, $.proxy(function (node, options) {
  837.             this.toggleSelectedState(node, options);
  838.         }, this));
  839.  
  840.         this.render();
  841.     };
  842.  
  843.  
  844.     /**
  845.         Collapse all tree nodes
  846.         @param {optional Object} options
  847.     */
  848.     Tree.prototype.collapseAll = function (options) {
  849.         var identifiers = this.findNodes('true', 'g', 'state.expanded');
  850.         this.forEachIdentifier(identifiers, options, $.proxy(function (node, options) {
  851.             this.setExpandedState(node, false, options);
  852.         }, this));
  853.  
  854.         this.render();
  855.     };
  856.  
  857.     /**
  858.         Collapse a given tree node
  859.         @param {Object|Number} identifiers - A valid node, node id or array of node identifiers
  860.         @param {optional Object} options
  861.     */
  862.     Tree.prototype.collapseNode = function (identifiers, options) {
  863.         this.forEachIdentifier(identifiers, options, $.proxy(function (node, options) {
  864.             this.setExpandedState(node, false, options);
  865.         }, this));
  866.  
  867.         this.render();
  868.     };
  869.  
  870.     /**
  871.         Expand all tree nodes
  872.         @param {optional Object} options
  873.     */
  874.     Tree.prototype.expandAll = function (options) {
  875.         options = $.extend({}, _default.options, options);
  876.  
  877.         if (options && options.levels) {
  878.             this.expandLevels(this.tree, options.levels, options);
  879.         }
  880.         else {
  881.             var identifiers = this.findNodes('false', 'g', 'state.expanded');
  882.             this.forEachIdentifier(identifiers, options, $.proxy(function (node, options) {
  883.                 this.setExpandedState(node, true, options);
  884.             }, this));
  885.         }
  886.  
  887.         this.render();
  888.     };
  889.  
  890.     /**
  891.         Expand a given tree node
  892.         @param {Object|Number} identifiers - A valid node, node id or array of node identifiers
  893.         @param {optional Object} options
  894.     */
  895.     Tree.prototype.expandNode = function (identifiers, options) {
  896.         this.forEachIdentifier(identifiers, options, $.proxy(function (node, options) {
  897.             this.setExpandedState(node, true, options);
  898.             if (node.nodes && (options && options.levels)) {
  899.                 this.expandLevels(node.nodes, options.levels-1, options);
  900.             }
  901.         }, this));
  902.  
  903.         this.render();
  904.     };
  905.  
  906.     Tree.prototype.expandLevels = function (nodes, level, options) {
  907.         options = $.extend({}, _default.options, options);
  908.  
  909.         $.each(nodes, $.proxy(function (index, node) {
  910.             this.setExpandedState(node, (level > 0) ? true : false, options);
  911.             if (node.nodes) {
  912.                 this.expandLevels(node.nodes, level-1, options);
  913.             }
  914.         }, this));
  915.     };
  916.  
  917.     /**
  918.         Reveals a given tree node, expanding the tree from node to root.
  919.         @param {Object|Number|Array} identifiers - A valid node, node id or array of node identifiers
  920.         @param {optional Object} options
  921.     */
  922.     Tree.prototype.revealNode = function (identifiers, options) {
  923.         this.forEachIdentifier(identifiers, options, $.proxy(function (node, options) {
  924.             var parentNode = this.getParent(node);
  925.             while (parentNode) {
  926.                 this.setExpandedState(parentNode, true, options);
  927.                 parentNode = this.getParent(parentNode);
  928.             };
  929.         }, this));
  930.  
  931.         this.render();
  932.     };
  933.  
  934.     /**
  935.         Toggles a nodes expanded state; collapsing if expanded, expanding if collapsed.
  936.         @param {Object|Number} identifiers - A valid node, node id or array of node identifiers
  937.         @param {optional Object} options
  938.     */
  939.     Tree.prototype.toggleNodeExpanded = function (identifiers, options) {
  940.         this.forEachIdentifier(identifiers, options, $.proxy(function (node, options) {
  941.             this.toggleExpandedState(node, options);
  942.         }, this));
  943.        
  944.         this.render();
  945.     };
  946.  
  947.  
  948.     /**
  949.         Check all tree nodes
  950.         @param {optional Object} options
  951.     */
  952.     Tree.prototype.checkAll = function (options) {
  953.         var identifiers = this.findNodes('false', 'g', 'state.checked');
  954.         this.forEachIdentifier(identifiers, options, $.proxy(function (node, options) {
  955.             this.setCheckedState(node, true, options);
  956.         }, this));
  957.  
  958.         this.render();
  959.     };
  960.  
  961.     /**
  962.         Check a given tree node
  963.         @param {Object|Number} identifiers - A valid node, node id or array of node identifiers
  964.         @param {optional Object} options
  965.     */
  966.     Tree.prototype.checkNode = function (identifiers, options) {
  967.         this.forEachIdentifier(identifiers, options, $.proxy(function (node, options) {
  968.             this.setCheckedState(node, true, options);
  969.         }, this));
  970.  
  971.         this.render();
  972.     };
  973.  
  974.     /**
  975.         Uncheck all tree nodes
  976.         @param {optional Object} options
  977.     */
  978.     Tree.prototype.uncheckAll = function (options) {
  979.         var identifiers = this.findNodes('true', 'g', 'state.checked');
  980.         this.forEachIdentifier(identifiers, options, $.proxy(function (node, options) {
  981.             this.setCheckedState(node, false, options);
  982.         }, this));
  983.  
  984.         this.render();
  985.     };
  986.  
  987.     /**
  988.         Uncheck a given tree node
  989.         @param {Object|Number} identifiers - A valid node, node id or array of node identifiers
  990.         @param {optional Object} options
  991.     */
  992.     Tree.prototype.uncheckNode = function (identifiers, options) {
  993.         this.forEachIdentifier(identifiers, options, $.proxy(function (node, options) {
  994.             this.setCheckedState(node, false, options);
  995.         }, this));
  996.  
  997.         this.render();
  998.     };
  999.  
  1000.     /**
  1001.         Toggles a nodes checked state; checking if unchecked, unchecking if checked.
  1002.         @param {Object|Number} identifiers - A valid node, node id or array of node identifiers
  1003.         @param {optional Object} options
  1004.     */
  1005.     Tree.prototype.toggleNodeChecked = function (identifiers, options) {
  1006.         this.forEachIdentifier(identifiers, options, $.proxy(function (node, options) {
  1007.             this.toggleCheckedState(node, options);
  1008.         }, this));
  1009.  
  1010.         this.render();
  1011.     };
  1012.  
  1013.  
  1014.     /**
  1015.         Disable all tree nodes
  1016.         @param {optional Object} options
  1017.     */
  1018.     Tree.prototype.disableAll = function (options) {
  1019.         var identifiers = this.findNodes('false', 'g', 'state.disabled');
  1020.         this.forEachIdentifier(identifiers, options, $.proxy(function (node, options) {
  1021.             this.setDisabledState(node, true, options);
  1022.         }, this));
  1023.  
  1024.         this.render();
  1025.     };
  1026.  
  1027.     /**
  1028.         Disable a given tree node
  1029.         @param {Object|Number} identifiers - A valid node, node id or array of node identifiers
  1030.         @param {optional Object} options
  1031.     */
  1032.     Tree.prototype.disableNode = function (identifiers, options) {
  1033.         this.forEachIdentifier(identifiers, options, $.proxy(function (node, options) {
  1034.             this.setDisabledState(node, true, options);
  1035.         }, this));
  1036.  
  1037.         this.render();
  1038.     };
  1039.  
  1040.     /**
  1041.         Enable all tree nodes
  1042.         @param {optional Object} options
  1043.     */
  1044.     Tree.prototype.enableAll = function (options) {
  1045.         var identifiers = this.findNodes('true', 'g', 'state.disabled');
  1046.         this.forEachIdentifier(identifiers, options, $.proxy(function (node, options) {
  1047.             this.setDisabledState(node, false, options);
  1048.         }, this));
  1049.  
  1050.         this.render();
  1051.     };
  1052.  
  1053.     /**
  1054.         Enable a given tree node
  1055.         @param {Object|Number} identifiers - A valid node, node id or array of node identifiers
  1056.         @param {optional Object} options
  1057.     */
  1058.     Tree.prototype.enableNode = function (identifiers, options) {
  1059.         this.forEachIdentifier(identifiers, options, $.proxy(function (node, options) {
  1060.             this.setDisabledState(node, false, options);
  1061.         }, this));
  1062.  
  1063.         this.render();
  1064.     };
  1065.  
  1066.     /**
  1067.         Toggles a nodes disabled state; disabling is enabled, enabling if disabled.
  1068.         @param {Object|Number} identifiers - A valid node, node id or array of node identifiers
  1069.         @param {optional Object} options
  1070.     */
  1071.     Tree.prototype.toggleNodeDisabled = function (identifiers, options) {
  1072.         this.forEachIdentifier(identifiers, options, $.proxy(function (node, options) {
  1073.             this.setDisabledState(node, !node.state.disabled, options);
  1074.         }, this));
  1075.  
  1076.         this.render();
  1077.     };
  1078.  
  1079.  
  1080.     /**
  1081.         Common code for processing multiple identifiers
  1082.     */
  1083.     Tree.prototype.forEachIdentifier = function (identifiers, options, callback) {
  1084.  
  1085.         options = $.extend({}, _default.options, options);
  1086.  
  1087.         if (!(identifiers instanceof Array)) {
  1088.             identifiers = [identifiers];
  1089.         }
  1090.  
  1091.         $.each(identifiers, $.proxy(function (index, identifier) {
  1092.             callback(this.identifyNode(identifier), options);
  1093.         }, this)); 
  1094.     };
  1095.  
  1096.     /*
  1097.         Identifies a node from either a node id or object
  1098.     */
  1099.     Tree.prototype.identifyNode = function (identifier) {
  1100.         return ((typeof identifier) === 'number') ?
  1101.                         this.nodes[identifier] :
  1102.                         identifier;
  1103.     };
  1104.  
  1105.     /**
  1106.         Searches the tree for nodes (text) that match given criteria
  1107.         @param {String} pattern - A given string to match against
  1108.         @param {optional Object} options - Search criteria options
  1109.         @return {Array} nodes - Matching nodes
  1110.     */
  1111.     Tree.prototype.search = function (pattern, options) {
  1112.         options = $.extend({}, _default.searchOptions, options);
  1113.  
  1114.         this.clearSearch({ render: false });
  1115.  
  1116.         var results = [];
  1117.         if (pattern && pattern.length > 0) {
  1118.  
  1119.             if (options.exactMatch) {
  1120.                 pattern = '^' + pattern + '$';
  1121.             }
  1122.  
  1123.             var modifier = 'g';
  1124.             if (options.ignoreCase) {
  1125.                 modifier += 'i';
  1126.             }
  1127.  
  1128.             results = this.findNodes(pattern, modifier);
  1129.  
  1130.             // Add searchResult property to all matching nodes
  1131.             // This will be used to apply custom styles
  1132.             // and when identifying result to be cleared
  1133.             $.each(results, function (index, node) {
  1134.                 node.searchResult = true;
  1135.             })
  1136.         }
  1137.  
  1138.         // If revealResults, then render is triggered from revealNode
  1139.         // otherwise we just call render.
  1140.         if (options.revealResults) {
  1141.             this.revealNode(results);
  1142.         }
  1143.         else {
  1144.             this.render();
  1145.         }
  1146.  
  1147.         this.$element.trigger('searchComplete', $.extend(true, {}, results));
  1148.  
  1149.         return results;
  1150.     };
  1151.  
  1152.     /**
  1153.         Clears previous search results
  1154.     */
  1155.     Tree.prototype.clearSearch = function (options) {
  1156.  
  1157.         options = $.extend({}, { render: true }, options);
  1158.  
  1159.         var results = $.each(this.findNodes('true', 'g', 'searchResult'), function (index, node) {
  1160.             node.searchResult = false;
  1161.         });
  1162.  
  1163.         if (options.render) {
  1164.             this.render(); 
  1165.         }
  1166.        
  1167.         this.$element.trigger('searchCleared', $.extend(true, {}, results));
  1168.     };
  1169.  
  1170.     /**
  1171.         Find nodes that match a given criteria
  1172.         @param {String} pattern - A given string to match against
  1173.         @param {optional String} modifier - Valid RegEx modifiers
  1174.         @param {optional String} attribute - Attribute to compare pattern against
  1175.         @return {Array} nodes - Nodes that match your criteria
  1176.     */
  1177.     Tree.prototype.findNodes = function (pattern, modifier, attribute) {
  1178.  
  1179.         modifier = modifier || 'g';
  1180.         attribute = attribute || 'text';
  1181.  
  1182.         var _this = this;
  1183.         return $.grep(this.nodes, function (node) {
  1184.             var val = _this.getNodeValue(node, attribute);
  1185.             if (typeof val === 'string') {
  1186.                 return val.match(new RegExp(pattern, modifier));
  1187.             }
  1188.         });
  1189.     };
  1190.  
  1191.     /**
  1192.         Recursive find for retrieving nested attributes values
  1193.         All values are return as strings, unless invalid
  1194.         @param {Object} obj - Typically a node, could be any object
  1195.         @param {String} attr - Identifies an object property using dot notation
  1196.         @return {String} value - Matching attributes string representation
  1197.     */
  1198.     Tree.prototype.getNodeValue = function (obj, attr) {
  1199.         var index = attr.indexOf('.');
  1200.         if (index > 0) {
  1201.             var _obj = obj[attr.substring(0, index)];
  1202.             var _attr = attr.substring(index + 1, attr.length);
  1203.             return this.getNodeValue(_obj, _attr);
  1204.         }
  1205.         else {
  1206.             if (obj.hasOwnProperty(attr)) {
  1207.                 return obj[attr].toString();
  1208.             }
  1209.             else {
  1210.                 return undefined;
  1211.             }
  1212.         }
  1213.     };
  1214.  
  1215.     var logError = function (message) {
  1216.         if (window.console) {
  1217.             window.console.error(message);
  1218.         }
  1219.     };
  1220.  
  1221.     // Prevent against multiple instantiations,
  1222.     // handle updates and method calls
  1223.     $.fn[pluginName] = function (options, args) {
  1224.  
  1225.         var result;
  1226.  
  1227.         this.each(function () {
  1228.             var _this = $.data(this, pluginName);
  1229.             if (typeof options === 'string') {
  1230.                 if (!_this) {
  1231.                     logError('Not initialized, can not call method : ' + options);
  1232.                 }
  1233.                 else if (!$.isFunction(_this[options]) || options.charAt(0) === '_') {
  1234.                     logError('No such method : ' + options);
  1235.                 }
  1236.                 else {
  1237.                     if (!(args instanceof Array)) {
  1238.                         args = [ args ];
  1239.                     }
  1240.                     result = _this[options].apply(_this, args);
  1241.                 }
  1242.             }
  1243.             else if (typeof options === 'boolean') {
  1244.                 result = _this;
  1245.             }
  1246.             else {
  1247.                 $.data(this, pluginName, new Tree(this, $.extend(true, {}, options)));
  1248.             }
  1249.         });
  1250.  
  1251.         return result || this;
  1252.     };
  1253.  
  1254. })(jQuery, window, document);
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement