vendor: 26,893 bytes, lines 1-778
1/*globals jQuery, define, module, exports, require, window, document, postMessage */ 2(function (factory) { 3 "use strict"; 4 if (typeof define === 'function' && define.amd) { 5 define(['jquery'], factory); 6 } 7 else if(typeof module !== 'undefined' && module.exports) { 8 module.exports = factory(require('jquery')); 9 } 10 else { 11 factory(jQuery); 12 } 13}(function ($, undefined) { 14 "use strict"; 15/*! 16 * jsTree 3.3.4 17 * http://jstree.com/ 18 * 19 * Copyright (c) 2014 Ivan Bozhanov (http://vakata.com) 20 * 21 * Licensed same as jquery - under the terms of the MIT License 22 * http://www.opensource.org/licenses/mit-license.php 23 */ 24/*! 25 * if using jslint please allow for the jQuery global and use following options: 26 * jslint: loopfunc: true, browser: true, ass: true, bitwise: true, continue: true, nomen: true, plusplus: true, regexp: true, unparam: true, todo: true, white: true 27 */ 28/*jshint -W083 */ 29 30 // prevent another load? maybe there is a better way? 31 if($.jstree) { 32 return; 33 } 34 35 /** 36 * ### jsTree core functionality 37 */ 38 39 // internal variables 40 var instance_counter = 0, 41 ccp_node = false, 42 ccp_mode = false, 43 ccp_inst = false, 44 themes_loaded = [], 45 src = $('script:last').attr('src'), 46 document = window.document; // local variable is always faster to access then a global 47 48 /** 49 * holds all jstree related functions and variables, including the actual class and methods to create, access and manipulate instances. 50 * @name $.jstree 51 */ 52 $.jstree = { 53 /** 54 * specifies the jstree version in use 55 * @name $.jstree.version 56 */ 57 version : '3.3.4', 58 /** 59 * holds all the default options used when creating new instances 60 * @name $.jstree.defaults 61 */ 62 defaults : { 63 /** 64 * configure which plugins will be active on an instance. Should be an array of strings, where each element is a plugin name. The default is `[]` 65 * @name $.jstree.defaults.plugins 66 */ 67 plugins : [] 68 }, 69 /** 70 * stores all loaded jstree plugins (used internally) 71 * @name $.jstree.plugins 72 */ 73 plugins : {}, 74 path : src && src.indexOf('/') !== -1 ? src.replace(/\/[^\/]+$/,'') : '', 75 idregex : /[\\:&!^|()\[\]<>@*'+~#";.,=\- \/${}%?`]/g, 76 root : '#' 77 }; 78 79 /** 80 * creates a jstree instance 81 * @name $.jstree.create(el [, options]) 82 * @param {DOMElement|jQuery|String} el the element to create the instance on, can be jQuery extended or a selector 83 * @param {Object} options options for this instance (extends `$.jstree.defaults`) 84 * @return {jsTree} the new instance 85 */ 86 $.jstree.create = function (el, options) { 87 var tmp = new $.jstree.core(++instance_counter), 88 opt = options; 89 options = $.extend(true, {}, $.jstree.defaults, options); 90 if(opt && opt.plugins) { 91 options.plugins = opt.plugins; 92 } 93 $.each(options.plugins, function (i, k) { 94 if(i !== 'core') { 95 tmp = tmp.plugin(k, options[k]); 96 } 97 }); 98 $(el).data('jstree', tmp); 99 tmp.init(el, options); 100 return tmp; 101 }; 102 /** 103 * remove all traces of jstree from the DOM and destroy all instances 104 * @name $.jstree.destroy() 105 */ 106 $.jstree.destroy = function () { 107 $('.jstree:jstree').jstree('destroy'); 108 $(document).off('.jstree'); 109 }; 110 /** 111 * the jstree class constructor, used only internally 112 * @private 113 * @name $.jstree.core(id) 114 * @param {Number} id this instance's index 115 */ 116 $.jstree.core = function (id) { 117 this._id = id; 118 this._cnt = 0; 119 this._wrk = null; 120 this._data = { 121 core : { 122 themes : { 123 name : false, 124 dots : false, 125 icons : false, 126 ellipsis : false 127 }, 128 selected : [], 129 last_error : {}, 130 working : false, 131 worker_queue : [], 132 focused : null 133 } 134 }; 135 }; 136 /** 137 * get a reference to an existing instance 138 * 139 * __Examples__ 140 * 141 * // provided a container with an ID of "tree", and a nested node with an ID of "branch" 142 * // all of there will return the same instance 143 * $.jstree.reference('tree'); 144 * $.jstree.reference('#tree'); 145 * $.jstree.reference($('#tree')); 146 * $.jstree.reference(document.getElementByID('tree')); 147 * $.jstree.reference('branch'); 148 * $.jstree.reference('#branch'); 149 * $.jstree.reference($('#branch')); 150 * $.jstree.reference(document.getElementByID('branch')); 151 * 152 * @name $.jstree.reference(needle) 153 * @param {DOMElement|jQuery|String} needle 154 * @return {jsTree|null} the instance or `null` if not found 155 */ 156 $.jstree.reference = function (needle) { 157 var tmp = null, 158 obj = null; 159 if(needle && needle.id && (!needle.tagName || !needle.nodeType)) { needle = needle.id; } 160 161 if(!obj || !obj.length) { 162 try { obj = $(needle); } catch (ignore) { } 163 } 164 if(!obj || !obj.length) { 165 try { obj = $('#' + needle.replace($.jstree.idregex,'\\$&')); } catch (ignore) { } 166 } 167 if(obj && obj.length && (obj = obj.closest('.jstree')).length && (obj = obj.data('jstree'))) { 168 tmp = obj; 169 } 170 else { 171 $('.jstree').each(function () { 172 var inst = $(this).data('jstree'); 173 if(inst && inst._model.data[needle]) { 174 tmp = inst; 175 return false; 176 } 177 }); 178 } 179 return tmp; 180 }; 181 /** 182 * Create an instance, get an instance or invoke a command on a instance. 183 * 184 * If there is no instance associated with the current node a new one is created and `arg` is used to extend `$.jstree.defaults` for this new instance. There would be no return value (chaining is not broken). 185 * 186 * If there is an existing instance and `arg` is a string the command specified by `arg` is executed on the instance, with any additional arguments passed to the function. If the function returns a value it will be returned (chaining could break depending on function). 187 * 188 * If there is an existing instance and `arg` is not a string the instance itself is returned (similar to `$.jstree.reference`). 189 * 190 * In any other case - nothing is returned and chaining is not broken. 191 * 192 * __Examples__ 193 * 194 * $('#tree1').jstree(); // creates an instance 195 * $('#tree2').jstree({ plugins : [] }); // create an instance with some options 196 * $('#tree1').jstree('open_node', '#branch_1'); // call a method on an existing instance, passing additional arguments 197 * $('#tree2').jstree(); // get an existing instance (or create an instance) 198 * $('#tree2').jstree(true); // get an existing instance (will not create new instance) 199 * $('#branch_1').jstree().select_node('#branch_1'); // get an instance (using a nested element and call a method) 200 * 201 * @name $().jstree([arg]) 202 * @param {String|Object} arg 203 * @return {Mixed} 204 */ 205 $.fn.jstree = function (arg) { 206 // check for string argument 207 var is_method = (typeof arg === 'string'), 208 args = Array.prototype.slice.call(arguments, 1), 209 result = null; 210 if(arg === true && !this.length) { return false; } 211 this.each(function () { 212 // get the instance (if there is one) and method (if it exists) 213 var instance = $.jstree.reference(this), 214 method = is_method && instance ? instance[arg] : null; 215 // if calling a method, and method is available - execute on the instance 216 result = is_method && method ? 217 method.apply(instance, args) : 218 null; 219 // if there is no instance and no method is being called - create one 220 if(!instance && !is_method && (arg === undefined || $.isPlainObject(arg))) { 221 $.jstree.create(this, arg); 222 } 223 // if there is an instance and no method is called - return the instance 224 if( (instance && !is_method) || arg === true ) { 225 result = instance || false; 226 } 227 // if there was a method call which returned a result - break and return the value 228 if(result !== null && result !== undefined) { 229 return false; 230 } 231 }); 232 // if there was a method call with a valid return value - return that, otherwise continue the chain 233 return result !== null && result !== undefined ? 234 result : this; 235 }; 236 /** 237 * used to find elements containing an instance 238 * 239 * __Examples__ 240 * 241 * $('div:jstree').each(function () { 242 * $(this).jstree('destroy'); 243 * }); 244 * 245 * @name $(':jstree') 246 * @return {jQuery} 247 */ 248 $.expr.pseudos.jstree = $.expr.createPseudo(function(search) { 249 return function(a) { 250 return $(a).hasClass('jstree') && 251 $(a).data('jstree') !== undefined; 252 }; 253 }); 254 255 /** 256 * stores all defaults for the core 257 * @name $.jstree.defaults.core 258 */ 259 $.jstree.defaults.core = { 260 /** 261 * data configuration 262 * 263 * If left as `false` the HTML inside the jstree container element is used to populate the tree (that should be an unordered list with list items). 264 * 265 * You can also pass in a HTML string or a JSON array here. 266 * 267 * It is possible to pass in a standard jQuery-like AJAX config and jstree will automatically determine if the response is JSON or HTML and use that to populate the tree. 268 * In addition to the standard jQuery ajax options here you can suppy functions for `data` and `url`, the functions will be run in the current instance's scope and a param will be passed indicating which node is being loaded, the return value of those functions will be used. 269 * 270 * The last option is to specify a function, that function will receive the node being loaded as argument and a second param which is a function which should be called with the result. 271 * 272 * __Examples__ 273 * 274 * // AJAX 275 * $('#tree').jstree({ 276 * 'core' : { 277 * 'data' : { 278 * 'url' : '/get/children/', 279 * 'data' : function (node) { 280 * return { 'id' : node.id }; 281 * } 282 * } 283 * }); 284 * 285 * // direct data 286 * $('#tree').jstree({ 287 * 'core' : { 288 * 'data' : [ 289 * 'Simple root node', 290 * { 291 * 'id' : 'node_2', 292 * 'text' : 'Root node with options', 293 * 'state' : { 'opened' : true, 'selected' : true }, 294 * 'children' : [ { 'text' : 'Child 1' }, 'Child 2'] 295 * } 296 * ] 297 * } 298 * }); 299 * 300 * // function 301 * $('#tree').jstree({ 302 * 'core' : { 303 * 'data' : function (obj, callback) { 304 * callback.call(this, ['Root 1', 'Root 2']); 305 * } 306 * }); 307 * 308 * @name $.jstree.defaults.core.data 309 */ 310 data : false, 311 /** 312 * configure the various strings used throughout the tree 313 * 314 * You can use an object where the key is the string you need to replace and the value is your replacement. 315 * Another option is to specify a function which will be called with an argument of the needed string and should return the replacement. 316 * If left as `false` no replacement is made. 317 * 318 * __Examples__ 319 * 320 * $('#tree').jstree({ 321 * 'core' : { 322 * 'strings' : { 323 * 'Loading ...' : 'Please wait ...' 324 * } 325 * } 326 * }); 327 * 328 * @name $.jstree.defaults.core.strings 329 */ 330 strings : false, 331 /** 332 * determines what happens when a user tries to modify the structure of the tree 333 * If left as `false` all operations like create, rename, delete, move or copy are prevented. 334 * You can set this to `true` to allow all interactions or use a function to have better control. 335 * 336 * __Examples__ 337 * 338 * $('#tree').jstree({ 339 * 'core' : { 340 * 'check_callback' : function (operation, node, node_parent, node_position, more) { 341 * // operation can be 'create_node', 'rename_node', 'delete_node', 'move_node', 'copy_node' or 'edit' 342 * // in case of 'rename_node' node_position is filled with the new node name 343 * return operation === 'rename_node' ? true : false; 344 * } 345 * } 346 * }); 347 * 348 * @name $.jstree.defaults.core.check_callback 349 */ 350 check_callback : false, 351 /** 352 * a callback called with a single object parameter in the instance's scope when something goes wrong (operation prevented, ajax failed, etc) 353 * @name $.jstree.defaults.core.error 354 */ 355 error : $.noop, 356 /** 357 * the open / close animation duration in milliseconds - set this to `false` to disable the animation (default is `200`) 358 * @name $.jstree.defaults.core.animation 359 */ 360 animation : 200, 361 /** 362 * a boolean indicating if multiple nodes can be selected 363 * @name $.jstree.defaults.core.multiple 364 */ 365 multiple : true, 366 /** 367 * theme configuration object 368 * @name $.jstree.defaults.core.themes 369 */ 370 themes : { 371 /** 372 * the name of the theme to use (if left as `false` the default theme is used) 373 * @name $.jstree.defaults.core.themes.name 374 */ 375 name : false, 376 /** 377 * the URL of the theme's CSS file, leave this as `false` if you have manually included the theme CSS (recommended). You can set this to `true` too which will try to autoload the theme. 378 * @name $.jstree.defaults.core.themes.url 379 */ 380 url : false, 381 /** 382 * the location of all jstree themes - only used if `url` is set to `true` 383 * @name $.jstree.defaults.core.themes.dir 384 */ 385 dir : false, 386 /** 387 * a boolean indicating if connecting dots are shown 388 * @name $.jstree.defaults.core.themes.dots 389 */ 390 dots : true, 391 /** 392 * a boolean indicating if node icons are shown 393 * @name $.jstree.defaults.core.themes.icons 394 */ 395 icons : true, 396 /** 397 * a boolean indicating if node ellipsis should be shown - this only works with a fixed with on the container 398 * @name $.jstree.defaults.core.themes.ellipsis 399 */ 400 ellipsis : false, 401 /** 402 * a boolean indicating if the tree background is striped 403 * @name $.jstree.defaults.core.themes.stripes 404 */ 405 stripes : false, 406 /** 407 * a string (or boolean `false`) specifying the theme variant to use (if the theme supports variants) 408 * @name $.jstree.defaults.core.themes.variant 409 */ 410 variant : false, 411 /** 412 * a boolean specifying if a reponsive version of the theme should kick in on smaller screens (if the theme supports it). Defaults to `false`. 413 * @name $.jstree.defaults.core.themes.responsive 414 */ 415 responsive : false 416 }, 417 /** 418 * if left as `true` all parents of all selected nodes will be opened once the tree loads (so that all selected nodes are visible to the user) 419 * @name $.jstree.defaults.core.expand_selected_onload 420 */ 421 expand_selected_onload : true, 422 /** 423 * if left as `true` web workers will be used to parse incoming JSON data where possible, so that the UI will not be blocked by large requests. Workers are however about 30% slower. Defaults to `true` 424 * @name $.jstree.defaults.core.worker 425 */ 426 worker : true, 427 /** 428 * Force node text to plain text (and escape HTML). Defaults to `false` 429 * @name $.jstree.defaults.core.force_text 430 */ 431 force_text : false, 432 /** 433 * Should the node should be toggled if the text is double clicked . Defaults to `true` 434 * @name $.jstree.defaults.core.dblclick_toggle 435 */ 436 dblclick_toggle : true 437 }; 438 $.jstree.core.prototype = { 439 /** 440 * used to decorate an instance with a plugin. Used internally. 441 * @private 442 * @name plugin(deco [, opts]) 443 * @param {String} deco the plugin to decorate with 444 * @param {Object} opts options for the plugin 445 * @return {jsTree} 446 */ 447 plugin : function (deco, opts) { 448 var Child = $.jstree.plugins[deco]; 449 if(Child) { 450 this._data[deco] = {}; 451 Child.prototype = this; 452 return new Child(opts, this); 453 } 454 return this; 455 }, 456 /** 457 * initialize the instance. Used internally. 458 * @private 459 * @name init(el, optons) 460 * @param {DOMElement|jQuery|String} el the element we are transforming 461 * @param {Object} options options for this instance 462 * @trigger init.jstree, loading.jstree, loaded.jstree, ready.jstree, changed.jstree 463 */ 464 init : function (el, options) { 465 this._model = { 466 data : {}, 467 changed : [], 468 force_full_redraw : false, 469 redraw_timeout : false, 470 default_state : { 471 loaded : true, 472 opened : false, 473 selected : false, 474 disabled : false 475 } 476 }; 477 this._model.data[$.jstree.root] = { 478 id : $.jstree.root, 479 parent : null, 480 parents : [], 481 children : [], 482 children_d : [], 483 state : { loaded : false } 484 }; 485 486 this.element = $(el).addClass('jstree jstree-' + this._id); 487 this.settings = options; 488 489 this._data.core.ready = false; 490 this._data.core.loaded = false; 491 this._data.core.rtl = (this.element.css("direction") === "rtl"); 492 this.element[this._data.core.rtl ? 'addClass' : 'removeClass']("jstree-rtl"); 493 this.element.attr('role','tree'); 494 if(this.settings.core.multiple) { 495 this.element.attr('aria-multiselectable', true); 496 } 497 if(!this.element.attr('tabindex')) { 498 this.element.attr('tabindex','0'); 499 } 500 501 this.bind(); 502 /** 503 * triggered after all events are bound 504 * @event 505 * @name init.jstree 506 */ 507 this.trigger("init"); 508 509 this._data.core.original_container_html = this.element.find(" > ul > li").clone(true); 510 this._data.core.original_container_html 511 .find("li").addBack() 512 .contents().filter(function() { 513 return this.nodeType === 3 && (!this.nodeValue || /^\s+$/.test(this.nodeValue)); 514 }) 515 .remove(); 516 this.element.html("<"+"ul class='jstree-container-ul jstree-children' role='group'><"+"li id='j"+this._id+"_loading' class='jstree-initial-node jstree-loading jstree-leaf jstree-last' role='tree-item'><i class='jstree-icon jstree-ocl'></i><"+"a class='jstree-anchor' href='#'><i class='jstree-icon jstree-themeicon-hidden'></i>" + this.get_string("Loading ...") + "</a></li></ul>"); 517 this.element.attr('aria-activedescendant','j' + this._id + '_loading'); 518 this._data.core.li_height = this.get_container_ul().children("li").first().outerHeight() || 24; 519 this._data.core.node = this._create_prototype_node(); 520 /** 521 * triggered after the loading text is shown and before loading starts 522 * @event 523 * @name loading.jstree 524 */ 525 this.trigger("loading"); 526 this.load_node($.jstree.root); 527 }, 528 /** 529 * destroy an instance 530 * @name destroy() 531 * @param {Boolean} keep_html if not set to `true` the container will be emptied, otherwise the current DOM elements will be kept intact 532 */ 533 destroy : function (keep_html) { 534 /** 535 * triggered before the tree is destroyed 536 * @event 537 * @name destroy.jstree 538 */ 539 this.trigger("destroy"); 540 if(this._wrk) { 541 try { 542 window.URL.revokeObjectURL(this._wrk); 543 this._wrk = null; 544 } 545 catch (ignore) { } 546 } 547 if(!keep_html) { this.element.empty(); } 548 this.teardown(); 549 }, 550 /** 551 * Create prototype node 552 */ 553 _create_prototype_node : function () { 554 var _node = document.createElement('LI'), _temp1, _temp2; 555 _node.setAttribute('role', 'treeitem'); 556 _temp1 = document.createElement('I'); 557 _temp1.className = 'jstree-icon jstree-ocl'; 558 _temp1.setAttribute('role', 'presentation'); 559 _node.appendChild(_temp1); 560 _temp1 = document.createElement('A'); 561 _temp1.className = 'jstree-anchor'; 562 _temp1.setAttribute('href','#'); 563 _temp1.setAttribute('tabindex','-1'); 564 _temp2 = document.createElement('I'); 565 _temp2.className = 'jstree-icon jstree-themeicon'; 566 _temp2.setAttribute('role', 'presentation'); 567 _temp1.appendChild(_temp2); 568 _node.appendChild(_temp1); 569 _temp1 = _temp2 = null; 570 571 return _node; 572 }, 573 /** 574 * part of the destroying of an instance. Used internally. 575 * @private 576 * @name teardown() 577 */ 578 teardown : function () { 579 this.unbind(); 580 this.element 581 .removeClass('jstree') 582 .removeData('jstree') 583 .find("[class^='jstree']") 584 .addBack() 585 .attr("class", function () { return this.className.replace(/jstree[^ ]*|$/ig,''); }); 586 this.element = null; 587 }, 588 /** 589 * bind all events. Used internally. 590 * @private 591 * @name bind() 592 */ 593 bind : function () { 594 var word = '', 595 tout = null, 596 was_click = 0; 597 this.element 598 .on("dblclick.jstree", function (e) { 599 if(e.target.tagName && e.target.tagName.toLowerCase() === "input") { return true; } 600 if(document.selection && document.selection.empty) { 601 document.selection.empty(); 602 } 603 else { 604 if(window.getSelection) { 605 var sel = window.getSelection(); 606 try { 607 sel.removeAllRanges(); 608 sel.collapse(); 609 } catch (ignore) { } 610 } 611 } 612 }) 613 .on("mousedown.jstree", $.proxy(function (e) { 614 if(e.target === this.element[0]) { 615 e.preventDefault(); // prevent losing focus when clicking scroll arrows (FF, Chrome) 616 was_click = +(new Date()); // ie does not allow to prevent losing focus 617 } 618 }, this)) 619 .on("mousedown.jstree", ".jstree-ocl", function (e) { 620 e.preventDefault(); // prevent any node inside from losing focus when clicking the open/close icon 621 }) 622 .on("click.jstree", ".jstree-ocl", $.proxy(function (e) { 623 this.toggle_node(e.target); 624 }, this)) 625 .on("dblclick.jstree", ".jstree-anchor", $.proxy(function (e) { 626 if(e.target.tagName && e.target.tagName.toLowerCase() === "input") { return true; } 627 if(this.settings.core.dblclick_toggle) { 628 this.toggle_node(e.target); 629 } 630 }, this)) 631 .on("click.jstree", ".jstree-anchor", $.proxy(function (e) { 632 e.preventDefault(); 633 if(e.currentTarget !== document.activeElement) { $(e.currentTarget).focus(); } 634 this.activate_node(e.currentTarget, e); 635 }, this)) 636 .on('keydown.jstree', '.jstree-anchor', $.proxy(function (e) { 637 if(e.target.tagName && e.target.tagName.toLowerCase() === "input") { return true; } 638 if(e.which !== 32 && e.which !== 13 && (e.shiftKey || e.ctrlKey || e.altKey || e.metaKey)) { return true; } 639 var o = null; 640 if(this._data.core.rtl) { 641 if(e.which === 37) { e.which = 39; } 642 else if(e.which === 39) { e.which = 37; } 643 } 644 switch(e.which) { 645 case 32: // aria defines space only with Ctrl 646 if(e.ctrlKey) { 647 e.type = "click"; 648 $(e.currentTarget).trigger(e); 649 } 650 break; 651 case 13: // enter 652 e.type = "click"; 653 $(e.currentTarget).trigger(e); 654 break; 655 case 37: // left 656 e.preventDefault(); 657 if(this.is_open(e.currentTarget)) { 658 this.close_node(e.currentTarget); 659 } 660 else { 661 o = this.get_parent(e.currentTarget); 662 if(o && o.id !== $.jstree.root) { this.get_node(o, true).children('.jstree-anchor').focus(); } 663 } 664 break; 665 case 38: // up 666 e.preventDefault(); 667 o = this.get_prev_dom(e.currentTarget); 668 if(o && o.length) { o.children('.jstree-anchor').focus(); } 669 break; 670 case 39: // right 671 e.preventDefault(); 672 if(this.is_closed(e.currentTarget)) { 673 this.open_node(e.currentTarget, function (o) { this.get_node(o, true).children('.jstree-anchor').focus(); }); 674 } 675 else if (this.is_open(e.currentTarget)) { 676 o = this.get_node(e.currentTarget, true).children('.jstree-children')[0]; 677 if(o) { $(this._firstChild(o)).children('.jstree-anchor').focus(); } 678 } 679 break; 680 case 40: // down 681 e.preventDefault(); 682 o = this.get_next_dom(e.currentTarget); 683 if(o && o.length) { o.children('.jstree-anchor').focus(); } 684 break; 685 case 106: // aria defines * on numpad as open_all - not very common 686 this.open_all(); 687 break; 688 case 36: // home 689 e.preventDefault(); 690 o = this._firstChild(this.get_container_ul()[0]); 691 if(o) { $(o).children('.jstree-anchor').filter(':visible').focus(); } 692 break; 693 case 35: // end 694 e.preventDefault(); 695 this.element.find('.jstree-anchor').filter(':visible').last().focus(); 696 break; 697 case 113: // f2 - safe to include - if check_callback is false it will fail 698 e.preventDefault(); 699 this.edit(e.currentTarget); 700 break; 701 default: 702 break; 703 /*! 704 // delete 705 case 46: 706 e.preventDefault(); 707 o = this.get_node(e.currentTarget); 708 if(o && o.id && o.id !== $.jstree.root) { 709 o = this.is_selected(o) ? this.get_selected() : o; 710 this.delete_node(o); 711 } 712 break; 713 714 */ 715 } 716 }, this)) 717 .on("load_node.jstree", $.proxy(function (e, data) { 718 if(data.status) { 719 if(data.node.id === $.jstree.root && !this._data.core.loaded) { 720 this._data.core.loaded = true; 721 if(this._firstChild(this.get_container_ul()[0])) { 722 this.element.attr('aria-activedescendant',this._firstChild(this.get_container_ul()[0]).id); 723 } 724 /** 725 * triggered after the root node is loaded for the first time 726 * @event 727 * @name loaded.jstree 728 */ 729 this.trigger("loaded"); 730 } 731 if(!this._data.core.ready) { 732 setTimeout($.proxy(function() { 733 if(this.element && !this.get_container_ul().find('.jstree-loading').length) { 734 this._data.core.ready = true; 735 if(this._data.core.selected.length) { 736 if(this.settings.core.expand_selected_onload) { 737 var tmp = [], i, j; 738 for(i = 0, j = this._data.core.selected.length; i < j; i++) { 739 tmp = tmp.concat(this._model.data[this._data.core.selected[i]].parents); 740 } 741 tmp = $.vakata.array_unique(tmp); 742 for(i = 0, j = tmp.length; i < j; i++) { 743 this.open_node(tmp[i], false, 0); 744 } 745 } 746 this.trigger('changed', { 'action' : 'ready', 'selected' : this._data.core.selected }); 747 } 748 /** 749 * triggered after all nodes are finished loading 750 * @event 751 * @name ready.jstree 752 */ 753 this.trigger("ready"); 754 } 755 }, this), 0); 756 } 757 } 758 }, this)) 759 // quick searching when the tree is focused 760 .on('keypress.jstree', $.proxy(function (e) { 761 if(e.target.tagName && e.target.tagName.toLowerCase() === "input") { return true; } 762 if(tout) { clearTimeout(tout); } 763 tout = setTimeout(function () { 764 word = ''; 765 }, 500); 766 767 var chr = String.fromCharCode(e.which).toLowerCase(), 768 col = this.element.find('.jstree-anchor').filter(':visible'), 769 ind = col.index(document.activeElement) || 0, 770 end = false; 771 word += chr; 772 773 // match for whole word from current node down (including the current node) 774 if(word.length > 1) { 775 col.slice(ind).each($.proxy(function (i, v) { 776 if($(v).text().toLowerCase().indexOf(word) === 0) { 777 $(v).focus(); 778 end = true;
778 779 return false; 780 } 781 }, this)); 782 if(end) { return; } 783 784 // match for whole word from the beginning of the tree 785 col.slice(0, ind).each($.proxy(function (i, v) { 786 if($(v).text().toLowerCase().indexOf(word) === 0) { 787 $(v).focus(); 788 end = true; 789 return false; 790 } 791 }, this)); 792 if(end) { return; } 793 } 794 // list nodes that start with that letter (only if word consists of a single char) 795 if(new RegExp('^' + chr.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&') + '+$').test(word)) { 796 // search for the next node starting with that letter 797 col.slice(ind + 1).each($.proxy(function (i, v) { 798 if($(v).text().toLowerCase().charAt(0) === chr) { 799 $(v).focus(); 800 end = true; 801 return false; 802 } 803 }, this)); 804 if(end) { return; } 805 806 // search from the beginning 807 col.slice(0, ind + 1).each($.proxy(function (i, v) { 808 if($(v).text().toLowerCase().charAt(0) === chr) { 809 $(v).focus(); 810 end = true; 811 return false; 812 } 813 }, this)); 814 if(end) { return; } 815 } 816 }, this)) 817 // THEME RELATED 818 .on("init.jstree", $.proxy(function () { 819 var s = this.settings.core.themes; 820 this._data.core.themes.dots = s.dots; 821 this._data.core.themes.stripes = s.stripes; 822 this._data.core.themes.icons = s.icons; 823 this._data.core.themes.ellipsis = s.ellipsis; 824 this.set_theme(s.name || "default", s.url); 825 this.set_theme_variant(s.variant); 826 }, this)) 827 .on("loading.jstree", $.proxy(function () { 828 this[ this._data.core.themes.dots ? "show_dots" : "hide_dots" ](); 829 this[ this._data.core.themes.icons ? "show_icons" : "hide_icons" ](); 830 this[ this._data.core.themes.stripes ? "show_stripes" : "hide_stripes" ](); 831 this[ this._data.core.themes.ellipsis ? "show_ellipsis" : "hide_ellipsis" ](); 832 }, this)) 833 .on('blur.jstree', '.jstree-anchor', $.proxy(function (e) { 834 this._data.core.focused = null; 835 $(e.currentTarget).filter('.jstree-hovered').mouseleave(); 836 this.element.attr('tabindex', '0'); 837 }, this)) 838 .on('focus.jstree', '.jstree-anchor', $.proxy(function (e) { 839 var tmp = this.get_node(e.currentTarget); 840 if(tmp && tmp.id) { 841 this._data.core.focused = tmp.id; 842 } 843 this.element.find('.jstree-hovered').not(e.currentTarget).mouseleave(); 844 $(e.currentTarget).mouseenter(); 845 this.element.attr('tabindex', '-1'); 846 }, this)) 847 .on('focus.jstree', $.proxy(function () { 848 if(+(new Date()) - was_click > 500 && !this._data.core.focused) { 849 was_click = 0; 850 var act = this.get_node(this.element.attr('aria-activedescendant'), true); 851 if(act) { 852 act.find('> .jstree-anchor').focus(); 853 } 854 } 855 }, this)) 856 .on('mouseenter.jstree', '.jstree-anchor', $.proxy(function (e) { 857 this.hover_node(e.currentTarget); 858 }, this)) 859 .on('mouseleave.jstree', '.jstree-anchor', $.proxy(function (e) { 860 this.dehover_node(e.currentTarget); 861 }, this)); 862 }, 863 /** 864 * part of the destroying of an instance. Used internally. 865 * @private 866 * @name unbind() 867 */ 868 unbind : function () { 869 this.element.off('.jstree'); 870 $(document).off('.jstree-' + this._id); 871 }, 872 /** 873 * trigger an event. Used internally. 874 * @private 875 * @name trigger(ev [, data]) 876 * @param {String} ev the name of the event to trigger 877 * @param {Object} data additional data to pass with the event 878 */ 879 trigger : function (ev, data) { 880 if(!data) { 881 data = {}; 882 } 883 data.instance = this; 884 this.element.triggerHandler(ev.replace('.jstree','') + '.jstree', data); 885 }, 886 /** 887 * returns the jQuery extended instance container 888 * @name get_container() 889 * @return {jQuery} 890 */ 891 get_container : function () { 892 return this.element; 893 }, 894 /** 895 * returns the jQuery extended main UL node inside the instance container. Used internally. 896 * @private 897 * @name get_container_ul() 898 * @return {jQuery} 899 */ 900 get_container_ul : function () { 901 return this.element.children(".jstree-children").first(); 902 }, 903 /** 904 * gets string replacements (localization). Used internally. 905 * @private 906 * @name get_string(key) 907 * @param {String} key 908 * @return {String} 909 */ 910 get_string : function (key) { 911 var a = this.settings.core.strings; 912 if($.isFunction(a)) { return a.call(this, key); } 913 if(a && a[key]) { return a[key]; } 914 return key; 915 }, 916 /** 917 * gets the first child of a DOM node. Used internally. 918 * @private 919 * @name _firstChild(dom) 920 * @param {DOMElement} dom 921 * @return {DOMElement} 922 */ 923 _firstChild : function (dom) { 924 dom = dom ? dom.firstChild : null; 925 while(dom !== null && dom.nodeType !== 1) { 926 dom = dom.nextSibling; 927 } 928 return dom; 929 }, 930 /** 931 * gets the next sibling of a DOM node. Used internally. 932 * @private 933 * @name _nextSibling(dom) 934 * @param {DOMElement} dom 935 * @return {DOMElement} 936 */ 937 _nextSibling : function (dom) { 938 dom = dom ? dom.nextSibling : null; 939 while(dom !== null && dom.nodeType !== 1) { 940 dom = dom.nextSibling; 941 } 942 return dom; 943 }, 944 /** 945 * gets the previous sibling of a DOM node. Used internally. 946 * @private 947 * @name _previousSibling(dom) 948 * @param {DOMElement} dom 949 * @return {DOMElement} 950 */ 951 _previousSibling : function (dom) { 952 dom = dom ? dom.previousSibling : null; 953 while(dom !== null && dom.nodeType !== 1) { 954 dom = dom.previousSibling; 955 } 956 return dom; 957 }, 958 /** 959 * get the JSON representation of a node (or the actual jQuery extended DOM node) by using any input (child DOM element, ID string, selector, etc) 960 * @name get_node(obj [, as_dom]) 961 * @param {mixed} obj 962 * @param {Boolean} as_dom 963 * @return {Object|jQuery} 964 */ 965 get_node : function (obj, as_dom) { 966 if(obj && obj.id) { 967 obj = obj.id; 968 } 969 var dom; 970 try { 971 if(this._model.data[obj]) { 972 obj = this._model.data[obj]; 973 } 974 else if(typeof obj === "string" && this._model.data[obj.replace(/^#/, '')]) { 975 obj = this._model.data[obj.replace(/^#/, '')]; 976 } 977 else if(typeof obj === "string" && (dom = $('#' + obj.replace($.jstree.idregex,'\\$&'), this.element)).length && this._model.data[dom.closest('.jstree-node').attr('id')]) { 978 obj = this._model.data[dom.closest('.jstree-node').attr('id')]; 979 } 980 else if((dom = $(obj, this.element)).length && this._model.data[dom.closest('.jstree-node').attr('id')]) { 981 obj = this._model.data[dom.closest('.jstree-node').attr('id')]; 982 } 983 else if((dom = $(obj, this.element)).length && dom.hasClass('jstree')) { 984 obj = this._model.data[$.jstree.root]; 985 } 986 else { 987 return false; 988 } 989 990 if(as_dom) { 991 obj = obj.id === $.jstree.root ? this.element : $('#' + obj.id.replace($.jstree.idregex,'\\$&'), this.element); 992 } 993 return obj;
vendor: 11,152 bytes, lines 994-1340
994 } catch (ex) { return false; } 995 }, 996 /** 997 * get the path to a node, either consisting of node texts, or of node IDs, optionally glued together (otherwise an array) 998 * @name get_path(obj [, glue, ids]) 999 * @param {mixed} obj the node 1000 * @param {String} glue if you want the path as a string - pass the glue here (for example '/'), if a falsy value is supplied here, an array is returned 1001 * @param {Boolean} ids if set to true build the path using ID, otherwise node text is used 1002 * @return {mixed} 1003 */ 1004 get_path : function (obj, glue, ids) { 1005 obj = obj.parents ? obj : this.get_node(obj); 1006 if(!obj || obj.id === $.jstree.root || !obj.parents) { 1007 return false; 1008 } 1009 var i, j, p = []; 1010 p.push(ids ? obj.id : obj.text); 1011 for(i = 0, j = obj.parents.length; i < j; i++) { 1012 p.push(ids ? obj.parents[i] : this.get_text(obj.parents[i])); 1013 } 1014 p = p.reverse().slice(1); 1015 return glue ? p.join(glue) : p; 1016 }, 1017 /** 1018 * get the next visible node that is below the `obj` node. If `strict` is set to `true` only sibling nodes are returned. 1019 * @name get_next_dom(obj [, strict]) 1020 * @param {mixed} obj 1021 * @param {Boolean} strict 1022 * @return {jQuery} 1023 */ 1024 get_next_dom : function (obj, strict) { 1025 var tmp; 1026 obj = this.get_node(obj, true); 1027 if(obj[0] === this.element[0]) { 1028 tmp = this._firstChild(this.get_container_ul()[0]); 1029 while (tmp && tmp.offsetHeight === 0) { 1030 tmp = this._nextSibling(tmp); 1031 } 1032 return tmp ? $(tmp) : false; 1033 } 1034 if(!obj || !obj.length) { 1035 return false; 1036 } 1037 if(strict) { 1038 tmp = obj[0]; 1039 do { 1040 tmp = this._nextSibling(tmp); 1041 } while (tmp && tmp.offsetHeight === 0); 1042 return tmp ? $(tmp) : false; 1043 } 1044 if(obj.hasClass("jstree-open")) { 1045 tmp = this._firstChild(obj.children('.jstree-children')[0]); 1046 while (tmp && tmp.offsetHeight === 0) { 1047 tmp = this._nextSibling(tmp); 1048 } 1049 if(tmp !== null) { 1050 return $(tmp); 1051 } 1052 } 1053 tmp = obj[0]; 1054 do { 1055 tmp = this._nextSibling(tmp); 1056 } while (tmp && tmp.offsetHeight === 0); 1057 if(tmp !== null) { 1058 return $(tmp); 1059 } 1060 return obj.parentsUntil(".jstree",".jstree-node").nextAll(".jstree-node:visible").first(); 1061 }, 1062 /** 1063 * get the previous visible node that is above the `obj` node. If `strict` is set to `true` only sibling nodes are returned. 1064 * @name get_prev_dom(obj [, strict]) 1065 * @param {mixed} obj 1066 * @param {Boolean} strict 1067 * @return {jQuery} 1068 */ 1069 get_prev_dom : function (obj, strict) { 1070 var tmp; 1071 obj = this.get_node(obj, true); 1072 if(obj[0] === this.element[0]) { 1073 tmp = this.get_container_ul()[0].lastChild; 1074 while (tmp && tmp.offsetHeight === 0) { 1075 tmp = this._previousSibling(tmp); 1076 } 1077 return tmp ? $(tmp) : false; 1078 } 1079 if(!obj || !obj.length) { 1080 return false; 1081 } 1082 if(strict) { 1083 tmp = obj[0]; 1084 do { 1085 tmp = this._previousSibling(tmp); 1086 } while (tmp && tmp.offsetHeight === 0); 1087 return tmp ? $(tmp) : false; 1088 } 1089 tmp = obj[0]; 1090 do { 1091 tmp = this._previousSibling(tmp); 1092 } while (tmp && tmp.offsetHeight === 0); 1093 if(tmp !== null) { 1094 obj = $(tmp); 1095 while(obj.hasClass("jstree-open")) { 1096 obj = obj.children(".jstree-children").first().children(".jstree-node:visible:last"); 1097 } 1098 return obj; 1099 } 1100 tmp = obj[0].parentNode.parentNode; 1101 return tmp && tmp.className && tmp.className.indexOf('jstree-node') !== -1 ? $(tmp) : false; 1102 }, 1103 /** 1104 * get the parent ID of a node 1105 * @name get_parent(obj) 1106 * @param {mixed} obj 1107 * @return {String} 1108 */ 1109 get_parent : function (obj) { 1110 obj = this.get_node(obj); 1111 if(!obj || obj.id === $.jstree.root) { 1112 return false; 1113 } 1114 return obj.parent; 1115 }, 1116 /** 1117 * get a jQuery collection of all the children of a node (node must be rendered) 1118 * @name get_children_dom(obj) 1119 * @param {mixed} obj 1120 * @return {jQuery} 1121 */ 1122 get_children_dom : function (obj) { 1123 obj = this.get_node(obj, true); 1124 if(obj[0] === this.element[0]) { 1125 return this.get_container_ul().children(".jstree-node"); 1126 } 1127 if(!obj || !obj.length) { 1128 return false; 1129 } 1130 return obj.children(".jstree-children").children(".jstree-node"); 1131 }, 1132 /** 1133 * checks if a node has children 1134 * @name is_parent(obj) 1135 * @param {mixed} obj 1136 * @return {Boolean} 1137 */ 1138 is_parent : function (obj) { 1139 obj = this.get_node(obj); 1140 return obj && (obj.state.loaded === false || obj.children.length > 0); 1141 }, 1142 /** 1143 * checks if a node is loaded (its children are available) 1144 * @name is_loaded(obj) 1145 * @param {mixed} obj 1146 * @return {Boolean} 1147 */ 1148 is_loaded : function (obj) { 1149 obj = this.get_node(obj); 1150 return obj && obj.state.loaded; 1151 }, 1152 /** 1153 * check if a node is currently loading (fetching children) 1154 * @name is_loading(obj) 1155 * @param {mixed} obj 1156 * @return {Boolean} 1157 */ 1158 is_loading : function (obj) { 1159 obj = this.get_node(obj); 1160 return obj && obj.state && obj.state.loading; 1161 }, 1162 /** 1163 * check if a node is opened 1164 * @name is_open(obj) 1165 * @param {mixed} obj 1166 * @return {Boolean} 1167 */ 1168 is_open : function (obj) { 1169 obj = this.get_node(obj); 1170 return obj && obj.state.opened; 1171 }, 1172 /** 1173 * check if a node is in a closed state 1174 * @name is_closed(obj) 1175 * @param {mixed} obj 1176 * @return {Boolean} 1177 */ 1178 is_closed : function (obj) { 1179 obj = this.get_node(obj); 1180 return obj && this.is_parent(obj) && !obj.state.opened; 1181 }, 1182 /** 1183 * check if a node has no children 1184 * @name is_leaf(obj) 1185 * @param {mixed} obj 1186 * @return {Boolean} 1187 */ 1188 is_leaf : function (obj) { 1189 return !this.is_parent(obj); 1190 }, 1191 /** 1192 * loads a node (fetches its children using the `core.data` setting). Multiple nodes can be passed to by using an array. 1193 * @name load_node(obj [, callback]) 1194 * @param {mixed} obj 1195 * @param {function} callback a function to be executed once loading is complete, the function is executed in the instance's scope and receives two arguments - the node and a boolean status 1196 * @return {Boolean} 1197 * @trigger load_node.jstree 1198 */ 1199 load_node : function (obj, callback) { 1200 var k, l, i, j, c; 1201 if($.isArray(obj)) { 1202 this._load_nodes(obj.slice(), callback); 1203 return true; 1204 } 1205 obj = this.get_node(obj); 1206 if(!obj) { 1207 if(callback) { callback.call(this, obj, false); } 1208 return false; 1209 } 1210 // if(obj.state.loading) { } // the node is already loading - just wait for it to load and invoke callback? but if called implicitly it should be loaded again? 1211 if(obj.state.loaded) { 1212 obj.state.loaded = false; 1213 for(i = 0, j = obj.parents.length; i < j; i++) { 1214 this._model.data[obj.parents[i]].children_d = $.vakata.array_filter(this._model.data[obj.parents[i]].children_d, function (v) { 1215 return $.inArray(v, obj.children_d) === -1; 1216 }); 1217 } 1218 for(k = 0, l = obj.children_d.length; k < l; k++) { 1219 if(this._model.data[obj.children_d[k]].state.selected) { 1220 c = true; 1221 } 1222 delete this._model.data[obj.children_d[k]]; 1223 } 1224 if (c) { 1225 this._data.core.selected = $.vakata.array_filter(this._data.core.selected, function (v) { 1226 return $.inArray(v, obj.children_d) === -1; 1227 }); 1228 } 1229 obj.children = []; 1230 obj.children_d = []; 1231 if(c) { 1232 this.trigger('changed', { 'action' : 'load_node', 'node' : obj, 'selected' : this._data.core.selected }); 1233 } 1234 } 1235 obj.state.failed = false; 1236 obj.state.loading = true; 1237 this.get_node(obj, true).addClass("jstree-loading").attr('aria-busy',true); 1238 this._load_node(obj, $.proxy(function (status) { 1239 obj = this._model.data[obj.id]; 1240 obj.state.loading = false; 1241 obj.state.loaded = status; 1242 obj.state.failed = !obj.state.loaded; 1243 var dom = this.get_node(obj, true), i = 0, j = 0, m = this._model.data, has_children = false; 1244 for(i = 0, j = obj.children.length; i < j; i++) { 1245 if(m[obj.children[i]] && !m[obj.children[i]].state.hidden) { 1246 has_children = true; 1247 break; 1248 } 1249 } 1250 if(obj.state.loaded && dom && dom.length) { 1251 dom.removeClass('jstree-closed jstree-open jstree-leaf'); 1252 if (!has_children) { 1253 dom.addClass('jstree-leaf'); 1254 } 1255 else { 1256 if (obj.id !== '#') { 1257 dom.addClass(obj.state.opened ? 'jstree-open' : 'jstree-closed'); 1258 } 1259 } 1260 } 1261 dom.removeClass("jstree-loading").attr('aria-busy',false); 1262 /** 1263 * triggered after a node is loaded 1264 * @event 1265 * @name load_node.jstree 1266 * @param {Object} node the node that was loading 1267 * @param {Boolean} status was the node loaded successfully 1268 */ 1269 this.trigger('load_node', { "node" : obj, "status" : status }); 1270 if(callback) { 1271 callback.call(this, obj, status); 1272 } 1273 }, this)); 1274 return true; 1275 }, 1276 /** 1277 * load an array of nodes (will also load unavailable nodes as soon as the appear in the structure). Used internally. 1278 * @private 1279 * @name _load_nodes(nodes [, callback]) 1280 * @param {array} nodes 1281 * @param {function} callback a function to be executed once loading is complete, the function is executed in the instance's scope and receives one argument - the array passed to _load_nodes 1282 */ 1283 _load_nodes : function (nodes, callback, is_callback, force_reload) { 1284 var r = true, 1285 c = function () { this._load_nodes(nodes, callback, true); }, 1286 m = this._model.data, i, j, tmp = []; 1287 for(i = 0, j = nodes.length; i < j; i++) { 1288 if(m[nodes[i]] && ( (!m[nodes[i]].state.loaded && !m[nodes[i]].state.failed) || (!is_callback && force_reload) )) { 1289 if(!this.is_loading(nodes[i])) { 1290 this.load_node(nodes[i], c); 1291 } 1292 r = false; 1293 } 1294 } 1295 if(r) { 1296 for(i = 0, j = nodes.length; i < j; i++) { 1297 if(m[nodes[i]] && m[nodes[i]].state.loaded) { 1298 tmp.push(nodes[i]); 1299 } 1300 } 1301 if(callback && !callback.done) { 1302 callback.call(this, tmp); 1303 callback.done = true; 1304 } 1305 } 1306 }, 1307 /** 1308 * loads all unloaded nodes 1309 * @name load_all([obj, callback]) 1310 * @param {mixed} obj the node to load recursively, omit to load all nodes in the tree 1311 * @param {function} callback a function to be executed once loading all the nodes is complete, 1312 * @trigger load_all.jstree 1313 */ 1314 load_all : function (obj, callback) { 1315 if(!obj) { obj = $.jstree.root; } 1316 obj = this.get_node(obj); 1317 if(!obj) { return false; } 1318 var to_load = [], 1319 m = this._model.data, 1320 c = m[obj.id].children_d, 1321 i, j; 1322 if(obj.state && !obj.state.loaded) { 1323 to_load.push(obj.id); 1324 } 1325 for(i = 0, j = c.length; i < j; i++) { 1326 if(m[c[i]] && m[c[i]].state && !m[c[i]].state.loaded) { 1327 to_load.push(c[i]); 1328 } 1329 } 1330 if(to_load.length) { 1331 this._load_nodes(to_load, function () { 1332 this.load_all(obj, callback); 1333 }); 1334 } 1335 else { 1336 /** 1337 * triggered after a load_all call completes 1338 * @event 1339 * @name load_all.jstree 1340 * @param {Object}
1340 node the recursively loaded node 1341 */ 1342 if(callback) { callback.call(this, obj); } 1343 this.trigger('load_all', { "node" : obj }); 1344 } 1345 }, 1346 /** 1347 * handles the actual loading of a node. Used only internally. 1348 * @private 1349 * @name _load_node(obj [, callback]) 1350 * @param {mixed} obj 1351 * @param {function} callback a function to be executed once loading is complete, the function is executed in the instance's scope and receives one argument - a boolean status 1352 * @return {Boolean} 1353 */ 1354 _load_node : function (obj, callback) { 1355 var s = this.settings.core.data, t; 1356 var notTextOrCommentNode = function notTextOrCommentNode () { 1357 return this.nodeType !== 3 && this.nodeType !== 8; 1358 }; 1359 // use original HTML 1360 if(!s) { 1361 if(obj.id === $.jstree.root) { 1362 return this._append_html_data(obj, this._data.core.original_container_html.clone(true), function (status) { 1363 callback.call(this, status); 1364 }); 1365 } 1366 else { 1367 return callback.call(this, false); 1368 } 1369 // return callback.call(this, obj.id === $.jstree.root ? this._append_html_data(obj, this._data.core.original_container_html.clone(true)) : false); 1370 } 1371 if($.isFunction(s)) { 1372 return s.call(this, obj, $.proxy(function (d) { 1373 if(d === false) { 1374 callback.call(this, false); 1375 } 1376 else { 1377 this[typeof d === 'string' ? '_append_html_data' : '_append_json_data'](obj, typeof d === 'string' ? $($.parseHTML(d)).filter(notTextOrCommentNode) : d, function (status) { 1378 callback.call(this, status); 1379 }); 1380 } 1381 // return d === false ? callback.call(this, false) : callback.call(this, this[typeof d === 'string' ? '_append_html_data' : '_append_json_data'](obj, typeof d === 'string' ? $(d) : d)); 1382 }, this)); 1383 } 1384 if(typeof s === 'object') { 1385 if(s.url) { 1386 s = $.extend(true, {}, s); 1387 if($.isFunction(s.url)) { 1388 s.url = s.url.call(this, obj); 1389 } 1390 if($.isFunction(s.data)) { 1391 s.data = s.data.call(this, obj); 1392 } 1393 return $.ajax(s) 1394 .done($.proxy(function (d,t,x) { 1395 var type = x.getResponseHeader('Content-Type'); 1396 if((type && type.indexOf('json') !== -1) || typeof d === "object") { 1397 return this._append_json_data(obj, d, function (status) { callback.call(this, status); }); 1398 //return callback.call(this, this._append_json_data(obj, d)); 1399 } 1400 if((type && type.indexOf('html') !== -1) || typeof d === "string") { 1401 return this._append_html_data(obj, $($.parseHTML(d)).filter(notTextOrCommentNode), function (status) { callback.call(this, status); }); 1402 // return callback.call(this, this._append_html_data(obj, $(d))); 1403 } 1404 this._data.core.last_error = { 'error' : 'ajax', 'plugin' : 'core', 'id' : 'core_04', 'reason' : 'Could not load node', 'data' : JSON.stringify({ 'id' : obj.id, 'xhr' : x }) }; 1405 this.settings.core.error.call(this, this._data.core.last_error); 1406 return callback.call(this, false); 1407 }, this)) 1408 .fail($.proxy(function (f) { 1409 this._data.core.last_error = { 'error' : 'ajax', 'plugin' : 'core', 'id' : 'core_04', 'reason' : 'Could not load node', 'data' : JSON.stringify({ 'id' : obj.id, 'xhr' : f }) }; 1410 callback.call(this, false); 1411 this.settings.core.error.call(this, this._data.core.last_error); 1412 }, this)); 1413 } 1414 if ($.isArray(s)) { 1415 t = $.extend(true, [], s); 1416 } else if ($.isPlainObject(s)) { 1417 t = $.extend(true, {}, s); 1418 } else { 1419 t = s; 1420 } 1421 if(obj.id === $.jstree.root) { 1422 return this._append_json_data(obj, t, function (status) { 1423 callback.call(this, status); 1424 }); 1425 } 1426 else { 1427 this._data.core.last_error = { 'error' : 'nodata', 'plugin' : 'core', 'id' : 'core_05', 'reason' : 'Could not load node', 'data' : JSON.stringify({ 'id' : obj.id }) }; 1428 this.settings.core.error.call(this, this._data.core.last_error); 1429 return callback.call(this, false); 1430 } 1431 //return callback.call(this, (obj.id === $.jstree.root ? this._append_json_data(obj, t) : false) ); 1432 } 1433 if(typeof s === 'string') { 1434 if(obj.id === $.jstree.root) { 1435 return this._append_html_data(obj, $($.parseHTML(s)).filter(notTextOrCommentNode), function (status) { 1436 callback.call(this, status); 1437 });
vendor: 4,179 bytes, lines 1438-1573
1438 } 1439 else { 1440 this._data.core.last_error = { 'error' : 'nodata', 'plugin' : 'core', 'id' : 'core_06', 'reason' : 'Could not load node', 'data' : JSON.stringify({ 'id' : obj.id }) }; 1441 this.settings.core.error.call(this, this._data.core.last_error); 1442 return callback.call(this, false); 1443 } 1444 //return callback.call(this, (obj.id === $.jstree.root ? this._append_html_data(obj, $(s)) : false) ); 1445 } 1446 return callback.call(this, false); 1447 }, 1448 /** 1449 * adds a node to the list of nodes to redraw. Used only internally. 1450 * @private 1451 * @name _node_changed(obj [, callback]) 1452 * @param {mixed} obj 1453 */ 1454 _node_changed : function (obj) { 1455 obj = this.get_node(obj); 1456 if(obj) { 1457 this._model.changed.push(obj.id); 1458 } 1459 }, 1460 /** 1461 * appends HTML content to the tree. Used internally. 1462 * @private 1463 * @name _append_html_data(obj, data) 1464 * @param {mixed} obj the node to append to 1465 * @param {String} data the HTML string to parse and append 1466 * @trigger model.jstree, changed.jstree 1467 */ 1468 _append_html_data : function (dom, data, cb) { 1469 dom = this.get_node(dom); 1470 dom.children = []; 1471 dom.children_d = []; 1472 var dat = data.is('ul') ? data.children() : data, 1473 par = dom.id, 1474 chd = [], 1475 dpc = [], 1476 m = this._model.data, 1477 p = m[par], 1478 s = this._data.core.selected.length, 1479 tmp, i, j; 1480 dat.each($.proxy(function (i, v) { 1481 tmp = this._parse_model_from_html($(v), par, p.parents.concat()); 1482 if(tmp) { 1483 chd.push(tmp); 1484 dpc.push(tmp); 1485 if(m[tmp].children_d.length) { 1486 dpc = dpc.concat(m[tmp].children_d); 1487 } 1488 } 1489 }, this)); 1490 p.children = chd; 1491 p.children_d = dpc; 1492 for(i = 0, j = p.parents.length; i < j; i++) { 1493 m[p.parents[i]].children_d = m[p.parents[i]].children_d.concat(dpc); 1494 } 1495 /** 1496 * triggered when new data is inserted to the tree model 1497 * @event 1498 * @name model.jstree 1499 * @param {Array} nodes an array of node IDs 1500 * @param {String} parent the parent ID of the nodes 1501 */ 1502 this.trigger('model', { "nodes" : dpc, 'parent' : par }); 1503 if(par !== $.jstree.root) { 1504 this._node_changed(par); 1505 this.redraw(); 1506 } 1507 else { 1508 this.get_container_ul().children('.jstree-initial-node').remove(); 1509 this.redraw(true); 1510 } 1511 if(this._data.core.selected.length !== s) { 1512 this.trigger('changed', { 'action' : 'model', 'selected' : this._data.core.selected }); 1513 } 1514 cb.call(this, true); 1515 }, 1516 /** 1517 * appends JSON content to the tree. Used internally. 1518 * @private 1519 * @name _append_json_data(obj, data) 1520 * @param {mixed} obj the node to append to 1521 * @param {String} data the JSON object to parse and append 1522 * @param {Boolean} force_processing internal param - do not set 1523 * @trigger model.jstree, changed.jstree 1524 */ 1525 _append_json_data : function (dom, data, cb, force_processing) { 1526 if(this.element === null) { return; } 1527 dom = this.get_node(dom); 1528 dom.children = []; 1529 dom.children_d = []; 1530 // *%$@!!! 1531 if(data.d) { 1532 data = data.d; 1533 if(typeof data === "string") { 1534 data = JSON.parse(data); 1535 } 1536 } 1537 if(!$.isArray(data)) { data = [data]; } 1538 var w = null, 1539 args = { 1540 'df' : this._model.default_state, 1541 'dat' : data, 1542 'par' : dom.id, 1543 'm' : this._model.data, 1544 't_id' : this._id, 1545 't_cnt' : this._cnt, 1546 'sel' : this._data.core.selected 1547 }, 1548 func = function (data, undefined) { 1549 if(data.data) { data = data.data; } 1550 var dat = data.dat, 1551 par = data.par, 1552 chd = [], 1553 dpc = [], 1554 add = [], 1555 df = data.df, 1556 t_id = data.t_id, 1557 t_cnt = data.t_cnt, 1558 m = data.m, 1559 p = m[par], 1560 sel = data.sel, 1561 tmp, i, j, rslt, 1562 parse_flat = function (d, p, ps) { 1563 if(!ps) { ps = []; } 1564 else { ps = ps.concat(); } 1565 if(p) { ps.unshift(p); } 1566 var tid = d.id.toString(), 1567 i, j, c, e, 1568 tmp = { 1569 id : tid, 1570 text : d.text || '', 1571 icon : d.icon !== undefined ? d.icon : true, 1572 parent : p, 1573 parents : ps,
vendor: 112,565 bytes, lines 1574-4876
1574 children : d.children || [], 1575 children_d : d.children_d || [], 1576 data : d.data, 1577 state : { }, 1578 li_attr : { id : false }, 1579 a_attr : { href : '#' }, 1580 original : false 1581 }; 1582 for(i in df) { 1583 if(df.hasOwnProperty(i)) { 1584 tmp.state[i] = df[i]; 1585 } 1586 } 1587 if(d && d.data && d.data.jstree && d.data.jstree.icon) { 1588 tmp.icon = d.data.jstree.icon; 1589 } 1590 if(tmp.icon === undefined || tmp.icon === null || tmp.icon === "") { 1591 tmp.icon = true; 1592 } 1593 if(d && d.data) { 1594 tmp.data = d.data; 1595 if(d.data.jstree) { 1596 for(i in d.data.jstree) { 1597 if(d.data.jstree.hasOwnProperty(i)) { 1598 tmp.state[i] = d.data.jstree[i]; 1599 } 1600 } 1601 } 1602 } 1603 if(d && typeof d.state === 'object') { 1604 for (i in d.state) { 1605 if(d.state.hasOwnProperty(i)) { 1606 tmp.state[i] = d.state[i]; 1607 } 1608 } 1609 } 1610 if(d && typeof d.li_attr === 'object') { 1611 for (i in d.li_attr) { 1612 if(d.li_attr.hasOwnProperty(i)) { 1613 tmp.li_attr[i] = d.li_attr[i]; 1614 } 1615 } 1616 } 1617 if(!tmp.li_attr.id) { 1618 tmp.li_attr.id = tid; 1619 } 1620 if(d && typeof d.a_attr === 'object') { 1621 for (i in d.a_attr) { 1622 if(d.a_attr.hasOwnProperty(i)) { 1623 tmp.a_attr[i] = d.a_attr[i]; 1624 } 1625 } 1626 } 1627 if(d && d.children && d.children === true) { 1628 tmp.state.loaded = false; 1629 tmp.children = []; 1630 tmp.children_d = []; 1631 } 1632 m[tmp.id] = tmp; 1633 for(i = 0, j = tmp.children.length; i < j; i++) { 1634 c = parse_flat(m[tmp.children[i]], tmp.id, ps); 1635 e = m[c]; 1636 tmp.children_d.push(c); 1637 if(e.children_d.length) { 1638 tmp.children_d = tmp.children_d.concat(e.children_d); 1639 } 1640 } 1641 delete d.data; 1642 delete d.children; 1643 m[tmp.id].original = d; 1644 if(tmp.state.selected) { 1645 add.push(tmp.id); 1646 } 1647 return tmp.id; 1648 }, 1649 parse_nest = function (d, p, ps) { 1650 if(!ps) { ps = []; } 1651 else { ps = ps.concat(); } 1652 if(p) { ps.unshift(p); } 1653 var tid = false, i, j, c, e, tmp; 1654 do { 1655 tid = 'j' + t_id + '_' + (++t_cnt); 1656 } while(m[tid]); 1657 1658 tmp = { 1659 id : false, 1660 text : typeof d === 'string' ? d : '', 1661 icon : typeof d === 'object' && d.icon !== undefined ? d.icon : true, 1662 parent : p, 1663 parents : ps, 1664 children : [], 1665 children_d : [], 1666 data : null, 1667 state : { }, 1668 li_attr : { id : false }, 1669 a_attr : { href : '#' }, 1670 original : false 1671 }; 1672 for(i in df) { 1673 if(df.hasOwnProperty(i)) { 1674 tmp.state[i] = df[i]; 1675 } 1676 } 1677 if(d && d.id) { tmp.id = d.id.toString(); } 1678 if(d && d.text) { tmp.text = d.text; } 1679 if(d && d.data && d.data.jstree && d.data.jstree.icon) { 1680 tmp.icon = d.data.jstree.icon; 1681 } 1682 if(tmp.icon === undefined || tmp.icon === null || tmp.icon === "") { 1683 tmp.icon = true; 1684 } 1685 if(d && d.data) { 1686 tmp.data = d.data; 1687 if(d.data.jstree) { 1688 for(i in d.data.jstree) { 1689 if(d.data.jstree.hasOwnProperty(i)) { 1690 tmp.state[i] = d.data.jstree[i]; 1691 } 1692 } 1693 } 1694 } 1695 if(d && typeof d.state === 'object') { 1696 for (i in d.state) { 1697 if(d.state.hasOwnProperty(i)) { 1698 tmp.state[i] = d.state[i]; 1699 } 1700 } 1701 } 1702 if(d && typeof d.li_attr === 'object') { 1703 for (i in d.li_attr) { 1704 if(d.li_attr.hasOwnProperty(i)) { 1705 tmp.li_attr[i] = d.li_attr[i]; 1706 } 1707 } 1708 } 1709 if(tmp.li_attr.id && !tmp.id) { 1710 tmp.id = tmp.li_attr.id.toString(); 1711 } 1712 if(!tmp.id) { 1713 tmp.id = tid; 1714 } 1715 if(!tmp.li_attr.id) { 1716 tmp.li_attr.id = tmp.id; 1717 } 1718 if(d && typeof d.a_attr === 'object') { 1719 for (i in d.a_attr) { 1720 if(d.a_attr.hasOwnProperty(i)) { 1721 tmp.a_attr[i] = d.a_attr[i]; 1722 } 1723 } 1724 } 1725 if(d && d.children && d.children.length) { 1726 for(i = 0, j = d.children.length; i < j; i++) { 1727 c = parse_nest(d.children[i], tmp.id, ps); 1728 e = m[c]; 1729 tmp.children.push(c); 1730 if(e.children_d.length) { 1731 tmp.children_d = tmp.children_d.concat(e.children_d); 1732 } 1733 } 1734 tmp.children_d = tmp.children_d.concat(tmp.children); 1735 } 1736 if(d && d.children && d.children === true) { 1737 tmp.state.loaded = false; 1738 tmp.children = []; 1739 tmp.children_d = []; 1740 } 1741 delete d.data; 1742 delete d.children; 1743 tmp.original = d; 1744 m[tmp.id] = tmp; 1745 if(tmp.state.selected) { 1746 add.push(tmp.id); 1747 } 1748 return tmp.id; 1749 }; 1750 1751 if(dat.length && dat[0].id !== undefined && dat[0].parent !== undefined) { 1752 // Flat JSON support (for easy import from DB): 1753 // 1) convert to object (foreach) 1754 for(i = 0, j = dat.length; i < j; i++) { 1755 if(!dat[i].children) { 1756 dat[i].children = []; 1757 } 1758 m[dat[i].id.toString()] = dat[i]; 1759 } 1760 // 2) populate children (foreach) 1761 for(i = 0, j = dat.length; i < j; i++) { 1762 m[dat[i].parent.toString()].children.push(dat[i].id.toString()); 1763 // populate parent.children_d 1764 p.children_d.push(dat[i].id.toString()); 1765 } 1766 // 3) normalize && populate parents and children_d with recursion 1767 for(i = 0, j = p.children.length; i < j; i++) { 1768 tmp = parse_flat(m[p.children[i]], par, p.parents.concat()); 1769 dpc.push(tmp); 1770 if(m[tmp].children_d.length) { 1771 dpc = dpc.concat(m[tmp].children_d); 1772 } 1773 } 1774 for(i = 0, j = p.parents.length; i < j; i++) { 1775 m[p.parents[i]].children_d = m[p.parents[i]].children_d.concat(dpc); 1776 } 1777 // ?) three_state selection - p.state.selected && t - (if three_state foreach(dat => ch) -> foreach(parents) if(parent.selected) child.selected = true; 1778 rslt = { 1779 'cnt' : t_cnt, 1780 'mod' : m, 1781 'sel' : sel, 1782 'par' : par, 1783 'dpc' : dpc, 1784 'add' : add 1785 }; 1786 } 1787 else { 1788 for(i = 0, j = dat.length; i < j; i++) { 1789 tmp = parse_nest(dat[i], par, p.parents.concat()); 1790 if(tmp) { 1791 chd.push(tmp); 1792 dpc.push(tmp); 1793 if(m[tmp].children_d.length) { 1794 dpc = dpc.concat(m[tmp].children_d); 1795 } 1796 } 1797 } 1798 p.children = chd; 1799 p.children_d = dpc; 1800 for(i = 0, j = p.parents.length; i < j; i++) { 1801 m[p.parents[i]].children_d = m[p.parents[i]].children_d.concat(dpc); 1802 } 1803 rslt = { 1804 'cnt' : t_cnt, 1805 'mod' : m, 1806 'sel' : sel, 1807 'par' : par, 1808 'dpc' : dpc, 1809 'add' : add 1810 }; 1811 } 1812 if(typeof window === 'undefined' || typeof window.document === 'undefined') { 1813 postMessage(rslt); 1814 } 1815 else { 1816 return rslt; 1817 } 1818 }, 1819 rslt = function (rslt, worker) { 1820 if(this.element === null) { return; } 1821 this._cnt = rslt.cnt; 1822 var i, m = this._model.data; 1823 for (i in m) { 1824 if (m.hasOwnProperty(i) && m[i].state && m[i].state.loading && rslt.mod[i]) { 1825 rslt.mod[i].state.loading = true; 1826 } 1827 } 1828 this._model.data = rslt.mod; // breaks the reference in load_node - careful 1829 1830 if(worker) { 1831 var j, a = rslt.add, r = rslt.sel, s = this._data.core.selected.slice(); 1832 m = this._model.data; 1833 // if selection was changed while calculating in worker 1834 if(r.length !== s.length || $.vakata.array_unique(r.concat(s)).length !== r.length) { 1835 // deselect nodes that are no longer selected 1836 for(i = 0, j = r.length; i < j; i++) { 1837 if($.inArray(r[i], a) === -1 && $.inArray(r[i], s) === -1) { 1838 m[r[i]].state.selected = false; 1839 } 1840 } 1841 // select nodes that were selected in the mean time 1842 for(i = 0, j = s.length; i < j; i++) { 1843 if($.inArray(s[i], r) === -1) { 1844 m[s[i]].state.selected = true; 1845 } 1846 } 1847 } 1848 } 1849 if(rslt.add.length) { 1850 this._data.core.selected = this._data.core.selected.concat(rslt.add); 1851 } 1852 1853 this.trigger('model', { "nodes" : rslt.dpc, 'parent' : rslt.par }); 1854 1855 if(rslt.par !== $.jstree.root) { 1856 this._node_changed(rslt.par); 1857 this.redraw(); 1858 } 1859 else { 1860 // this.get_container_ul().children('.jstree-initial-node').remove(); 1861 this.redraw(true); 1862 } 1863 if(rslt.add.length) { 1864 this.trigger('changed', { 'action' : 'model', 'selected' : this._data.core.selected }); 1865 } 1866 cb.call(this, true); 1867 }; 1868 if(this.settings.core.worker && window.Blob && window.URL && window.Worker) { 1869 try { 1870 if(this._wrk === null) { 1871 this._wrk = window.URL.createObjectURL( 1872 new window.Blob( 1873 ['self.onmessage = ' + func.toString()], 1874 {type:"text/javascript"} 1875 ) 1876 ); 1877 } 1878 if(!this._data.core.working || force_processing) { 1879 this._data.core.working = true; 1880 w = new window.Worker(this._wrk); 1881 w.onmessage = $.proxy(function (e) { 1882 rslt.call(this, e.data, true); 1883 try { w.terminate(); w = null; } catch(ignore) { } 1884 if(this._data.core.worker_queue.length) { 1885 this._append_json_data.apply(this, this._data.core.worker_queue.shift()); 1886 } 1887 else { 1888 this._data.core.working = false; 1889 } 1890 }, this); 1891 if(!args.par) { 1892 if(this._data.core.worker_queue.length) { 1893 this._append_json_data.apply(this, this._data.core.worker_queue.shift()); 1894 } 1895 else { 1896 this._data.core.working = false; 1897 } 1898 } 1899 else { 1900 w.postMessage(args); 1901 } 1902 } 1903 else { 1904 this._data.core.worker_queue.push([dom, data, cb, true]); 1905 } 1906 } 1907 catch(e) { 1908 rslt.call(this, func(args), false); 1909 if(this._data.core.worker_queue.length) { 1910 this._append_json_data.apply(this, this._data.core.worker_queue.shift()); 1911 } 1912 else { 1913 this._data.core.working = false; 1914 } 1915 } 1916 } 1917 else { 1918 rslt.call(this, func(args), false); 1919 } 1920 }, 1921 /** 1922 * parses a node from a jQuery object and appends them to the in memory tree model. Used internally. 1923 * @private 1924 * @name _parse_model_from_html(d [, p, ps]) 1925 * @param {jQuery} d the jQuery object to parse 1926 * @param {String} p the parent ID 1927 * @param {Array} ps list of all parents 1928 * @return {String} the ID of the object added to the model 1929 */ 1930 _parse_model_from_html : function (d, p, ps) { 1931 if(!ps) { ps = []; } 1932 else { ps = [].concat(ps); } 1933 if(p) { ps.unshift(p); } 1934 var c, e, m = this._model.data, 1935 data = { 1936 id : false, 1937 text : false, 1938 icon : true, 1939 parent : p, 1940 parents : ps, 1941 children : [], 1942 children_d : [], 1943 data : null, 1944 state : { }, 1945 li_attr : { id : false }, 1946 a_attr : { href : '#' }, 1947 original : false 1948 }, i, tmp, tid; 1949 for(i in this._model.default_state) { 1950 if(this._model.default_state.hasOwnProperty(i)) { 1951 data.state[i] = this._model.default_state[i]; 1952 } 1953 } 1954 tmp = $.vakata.attributes(d, true); 1955 $.each(tmp, function (i, v) { 1956 v = $.trim(v); 1957 if(!v.length) { return true; } 1958 data.li_attr[i] = v; 1959 if(i === 'id') { 1960 data.id = v.toString(); 1961 } 1962 }); 1963 tmp = d.children('a').first(); 1964 if(tmp.length) { 1965 tmp = $.vakata.attributes(tmp, true); 1966 $.each(tmp, function (i, v) { 1967 v = $.trim(v); 1968 if(v.length) { 1969 data.a_attr[i] = v; 1970 } 1971 }); 1972 } 1973 tmp = d.children("a").first().length ? d.children("a").first().clone() : d.clone(); 1974 tmp.children("ins, i, ul").remove(); 1975 tmp = tmp.html(); 1976 tmp = $('<div />').html(tmp); 1977 data.text = this.settings.core.force_text ? tmp.text() : tmp.html(); 1978 tmp = d.data(); 1979 data.data = tmp ? $.extend(true, {}, tmp) : null; 1980 data.state.opened = d.hasClass('jstree-open'); 1981 data.state.selected = d.children('a').hasClass('jstree-clicked'); 1982 data.state.disabled = d.children('a').hasClass('jstree-disabled'); 1983 if(data.data && data.data.jstree) { 1984 for(i in data.data.jstree) { 1985 if(data.data.jstree.hasOwnProperty(i)) { 1986 data.state[i] = data.data.jstree[i]; 1987 } 1988 } 1989 } 1990 tmp = d.children("a").children(".jstree-themeicon"); 1991 if(tmp.length) { 1992 data.icon = tmp.hasClass('jstree-themeicon-hidden') ? false : tmp.attr('rel'); 1993 } 1994 if(data.state.icon !== undefined) { 1995 data.icon = data.state.icon; 1996 } 1997 if(data.icon === undefined || data.icon === null || data.icon === "") { 1998 data.icon = true; 1999 } 2000 tmp = d.children("ul").children("li"); 2001 do { 2002 tid = 'j' + this._id + '_' + (++this._cnt); 2003 } while(m[tid]); 2004 data.id = data.li_attr.id ? data.li_attr.id.toString() : tid; 2005 if(tmp.length) { 2006 tmp.each($.proxy(function (i, v) { 2007 c = this._parse_model_from_html($(v), data.id, ps); 2008 e = this._model.data[c]; 2009 data.children.push(c); 2010 if(e.children_d.length) { 2011 data.children_d = data.children_d.concat(e.children_d); 2012 } 2013 }, this)); 2014 data.children_d = data.children_d.concat(data.children); 2015 } 2016 else { 2017 if(d.hasClass('jstree-closed')) { 2018 data.state.loaded = false; 2019 } 2020 } 2021 if(data.li_attr['class']) { 2022 data.li_attr['class'] = data.li_attr['class'].replace('jstree-closed','').replace('jstree-open',''); 2023 } 2024 if(data.a_attr['class']) { 2025 data.a_attr['class'] = data.a_attr['class'].replace('jstree-clicked','').replace('jstree-disabled',''); 2026 } 2027 m[data.id] = data; 2028 if(data.state.selected) { 2029 this._data.core.selected.push(data.id); 2030 } 2031 return data.id; 2032 }, 2033 /** 2034 * parses a node from a JSON object (used when dealing with flat data, which has no nesting of children, but has id and parent properties) and appends it to the in memory tree model. Used internally. 2035 * @private 2036 * @name _parse_model_from_flat_json(d [, p, ps]) 2037 * @param {Object} d the JSON object to parse 2038 * @param {String} p the parent ID 2039 * @param {Array} ps list of all parents 2040 * @return {String} the ID of the object added to the model 2041 */ 2042 _parse_model_from_flat_json : function (d, p, ps) { 2043 if(!ps) { ps = []; } 2044 else { ps = ps.concat(); } 2045 if(p) { ps.unshift(p); } 2046 var tid = d.id.toString(), 2047 m = this._model.data, 2048 df = this._model.default_state, 2049 i, j, c, e, 2050 tmp = { 2051 id : tid, 2052 text : d.text || '', 2053 icon : d.icon !== undefined ? d.icon : true, 2054 parent : p, 2055 parents : ps, 2056 children : d.children || [], 2057 children_d : d.children_d || [], 2058 data : d.data, 2059 state : { }, 2060 li_attr : { id : false }, 2061 a_attr : { href : '#' }, 2062 original : false 2063 }; 2064 for(i in df) { 2065 if(df.hasOwnProperty(i)) { 2066 tmp.state[i] = df[i]; 2067 } 2068 } 2069 if(d && d.data && d.data.jstree && d.data.jstree.icon) { 2070 tmp.icon = d.data.jstree.icon; 2071 } 2072 if(tmp.icon === undefined || tmp.icon === null || tmp.icon === "") { 2073 tmp.icon = true; 2074 } 2075 if(d && d.data) { 2076 tmp.data = d.data; 2077 if(d.data.jstree) { 2078 for(i in d.data.jstree) { 2079 if(d.data.jstree.hasOwnProperty(i)) { 2080 tmp.state[i] = d.data.jstree[i]; 2081 } 2082 } 2083 } 2084 } 2085 if(d && typeof d.state === 'object') { 2086 for (i in d.state) { 2087 if(d.state.hasOwnProperty(i)) { 2088 tmp.state[i] = d.state[i]; 2089 } 2090 } 2091 } 2092 if(d && typeof d.li_attr === 'object') { 2093 for (i in d.li_attr) { 2094 if(d.li_attr.hasOwnProperty(i)) { 2095 tmp.li_attr[i] = d.li_attr[i]; 2096 } 2097 } 2098 } 2099 if(!tmp.li_attr.id) { 2100 tmp.li_attr.id = tid; 2101 } 2102 if(d && typeof d.a_attr === 'object') { 2103 for (i in d.a_attr) { 2104 if(d.a_attr.hasOwnProperty(i)) { 2105 tmp.a_attr[i] = d.a_attr[i]; 2106 } 2107 } 2108 } 2109 if(d && d.children && d.children === true) { 2110 tmp.state.loaded = false; 2111 tmp.children = []; 2112 tmp.children_d = []; 2113 } 2114 m[tmp.id] = tmp; 2115 for(i = 0, j = tmp.children.length; i < j; i++) { 2116 c = this._parse_model_from_flat_json(m[tmp.children[i]], tmp.id, ps); 2117 e = m[c]; 2118 tmp.children_d.push(c); 2119 if(e.children_d.length) { 2120 tmp.children_d = tmp.children_d.concat(e.children_d); 2121 } 2122 } 2123 delete d.data; 2124 delete d.children; 2125 m[tmp.id].original = d; 2126 if(tmp.state.selected) { 2127 this._data.core.selected.push(tmp.id); 2128 } 2129 return tmp.id; 2130 }, 2131 /** 2132 * parses a node from a JSON object and appends it to the in memory tree model. Used internally. 2133 * @private 2134 * @name _parse_model_from_json(d [, p, ps]) 2135 * @param {Object} d the JSON object to parse 2136 * @param {String} p the parent ID 2137 * @param {Array} ps list of all parents 2138 * @return {String} the ID of the object added to the model 2139 */ 2140 _parse_model_from_json : function (d, p, ps) { 2141 if(!ps) { ps = []; } 2142 else { ps = ps.concat(); } 2143 if(p) { ps.unshift(p); } 2144 var tid = false, i, j, c, e, m = this._model.data, df = this._model.default_state, tmp; 2145 do { 2146 tid = 'j' + this._id + '_' + (++this._cnt); 2147 } while(m[tid]); 2148 2149 tmp = { 2150 id : false, 2151 text : typeof d === 'string' ? d : '', 2152 icon : typeof d === 'object' && d.icon !== undefined ? d.icon : true, 2153 parent : p, 2154 parents : ps, 2155 children : [], 2156 children_d : [], 2157 data : null, 2158 state : { }, 2159 li_attr : { id : false }, 2160 a_attr : { href : '#' }, 2161 original : false 2162 }; 2163 for(i in df) { 2164 if(df.hasOwnProperty(i)) { 2165 tmp.state[i] = df[i]; 2166 } 2167 } 2168 if(d && d.id) { tmp.id = d.id.toString(); } 2169 if(d && d.text) { tmp.text = d.text; } 2170 if(d && d.data && d.data.jstree && d.data.jstree.icon) { 2171 tmp.icon = d.data.jstree.icon; 2172 } 2173 if(tmp.icon === undefined || tmp.icon === null || tmp.icon === "") { 2174 tmp.icon = true; 2175 } 2176 if(d && d.data) { 2177 tmp.data = d.data; 2178 if(d.data.jstree) { 2179 for(i in d.data.jstree) { 2180 if(d.data.jstree.hasOwnProperty(i)) { 2181 tmp.state[i] = d.data.jstree[i]; 2182 } 2183 } 2184 } 2185 } 2186 if(d && typeof d.state === 'object') { 2187 for (i in d.state) { 2188 if(d.state.hasOwnProperty(i)) { 2189 tmp.state[i] = d.state[i]; 2190 } 2191 } 2192 } 2193 if(d && typeof d.li_attr === 'object') { 2194 for (i in d.li_attr) { 2195 if(d.li_attr.hasOwnProperty(i)) { 2196 tmp.li_attr[i] = d.li_attr[i]; 2197 } 2198 } 2199 } 2200 if(tmp.li_attr.id && !tmp.id) { 2201 tmp.id = tmp.li_attr.id.toString(); 2202 } 2203 if(!tmp.id) { 2204 tmp.id = tid; 2205 } 2206 if(!tmp.li_attr.id) { 2207 tmp.li_attr.id = tmp.id; 2208 } 2209 if(d && typeof d.a_attr === 'object') { 2210 for (i in d.a_attr) { 2211 if(d.a_attr.hasOwnProperty(i)) { 2212 tmp.a_attr[i] = d.a_attr[i]; 2213 } 2214 } 2215 } 2216 if(d && d.children && d.children.length) { 2217 for(i = 0, j = d.children.length; i < j; i++) { 2218 c = this._parse_model_from_json(d.children[i], tmp.id, ps); 2219 e = m[c]; 2220 tmp.children.push(c); 2221 if(e.children_d.length) { 2222 tmp.children_d = tmp.children_d.concat(e.children_d); 2223 } 2224 } 2225 tmp.children_d = tmp.children_d.concat(tmp.children); 2226 } 2227 if(d && d.children && d.children === true) { 2228 tmp.state.loaded = false; 2229 tmp.children = []; 2230 tmp.children_d = []; 2231 } 2232 delete d.data; 2233 delete d.children; 2234 tmp.original = d; 2235 m[tmp.id] = tmp; 2236 if(tmp.state.selected) { 2237 this._data.core.selected.push(tmp.id); 2238 } 2239 return tmp.id; 2240 }, 2241 /** 2242 * redraws all nodes that need to be redrawn. Used internally. 2243 * @private 2244 * @name _redraw() 2245 * @trigger redraw.jstree 2246 */ 2247 _redraw : function () { 2248 var nodes = this._model.force_full_redraw ? this._model.data[$.jstree.root].children.concat([]) : this._model.changed.concat([]), 2249 f = document.createElement('UL'), tmp, i, j, fe = this._data.core.focused; 2250 for(i = 0, j = nodes.length; i < j; i++) { 2251 tmp = this.redraw_node(nodes[i], true, this._model.force_full_redraw); 2252 if(tmp && this._model.force_full_redraw) { 2253 f.appendChild(tmp); 2254 } 2255 } 2256 if(this._model.force_full_redraw) { 2257 f.className = this.get_container_ul()[0].className; 2258 f.setAttribute('role','group'); 2259 this.element.empty().append(f); 2260 //this.get_container_ul()[0].appendChild(f); 2261 } 2262 if(fe !== null) { 2263 tmp = this.get_node(fe, true); 2264 if(tmp && tmp.length && tmp.children('.jstree-anchor')[0] !== document.activeElement) { 2265 tmp.children('.jstree-anchor').focus(); 2266 } 2267 else { 2268 this._data.core.focused = null; 2269 } 2270 } 2271 this._model.force_full_redraw = false; 2272 this._model.changed = []; 2273 /** 2274 * triggered after nodes are redrawn 2275 * @event 2276 * @name redraw.jstree 2277 * @param {array} nodes the redrawn nodes 2278 */ 2279 this.trigger('redraw', { "nodes" : nodes }); 2280 }, 2281 /** 2282 * redraws all nodes that need to be redrawn or optionally - the whole tree 2283 * @name redraw([full]) 2284 * @param {Boolean} full if set to `true` all nodes are redrawn. 2285 */ 2286 redraw : function (full) { 2287 if(full) { 2288 this._model.force_full_redraw = true; 2289 } 2290 //if(this._model.redraw_timeout) { 2291 // clearTimeout(this._model.redraw_timeout); 2292 //} 2293 //this._model.redraw_timeout = setTimeout($.proxy(this._redraw, this),0); 2294 this._redraw(); 2295 }, 2296 /** 2297 * redraws a single node's children. Used internally. 2298 * @private 2299 * @name draw_children(node) 2300 * @param {mixed} node the node whose children will be redrawn 2301 */ 2302 draw_children : function (node) { 2303 var obj = this.get_node(node), 2304 i = false, 2305 j = false, 2306 k = false, 2307 d = document; 2308 if(!obj) { return false; } 2309 if(obj.id === $.jstree.root) { return this.redraw(true); } 2310 node = this.get_node(node, true); 2311 if(!node || !node.length) { return false; } // TODO: quick toggle 2312 2313 node.children('.jstree-children').remove(); 2314 node = node[0]; 2315 if(obj.children.length && obj.state.loaded) { 2316 k = d.createElement('UL'); 2317 k.setAttribute('role', 'group'); 2318 k.className = 'jstree-children'; 2319 for(i = 0, j = obj.children.length; i < j; i++) { 2320 k.appendChild(this.redraw_node(obj.children[i], true, true)); 2321 } 2322 node.appendChild(k); 2323 } 2324 }, 2325 /** 2326 * redraws a single node. Used internally. 2327 * @private 2328 * @name redraw_node(node, deep, is_callback, force_render) 2329 * @param {mixed} node the node to redraw 2330 * @param {Boolean} deep should child nodes be redrawn too 2331 * @param {Boolean} is_callback is this a recursion call 2332 * @param {Boolean} force_render should children of closed parents be drawn anyway 2333 */ 2334 redraw_node : function (node, deep, is_callback, force_render) { 2335 var obj = this.get_node(node), 2336 par = false, 2337 ind = false, 2338 old = false, 2339 i = false, 2340 j = false, 2341 k = false, 2342 c = '', 2343 d = document, 2344 m = this._model.data, 2345 f = false, 2346 s = false, 2347 tmp = null, 2348 t = 0, 2349 l = 0, 2350 has_children = false, 2351 last_sibling = false; 2352 if(!obj) { return false; } 2353 if(obj.id === $.jstree.root) { return this.redraw(true); } 2354 deep = deep || obj.children.length === 0; 2355 node = !document.querySelector ? document.getElementById(obj.id) : this.element[0].querySelector('#' + ("0123456789".indexOf(obj.id[0]) !== -1 ? '\\3' + obj.id[0] + ' ' + obj.id.substr(1).replace($.jstree.idregex,'\\$&') : obj.id.replace($.jstree.idregex,'\\$&')) ); //, this.element); 2356 if(!node) { 2357 deep = true; 2358 //node = d.createElement('LI'); 2359 if(!is_callback) { 2360 par = obj.parent !== $.jstree.root ? $('#' + obj.parent.replace($.jstree.idregex,'\\$&'), this.element)[0] : null; 2361 if(par !== null && (!par || !m[obj.parent].state.opened)) { 2362 return false; 2363 } 2364 ind = $.inArray(obj.id, par === null ? m[$.jstree.root].children : m[obj.parent].children); 2365 } 2366 } 2367 else { 2368 node = $(node); 2369 if(!is_callback) { 2370 par = node.parent().parent()[0]; 2371 if(par === this.element[0]) { 2372 par = null; 2373 } 2374 ind = node.index(); 2375 } 2376 // m[obj.id].data = node.data(); // use only node's data, no need to touch jquery storage 2377 if(!deep && obj.children.length && !node.children('.jstree-children').length) { 2378 deep = true; 2379 } 2380 if(!deep) { 2381 old = node.children('.jstree-children')[0]; 2382 } 2383 f = node.children('.jstree-anchor')[0] === document.activeElement; 2384 node.remove(); 2385 //node = d.createElement('LI'); 2386 //node = node[0]; 2387 } 2388 node = this._data.core.node.cloneNode(true); 2389 // node is DOM, deep is boolean 2390 2391 c = 'jstree-node '; 2392 for(i in obj.li_attr) { 2393 if(obj.li_attr.hasOwnProperty(i)) { 2394 if(i === 'id') { continue; } 2395 if(i !== 'class') { 2396 node.setAttribute(i, obj.li_attr[i]); 2397 } 2398 else { 2399 c += obj.li_attr[i]; 2400 } 2401 } 2402 } 2403 if(!obj.a_attr.id) { 2404 obj.a_attr.id = obj.id + '_anchor'; 2405 } 2406 node.setAttribute('aria-selected', !!obj.state.selected); 2407 node.setAttribute('aria-level', obj.parents.length); 2408 node.setAttribute('aria-labelledby', obj.a_attr.id); 2409 if(obj.state.disabled) { 2410 node.setAttribute('aria-disabled', true); 2411 } 2412 2413 for(i = 0, j = obj.children.length; i < j; i++) { 2414 if(!m[obj.children[i]].state.hidden) { 2415 has_children = true; 2416 break; 2417 } 2418 } 2419 if(obj.parent !== null && m[obj.parent] && !obj.state.hidden) { 2420 i = $.inArray(obj.id, m[obj.parent].children); 2421 last_sibling = obj.id; 2422 if(i !== -1) { 2423 i++; 2424 for(j = m[obj.parent].children.length; i < j; i++) { 2425 if(!m[m[obj.parent].children[i]].state.hidden) { 2426 last_sibling = m[obj.parent].children[i]; 2427 } 2428 if(last_sibling !== obj.id) { 2429 break; 2430 } 2431 } 2432 } 2433 } 2434 2435 if(obj.state.hidden) { 2436 c += ' jstree-hidden'; 2437 } 2438 if(obj.state.loaded && !has_children) { 2439 c += ' jstree-leaf'; 2440 } 2441 else { 2442 c += obj.state.opened && obj.state.loaded ? ' jstree-open' : ' jstree-closed'; 2443 node.setAttribute('aria-expanded', (obj.state.opened && obj.state.loaded) ); 2444 } 2445 if(last_sibling === obj.id) { 2446 c += ' jstree-last'; 2447 } 2448 node.id = obj.id; 2449 node.className = c; 2450 c = ( obj.state.selected ? ' jstree-clicked' : '') + ( obj.state.disabled ? ' jstree-disabled' : ''); 2451 for(j in obj.a_attr) { 2452 if(obj.a_attr.hasOwnProperty(j)) { 2453 if(j === 'href' && obj.a_attr[j] === '#') { continue; } 2454 if(j !== 'class') { 2455 node.childNodes[1].setAttribute(j, obj.a_attr[j]); 2456 } 2457 else { 2458 c += ' ' + obj.a_attr[j]; 2459 } 2460 } 2461 } 2462 if(c.length) { 2463 node.childNodes[1].className = 'jstree-anchor ' + c; 2464 } 2465 if((obj.icon && obj.icon !== true) || obj.icon === false) { 2466 if(obj.icon === false) { 2467 node.childNodes[1].childNodes[0].className += ' jstree-themeicon-hidden'; 2468 } 2469 else if(obj.icon.indexOf('/') === -1 && obj.icon.indexOf('.') === -1) { 2470 node.childNodes[1].childNodes[0].className += ' ' + obj.icon + ' jstree-themeicon-custom'; 2471 } 2472 else { 2473 node.childNodes[1].childNodes[0].style.backgroundImage = 'url("'+obj.icon+'")'; 2474 node.childNodes[1].childNodes[0].style.backgroundPosition = 'center center'; 2475 node.childNodes[1].childNodes[0].style.backgroundSize = 'auto'; 2476 node.childNodes[1].childNodes[0].className += ' jstree-themeicon-custom'; 2477 } 2478 } 2479 2480 if(this.settings.core.force_text) { 2481 node.childNodes[1].appendChild(d.createTextNode(obj.text)); 2482 } 2483 else { 2484 node.childNodes[1].innerHTML += obj.text; 2485 } 2486 2487 2488 if(deep && obj.children.length && (obj.state.opened || force_render) && obj.state.loaded) { 2489 k = d.createElement('UL'); 2490 k.setAttribute('role', 'group'); 2491 k.className = 'jstree-children'; 2492 for(i = 0, j = obj.children.length; i < j; i++) { 2493 k.appendChild(this.redraw_node(obj.children[i], deep, true)); 2494 } 2495 node.appendChild(k); 2496 } 2497 if(old) { 2498 node.appendChild(old); 2499 } 2500 if(!is_callback) { 2501 // append back using par / ind 2502 if(!par) { 2503 par = this.element[0]; 2504 } 2505 for(i = 0, j = par.childNodes.length; i < j; i++) { 2506 if(par.childNodes[i] && par.childNodes[i].className && par.childNodes[i].className.indexOf('jstree-children') !== -1) { 2507 tmp = par.childNodes[i]; 2508 break; 2509 } 2510 } 2511 if(!tmp) { 2512 tmp = d.createElement('UL'); 2513 tmp.setAttribute('role', 'group'); 2514 tmp.className = 'jstree-children'; 2515 par.appendChild(tmp); 2516 } 2517 par = tmp; 2518 2519 if(ind < par.childNodes.length) { 2520 par.insertBefore(node, par.childNodes[ind]); 2521 } 2522 else { 2523 par.appendChild(node); 2524 } 2525 if(f) { 2526 t = this.element[0].scrollTop; 2527 l = this.element[0].scrollLeft; 2528 node.childNodes[1].focus(); 2529 this.element[0].scrollTop = t; 2530 this.element[0].scrollLeft = l; 2531 } 2532 } 2533 if(obj.state.opened && !obj.state.loaded) { 2534 obj.state.opened = false; 2535 setTimeout($.proxy(function () { 2536 this.open_node(obj.id, false, 0); 2537 }, this), 0); 2538 } 2539 return node; 2540 }, 2541 /** 2542 * opens a node, revaling its children. If the node is not loaded it will be loaded and opened once ready. 2543 * @name open_node(obj [, callback, animation]) 2544 * @param {mixed} obj the node to open 2545 * @param {Function} callback a function to execute once the node is opened 2546 * @param {Number} animation the animation duration in milliseconds when opening the node (overrides the `core.animation` setting). Use `false` for no animation. 2547 * @trigger open_node.jstree, after_open.jstree, before_open.jstree 2548 */ 2549 open_node : function (obj, callback, animation) { 2550 var t1, t2, d, t; 2551 if($.isArray(obj)) { 2552 obj = obj.slice(); 2553 for(t1 = 0, t2 = obj.length; t1 < t2; t1++) { 2554 this.open_node(obj[t1], callback, animation); 2555 } 2556 return true; 2557 } 2558 obj = this.get_node(obj); 2559 if(!obj || obj.id === $.jstree.root) { 2560 return false; 2561 } 2562 animation = animation === undefined ? this.settings.core.animation : animation; 2563 if(!this.is_closed(obj)) { 2564 if(callback) { 2565 callback.call(this, obj, false); 2566 } 2567 return false; 2568 } 2569 if(!this.is_loaded(obj)) { 2570 if(this.is_loading(obj)) { 2571 return setTimeout($.proxy(function () { 2572 this.open_node(obj, callback, animation); 2573 }, this), 500); 2574 } 2575 this.load_node(obj, function (o, ok) { 2576 return ok ? this.open_node(o, callback, animation) : (callback ? callback.call(this, o, false) : false); 2577 }); 2578 } 2579 else { 2580 d = this.get_node(obj, true); 2581 t = this; 2582 if(d.length) { 2583 if(animation && d.children(".jstree-children").length) { 2584 d.children(".jstree-children").stop(true, true); 2585 } 2586 if(obj.children.length && !this._firstChild(d.children('.jstree-children')[0])) { 2587 this.draw_children(obj); 2588 //d = this.get_node(obj, true); 2589 } 2590 if(!animation) { 2591 this.trigger('before_open', { "node" : obj }); 2592 d[0].className = d[0].className.replace('jstree-closed', 'jstree-open'); 2593 d[0].setAttribute("aria-expanded", true); 2594 } 2595 else { 2596 this.trigger('before_open', { "node" : obj }); 2597 d 2598 .children(".jstree-children").css("display","none").end() 2599 .removeClass("jstree-closed").addClass("jstree-open").attr("aria-expanded", true) 2600 .children(".jstree-children").stop(true, true) 2601 .slideDown(animation, function () { 2602 this.style.display = ""; 2603 if (t.element) { 2604 t.trigger("after_open", { "node" : obj }); 2605 } 2606 }); 2607 } 2608 } 2609 obj.state.opened = true; 2610 if(callback) { 2611 callback.call(this, obj, true); 2612 } 2613 if(!d.length) { 2614 /** 2615 * triggered when a node is about to be opened (if the node is supposed to be in the DOM, it will be, but it won't be visible yet) 2616 * @event 2617 * @name before_open.jstree 2618 * @param {Object} node the opened node 2619 */ 2620 this.trigger('before_open', { "node" : obj }); 2621 } 2622 /** 2623 * triggered when a node is opened (if there is an animation it will not be completed yet) 2624 * @event 2625 * @name open_node.jstree 2626 * @param {Object} node the opened node 2627 */ 2628 this.trigger('open_node', { "node" : obj }); 2629 if(!animation || !d.length) { 2630 /** 2631 * triggered when a node is opened and the animation is complete 2632 * @event 2633 * @name after_open.jstree 2634 * @param {Object} node the opened node 2635 */ 2636 this.trigger("after_open", { "node" : obj }); 2637 } 2638 return true; 2639 } 2640 }, 2641 /** 2642 * opens every parent of a node (node should be loaded) 2643 * @name _open_to(obj) 2644 * @param {mixed} obj the node to reveal 2645 * @private 2646 */ 2647 _open_to : function (obj) { 2648 obj = this.get_node(obj); 2649 if(!obj || obj.id === $.jstree.root) { 2650 return false; 2651 } 2652 var i, j, p = obj.parents; 2653 for(i = 0, j = p.length; i < j; i+=1) { 2654 if(i !== $.jstree.root) { 2655 this.open_node(p[i], false, 0); 2656 } 2657 } 2658 return $('#' + obj.id.replace($.jstree.idregex,'\\$&'), this.element); 2659 }, 2660 /** 2661 * closes a node, hiding its children 2662 * @name close_node(obj [, animation]) 2663 * @param {mixed} obj the node to close 2664 * @param {Number} animation the animation duration in milliseconds when closing the node (overrides the `core.animation` setting). Use `false` for no animation. 2665 * @trigger close_node.jstree, after_close.jstree 2666 */ 2667 close_node : function (obj, animation) { 2668 var t1, t2, t, d; 2669 if($.isArray(obj)) { 2670 obj = obj.slice(); 2671 for(t1 = 0, t2 = obj.length; t1 < t2; t1++) { 2672 this.close_node(obj[t1], animation); 2673 } 2674 return true; 2675 } 2676 obj = this.get_node(obj); 2677 if(!obj || obj.id === $.jstree.root) { 2678 return false; 2679 } 2680 if(this.is_closed(obj)) { 2681 return false; 2682 } 2683 animation = animation === undefined ? this.settings.core.animation : animation; 2684 t = this; 2685 d = this.get_node(obj, true); 2686 2687 obj.state.opened = false; 2688 /** 2689 * triggered when a node is closed (if there is an animation it will not be complete yet) 2690 * @event 2691 * @name close_node.jstree 2692 * @param {Object} node the closed node 2693 */ 2694 this.trigger('close_node',{ "node" : obj }); 2695 if(!d.length) { 2696 /** 2697 * triggered when a node is closed and the animation is complete 2698 * @event 2699 * @name after_close.jstree 2700 * @param {Object} node the closed node 2701 */ 2702 this.trigger("after_close", { "node" : obj }); 2703 } 2704 else { 2705 if(!animation) { 2706 d[0].className = d[0].className.replace('jstree-open', 'jstree-closed'); 2707 d.attr("aria-expanded", false).children('.jstree-children').remove(); 2708 this.trigger("after_close", { "node" : obj }); 2709 } 2710 else { 2711 d 2712 .children(".jstree-children").attr("style","display:block !important").end() 2713 .removeClass("jstree-open").addClass("jstree-closed").attr("aria-expanded", false) 2714 .children(".jstree-children").stop(true, true).slideUp(animation, function () { 2715 this.style.display = ""; 2716 d.children('.jstree-children').remove(); 2717 if (t.element) { 2718 t.trigger("after_close", { "node" : obj }); 2719 } 2720 }); 2721 } 2722 } 2723 }, 2724 /** 2725 * toggles a node - closing it if it is open, opening it if it is closed 2726 * @name toggle_node(obj) 2727 * @param {mixed} obj the node to toggle 2728 */ 2729 toggle_node : function (obj) { 2730 var t1, t2; 2731 if($.isArray(obj)) { 2732 obj = obj.slice(); 2733 for(t1 = 0, t2 = obj.length; t1 < t2; t1++) { 2734 this.toggle_node(obj[t1]); 2735 } 2736 return true; 2737 } 2738 if(this.is_closed(obj)) { 2739 return this.open_node(obj); 2740 } 2741 if(this.is_open(obj)) { 2742 return this.close_node(obj); 2743 } 2744 }, 2745 /** 2746 * opens all nodes within a node (or the tree), revaling their children. If the node is not loaded it will be loaded and opened once ready. 2747 * @name open_all([obj, animation, original_obj]) 2748 * @param {mixed} obj the node to open recursively, omit to open all nodes in the tree 2749 * @param {Number} animation the animation duration in milliseconds when opening the nodes, the default is no animation 2750 * @param {jQuery} reference to the node that started the process (internal use) 2751 * @trigger open_all.jstree 2752 */ 2753 open_all : function (obj, animation, original_obj) { 2754 if(!obj) { obj = $.jstree.root; } 2755 obj = this.get_node(obj); 2756 if(!obj) { return false; } 2757 var dom = obj.id === $.jstree.root ? this.get_container_ul() : this.get_node(obj, true), i, j, _this; 2758 if(!dom.length) { 2759 for(i = 0, j = obj.children_d.length; i < j; i++) { 2760 if(this.is_closed(this._model.data[obj.children_d[i]])) { 2761 this._model.data[obj.children_d[i]].state.opened = true; 2762 } 2763 } 2764 return this.trigger('open_all', { "node" : obj }); 2765 } 2766 original_obj = original_obj || dom; 2767 _this = this; 2768 dom = this.is_closed(obj) ? dom.find('.jstree-closed').addBack() : dom.find('.jstree-closed'); 2769 dom.each(function () { 2770 _this.open_node( 2771 this, 2772 function(node, status) { if(status && this.is_parent(node)) { this.open_all(node, animation, original_obj); } }, 2773 animation || 0 2774 ); 2775 }); 2776 if(original_obj.find('.jstree-closed').length === 0) { 2777 /** 2778 * triggered when an `open_all` call completes 2779 * @event 2780 * @name open_all.jstree 2781 * @param {Object} node the opened node 2782 */ 2783 this.trigger('open_all', { "node" : this.get_node(original_obj) }); 2784 } 2785 }, 2786 /** 2787 * closes all nodes within a node (or the tree), revaling their children 2788 * @name close_all([obj, animation]) 2789 * @param {mixed} obj the node to close recursively, omit to close all nodes in the tree 2790 * @param {Number} animation the animation duration in milliseconds when closing the nodes, the default is no animation 2791 * @trigger close_all.jstree 2792 */ 2793 close_all : function (obj, animation) { 2794 if(!obj) { obj = $.jstree.root; } 2795 obj = this.get_node(obj); 2796 if(!obj) { return false; } 2797 var dom = obj.id === $.jstree.root ? this.get_container_ul() : this.get_node(obj, true), 2798 _this = this, i, j; 2799 if(dom.length) { 2800 dom = this.is_open(obj) ? dom.find('.jstree-open').addBack() : dom.find('.jstree-open'); 2801 $(dom.get().reverse()).each(function () { _this.close_node(this, animation || 0); }); 2802 } 2803 for(i = 0, j = obj.children_d.length; i < j; i++) { 2804 this._model.data[obj.children_d[i]].state.opened = false; 2805 } 2806 /** 2807 * triggered when an `close_all` call completes 2808 * @event 2809 * @name close_all.jstree 2810 * @param {Object} node the closed node 2811 */ 2812 this.trigger('close_all', { "node" : obj }); 2813 }, 2814 /** 2815 * checks if a node is disabled (not selectable) 2816 * @name is_disabled(obj) 2817 * @param {mixed} obj 2818 * @return {Boolean} 2819 */ 2820 is_disabled : function (obj) { 2821 obj = this.get_node(obj); 2822 return obj && obj.state && obj.state.disabled; 2823 }, 2824 /** 2825 * enables a node - so that it can be selected 2826 * @name enable_node(obj) 2827 * @param {mixed} obj the node to enable 2828 * @trigger enable_node.jstree 2829 */ 2830 enable_node : function (obj) { 2831 var t1, t2; 2832 if($.isArray(obj)) { 2833 obj = obj.slice(); 2834 for(t1 = 0, t2 = obj.length; t1 < t2; t1++) { 2835 this.enable_node(obj[t1]); 2836 } 2837 return true; 2838 } 2839 obj = this.get_node(obj); 2840 if(!obj || obj.id === $.jstree.root) { 2841 return false; 2842 } 2843 obj.state.disabled = false; 2844 this.get_node(obj,true).children('.jstree-anchor').removeClass('jstree-disabled').attr('aria-disabled', false); 2845 /** 2846 * triggered when an node is enabled 2847 * @event 2848 * @name enable_node.jstree 2849 * @param {Object} node the enabled node 2850 */ 2851 this.trigger('enable_node', { 'node' : obj }); 2852 }, 2853 /** 2854 * disables a node - so that it can not be selected 2855 * @name disable_node(obj) 2856 * @param {mixed} obj the node to disable 2857 * @trigger disable_node.jstree 2858 */ 2859 disable_node : function (obj) { 2860 var t1, t2; 2861 if($.isArray(obj)) { 2862 obj = obj.slice(); 2863 for(t1 = 0, t2 = obj.length; t1 < t2; t1++) { 2864 this.disable_node(obj[t1]); 2865 } 2866 return true; 2867 } 2868 obj = this.get_node(obj); 2869 if(!obj || obj.id === $.jstree.root) { 2870 return false; 2871 } 2872 obj.state.disabled = true; 2873 this.get_node(obj,true).children('.jstree-anchor').addClass('jstree-disabled').attr('aria-disabled', true); 2874 /** 2875 * triggered when an node is disabled 2876 * @event 2877 * @name disable_node.jstree 2878 * @param {Object} node the disabled node 2879 */ 2880 this.trigger('disable_node', { 'node' : obj }); 2881 }, 2882 /** 2883 * determines if a node is hidden 2884 * @name is_hidden(obj) 2885 * @param {mixed} obj the node 2886 */ 2887 is_hidden : function (obj) { 2888 obj = this.get_node(obj); 2889 return obj.state.hidden === true; 2890 }, 2891 /** 2892 * hides a node - it is still in the structure but will not be visible 2893 * @name hide_node(obj) 2894 * @param {mixed} obj the node to hide 2895 * @param {Boolean} skip_redraw internal parameter controlling if redraw is called 2896 * @trigger hide_node.jstree 2897 */ 2898 hide_node : function (obj, skip_redraw) { 2899 var t1, t2; 2900 if($.isArray(obj)) { 2901 obj = obj.slice(); 2902 for(t1 = 0, t2 = obj.length; t1 < t2; t1++) { 2903 this.hide_node(obj[t1], true); 2904 } 2905 if (!skip_redraw) { 2906 this.redraw(); 2907 } 2908 return true; 2909 } 2910 obj = this.get_node(obj); 2911 if(!obj || obj.id === $.jstree.root) { 2912 return false; 2913 } 2914 if(!obj.state.hidden) { 2915 obj.state.hidden = true; 2916 this._node_changed(obj.parent); 2917 if(!skip_redraw) { 2918 this.redraw(); 2919 } 2920 /** 2921 * triggered when an node is hidden 2922 * @event 2923 * @name hide_node.jstree 2924 * @param {Object} node the hidden node 2925 */ 2926 this.trigger('hide_node', { 'node' : obj }); 2927 } 2928 }, 2929 /** 2930 * shows a node 2931 * @name show_node(obj) 2932 * @param {mixed} obj the node to show 2933 * @param {Boolean} skip_redraw internal parameter controlling if redraw is called 2934 * @trigger show_node.jstree 2935 */ 2936 show_node : function (obj, skip_redraw) { 2937 var t1, t2; 2938 if($.isArray(obj)) { 2939 obj = obj.slice(); 2940 for(t1 = 0, t2 = obj.length; t1 < t2; t1++) { 2941 this.show_node(obj[t1], true); 2942 } 2943 if (!skip_redraw) { 2944 this.redraw(); 2945 } 2946 return true; 2947 } 2948 obj = this.get_node(obj); 2949 if(!obj || obj.id === $.jstree.root) { 2950 return false; 2951 } 2952 if(obj.state.hidden) { 2953 obj.state.hidden = false; 2954 this._node_changed(obj.parent); 2955 if(!skip_redraw) { 2956 this.redraw(); 2957 } 2958 /** 2959 * triggered when an node is shown 2960 * @event 2961 * @name show_node.jstree 2962 * @param {Object} node the shown node 2963 */ 2964 this.trigger('show_node', { 'node' : obj }); 2965 } 2966 }, 2967 /** 2968 * hides all nodes 2969 * @name hide_all() 2970 * @trigger hide_all.jstree 2971 */ 2972 hide_all : function (skip_redraw) { 2973 var i, m = this._model.data, ids = []; 2974 for(i in m) { 2975 if(m.hasOwnProperty(i) && i !== $.jstree.root && !m[i].state.hidden) { 2976 m[i].state.hidden = true; 2977 ids.push(i); 2978 } 2979 } 2980 this._model.force_full_redraw = true; 2981 if(!skip_redraw) { 2982 this.redraw(); 2983 } 2984 /** 2985 * triggered when all nodes are hidden 2986 * @event 2987 * @name hide_all.jstree 2988 * @param {Array} nodes the IDs of all hidden nodes 2989 */ 2990 this.trigger('hide_all', { 'nodes' : ids }); 2991 return ids; 2992 }, 2993 /** 2994 * shows all nodes 2995 * @name show_all() 2996 * @trigger show_all.jstree 2997 */ 2998 show_all : function (skip_redraw) { 2999 var i, m = this._model.data, ids = []; 3000 for(i in m) { 3001 if(m.hasOwnProperty(i) && i !== $.jstree.root && m[i].state.hidden) { 3002 m[i].state.hidden = false; 3003 ids.push(i); 3004 } 3005 } 3006 this._model.force_full_redraw = true; 3007 if(!skip_redraw) { 3008 this.redraw(); 3009 } 3010 /** 3011 * triggered when all nodes are shown 3012 * @event 3013 * @name show_all.jstree 3014 * @param {Array} nodes the IDs of all shown nodes 3015 */ 3016 this.trigger('show_all', { 'nodes' : ids }); 3017 return ids; 3018 }, 3019 /** 3020 * called when a node is selected by the user. Used internally. 3021 * @private 3022 * @name activate_node(obj, e) 3023 * @param {mixed} obj the node 3024 * @param {Object} e the related event 3025 * @trigger activate_node.jstree, changed.jstree 3026 */ 3027 activate_node : function (obj, e) { 3028 if(this.is_disabled(obj)) { 3029 return false; 3030 } 3031 if(!e || typeof e !== 'object') { 3032 e = {}; 3033 } 3034 3035 // ensure last_clicked is still in the DOM, make it fresh (maybe it was moved?) and make sure it is still selected, if not - make last_clicked the last selected node 3036 this._data.core.last_clicked = this._data.core.last_clicked && this._data.core.last_clicked.id !== undefined ? this.get_node(this._data.core.last_clicked.id) : null; 3037 if(this._data.core.last_clicked && !this._data.core.last_clicked.state.selected) { this._data.core.last_clicked = null; } 3038 if(!this._data.core.last_clicked && this._data.core.selected.length) { this._data.core.last_clicked = this.get_node(this._data.core.selected[this._data.core.selected.length - 1]); } 3039 3040 if(!this.settings.core.multiple || (!e.metaKey && !e.ctrlKey && !e.shiftKey) || (e.shiftKey && (!this._data.core.last_clicked || !this.get_parent(obj) || this.get_parent(obj) !== this._data.core.last_clicked.parent ) )) { 3041 if(!this.settings.core.multiple && (e.metaKey || e.ctrlKey || e.shiftKey) && this.is_selected(obj)) { 3042 this.deselect_node(obj, false, e); 3043 } 3044 else { 3045 this.deselect_all(true); 3046 this.select_node(obj, false, false, e); 3047 this._data.core.last_clicked = this.get_node(obj); 3048 } 3049 } 3050 else { 3051 if(e.shiftKey) { 3052 var o = this.get_node(obj).id, 3053 l = this._data.core.last_clicked.id, 3054 p = this.get_node(this._data.core.last_clicked.parent).children, 3055 c = false, 3056 i, j; 3057 for(i = 0, j = p.length; i < j; i += 1) { 3058 // separate IFs work whem o and l are the same 3059 if(p[i] === o) { 3060 c = !c; 3061 } 3062 if(p[i] === l) { 3063 c = !c; 3064 } 3065 if(!this.is_disabled(p[i]) && (c || p[i] === o || p[i] === l)) { 3066 if (!this.is_hidden(p[i])) { 3067 this.select_node(p[i], true, false, e); 3068 } 3069 } 3070 else { 3071 this.deselect_node(p[i], true, e); 3072 } 3073 } 3074 this.trigger('changed', { 'action' : 'select_node', 'node' : this.get_node(obj), 'selected' : this._data.core.selected, 'event' : e }); 3075 } 3076 else { 3077 if(!this.is_selected(obj)) { 3078 this.select_node(obj, false, false, e); 3079 } 3080 else { 3081 this.deselect_node(obj, false, e); 3082 } 3083 } 3084 } 3085 /** 3086 * triggered when an node is clicked or intercated with by the user 3087 * @event 3088 * @name activate_node.jstree 3089 * @param {Object} node 3090 * @param {Object} event the ooriginal event (if any) which triggered the call (may be an empty object) 3091 */ 3092 this.trigger('activate_node', { 'node' : this.get_node(obj), 'event' : e }); 3093 }, 3094 /** 3095 * applies the hover state on a node, called when a node is hovered by the user. Used internally. 3096 * @private 3097 * @name hover_node(obj) 3098 * @param {mixed} obj 3099 * @trigger hover_node.jstree 3100 */ 3101 hover_node : function (obj) { 3102 obj = this.get_node(obj, true); 3103 if(!obj || !obj.length || obj.children('.jstree-hovered').length) { 3104 return false; 3105 } 3106 var o = this.element.find('.jstree-hovered'), t = this.element; 3107 if(o && o.length) { this.dehover_node(o); } 3108 3109 obj.children('.jstree-anchor').addClass('jstree-hovered'); 3110 /** 3111 * triggered when an node is hovered 3112 * @event 3113 * @name hover_node.jstree 3114 * @param {Object} node 3115 */ 3116 this.trigger('hover_node', { 'node' : this.get_node(obj) }); 3117 setTimeout(function () { t.attr('aria-activedescendant', obj[0].id); }, 0); 3118 }, 3119 /** 3120 * removes the hover state from a nodecalled when a node is no longer hovered by the user. Used internally. 3121 * @private 3122 * @name dehover_node(obj) 3123 * @param {mixed} obj 3124 * @trigger dehover_node.jstree 3125 */ 3126 dehover_node : function (obj) { 3127 obj = this.get_node(obj, true); 3128 if(!obj || !obj.length || !obj.children('.jstree-hovered').length) { 3129 return false; 3130 } 3131 obj.children('.jstree-anchor').removeClass('jstree-hovered'); 3132 /** 3133 * triggered when an node is no longer hovered 3134 * @event 3135 * @name dehover_node.jstree 3136 * @param {Object} node 3137 */ 3138 this.trigger('dehover_node', { 'node' : this.get_node(obj) }); 3139 }, 3140 /** 3141 * select a node 3142 * @name select_node(obj [, supress_event, prevent_open]) 3143 * @param {mixed} obj an array can be used to select multiple nodes 3144 * @param {Boolean} supress_event if set to `true` the `changed.jstree` event won't be triggered 3145 * @param {Boolean} prevent_open if set to `true` parents of the selected node won't be opened 3146 * @trigger select_node.jstree, changed.jstree 3147 */ 3148 select_node : function (obj, supress_event, prevent_open, e) { 3149 var dom, t1, t2, th; 3150 if($.isArray(obj)) { 3151 obj = obj.slice(); 3152 for(t1 = 0, t2 = obj.length; t1 < t2; t1++) { 3153 this.select_node(obj[t1], supress_event, prevent_open, e); 3154 } 3155 return true; 3156 } 3157 obj = this.get_node(obj); 3158 if(!obj || obj.id === $.jstree.root) { 3159 return false; 3160 } 3161 dom = this.get_node(obj, true); 3162 if(!obj.state.selected) { 3163 obj.state.selected = true; 3164 this._data.core.selected.push(obj.id); 3165 if(!prevent_open) { 3166 dom = this._open_to(obj); 3167 } 3168 if(dom && dom.length) { 3169 dom.attr('aria-selected', true).children('.jstree-anchor').addClass('jstree-clicked'); 3170 } 3171 /** 3172 * triggered when an node is selected 3173 * @event 3174 * @name select_node.jstree 3175 * @param {Object} node 3176 * @param {Array} selected the current selection 3177 * @param {Object} event the event (if any) that triggered this select_node 3178 */ 3179 this.trigger('select_node', { 'node' : obj, 'selected' : this._data.core.selected, 'event' : e }); 3180 if(!supress_event) { 3181 /** 3182 * triggered when selection changes 3183 * @event 3184 * @name changed.jstree 3185 * @param {Object} node 3186 * @param {Object} action the action that caused the selection to change 3187 * @param {Array} selected the current selection 3188 * @param {Object} event the event (if any) that triggered this changed event 3189 */ 3190 this.trigger('changed', { 'action' : 'select_node', 'node' : obj, 'selected' : this._data.core.selected, 'event' : e }); 3191 } 3192 } 3193 }, 3194 /** 3195 * deselect a node 3196 * @name deselect_node(obj [, supress_event]) 3197 * @param {mixed} obj an array can be used to deselect multiple nodes 3198 * @param {Boolean} supress_event if set to `true` the `changed.jstree` event won't be triggered 3199 * @trigger deselect_node.jstree, changed.jstree 3200 */ 3201 deselect_node : function (obj, supress_event, e) { 3202 var t1, t2, dom; 3203 if($.isArray(obj)) { 3204 obj = obj.slice(); 3205 for(t1 = 0, t2 = obj.length; t1 < t2; t1++) { 3206 this.deselect_node(obj[t1], supress_event, e); 3207 } 3208 return true; 3209 } 3210 obj = this.get_node(obj); 3211 if(!obj || obj.id === $.jstree.root) { 3212 return false; 3213 } 3214 dom = this.get_node(obj, true); 3215 if(obj.state.selected) { 3216 obj.state.selected = false; 3217 this._data.core.selected = $.vakata.array_remove_item(this._data.core.selected, obj.id); 3218 if(dom.length) { 3219 dom.attr('aria-selected', false).children('.jstree-anchor').removeClass('jstree-clicked'); 3220 } 3221 /** 3222 * triggered when an node is deselected 3223 * @event 3224 * @name deselect_node.jstree 3225 * @param {Object} node 3226 * @param {Array} selected the current selection 3227 * @param {Object} event the event (if any) that triggered this deselect_node 3228 */ 3229 this.trigger('deselect_node', { 'node' : obj, 'selected' : this._data.core.selected, 'event' : e }); 3230 if(!supress_event) { 3231 this.trigger('changed', { 'action' : 'deselect_node', 'node' : obj, 'selected' : this._data.core.selected, 'event' : e }); 3232 } 3233 } 3234 }, 3235 /** 3236 * select all nodes in the tree 3237 * @name select_all([supress_event]) 3238 * @param {Boolean} supress_event if set to `true` the `changed.jstree` event won't be triggered 3239 * @trigger select_all.jstree, changed.jstree 3240 */ 3241 select_all : function (supress_event) { 3242 var tmp = this._data.core.selected.concat([]), i, j; 3243 this._data.core.selected = this._model.data[$.jstree.root].children_d.concat(); 3244 for(i = 0, j = this._data.core.selected.length; i < j; i++) { 3245 if(this._model.data[this._data.core.selected[i]]) { 3246 this._model.data[this._data.core.selected[i]].state.selected = true; 3247 } 3248 } 3249 this.redraw(true); 3250 /** 3251 * triggered when all nodes are selected 3252 * @event 3253 * @name select_all.jstree 3254 * @param {Array} selected the current selection 3255 */ 3256 this.trigger('select_all', { 'selected' : this._data.core.selected }); 3257 if(!supress_event) { 3258 this.trigger('changed', { 'action' : 'select_all', 'selected' : this._data.core.selected, 'old_selection' : tmp }); 3259 } 3260 }, 3261 /** 3262 * deselect all selected nodes 3263 * @name deselect_all([supress_event]) 3264 * @param {Boolean} supress_event if set to `true` the `changed.jstree` event won't be triggered 3265 * @trigger deselect_all.jstree, changed.jstree 3266 */ 3267 deselect_all : function (supress_event) { 3268 var tmp = this._data.core.selected.concat([]), i, j; 3269 for(i = 0, j = this._data.core.selected.length; i < j; i++) { 3270 if(this._model.data[this._data.core.selected[i]]) { 3271 this._model.data[this._data.core.selected[i]].state.selected = false; 3272 } 3273 } 3274 this._data.core.selected = []; 3275 this.element.find('.jstree-clicked').removeClass('jstree-clicked').parent().attr('aria-selected', false); 3276 /** 3277 * triggered when all nodes are deselected 3278 * @event 3279 * @name deselect_all.jstree 3280 * @param {Object} node the previous selection 3281 * @param {Array} selected the current selection 3282 */ 3283 this.trigger('deselect_all', { 'selected' : this._data.core.selected, 'node' : tmp }); 3284 if(!supress_event) { 3285 this.trigger('changed', { 'action' : 'deselect_all', 'selected' : this._data.core.selected, 'old_selection' : tmp }); 3286 } 3287 }, 3288 /** 3289 * checks if a node is selected 3290 * @name is_selected(obj) 3291 * @param {mixed} obj 3292 * @return {Boolean} 3293 */ 3294 is_selected : function (obj) { 3295 obj = this.get_node(obj); 3296 if(!obj || obj.id === $.jstree.root) { 3297 return false; 3298 } 3299 return obj.state.selected; 3300 }, 3301 /** 3302 * get an array of all selected nodes 3303 * @name get_selected([full]) 3304 * @param {mixed} full if set to `true` the returned array will consist of the full node objects, otherwise - only IDs will be returned 3305 * @return {Array} 3306 */ 3307 get_selected : function (full) { 3308 return full ? $.map(this._data.core.selected, $.proxy(function (i) { return this.get_node(i); }, this)) : this._data.core.selected.slice(); 3309 }, 3310 /** 3311 * get an array of all top level selected nodes (ignoring children of selected nodes) 3312 * @name get_top_selected([full]) 3313 * @param {mixed} full if set to `true` the returned array will consist of the full node objects, otherwise - only IDs will be returned 3314 * @return {Array} 3315 */ 3316 get_top_selected : function (full) { 3317 var tmp = this.get_selected(true), 3318 obj = {}, i, j, k, l; 3319 for(i = 0, j = tmp.length; i < j; i++) { 3320 obj[tmp[i].id] = tmp[i]; 3321 } 3322 for(i = 0, j = tmp.length; i < j; i++) { 3323 for(k = 0, l = tmp[i].children_d.length; k < l; k++) { 3324 if(obj[tmp[i].children_d[k]]) { 3325 delete obj[tmp[i].children_d[k]]; 3326 } 3327 } 3328 } 3329 tmp = []; 3330 for(i in obj) { 3331 if(obj.hasOwnProperty(i)) { 3332 tmp.push(i); 3333 } 3334 } 3335 return full ? $.map(tmp, $.proxy(function (i) { return this.get_node(i); }, this)) : tmp; 3336 }, 3337 /** 3338 * get an array of all bottom level selected nodes (ignoring selected parents) 3339 * @name get_bottom_selected([full]) 3340 * @param {mixed} full if set to `true` the returned array will consist of the full node objects, otherwise - only IDs will be returned 3341 * @return {Array} 3342 */ 3343 get_bottom_selected : function (full) { 3344 var tmp = this.get_selected(true), 3345 obj = [], i, j; 3346 for(i = 0, j = tmp.length; i < j; i++) { 3347 if(!tmp[i].children.length) { 3348 obj.push(tmp[i].id); 3349 } 3350 } 3351 return full ? $.map(obj, $.proxy(function (i) { return this.get_node(i); }, this)) : obj; 3352 }, 3353 /** 3354 * gets the current state of the tree so that it can be restored later with `set_state(state)`. Used internally. 3355 * @name get_state() 3356 * @private 3357 * @return {Object} 3358 */ 3359 get_state : function () { 3360 var state = { 3361 'core' : { 3362 'open' : [], 3363 'scroll' : { 3364 'left' : this.element.scrollLeft(), 3365 'top' : this.element.scrollTop() 3366 }, 3367 /*! 3368 'themes' : { 3369 'name' : this.get_theme(), 3370 'icons' : this._data.core.themes.icons, 3371 'dots' : this._data.core.themes.dots 3372 }, 3373 */ 3374 'selected' : [] 3375 } 3376 }, i; 3377 for(i in this._model.data) { 3378 if(this._model.data.hasOwnProperty(i)) { 3379 if(i !== $.jstree.root) { 3380 if(this._model.data[i].state.opened) { 3381 state.core.open.push(i); 3382 } 3383 if(this._model.data[i].state.selected) { 3384 state.core.selected.push(i); 3385 } 3386 } 3387 } 3388 } 3389 return state; 3390 }, 3391 /** 3392 * sets the state of the tree. Used internally. 3393 * @name set_state(state [, callback]) 3394 * @private 3395 * @param {Object} state the state to restore. Keep in mind this object is passed by reference and jstree will modify it. 3396 * @param {Function} callback an optional function to execute once the state is restored. 3397 * @trigger set_state.jstree 3398 */ 3399 set_state : function (state, callback) { 3400 if(state) { 3401 if(state.core && state.core.selected && state.core.initial_selection === undefined) { 3402 state.core.initial_selection = this._data.core.selected.concat([]).sort().join(','); 3403 } 3404 if(state.core) { 3405 var res, n, t, _this, i; 3406 if(state.core.open) { 3407 if(!$.isArray(state.core.open) || !state.core.open.length) { 3408 delete state.core.open; 3409 this.set_state(state, callback); 3410 } 3411 else { 3412 this._load_nodes(state.core.open, function (nodes) { 3413 this.open_node(nodes, false, 0); 3414 delete state.core.open; 3415 this.set_state(state, callback); 3416 }); 3417 } 3418 return false; 3419 } 3420 if(state.core.scroll) { 3421 if(state.core.scroll && state.core.scroll.left !== undefined) { 3422 this.element.scrollLeft(state.core.scroll.left); 3423 } 3424 if(state.core.scroll && state.core.scroll.top !== undefined) { 3425 this.element.scrollTop(state.core.scroll.top); 3426 } 3427 delete state.core.scroll; 3428 this.set_state(state, callback); 3429 return false; 3430 } 3431 if(state.core.selected) { 3432 _this = this; 3433 if (state.core.initial_selection === undefined || 3434 state.core.initial_selection === this._data.core.selected.concat([]).sort().join(',') 3435 ) { 3436 this.deselect_all(); 3437 $.each(state.core.selected, function (i, v) { 3438 _this.select_node(v, false, true); 3439 }); 3440 } 3441 delete state.core.initial_selection; 3442 delete state.core.selected; 3443 this.set_state(state, callback); 3444 return false; 3445 } 3446 for(i in state) { 3447 if(state.hasOwnProperty(i) && i !== "core" && $.inArray(i, this.settings.plugins) === -1) { 3448 delete state[i]; 3449 } 3450 } 3451 if($.isEmptyObject(state.core)) { 3452 delete state.core; 3453 this.set_state(state, callback); 3454 return false; 3455 } 3456 } 3457 if($.isEmptyObject(state)) { 3458 state = null; 3459 if(callback) { callback.call(this); } 3460 /** 3461 * triggered when a `set_state` call completes 3462 * @event 3463 * @name set_state.jstree 3464 */ 3465 this.trigger('set_state'); 3466 return false; 3467 } 3468 return true; 3469 } 3470 return false; 3471 }, 3472 /** 3473 * refreshes the tree - all nodes are reloaded with calls to `load_node`. 3474 * @name refresh() 3475 * @param {Boolean} skip_loading an option to skip showing the loading indicator 3476 * @param {Mixed} forget_state if set to `true` state will not be reapplied, if set to a function (receiving the current state as argument) the result of that function will be used as state 3477 * @trigger refresh.jstree 3478 */ 3479 refresh : function (skip_loading, forget_state) { 3480 this._data.core.state = forget_state === true ? {} : this.get_state(); 3481 if(forget_state && $.isFunction(forget_state)) { this._data.core.state = forget_state.call(this, this._data.core.state); } 3482 this._cnt = 0; 3483 this._model.data = {}; 3484 this._model.data[$.jstree.root] = { 3485 id : $.jstree.root, 3486 parent : null, 3487 parents : [], 3488 children : [], 3489 children_d : [], 3490 state : { loaded : false } 3491 }; 3492 this._data.core.selected = []; 3493 this._data.core.last_clicked = null; 3494 this._data.core.focused = null; 3495 3496 var c = this.get_container_ul()[0].className; 3497 if(!skip_loading) { 3498 this.element.html("<"+"ul class='"+c+"' role='group'><"+"li class='jstree-initial-node jstree-loading jstree-leaf jstree-last' role='treeitem' id='j"+this._id+"_loading'><i class='jstree-icon jstree-ocl'></i><"+"a class='jstree-anchor' href='#'><i class='jstree-icon jstree-themeicon-hidden'></i>" + this.get_string("Loading ...") + "</a></li></ul>"); 3499 this.element.attr('aria-activedescendant','j'+this._id+'_loading'); 3500 } 3501 this.load_node($.jstree.root, function (o, s) { 3502 if(s) { 3503 this.get_container_ul()[0].className = c; 3504 if(this._firstChild(this.get_container_ul()[0])) { 3505 this.element.attr('aria-activedescendant',this._firstChild(this.get_container_ul()[0]).id); 3506 } 3507 this.set_state($.extend(true, {}, this._data.core.state), function () { 3508 /** 3509 * triggered when a `refresh` call completes 3510 * @event 3511 * @name refresh.jstree 3512 */ 3513 this.trigger('refresh'); 3514 }); 3515 } 3516 this._data.core.state = null; 3517 }); 3518 }, 3519 /** 3520 * refreshes a node in the tree (reload its children) all opened nodes inside that node are reloaded with calls to `load_node`. 3521 * @name refresh_node(obj) 3522 * @param {mixed} obj the node 3523 * @trigger refresh_node.jstree 3524 */ 3525 refresh_node : function (obj) { 3526 obj = this.get_node(obj); 3527 if(!obj || obj.id === $.jstree.root) { return false; } 3528 var opened = [], to_load = [], s = this._data.core.selected.concat([]); 3529 to_load.push(obj.id); 3530 if(obj.state.opened === true) { opened.push(obj.id); } 3531 this.get_node(obj, true).find('.jstree-open').each(function() { to_load.push(this.id); opened.push(this.id); }); 3532 this._load_nodes(to_load, $.proxy(function (nodes) { 3533 this.open_node(opened, false, 0); 3534 this.select_node(s); 3535 /** 3536 * triggered when a node is refreshed 3537 * @event 3538 * @name refresh_node.jstree 3539 * @param {Object} node - the refreshed node 3540 * @param {Array} nodes - an array of the IDs of the nodes that were reloaded 3541 */ 3542 this.trigger('refresh_node', { 'node' : obj, 'nodes' : nodes }); 3543 }, this), false, true); 3544 }, 3545 /** 3546 * set (change) the ID of a node 3547 * @name set_id(obj, id) 3548 * @param {mixed} obj the node 3549 * @param {String} id the new ID 3550 * @return {Boolean} 3551 * @trigger set_id.jstree 3552 */ 3553 set_id : function (obj, id) { 3554 obj = this.get_node(obj); 3555 if(!obj || obj.id === $.jstree.root) { return false; } 3556 var i, j, m = this._model.data, old = obj.id; 3557 id = id.toString(); 3558 // update parents (replace current ID with new one in children and children_d) 3559 m[obj.parent].children[$.inArray(obj.id, m[obj.parent].children)] = id; 3560 for(i = 0, j = obj.parents.length; i < j; i++) { 3561 m[obj.parents[i]].children_d[$.inArray(obj.id, m[obj.parents[i]].children_d)] = id; 3562 } 3563 // update children (replace current ID with new one in parent and parents) 3564 for(i = 0, j = obj.children.length; i < j; i++) { 3565 m[obj.children[i]].parent = id; 3566 } 3567 for(i = 0, j = obj.children_d.length; i < j; i++) { 3568 m[obj.children_d[i]].parents[$.inArray(obj.id, m[obj.children_d[i]].parents)] = id; 3569 } 3570 i = $.inArray(obj.id, this._data.core.selected); 3571 if(i !== -1) { this._data.core.selected[i] = id; } 3572 // update model and obj itself (obj.id, this._model.data[KEY]) 3573 i = this.get_node(obj.id, true); 3574 if(i) { 3575 i.attr('id', id); //.children('.jstree-anchor').attr('id', id + '_anchor').end().attr('aria-labelledby', id + '_anchor'); 3576 if(this.element.attr('aria-activedescendant') === obj.id) { 3577 this.element.attr('aria-activedescendant', id); 3578 } 3579 } 3580 delete m[obj.id]; 3581 obj.id = id; 3582 obj.li_attr.id = id; 3583 m[id] = obj; 3584 /** 3585 * triggered when a node id value is changed 3586 * @event 3587 * @name set_id.jstree 3588 * @param {Object} node 3589 * @param {String} old the old id 3590 */ 3591 this.trigger('set_id',{ "node" : obj, "new" : obj.id, "old" : old }); 3592 return true; 3593 }, 3594 /** 3595 * get the text value of a node 3596 * @name get_text(obj) 3597 * @param {mixed} obj the node 3598 * @return {String} 3599 */ 3600 get_text : function (obj) { 3601 obj = this.get_node(obj); 3602 return (!obj || obj.id === $.jstree.root) ? false : obj.text; 3603 }, 3604 /** 3605 * set the text value of a node. Used internally, please use `rename_node(obj, val)`. 3606 * @private 3607 * @name set_text(obj, val) 3608 * @param {mixed} obj the node, you can pass an array to set the text on multiple nodes 3609 * @param {String} val the new text value 3610 * @return {Boolean} 3611 * @trigger set_text.jstree 3612 */ 3613 set_text : function (obj, val) { 3614 var t1, t2; 3615 if($.isArray(obj)) { 3616 obj = obj.slice(); 3617 for(t1 = 0, t2 = obj.length; t1 < t2; t1++) { 3618 this.set_text(obj[t1], val); 3619 } 3620 return true; 3621 } 3622 obj = this.get_node(obj); 3623 if(!obj || obj.id === $.jstree.root) { return false; } 3624 obj.text = val; 3625 if(this.get_node(obj, true).length) { 3626 this.redraw_node(obj.id); 3627 } 3628 /** 3629 * triggered when a node text value is changed 3630 * @event 3631 * @name set_text.jstree 3632 * @param {Object} obj 3633 * @param {String} text the new value 3634 */ 3635 this.trigger('set_text',{ "obj" : obj, "text" : val }); 3636 return true; 3637 }, 3638 /** 3639 * gets a JSON representation of a node (or the whole tree) 3640 * @name get_json([obj, options]) 3641 * @param {mixed} obj 3642 * @param {Object} options 3643 * @param {Boolean} options.no_state do not return state information 3644 * @param {Boolean} options.no_id do not return ID 3645 * @param {Boolean} options.no_children do not include children 3646 * @param {Boolean} options.no_data do not include node data 3647 * @param {Boolean} options.no_li_attr do not include LI attributes 3648 * @param {Boolean} options.no_a_attr do not include A attributes 3649 * @param {Boolean} options.flat return flat JSON instead of nested 3650 * @return {Object} 3651 */ 3652 get_json : function (obj, options, flat) { 3653 obj = this.get_node(obj || $.jstree.root); 3654 if(!obj) { return false; } 3655 if(options && options.flat && !flat) { flat = []; } 3656 var tmp = { 3657 'id' : obj.id, 3658 'text' : obj.text, 3659 'icon' : this.get_icon(obj), 3660 'li_attr' : $.extend(true, {}, obj.li_attr), 3661 'a_attr' : $.extend(true, {}, obj.a_attr), 3662 'state' : {}, 3663 'data' : options && options.no_data ? false : $.extend(true, $.isArray(obj.data)?[]:{}, obj.data) 3664 //( this.get_node(obj, true).length ? this.get_node(obj, true).data() : obj.data ), 3665 }, i, j; 3666 if(options && options.flat) { 3667 tmp.parent = obj.parent; 3668 } 3669 else { 3670 tmp.children = []; 3671 } 3672 if(!options || !options.no_state) { 3673 for(i in obj.state) { 3674 if(obj.state.hasOwnProperty(i)) { 3675 tmp.state[i] = obj.state[i]; 3676 } 3677 } 3678 } else { 3679 delete tmp.state; 3680 } 3681 if(options && options.no_li_attr) { 3682 delete tmp.li_attr; 3683 } 3684 if(options && options.no_a_attr) { 3685 delete tmp.a_attr; 3686 } 3687 if(options && options.no_id) { 3688 delete tmp.id; 3689 if(tmp.li_attr && tmp.li_attr.id) { 3690 delete tmp.li_attr.id; 3691 } 3692 if(tmp.a_attr && tmp.a_attr.id) { 3693 delete tmp.a_attr.id; 3694 } 3695 } 3696 if(options && options.flat && obj.id !== $.jstree.root) { 3697 flat.push(tmp); 3698 } 3699 if(!options || !options.no_children) { 3700 for(i = 0, j = obj.children.length; i < j; i++) { 3701 if(options && options.flat) { 3702 this.get_json(obj.children[i], options, flat); 3703 } 3704 else { 3705 tmp.children.push(this.get_json(obj.children[i], options)); 3706 } 3707 } 3708 } 3709 return options && options.flat ? flat : (obj.id === $.jstree.root ? tmp.children : tmp); 3710 }, 3711 /** 3712 * create a new node (do not confuse with load_node) 3713 * @name create_node([par, node, pos, callback, is_loaded]) 3714 * @param {mixed} par the parent node (to create a root node use either "#" (string) or `null`) 3715 * @param {mixed} node the data for the new node (a valid JSON object, or a simple string with the name) 3716 * @param {mixed} pos the index at which to insert the node, "first" and "last" are also supported, default is "last" 3717 * @param {Function} callback a function to be called once the node is created 3718 * @param {Boolean} is_loaded internal argument indicating if the parent node was succesfully loaded 3719 * @return {String} the ID of the newly create node 3720 * @trigger model.jstree, create_node.jstree 3721 */ 3722 create_node : function (par, node, pos, callback, is_loaded) { 3723 if(par === null) { par = $.jstree.root; } 3724 par = this.get_node(par); 3725 if(!par) { return false; } 3726 pos = pos === undefined ? "last" : pos; 3727 if(!pos.toString().match(/^(before|after)$/) && !is_loaded && !this.is_loaded(par)) { 3728 return this.load_node(par, function () { this.create_node(par, node, pos, callback, true); }); 3729 } 3730 if(!node) { node = { "text" : this.get_string('New node') }; } 3731 if(typeof node === "string") { 3732 node = { "text" : node }; 3733 } else { 3734 node = $.extend(true, {}, node); 3735 } 3736 if(node.text === undefined) { node.text = this.get_string('New node'); } 3737 var tmp, dpc, i, j; 3738 3739 if(par.id === $.jstree.root) { 3740 if(pos === "before") { pos = "first"; } 3741 if(pos === "after") { pos = "last"; } 3742 } 3743 switch(pos) { 3744 case "before": 3745 tmp = this.get_node(par.parent); 3746 pos = $.inArray(par.id, tmp.children); 3747 par = tmp; 3748 break; 3749 case "after" : 3750 tmp = this.get_node(par.parent); 3751 pos = $.inArray(par.id, tmp.children) + 1; 3752 par = tmp; 3753 break; 3754 case "inside": 3755 case "first": 3756 pos = 0; 3757 break; 3758 case "last": 3759 pos = par.children.length; 3760 break; 3761 default: 3762 if(!pos) { pos = 0; } 3763 break; 3764 } 3765 if(pos > par.children.length) { pos = par.children.length; } 3766 if(!node.id) { node.id = true; } 3767 if(!this.check("create_node", node, par, pos)) { 3768 this.settings.core.error.call(this, this._data.core.last_error); 3769 return false; 3770 } 3771 if(node.id === true) { delete node.id; } 3772 node = this._parse_model_from_json(node, par.id, par.parents.concat()); 3773 if(!node) { return false; } 3774 tmp = this.get_node(node); 3775 dpc = []; 3776 dpc.push(node); 3777 dpc = dpc.concat(tmp.children_d); 3778 this.trigger('model', { "nodes" : dpc, "parent" : par.id }); 3779 3780 par.children_d = par.children_d.concat(dpc); 3781 for(i = 0, j = par.parents.length; i < j; i++) { 3782 this._model.data[par.parents[i]].children_d = this._model.data[par.parents[i]].children_d.concat(dpc); 3783 } 3784 node = tmp; 3785 tmp = []; 3786 for(i = 0, j = par.children.length; i < j; i++) { 3787 tmp[i >= pos ? i+1 : i] = par.children[i]; 3788 } 3789 tmp[pos] = node.id; 3790 par.children = tmp; 3791 3792 this.redraw_node(par, true); 3793 /** 3794 * triggered when a node is created 3795 * @event 3796 * @name create_node.jstree 3797 * @param {Object} node 3798 * @param {String} parent the parent's ID 3799 * @param {Number} position the position of the new node among the parent's children 3800 */ 3801 this.trigger('create_node', { "node" : this.get_node(node), "parent" : par.id, "position" : pos }); 3802 if(callback) { callback.call(this, this.get_node(node)); } 3803 return node.id; 3804 }, 3805 /** 3806 * set the text value of a node 3807 * @name rename_node(obj, val) 3808 * @param {mixed} obj the node, you can pass an array to rename multiple nodes to the same name 3809 * @param {String} val the new text value 3810 * @return {Boolean} 3811 * @trigger rename_node.jstree 3812 */ 3813 rename_node : function (obj, val) { 3814 var t1, t2, old; 3815 if($.isArray(obj)) { 3816 obj = obj.slice(); 3817 for(t1 = 0, t2 = obj.length; t1 < t2; t1++) { 3818 this.rename_node(obj[t1], val); 3819 } 3820 return true; 3821 } 3822 obj = this.get_node(obj); 3823 if(!obj || obj.id === $.jstree.root) { return false; } 3824 old = obj.text; 3825 if(!this.check("rename_node", obj, this.get_parent(obj), val)) { 3826 this.settings.core.error.call(this, this._data.core.last_error); 3827 return false; 3828 } 3829 this.set_text(obj, val); // .apply(this, Array.prototype.slice.call(arguments)) 3830 /** 3831 * triggered when a node is renamed 3832 * @event 3833 * @name rename_node.jstree 3834 * @param {Object} node 3835 * @param {String} text the new value 3836 * @param {String} old the old value 3837 */ 3838 this.trigger('rename_node', { "node" : obj, "text" : val, "old" : old }); 3839 return true; 3840 }, 3841 /** 3842 * remove a node 3843 * @name delete_node(obj) 3844 * @param {mixed} obj the node, you can pass an array to delete multiple nodes 3845 * @return {Boolean} 3846 * @trigger delete_node.jstree, changed.jstree 3847 */ 3848 delete_node : function (obj) { 3849 var t1, t2, par, pos, tmp, i, j, k, l, c, top, lft; 3850 if($.isArray(obj)) { 3851 obj = obj.slice(); 3852 for(t1 = 0, t2 = obj.length; t1 < t2; t1++) { 3853 this.delete_node(obj[t1]); 3854 } 3855 return true; 3856 } 3857 obj = this.get_node(obj); 3858 if(!obj || obj.id === $.jstree.root) { return false; } 3859 par = this.get_node(obj.parent); 3860 pos = $.inArray(obj.id, par.children); 3861 c = false; 3862 if(!this.check("delete_node", obj, par, pos)) { 3863 this.settings.core.error.call(this, this._data.core.last_error); 3864 return false; 3865 } 3866 if(pos !== -1) { 3867 par.children = $.vakata.array_remove(par.children, pos); 3868 } 3869 tmp = obj.children_d.concat([]); 3870 tmp.push(obj.id); 3871 for(i = 0, j = obj.parents.length; i < j; i++) { 3872 this._model.data[obj.parents[i]].children_d = $.vakata.array_filter(this._model.data[obj.parents[i]].children_d, function (v) { 3873 return $.inArray(v, tmp) === -1; 3874 }); 3875 } 3876 for(k = 0, l = tmp.length; k < l; k++) { 3877 if(this._model.data[tmp[k]].state.selected) { 3878 c = true; 3879 break; 3880 } 3881 } 3882 if (c) { 3883 this._data.core.selected = $.vakata.array_filter(this._data.core.selected, function (v) { 3884 return $.inArray(v, tmp) === -1; 3885 }); 3886 } 3887 /** 3888 * triggered when a node is deleted 3889 * @event 3890 * @name delete_node.jstree 3891 * @param {Object} node 3892 * @param {String} parent the parent's ID 3893 */ 3894 this.trigger('delete_node', { "node" : obj, "parent" : par.id }); 3895 if(c) { 3896 this.trigger('changed', { 'action' : 'delete_node', 'node' : obj, 'selected' : this._data.core.selected, 'parent' : par.id }); 3897 } 3898 for(k = 0, l = tmp.length; k < l; k++) { 3899 delete this._model.data[tmp[k]]; 3900 } 3901 if($.inArray(this._data.core.focused, tmp) !== -1) { 3902 this._data.core.focused = null; 3903 top = this.element[0].scrollTop; 3904 lft = this.element[0].scrollLeft; 3905 if(par.id === $.jstree.root) { 3906 if (this._model.data[$.jstree.root].children[0]) { 3907 this.get_node(this._model.data[$.jstree.root].children[0], true).children('.jstree-anchor').focus(); 3908 } 3909 } 3910 else { 3911 this.get_node(par, true).children('.jstree-anchor').focus(); 3912 } 3913 this.element[0].scrollTop = top; 3914 this.element[0].scrollLeft = lft; 3915 } 3916 this.redraw_node(par, true); 3917 return true; 3918 }, 3919 /** 3920 * check if an operation is premitted on the tree. Used internally. 3921 * @private 3922 * @name check(chk, obj, par, pos) 3923 * @param {String} chk the operation to check, can be "create_node", "rename_node", "delete_node", "copy_node" or "move_node" 3924 * @param {mixed} obj the node 3925 * @param {mixed} par the parent 3926 * @param {mixed} pos the position to insert at, or if "rename_node" - the new name 3927 * @param {mixed} more some various additional information, for example if a "move_node" operations is triggered by DND this will be the hovered node 3928 * @return {Boolean} 3929 */ 3930 check : function (chk, obj, par, pos, more) { 3931 obj = obj && obj.id ? obj : this.get_node(obj); 3932 par = par && par.id ? par : this.get_node(par); 3933 var tmp = chk.match(/^move_node|copy_node|create_node$/i) ? par : obj, 3934 chc = this.settings.core.check_callback; 3935 if(chk === "move_node" || chk === "copy_node") { 3936 if((!more || !more.is_multi) && (obj.id === par.id || (chk === "move_node" && $.inArray(obj.id, par.children) === pos) || $.inArray(par.id, obj.children_d) !== -1)) { 3937 this._data.core.last_error = { 'error' : 'check', 'plugin' : 'core', 'id' : 'core_01', 'reason' : 'Moving parent inside child', 'data' : JSON.stringify({ 'chk' : chk, 'pos' : pos, 'obj' : obj && obj.id ? obj.id : false, 'par' : par && par.id ? par.id : false }) }; 3938 return false; 3939 } 3940 } 3941 if(tmp && tmp.data) { tmp = tmp.data; } 3942 if(tmp && tmp.functions && (tmp.functions[chk] === false || tmp.functions[chk] === true)) { 3943 if(tmp.functions[chk] === false) { 3944 this._data.core.last_error = { 'error' : 'check', 'plugin' : 'core', 'id' : 'core_02', 'reason' : 'Node data prevents function: ' + chk, 'data' : JSON.stringify({ 'chk' : chk, 'pos' : pos, 'obj' : obj && obj.id ? obj.id : false, 'par' : par && par.id ? par.id : false }) }; 3945 } 3946 return tmp.functions[chk]; 3947 } 3948 if(chc === false || ($.isFunction(chc) && chc.call(this, chk, obj, par, pos, more) === false) || (chc && chc[chk] === false)) { 3949 this._data.core.last_error = { 'error' : 'check', 'plugin' : 'core', 'id' : 'core_03', 'reason' : 'User config for core.check_callback prevents function: ' + chk, 'data' : JSON.stringify({ 'chk' : chk, 'pos' : pos, 'obj' : obj && obj.id ? obj.id : false, 'par' : par && par.id ? par.id : false }) }; 3950 return false; 3951 } 3952 return true; 3953 }, 3954 /** 3955 * get the last error 3956 * @name last_error() 3957 * @return {Object} 3958 */ 3959 last_error : function () { 3960 return this._data.core.last_error; 3961 }, 3962 /** 3963 * move a node to a new parent 3964 * @name move_node(obj, par [, pos, callback, is_loaded]) 3965 * @param {mixed} obj the node to move, pass an array to move multiple nodes 3966 * @param {mixed} par the new parent 3967 * @param {mixed} pos the position to insert at (besides integer values, "first" and "last" are supported, as well as "before" and "after"), defaults to integer `0` 3968 * @param {function} callback a function to call once the move is completed, receives 3 arguments - the node, the new parent and the position 3969 * @param {Boolean} is_loaded internal parameter indicating if the parent node has been loaded 3970 * @param {Boolean} skip_redraw internal parameter indicating if the tree should be redrawn 3971 * @param {Boolean} instance internal parameter indicating if the node comes from another instance 3972 * @trigger move_node.jstree 3973 */ 3974 move_node : function (obj, par, pos, callback, is_loaded, skip_redraw, origin) { 3975 var t1, t2, old_par, old_pos, new_par, old_ins, is_multi, dpc, tmp, i, j, k, l, p; 3976 3977 par = this.get_node(par); 3978 pos = pos === undefined ? 0 : pos; 3979 if(!par) { return false; } 3980 if(!pos.toString().match(/^(before|after)$/) && !is_loaded && !this.is_loaded(par)) { 3981 return this.load_node(par, function () { this.move_node(obj, par, pos, callback, true, false, origin); }); 3982 } 3983 3984 if($.isArray(obj)) { 3985 if(obj.length === 1) { 3986 obj = obj[0]; 3987 } 3988 else { 3989 //obj = obj.slice(); 3990 for(t1 = 0, t2 = obj.length; t1 < t2; t1++) { 3991 if((tmp = this.move_node(obj[t1], par, pos, callback, is_loaded, false, origin))) { 3992 par = tmp; 3993 pos = "after"; 3994 } 3995 } 3996 this.redraw(); 3997 return true; 3998 } 3999 } 4000 obj = obj && obj.id ? obj : this.get_node(obj); 4001 4002 if(!obj || obj.id === $.jstree.root) { return false; } 4003 4004 old_par = (obj.parent || $.jstree.root).toString(); 4005 new_par = (!pos.toString().match(/^(before|after)$/) || par.id === $.jstree.root) ? par : this.get_node(par.parent); 4006 old_ins = origin ? origin : (this._model.data[obj.id] ? this : $.jstree.reference(obj.id)); 4007 is_multi = !old_ins || !old_ins._id || (this._id !== old_ins._id); 4008 old_pos = old_ins && old_ins._id && old_par && old_ins._model.data[old_par] && old_ins._model.data[old_par].children ? $.inArray(obj.id, old_ins._model.data[old_par].children) : -1; 4009 if(old_ins && old_ins._id) { 4010 obj = old_ins._model.data[obj.id]; 4011 } 4012 4013 if(is_multi) { 4014 if((tmp = this.copy_node(obj, par, pos, callback, is_loaded, false, origin))) { 4015 if(old_ins) { old_ins.delete_node(obj); } 4016 return tmp; 4017 } 4018 return false; 4019 } 4020 //var m = this._model.data; 4021 if(par.id === $.jstree.root) { 4022 if(pos === "before") { pos = "first"; } 4023 if(pos === "after") { pos = "last"; } 4024 } 4025 switch(pos) { 4026 case "before": 4027 pos = $.inArray(par.id, new_par.children); 4028 break; 4029 case "after" : 4030 pos = $.inArray(par.id, new_par.children) + 1; 4031 break; 4032 case "inside": 4033 case "first": 4034 pos = 0; 4035 break; 4036 case "last": 4037 pos = new_par.children.length; 4038 break; 4039 default: 4040 if(!pos) { pos = 0; } 4041 break; 4042 } 4043 if(pos > new_par.children.length) { pos = new_par.children.length; } 4044 if(!this.check("move_node", obj, new_par, pos, { 'core' : true, 'origin' : origin, 'is_multi' : (old_ins && old_ins._id && old_ins._id !== this._id), 'is_foreign' : (!old_ins || !old_ins._id) })) { 4045 this.settings.core.error.call(this, this._data.core.last_error); 4046 return false; 4047 } 4048 if(obj.parent === new_par.id) { 4049 dpc = new_par.children.concat(); 4050 tmp = $.inArray(obj.id, dpc); 4051 if(tmp !== -1) { 4052 dpc = $.vakata.array_remove(dpc, tmp); 4053 if(pos > tmp) { pos--; } 4054 } 4055 tmp = []; 4056 for(i = 0, j = dpc.length; i < j; i++) { 4057 tmp[i >= pos ? i+1 : i] = dpc[i]; 4058 } 4059 tmp[pos] = obj.id; 4060 new_par.children = tmp; 4061 this._node_changed(new_par.id); 4062 this.redraw(new_par.id === $.jstree.root); 4063 } 4064 else { 4065 // clean old parent and up 4066 tmp = obj.children_d.concat(); 4067 tmp.push(obj.id); 4068 for(i = 0, j = obj.parents.length; i < j; i++) { 4069 dpc = []; 4070 p = old_ins._model.data[obj.parents[i]].children_d; 4071 for(k = 0, l = p.length; k < l; k++) { 4072 if($.inArray(p[k], tmp) === -1) { 4073 dpc.push(p[k]); 4074 } 4075 } 4076 old_ins._model.data[obj.parents[i]].children_d = dpc; 4077 } 4078 old_ins._model.data[old_par].children = $.vakata.array_remove_item(old_ins._model.data[old_par].children, obj.id); 4079 4080 // insert into new parent and up 4081 for(i = 0, j = new_par.parents.length; i < j; i++) { 4082 this._model.data[new_par.parents[i]].children_d = this._model.data[new_par.parents[i]].children_d.concat(tmp); 4083 } 4084 dpc = []; 4085 for(i = 0, j = new_par.children.length; i < j; i++) { 4086 dpc[i >= pos ? i+1 : i] = new_par.children[i]; 4087 } 4088 dpc[pos] = obj.id; 4089 new_par.children = dpc; 4090 new_par.children_d.push(obj.id); 4091 new_par.children_d = new_par.children_d.concat(obj.children_d); 4092 4093 // update object 4094 obj.parent = new_par.id; 4095 tmp = new_par.parents.concat(); 4096 tmp.unshift(new_par.id); 4097 p = obj.parents.length; 4098 obj.parents = tmp; 4099 4100 // update object children 4101 tmp = tmp.concat(); 4102 for(i = 0, j = obj.children_d.length; i < j; i++) { 4103 this._model.data[obj.children_d[i]].parents = this._model.data[obj.children_d[i]].parents.slice(0,p*-1); 4104 Array.prototype.push.apply(this._model.data[obj.children_d[i]].parents, tmp); 4105 } 4106 4107 if(old_par === $.jstree.root || new_par.id === $.jstree.root) { 4108 this._model.force_full_redraw = true; 4109 } 4110 if(!this._model.force_full_redraw) { 4111 this._node_changed(old_par); 4112 this._node_changed(new_par.id); 4113 } 4114 if(!skip_redraw) { 4115 this.redraw(); 4116 } 4117 } 4118 if(callback) { callback.call(this, obj, new_par, pos); } 4119 /** 4120 * triggered when a node is moved 4121 * @event 4122 * @name move_node.jstree 4123 * @param {Object} node 4124 * @param {String} parent the parent's ID 4125 * @param {Number} position the position of the node among the parent's children 4126 * @param {String} old_parent the old parent of the node 4127 * @param {Number} old_position the old position of the node 4128 * @param {Boolean} is_multi do the node and new parent belong to different instances 4129 * @param {jsTree} old_instance the instance the node came from 4130 * @param {jsTree} new_instance the instance of the new parent 4131 */ 4132 this.trigger('move_node', { "node" : obj, "parent" : new_par.id, "position" : pos, "old_parent" : old_par, "old_position" : old_pos, 'is_multi' : (old_ins && old_ins._id && old_ins._id !== this._id), 'is_foreign' : (!old_ins || !old_ins._id), 'old_instance' : old_ins, 'new_instance' : this }); 4133 return obj.id; 4134 }, 4135 /** 4136 * copy a node to a new parent 4137 * @name copy_node(obj, par [, pos, callback, is_loaded]) 4138 * @param {mixed} obj the node to copy, pass an array to copy multiple nodes 4139 * @param {mixed} par the new parent 4140 * @param {mixed} pos the position to insert at (besides integer values, "first" and "last" are supported, as well as "before" and "after"), defaults to integer `0` 4141 * @param {function} callback a function to call once the move is completed, receives 3 arguments - the node, the new parent and the position 4142 * @param {Boolean} is_loaded internal parameter indicating if the parent node has been loaded 4143 * @param {Boolean} skip_redraw internal parameter indicating if the tree should be redrawn 4144 * @param {Boolean} instance internal parameter indicating if the node comes from another instance 4145 * @trigger model.jstree copy_node.jstree 4146 */ 4147 copy_node : function (obj, par, pos, callback, is_loaded, skip_redraw, origin) { 4148 var t1, t2, dpc, tmp, i, j, node, old_par, new_par, old_ins, is_multi; 4149 4150 par = this.get_node(par); 4151 pos = pos === undefined ? 0 : pos; 4152 if(!par) { return false; } 4153 if(!pos.toString().match(/^(before|after)$/) && !is_loaded && !this.is_loaded(par)) { 4154 return this.load_node(par, function () { this.copy_node(obj, par, pos, callback, true, false, origin); }); 4155 } 4156 4157 if($.isArray(obj)) { 4158 if(obj.length === 1) { 4159 obj = obj[0]; 4160 } 4161 else { 4162 //obj = obj.slice(); 4163 for(t1 = 0, t2 = obj.length; t1 < t2; t1++) { 4164 if((tmp = this.copy_node(obj[t1], par, pos, callback, is_loaded, true, origin))) { 4165 par = tmp; 4166 pos = "after"; 4167 } 4168 } 4169 this.redraw(); 4170 return true; 4171 } 4172 } 4173 obj = obj && obj.id ? obj : this.get_node(obj); 4174 if(!obj || obj.id === $.jstree.root) { return false; } 4175 4176 old_par = (obj.parent || $.jstree.root).toString(); 4177 new_par = (!pos.toString().match(/^(before|after)$/) || par.id === $.jstree.root) ? par : this.get_node(par.parent); 4178 old_ins = origin ? origin : (this._model.data[obj.id] ? this : $.jstree.reference(obj.id)); 4179 is_multi = !old_ins || !old_ins._id || (this._id !== old_ins._id); 4180 4181 if(old_ins && old_ins._id) { 4182 obj = old_ins._model.data[obj.id]; 4183 } 4184 4185 if(par.id === $.jstree.root) { 4186 if(pos === "before") { pos = "first"; } 4187 if(pos === "after") { pos = "last"; } 4188 } 4189 switch(pos) { 4190 case "before": 4191 pos = $.inArray(par.id, new_par.children); 4192 break; 4193 case "after" : 4194 pos = $.inArray(par.id, new_par.children) + 1; 4195 break; 4196 case "inside": 4197 case "first": 4198 pos = 0; 4199 break; 4200 case "last": 4201 pos = new_par.children.length; 4202 break; 4203 default: 4204 if(!pos) { pos = 0; } 4205 break; 4206 } 4207 if(pos > new_par.children.length) { pos = new_par.children.length; } 4208 if(!this.check("copy_node", obj, new_par, pos, { 'core' : true, 'origin' : origin, 'is_multi' : (old_ins && old_ins._id && old_ins._id !== this._id), 'is_foreign' : (!old_ins || !old_ins._id) })) { 4209 this.settings.core.error.call(this, this._data.core.last_error); 4210 return false; 4211 } 4212 node = old_ins ? old_ins.get_json(obj, { no_id : true, no_data : true, no_state : true }) : obj; 4213 if(!node) { return false; } 4214 if(node.id === true) { delete node.id; } 4215 node = this._parse_model_from_json(node, new_par.id, new_par.parents.concat()); 4216 if(!node) { return false; } 4217 tmp = this.get_node(node); 4218 if(obj && obj.state && obj.state.loaded === false) { tmp.state.loaded = false; } 4219 dpc = []; 4220 dpc.push(node); 4221 dpc = dpc.concat(tmp.children_d); 4222 this.trigger('model', { "nodes" : dpc, "parent" : new_par.id }); 4223 4224 // insert into new parent and up 4225 for(i = 0, j = new_par.parents.length; i < j; i++) { 4226 this._model.data[new_par.parents[i]].children_d = this._model.data[new_par.parents[i]].children_d.concat(dpc); 4227 } 4228 dpc = []; 4229 for(i = 0, j = new_par.children.length; i < j; i++) { 4230 dpc[i >= pos ? i+1 : i] = new_par.children[i]; 4231 } 4232 dpc[pos] = tmp.id; 4233 new_par.children = dpc; 4234 new_par.children_d.push(tmp.id); 4235 new_par.children_d = new_par.children_d.concat(tmp.children_d); 4236 4237 if(new_par.id === $.jstree.root) { 4238 this._model.force_full_redraw = true; 4239 } 4240 if(!this._model.force_full_redraw) { 4241 this._node_changed(new_par.id); 4242 } 4243 if(!skip_redraw) { 4244 this.redraw(new_par.id === $.jstree.root); 4245 } 4246 if(callback) { callback.call(this, tmp, new_par, pos); } 4247 /** 4248 * triggered when a node is copied 4249 * @event 4250 * @name copy_node.jstree 4251 * @param {Object} node the copied node 4252 * @param {Object} original the original node 4253 * @param {String} parent the parent's ID 4254 * @param {Number} position the position of the node among the parent's children 4255 * @param {String} old_parent the old parent of the node 4256 * @param {Number} old_position the position of the original node 4257 * @param {Boolean} is_multi do the node and new parent belong to different instances 4258 * @param {jsTree} old_instance the instance the node came from 4259 * @param {jsTree} new_instance the instance of the new parent 4260 */ 4261 this.trigger('copy_node', { "node" : tmp, "original" : obj, "parent" : new_par.id, "position" : pos, "old_parent" : old_par, "old_position" : old_ins && old_ins._id && old_par && old_ins._model.data[old_par] && old_ins._model.data[old_par].children ? $.inArray(obj.id, old_ins._model.data[old_par].children) : -1,'is_multi' : (old_ins && old_ins._id && old_ins._id !== this._id), 'is_foreign' : (!old_ins || !old_ins._id), 'old_instance' : old_ins, 'new_instance' : this }); 4262 return tmp.id; 4263 }, 4264 /** 4265 * cut a node (a later call to `paste(obj)` would move the node) 4266 * @name cut(obj) 4267 * @param {mixed} obj multiple objects can be passed using an array 4268 * @trigger cut.jstree 4269 */ 4270 cut : function (obj) { 4271 if(!obj) { obj = this._data.core.selected.concat(); } 4272 if(!$.isArray(obj)) { obj = [obj]; } 4273 if(!obj.length) { return false; } 4274 var tmp = [], o, t1, t2; 4275 for(t1 = 0, t2 = obj.length; t1 < t2; t1++) { 4276 o = this.get_node(obj[t1]); 4277 if(o && o.id && o.id !== $.jstree.root) { tmp.push(o); } 4278 } 4279 if(!tmp.length) { return false; } 4280 ccp_node = tmp; 4281 ccp_inst = this; 4282 ccp_mode = 'move_node'; 4283 /** 4284 * triggered when nodes are added to the buffer for moving 4285 * @event 4286 * @name cut.jstree 4287 * @param {Array} node 4288 */ 4289 this.trigger('cut', { "node" : obj }); 4290 }, 4291 /** 4292 * copy a node (a later call to `paste(obj)` would copy the node) 4293 * @name copy(obj) 4294 * @param {mixed} obj multiple objects can be passed using an array 4295 * @trigger copy.jstree 4296 */ 4297 copy : function (obj) { 4298 if(!obj) { obj = this._data.core.selected.concat(); } 4299 if(!$.isArray(obj)) { obj = [obj]; } 4300 if(!obj.length) { return false; } 4301 var tmp = [], o, t1, t2; 4302 for(t1 = 0, t2 = obj.length; t1 < t2; t1++) { 4303 o = this.get_node(obj[t1]); 4304 if(o && o.id && o.id !== $.jstree.root) { tmp.push(o); } 4305 } 4306 if(!tmp.length) { return false; } 4307 ccp_node = tmp; 4308 ccp_inst = this; 4309 ccp_mode = 'copy_node'; 4310 /** 4311 * triggered when nodes are added to the buffer for copying 4312 * @event 4313 * @name copy.jstree 4314 * @param {Array} node 4315 */ 4316 this.trigger('copy', { "node" : obj }); 4317 }, 4318 /** 4319 * get the current buffer (any nodes that are waiting for a paste operation) 4320 * @name get_buffer() 4321 * @return {Object} an object consisting of `mode` ("copy_node" or "move_node"), `node` (an array of objects) and `inst` (the instance) 4322 */ 4323 get_buffer : function () { 4324 return { 'mode' : ccp_mode, 'node' : ccp_node, 'inst' : ccp_inst }; 4325 }, 4326 /** 4327 * check if there is something in the buffer to paste 4328 * @name can_paste() 4329 * @return {Boolean} 4330 */ 4331 can_paste : function () { 4332 return ccp_mode !== false && ccp_node !== false; // && ccp_inst._model.data[ccp_node]; 4333 }, 4334 /** 4335 * copy or move the previously cut or copied nodes to a new parent 4336 * @name paste(obj [, pos]) 4337 * @param {mixed} obj the new parent 4338 * @param {mixed} pos the position to insert at (besides integer, "first" and "last" are supported), defaults to integer `0` 4339 * @trigger paste.jstree 4340 */ 4341 paste : function (obj, pos) { 4342 obj = this.get_node(obj); 4343 if(!obj || !ccp_mode || !ccp_mode.match(/^(copy_node|move_node)$/) || !ccp_node) { return false; } 4344 if(this[ccp_mode](ccp_node, obj, pos, false, false, false, ccp_inst)) { 4345 /** 4346 * triggered when paste is invoked 4347 * @event 4348 * @name paste.jstree 4349 * @param {String} parent the ID of the receiving node 4350 * @param {Array} node the nodes in the buffer 4351 * @param {String} mode the performed operation - "copy_node" or "move_node" 4352 */ 4353 this.trigger('paste', { "parent" : obj.id, "node" : ccp_node, "mode" : ccp_mode }); 4354 } 4355 ccp_node = false; 4356 ccp_mode = false; 4357 ccp_inst = false; 4358 }, 4359 /** 4360 * clear the buffer of previously copied or cut nodes 4361 * @name clear_buffer() 4362 * @trigger clear_buffer.jstree 4363 */ 4364 clear_buffer : function () { 4365 ccp_node = false; 4366 ccp_mode = false; 4367 ccp_inst = false; 4368 /** 4369 * triggered when the copy / cut buffer is cleared 4370 * @event 4371 * @name clear_buffer.jstree 4372 */ 4373 this.trigger('clear_buffer'); 4374 }, 4375 /** 4376 * put a node in edit mode (input field to rename the node) 4377 * @name edit(obj [, default_text, callback]) 4378 * @param {mixed} obj 4379 * @param {String} default_text the text to populate the input with (if omitted or set to a non-string value the node's text value is used) 4380 * @param {Function} callback a function to be called once the text box is blurred, it is called in the instance's scope and receives the node, a status parameter (true if the rename is successful, false otherwise) and a boolean indicating if the user cancelled the edit. You can access the node's title using .text 4381 */ 4382 edit : function (obj, default_text, callback) { 4383 var rtl, w, a, s, t, h1, h2, fn, tmp, cancel = false; 4384 obj = this.get_node(obj); 4385 if(!obj) { return false; } 4386 if(!this.check("edit", obj, this.get_parent(obj))) { 4387 this.settings.core.error.call(this, this._data.core.last_error); 4388 return false; 4389 } 4390 tmp = obj; 4391 default_text = typeof default_text === 'string' ? default_text : obj.text; 4392 this.set_text(obj, ""); 4393 obj = this._open_to(obj); 4394 tmp.text = default_text; 4395 4396 rtl = this._data.core.rtl; 4397 w = this.element.width(); 4398 this._data.core.focused = tmp.id; 4399 a = obj.children('.jstree-anchor').focus(); 4400 s = $('<span>'); 4401 /*! 4402 oi = obj.children("i:visible"), 4403 ai = a.children("i:visible"), 4404 w1 = oi.width() * oi.length, 4405 w2 = ai.width() * ai.length, 4406 */ 4407 t = default_text; 4408 h1 = $("<"+"div />", { css : { "position" : "absolute", "top" : "-200px", "left" : (rtl ? "0px" : "-1000px"), "visibility" : "hidden" } }).appendTo("body"); 4409 h2 = $("<"+"input />", { 4410 "value" : t, 4411 "class" : "jstree-rename-input", 4412 // "size" : t.length, 4413 "css" : { 4414 "padding" : "0", 4415 "border" : "1px solid silver", 4416 "box-sizing" : "border-box", 4417 "display" : "inline-block", 4418 "height" : (this._data.core.li_height) + "px", 4419 "lineHeight" : (this._data.core.li_height) + "px", 4420 "width" : "150px" // will be set a bit further down 4421 }, 4422 "blur" : $.proxy(function (e) { 4423 e.stopImmediatePropagation(); 4424 e.preventDefault(); 4425 var i = s.children(".jstree-rename-input"), 4426 v = i.val(), 4427 f = this.settings.core.force_text, 4428 nv; 4429 if(v === "") { v = t; } 4430 h1.remove(); 4431 s.replaceWith(a); 4432 s.remove(); 4433 t = f ? t : $('<div></div>').append($.parseHTML(t)).html(); 4434 this.set_text(obj, t); 4435 nv = !!this.rename_node(obj, f ? $('<div></div>').text(v).text() : $('<div></div>').append($.parseHTML(v)).html()); 4436 if(!nv) { 4437 this.set_text(obj, t); // move this up? and fix #483 4438 } 4439 this._data.core.focused = tmp.id; 4440 setTimeout($.proxy(function () { 4441 var node = this.get_node(tmp.id, true); 4442 if(node.length) { 4443 this._data.core.focused = tmp.id; 4444 node.children('.jstree-anchor').focus(); 4445 } 4446 }, this), 0); 4447 if(callback) { 4448 callback.call(this, tmp, nv, cancel); 4449 } 4450 h2 = null; 4451 }, this), 4452 "keydown" : function (e) { 4453 var key = e.which; 4454 if(key === 27) { 4455 cancel = true; 4456 this.value = t; 4457 } 4458 if(key === 27 || key === 13 || key === 37 || key === 38 || key === 39 || key === 40 || key === 32) { 4459 e.stopImmediatePropagation(); 4460 } 4461 if(key === 27 || key === 13) { 4462 e.preventDefault(); 4463 this.blur(); 4464 } 4465 }, 4466 "click" : function (e) { e.stopImmediatePropagation(); }, 4467 "mousedown" : function (e) { e.stopImmediatePropagation(); }, 4468 "keyup" : function (e) { 4469 h2.width(Math.min(h1.text("pW" + this.value).width(),w)); 4470 }, 4471 "keypress" : function(e) { 4472 if(e.which === 13) { return false; } 4473 } 4474 }); 4475 fn = { 4476 fontFamily : a.css('fontFamily') || '', 4477 fontSize : a.css('fontSize') || '', 4478 fontWeight : a.css('fontWeight') || '', 4479 fontStyle : a.css('fontStyle') || '', 4480 fontStretch : a.css('fontStretch') || '', 4481 fontVariant : a.css('fontVariant') || '', 4482 letterSpacing : a.css('letterSpacing') || '', 4483 wordSpacing : a.css('wordSpacing') || '' 4484 }; 4485 s.attr('class', a.attr('class')).append(a.contents().clone()).append(h2); 4486 a.replaceWith(s); 4487 h1.css(fn); 4488 h2.css(fn).width(Math.min(h1.text("pW" + h2[0].value).width(),w))[0].select(); 4489 $(document).one('mousedown.jstree touchstart.jstree dnd_start.vakata', function (e) { 4490 if (h2 && e.target !== h2) { 4491 $(h2).blur(); 4492 } 4493 }); 4494 }, 4495 4496 4497 /** 4498 * changes the theme 4499 * @name set_theme(theme_name [, theme_url]) 4500 * @param {String} theme_name the name of the new theme to apply 4501 * @param {mixed} theme_url the location of the CSS file for this theme. Omit or set to `false` if you manually included the file. Set to `true` to autoload from the `core.themes.dir` directory. 4502 * @trigger set_theme.jstree 4503 */ 4504 set_theme : function (theme_name, theme_url) { 4505 if(!theme_name) { return false; } 4506 if(theme_url === true) { 4507 var dir = this.settings.core.themes.dir; 4508 if(!dir) { dir = $.jstree.path + '/themes'; } 4509 theme_url = dir + '/' + theme_name + '/style.css'; 4510 } 4511 if(theme_url && $.inArray(theme_url, themes_loaded) === -1) { 4512 $('head').append('<'+'link rel="stylesheet" href="' + theme_url + '" type="text/css" />'); 4513 themes_loaded.push(theme_url); 4514 } 4515 if(this._data.core.themes.name) { 4516 this.element.removeClass('jstree-' + this._data.core.themes.name); 4517 } 4518 this._data.core.themes.name = theme_name; 4519 this.element.addClass('jstree-' + theme_name); 4520 this.element[this.settings.core.themes.responsive ? 'addClass' : 'removeClass' ]('jstree-' + theme_name + '-responsive'); 4521 /** 4522 * triggered when a theme is set 4523 * @event 4524 * @name set_theme.jstree 4525 * @param {String} theme the new theme 4526 */ 4527 this.trigger('set_theme', { 'theme' : theme_name }); 4528 }, 4529 /** 4530 * gets the name of the currently applied theme name 4531 * @name get_theme() 4532 * @return {String} 4533 */ 4534 get_theme : function () { return this._data.core.themes.name; }, 4535 /** 4536 * changes the theme variant (if the theme has variants) 4537 * @name set_theme_variant(variant_name) 4538 * @param {String|Boolean} variant_name the variant to apply (if `false` is used the current variant is removed) 4539 */ 4540 set_theme_variant : function (variant_name) { 4541 if(this._data.core.themes.variant) { 4542 this.element.removeClass('jstree-' + this._data.core.themes.name + '-' + this._data.core.themes.variant); 4543 } 4544 this._data.core.themes.variant = variant_name; 4545 if(variant_name) { 4546 this.element.addClass('jstree-' + this._data.core.themes.name + '-' + this._data.core.themes.variant); 4547 } 4548 }, 4549 /** 4550 * gets the name of the currently applied theme variant 4551 * @name get_theme() 4552 * @return {String} 4553 */ 4554 get_theme_variant : function () { return this._data.core.themes.variant; }, 4555 /** 4556 * shows a striped background on the container (if the theme supports it) 4557 * @name show_stripes() 4558 */ 4559 show_stripes : function () { 4560 this._data.core.themes.stripes = true; 4561 this.get_container_ul().addClass("jstree-striped"); 4562 /** 4563 * triggered when stripes are shown 4564 * @event 4565 * @name show_stripes.jstree 4566 */ 4567 this.trigger('show_stripes'); 4568 }, 4569 /** 4570 * hides the striped background on the container 4571 * @name hide_stripes() 4572 */ 4573 hide_stripes : function () { 4574 this._data.core.themes.stripes = false; 4575 this.get_container_ul().removeClass("jstree-striped"); 4576 /** 4577 * triggered when stripes are hidden 4578 * @event 4579 * @name hide_stripes.jstree 4580 */ 4581 this.trigger('hide_stripes'); 4582 }, 4583 /** 4584 * toggles the striped background on the container 4585 * @name toggle_stripes() 4586 */ 4587 toggle_stripes : function () { if(this._data.core.themes.stripes) { this.hide_stripes(); } else { this.show_stripes(); } }, 4588 /** 4589 * shows the connecting dots (if the theme supports it) 4590 * @name show_dots() 4591 */ 4592 show_dots : function () { 4593 this._data.core.themes.dots = true; 4594 this.get_container_ul().removeClass("jstree-no-dots"); 4595 /** 4596 * triggered when dots are shown 4597 * @event 4598 * @name show_dots.jstree 4599 */ 4600 this.trigger('show_dots'); 4601 }, 4602 /** 4603 * hides the connecting dots 4604 * @name hide_dots() 4605 */ 4606 hide_dots : function () { 4607 this._data.core.themes.dots = false; 4608 this.get_container_ul().addClass("jstree-no-dots"); 4609 /** 4610 * triggered when dots are hidden 4611 * @event 4612 * @name hide_dots.jstree 4613 */ 4614 this.trigger('hide_dots'); 4615 }, 4616 /** 4617 * toggles the connecting dots 4618 * @name toggle_dots() 4619 */ 4620 toggle_dots : function () { if(this._data.core.themes.dots) { this.hide_dots(); } else { this.show_dots(); } }, 4621 /** 4622 * show the node icons 4623 * @name show_icons() 4624 */ 4625 show_icons : function () { 4626 this._data.core.themes.icons = true; 4627 this.get_container_ul().removeClass("jstree-no-icons"); 4628 /** 4629 * triggered when icons are shown 4630 * @event 4631 * @name show_icons.jstree 4632 */ 4633 this.trigger('show_icons'); 4634 }, 4635 /** 4636 * hide the node icons 4637 * @name hide_icons() 4638 */ 4639 hide_icons : function () { 4640 this._data.core.themes.icons = false; 4641 this.get_container_ul().addClass("jstree-no-icons"); 4642 /** 4643 * triggered when icons are hidden 4644 * @event 4645 * @name hide_icons.jstree 4646 */ 4647 this.trigger('hide_icons'); 4648 }, 4649 /** 4650 * toggle the node icons 4651 * @name toggle_icons() 4652 */ 4653 toggle_icons : function () { if(this._data.core.themes.icons) { this.hide_icons(); } else { this.show_icons(); } }, 4654 /** 4655 * show the node ellipsis 4656 * @name show_icons() 4657 */ 4658 show_ellipsis : function () { 4659 this._data.core.themes.ellipsis = true; 4660 this.get_container_ul().addClass("jstree-ellipsis"); 4661 /** 4662 * triggered when ellisis is shown 4663 * @event 4664 * @name show_ellipsis.jstree 4665 */ 4666 this.trigger('show_ellipsis'); 4667 }, 4668 /** 4669 * hide the node ellipsis 4670 * @name hide_ellipsis() 4671 */ 4672 hide_ellipsis : function () { 4673 this._data.core.themes.ellipsis = false; 4674 this.get_container_ul().removeClass("jstree-ellipsis"); 4675 /** 4676 * triggered when ellisis is hidden 4677 * @event 4678 * @name hide_ellipsis.jstree 4679 */ 4680 this.trigger('hide_ellipsis'); 4681 }, 4682 /** 4683 * toggle the node ellipsis 4684 * @name toggle_icons() 4685 */ 4686 toggle_ellipsis : function () { if(this._data.core.themes.ellipsis) { this.hide_ellipsis(); } else { this.show_ellipsis(); } }, 4687 /** 4688 * set the node icon for a node 4689 * @name set_icon(obj, icon) 4690 * @param {mixed} obj 4691 * @param {String} icon the new icon - can be a path to an icon or a className, if using an image that is in the current directory use a `./` prefix, otherwise it will be detected as a class 4692 */ 4693 set_icon : function (obj, icon) { 4694 var t1, t2, dom, old; 4695 if($.isArray(obj)) { 4696 obj = obj.slice(); 4697 for(t1 = 0, t2 = obj.length; t1 < t2; t1++) { 4698 this.set_icon(obj[t1], icon); 4699 } 4700 return true; 4701 } 4702 obj = this.get_node(obj); 4703 if(!obj || obj.id === $.jstree.root) { return false; } 4704 old = obj.icon; 4705 obj.icon = icon === true || icon === null || icon === undefined || icon === '' ? true : icon; 4706 dom = this.get_node(obj, true).children(".jstree-anchor").children(".jstree-themeicon"); 4707 if(icon === false) { 4708 this.hide_icon(obj); 4709 } 4710 else if(icon === true || icon === null || icon === undefined || icon === '') { 4711 dom.removeClass('jstree-themeicon-custom ' + old).css("background","").removeAttr("rel"); 4712 if(old === false) { this.show_icon(obj); } 4713 } 4714 else if(icon.indexOf("/") === -1 && icon.indexOf(".") === -1) { 4715 dom.removeClass(old).css("background",""); 4716 dom.addClass(icon + ' jstree-themeicon-custom').attr("rel",icon); 4717 if(old === false) { this.show_icon(obj); } 4718 } 4719 else { 4720 dom.removeClass(old).css("background",""); 4721 dom.addClass('jstree-themeicon-custom').css("background", "url('" + icon + "') center center no-repeat").attr("rel",icon); 4722 if(old === false) { this.show_icon(obj); } 4723 } 4724 return true; 4725 }, 4726 /** 4727 * get the node icon for a node 4728 * @name get_icon(obj) 4729 * @param {mixed} obj 4730 * @return {String} 4731 */ 4732 get_icon : function (obj) { 4733 obj = this.get_node(obj); 4734 return (!obj || obj.id === $.jstree.root) ? false : obj.icon; 4735 }, 4736 /** 4737 * hide the icon on an individual node 4738 * @name hide_icon(obj) 4739 * @param {mixed} obj 4740 */ 4741 hide_icon : function (obj) { 4742 var t1, t2; 4743 if($.isArray(obj)) { 4744 obj = obj.slice(); 4745 for(t1 = 0, t2 = obj.length; t1 < t2; t1++) { 4746 this.hide_icon(obj[t1]); 4747 } 4748 return true; 4749 } 4750 obj = this.get_node(obj); 4751 if(!obj || obj === $.jstree.root) { return false; } 4752 obj.icon = false; 4753 this.get_node(obj, true).children(".jstree-anchor").children(".jstree-themeicon").addClass('jstree-themeicon-hidden'); 4754 return true; 4755 }, 4756 /** 4757 * show the icon on an individual node 4758 * @name show_icon(obj) 4759 * @param {mixed} obj 4760 */ 4761 show_icon : function (obj) { 4762 var t1, t2, dom; 4763 if($.isArray(obj)) { 4764 obj = obj.slice(); 4765 for(t1 = 0, t2 = obj.length; t1 < t2; t1++) { 4766 this.show_icon(obj[t1]); 4767 } 4768 return true; 4769 } 4770 obj = this.get_node(obj); 4771 if(!obj || obj === $.jstree.root) { return false; } 4772 dom = this.get_node(obj, true); 4773 obj.icon = dom.length ? dom.children(".jstree-anchor").children(".jstree-themeicon").attr('rel') : true; 4774 if(!obj.icon) { obj.icon = true; } 4775 dom.children(".jstree-anchor").children(".jstree-themeicon").removeClass('jstree-themeicon-hidden'); 4776 return true; 4777 } 4778 }; 4779 4780 // helpers 4781 $.vakata = {}; 4782 // collect attributes 4783 $.vakata.attributes = function(node, with_values) { 4784 node = $(node)[0]; 4785 var attr = with_values ? {} : []; 4786 if(node && node.attributes) { 4787 $.each(node.attributes, function (i, v) { 4788 if($.inArray(v.name.toLowerCase(),['style','contenteditable','hasfocus','tabindex']) !== -1) { return; } 4789 if(v.value !== null && $.trim(v.value) !== '') { 4790 if(with_values) { attr[v.name] = v.value; } 4791 else { attr.push(v.name); } 4792 } 4793 }); 4794 } 4795 return attr; 4796 }; 4797 $.vakata.array_unique = function(array) { 4798 var a = [], i, j, l, o = {}; 4799 for(i = 0, l = array.length; i < l; i++) { 4800 if(o[array[i]] === undefined) { 4801 a.push(array[i]); 4802 o[array[i]] = true; 4803 } 4804 } 4805 return a; 4806 }; 4807 // remove item from array 4808 $.vakata.array_remove = function(array, from) { 4809 array.splice(from, 1); 4810 return array; 4811 //var rest = array.slice((to || from) + 1 || array.length); 4812 //array.length = from < 0 ? array.length + from : from; 4813 //array.push.apply(array, rest); 4814 //return array; 4815 }; 4816 // remove item from array 4817 $.vakata.array_remove_item = function(array, item) { 4818 var tmp = $.inArray(item, array); 4819 return tmp !== -1 ? $.vakata.array_remove(array, tmp) : array; 4820 }; 4821 $.vakata.array_filter = function(c,a,b,d,e) { 4822 if (c.filter) { 4823 return c.filter(a, b); 4824 } 4825 d=[]; 4826 for (e in c) { 4827 if (~~e+''===e+'' && e>=0 && a.call(b,c[e],+e,c)) { 4828 d.push(c[e]); 4829 } 4830 } 4831 return d; 4832 }; 4833 4834 4835/** 4836 * ### Changed plugin 4837 * 4838 * This plugin adds more information to the `changed.jstree` event. The new data is contained in the `changed` event data property, and contains a lists of `selected` and `deselected` nodes. 4839 */ 4840 4841 $.jstree.plugins.changed = function (options, parent) { 4842 var last = []; 4843 this.trigger = function (ev, data) { 4844 var i, j; 4845 if(!data) { 4846 data = {}; 4847 } 4848 if(ev.replace('.jstree','') === 'changed') { 4849 data.changed = { selected : [], deselected : [] }; 4850 var tmp = {}; 4851 for(i = 0, j = last.length; i < j; i++) { 4852 tmp[last[i]] = 1; 4853 } 4854 for(i = 0, j = data.selected.length; i < j; i++) { 4855 if(!tmp[data.selected[i]]) { 4856 data.changed.selected.push(data.selected[i]); 4857 } 4858 else { 4859 tmp[data.selected[i]] = 2; 4860 } 4861 } 4862 for(i = 0, j = last.length; i < j; i++) { 4863 if(tmp[last[i]] === 1) { 4864 data.changed.deselected.push(last[i]); 4865 } 4866 } 4867 last = data.selected.slice(); 4868 } 4869 /** 4870 * triggered when selection changes (the "changed" plugin enhances the original event with more data) 4871 * @event 4872 * @name changed.jstree 4873 * @param {Object} node 4874 * @param {Object} action the action that caused the selection to change 4875 * @param {Array} selected the current selection 4876 * @param {Object}
4876 changed an object containing two properties `selected` and `deselected` - both arrays of node IDs, which were selected or deselected since the last changed event 4877 * @param {Object} event the event (if any) that triggered this changed event 4878 * @plugin changed 4879 */ 4880 parent.trigger.call(this, ev, data); 4881 }; 4882 this.refresh = function (skip_loading, forget_state) { 4883 last = []; 4884 return parent.refresh.apply(this, arguments); 4885 }; 4886 }; 4887 4888/** 4889 * ### Checkbox plugin 4890 * 4891 * This plugin renders checkbox icons in front of each node, making multiple selection much easier. 4892 * It also supports tri-state behavior, meaning that if a node has a few of its children checked it will be rendered as undetermined, and state will be propagated up. 4893 */ 4894 4895 var _i = document.createElement('I'); 4896 _i.className = 'jstree-icon jstree-checkbox'; 4897 _i.setAttribute('role', 'presentation'); 4898 /** 4899 * stores all defaults for the checkbox plugin 4900 * @name $.jstree.defaults.checkbox 4901 * @plugin checkbox 4902 */ 4903 $.jstree.defaults.checkbox = { 4904 /** 4905 * a boolean indicating if checkboxes should be visible (can be changed at a later time using `show_checkboxes()` and `hide_checkboxes`). Defaults to `true`. 4906 * @name $.jstree.defaults.checkbox.visible 4907 * @plugin checkbox 4908 */ 4909 visible : true, 4910 /** 4911 * a boolean indicating if checkboxes should cascade down and have an undetermined state. Defaults to `true`. 4912 * @name $.jstree.defaults.checkbox.three_state 4913 * @plugin checkbox 4914 */ 4915 three_state : true, 4916 /** 4917 * a boolean indicating if clicking anywhere on the node should act as clicking on the checkbox. Defaults to `true`. 4918 * @name $.jstree.defaults.checkbox.whole_node 4919 * @plugin checkbox 4920 */ 4921 whole_node : true, 4922 /** 4923 * a boolean indicating if the selected style of a node should be kept, or removed. Defaults to `true`. 4924 * @name $.jstree.defaults.checkbox.keep_selected_style 4925 * @plugin checkbox 4926 */ 4927 keep_selected_style : true, 4928 /** 4929 * This setting controls how cascading and undetermined nodes are applied. 4930 * If 'up' is in the string - cascading up is enabled, if 'down' is in the string - cascading down is enabled, if 'undetermined' is in the string - undetermined nodes will be used. 4931 * If `three_state` is set to `true` this setting is automatically set to 'up+down+undetermined'. Defaults to ''. 4932 * @name $.jstree.defaults.checkbox.cascade 4933 * @plugin checkbox 4934 */ 4935 cascade : '', 4936 /** 4937 * This setting controls if checkbox are bound to the general tree selection or to an internal array maintained by the checkbox plugin. Defaults to `true`, only set to `false` if you know exactly what you are doing. 4938 * @name $.jstree.defaults.checkbox.tie_selection 4939 * @plugin checkbox 4940 */ 4941 tie_selection : true, 4942 4943 /** 4944 * This setting controls if cascading down affects disabled checkboxes 4945 * @name $.jstree.defaults.checkbox.cascade_to_disabled 4946 * @plugin checkbox 4947 */ 4948 cascade_to_disabled : true, 4949 4950 /** 4951 * This setting controls if cascading down affects hidden checkboxes 4952 * @name $.jstree.defaults.checkbox.cascade_to_hidden 4953 * @plugin checkbox 4954 */ 4955 cascade_to_hidden : true 4956 }; 4957 $.jstree.plugins.checkbox = function (options, parent) { 4958 this.bind = function () { 4959 parent.bind.call(this); 4960 this._data.checkbox.uto = false; 4961 this._data.checkbox.selected = []; 4962 if(this.settings.checkbox.three_state) { 4963 this.settings.checkbox.cascade = 'up+down+undetermined'; 4964 } 4965 this.element 4966 .on("init.jstree", $.proxy(function () { 4967 this._data.checkbox.visible = this.settings.checkbox.visible; 4968 if(!this.settings.checkbox.keep_selected_style) { 4969 this.element.addClass('jstree-checkbox-no-clicked'); 4970 } 4971 if(this.settings.checkbox.tie_selection) { 4972 this.element.addClass('jstree-checkbox-selection'); 4973 } 4974 }, this)) 4975 .on("loading.jstree", $.proxy(function () { 4976 this[ this._data.checkbox.visible ? 'show_checkboxes' : 'hide_checkboxes' ](); 4977 }, this)); 4978 if(this.settings.checkbox.cascade.indexOf('undetermined') !== -1) { 4979 this.element 4980 .on('changed.jstree uncheck_node.jstree check_node.jstree uncheck_all.
4980jstree check_all.jstree move_node.jstree copy_node.jstree redraw.jstree open_node.jstree', $.proxy(function () { 4981 // only if undetermined is in setting 4982 if(this._data.checkbox.uto) { clearTimeout(this._data.checkbox.uto); } 4983 this._data.checkbox.uto = setTimeout($.proxy(this._undetermined, this), 50); 4984 }, this)); 4985 } 4986 if(!this.settings.checkbox.tie_selection) { 4987 this.element 4988 .on('model.jstree', $.proxy(function (e, data) { 4989 var m = this._model.data, 4990 p = m[data.parent], 4991 dpc = data.nodes, 4992 i, j; 4993 for(i = 0, j = dpc.length; i < j; i++) { 4994 m[dpc[i]].state.checked = m[dpc[i]].state.checked || (m[dpc[i]].original && m[dpc[i]].original.state && m[dpc[i]].original.state.checked); 4995 if(m[dpc[i]].state.checked) { 4996 this._data.checkbox.selected.push(dpc[i]); 4997 } 4998 } 4999 }, this)); 5000 } 5001 if(this.settings.checkbox.cascade.indexOf('up') !== -1 || this.settings.checkbox.cascade.indexOf('down') !== -1) { 5002 this.element 5003 .on('model.jstree', $.proxy(function (e, data) { 5004 var m = this._model.data, 5005 p = m[data.parent], 5006 dpc = data.nodes, 5007 chd = [], 5008 c, i, j, k, l, tmp, s = this.settings.checkbox.cascade, t = this.settings.checkbox.tie_selection; 5009 5010 if(s.indexOf('down') !== -1) { 5011 // apply down 5012 if(p.state[ t ? 'selected' : 'checked' ]) { 5013 for(i = 0, j = dpc.length; i < j; i++) { 5014 m[dpc[i]].state[ t ? 'selected' : 'checked' ] = true; 5015 } 5016 5017 this._data[ t ? 'core' : 'checkbox' ].selected = this._data[ t ? 'core' : 'checkbox' ].selected.concat(dpc); 5018 } 5019 else { 5020 for(i = 0, j = dpc.length; i < j; i++) { 5021 if(m[dpc[i]].state[ t ? 'selected' : 'checked' ]) { 5022 for(k = 0, l = m[dpc[i]].children_d.length; k < l; k++) { 5023 m[m[dpc[i]].children_d[k]].state[ t ? 'selected' : 'checked' ] = true; 5024 } 5025 this._data[ t ? 'core' : 'checkbox' ].selected = this._data[ t ? 'core' : 'checkbox' ].selected.concat(m[dpc[i]].children_d); 5026 } 5027 } 5028 } 5029 } 5030 5031 if(s.indexOf('up') !== -1) { 5032 // apply up 5033 for(i = 0, j = p.children_d.length; i < j; i++) { 5034 if(!m[p.children_d[i]].children.length) { 5035 chd.push(m[p.children_d[i]].parent); 5036 } 5037 } 5038 chd = $.vakata.array_unique(chd); 5039 for(k = 0, l = chd.length; k < l; k++) { 5040 p = m[chd[k]]; 5041 while(p && p.id !== $.jstree.root) { 5042 c = 0; 5043 for(i = 0, j = p.children.length; i < j; i++) { 5044 c += m[p.children[i]].state[ t ? 'selected' : 'checked' ]; 5045 } 5046 if(c === j) { 5047 p.state[ t ? 'selected' : 'checked' ] = true; 5048 this._data[ t ? 'core' : 'checkbox' ].selected.push(p.id); 5049 tmp = this.get_node(p, true); 5050 if(tmp && tmp.length) { 5051 tmp.attr('aria-selected', true).children('.jstree-anchor').addClass( t ? 'jstree-clicked' : 'jstree-checked'); 5052 } 5053 } 5054 else { 5055 break; 5056 } 5057 p = this.get_node(p.parent); 5058 } 5059 } 5060 } 5061 5062 this._data[ t ? 'core' : 'checkbox' ].selected = $.vakata.array_unique(this._data[ t ? 'core' : 'checkbox' ].selected); 5063 }, this)) 5064 .on(this.settings.checkbox.tie_selection ? 'select_node.jstree' : 'check_node.jstree', $.proxy(function (e, data) { 5065 var self = this, 5066 obj = data.node, 5067 m = this._model.data, 5068 par = this.get_node(obj.parent), 5069 i, j, c, tmp, s = this.settings.checkbox.cascade, t = this.settings.checkbox.tie_selection, 5070 sel = {}, cur = this._data[ t ? 'core' : 'checkbox' ].selected; 5071 5072 for (i = 0, j = cur.length; i < j; i++) { 5073 sel[cur[i]] = true; 5074 } 5075 5076 // apply down 5077 if(s.indexOf('down') !== -1) { 5078 //this._data[ t ? 'core' : 'checkbox' ].selected = $.vakata.array_unique(this._data[ t ? 'core' : 'checkbox' ].selected.concat(obj.children_d)); 5079 var selectedIds = this._cascade_new_checked_state(obj.id, true); 5080 obj.children_d.concat(obj.id).forEac
5080h(function(id) { 5081 if (selectedIds.indexOf(id) > -1) { 5082 sel[id] = true; 5083 } 5084 else { 5085 delete sel[id]; 5086 } 5087 }); 5088 } 5089 5090 // apply up 5091 if(s.indexOf('up') !== -1) { 5092 while(par && par.id !== $.jstree.root) { 5093 c = 0; 5094 for(i = 0, j = par.children.length; i < j; i++) { 5095 c += m[par.children[i]].state[ t ? 'selected' : 'checked' ]; 5096 } 5097 if(c === j) { 5098 par.state[ t ? 'selected' : 'checked' ] = true; 5099 sel[par.id] = true; 5100 //this._data[ t ? 'core' : 'checkbox' ].selected.push(par.id); 5101 tmp = this.get_node(par, true); 5102 if(tmp && tmp.length) { 5103 tmp.attr('aria-selected', true).children('.jstree-anchor').addClass(t ? 'jstree-clicked' : 'jstree-checked'); 5104 } 5105 } 5106 else { 5107 break; 5108 } 5109 par = this.get_node(par.parent); 5110 } 5111 } 5112 5113 cur = []; 5114 for (i in sel) { 5115 if (sel.hasOwnProperty(i)) { 5116 cur.push(i); 5117 } 5118 } 5119 this._data[ t ? 'core' : 'checkbox' ].selected = cur; 5120 }, this)) 5121 .on(this.settings.checkbox.tie_selection ? 'deselect_all.jstree' : 'uncheck_all.jstree', $.proxy(function (e, data) { 5122 var obj = this.get_node($.jstree.root), 5123 m = this._model.data, 5124 i, j, tmp; 5125 for(i = 0, j = obj.children_d.length; i < j; i++) { 5126 tmp = m[obj.children_d[i]]; 5127 if(tmp && tmp.original && tmp.original.state && tmp.original.state.undetermined) { 5128 tmp.original.state.undetermined = false; 5129 } 5130 } 5131 }, this)) 5132 .on(this.settings.checkbox.tie_selection ? 'deselect_node.jstree' : 'uncheck_node.jstree', $.proxy(function (e, data) { 5133 var self = this, 5134 obj = data.node, 5135 dom = this.get_node(obj, true), 5136 i, j, tmp, s = this.settings.checkbox.cascade, t = this.settings.checkbox.tie_selection, 5137 cur = this._data[ t ? 'core' : 'checkbox' ].selected, sel = {}, 5138 stillSelectedIds = [], 5139 allIds = obj.children_d.concat(obj.id); 5140 5141 // apply down 5142 if(s.indexOf('down') !== -1) { 5143 var selectedIds = this._cascade_new_checked_state(obj.id, false); 5144 5145 cur = cur.filter(function(id) { 5146 return allIds.indexOf(id) === -1 || selectedIds.indexOf(id) > -1; 5147 });
vendor: 12,545 bytes, lines 5148-5492
5148 } 5149 5150 // only apply up if cascade up is enabled and if this node is not selected 5151 // (if all child nodes are disabled and cascade_to_disabled === false then this node will till be selected). 5152 if(s.indexOf('up') !== -1 && cur.indexOf(obj.id) === -1) { 5153 for(i = 0, j = obj.parents.length; i < j; i++) { 5154 tmp = this._model.data[obj.parents[i]]; 5155 tmp.state[ t ? 'selected' : 'checked' ] = false; 5156 if(tmp && tmp.original && tmp.original.state && tmp.original.state.undetermined) { 5157 tmp.original.state.undetermined = false; 5158 } 5159 tmp = this.get_node(obj.parents[i], true); 5160 if(tmp && tmp.length) { 5161 tmp.attr('aria-selected', false).children('.jstree-anchor').removeClass(t ? 'jstree-clicked' : 'jstree-checked'); 5162 } 5163 } 5164 5165 cur = cur.filter(function(id) { 5166 return obj.parents.indexOf(id) === -1; 5167 }); 5168 } 5169 5170 this._data[ t ? 'core' : 'checkbox' ].selected = cur; 5171 }, this)); 5172 } 5173 if(this.settings.checkbox.cascade.indexOf('up') !== -1) { 5174 this.element 5175 .on('delete_node.jstree', $.proxy(function (e, data) { 5176 // apply up (whole handler) 5177 var p = this.get_node(data.parent), 5178 m = this._model.data, 5179 i, j, c, tmp, t = this.settings.checkbox.tie_selection; 5180 while(p && p.id !== $.jstree.root && !p.state[ t ? 'selected' : 'checked' ]) { 5181 c = 0; 5182 for(i = 0, j = p.children.length; i < j; i++) { 5183 c += m[p.children[i]].state[ t ? 'selected' : 'checked' ]; 5184 } 5185 if(j > 0 && c === j) { 5186 p.state[ t ? 'selected' : 'checked' ] = true; 5187 this._data[ t ? 'core' : 'checkbox' ].selected.push(p.id); 5188 tmp = this.get_node(p, true); 5189 if(tmp && tmp.length) { 5190 tmp.attr('aria-selected', true).children('.jstree-anchor').addClass(t ? 'jstree-clicked' : 'jstree-checked'); 5191 } 5192 } 5193 else { 5194 break; 5195 } 5196 p = this.get_node(p.parent); 5197 } 5198 }, this)) 5199 .on('move_node.jstree', $.proxy(function (e, data) { 5200 // apply up (whole handler) 5201 var is_multi = data.is_multi, 5202 old_par = data.old_parent, 5203 new_par = this.get_node(data.parent), 5204 m = this._model.data, 5205 p, c, i, j, tmp, t = this.settings.checkbox.tie_selection; 5206 if(!is_multi) { 5207 p = this.get_node(old_par); 5208 while(p && p.id !== $.jstree.root && !p.state[ t ? 'selected' : 'checked' ]) { 5209 c = 0; 5210 for(i = 0, j = p.children.length; i < j; i++) { 5211 c += m[p.children[i]].state[ t ? 'selected' : 'checked' ]; 5212 } 5213 if(j > 0 && c === j) { 5214 p.state[ t ? 'selected' : 'checked' ] = true; 5215 this._data[ t ? 'core' : 'checkbox' ].selected.push(p.id); 5216 tmp = this.get_node(p, true); 5217 if(tmp && tmp.length) { 5218 tmp.attr('aria-selected', true).children('.jstree-anchor').addClass(t ? 'jstree-clicked' : 'jstree-checked'); 5219 } 5220 } 5221 else { 5222 break; 5223 } 5224 p = this.get_node(p.parent); 5225 } 5226 } 5227 p = new_par; 5228 while(p && p.id !== $.jstree.root) { 5229 c = 0; 5230 for(i = 0, j = p.children.length; i < j; i++) { 5231 c += m[p.children[i]].state[ t ? 'selected' : 'checked' ]; 5232 } 5233 if(c === j) { 5234 if(!p.state[ t ? 'selected' : 'checked' ]) { 5235 p.state[ t ? 'selected' : 'checked' ] = true; 5236 this._data[ t ? 'core' : 'checkbox' ].selected.push(p.id); 5237 tmp = this.get_node(p, true); 5238 if(tmp && tmp.length) { 5239 tmp.attr('aria-selected', true).children('.jstree-anchor').addClass(t ? 'jstree-clicked' : 'jstree-checked'); 5240 } 5241 } 5242 } 5243 else { 5244 if(p.state[ t ? 'selected' : 'checked' ]) { 5245 p.state[ t ? 'selected' : 'checked' ] = false; 5246 this._data[ t ? 'core' : 'checkbox' ].selected = $.vakata.array_remove_item(this._data[ t ? 'core' : 'checkbox' ].selected, p.id); 5247 tmp = this.get_node(p, true); 5248 if(tmp && tmp.length) { 5249 tmp.attr('aria-selected', false).children('.jstree-anchor').removeClass(t ? 'jstree-clicked' : 'jstree-checked'); 5250 } 5251 } 5252 else { 5253 break; 5254 } 5255 } 5256 p = this.get_node(p.parent); 5257 } 5258 }, this)); 5259 } 5260 }; 5261 5262 /** 5263 * set the undetermined state where and if necessary. Used internally. 5264 * @private 5265 * @name _undetermined() 5266 * @plugin checkbox 5267 */ 5268 this._undetermined = function () { 5269 if(this.element === null) { return; } 5270 var i, j, k, l, o = {}, m = this._model.data, t = this.settings.checkbox.tie_selection, s = this._data[ t ? 'core' : 'checkbox' ].selected, p = [], tt = this; 5271 for(i = 0, j = s.length; i < j; i++) { 5272 if(m[s[i]] && m[s[i]].parents) { 5273 for(k = 0, l = m[s[i]].parents.length; k < l; k++) { 5274 if(o[m[s[i]].parents[k]] !== undefined) { 5275 break; 5276 } 5277 if(m[s[i]].parents[k] !== $.jstree.root) { 5278 o[m[s[i]].parents[k]] = true; 5279 p.push(m[s[i]].parents[k]); 5280 } 5281 } 5282 } 5283 } 5284 // attempt for server side undetermined state 5285 this.element.find('.jstree-closed').not(':has(.jstree-children)') 5286 .each(function () { 5287 var tmp = tt.get_node(this), tmp2; 5288 5289 if(!tmp) { return; } 5290 5291 if(!tmp.state.loaded) { 5292 if(tmp.original && tmp.original.state && tmp.original.state.undetermined && tmp.original.state.undetermined === true) { 5293 if(o[tmp.id] === undefined && tmp.id !== $.jstree.root) { 5294 o[tmp.id] = true; 5295 p.push(tmp.id); 5296 } 5297 for(k = 0, l = tmp.parents.length; k < l; k++) { 5298 if(o[tmp.parents[k]] === undefined && tmp.parents[k] !== $.jstree.root) { 5299 o[tmp.parents[k]] = true; 5300 p.push(tmp.parents[k]); 5301 } 5302 } 5303 } 5304 } 5305 else { 5306 for(i = 0, j = tmp.children_d.length; i < j; i++) { 5307 tmp2 = m[tmp.children_d[i]]; 5308 if(!tmp2.state.loaded && tmp2.original && tmp2.original.state && tmp2.original.state.undetermined && tmp2.original.state.undetermined === true) { 5309 if(o[tmp2.id] === undefined && tmp2.id !== $.jstree.root) { 5310 o[tmp2.id] = true; 5311 p.push(tmp2.id); 5312 } 5313 for(k = 0, l = tmp2.parents.length; k < l; k++) { 5314 if(o[tmp2.parents[k]] === undefined && tmp2.parents[k] !== $.jstree.root) { 5315 o[tmp2.parents[k]] = true; 5316 p.push(tmp2.parents[k]); 5317 } 5318 } 5319 } 5320 } 5321 } 5322 }); 5323 5324 this.element.find('.jstree-undetermined').removeClass('jstree-undetermined'); 5325 for(i = 0, j = p.length; i < j; i++) { 5326 if(!m[p[i]].state[ t ? 'selected' : 'checked' ]) { 5327 s = this.get_node(p[i], true); 5328 if(s && s.length) { 5329 s.children('.jstree-anchor').children('.jstree-checkbox').addClass('jstree-undetermined'); 5330 } 5331 } 5332 } 5333 }; 5334 this.redraw_node = function(obj, deep, is_callback, force_render) { 5335 obj = parent.redraw_node.apply(this, arguments); 5336 if(obj) { 5337 var i, j, tmp = null, icon = null; 5338 for(i = 0, j = obj.childNodes.length; i < j; i++) { 5339 if(obj.childNodes[i] && obj.childNodes[i].className && obj.childNodes[i].className.indexOf("jstree-anchor") !== -1) { 5340 tmp = obj.childNodes[i]; 5341 break; 5342 } 5343 } 5344 if(tmp) { 5345 if(!this.settings.checkbox.tie_selection && this._model.data[obj.id].state.checked) { tmp.className += ' jstree-checked'; } 5346 icon = _i.cloneNode(false); 5347 if(this._model.data[obj.id].state.checkbox_disabled) { icon.className += ' jstree-checkbox-disabled'; } 5348 tmp.insertBefore(icon, tmp.childNodes[0]); 5349 } 5350 } 5351 if(!is_callback && this.settings.checkbox.cascade.indexOf('undetermined') !== -1) { 5352 if(this._data.checkbox.uto) { clearTimeout(this._data.checkbox.uto); } 5353 this._data.checkbox.uto = setTimeout($.proxy(this._undetermined, this), 50); 5354 } 5355 return obj; 5356 }; 5357 /** 5358 * show the node checkbox icons 5359 * @name show_checkboxes() 5360 * @plugin checkbox 5361 */ 5362 this.show_checkboxes = function () { this._data.core.themes.checkboxes = true; this.get_container_ul().removeClass("jstree-no-checkboxes"); }; 5363 /** 5364 * hide the node checkbox icons 5365 * @name hide_checkboxes() 5366 * @plugin checkbox 5367 */ 5368 this.hide_checkboxes = function () { this._data.core.themes.checkboxes = false; this.get_container_ul().addClass("jstree-no-checkboxes"); }; 5369 /** 5370 * toggle the node icons 5371 * @name toggle_checkboxes() 5372 * @plugin checkbox 5373 */ 5374 this.toggle_checkboxes = function () { if(this._data.core.themes.checkboxes) { this.hide_checkboxes(); } else { this.show_checkboxes(); } }; 5375 /** 5376 * checks if a node is in an undetermined state 5377 * @name is_undetermined(obj) 5378 * @param {mixed} obj 5379 * @return {Boolean} 5380 */ 5381 this.is_undetermined = function (obj) { 5382 obj = this.get_node(obj); 5383 var s = this.settings.checkbox.cascade, i, j, t = this.settings.checkbox.tie_selection, d = this._data[ t ? 'core' : 'checkbox' ].selected, m = this._model.data; 5384 if(!obj || obj.state[ t ? 'selected' : 'checked' ] === true || s.indexOf('undetermined') === -1 || (s.indexOf('down') === -1 && s.indexOf('up') === -1)) { 5385 return false; 5386 } 5387 if(!obj.state.loaded && obj.original.state.undetermined === true) { 5388 return true; 5389 } 5390 for(i = 0, j = obj.children_d.length; i < j; i++) { 5391 if($.inArray(obj.children_d[i], d) !== -1 || (!m[obj.children_d[i]].state.loaded && m[obj.children_d[i]].original.state.undetermined)) { 5392 return true; 5393 } 5394 } 5395 return false; 5396 }; 5397 /** 5398 * disable a node's checkbox 5399 * @name disable_checkbox(obj) 5400 * @param {mixed} obj an array can be used too 5401 * @trigger disable_checkbox.jstree 5402 * @plugin checkbox 5403 */ 5404 this.disable_checkbox = function (obj) { 5405 var t1, t2, dom; 5406 if($.isArray(obj)) { 5407 obj = obj.slice(); 5408 for(t1 = 0, t2 = obj.length; t1 < t2; t1++) { 5409 this.disable_checkbox(obj[t1]); 5410 } 5411 return true; 5412 } 5413 obj = this.get_node(obj); 5414 if(!obj || obj.id === $.jstree.root) { 5415 return false; 5416 } 5417 dom = this.get_node(obj, true); 5418 if(!obj.state.checkbox_disabled) { 5419 obj.state.checkbox_disabled = true; 5420 if(dom && dom.length) { 5421 dom.children('.jstree-anchor').children('.jstree-checkbox').addClass('jstree-checkbox-disabled'); 5422 } 5423 /** 5424 * triggered when an node's checkbox is disabled 5425 * @event 5426 * @name disable_checkbox.jstree 5427 * @param {Object} node 5428 * @plugin checkbox 5429 */ 5430 this.trigger('disable_checkbox', { 'node' : obj }); 5431 } 5432 }; 5433 /** 5434 * enable a node's checkbox 5435 * @name disable_checkbox(obj) 5436 * @param {mixed} obj an array can be used too 5437 * @trigger enable_checkbox.jstree 5438 * @plugin checkbox 5439 */ 5440 this.enable_checkbox = function (obj) { 5441 var t1, t2, dom; 5442 if($.isArray(obj)) { 5443 obj = obj.slice(); 5444 for(t1 = 0, t2 = obj.length; t1 < t2; t1++) { 5445 this.enable_checkbox(obj[t1]); 5446 } 5447 return true; 5448 } 5449 obj = this.get_node(obj); 5450 if(!obj || obj.id === $.jstree.root) { 5451 return false; 5452 } 5453 dom = this.get_node(obj, true); 5454 if(obj.state.checkbox_disabled) { 5455 obj.state.checkbox_disabled = false; 5456 if(dom && dom.length) { 5457 dom.children('.jstree-anchor').children('.jstree-checkbox').removeClass('jstree-checkbox-disabled'); 5458 } 5459 /** 5460 * triggered when an node's checkbox is enabled 5461 * @event 5462 * @name enable_checkbox.jstree 5463 * @param {Object} node 5464 * @plugin checkbox 5465 */ 5466 this.trigger('enable_checkbox', { 'node' : obj }); 5467 } 5468 }; 5469 5470 this.activate_node = function (obj, e) { 5471 if($(e.target).hasClass('jstree-checkbox-disabled')) { 5472 return false; 5473 } 5474 if(this.settings.checkbox.tie_selection && (this.settings.checkbox.whole_node || $(e.target).hasClass('jstree-checkbox'))) { 5475 e.ctrlKey = true; 5476 } 5477 if(this.settings.checkbox.tie_selection || (!this.settings.checkbox.whole_node && !$(e.target).hasClass('jstree-checkbox'))) { 5478 return parent.activate_node.call(this, obj, e); 5479 } 5480 if(this.is_disabled(obj)) { 5481 return false; 5482 } 5483 if(this.is_checked(obj)) { 5484 this.uncheck_node(obj, e); 5485 } 5486 else { 5487 this.check_node(obj, e); 5488 } 5489 this.trigger('activate_node', { 'node' : this.get_node(obj) }); 5490 }; 5491 5492 /**
5493 * Unchecks a node and all its descendants. This function does NOT affect hidden and disabled nodes (or their descendants). 5494 * However if these unaffected nodes are already selected their ids will be included in the returned array. 5495 * @param id 5496 * @param checkedState 5497 * @returns {Array} Array of all node id's (in this tree branch) that are checked. 5498 */ 5499 this._cascade_new_checked_state = function(id, checkedState) { 5500 var self = this; 5501 var t = this.settings.checkbox.tie_selection; 5502 var node = this._model.data[id]; 5503 var selectedNodeIds = []; 5504 var selectedChildrenIds = []; 5505 5506 if ( 5507 (this.settings.checkbox.cascade_to_disabled || !node.state.disabled) && 5508 (this.settings.checkbox.cascade_to_hidden || !node.state.hidden) 5509 ) { 5510 //First try and check/uncheck the children 5511 if (node.children) { 5512 node.children.forEach(function(childId) { 5513 var selectedChildIds = self._cascade_new_checked_state(childId, checkedState); 5514 selectedNodeIds = selectedNodeIds.concat(selectedChildIds); 5515 if (selectedChildIds.indexOf(childId) > -1) { 5516 selectedChildrenIds.push(childId); 5517 } 5518 }); 5519 } 5520 5521 var dom = self.get_node(node, true); 5522 5523 //A node's state is undetermined if some but not all of it's children are checked/selected . 5524 var undetermined = selectedChildrenIds.length > 0 && selectedChildrenIds.length < node.children.length; 5525 5526 if(node.original && node.original.state && node.original.state.undetermined) { 5527 node.original.state.undetermined = undetermined; 5528 } 5529 5530 //If a node is undetermined then remove selected class 5531 if (undetermined) { 5532 node.state[ t ? 'selected' : 'checked' ] = false; 5533 dom.attr('aria-selected', false).children('.jstree-anchor').removeClass(t ? 'jstree-clicked' : 'jstree-checked'); 5534 } 5535 //Otherwise, if the checkedState === true (i.e. the node is being checked now) and all of the node's children are checked (if it has any children), 5536 //check the node and style it correctly. 5537 else if (checkedState && selectedChildrenIds.length === node.children.length) { 5538 node.state[ t ? 'selected' : 'checked' ] = checkedState; 5539 selectedNodeIds.push(node.id); 5540 5541 dom.attr('aria-selected', true).children('.jstree-anchor').addClass(t ? 'jstree-clicked' : 'jstree-checked'); 5542 } 5543 else { 5544 node.state[ t ? 'selected' : 'checked' ] = false; 5545 dom.attr('aria-selected', false).children('.jstree-anchor').removeClass(t ? 'jstree-clicked' : 'jstree-checked'); 5546 } 5547 } 5548 else { 5549 var selectedChildIds = this.get_checked_descendants(id); 5550 5551 if (node.state[ t ? 'selected' : 'checked' ]) { 5552 selectedChildIds.push(node.id); 5553 } 5554 5555 selectedNodeIds = selectedNodeIds.concat(selectedChildIds); 5556 } 5557 5558 return selectedNodeIds; 5559 }; 5560 5561 /** 5562 * Gets ids of nodes selected in branch (of tree) specified by id (does not include the node specified by id) 5563 * @param id 5564 */ 5565 this.get_checked_descendants = function(id) { 5566 var self = this; 5567 var t = self.settings.checkbox.tie_selection; 5568 var node = self._model.data[id]; 5569 5570 return node.children_d.filter(function(_id) { 5571 return self._model.data[_id].state[ t ? 'selected' : 'checked' ]; 5572 }); 5573 }; 5574 5575 /** 5576 * check a node (only if tie_selection in checkbox settings is false, otherwise select_node will be called internally) 5577 * @name check_node(obj) 5578 * @param {mixed} obj an array can be used to check multiple nodes 5579 * @trigger check_node.jstree 5580 * @plugin checkbox 5581 */ 5582 this.check_node = function (obj, e) { 5583 if(this.settings.checkbox.tie_selection) { return this.select_node(obj, false, true, e); } 5584 var dom, t1, t2, th; 5585 if($.isArray(obj)) { 5586 obj = obj.slice(); 5587 for(t1 = 0, t2 = obj.length; t1 < t2; t1++) { 5588 this.check_node(obj[t1], e); 5589 } 5590 return true; 5591 } 5592 obj = this.get_node(obj); 5593 if(!obj || obj.id === $.jstree.root) { 5594 return false; 5595 } 5596 dom = this.get_node(obj, true); 5597 if(!obj.state.checked) { 5598 obj.state.checked = true; 5599 this._data.checkbox.selected.push(obj.id); 5600 if(dom && dom.length) { 5601 dom.children('.jstree-anchor').addClass('jstree-checked'); 5602 } 5603 /** 5604 * triggered when an node is checked (only if tie_selection in checkbox settings is false) 5605 * @event 5606 * @name check_node.jstree 5607 * @param {Object} node 5608 * @param {Array} selected the current selection 5609 * @param {Object} event the event (if any) that triggered this check_node 5610 * @plugin checkbox 5611 */ 5612 this.trigger('check_node', { 'node' : obj, 'selected' : this._data.checkbox.selected, 'event' : e }); 5613 } 5614 }; 5615 /** 5616 * uncheck a node (only if tie_selection in checkbox settings is false, otherwise deselect_node will be called internally) 5617 * @name uncheck_node(obj) 5618 * @param {mixed} obj an array can be used to uncheck multiple nodes 5619 * @trigger uncheck_node.jstree 5620 * @plugin checkbox 5621 */ 5622 this.uncheck_node = function (obj, e) { 5623 if(this.settings.checkbox.tie_selection) { return this.deselect_node(obj, false, e); } 5624 var t1, t2, dom; 5625 if($.isArray(obj)) { 5626 obj = obj.slice(); 5627 for(t1 = 0, t2 = obj.length; t1 < t2; t1++) { 5628 this.uncheck_node(obj[t1], e); 5629 } 5630 return true; 5631 } 5632 obj = this.get_node(obj); 5633 if(!obj || obj.id === $.jstree.root) { 5634 return false; 5635 } 5636 dom = this.get_node(obj, true); 5637 if(obj.state.checked) { 5638 obj.state.checked = false; 5639 this._data.checkbox.selected = $.vakata.array_remove_item(this._data.checkbox.selected, obj.id); 5640 if(dom.length) { 5641 dom.children('.jstree-anchor').removeClass('jstree-checked'); 5642 } 5643 /** 5644 * triggered when an node is unchecked (only if tie_selection in checkbox settings is false) 5645 * @event 5646 * @name uncheck_node.jstree 5647 * @param {Object} node 5648 * @param {Array} selected the current selection 5649 * @param {Object} event the event (if any) that triggered this uncheck_node 5650 * @plugin checkbox 5651 */ 5652 this.trigger('uncheck_node', { 'node' : obj, 'selected' : this._data.checkbox.selected, 'event' : e }); 5653 } 5654 }; 5655 5656 /** 5657 * checks all nodes in the tree (only if tie_selection in checkbox settings is false, otherwise select_all will be called internally) 5658 * @name check_all()
vendor: 4,254 bytes, lines 5659-5760
5659 * @trigger check_all.jstree, changed.jstree 5660 * @plugin checkbox 5661 */ 5662 this.check_all = function () { 5663 if(this.settings.checkbox.tie_selection) { return this.select_all(); } 5664 var tmp = this._data.checkbox.selected.concat([]), i, j; 5665 this._data.checkbox.selected = this._model.data[$.jstree.root].children_d.concat(); 5666 for(i = 0, j = this._data.checkbox.selected.length; i < j; i++) { 5667 if(this._model.data[this._data.checkbox.selected[i]]) { 5668 this._model.data[this._data.checkbox.selected[i]].state.checked = true; 5669 } 5670 } 5671 this.redraw(true); 5672 /** 5673 * triggered when all nodes are checked (only if tie_selection in checkbox settings is false) 5674 * @event 5675 * @name check_all.jstree 5676 * @param {Array} selected the current selection 5677 * @plugin checkbox 5678 */ 5679 this.trigger('check_all', { 'selected' : this._data.checkbox.selected }); 5680 }; 5681 /** 5682 * uncheck all checked nodes (only if tie_selection in checkbox settings is false, otherwise deselect_all will be called internally) 5683 * @name uncheck_all() 5684 * @trigger uncheck_all.jstree 5685 * @plugin checkbox 5686 */ 5687 this.uncheck_all = function () { 5688 if(this.settings.checkbox.tie_selection) { return this.deselect_all(); } 5689 var tmp = this._data.checkbox.selected.concat([]), i, j; 5690 for(i = 0, j = this._data.checkbox.selected.length; i < j; i++) { 5691 if(this._model.data[this._data.checkbox.selected[i]]) { 5692 this._model.data[this._data.checkbox.selected[i]].state.checked = false; 5693 } 5694 } 5695 this._data.checkbox.selected = []; 5696 this.element.find('.jstree-checked').removeClass('jstree-checked'); 5697 /** 5698 * triggered when all nodes are unchecked (only if tie_selection in checkbox settings is false) 5699 * @event 5700 * @name uncheck_all.jstree 5701 * @param {Object} node the previous selection 5702 * @param {Array} selected the current selection 5703 * @plugin checkbox 5704 */ 5705 this.trigger('uncheck_all', { 'selected' : this._data.checkbox.selected, 'node' : tmp }); 5706 }; 5707 /** 5708 * checks if a node is checked (if tie_selection is on in the settings this function will return the same as is_selected) 5709 * @name is_checked(obj) 5710 * @param {mixed} obj 5711 * @return {Boolean} 5712 * @plugin checkbox 5713 */ 5714 this.is_checked = function (obj) { 5715 if(this.settings.checkbox.tie_selection) { return this.is_selected(obj); } 5716 obj = this.get_node(obj); 5717 if(!obj || obj.id === $.jstree.root) { return false; } 5718 return obj.state.checked; 5719 }; 5720 /** 5721 * get an array of all checked nodes (if tie_selection is on in the settings this function will return the same as get_selected) 5722 * @name get_checked([full]) 5723 * @param {mixed} full if set to `true` the returned array will consist of the full node objects, otherwise - only IDs will be returned 5724 * @return {Array} 5725 * @plugin checkbox 5726 */ 5727 this.get_checked = function (full) { 5728 if(this.settings.checkbox.tie_selection) { return this.get_selected(full); } 5729 return full ? $.map(this._data.checkbox.selected, $.proxy(function (i) { return this.get_node(i); }, this)) : this._data.checkbox.selected; 5730 }; 5731 /** 5732 * get an array of all top level checked nodes (ignoring children of checked nodes) (if tie_selection is on in the settings this function will return the same as get_top_selected) 5733 * @name get_top_checked([full]) 5734 * @param {mixed} full if set to `true` the returned array will consist of the full node objects, otherwise - only IDs will be returned 5735 * @return {Array} 5736 * @plugin checkbox 5737 */ 5738 this.get_top_checked = function (full) { 5739 if(this.settings.checkbox.tie_selection) { return this.get_top_selected(full); } 5740 var tmp = this.get_checked(true), 5741 obj = {}, i, j, k, l; 5742 for(i = 0, j = tmp.length; i < j; i++) { 5743 obj[tmp[i].id] = tmp[i]; 5744 } 5745 for(i = 0, j = tmp.length; i < j; i++) { 5746 for(k = 0, l = tmp[i].children_d.length; k < l; k++) { 5747 if(obj[tmp[i].children_d[k]]) { 5748 delete obj[tmp[i].children_d[k]]; 5749 } 5750 } 5751 } 5752 tmp = []; 5753 for(i in obj) { 5754 if(obj.hasOwnProperty(i)) { 5755 tmp.push(i); 5756 } 5757 } 5758 return full ? $.map(tmp, $.proxy(function (i) { return this.get_node(i); }, this)) : tmp; 5759 }; 5760 /**
5761 * get an array of all bottom level checked nodes (ignoring selected parents) (if tie_selection is on in the settings this function will return the same as get_bottom_selected) 5762 * @name get_bottom_checked([full]) 5763 * @param {mixed} full if set to `true` the returned array will consist of the full node objects, otherwise - only IDs will be returned 5764 * @return {Array} 5765 * @plugin checkbox 5766 */ 5767 this.get_bottom_checked = function (full) { 5768 if(this.settings.checkbox.tie_selection) { return this.get_bottom_selected(full); } 5769 var tmp = this.get_checked(true), 5770 obj = [], i, j; 5771 for(i = 0, j = tmp.length; i < j; i++) { 5772 if(!tmp[i].children.length) { 5773 obj.push(tmp[i].id); 5774 } 5775 } 5776 return full ? $.map(obj, $.proxy(function (i) { return this.get_node(i); }, this)) : obj; 5777 }; 5778 this.load_node = function (obj, callback) { 5779 var k, l, i, j, c, tmp; 5780 if(!$.isArray(obj) && !this.settings.checkbox.tie_selection) { 5781 tmp = this.get_node(obj); 5782 if(tmp && tmp.state.loaded) { 5783 for(k = 0, l = tmp.children_d.length; k < l; k++) { 5784 if(this._model.data[tmp.children_d[k]].state.checked) { 5785 c = true; 5786 this._data.checkbox.selected = $.vakata.array_remove_item(this._data.checkbox.selected, tmp.children_d[k]); 5787 } 5788 } 5789 } 5790 } 5791 return parent.load_node.apply(this, arguments); 5792 }; 5793 this.get_state = function () { 5794 var state = parent.get_state.apply(this, arguments); 5795 if(this.settings.checkbox.tie_selection) { return state; } 5796 state.checkbox = this._data.checkbox.selected.slice(); 5797 return state; 5798 }; 5799 this.set_state = function (state, callback) { 5800 var res = parent.set_state.apply(this, arguments); 5801 if(res && state.checkbox) { 5802 if(!this.settings.checkbox.tie_selection) { 5803 this.uncheck_all(); 5804 var _this = this; 5805 $.each(state.checkbox, function (i, v) { 5806 _this.check_node(v); 5807 }); 5808 } 5809 delete state.checkbox; 5810 this.set_state(state, callback); 5811 return false; 5812 } 5813 return res; 5814 }; 5815 this.refresh = function (skip_loading, forget_state) { 5816 if(!this.settings.checkbox.tie_selection) { 5817 this._data.checkbox.selected = []; 5818 } 5819 return parent.refresh.apply(this, arguments); 5820 }; 5821 }; 5822 5823 // include the checkbox plugin by default 5824 // $.jstree.defaults.plugins.push("checkbox"); 5825 5826 5827/** 5828 * ### Conditionalselect plugin 5829 * 5830 * This plugin allows defining a callback to allow or deny node selection by user input (activate node method). 5831 */ 5832 5833 /** 5834 * a callback (function) which is invoked in the instance's scope and receives two arguments - the node and the event that triggered the `activate_node` call. Returning false prevents working with the node, returning true allows invoking activate_node. Defaults to returning `true`. 5835 * @name $.jstree.defaults.checkbox.visible 5836 * @plugin checkbox 5837 */ 5838 $.jstree.defaults.conditionalselect = function () { return true; }; 5839 $.jstree.plugins.conditionalselect = function (options, parent) { 5840 // own function 5841 this.activate_node = function (obj, e) { 5842 if(this.settings.conditionalselect.call(this, this.get_node(obj), e)) { 5843 parent.activate_node.call(this, obj, e); 5844 } 5845 }; 5846 }; 5847 5848 5849/** 5850 * ### Contextmenu plugin 5851 * 5852 * Shows a context menu when a node is right-clicked. 5853 */ 5854 5855 /** 5856 * stores all defaults for the contextmenu plugin 5857 * @name $.jstree.defaults.contextmenu 5858 * @plugin contextmenu 5859 */ 5860 $.jstree.defaults.contextmenu = { 5861 /** 5862 * a boolean indicating if the node should be selected when the context menu is invoked on it. Defaults to `true`. 5863 * @name $.jstree.defaults.contextmenu.select_node 5864 * @plugin contextmenu 5865 */ 5866 select_node : true, 5867 /** 5868 * a boolean indicating if the menu should be shown aligned with the node. Defaults to `true`, otherwise the mouse coordinates are used. 5869 * @name $.jstree.defaults.contextmenu.show_at_node 5870 * @plugin contextmenu 5871 */ 5872 show_at_node : true, 5873 /** 5874 * an object of actions, or a function that accepts a node and a callback function and calls the callback function with an object of actions available for that node (you can also return the items too). 5875 * 5876 * Each action consists of a key (a unique name) and a value which is an object with the following properties (only label and action are required). Once a menu item is activated the `action` function will be invoked with an object c
5876ontaining the following keys: item - the contextmenu item definition as seen below, reference - the DOM node that was used (the tree node), element - the contextmenu DOM element, position - an object with x/y properties indicating the position of the menu. 5877 * 5878 * * `separator_before` - a boolean indicating if there should be a separator before this item 5879 * * `separator_after` - a boolean indicating if there should be a separator after this item 5880 * * `_disabled` - a boolean indicating if this action should be disabled 5881 * * `label` - a string - the name of the action (could be a function returning a string) 5882 * * `title` - a string - an optional tooltip for the item 5883 * * `action` - a function to be executed if this item is chosen, the function will receive 5884 * * `icon` - a string, can be a path to an icon or a className, if using an image that is in the current directory use a `./` prefix, otherwise it will be detected as a class 5885 * * `shortcut` - keyCode which will trigger the action if the menu is open (for example `113` for rename, which equals F2) 5886 * * `shortcut_label` - shortcut label (like for example `F2` for rename) 5887 * * `submenu` - an object with the same structure as $.jstree.defaults.contextmenu.items which can be used to create a submenu - each key will be rendered as a separate option in a submenu that will appear once the current item is hovered 5888 * 5889 * @name $.jstree.defaults.contextmenu.items 5890 * @plugin contextmenu 5891 */ 5892 items : function (o, cb) { // Could be an object directly 5893 return { 5894 "create" : { 5895 "separator_before" : false, 5896 "separator_after" : true, 5897 "_disabled" : false, //(this.check("create_node", data.reference, {}, "last")), 5898 "label" : "Create", 5899 "action" : function (data) { 5900 var inst = $.jstree.reference(data.reference), 5901 obj = inst.get_node(data.reference); 5902 inst.create_node(obj, {}, "last", function (new_node) { 5903 try { 5904 inst.edit(new_node); 5905 } catch (ex) { 5906 setTimeout(function () { inst.edit(new_node); },0); 5907 } 5908 }); 5909 } 5910 }, 5911 "rename" : { 5912 "separator_before" : false, 5913 "separator_after" : false, 5914 "_disabled" : false, //(this.check("rename_node", data.reference, this.get_parent(data.reference), "")), 5915 "label" : "Rename", 5916 /*! 5917 "shortcut" : 113, 5918 "shortcut_label" : 'F2', 5919 "icon" : "glyphicon glyphicon-leaf", 5920 */ 5921 "action" : function (data) { 5922 var inst = $.jstree.reference(data.reference), 5923 obj = inst.get_node(data.reference); 5924 inst.edit(obj); 5925 } 5926 }, 5927 "remove" : { 5928 "separator_before" : false, 5929 "icon" : false, 5930 "separator_after" : false, 5931 "_disabled" : false, //(this.check("delete_node", data.reference, this.get_parent(data.reference), "")), 5932 "label" : "Delete", 5933 "action" : function (data) { 5934 var inst = $.jstree.reference(data.reference), 5935 obj = inst.get_node(data.reference); 5936 if(inst.is_selected(obj)) { 5937 inst.delete_node(inst.get_selected()); 5938 } 5939 else { 5940 inst.delete_node(obj); 5941 } 5942 } 5943 }, 5944 "ccp" : { 5945 "separator_before" : true, 5946 "icon" : false, 5947 "separator_after" : false, 5948 "label" : "Edit", 5949 "action" : false, 5950 "submenu" : { 5951 "cut" : { 5952 "separator_before" : false, 5953 "separator_after" : false, 5954 "label" : "Cut", 5955 "action" : function (data) { 5956 var inst = $.jstree.reference(data.reference), 5957 obj = inst.get_node(data.reference); 5958 if(inst.is_selected(obj)) { 5959 inst.cut(inst.get_top_selected()); 5960 } 5961 else { 5962 inst.cut(obj); 5963 } 5964 } 5965 }, 5966 "copy" : { 5967 "separator_before" : false, 5968 "icon" : false, 5969 "separator_after" : false, 5970 "label" : "Copy", 5971 "action" : function (data) { 5972 var inst = $.jstree.reference(data.reference), 5973 obj = inst.get_node(data.reference); 5974 if(inst.is_selected(obj)) { 5975 inst.copy(inst.get_top_selected()); 5976 } 5977 else { 5978 inst.copy(obj); 5979 } 5980 } 5981 }, 5982 "paste" : { 5983 "separator_before" : false, 5984 "icon" : false, 5985 "_disabled" : function (data) { 5986 return !$.jstree.reference(data.reference).can_paste(); 5987 }, 5988 "separator_after" : false, 5989 "label" : "Paste", 5990 "action" : function (data) { 5991 var inst = $.jstree.reference(data.reference), 5992 obj = inst.get_node(data.reference); 5993 inst.paste(obj); 5994 } 5995 } 5996 } 5997 } 5998 }; 5999 } 6000 }; 6001 6002 $.jstree.plugins.contextmenu = function (options, parent) { 6003 this.bind = function () { 6004 parent.bind.call(this); 6005 6006 var last_ts = 0, cto = null, ex, ey; 6007 this.element 6008 .on("init.jstree loading.jstree ready.jstree", $.proxy(function () { 6009 this.get_container_ul().addClass('jstree-contextmenu'); 6010 }, this)) 6011 .on("contextmenu.jstree", ".jstree-anchor", $.proxy(function (e, data) { 6012 if (e.target.tagName.toLowerCase() === 'input') { 6013 return; 6014 } 6015 e.preventDefault(); 6016 last_ts = e.ctrlKey ? +new Date() : 0; 6017 if(data || cto) { 6018 last_ts = (+new Date()) + 10000; 6019 } 6020 if(cto) { 6021 clearTimeout(cto); 6022 } 6023 if(!this.is_loading(e.currentTarget)) { 6024 this.show_contextmenu(e.currentTarget, e.pageX, e.pageY, e); 6025 } 6026 }, this)) 6027 .on("click.jstree", ".jstree-anchor", $.proxy(function (e) { 6028 if(this._data.contextmenu.visible && (!last_ts || (+new Date()) - last_ts > 250)) { // work around safari & macOS ctrl+click 6029 $.vakata.context.hide(); 6030 } 6031 last_ts = 0; 6032 }, this)) 6033 .on("touchstart.jstree", ".jstree-anchor", function (e) { 6034 if(!e.originalEvent || !e.originalEvent.changedTouches || !e.originalEvent.changedTouches[0]) { 6035 return; 6036 } 6037 ex = e.originalEvent.changedTouches[0].clientX; 6038 ey = e.originalEvent.changedTouches[0].clientY; 6039 cto = setTimeout(function () { 6040 $(e.currentTarget).trigger('contextmenu', true); 6041 }, 750); 6042 }) 6043 .on('touchmove.vakata.jstree', function (e) { 6044 if(cto && e.originalEvent && e.originalEvent.changedTouches && e.originalEvent.changedTouches[0] && (Math.abs(ex - e.originalEvent.changedTouches[0].clientX) > 50 || Math.abs(ey - e.originalEvent.changedTouches[0].clientY) > 50)) { 6045 clearTimeout(cto); 6046 } 6047 }) 6048 .on('touchend.vakata.jstree', function (e) { 6049 if(cto) { 6050 clearTimeout(cto); 6051 } 6052 });
vendor: 4,407 bytes, lines 6053-6186
6053 6054 /*! 6055 if(!('oncontextmenu' in document.body) && ('ontouchstart' in document.body)) { 6056 var el = null, tm = null; 6057 this.element 6058 .on("touchstart", ".jstree-anchor", function (e) { 6059 el = e.currentTarget; 6060 tm = +new Date(); 6061 $(document).one("touchend", function (e) { 6062 e.target = document.elementFromPoint(e.originalEvent.targetTouches[0].pageX - window.pageXOffset, e.originalEvent.targetTouches[0].pageY - window.pageYOffset); 6063 e.currentTarget = e.target; 6064 tm = ((+(new Date())) - tm); 6065 if(e.target === el && tm > 600 && tm < 1000) { 6066 e.preventDefault(); 6067 $(el).trigger('contextmenu', e); 6068 } 6069 el = null; 6070 tm = null; 6071 }); 6072 }); 6073 } 6074 */ 6075 $(document).on("context_hide.vakata.jstree", $.proxy(function (e, data) { 6076 this._data.contextmenu.visible = false; 6077 $(data.reference).removeClass('jstree-context'); 6078 }, this)); 6079 }; 6080 this.teardown = function () { 6081 if(this._data.contextmenu.visible) { 6082 $.vakata.context.hide(); 6083 } 6084 parent.teardown.call(this); 6085 }; 6086 6087 /** 6088 * prepare and show the context menu for a node 6089 * @name show_contextmenu(obj [, x, y]) 6090 * @param {mixed} obj the node 6091 * @param {Number} x the x-coordinate relative to the document to show the menu at 6092 * @param {Number} y the y-coordinate relative to the document to show the menu at 6093 * @param {Object} e the event if available that triggered the contextmenu 6094 * @plugin contextmenu 6095 * @trigger show_contextmenu.jstree 6096 */ 6097 this.show_contextmenu = function (obj, x, y, e) { 6098 obj = this.get_node(obj); 6099 if(!obj || obj.id === $.jstree.root) { return false; } 6100 var s = this.settings.contextmenu, 6101 d = this.get_node(obj, true), 6102 a = d.children(".jstree-anchor"), 6103 o = false, 6104 i = false; 6105 if(s.show_at_node || x === undefined || y === undefined) { 6106 o = a.offset(); 6107 x = o.left; 6108 y = o.top + this._data.core.li_height; 6109 } 6110 if(this.settings.contextmenu.select_node && !this.is_selected(obj)) { 6111 this.activate_node(obj, e); 6112 } 6113 6114 i = s.items; 6115 if($.isFunction(i)) { 6116 i = i.call(this, obj, $.proxy(function (i) { 6117 this._show_contextmenu(obj, x, y, i); 6118 }, this)); 6119 } 6120 if($.isPlainObject(i)) { 6121 this._show_contextmenu(obj, x, y, i); 6122 } 6123 }; 6124 /** 6125 * show the prepared context menu for a node 6126 * @name _show_contextmenu(obj, x, y, i) 6127 * @param {mixed} obj the node 6128 * @param {Number} x the x-coordinate relative to the document to show the menu at 6129 * @param {Number} y the y-coordinate relative to the document to show the menu at 6130 * @param {Number} i the object of items to show 6131 * @plugin contextmenu 6132 * @trigger show_contextmenu.jstree 6133 * @private 6134 */ 6135 this._show_contextmenu = function (obj, x, y, i) { 6136 var d = this.get_node(obj, true), 6137 a = d.children(".jstree-anchor"); 6138 $(document).one("context_show.vakata.jstree", $.proxy(function (e, data) { 6139 var cls = 'jstree-contextmenu jstree-' + this.get_theme() + '-contextmenu'; 6140 $(data.element).addClass(cls); 6141 a.addClass('jstree-context'); 6142 }, this)); 6143 this._data.contextmenu.visible = true; 6144 $.vakata.context.show(a, { 'x' : x, 'y' : y }, i); 6145 /** 6146 * triggered when the contextmenu is shown for a node 6147 * @event 6148 * @name show_contextmenu.jstree 6149 * @param {Object} node the node 6150 * @param {Number} x the x-coordinate of the menu relative to the document 6151 * @param {Number} y the y-coordinate of the menu relative to the document 6152 * @plugin contextmenu 6153 */ 6154 this.trigger('show_contextmenu', { "node" : obj, "x" : x, "y" : y }); 6155 }; 6156 }; 6157 6158 // contextmenu helper 6159 (function ($) { 6160 var right_to_left = false, 6161 vakata_context = { 6162 element : false, 6163 reference : false, 6164 position_x : 0, 6165 position_y : 0, 6166 items : [], 6167 html : "", 6168 is_visible : false 6169 }; 6170 6171 $.vakata.context = { 6172 settings : { 6173 hide_onmouseleave : 0, 6174 icons : true 6175 }, 6176 _trigger : function (event_name) { 6177 $(document).triggerHandler("context_" + event_name + ".vakata", { 6178 "reference" : vakata_context.reference, 6179 "element" : vakata_context.element, 6180 "position" : { 6181 "x" : vakata_context.position_x, 6182 "y" : vakata_context.position_y 6183 } 6184 }); 6185 }, 6186 _execute : function (i) {
vendor: 5,527 bytes, lines 6187-6315
6187 i = vakata_context.items[i]; 6188 return i && (!i._disabled || ($.isFunction(i._disabled) && !i._disabled({ "item" : i, "reference" : vakata_context.reference, "element" : vakata_context.element }))) && i.action ? i.action.call(null, { 6189 "item" : i, 6190 "reference" : vakata_context.reference, 6191 "element" : vakata_context.element, 6192 "position" : { 6193 "x" : vakata_context.position_x, 6194 "y" : vakata_context.position_y 6195 } 6196 }) : false; 6197 }, 6198 _parse : function (o, is_callback) { 6199 if(!o) { return false; } 6200 if(!is_callback) { 6201 vakata_context.html = ""; 6202 vakata_context.items = []; 6203 } 6204 var str = "", 6205 sep = false, 6206 tmp; 6207 6208 if(is_callback) { str += "<"+"ul>"; } 6209 $.each(o, function (i, val) { 6210 if(!val) { return true; } 6211 vakata_context.items.push(val); 6212 if(!sep && val.separator_before) { 6213 str += "<"+"li class='vakata-context-separator'><"+"a href='#' " + ($.vakata.context.settings.icons ? '' : 'style="margin-left:0px;"') + "> <"+"/a><"+"/li>"; 6214 } 6215 sep = false; 6216 str += "<"+"li class='" + (val._class || "") + (val._disabled === true || ($.isFunction(val._disabled) && val._disabled({ "item" : val, "reference" : vakata_context.reference, "element" : vakata_context.element })) ? " vakata-contextmenu-disabled " : "") + "' "+(val.shortcut?" data-shortcut='"+val.shortcut+"' ":'')+">"; 6217 str += "<"+"a href='#' rel='" + (vakata_context.items.length - 1) + "' " + (val.title ? "title='" + val.title + "'" : "") + ">"; 6218 if($.vakata.context.settings.icons) { 6219 str += "<"+"i "; 6220 if(val.icon) { 6221 if(val.icon.indexOf("/") !== -1 || val.icon.indexOf(".") !== -1) { str += " style='background:url(\"" + val.icon + "\") center center no-repeat' "; } 6222 else { str += " class='" + val.icon + "' "; } 6223 } 6224 str += "><"+"/i><"+"span class='vakata-contextmenu-sep'> <"+"/span>"; 6225 } 6226 str += ($.isFunction(val.label) ? val.label({ "item" : i, "reference" : vakata_context.reference, "element" : vakata_context.element }) : val.label) + (val.shortcut?' <span class="vakata-contextmenu-shortcut vakata-contextmenu-shortcut-'+val.shortcut+'">'+ (val.shortcut_label || '') +'</span>':'') + "<"+"/a>"; 6227 if(val.submenu) { 6228 tmp = $.vakata.context._parse(val.submenu, true); 6229 if(tmp) { str += tmp; } 6230 } 6231 str += "<"+"/li>"; 6232 if(val.separator_after) { 6233 str += "<"+"li class='vakata-context-separator'><"+"a href='#' " + ($.vakata.context.settings.icons ? '' : 'style="margin-left:0px;"') + "> <"+"/a><"+"/li>"; 6234 sep = true; 6235 } 6236 }); 6237 str = str.replace(/<li class\='vakata-context-separator'\><\/li\>$/,""); 6238 if(is_callback) { str += "</ul>"; } 6239 /** 6240 * triggered on the document when the contextmenu is parsed (HTML is built) 6241 * @event 6242 * @plugin contextmenu 6243 * @name context_parse.vakata 6244 * @param {jQuery} reference the element that was right clicked 6245 * @param {jQuery} element the DOM element of the menu itself 6246 * @param {Object} position the x & y coordinates of the menu 6247 */ 6248 if(!is_callback) { vakata_context.html = str; $.vakata.context._trigger("parse"); } 6249 return str.length > 10 ? str : false; 6250 }, 6251 _show_submenu : function (o) { 6252 o = $(o); 6253 if(!o.length || !o.children("ul").length) { return; } 6254 var e = o.children("ul"), 6255 xl = o.offset().left, 6256 x = xl + o.outerWidth(), 6257 y = o.offset().top, 6258 w = e.width(), 6259 h = e.height(), 6260 dw = $(window).width() + $(window).scrollLeft(), 6261 dh = $(window).height() + $(window).scrollTop(); 6262 // може да Ñе ÑпеÑÑи е една пÑовеÑка - дали нÑма нÑкой Ð¾Ñ ÐºÐ»Ð°ÑовеÑе веÑе нагоÑе 6263 if(right_to_left) { 6264 o[x - (w + 10 + o.outerWidth()) < 0 ? "addClass" : "removeClass"]("vakata-context-left"); 6265 } 6266 else { 6267 o[x + w > dw && xl > dw - x ? "addClass" : "removeClass"]("vakata-context-right"); 6268 } 6269 if(y + h + 10 > dh) { 6270 e.css("bottom","-1px"); 6271 } 6272 6273 //if does not fit - stick it to the side 6274 if (o.hasClass('vakata-context-right')) { 6275 if (xl < w) { 6276 e.css("margin-right", xl - w); 6277 } 6278 } else { 6279 if (dw - x < w) { 6280 e.css("margin-left", dw - x - w); 6281 } 6282 } 6283 6284 e.show(); 6285 }, 6286 show : function (reference, position, data) { 6287 var o, e, x, y, w, h, dw, dh, cond = true; 6288 if(vakata_context.element && vakata_context.element.length) { 6289 vakata_context.element.width(''); 6290 } 6291 switch(cond) { 6292 case (!position && !reference): 6293 return false; 6294 case (!!position && !!reference): 6295 vakata_context.reference = reference; 6296 vakata_context.position_x = position.x; 6297 vakata_context.position_y = position.y; 6298 break; 6299 case (!position && !!reference): 6300 vakata_context.reference = reference; 6301 o = reference.offset(); 6302 vakata_context.position_x = o.left + reference.outerHeight(); 6303 vakata_context.position_y = o.top; 6304 break; 6305 case (!!position && !reference): 6306 vakata_context.position_x = position.x; 6307 vakata_context.position_y = position.y; 6308 break; 6309 } 6310 if(!!reference && !data && $(reference).data('vakata_contextmenu')) { 6311 data = $(reference).data('vakata_contextmenu'); 6312 } 6313 if($.vakata.context._parse(data)) { 6314 vakata_context.element.html(vakata_context.html); 6315 }
vendor: 4,304 bytes, lines 6316-6433
6316 if(vakata_context.items.length) { 6317 vakata_context.element.appendTo("body"); 6318 e = vakata_context.element; 6319 x = vakata_context.position_x; 6320 y = vakata_context.position_y; 6321 w = e.width(); 6322 h = e.height(); 6323 dw = $(window).width() + $(window).scrollLeft(); 6324 dh = $(window).height() + $(window).scrollTop(); 6325 if(right_to_left) { 6326 x -= (e.outerWidth() - $(reference).outerWidth()); 6327 if(x < $(window).scrollLeft() + 20) { 6328 x = $(window).scrollLeft() + 20; 6329 } 6330 } 6331 if(x + w + 20 > dw) { 6332 x = dw - (w + 20); 6333 } 6334 if(y + h + 20 > dh) { 6335 y = dh - (h + 20); 6336 } 6337 6338 vakata_context.element 6339 .css({ "left" : x, "top" : y }) 6340 .show() 6341 .find('a').first().focus().parent().addClass("vakata-context-hover"); 6342 vakata_context.is_visible = true; 6343 /** 6344 * triggered on the document when the contextmenu is shown 6345 * @event 6346 * @plugin contextmenu 6347 * @name context_show.vakata 6348 * @param {jQuery} reference the element that was right clicked 6349 * @param {jQuery} element the DOM element of the menu itself 6350 * @param {Object} position the x & y coordinates of the menu 6351 */ 6352 $.vakata.context._trigger("show"); 6353 } 6354 }, 6355 hide : function () { 6356 if(vakata_context.is_visible) { 6357 vakata_context.element.hide().find("ul").hide().end().find(':focus').blur().end().detach(); 6358 vakata_context.is_visible = false; 6359 /** 6360 * triggered on the document when the contextmenu is hidden 6361 * @event 6362 * @plugin contextmenu 6363 * @name context_hide.vakata 6364 * @param {jQuery} reference the element that was right clicked 6365 * @param {jQuery} element the DOM element of the menu itself 6366 * @param {Object} position the x & y coordinates of the menu 6367 */ 6368 $.vakata.context._trigger("hide"); 6369 } 6370 } 6371 }; 6372 $(function () { 6373 right_to_left = $("body").css("direction") === "rtl"; 6374 var to = false; 6375 6376 vakata_context.element = $("<ul class='vakata-context'></ul>"); 6377 vakata_context.element 6378 .on("mouseenter", "li", function (e) { 6379 e.stopImmediatePropagation(); 6380 6381 if($.contains(this, e.relatedTarget)) { 6382 // пÑÐµÐ¼Ð°Ñ Ð½Ð°Ñо заÑади delegate mouseleave по-Ð´Ð¾Ð»Ñ 6383 // $(this).find(".vakata-context-hover").removeClass("vakata-context-hover"); 6384 return; 6385 } 6386 6387 if(to) { clearTimeout(to); } 6388 vakata_context.element.find(".vakata-context-hover").removeClass("vakata-context-hover").end(); 6389 6390 $(this) 6391 .siblings().find("ul").hide().end().end() 6392 .parentsUntil(".vakata-context", "li").addBack().addClass("vakata-context-hover"); 6393 $.vakata.context._show_submenu(this); 6394 }) 6395 // ÑеÑÑово - дали не наÑоваÑва? 6396 .on("mouseleave", "li", function (e) { 6397 if($.contains(this, e.relatedTarget)) { return; } 6398 $(this).find(".vakata-context-hover").addBack().removeClass("vakata-context-hover"); 6399 }) 6400 .on("mouseleave", function (e) { 6401 $(this).find(".vakata-context-hover").removeClass("vakata-context-hover"); 6402 if($.vakata.context.settings.hide_onmouseleave) { 6403 to = setTimeout( 6404 (function (t) { 6405 return function () { $.vakata.context.hide(); }; 6406 }(this)), $.vakata.context.settings.hide_onmouseleave); 6407 } 6408 }) 6409 .on("click", "a", function (e) { 6410 e.preventDefault(); 6411 //}) 6412 //.on("mouseup", "a", function (e) { 6413 if(!$(this).blur().parent().hasClass("vakata-context-disabled") && $.vakata.context._execute($(this).attr("rel")) !== false) { 6414 $.vakata.context.hide(); 6415 } 6416 }) 6417 .on('keydown', 'a', function (e) { 6418 var o = null; 6419 switch(e.which) { 6420 case 13: 6421 case 32: 6422 e.type = "click"; 6423 e.preventDefault(); 6424 $(e.currentTarget).trigger(e); 6425 break; 6426 case 37: 6427 if(vakata_context.is_visible) { 6428 vakata_context.element.find(".vakata-context-hover").last().closest("li").first().find("ul").hide().find(".vakata-context-hover").removeClass("vakata-context-hover").end().end().children('a').focus(); 6429 e.stopImmediatePropagation(); 6430 e.preventDefault(); 6431 } 6432 break; 6433 case 38:
vendor: 5,319 bytes, lines 6434-6555
6434 if(vakata_context.is_visible) { 6435 o = vakata_context.element.find("ul:visible").addBack().last().children(".vakata-context-hover").removeClass("vakata-context-hover").prevAll("li:not(.vakata-context-separator)").first(); 6436 if(!o.length) { o = vakata_context.element.find("ul:visible").addBack().last().children("li:not(.vakata-context-separator)").last(); } 6437 o.addClass("vakata-context-hover").children('a').focus(); 6438 e.stopImmediatePropagation(); 6439 e.preventDefault(); 6440 } 6441 break; 6442 case 39: 6443 if(vakata_context.is_visible) { 6444 vakata_context.element.find(".vakata-context-hover").last().children("ul").show().children("li:not(.vakata-context-separator)").removeClass("vakata-context-hover").first().addClass("vakata-context-hover").children('a').focus(); 6445 e.stopImmediatePropagation(); 6446 e.preventDefault(); 6447 } 6448 break; 6449 case 40: 6450 if(vakata_context.is_visible) { 6451 o = vakata_context.element.find("ul:visible").addBack().last().children(".vakata-context-hover").removeClass("vakata-context-hover").nextAll("li:not(.vakata-context-separator)").first(); 6452 if(!o.length) { o = vakata_context.element.find("ul:visible").addBack().last().children("li:not(.vakata-context-separator)").first(); } 6453 o.addClass("vakata-context-hover").children('a').focus(); 6454 e.stopImmediatePropagation(); 6455 e.preventDefault(); 6456 } 6457 break; 6458 case 27: 6459 $.vakata.context.hide(); 6460 e.preventDefault(); 6461 break; 6462 default: 6463 //console.log(e.which); 6464 break; 6465 } 6466 }) 6467 .on('keydown', function (e) { 6468 e.preventDefault(); 6469 var a = vakata_context.element.find('.vakata-contextmenu-shortcut-' + e.which).parent(); 6470 if(a.parent().not('.vakata-context-disabled')) { 6471 a.click(); 6472 } 6473 }); 6474 6475 $(document) 6476 .on("mousedown.vakata.jstree", function (e) { 6477 if(vakata_context.is_visible && vakata_context.element[0] !== e.target && !$.contains(vakata_context.element[0], e.target)) { 6478 $.vakata.context.hide(); 6479 } 6480 }) 6481 .on("context_show.vakata.jstree", function (e, data) { 6482 vakata_context.element.find("li:has(ul)").children("a").addClass("vakata-context-parent"); 6483 if(right_to_left) { 6484 vakata_context.element.addClass("vakata-context-rtl").css("direction", "rtl"); 6485 } 6486 // also apply a RTL class? 6487 vakata_context.element.find("ul").hide().end(); 6488 }); 6489 }); 6490 }($)); 6491 // $.jstree.defaults.plugins.push("contextmenu"); 6492 6493 6494/** 6495 * ### Drag'n'drop plugin 6496 * 6497 * Enables dragging and dropping of nodes in the tree, resulting in a move or copy operations. 6498 */ 6499 6500 /** 6501 * stores all defaults for the drag'n'drop plugin 6502 * @name $.jstree.defaults.dnd 6503 * @plugin dnd 6504 */ 6505 $.jstree.defaults.dnd = { 6506 /** 6507 * a boolean indicating if a copy should be possible while dragging (by pressint the meta key or Ctrl). Defaults to `true`. 6508 * @name $.jstree.defaults.dnd.copy 6509 * @plugin dnd 6510 */ 6511 copy : true, 6512 /** 6513 * a number indicating how long a node should remain hovered while dragging to be opened. Defaults to `500`. 6514 * @name $.jstree.defaults.dnd.open_timeout 6515 * @plugin dnd 6516 */ 6517 open_timeout : 500, 6518 /** 6519 * a function invoked each time a node is about to be dragged, invoked in the tree's scope and receives the nodes about to be dragged as an argument (array) and the event that started the drag - return `false` to prevent dragging 6520 * @name $.jstree.defaults.dnd.is_draggable 6521 * @plugin dnd 6522 */ 6523 is_draggable : true, 6524 /** 6525 * a boolean indicating if checks should constantly be made while the user is dragging the node (as opposed to checking only on drop), default is `true` 6526 * @name $.jstree.defaults.dnd.check_while_dragging 6527 * @plugin dnd 6528 */ 6529 check_while_dragging : true, 6530 /** 6531 * a boolean indicating if nodes from this tree should only be copied with dnd (as opposed to moved), default is `false` 6532 * @name $.jstree.defaults.dnd.always_copy 6533 * @plugin dnd 6534 */ 6535 always_copy : false, 6536 /** 6537 * when dropping a node "inside", this setting indicates the position the node should go to - it can be an integer or a string: "first" (same as 0) or "last", default is `0` 6538 * @name $.jstree.defaults.dnd.inside_pos 6539 * @plugin dnd 6540 */ 6541 inside_pos : 0, 6542 /** 6543 * when starting the drag on a node that is selected this setting controls if all selected nodes are dragged or only the single node, default is `true`, which means all selected nodes are dragged when the drag is started on a selected node 6544 * @name $.jstree.defaults.dnd.drag_selection 6545 * @plugin dnd 6546 */ 6547 drag_selection : true, 6548 /** 6549 * controls whether dnd works on touch devices. If left as boolean true dnd will work the same as in desktop browsers, which in some cases may impair scrolling. If set to boolean false dnd will not work on touch devices. There is a special third option - string "selected" which means only selected nodes can be dragged on touch devices. 6550 * @name $.jstree.defaults.dnd.touch 6551 * @plugin dnd 6552 */ 6553 touch : true, 6554 /** 6555 * controls whether items can be dropped anywhere on the node, not just on the anch
6555or, by default only the node anchor is a valid drop target. Works best with the wholerow plugin. If enabled on mobile depending on the interface it might be hard for the user to cancel the drop, since the whole tree container will be a valid drop target. 6556 * @name $.jstree.defaults.dnd.large_drop_target 6557 * @plugin dnd 6558 */ 6559 large_drop_target : false, 6560 /** 6561 * controls whether a drag can be initiated from any part of the node and not just the text/icon part, works best with the wholerow plugin. Keep in mind it can cause problems with tree scrolling on mobile depending on the interface - in that case set the touch option to "selected". 6562 * @name $.jstree.defaults.dnd.large_drag_target 6563 * @plugin dnd 6564 */ 6565 large_drag_target : false, 6566 /** 6567 * controls whether use HTML5 dnd api instead of classical. That will allow better integration of dnd events with other HTML5 controls. 6568 * @reference http://caniuse.com/#feat=dragndrop 6569 * @name $.jstree.defaults.dnd.use_html5 6570 * @plugin dnd 6571 */ 6572 use_html5: false 6573 }; 6574 var drg, elm; 6575 // TODO: now check works by checking for each node individually, how about max_children, unique, etc? 6576 $.jstree.plugins.dnd = function (options, parent) { 6577 this.init = function (el, options) { 6578 parent.init.call(this, el, options); 6579 this.settings.dnd.use_html5 = this.settings.dnd.use_html5 && ('draggable' in document.createElement('span')); 6580 }; 6581 this.bind = function () { 6582 parent.bind.call(this); 6583 6584 this.element 6585 .on(this.settings.dnd.use_html5 ? 'dragstart.jstree' : 'mousedown.jstree touchstart.jstree', this.settings.dnd.large_drag_target ? '.jstree-node' : '.jstree-anchor', $.proxy(function (e) { 6586 if(this.settings.dnd.large_drag_target && $(e.target).closest('.jstree-node')[0] !== e.currentTarget) { 6587 return true; 6588 } 6589 if(e.type === "touchstart" && (!this.settings.dnd.touch || (this.settings.dnd.touch === 'selected' && !$(e.currentTarget).closest('.jstree-node').children('.jstree-anchor').hasClass('jstree-clicked')))) { 6590 return true; 6591 } 6592 var obj = this.get_node(e.target), 6593 mlt = this.is_selected(obj) && this.settings.dnd.drag_selection ? this.get_top_selected().length : 1, 6594 txt = (mlt > 1 ? mlt + ' ' + this.get_string('nodes') : this.get_text(e.currentTarget)); 6595 if(this.settings.core.force_text) { 6596 txt = $.vakata.html.escape(txt); 6597 } 6598 if(obj && obj.id && obj.id !== $.jstree.root && (e.which === 1 || e.type === "touchstart" || e.type === "dragstart") && 6599 (this.settings.dnd.is_draggable === true || ($.isFunction(this.settings.dnd.is_draggable) && this.settings.dnd.is_draggable.call(this, (mlt > 1 ? this.get_top_selected(true) : [obj]), e))) 6600 ) { 6601 drg = { 'jstree' : true, 'origin' : this, 'obj' : this.get_node(obj,true), 'nodes' : mlt > 1 ? this.get_top_selected() : [obj.id] }; 6602 elm = e.currentTarget; 6603 if (this.settings.dnd.use_html5) { 6604 $.vakata.dnd._trigger('start', e, { 'helper': $(), 'element': elm, 'data': drg }); 6605 } else { 6606 this.element.trigger('mousedown.jstree'); 6607 return $.vakata.dnd.start(e, drg, '<div id="jstree-dnd" class="jstree-' + this.get_theme() + ' jstree-' + this.get_theme() + '-' + this.get_theme_variant() + ' ' + ( this.settings.core.themes.responsive ? ' jstree-dnd-responsive' : '' ) + '"><i class="jstree-icon jstree-er"></i>' + txt + '<ins class="jstree-copy" style="display:none;">+</ins></div>'); 6608 } 6609 } 6610 }, this)); 6611 if (this.settings.dnd.use_html5) { 6612 this.element 6613 .on('dragover.jstree', function (e) { 6614 e.preventDefault(); 6615 $.vakata.dnd._trigger('move', e, { 'helper': $(), 'element': elm, 'data': drg }); 6616 return false; 6617 }) 6618 //.on('dragenter.jstree', this.settings.dnd.large_drop_target ? '.jstree-node' : '.jstree-anchor', $.proxy(function (e) { 6619 // e.preventDefault(); 6620 // $.vakata.dnd._trigger('move', e, { 'helper': $(), 'element': elm, 'data': drg }); 6621 // return false; 6622 // }, this)) 6623 .on('drop.jstree', $.proxy(function (e) { 6624 e.preventDefault(); 6625 $.vakata.dnd._trigger('stop', e, { 'helper': $(), 'element': elm, 'data': drg }); 6626 return false; 6627 }, this)); 6628 } 6629 }; 6630 this.redraw_node = function(obj, deep, callback, force_render) { 6631 obj = parent.redraw_node.apply(this, arguments); 6632 if (obj && this.settings.dnd.use_html5) { 6633 if (this.settings.dnd.large_drag_target) { 6634 obj.setAttribute('draggable', true); 6635 } else { 6636 var i, j, tmp = null; 6637 for(i = 0, j = obj.childNodes.length; i < j; i++) { 6638 if(obj.childNodes[i] && obj.childNodes[i].className && obj.childNodes[i].className.indexOf("jstree-anchor") !== -1) { 6639 tmp = obj.childNodes[i];
vendor: 25,453 bytes, lines 6640-7272
6640 break; 6641 } 6642 } 6643 if(tmp) { 6644 tmp.setAttribute('draggable', true); 6645 } 6646 } 6647 } 6648 return obj; 6649 }; 6650 }; 6651 6652 $(function() { 6653 // bind only once for all instances 6654 var lastmv = false, 6655 laster = false, 6656 lastev = false, 6657 opento = false, 6658 marker = $('<div id="jstree-marker"> </div>').hide(); //.appendTo('body'); 6659 6660 $(document) 6661 .on('dnd_start.vakata.jstree', function (e, data) { 6662 lastmv = false; 6663 lastev = false; 6664 if(!data || !data.data || !data.data.jstree) { return; } 6665 marker.appendTo('body'); //.show(); 6666 }) 6667 .on('dnd_move.vakata.jstree', function (e, data) { 6668 var isDifferentNode = data.event.target !== lastev.target; 6669 if(opento) { 6670 if (!data.event || data.event.type !== 'dragover' || isDifferentNode) { 6671 clearTimeout(opento); 6672 } 6673 } 6674 if(!data || !data.data || !data.data.jstree) { return; } 6675 6676 // if we are hovering the marker image do nothing (can happen on "inside" drags) 6677 if(data.event.target.id && data.event.target.id === 'jstree-marker') { 6678 return; 6679 } 6680 lastev = data.event; 6681 6682 var ins = $.jstree.reference(data.event.target), 6683 ref = false, 6684 off = false, 6685 rel = false, 6686 tmp, l, t, h, p, i, o, ok, t1, t2, op, ps, pr, ip, tm, is_copy, pn; 6687 // if we are over an instance 6688 if(ins && ins._data && ins._data.dnd) { 6689 marker.attr('class', 'jstree-' + ins.get_theme() + ( ins.settings.core.themes.responsive ? ' jstree-dnd-responsive' : '' )); 6690 is_copy = data.data.origin && (data.data.origin.settings.dnd.always_copy || (data.data.origin.settings.dnd.copy && (data.event.metaKey || data.event.ctrlKey))); 6691 data.helper 6692 .children().attr('class', 'jstree-' + ins.get_theme() + ' jstree-' + ins.get_theme() + '-' + ins.get_theme_variant() + ' ' + ( ins.settings.core.themes.responsive ? ' jstree-dnd-responsive' : '' )) 6693 .find('.jstree-copy').first()[ is_copy ? 'show' : 'hide' ](); 6694 6695 // if are hovering the container itself add a new root node 6696 //console.log(data.event); 6697 if( (data.event.target === ins.element[0] || data.event.target === ins.get_container_ul()[0]) && ins.get_container_ul().children().length === 0) { 6698 ok = true; 6699 for(t1 = 0, t2 = data.data.nodes.length; t1 < t2; t1++) { 6700 ok = ok && ins.check( (data.data.origin && (data.data.origin.settings.dnd.always_copy || (data.data.origin.settings.dnd.copy && (data.event.metaKey || data.event.ctrlKey)) ) ? "copy_node" : "move_node"), (data.data.origin && data.data.origin !== ins ? data.data.origin.get_node(data.data.nodes[t1]) : data.data.nodes[t1]), $.jstree.root, 'last', { 'dnd' : true, 'ref' : ins.get_node($.jstree.root), 'pos' : 'i', 'origin' : data.data.origin, 'is_multi' : (data.data.origin && data.data.origin !== ins), 'is_foreign' : (!data.data.origin) }); 6701 if(!ok) { break; } 6702 } 6703 if(ok) { 6704 lastmv = { 'ins' : ins, 'par' : $.jstree.root, 'pos' : 'last' }; 6705 marker.hide(); 6706 data.helper.find('.jstree-icon').first().removeClass('jstree-er').addClass('jstree-ok'); 6707 if (data.event.originalEvent && data.event.originalEvent.dataTransfer) { 6708 data.event.originalEvent.dataTransfer.dropEffect = is_copy ? 'copy' : 'move'; 6709 } 6710 return; 6711 } 6712 } 6713 else { 6714 // if we are hovering a tree node 6715 ref = ins.settings.dnd.large_drop_target ? $(data.event.target).closest('.jstree-node').children('.jstree-anchor') : $(data.event.target).closest('.jstree-anchor'); 6716 if(ref && ref.length && ref.parent().is('.jstree-closed, .jstree-open, .jstree-leaf')) { 6717 off = ref.offset(); 6718 rel = (data.event.pageY !== undefined ? data.event.pageY : data.event.originalEvent.pageY) - off.top; 6719 h = ref.outerHeight(); 6720 if(rel < h / 3) { 6721 o = ['b', 'i', 'a']; 6722 } 6723 else if(rel > h - h / 3) { 6724 o = ['a', 'i', 'b']; 6725 } 6726 else { 6727 o = rel > h / 2 ? ['i', 'a', 'b'] : ['i', 'b', 'a']; 6728 } 6729 $.each(o, function (j, v) { 6730 switch(v) { 6731 case 'b': 6732 l = off.left - 6; 6733 t = off.top; 6734 p = ins.get_parent(ref); 6735 i = ref.parent().index(); 6736 break; 6737 case 'i': 6738 ip = ins.settings.dnd.inside_pos; 6739 tm = ins.get_node(ref.parent()); 6740 l = off.left - 2; 6741 t = off.top + h / 2 + 1; 6742 p = tm.id; 6743 i = ip === 'first' ? 0 : (ip === 'last' ? tm.children.length : Math.min(ip, tm.children.length)); 6744 break; 6745 case 'a': 6746 l = off.left - 6; 6747 t = off.top + h; 6748 p = ins.get_parent(ref); 6749 i = ref.parent().index() + 1; 6750 break; 6751 } 6752 ok = true; 6753 for(t1 = 0, t2 = data.data.nodes.length; t1 < t2; t1++) { 6754 op = data.data.origin && (data.data.origin.settings.dnd.always_copy || (data.data.origin.settings.dnd.copy && (data.event.metaKey || data.event.ctrlKey))) ? "copy_node" : "move_node"; 6755 ps = i; 6756 if(op === "move_node" && v === 'a' && (data.data.origin && data.data.origin === ins) && p === ins.get_parent(data.data.nodes[t1])) { 6757 pr = ins.get_node(p); 6758 if(ps > $.inArray(data.data.nodes[t1], pr.children)) { 6759 ps -= 1; 6760 } 6761 } 6762 ok = ok && ( (ins && ins.settings && ins.settings.dnd && ins.settings.dnd.check_while_dragging === false) || ins.check(op, (data.data.origin && data.data.origin !== ins ? data.data.origin.get_node(data.data.nodes[t1]) : data.data.nodes[t1]), p, ps, { 'dnd' : true, 'ref' : ins.get_node(ref.parent()), 'pos' : v, 'origin' : data.data.origin, 'is_multi' : (data.data.origin && data.data.origin !== ins), 'is_foreign' : (!data.data.origin) }) ); 6763 if(!ok) { 6764 if(ins && ins.last_error) { laster = ins.last_error(); } 6765 break; 6766 } 6767 } 6768 if(v === 'i' && ref.parent().is('.jstree-closed') && ins.settings.dnd.open_timeout) { 6769 if (!data.event || data.event.type !== 'dragover' || isDifferentNode) { 6770 if (opento) { clearTimeout(opento); } 6771 opento = setTimeout((function (x, z) { return function () { x.open_node(z); }; }(ins, ref)), ins.settings.dnd.open_timeout); 6772 } 6773 } 6774 if(ok) { 6775 pn = ins.get_node(p, true); 6776 if (!pn.hasClass('.jstree-dnd-parent')) { 6777 $('.jstree-dnd-parent').removeClass('jstree-dnd-parent'); 6778 pn.addClass('jstree-dnd-parent'); 6779 } 6780 lastmv = { 'ins' : ins, 'par' : p, 'pos' : v === 'i' && ip === 'last' && i === 0 && !ins.is_loaded(tm) ? 'last' : i }; 6781 marker.css({ 'left' : l + 'px', 'top' : t + 'px' }).show(); 6782 data.helper.find('.jstree-icon').first().removeClass('jstree-er').addClass('jstree-ok'); 6783 if (data.event.originalEvent && data.event.originalEvent.dataTransfer) { 6784 data.event.originalEvent.dataTransfer.dropEffect = is_copy ? 'copy' : 'move'; 6785 } 6786 laster = {}; 6787 o = true; 6788 return false; 6789 } 6790 }); 6791 if(o === true) { return; } 6792 } 6793 } 6794 } 6795 $('.jstree-dnd-parent').removeClass('jstree-dnd-parent'); 6796 lastmv = false; 6797 data.helper.find('.jstree-icon').removeClass('jstree-ok').addClass('jstree-er'); 6798 if (data.event.originalEvent && data.event.originalEvent.dataTransfer) { 6799 data.event.originalEvent.dataTransfer.dropEffect = 'none'; 6800 } 6801 marker.hide(); 6802 }) 6803 .on('dnd_scroll.vakata.jstree', function (e, data) { 6804 if(!data || !data.data || !data.data.jstree) { return; } 6805 marker.hide(); 6806 lastmv = false; 6807 lastev = false; 6808 data.helper.find('.jstree-icon').first().removeClass('jstree-ok').addClass('jstree-er'); 6809 }) 6810 .on('dnd_stop.vakata.jstree', function (e, data) { 6811 $('.jstree-dnd-parent').removeClass('jstree-dnd-parent'); 6812 if(opento) { clearTimeout(opento); } 6813 if(!data || !data.data || !data.data.jstree) { return; } 6814 marker.hide().detach(); 6815 var i, j, nodes = []; 6816 if(lastmv) { 6817 for(i = 0, j = data.data.nodes.length; i < j; i++) { 6818 nodes[i] = data.data.origin ? data.data.origin.get_node(data.data.nodes[i]) : data.data.nodes[i]; 6819 } 6820 lastmv.ins[ data.data.origin && (data.data.origin.settings.dnd.always_copy || (data.data.origin.settings.dnd.copy && (data.event.metaKey || data.event.ctrlKey))) ? 'copy_node' : 'move_node' ](nodes, lastmv.par, lastmv.pos, false, false, false, data.data.origin); 6821 } 6822 else { 6823 i = $(data.event.target).closest('.jstree'); 6824 if(i.length && laster && laster.error && laster.error === 'check') { 6825 i = i.jstree(true); 6826 if(i) { 6827 i.settings.core.error.call(this, laster); 6828 } 6829 } 6830 } 6831 lastev = false; 6832 lastmv = false; 6833 }) 6834 .on('keyup.jstree keydown.jstree', function (e, data) { 6835 data = $.vakata.dnd._get(); 6836 if(data && data.data && data.data.jstree) { 6837 if (e.type === "keyup" && e.which === 27) { 6838 if (opento) { clearTimeout(opento); } 6839 lastmv = false; 6840 laster = false; 6841 lastev = false; 6842 opento = false; 6843 marker.hide().detach(); 6844 $.vakata.dnd._clean(); 6845 } else { 6846 data.helper.find('.jstree-copy').first()[ data.data.origin && (data.data.origin.settings.dnd.always_copy || (data.data.origin.settings.dnd.copy && (e.metaKey || e.ctrlKey))) ? 'show' : 'hide' ](); 6847 if(lastev) { 6848 lastev.metaKey = e.metaKey; 6849 lastev.ctrlKey = e.ctrlKey; 6850 $.vakata.dnd._trigger('move', lastev); 6851 } 6852 } 6853 } 6854 }); 6855 }); 6856 6857 // helpers 6858 (function ($) { 6859 $.vakata.html = { 6860 div : $('<div />'), 6861 escape : function (str) { 6862 return $.vakata.html.div.text(str).html(); 6863 }, 6864 strip : function (str) { 6865 return $.vakata.html.div.empty().append($.parseHTML(str)).text(); 6866 } 6867 }; 6868 // private variable 6869 var vakata_dnd = { 6870 element : false, 6871 target : false, 6872 is_down : false, 6873 is_drag : false, 6874 helper : false, 6875 helper_w: 0, 6876 data : false, 6877 init_x : 0, 6878 init_y : 0, 6879 scroll_l: 0, 6880 scroll_t: 0, 6881 scroll_e: false, 6882 scroll_i: false, 6883 is_touch: false 6884 }; 6885 $.vakata.dnd = { 6886 settings : { 6887 scroll_speed : 10, 6888 scroll_proximity : 20, 6889 helper_left : 5, 6890 helper_top : 10, 6891 threshold : 5, 6892 threshold_touch : 50 6893 }, 6894 _trigger : function (event_name, e, data) { 6895 if (data === undefined) { 6896 data = $.vakata.dnd._get(); 6897 } 6898 data.event = e; 6899 $(document).triggerHandler("dnd_" + event_name + ".vakata", data); 6900 }, 6901 _get : function () { 6902 return { 6903 "data" : vakata_dnd.data, 6904 "element" : vakata_dnd.element, 6905 "helper" : vakata_dnd.helper 6906 }; 6907 }, 6908 _clean : function () { 6909 if(vakata_dnd.helper) { vakata_dnd.helper.remove(); } 6910 if(vakata_dnd.scroll_i) { clearInterval(vakata_dnd.scroll_i); vakata_dnd.scroll_i = false; } 6911 vakata_dnd = { 6912 element : false, 6913 target : false, 6914 is_down : false, 6915 is_drag : false, 6916 helper : false, 6917 helper_w: 0, 6918 data : false, 6919 init_x : 0, 6920 init_y : 0, 6921 scroll_l: 0, 6922 scroll_t: 0, 6923 scroll_e: false, 6924 scroll_i: false, 6925 is_touch: false 6926 }; 6927 $(document).off("mousemove.vakata.jstree touchmove.vakata.jstree", $.vakata.dnd.drag); 6928 $(document).off("mouseup.vakata.jstree touchend.vakata.jstree", $.vakata.dnd.stop); 6929 }, 6930 _scroll : function (init_only) { 6931 if(!vakata_dnd.scroll_e || (!vakata_dnd.scroll_l && !vakata_dnd.scroll_t)) { 6932 if(vakata_dnd.scroll_i) { clearInterval(vakata_dnd.scroll_i); vakata_dnd.scroll_i = false; } 6933 return false; 6934 } 6935 if(!vakata_dnd.scroll_i) { 6936 vakata_dnd.scroll_i = setInterval($.vakata.dnd._scroll, 100); 6937 return false; 6938 } 6939 if(init_only === true) { return false; } 6940 6941 var i = vakata_dnd.scroll_e.scrollTop(), 6942 j = vakata_dnd.scroll_e.scrollLeft(); 6943 vakata_dnd.scroll_e.scrollTop(i + vakata_dnd.scroll_t * $.vakata.dnd.settings.scroll_speed); 6944 vakata_dnd.scroll_e.scrollLeft(j + vakata_dnd.scroll_l * $.vakata.dnd.settings.scroll_speed); 6945 if(i !== vakata_dnd.scroll_e.scrollTop() || j !== vakata_dnd.scroll_e.scrollLeft()) { 6946 /** 6947 * triggered on the document when a drag causes an element to scroll 6948 * @event 6949 * @plugin dnd 6950 * @name dnd_scroll.vakata 6951 * @param {Mixed} data any data supplied with the call to $.vakata.dnd.start 6952 * @param {DOM} element the DOM element being dragged 6953 * @param {jQuery} helper the helper shown next to the mouse 6954 * @param {jQuery} event the element that is scrolling 6955 */ 6956 $.vakata.dnd._trigger("scroll", vakata_dnd.scroll_e); 6957 } 6958 }, 6959 start : function (e, data, html) { 6960 if(e.type === "touchstart" && e.originalEvent && e.originalEvent.changedTouches && e.originalEvent.changedTouches[0]) { 6961 e.pageX = e.originalEvent.changedTouches[0].pageX; 6962 e.pageY = e.originalEvent.changedTouches[0].pageY; 6963 e.target = document.elementFromPoint(e.originalEvent.changedTouches[0].pageX - window.pageXOffset, e.originalEvent.changedTouches[0].pageY - window.pageYOffset); 6964 } 6965 if(vakata_dnd.is_drag) { $.vakata.dnd.stop({}); } 6966 try { 6967 e.currentTarget.unselectable = "on"; 6968 e.currentTarget.onselectstart = function() { return false; }; 6969 if(e.currentTarget.style) { 6970 e.currentTarget.style.touchAction = "none"; 6971 e.currentTarget.style.msTouchAction = "none"; 6972 e.currentTarget.style.MozUserSelect = "none"; 6973 } 6974 } catch(ignore) { } 6975 vakata_dnd.init_x = e.pageX; 6976 vakata_dnd.init_y = e.pageY; 6977 vakata_dnd.data = data; 6978 vakata_dnd.is_down = true; 6979 vakata_dnd.element = e.currentTarget; 6980 vakata_dnd.target = e.target; 6981 vakata_dnd.is_touch = e.type === "touchstart"; 6982 if(html !== false) { 6983 vakata_dnd.helper = $("<div id='vakata-dnd'></div>").html(html).css({ 6984 "display" : "block", 6985 "margin" : "0", 6986 "padding" : "0", 6987 "position" : "absolute", 6988 "top" : "-2000px", 6989 "lineHeight" : "16px", 6990 "zIndex" : "10000" 6991 }); 6992 } 6993 $(document).on("mousemove.vakata.jstree touchmove.vakata.jstree", $.vakata.dnd.drag); 6994 $(document).on("mouseup.vakata.jstree touchend.vakata.jstree", $.vakata.dnd.stop); 6995 return false; 6996 }, 6997 drag : function (e) { 6998 if(e.type === "touchmove" && e.originalEvent && e.originalEvent.changedTouches && e.originalEvent.changedTouches[0]) { 6999 e.pageX = e.originalEvent.changedTouches[0].pageX; 7000 e.pageY = e.originalEvent.changedTouches[0].pageY; 7001 e.target = document.elementFromPoint(e.originalEvent.changedTouches[0].pageX - window.pageXOffset, e.originalEvent.changedTouches[0].pageY - window.pageYOffset); 7002 } 7003 if(!vakata_dnd.is_down) { return; } 7004 if(!vakata_dnd.is_drag) { 7005 if( 7006 Math.abs(e.pageX - vakata_dnd.init_x) > (vakata_dnd.is_touch ? $.vakata.dnd.settings.threshold_touch : $.vakata.dnd.settings.threshold) || 7007 Math.abs(e.pageY - vakata_dnd.init_y) > (vakata_dnd.is_touch ? $.vakata.dnd.settings.threshold_touch : $.vakata.dnd.settings.threshold) 7008 ) { 7009 if(vakata_dnd.helper) { 7010 vakata_dnd.helper.appendTo("body"); 7011 vakata_dnd.helper_w = vakata_dnd.helper.outerWidth(); 7012 } 7013 vakata_dnd.is_drag = true; 7014 $(vakata_dnd.target).one('click.vakata', false); 7015 /** 7016 * triggered on the document when a drag starts 7017 * @event 7018 * @plugin dnd 7019 * @name dnd_start.vakata 7020 * @param {Mixed} data any data supplied with the call to $.vakata.dnd.start 7021 * @param {DOM} element the DOM element being dragged 7022 * @param {jQuery} helper the helper shown next to the mouse 7023 * @param {Object} event the event that caused the start (probably mousemove) 7024 */ 7025 $.vakata.dnd._trigger("start", e); 7026 } 7027 else { return; } 7028 } 7029 7030 var d = false, w = false, 7031 dh = false, wh = false, 7032 dw = false, ww = false, 7033 dt = false, dl = false, 7034 ht = false, hl = false; 7035 7036 vakata_dnd.scroll_t = 0; 7037 vakata_dnd.scroll_l = 0; 7038 vakata_dnd.scroll_e = false; 7039 $($(e.target).parentsUntil("body").addBack().get().reverse()) 7040 .filter(function () { 7041 return (/^auto|scroll$/).test($(this).css("overflow")) && 7042 (this.scrollHeight > this.offsetHeight || this.scrollWidth > this.offsetWidth); 7043 }) 7044 .each(function () { 7045 var t = $(this), o = t.offset(); 7046 if(this.scrollHeight > this.offsetHeight) { 7047 if(o.top + t.height() - e.pageY < $.vakata.dnd.settings.scroll_proximity) { vakata_dnd.scroll_t = 1; } 7048 if(e.pageY - o.top < $.vakata.dnd.settings.scroll_proximity) { vakata_dnd.scroll_t = -1; } 7049 } 7050 if(this.scrollWidth > this.offsetWidth) { 7051 if(o.left + t.width() - e.pageX < $.vakata.dnd.settings.scroll_proximity) { vakata_dnd.scroll_l = 1; } 7052 if(e.pageX - o.left < $.vakata.dnd.settings.scroll_proximity) { vakata_dnd.scroll_l = -1; } 7053 } 7054 if(vakata_dnd.scroll_t || vakata_dnd.scroll_l) { 7055 vakata_dnd.scroll_e = $(this); 7056 return false; 7057 } 7058 }); 7059 7060 if(!vakata_dnd.scroll_e) { 7061 d = $(document); w = $(window); 7062 dh = d.height(); wh = w.height(); 7063 dw = d.width(); ww = w.width(); 7064 dt = d.scrollTop(); dl = d.scrollLeft(); 7065 if(dh > wh && e.pageY - dt < $.vakata.dnd.settings.scroll_proximity) { vakata_dnd.scroll_t = -1; } 7066 if(dh > wh && wh - (e.pageY - dt) < $.vakata.dnd.settings.scroll_proximity) { vakata_dnd.scroll_t = 1; } 7067 if(dw > ww && e.pageX - dl < $.vakata.dnd.settings.scroll_proximity) { vakata_dnd.scroll_l = -1; } 7068 if(dw > ww && ww - (e.pageX - dl) < $.vakata.dnd.settings.scroll_proximity) { vakata_dnd.scroll_l = 1; } 7069 if(vakata_dnd.scroll_t || vakata_dnd.scroll_l) { 7070 vakata_dnd.scroll_e = d; 7071 } 7072 } 7073 if(vakata_dnd.scroll_e) { $.vakata.dnd._scroll(true); } 7074 7075 if(vakata_dnd.helper) { 7076 ht = parseInt(e.pageY + $.vakata.dnd.settings.helper_top, 10); 7077 hl = parseInt(e.pageX + $.vakata.dnd.settings.helper_left, 10); 7078 if(dh && ht + 25 > dh) { ht = dh - 50; } 7079 if(dw && hl + vakata_dnd.helper_w > dw) { hl = dw - (vakata_dnd.helper_w + 2); } 7080 vakata_dnd.helper.css({ 7081 left : hl + "px", 7082 top : ht + "px" 7083 }); 7084 } 7085 /** 7086 * triggered on the document when a drag is in progress 7087 * @event 7088 * @plugin dnd 7089 * @name dnd_move.vakata 7090 * @param {Mixed} data any data supplied with the call to $.vakata.dnd.start 7091 * @param {DOM} element the DOM element being dragged 7092 * @param {jQuery} helper the helper shown next to the mouse 7093 * @param {Object} event the event that caused this to trigger (most likely mousemove) 7094 */ 7095 $.vakata.dnd._trigger("move", e); 7096 return false; 7097 }, 7098 stop : function (e) { 7099 if(e.type === "touchend" && e.originalEvent && e.originalEvent.changedTouches && e.originalEvent.changedTouches[0]) { 7100 e.pageX = e.originalEvent.changedTouches[0].pageX; 7101 e.pageY = e.originalEvent.changedTouches[0].pageY; 7102 e.target = document.elementFromPoint(e.originalEvent.changedTouches[0].pageX - window.pageXOffset, e.originalEvent.changedTouches[0].pageY - window.pageYOffset); 7103 } 7104 if(vakata_dnd.is_drag) { 7105 /** 7106 * triggered on the document when a drag stops (the dragged element is dropped) 7107 * @event 7108 * @plugin dnd 7109 * @name dnd_stop.vakata 7110 * @param {Mixed} data any data supplied with the call to $.vakata.dnd.start 7111 * @param {DOM} element the DOM element being dragged 7112 * @param {jQuery} helper the helper shown next to the mouse 7113 * @param {Object} event the event that caused the stop 7114 */ 7115 if (e.target !== vakata_dnd.target) { 7116 $(vakata_dnd.target).off('click.vakata'); 7117 } 7118 $.vakata.dnd._trigger("stop", e); 7119 } 7120 else { 7121 if(e.type === "touchend" && e.target === vakata_dnd.target) { 7122 var to = setTimeout(function () { $(e.target).click(); }, 100); 7123 $(e.target).one('click', function() { if(to) { clearTimeout(to); } }); 7124 } 7125 } 7126 $.vakata.dnd._clean(); 7127 return false; 7128 } 7129 }; 7130 }($)); 7131 7132 // include the dnd plugin by default 7133 // $.jstree.defaults.plugins.push("dnd"); 7134 7135 7136/** 7137 * ### Massload plugin 7138 * 7139 * Adds massload functionality to jsTree, so that multiple nodes can be loaded in a single request (only useful with lazy loading). 7140 */ 7141 7142 /** 7143 * massload configuration 7144 * 7145 * It is possible to set this to a standard jQuery-like AJAX config. 7146 * In addition to the standard jQuery ajax options here you can supply functions for `data` and `url`, the functions will be run in the current instance's scope and a param will be passed indicating which node IDs need to be loaded, the return value of those functions will be used. 7147 * 7148 * You can also set this to a function, that function will receive the node IDs being loaded as argument and a second param which is a function (callback) which should be called with the result. 7149 * 7150 * Both the AJAX and the function approach rely on the same return value - an object where the keys are the node IDs, and the value is the children of that node as an array. 7151 * 7152 * { 7153 * "id1" : [{ "text" : "Child of ID1", "id" : "c1" }, { "text" : "Another child of ID1", "id" : "c2" }], 7154 * "id2" : [{ "text" : "Child of ID2", "id" : "c3" }] 7155 * } 7156 * 7157 * @name $.jstree.defaults.massload 7158 * @plugin massload 7159 */ 7160 $.jstree.defaults.massload = null; 7161 $.jstree.plugins.massload = function (options, parent) { 7162 this.init = function (el, options) { 7163 this._data.massload = {}; 7164 parent.init.call(this, el, options); 7165 }; 7166 this._load_nodes = function (nodes, callback, is_callback, force_reload) { 7167 var s = this.settings.massload, 7168 nodesString = JSON.stringify(nodes), 7169 toLoad = [], 7170 m = this._model.data, 7171 i, j, dom; 7172 if (!is_callback) { 7173 for(i = 0, j = nodes.length; i < j; i++) { 7174 if(!m[nodes[i]] || ( (!m[nodes[i]].state.loaded && !m[nodes[i]].state.failed) || force_reload) ) { 7175 toLoad.push(nodes[i]); 7176 dom = this.get_node(nodes[i], true); 7177 if (dom && dom.length) { 7178 dom.addClass("jstree-loading").attr('aria-busy',true); 7179 } 7180 } 7181 } 7182 this._data.massload = {}; 7183 if (toLoad.length) { 7184 if($.isFunction(s)) { 7185 return s.call(this, toLoad, $.proxy(function (data) { 7186 var i, j; 7187 if(data) { 7188 for(i in data) { 7189 if(data.hasOwnProperty(i)) { 7190 this._data.massload[i] = data[i]; 7191 } 7192 } 7193 } 7194 for(i = 0, j = nodes.length; i < j; i++) { 7195 dom = this.get_node(nodes[i], true); 7196 if (dom && dom.length) { 7197 dom.removeClass("jstree-loading").attr('aria-busy',false); 7198 } 7199 } 7200 parent._load_nodes.call(this, nodes, callback, is_callback, force_reload); 7201 }, this)); 7202 } 7203 if(typeof s === 'object' && s && s.url) { 7204 s = $.extend(true, {}, s); 7205 if($.isFunction(s.url)) { 7206 s.url = s.url.call(this, toLoad); 7207 } 7208 if($.isFunction(s.data)) { 7209 s.data = s.data.call(this, toLoad); 7210 } 7211 return $.ajax(s) 7212 .done($.proxy(function (data,t,x) { 7213 var i, j; 7214 if(data) { 7215 for(i in data) { 7216 if(data.hasOwnProperty(i)) { 7217 this._data.massload[i] = data[i]; 7218 } 7219 } 7220 } 7221 for(i = 0, j = nodes.length; i < j; i++) { 7222 dom = this.get_node(nodes[i], true); 7223 if (dom && dom.length) { 7224 dom.removeClass("jstree-loading").attr('aria-busy',false); 7225 } 7226 } 7227 parent._load_nodes.call(this, nodes, callback, is_callback, force_reload); 7228 }, this)) 7229 .fail($.proxy(function (f) { 7230 parent._load_nodes.call(this, nodes, callback, is_callback, force_reload); 7231 }, this)); 7232 } 7233 } 7234 } 7235 return parent._load_nodes.call(this, nodes, callback, is_callback, force_reload); 7236 }; 7237 this._load_node = function (obj, callback) { 7238 var data = this._data.massload[obj.id], 7239 rslt = null, dom; 7240 if(data) { 7241 rslt = this[typeof data === 'string' ? '_append_html_data' : '_append_json_data']( 7242 obj, 7243 typeof data === 'string' ? $($.parseHTML(data)).filter(function () { return this.nodeType !== 3; }) : data, 7244 function (status) { callback.call(this, status); } 7245 ); 7246 dom = this.get_node(obj.id, true); 7247 if (dom && dom.length) { 7248 dom.removeClass("jstree-loading").attr('aria-busy',false); 7249 } 7250 delete this._data.massload[obj.id]; 7251 return rslt; 7252 } 7253 return parent._load_node.call(this, obj, callback); 7254 }; 7255 }; 7256 7257/** 7258 * ### Search plugin 7259 * 7260 * Adds search functionality to jsTree. 7261 */ 7262 7263 /** 7264 * stores all defaults for the search plugin 7265 * @name $.jstree.defaults.search 7266 * @plugin search 7267 */ 7268 $.jstree.defaults.search = { 7269 /** 7270 * a jQuery-like AJAX config, which jstree uses if a server should be queried for results. 7271 * 7272 * A `str` (which is the search string) parameter will be added with the request, an optional `inside` parameter will be added if the search is limited to a node id. The expected result is a JSON array with
7272nodes that need to be opened so that matching nodes will be revealed. 7273 * Leave this setting as `false` to not query the server. You can also set this to a function, which will be invoked in the instance's scope and receive 3 parameters - the search string, the callback to call with the array of nodes to load, and the optional node ID to limit the search to 7274 * @name $.jstree.defaults.search.ajax 7275 * @plugin search 7276 */ 7277 ajax : false, 7278 /** 7279 * Indicates if the search should be fuzzy or not (should `chnd3` match `child node 3`). Default is `false`. 7280 * @name $.jstree.defaults.search.fuzzy 7281 * @plugin search 7282 */ 7283 fuzzy : false, 7284 /** 7285 * Indicates if the search should be case sensitive. Default is `false`. 7286 * @name $.jstree.defaults.search.case_sensitive 7287 * @plugin search 7288 */ 7289 case_sensitive : false, 7290 /** 7291 * Indicates if the tree should be filtered (by default) to show only matching nodes (keep in mind this can be a heavy on large trees in old browsers). 7292 * This setting can be changed at runtime when calling the search method. Default is `false`. 7293 * @name $.jstree.defaults.search.show_only_matches 7294 * @plugin search 7295 */ 7296 show_only_matches : false, 7297 /** 7298 * Indicates if the children of matched element are shown (when show_only_matches is true) 7299 * This setting can be changed at runtime when calling the search method. Default is `false`. 7300 * @name $.jstree.defaults.search.show_only_matches_children 7301 * @plugin search 7302 */ 7303 show_only_matches_children : false, 7304 /** 7305 * Indicates if all nodes opened to reveal the search result, should be closed when the search is cleared or a new search is performed. Default is `true`. 7306 * @name $.jstree.defaults.search.close_opened_onclear 7307 * @plugin search 7308 */ 7309 close_opened_onclear : true, 7310 /** 7311 * Indicates if only leaf nodes should be included in search results. Default is `false`. 7312 * @name $.jstree.defaults.search.search_leaves_only 7313 * @plugin search 7314 */ 7315 search_leaves_only : false, 7316 /** 7317 * If set to a function it wil be called in the instance's scope with two arguments - search string and node (where node will be every node in the structure, so use with caution). 7318 * If the function returns a truthy value the node will be considered a match (it might not be displayed if search_only_leaves is set to true and the node is not a leaf). Default is `false`. 7319 * @name $.jstree.defaults.search.search_callback 7320 * @plugin search 7321 */ 7322 search_callback : false 7323 }; 7324 7325 $.jstree.plugins.search = function (options, parent) { 7326 this.bind = function () { 7327 parent.bind.call(this); 7328 7329 this._data.search.str = ""; 7330 this._data.search.dom = $(); 7331 this._data.search.res = []; 7332 this._data.search.opn = []; 7333 this._data.search.som = false; 7334 this._data.search.smc = false; 7335 this._data.search.hdn = []; 7336 7337 this.element 7338 .on("search.jstree", $.proxy(function (e, data) { 7339 if(this._data.search.som && data.res.length) { 7340 var m = this._model.data, i, j, p = [], k, l; 7341 for(i = 0, j = data.res.length; i < j; i++) { 7342 if(m[data.res[i]] && !m[data.res[i]].state.hidden) { 7343 p.push(data.res[i]); 7344 p = p.concat(m[data.res[i]].parents); 7345 if(this._data.search.smc) { 7346 for (k = 0, l = m[data.res[i]].children_d.length; k < l; k++) { 7347 if (m[m[data.res[i]].children_d[k]] && !m[m[data.res[i]].children_d[k]].state.hidden) { 7348 p.push(m[data.res[i]].children_d[k]); 7349 } 7350 } 7351 } 7352 } 7353 } 7354 p = $.vakata.array_remove_item($.vakata.array_unique(p), $.jstree.root); 7355 this._data.search.hdn = this.hide_all(true); 7356 this.show_node(p, true); 7357 this.redraw(true); 7358 } 7359 }, this)) 7360 .on("clear_search.jstree", $.proxy(function (e, data) { 7361 if(this._data.search.som && data.res.length) { 7362 this.show_node(this._data.search.hdn, true); 7363 this.redraw(true); 7364 } 7365 }, this)); 7366 }; 7367 /** 7368 * used to search the tree nodes for a given string 7369 * @name search(str [, skip_async]) 7370 * @param {String} str the search string 7371 * @param {Boolean} skip_async if set to true server will not be queried even if configured 7372 * @param {Boolean} show_only_matches if set to true only matching nodes will be shown (keep in mind this can be very slow on large trees or old browsers) 7373 * @param {mixed} inside an optional node to whose children to limit the search 7374 * @param {Boolean} append if set to true the results of this search are appended to the previous search 7375 * @plugin search 7376 * @trigger search.jstree 7377 */ 7378 this.search = function (str, skip_async, show_only_matches, inside, append, show_only_matches_children) { 7379 if(str === false || $.trim(str.toString()) === "") { 7380 return this.clear_search(); 7381 } 7382 inside = this.get_node(inside); 7383 inside = inside && inside.id ? inside.id : null; 7384 str = str.toString(); 7385 var s = this.settings.search, 7386 a = s.ajax ? s.ajax : false, 7387 m = this._model.data, 7388 f = null, 7389 r = [], 7390 p = [], i, j; 7391 if(this._data.search.res.length && !append) { 7392 this.clear_search(); 7393 } 7394 if(show_only_matches === undefined) { 7395 show_only_matches = s.show_only_matches; 7396 } 7397 if(show_only_matches_children === undefined) { 7398 show_only_matches_children = s.show_only_matches_children; 7399 } 7400 if(!skip_async && a !== false) { 7401 if($.isFunction(a)) { 7402 return a.call(this, str, $.proxy(function (d) { 7403 if(d && d.d) { d = d.d; } 7404 this._load_nodes(!$.isArray(d) ? [] : $.vakata.array_unique(d), function () { 7405 this.search(str, true, show_only_matches, inside, append, show_only_matches_children); 7406 });
vendor: 9,463 bytes, lines 7407-7671
7407 }, this), inside); 7408 } 7409 else { 7410 a = $.extend({}, a); 7411 if(!a.data) { a.data = {}; } 7412 a.data.str = str; 7413 if(inside) { 7414 a.data.inside = inside; 7415 } 7416 if (this._data.search.lastRequest) { 7417 this._data.search.lastRequest.abort(); 7418 } 7419 this._data.search.lastRequest = $.ajax(a) 7420 .fail($.proxy(function () { 7421 this._data.core.last_error = { 'error' : 'ajax', 'plugin' : 'search', 'id' : 'search_01', 'reason' : 'Could not load search parents', 'data' : JSON.stringify(a) }; 7422 this.settings.core.error.call(this, this._data.core.last_error); 7423 }, this)) 7424 .done($.proxy(function (d) { 7425 if(d && d.d) { d = d.d; } 7426 this._load_nodes(!$.isArray(d) ? [] : $.vakata.array_unique(d), function () { 7427 this.search(str, true, show_only_matches, inside, append, show_only_matches_children); 7428 }); 7429 }, this)); 7430 return this._data.search.lastRequest; 7431 } 7432 } 7433 if(!append) { 7434 this._data.search.str = str; 7435 this._data.search.dom = $(); 7436 this._data.search.res = []; 7437 this._data.search.opn = []; 7438 this._data.search.som = show_only_matches; 7439 this._data.search.smc = show_only_matches_children; 7440 } 7441 7442 f = new $.vakata.search(str, true, { caseSensitive : s.case_sensitive, fuzzy : s.fuzzy }); 7443 $.each(m[inside ? inside : $.jstree.root].children_d, function (ii, i) { 7444 var v = m[i]; 7445 if(v.text && !v.state.hidden && (!s.search_leaves_only || (v.state.loaded && v.children.length === 0)) && ( (s.search_callback && s.search_callback.call(this, str, v)) || (!s.search_callback && f.search(v.text).isMatch) ) ) { 7446 r.push(i); 7447 p = p.concat(v.parents); 7448 } 7449 }); 7450 if(r.length) { 7451 p = $.vakata.array_unique(p); 7452 for(i = 0, j = p.length; i < j; i++) { 7453 if(p[i] !== $.jstree.root && m[p[i]] && this.open_node(p[i], null, 0) === true) { 7454 this._data.search.opn.push(p[i]); 7455 } 7456 } 7457 if(!append) { 7458 this._data.search.dom = $(this.element[0].querySelectorAll('#' + $.map(r, function (v) { return "0123456789".indexOf(v[0]) !== -1 ? '\\3' + v[0] + ' ' + v.substr(1).replace($.jstree.idregex,'\\$&') : v.replace($.jstree.idregex,'\\$&'); }).join(', #'))); 7459 this._data.search.res = r; 7460 } 7461 else { 7462 this._data.search.dom = this._data.search.dom.add($(this.element[0].querySelectorAll('#' + $.map(r, function (v) { return "0123456789".indexOf(v[0]) !== -1 ? '\\3' + v[0] + ' ' + v.substr(1).replace($.jstree.idregex,'\\$&') : v.replace($.jstree.idregex,'\\$&'); }).join(', #')))); 7463 this._data.search.res = $.vakata.array_unique(this._data.search.res.concat(r)); 7464 } 7465 this._data.search.dom.children(".jstree-anchor").addClass('jstree-search'); 7466 } 7467 /** 7468 * triggered after search is complete 7469 * @event 7470 * @name search.jstree 7471 * @param {jQuery} nodes a jQuery collection of matching nodes 7472 * @param {String} str the search string 7473 * @param {Array} res a collection of objects represeing the matching nodes 7474 * @plugin search 7475 */ 7476 this.trigger('search', { nodes : this._data.search.dom, str : str, res : this._data.search.res, show_only_matches : show_only_matches }); 7477 }; 7478 /** 7479 * used to clear the last search (removes classes and shows all nodes if filtering is on) 7480 * @name clear_search() 7481 * @plugin search 7482 * @trigger clear_search.jstree 7483 */ 7484 this.clear_search = function () { 7485 if(this.settings.search.close_opened_onclear) { 7486 this.close_node(this._data.search.opn, 0); 7487 } 7488 /** 7489 * triggered after search is complete 7490 * @event 7491 * @name clear_search.jstree 7492 * @param {jQuery} nodes a jQuery collection of matching nodes (the result from the last search) 7493 * @param {String} str the search string (the last search string) 7494 * @param {Array} res a collection of objects represeing the matching nodes (the result from the last search) 7495 * @plugin search 7496 */ 7497 this.trigger('clear_search', { 'nodes' : this._data.search.dom, str : this._data.search.str, res : this._data.search.res }); 7498 if(this._data.search.res.length) { 7499 this._data.search.dom = $(this.element[0].querySelectorAll('#' + $.map(this._data.search.res, function (v) { 7500 return "0123456789".indexOf(v[0]) !== -1 ? '\\3' + v[0] + ' ' + v.substr(1).replace($.jstree.idregex,'\\$&') : v.replace($.jstree.idregex,'\\$&'); 7501 }).join(', #'))); 7502 this._data.search.dom.children(".jstree-anchor").removeClass("jstree-search"); 7503 } 7504 this._data.search.str = ""; 7505 this._data.search.res = []; 7506 this._data.search.opn = []; 7507 this._data.search.dom = $(); 7508 }; 7509 7510 this.redraw_node = function(obj, deep, callback, force_render) { 7511 obj = parent.redraw_node.apply(this, arguments); 7512 if(obj) { 7513 if($.inArray(obj.id, this._data.search.res) !== -1) { 7514 var i, j, tmp = null; 7515 for(i = 0, j = obj.childNodes.length; i < j; i++) { 7516 if(obj.childNodes[i] && obj.childNodes[i].className && obj.childNodes[i].className.indexOf("jstree-anchor") !== -1) { 7517 tmp = obj.childNodes[i]; 7518 break; 7519 } 7520 } 7521 if(tmp) { 7522 tmp.className += ' jstree-search'; 7523 } 7524 } 7525 } 7526 return obj; 7527 }; 7528 }; 7529 7530 // helpers 7531 (function ($) { 7532 // from http://kiro.me/projects/fuse.html 7533 $.vakata.search = function(pattern, txt, options) { 7534 options = options || {}; 7535 options = $.extend({}, $.vakata.search.defaults, options); 7536 if(options.fuzzy !== false) { 7537 options.fuzzy = true; 7538 } 7539 pattern = options.caseSensitive ? pattern : pattern.toLowerCase(); 7540 var MATCH_LOCATION = options.location, 7541 MATCH_DISTANCE = options.distance, 7542 MATCH_THRESHOLD = options.threshold, 7543 patternLen = pattern.length, 7544 matchmask, pattern_alphabet, match_bitapScore, search; 7545 if(patternLen > 32) { 7546 options.fuzzy = false; 7547 } 7548 if(options.fuzzy) { 7549 matchmask = 1 << (patternLen - 1); 7550 pattern_alphabet = (function () { 7551 var mask = {}, 7552 i = 0; 7553 for (i = 0; i < patternLen; i++) { 7554 mask[pattern.charAt(i)] = 0; 7555 } 7556 for (i = 0; i < patternLen; i++) { 7557 mask[pattern.charAt(i)] |= 1 << (patternLen - i - 1); 7558 } 7559 return mask; 7560 }()); 7561 match_bitapScore = function (e, x) { 7562 var accuracy = e / patternLen, 7563 proximity = Math.abs(MATCH_LOCATION - x); 7564 if(!MATCH_DISTANCE) { 7565 return proximity ? 1.0 : accuracy; 7566 } 7567 return accuracy + (proximity / MATCH_DISTANCE); 7568 }; 7569 } 7570 search = function (text) { 7571 text = options.caseSensitive ? text : text.toLowerCase(); 7572 if(pattern === text || text.indexOf(pattern) !== -1) { 7573 return { 7574 isMatch: true, 7575 score: 0 7576 }; 7577 } 7578 if(!options.fuzzy) { 7579 return { 7580 isMatch: false, 7581 score: 1 7582 }; 7583 } 7584 var i, j, 7585 textLen = text.length, 7586 scoreThreshold = MATCH_THRESHOLD, 7587 bestLoc = text.indexOf(pattern, MATCH_LOCATION), 7588 binMin, binMid, 7589 binMax = patternLen + textLen, 7590 lastRd, start, finish, rd, charMatch, 7591 score = 1, 7592 locations = []; 7593 if (bestLoc !== -1) { 7594 scoreThreshold = Math.min(match_bitapScore(0, bestLoc), scoreThreshold); 7595 bestLoc = text.lastIndexOf(pattern, MATCH_LOCATION + patternLen); 7596 if (bestLoc !== -1) { 7597 scoreThreshold = Math.min(match_bitapScore(0, bestLoc), scoreThreshold); 7598 } 7599 } 7600 bestLoc = -1; 7601 for (i = 0; i < patternLen; i++) { 7602 binMin = 0; 7603 binMid = binMax; 7604 while (binMin < binMid) { 7605 if (match_bitapScore(i, MATCH_LOCATION + binMid) <= scoreThreshold) { 7606 binMin = binMid; 7607 } else { 7608 binMax = binMid; 7609 } 7610 binMid = Math.floor((binMax - binMin) / 2 + binMin); 7611 } 7612 binMax = binMid; 7613 start = Math.max(1, MATCH_LOCATION - binMid + 1); 7614 finish = Math.min(MATCH_LOCATION + binMid, textLen) + patternLen; 7615 rd = new Array(finish + 2); 7616 rd[finish + 1] = (1 << i) - 1; 7617 for (j = finish; j >= start; j--) { 7618 charMatch = pattern_alphabet[text.charAt(j - 1)]; 7619 if (i === 0) { 7620 rd[j] = ((rd[j + 1] << 1) | 1) & charMatch; 7621 } else { 7622 rd[j] = ((rd[j + 1] << 1) | 1) & charMatch | (((lastRd[j + 1] | lastRd[j]) << 1) | 1) | lastRd[j + 1]; 7623 } 7624 if (rd[j] & matchmask) { 7625 score = match_bitapScore(i, j - 1); 7626 if (score <= scoreThreshold) { 7627 scoreThreshold = score; 7628 bestLoc = j - 1; 7629 locations.push(bestLoc); 7630 if (bestLoc > MATCH_LOCATION) { 7631 start = Math.max(1, 2 * MATCH_LOCATION - bestLoc); 7632 } else { 7633 break; 7634 } 7635 } 7636 } 7637 } 7638 if (match_bitapScore(i + 1, MATCH_LOCATION) > scoreThreshold) { 7639 break; 7640 } 7641 lastRd = rd; 7642 } 7643 return { 7644 isMatch: bestLoc >= 0, 7645 score: score 7646 }; 7647 }; 7648 return txt === true ? { 'search' : search } : search(txt); 7649 }; 7650 $.vakata.search.defaults = { 7651 location : 0, 7652 distance : 100, 7653 threshold : 0.6, 7654 fuzzy : false, 7655 caseSensitive : false 7656 }; 7657 }($)); 7658 7659 // include the search plugin by default 7660 // $.jstree.defaults.plugins.push("search"); 7661 7662 7663/** 7664 * ### Sort plugin 7665 * 7666 * Automatically sorts all siblings in the tree according to a sorting function. 7667 */ 7668 7669 /** 7670 * the settings function used to sort the nodes. 7671 * It is executed in the tree's context, accepts two nodes as arguments and should return `1` or `-1`.
vendor: 4,334 bytes, lines 7672-7797
7672 * @name $.jstree.defaults.sort 7673 * @plugin sort 7674 */ 7675 $.jstree.defaults.sort = function (a, b) { 7676 //return this.get_type(a) === this.get_type(b) ? (this.get_text(a) > this.get_text(b) ? 1 : -1) : this.get_type(a) >= this.get_type(b); 7677 return this.get_text(a) > this.get_text(b) ? 1 : -1; 7678 }; 7679 $.jstree.plugins.sort = function (options, parent) { 7680 this.bind = function () { 7681 parent.bind.call(this); 7682 this.element 7683 .on("model.jstree", $.proxy(function (e, data) { 7684 this.sort(data.parent, true); 7685 }, this)) 7686 .on("rename_node.jstree create_node.jstree", $.proxy(function (e, data) { 7687 this.sort(data.parent || data.node.parent, false); 7688 this.redraw_node(data.parent || data.node.parent, true); 7689 }, this)) 7690 .on("move_node.jstree copy_node.jstree", $.proxy(function (e, data) { 7691 this.sort(data.parent, false); 7692 this.redraw_node(data.parent, true); 7693 }, this)); 7694 }; 7695 /** 7696 * used to sort a node's children 7697 * @private 7698 * @name sort(obj [, deep]) 7699 * @param {mixed} obj the node 7700 * @param {Boolean} deep if set to `true` nodes are sorted recursively. 7701 * @plugin sort 7702 * @trigger search.jstree 7703 */ 7704 this.sort = function (obj, deep) { 7705 var i, j; 7706 obj = this.get_node(obj); 7707 if(obj && obj.children && obj.children.length) { 7708 obj.children.sort($.proxy(this.settings.sort, this)); 7709 if(deep) { 7710 for(i = 0, j = obj.children_d.length; i < j; i++) { 7711 this.sort(obj.children_d[i], false); 7712 } 7713 } 7714 } 7715 }; 7716 }; 7717 7718 // include the sort plugin by default 7719 // $.jstree.defaults.plugins.push("sort"); 7720 7721/** 7722 * ### State plugin 7723 * 7724 * Saves the state of the tree (selected nodes, opened nodes) on the user's computer using available options (localStorage, cookies, etc) 7725 */ 7726 7727 var to = false; 7728 /** 7729 * stores all defaults for the state plugin 7730 * @name $.jstree.defaults.state 7731 * @plugin state 7732 */ 7733 $.jstree.defaults.state = { 7734 /** 7735 * A string for the key to use when saving the current tree (change if using multiple trees in your project). Defaults to `jstree`. 7736 * @name $.jstree.defaults.state.key 7737 * @plugin state 7738 */ 7739 key : 'jstree', 7740 /** 7741 * A space separated list of events that trigger a state save. Defaults to `changed.jstree open_node.jstree close_node.jstree`. 7742 * @name $.jstree.defaults.state.events 7743 * @plugin state 7744 */ 7745 events : 'changed.jstree open_node.jstree close_node.jstree check_node.jstree uncheck_node.jstree', 7746 /** 7747 * Time in milliseconds after which the state will expire. Defaults to 'false' meaning - no expire. 7748 * @name $.jstree.defaults.state.ttl 7749 * @plugin state 7750 */ 7751 ttl : false, 7752 /** 7753 * A function that will be executed prior to restoring state with one argument - the state object. Can be used to clear unwanted parts of the state. 7754 * @name $.jstree.defaults.state.filter 7755 * @plugin state 7756 */ 7757 filter : false 7758 }; 7759 $.jstree.plugins.state = function (options, parent) { 7760 this.bind = function () { 7761 parent.bind.call(this); 7762 var bind = $.proxy(function () { 7763 this.element.on(this.settings.state.events, $.proxy(function () { 7764 if(to) { clearTimeout(to); } 7765 to = setTimeout($.proxy(function () { this.save_state(); }, this), 100); 7766 }, this)); 7767 /** 7768 * triggered when the state plugin is finished restoring the state (and immediately after ready if there is no state to restore). 7769 * @event 7770 * @name state_ready.jstree 7771 * @plugin state 7772 */ 7773 this.trigger('state_ready'); 7774 }, this); 7775 this.element 7776 .on("ready.jstree", $.proxy(function (e, data) { 7777 this.element.one("restore_state.jstree", bind); 7778 if(!this.restore_state()) { bind(); } 7779 }, this)); 7780 }; 7781 /** 7782 * save the state 7783 * @name save_state() 7784 * @plugin state 7785 */ 7786 this.save_state = function () { 7787 var st = { 'state' : this.get_state(), 'ttl' : this.settings.state.ttl, 'sec' : +(new Date()) }; 7788 $.vakata.storage.set(this.settings.state.key, JSON.stringify(st)); 7789 }; 7790 /** 7791 * restore the state from the user's computer 7792 * @name restore_state() 7793 * @plugin state 7794 */ 7795 this.restore_state = function () { 7796 var k = $.vakata.storage.get(this.settings.state.key); 7797 if(!!k) { try { k = JSON.parse(k); } catch(ex) { return false; } }
vendor: 15,790 bytes, lines 7798-8218
7798 if(!!k && k.ttl && k.sec && +(new Date()) - k.sec > k.ttl) { return false; } 7799 if(!!k && k.state) { k = k.state; } 7800 if(!!k && $.isFunction(this.settings.state.filter)) { k = this.settings.state.filter.call(this, k); } 7801 if(!!k) { 7802 this.element.one("set_state.jstree", function (e, data) { data.instance.trigger('restore_state', { 'state' : $.extend(true, {}, k) }); }); 7803 this.set_state(k); 7804 return true; 7805 } 7806 return false; 7807 }; 7808 /** 7809 * clear the state on the user's computer 7810 * @name clear_state() 7811 * @plugin state 7812 */ 7813 this.clear_state = function () { 7814 return $.vakata.storage.del(this.settings.state.key); 7815 }; 7816 }; 7817 7818 (function ($, undefined) { 7819 $.vakata.storage = { 7820 // simply specifying the functions in FF throws an error 7821 set : function (key, val) { return window.localStorage.setItem(key, val); }, 7822 get : function (key) { return window.localStorage.getItem(key); }, 7823 del : function (key) { return window.localStorage.removeItem(key); } 7824 }; 7825 }($)); 7826 7827 // include the state plugin by default 7828 // $.jstree.defaults.plugins.push("state"); 7829 7830/** 7831 * ### Types plugin 7832 * 7833 * Makes it possible to add predefined types for groups of nodes, which make it possible to easily control nesting rules and icon for each group. 7834 */ 7835 7836 /** 7837 * An object storing all types as key value pairs, where the key is the type name and the value is an object that could contain following keys (all optional). 7838 * 7839 * * `max_children` the maximum number of immediate children this node type can have. Do not specify or set to `-1` for unlimited. 7840 * * `max_depth` the maximum number of nesting this node type can have. A value of `1` would mean that the node can have children, but no grandchildren. Do not specify or set to `-1` for unlimited. 7841 * * `valid_children` an array of node type strings, that nodes of this type can have as children. Do not specify or set to `-1` for no limits. 7842 * * `icon` a string - can be a path to an icon or a className, if using an image that is in the current directory use a `./` prefix, otherwise it will be detected as a class. Omit to use the default icon from your theme. 7843 * * `li_attr` an object of values which will be used to add HTML attributes on the resulting LI DOM node (merged with the node's own data) 7844 * * `a_attr` an object of values which will be used to add HTML attributes on the resulting A DOM node (merged with the node's own data) 7845 * 7846 * There are two predefined types: 7847 * 7848 * * `#` represents the root of the tree, for example `max_children` would control the maximum number of root nodes. 7849 * * `default` represents the default node - any settings here will be applied to all nodes that do not have a type specified. 7850 * 7851 * @name $.jstree.defaults.types 7852 * @plugin types 7853 */ 7854 $.jstree.defaults.types = { 7855 'default' : {} 7856 }; 7857 $.jstree.defaults.types[$.jstree.root] = {}; 7858 7859 $.jstree.plugins.types = function (options, parent) { 7860 this.init = function (el, options) { 7861 var i, j; 7862 if(options && options.types && options.types['default']) { 7863 for(i in options.types) { 7864 if(i !== "default" && i !== $.jstree.root && options.types.hasOwnProperty(i)) { 7865 for(j in options.types['default']) { 7866 if(options.types['default'].hasOwnProperty(j) && options.types[i][j] === undefined) { 7867 options.types[i][j] = options.types['default'][j]; 7868 } 7869 } 7870 } 7871 } 7872 } 7873 parent.init.call(this, el, options); 7874 this._model.data[$.jstree.root].type = $.jstree.root; 7875 }; 7876 this.refresh = function (skip_loading, forget_state) { 7877 parent.refresh.call(this, skip_loading, forget_state); 7878 this._model.data[$.jstree.root].type = $.jstree.root; 7879 }; 7880 this.bind = function () { 7881 this.element 7882 .on('model.jstree', $.proxy(function (e, data) { 7883 var m = this._model.data, 7884 dpc = data.nodes, 7885 t = this.settings.types, 7886 i, j, c = 'default', k; 7887 for(i = 0, j = dpc.length; i < j; i++) { 7888 c = 'default'; 7889 if(m[dpc[i]].original && m[dpc[i]].original.type && t[m[dpc[i]].original.type]) { 7890 c = m[dpc[i]].original.type; 7891 } 7892 if(m[dpc[i]].data && m[dpc[i]].data.jstree && m[dpc[i]].data.jstree.type && t[m[dpc[i]].data.jstree.type]) { 7893 c = m[dpc[i]].data.jstree.type; 7894 } 7895 m[dpc[i]].type = c; 7896 if(m[dpc[i]].icon === true && t[c].icon !== undefined) { 7897 m[dpc[i]].icon = t[c].icon; 7898 } 7899 if(t[c].li_attr !== undefined && typeof t[c].li_attr === 'object') { 7900 for (k in t[c].li_attr) { 7901 if (t[c].li_attr.hasOwnProperty(k)) { 7902 if (k === 'id') { 7903 continue; 7904 } 7905 else if (m[dpc[i]].li_attr[k] === undefined) { 7906 m[dpc[i]].li_attr[k] = t[c].li_attr[k]; 7907 } 7908 else if (k === 'class') { 7909 m[dpc[i]].li_attr['class'] = t[c].li_attr['class'] + ' ' + m[dpc[i]].li_attr['class']; 7910 } 7911 } 7912 } 7913 } 7914 if(t[c].a_attr !== undefined && typeof t[c].a_attr === 'object') { 7915 for (k in t[c].a_attr) { 7916 if (t[c].a_attr.hasOwnProperty(k)) { 7917 if (k === 'id') { 7918 continue; 7919 } 7920 else if (m[dpc[i]].a_attr[k] === undefined) { 7921 m[dpc[i]].a_attr[k] = t[c].a_attr[k]; 7922 } 7923 else if (k === 'href' && m[dpc[i]].a_attr[k] === '#') { 7924 m[dpc[i]].a_attr['href'] = t[c].a_attr['href']; 7925 } 7926 else if (k === 'class') { 7927 m[dpc[i]].a_attr['class'] = t[c].a_attr['class'] + ' ' + m[dpc[i]].a_attr['class']; 7928 } 7929 } 7930 } 7931 } 7932 } 7933 m[$.jstree.root].type = $.jstree.root; 7934 }, this)); 7935 parent.bind.call(this); 7936 }; 7937 this.get_json = function (obj, options, flat) { 7938 var i, j, 7939 m = this._model.data, 7940 opt = options ? $.extend(true, {}, options, {no_id:false}) : {}, 7941 tmp = parent.get_json.call(this, obj, opt, flat); 7942 if(tmp === false) { return false; } 7943 if($.isArray(tmp)) { 7944 for(i = 0, j = tmp.length; i < j; i++) { 7945 tmp[i].type = tmp[i].id && m[tmp[i].id] && m[tmp[i].id].type ? m[tmp[i].id].type : "default"; 7946 if(options && options.no_id) { 7947 delete tmp[i].id; 7948 if(tmp[i].li_attr && tmp[i].li_attr.id) { 7949 delete tmp[i].li_attr.id; 7950 } 7951 if(tmp[i].a_attr && tmp[i].a_attr.id) { 7952 delete tmp[i].a_attr.id; 7953 } 7954 } 7955 } 7956 } 7957 else { 7958 tmp.type = tmp.id && m[tmp.id] && m[tmp.id].type ? m[tmp.id].type : "default"; 7959 if(options && options.no_id) { 7960 tmp = this._delete_ids(tmp); 7961 } 7962 } 7963 return tmp; 7964 }; 7965 this._delete_ids = function (tmp) { 7966 if($.isArray(tmp)) { 7967 for(var i = 0, j = tmp.length; i < j; i++) { 7968 tmp[i] = this._delete_ids(tmp[i]); 7969 } 7970 return tmp; 7971 } 7972 delete tmp.id; 7973 if(tmp.li_attr && tmp.li_attr.id) { 7974 delete tmp.li_attr.id; 7975 } 7976 if(tmp.a_attr && tmp.a_attr.id) { 7977 delete tmp.a_attr.id; 7978 } 7979 if(tmp.children && $.isArray(tmp.children)) { 7980 tmp.children = this._delete_ids(tmp.children); 7981 } 7982 return tmp; 7983 }; 7984 this.check = function (chk, obj, par, pos, more) { 7985 if(parent.check.call(this, chk, obj, par, pos, more) === false) { return false; } 7986 obj = obj && obj.id ? obj : this.get_node(obj); 7987 par = par && par.id ? par : this.get_node(par); 7988 var m = obj && obj.id ? (more && more.origin ? more.origin : $.jstree.reference(obj.id)) : null, tmp, d, i, j; 7989 m = m && m._model && m._model.data ? m._model.data : null; 7990 switch(chk) { 7991 case "create_node": 7992 case "move_node": 7993 case "copy_node": 7994 if(chk !== 'move_node' || $.inArray(obj.id, par.children) === -1) { 7995 tmp = this.get_rules(par); 7996 if(tmp.max_children !== undefined && tmp.max_children !== -1 && tmp.max_children === par.children.length) { 7997 this._data.core.last_error = { 'error' : 'check', 'plugin' : 'types', 'id' : 'types_01', 'reason' : 'max_children prevents function: ' + chk, 'data' : JSON.stringify({ 'chk' : chk, 'pos' : pos, 'obj' : obj && obj.id ? obj.id : false, 'par' : par && par.id ? par.id : false }) }; 7998 return false; 7999 } 8000 if(tmp.valid_children !== undefined && tmp.valid_children !== -1 && $.inArray((obj.type || 'default'), tmp.valid_children) === -1) { 8001 this._data.core.last_error = { 'error' : 'check', 'plugin' : 'types', 'id' : 'types_02', 'reason' : 'valid_children prevents function: ' + chk, 'data' : JSON.stringify({ 'chk' : chk, 'pos' : pos, 'obj' : obj && obj.id ? obj.id : false, 'par' : par && par.id ? par.id : false }) }; 8002 return false; 8003 } 8004 if(m && obj.children_d && obj.parents) { 8005 d = 0; 8006 for(i = 0, j = obj.children_d.length; i < j; i++) { 8007 d = Math.max(d, m[obj.children_d[i]].parents.length); 8008 } 8009 d = d - obj.parents.length + 1; 8010 } 8011 if(d <= 0 || d === undefined) { d = 1; } 8012 do { 8013 if(tmp.max_depth !== undefined && tmp.max_depth !== -1 && tmp.max_depth < d) { 8014 this._data.core.last_error = { 'error' : 'check', 'plugin' : 'types', 'id' : 'types_03', 'reason' : 'max_depth prevents function: ' + chk, 'data' : JSON.stringify({ 'chk' : chk, 'pos' : pos, 'obj' : obj && obj.id ? obj.id : false, 'par' : par && par.id ? par.id : false }) }; 8015 return false; 8016 } 8017 par = this.get_node(par.parent); 8018 tmp = this.get_rules(par); 8019 d++; 8020 } while(par); 8021 } 8022 break; 8023 } 8024 return true; 8025 }; 8026 /** 8027 * used to retrieve the type settings object for a node 8028 * @name get_rules(obj) 8029 * @param {mixed} obj the node to find the rules for 8030 * @return {Object} 8031 * @plugin types 8032 */ 8033 this.get_rules = function (obj) { 8034 obj = this.get_node(obj); 8035 if(!obj) { return false; } 8036 var tmp = this.get_type(obj, true); 8037 if(tmp.max_depth === undefined) { tmp.max_depth = -1; } 8038 if(tmp.max_children === undefined) { tmp.max_children = -1; } 8039 if(tmp.valid_children === undefined) { tmp.valid_children = -1; } 8040 return tmp; 8041 }; 8042 /** 8043 * used to retrieve the type string or settings object for a node 8044 * @name get_type(obj [, rules]) 8045 * @param {mixed} obj the node to find the rules for 8046 * @param {Boolean} rules if set to `true` instead of a string the settings object will be returned 8047 * @return {String|Object} 8048 * @plugin types 8049 */ 8050 this.get_type = function (obj, rules) { 8051 obj = this.get_node(obj); 8052 return (!obj) ? false : ( rules ? $.extend({ 'type' : obj.type }, this.settings.types[obj.type]) : obj.type); 8053 }; 8054 /** 8055 * used to change a node's type 8056 * @name set_type(obj, type) 8057 * @param {mixed} obj the node to change 8058 * @param {String} type the new type 8059 * @plugin types 8060 */ 8061 this.set_type = function (obj, type) { 8062 var m = this._model.data, t, t1, t2, old_type, old_icon, k, d, a; 8063 if($.isArray(obj)) { 8064 obj = obj.slice(); 8065 for(t1 = 0, t2 = obj.length; t1 < t2; t1++) { 8066 this.set_type(obj[t1], type); 8067 } 8068 return true; 8069 } 8070 t = this.settings.types; 8071 obj = this.get_node(obj); 8072 if(!t[type] || !obj) { return false; } 8073 d = this.get_node(obj, true); 8074 if (d && d.length) { 8075 a = d.children('.jstree-anchor'); 8076 } 8077 old_type = obj.type; 8078 old_icon = this.get_icon(obj); 8079 obj.type = type; 8080 if(old_icon === true || !t[old_type] || (t[old_type].icon !== undefined && old_icon === t[old_type].icon)) { 8081 this.set_icon(obj, t[type].icon !== undefined ? t[type].icon : true); 8082 } 8083 8084 // remove old type props 8085 if(t[old_type] && t[old_type].li_attr !== undefined && typeof t[old_type].li_attr === 'object') { 8086 for (k in t[old_type].li_attr) { 8087 if (t[old_type].li_attr.hasOwnProperty(k)) { 8088 if (k === 'id') { 8089 continue; 8090 } 8091 else if (k === 'class') { 8092 m[obj.id].li_attr['class'] = (m[obj.id].li_attr['class'] || '').replace(t[old_type].li_attr[k], ''); 8093 if (d) { d.removeClass(t[old_type].li_attr[k]); } 8094 } 8095 else if (m[obj.id].li_attr[k] === t[old_type].li_attr[k]) { 8096 m[obj.id].li_attr[k] = null; 8097 if (d) { d.removeAttr(k); } 8098 } 8099 } 8100 } 8101 } 8102 if(t[old_type] && t[old_type].a_attr !== undefined && typeof t[old_type].a_attr === 'object') { 8103 for (k in t[old_type].a_attr) { 8104 if (t[old_type].a_attr.hasOwnProperty(k)) { 8105 if (k === 'id') { 8106 continue; 8107 } 8108 else if (k === 'class') { 8109 m[obj.id].a_attr['class'] = (m[obj.id].a_attr['class'] || '').replace(t[old_type].a_attr[k], ''); 8110 if (a) { a.removeClass(t[old_type].a_attr[k]); } 8111 } 8112 else if (m[obj.id].a_attr[k] === t[old_type].a_attr[k]) { 8113 if (k === 'href') { 8114 m[obj.id].a_attr[k] = '#'; 8115 if (a) { a.attr('href', '#'); } 8116 } 8117 else { 8118 delete m[obj.id].a_attr[k]; 8119 if (a) { a.removeAttr(k); } 8120 } 8121 } 8122 } 8123 } 8124 } 8125 8126 // add new props 8127 if(t[type].li_attr !== undefined && typeof t[type].li_attr === 'object') { 8128 for (k in t[type].li_attr) { 8129 if (t[type].li_attr.hasOwnProperty(k)) { 8130 if (k === 'id') { 8131 continue; 8132 } 8133 else if (m[obj.id].li_attr[k] === undefined) { 8134 m[obj.id].li_attr[k] = t[type].li_attr[k]; 8135 if (d) { 8136 if (k === 'class') { 8137 d.addClass(t[type].li_attr[k]); 8138 } 8139 else { 8140 d.attr(k, t[type].li_attr[k]); 8141 } 8142 } 8143 } 8144 else if (k === 'class') { 8145 m[obj.id].li_attr['class'] = t[type].li_attr[k] + ' ' + m[obj.id].li_attr['class']; 8146 if (d) { d.addClass(t[type].li_attr[k]); } 8147 } 8148 } 8149 } 8150 } 8151 if(t[type].a_attr !== undefined && typeof t[type].a_attr === 'object') { 8152 for (k in t[type].a_attr) { 8153 if (t[type].a_attr.hasOwnProperty(k)) { 8154 if (k === 'id') { 8155 continue; 8156 } 8157 else if (m[obj.id].a_attr[k] === undefined) { 8158 m[obj.id].a_attr[k] = t[type].a_attr[k]; 8159 if (a) { 8160 if (k === 'class') { 8161 a.addClass(t[type].a_attr[k]); 8162 } 8163 else { 8164 a.attr(k, t[type].a_attr[k]); 8165 } 8166 } 8167 } 8168 else if (k === 'href' && m[obj.id].a_attr[k] === '#') { 8169 m[obj.id].a_attr['href'] = t[type].a_attr['href']; 8170 if (a) { a.attr('href', t[type].a_attr['href']); } 8171 } 8172 else if (k === 'class') { 8173 m[obj.id].a_attr['class'] = t[type].a_attr['class'] + ' ' + m[obj.id].a_attr['class']; 8174 if (a) { a.addClass(t[type].a_attr[k]); } 8175 } 8176 } 8177 } 8178 } 8179 8180 return true; 8181 }; 8182 }; 8183 // include the types plugin by default 8184 // $.jstree.defaults.plugins.push("types"); 8185 8186 8187/** 8188 * ### Unique plugin 8189 * 8190 * Enforces that no nodes with the same name can coexist as siblings. 8191 */ 8192 8193 /** 8194 * stores all defaults for the unique plugin 8195 * @name $.jstree.defaults.unique 8196 * @plugin unique 8197 */ 8198 $.jstree.defaults.unique = { 8199 /** 8200 * Indicates if the comparison should be case sensitive. Default is `false`. 8201 * @name $.jstree.defaults.unique.case_sensitive 8202 * @plugin unique 8203 */ 8204 case_sensitive : false, 8205 /** 8206 * A callback executed in the instance's scope when a new node is created and the name is already taken, the two arguments are the conflicting name and the counter. The default will produce results like `New node (2)`. 8207 * @name $.jstree.defaults.unique.duplicate 8208 * @plugin unique 8209 */ 8210 duplicate : function (name, counter) { 8211 return name + ' (' + counter + ')'; 8212 } 8213 }; 8214 8215 $.jstree.plugins.unique = function (options, parent) { 8216 this.check = function (chk, obj, par, pos, more) { 8217 if(parent.check.call(this, chk, obj, par, pos, more) === false) { return false; } 8218 obj = obj && obj.id ? obj : this.get_node(obj);
vendor: 5,693 bytes, lines 8219-8339
8219 par = par && par.id ? par : this.get_node(par); 8220 if(!par || !par.children) { return true; } 8221 var n = chk === "rename_node" ? pos : obj.text, 8222 c = [], 8223 s = this.settings.unique.case_sensitive, 8224 m = this._model.data, i, j; 8225 for(i = 0, j = par.children.length; i < j; i++) { 8226 c.push(s ? m[par.children[i]].text : m[par.children[i]].text.toLowerCase()); 8227 } 8228 if(!s) { n = n.toLowerCase(); } 8229 switch(chk) { 8230 case "delete_node": 8231 return true; 8232 case "rename_node": 8233 i = ($.inArray(n, c) === -1 || (obj.text && obj.text[ s ? 'toString' : 'toLowerCase']() === n)); 8234 if(!i) { 8235 this._data.core.last_error = { 'error' : 'check', 'plugin' : 'unique', 'id' : 'unique_01', 'reason' : 'Child with name ' + n + ' already exists. Preventing: ' + chk, 'data' : JSON.stringify({ 'chk' : chk, 'pos' : pos, 'obj' : obj && obj.id ? obj.id : false, 'par' : par && par.id ? par.id : false }) }; 8236 } 8237 return i; 8238 case "create_node": 8239 i = ($.inArray(n, c) === -1); 8240 if(!i) { 8241 this._data.core.last_error = { 'error' : 'check', 'plugin' : 'unique', 'id' : 'unique_04', 'reason' : 'Child with name ' + n + ' already exists. Preventing: ' + chk, 'data' : JSON.stringify({ 'chk' : chk, 'pos' : pos, 'obj' : obj && obj.id ? obj.id : false, 'par' : par && par.id ? par.id : false }) }; 8242 } 8243 return i; 8244 case "copy_node": 8245 i = ($.inArray(n, c) === -1); 8246 if(!i) { 8247 this._data.core.last_error = { 'error' : 'check', 'plugin' : 'unique', 'id' : 'unique_02', 'reason' : 'Child with name ' + n + ' already exists. Preventing: ' + chk, 'data' : JSON.stringify({ 'chk' : chk, 'pos' : pos, 'obj' : obj && obj.id ? obj.id : false, 'par' : par && par.id ? par.id : false }) }; 8248 } 8249 return i; 8250 case "move_node": 8251 i = ( (obj.parent === par.id && (!more || !more.is_multi)) || $.inArray(n, c) === -1); 8252 if(!i) { 8253 this._data.core.last_error = { 'error' : 'check', 'plugin' : 'unique', 'id' : 'unique_03', 'reason' : 'Child with name ' + n + ' already exists. Preventing: ' + chk, 'data' : JSON.stringify({ 'chk' : chk, 'pos' : pos, 'obj' : obj && obj.id ? obj.id : false, 'par' : par && par.id ? par.id : false }) }; 8254 } 8255 return i; 8256 } 8257 return true; 8258 }; 8259 this.create_node = function (par, node, pos, callback, is_loaded) { 8260 if(!node || node.text === undefined) { 8261 if(par === null) { 8262 par = $.jstree.root; 8263 } 8264 par = this.get_node(par); 8265 if(!par) { 8266 return parent.create_node.call(this, par, node, pos, callback, is_loaded); 8267 } 8268 pos = pos === undefined ? "last" : pos; 8269 if(!pos.toString().match(/^(before|after)$/) && !is_loaded && !this.is_loaded(par)) { 8270 return parent.create_node.call(this, par, node, pos, callback, is_loaded); 8271 } 8272 if(!node) { node = {}; } 8273 var tmp, n, dpc, i, j, m = this._model.data, s = this.settings.unique.case_sensitive, cb = this.settings.unique.duplicate; 8274 n = tmp = this.get_string('New node'); 8275 dpc = []; 8276 for(i = 0, j = par.children.length; i < j; i++) { 8277 dpc.push(s ? m[par.children[i]].text : m[par.children[i]].text.toLowerCase()); 8278 } 8279 i = 1; 8280 while($.inArray(s ? n : n.toLowerCase(), dpc) !== -1) { 8281 n = cb.call(this, tmp, (++i)).toString(); 8282 } 8283 node.text = n; 8284 } 8285 return parent.create_node.call(this, par, node, pos, callback, is_loaded); 8286 }; 8287 }; 8288 8289 // include the unique plugin by default 8290 // $.jstree.defaults.plugins.push("unique"); 8291 8292 8293/** 8294 * ### Wholerow plugin 8295 * 8296 * Makes each node appear block level. Making selection easier. May cause slow down for large trees in old browsers. 8297 */ 8298 8299 var div = document.createElement('DIV'); 8300 div.setAttribute('unselectable','on'); 8301 div.setAttribute('role','presentation'); 8302 div.className = 'jstree-wholerow'; 8303 div.innerHTML = ' '; 8304 $.jstree.plugins.wholerow = function (options, parent) { 8305 this.bind = function () { 8306 parent.bind.call(this); 8307 8308 this.element 8309 .on('ready.jstree set_state.jstree', $.proxy(function () { 8310 this.hide_dots(); 8311 }, this)) 8312 .on("init.jstree loading.jstree ready.jstree", $.proxy(function () { 8313 //div.style.height = this._data.core.li_height + 'px'; 8314 this.get_container_ul().addClass('jstree-wholerow-ul'); 8315 }, this)) 8316 .on("deselect_all.jstree", $.proxy(function (e, data) { 8317 this.element.find('.jstree-wholerow-clicked').removeClass('jstree-wholerow-clicked'); 8318 }, this)) 8319 .on("changed.jstree", $.proxy(function (e, data) { 8320 this.element.find('.jstree-wholerow-clicked').removeClass('jstree-wholerow-clicked'); 8321 var tmp = false, i, j; 8322 for(i = 0, j = data.selected.length; i < j; i++) { 8323 tmp = this.get_node(data.selected[i], true); 8324 if(tmp && tmp.length) { 8325 tmp.children('.jstree-wholerow').addClass('jstree-wholerow-clicked'); 8326 } 8327 } 8328 }, this)) 8329 .on("open_node.jstree", $.proxy(function (e, data) { 8330 this.get_node(data.node, true).find('.jstree-clicked').parent().children('.jstree-wholerow').addClass('jstree-wholerow-clicked'); 8331 }, this)) 8332 .on("hover_node.jstree dehover_node.jstree", $.proxy(function (e, data) { 8333 if(e.type === "hover_node" && this.is_disabled(data.node)) { return; } 8334 this.get_node(data.node, true).children('.jstree-wholerow')[e.type === "hover_node"?"addClass":"removeClass"]('jstree-wholerow-hovered'); 8335 }, this)) 8336 .on("contextmenu.jstree", ".jstree-wholerow", $.proxy(function (e) { 8337 if (this._data.contextmenu) { 8338 e.preventDefault(); 8339 var tmp = $.Event('contextmenu', { metaKey : e.metaKey, ctrlKey : e.ctrlKey, altKey : e.altKey, shift
8339Key : e.shiftKey, pageX : e.pageX, pageY : e.pageY }); 8340 $(e.currentTarget).closest(".jstree-node").children(".jstree-anchor").first().trigger(tmp); 8341 } 8342 }, this)) 8343 /*! 8344 .on("mousedown.jstree touchstart.jstree", ".jstree-wholerow", function (e) { 8345 if(e.target === e.currentTarget) { 8346 var a = $(e.currentTarget).closest(".jstree-node").children(".jstree-anchor"); 8347 e.target = a[0]; 8348 a.trigger(e); 8349 } 8350 }) 8351 */ 8352 .on("click.jstree", ".jstree-wholerow", function (e) { 8353 e.stopImmediatePropagation(); 8354 var tmp = $.Event('click', { metaKey : e.metaKey, ctrlKey : e.ctrlKey, altKey : e.altKey, shiftKey : e.shiftKey }); 8355 $(e.currentTarget).closest(".jstree-node").children(".jstree-anchor").first().trigger(tmp).focus(); 8356 }) 8357 .on("dblclick.jstree", ".jstree-wholerow", function (e) { 8358 e.stopImmediatePropagation(); 8359 var tmp = $.Event('dblclick', { metaKey : e.metaKey, ctrlKey : e.ctrlKey, altKey : e.altKey, shiftKey : e.shiftKey }); 8360 $(e.currentTarget).closest(".jstree-node").children(".jstree-anchor").first().trigger(tmp).focus(); 8361 }) 8362 .on("click.jstree", ".jstree-leaf > .jstree-ocl", $.proxy(function (e) { 8363 e.stopImmediatePropagation(); 8364 var tmp = $.Event('click', { metaKey : e.metaKey, ctrlKey : e.ctrlKey, altKey : e.altKey, shiftKey : e.shiftKey }); 8365 $(e.currentTarget).closest(".jstree-node").children(".jstree-anchor").first().trigger(tmp).focus(); 8366 }, this)) 8367 .on("mouseover.jstree", ".jstree-wholerow, .jstree-icon", $.proxy(function (e) { 8368 e.stopImmediatePropagation(); 8369 if(!this.is_disabled(e.currentTarget)) { 8370 this.hover_node(e.currentTarget); 8371 } 8372 return false; 8373 }, this)) 8374 .on("mouseleave.jstree", ".jstree-node", $.proxy(function (e) { 8375 this.dehover_node(e.currentTarget); 8376 }, this)); 8377 }; 8378 this.teardown = function () { 8379 if(this.settings.wholerow) { 8380 this.element.find(".jstree-wholerow").remove(); 8381 } 8382 parent.teardown.call(this); 8383 }; 8384 this.redraw_node = function(obj, deep, callback, force_render) { 8385 obj = parent.redraw_node.apply(this, arguments); 8386 if(obj) { 8387 var tmp = div.cloneNode(true); 8388 //tmp.style.height = this._data.core.li_height + 'px'; 8389 if($.inArray(obj.id, this._data.core.selected) !== -1) { tmp.className += ' jstree-wholerow-clicked'; } 8390 if(this._data.core.focused && this._data.core.focused === obj.id) { tmp.className += ' jstree-wholerow-hovered'; } 8391 obj.insertBefore(tmp, obj.childNodes[0]); 8392 } 8393 return obj; 8394 }; 8395 }; 8396 // include the wholerow plugin by default 8397 // $.jstree.defaults.plugins.push("wholerow");
vendor: 881 bytes, lines 8398-8423
8398 if(document.registerElement && Object && Object.create) { 8399 var proto = Object.create(HTMLElement.prototype); 8400 proto.createdCallback = function () { 8401 var c = { core : {}, plugins : [] }, i; 8402 for(i in $.jstree.plugins) { 8403 if($.jstree.plugins.hasOwnProperty(i) && this.attributes[i]) { 8404 c.plugins.push(i); 8405 if(this.getAttribute(i) && JSON.parse(this.getAttribute(i))) { 8406 c[i] = JSON.parse(this.getAttribute(i)); 8407 } 8408 } 8409 } 8410 for(i in $.jstree.defaults.core) { 8411 if($.jstree.defaults.core.hasOwnProperty(i) && this.attributes[i]) { 8412 c.core[i] = JSON.parse(this.getAttribute(i)) || this.getAttribute(i); 8413 } 8414 } 8415 $(this).jstree(c); 8416 }; 8417 // proto.attributeChangedCallback = function (name, previous, value) { }; 8418 try { 8419 document.registerElement("vakata-jstree", { prototype: proto }); 8420 } catch(ignore) { } 8421 } 8422 8423}));
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.