1// Extending Fancytree 2// =================== 3// 4// See also the [live demo](https://wwWendt.de/tech/fancytree/demo/sample-ext-childcounter.html) of this code. 5// 6// Every extension should have a comment header containing some information 7// about the author, copyright and licensing. Also a pointer to the latest 8// source code. 9// Prefix with `/*!` so the comment is not removed by the minifier. 10 11/*! 12 * jquery.fancytree.childcounter.js 13 * 14 * Add a child counter bubble to tree nodes. 15 * (Extension module for jquery.fancytree.js: https://github.com/mar10/fancytree/) 16 * 17 * Copyright (c) 2008-2023, Martin Wendt (https://wwWendt.de) 18 * 19 * Released under the MIT license 20 * https://github.com/mar10/fancytree/wiki/LicenseInfo 21 * 22 * @version 2.38.3 23 * @date 2023-02-01T20:52:50Z 24 */ 25 26// To keep the global namespace clean, we wrap everything in a closure. 27// The UMD wrapper pattern defines the dependencies on jQuery and the 28// Fancytree core module, and makes sure that we can use the `require()` 29// syntax with package loaders. 30 31(function (factory) { 32 if (typeof define === "function" && define.amd) { 33 // AMD. Register as an anonymous module. 34 define(["jquery", "./jquery.fancytree"], factory); 35 } else if (typeof module === "object" && module.exports) { 36 // Node/CommonJS 37 require("./jquery.fancytree"); 38 module.exports = factory(require("jquery")); 39 } else { 40 // Browser globals 41 factory(jQuery); 42 } 43})(function ($) { 44 // Consider to use [strict mode](http://ejohn.org/blog/ecmascript-5-strict-mode-json-and-more/) 45 "use strict"; 46 47 // The [coding guidelines](http://contribute.jquery.org/style-guide/js/) 48 // require jshint /eslint compliance. 49 // But for this sample, we want to allow unused variables for demonstration purpose. 50 51 /*eslint-disable no-unused-vars */ 52 53 // Adding methods 54 // -------------- 55 56 // New member functions can be added to the `Fancytree` class. 57 // This function will be available for every tree instance: 58 // 59 // var tree = $.ui.fancytree.getTree("#tree"); 60 // tree.countSelected(false); 61 62 $.ui.fancytree._FancytreeClass.prototype.countSelected = function ( 63 topOnly 64 ) { 65 var tree = this, 66 treeOptions = tree.options; 67 68 return tree.getSelectedNodes(topOnly).length; 69 }; 70 71 // The `FancytreeNode` class can also be easily extended. This would be called 72 // like 73 // node.updateCounters(); 74 // 75 // It is also good practice to add a docstring comment. 76 /** 77 * [ext-childcounter] Update counter badges for `node` and its parents. 78 * May be called in the `loadChildren` event, to update parents of lazy loaded 79 * nodes. 80 * @alias FancytreeNode#updateCounters 81 * @requires jquery.fancytree.childcounters.js 82 */ 83 $.ui.fancytree._FancytreeNodeClass.prototype.updateCounters = function () { 84 var node = this, 85 $badge = $("span.fancytree-childcounter", node.span), 86 extOpts = node.tree.options.childcounter, 87 count = node.countChildren(extOpts.deep); 88 89 node.data.childCounter = count; 90 if ( 91 (count || !extOpts.hideZeros) && 92 (!node.isExpanded() || !extOpts.hideExpanded) 93 ) { 94 if (!$badge.length) { 95 $badge = $("<span class='fancytree-childcounter'/>").appendTo( 96 $( 97 "span.fancytree-icon,span.fancytree-custom-icon", 98 node.span 99 ) 100 ); 101 } 102 $badge.text(count); 103 } else { 104 $badge.remove(); 105 } 106 if (extOpts.deep && !node.isTopLevel() && !node.isRootNode()) { 107 node.parent.updateCounters(); 108 } 109 }; 110 111 // Finally, we can extend the widget API and create functions that are called 112 // like so: 113 // 114 // $("#tree").fancytree("widgetMethod1", "abc"); 115 116 $.ui.fancytree.prototype.widgetMethod1 = function (arg1) { 117 var tree = this.tree; 118 return arg1; 119 }; 120 121 // Register a Fancytree extension 122 // ------------------------------ 123 // A full blown extension, extension is available for all trees and can be 124 // enabled like so (see also the [live demo](https://wwWendt.de/tech/fancytree/demo/sample-ext-childcounter.html)): 125 // 126 // <script src="../src/jquery.fancytree.js"></script> 127 // <script src="../src/jquery.fancytree.childcounter.js"></script> 128 // ... 129 // 130 // $("#tree").fancytree({ 131 // extensions: ["childcounter"], 132 // childcounter: { 133 // hideExpanded: true 134 // }, 135 // ... 136 // }); 137 // 138 139 /* 'childcounter' extension */ 140 $.ui.fancytree.registerExtension({ 141 // Every extension must be registered by a unique name. 142 name: "childcounter", 143 // Version information should be compliant with [semver](http://semver.org) 144 version: "2.38.3", 145 146 // Extension specific options and their defaults. 147 // This options will be available as `tree.options.childcounter.hideExpanded` 148 149 options: { 150 deep: true, 151 hideZeros: true, 152 hideExpanded: false, 153 }, 154 155 // Attributes other than `options` (or functions) can be defined here, and 156 // will be added to the tree.ext.EXTNAME namespace, in this case `tree.ext.childcounter.foo`. 157 // They can also be accessed as `this._local.foo` from within the extension 158 // methods. 159 foo: 42, 160 161 // Local functions are prefixed with an underscore '_'. 162 // Callable as `this._local._appendCounter()`. 163 164 _appendCounter: function (bar) { 165 var tree = this; 166 }, 167 168 // **Override virtual methods for this extension.** 169 // 170 // Fancytree implements a number of 'hook methods', prefixed by 'node...' or 'tree...'. 171 // with a `ctx` argument (see [EventData](https://wwWendt.de/tech/fancytree/doc/jsdoc/global.html#EventData) 172 // for details) and an extended calling context:<br> 173 // `this` : the Fancytree instance<br> 174 // `this._local`: the namespace that contains extension attributes and private methods (same as this.ext.EXTNAME)<br> 175 // `this._super`: the virtual function that was overridden (member of previous extension or Fancytree) 176 // 177 // See also the [complete list of available hook functions](https://wwWendt.de/tech/fancytree/doc/jsdoc/Fancytree_Hooks.html). 178 179 /* Init */ 180 // `treeInit` is triggered when a tree is initalized. We can set up classes or 181 // bind event handlers here... 182 treeInit: function (ctx) { 183 var tree = this, // same as ctx.tree, 184 opts = ctx.options, 185 extOpts = ctx.options.childcounter; 186 // Optionally check for dependencies with other extensions 187 /* this._requireExtension("glyph", false, false); */ 188 // Call the base implementation 189 this._superApply(arguments); 190 // Add a class to the tree container 191 this.$container.addClass("fancytree-ext-childcounter"); 192 }, 193 194 // Destroy this tree instance (we only call the default implementation, so 195 // this method could as well be omitted). 196 197 treeDestroy: function (ctx) { 198 this._superApply(arguments); 199 }, 200 201 // Overload the `renderTitle` hook, to append a counter badge 202 nodeRenderTitle: function (ctx, title) { 203 var node = ctx.node, 204 extOpts = ctx.options.childcounter, 205 count = 206 node.data.childCounter == null 207 ? node.countChildren(extOpts.deep) 208 : +node.data.childCounter; 209 // Let the base implementation render the title 210 // We use `_super()` instead of `_superApply()` here, since it is a little bit 211 // more performant when called often 212 this._super(ctx, title); 213 // Append a counter badge 214 if ( 215 (count || !extOpts.hideZeros) && 216 (!node.isExpanded() || !extOpts.hideExpanded) 217 ) { 218 $( 219 "span.fancytree-icon,span.fancytree-custom-icon", 220 node.span 221 ).append( 222 $("<span class='fancytree-childcounter'/>").text(count) 223 ); 224 } 225 }, 226 // Overload the `setExpanded` hook, so the counters are updated 227 nodeSetExpanded: function (ctx, flag, callOpts) { 228 var tree = ctx.tree, 229 node = ctx.node; 230 // Let the base implementation expand/collapse the node, then redraw the title 231 // after the animation has finished 232 return this._superApply(arguments).always(function () { 233 tree.nodeRenderTitle(ctx); 234 }); 235 }, 236 237 // End of extension definition 238 }); 239 // Value returned by `require('jquery.fancytree..')` 240 return $.ui.fancytree; 241}); // End of closure 242