vendor: 4,499 bytes, lines 1-162
1/*globals jQuery, define, exports, require, window, document */ 2(function (factory) { 3 "use strict"; 4 if (typeof define === 'function' && define.amd) { 5 define(['jquery'], factory); 6 } 7 else if(typeof exports === 'object') { 8 factory(require('jquery')); 9 } 10 else { 11 factory(jQuery); 12 } 13}(function ($, undefined) { 14 "use strict"; 15/*! 16 * jsTree 3.0.0 17 * http://jstree.com/ 18 * 19 * Copyright (c) 2013 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: browser: true, ass: true, bitwise: true, continue: true, nomen: true, plusplus: true, regexp: true, unparam: true, todo: true, white: true 27 */ 28 29 // prevent another load? maybe there is a better way? 30 if($.jstree) { 31 return; 32 } 33 34 /** 35 * ### jsTree core functionality 36 */ 37 38 // internal variables 39 var instance_counter = 0, 40 ccp_node = false, 41 ccp_mode = false, 42 ccp_inst = false, 43 themes_loaded = [], 44 src = $('script:last').attr('src'), 45 _d = document, _node = _d.createElement('LI'), _temp1, _temp2; 46 47 _node.setAttribute('role', 'treeitem'); 48 _temp1 = _d.createElement('I'); 49 _temp1.className = 'jstree-icon jstree-ocl'; 50 _node.appendChild(_temp1); 51 _temp1 = _d.createElement('A'); 52 _temp1.className = 'jstree-anchor'; 53 _temp1.setAttribute('href','#'); 54 _temp2 = _d.createElement('I'); 55 _temp2.className = 'jstree-icon jstree-themeicon'; 56 _temp1.appendChild(_temp2); 57 _node.appendChild(_temp1); 58 _temp1 = _temp2 = null; 59 60 61 /** 62 * holds all jstree related functions and variables, including the actual class and methods to create, access and manipulate instances. 63 * @name $.jstree 64 */ 65 $.jstree = { 66 /** 67 * specifies the jstree version in use 68 * @name $.jstree.version 69 */ 70 version : '3.0.0-beta9', 71 /** 72 * holds all the default options used when creating new instances 73 * @name $.jstree.defaults 74 */ 75 defaults : { 76 /** 77 * 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 `[]` 78 * @name $.jstree.defaults.plugins 79 */ 80 plugins : [] 81 }, 82 /** 83 * stores all loaded jstree plugins (used internally) 84 * @name $.jstree.plugins 85 */ 86 plugins : {}, 87 path : src && src.indexOf('/') !== -1 ? src.replace(/\/[^\/]+$/,'') : '' 88 }; 89 /** 90 * creates a jstree instance 91 * @name $.jstree.create(el [, options]) 92 * @param {DOMElement|jQuery|String} el the element to create the instance on, can be jQuery extended or a selector 93 * @param {Object} options options for this instance (extends `$.jstree.defaults`) 94 * @return {jsTree} the new instance 95 */ 96 $.jstree.create = function (el, options) { 97 var tmp = new $.jstree.core(++instance_counter), 98 opt = options; 99 options = $.extend(true, {}, $.jstree.defaults, options); 100 if(opt && opt.plugins) { 101 options.plugins = opt.plugins; 102 } 103 $.each(options.plugins, function (i, k) { 104 if(i !== 'core') { 105 tmp = tmp.plugin(k, options[k]); 106 } 107 }); 108 tmp.init(el, options); 109 return tmp; 110 }; 111 /** 112 * the jstree class constructor, used only internally 113 * @private 114 * @name $.jstree.core(id) 115 * @param {Number} id this instance's index 116 */ 117 $.jstree.core = function (id) { 118 this._id = id; 119 this._cnt = 0; 120 this._data = { 121 core : { 122 themes : { 123 name : false, 124 dots : false, 125 icons : false 126 }, 127 selected : [], 128 last_error : {} 129 } 130 }; 131 }; 132 /** 133 * get a reference to an existing instance 134 * 135 * __Examples__ 136 * 137 * // provided a container with an ID of "tree", and a nested node with an ID of "branch" 138 * // all of there will return the same instance 139 * $.jstree.reference('tree'); 140 * $.jstree.reference('#tree'); 141 * $.jstree.reference($('#tree')); 142 * $.jstree.reference(document.getElementByID('tree')); 143 * $.jstree.reference('branch'); 144 * $.jstree.reference('#branch'); 145 * $.jstree.reference($('#branch')); 146 * $.jstree.reference(document.getElementByID('branch')); 147 * 148 * @name $.jstree.reference(needle) 149 * @param {DOMElement|jQuery|String} needle 150 * @return {jsTree|null} the instance or `null` if not found 151 */ 152 $.jstree.reference = function (needle) { 153 if(needle && !$(needle).length) { 154 if(needle.id) { 155 needle = needle.id; 156 } 157 var tmp = null; 158 $('.jstree').each(function () { 159 var inst = $(this).data('jstree'); 160 if(inst && inst._model.data[needle]) { 161 tmp = inst; 162 return false;
163 } 164 }); 165 return tmp; 166 } 167 return $(needle).closest('.jstree').data('jstree'); 168 }; 169 /** 170 * Create an instance, get an instance or invoke a command on a instance. 171 * 172 * 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). 173 * 174 * 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). 175 * 176 * If there is an existing instance and `arg` is not a string the instance itself is returned (similar to `$.jstree.reference`). 177 * 178 * In any other case - nothing is returned and chaining is not broken. 179 * 180 * __Examples__ 181 * 182 * $('#tree1').jstree(); // creates an instance 183 * $('#tree2').jstree({ plugins : [] }); // create an instance with some options 184 * $('#tree1').jstree('open_node', '#branch_1'); // call a method on an existing instance, passing additional arguments 185 * $('#tree2').jstree(); // get an existing instance (or create an instance) 186 * $('#tree2').jstree(true); // get an existing instance (will not create new instance) 187 * $('#branch_1').jstree().select_node('#branch_1'); // get an instance (using a nested element and call a method) 188 * 189 * @name $().jstree([arg]) 190 * @param {String|Object} arg 191 * @return {Mixed} 192 */ 193 $.fn.jstree = function (arg) { 194 // check for string argument 195 var is_method = (typeof arg === 'string'), 196 args = Array.prototype.slice.call(arguments, 1), 197 result = null; 198 this.each(function () { 199 // get the instance (if there is one) and method (if it exists) 200 var instance = $.jstree.reference(this), 201 method = is_method && instance ? instance[arg] : null; 202 // if calling a method, and method is available - execute on the instance 203 result = is_method && method ? 204 method.apply(instance, args) : 205 null; 206 // if there is no instance and no method is being called - create one 207 if(!instance && !is_method && (arg === undefined || $.isPlainObject(arg))) { 208 $(this).data('jstree', new $.jstree.create(this, arg)); 209 } 210 // if there is an instance and no method is called - return the instance 211 if(instance && !is_method) { 212 result = instance; 213 } 214 // if there was a method call which returned a result - break and return the value 215 if(result !== null && result !== undefined) { 216 return false; 217 } 218 }); 219 // if there was a method call with a valid return value - return that, otherwise continue the chain 220 return result !== null && result !== undefined ? 221 result : this; 222 }; 223 /** 224 * used to find elements containing an instance 225 * 226 * __Examples__ 227 * 228 * $('div:jstree').each(function () { 229 * $(this).jstree('destroy'); 230 * }); 231 * 232 * @name $(':jstree') 233 * @return {jQuery} 234 */ 235 $.expr[':'].jstree = $.expr.createPseudo(function(search) { 236 return function(a) { 237 return $(a).hasClass('jstree') && 238 $(a).data('jstree') !== undefined; 239 }; 240 }); 241 242 /** 243 * stores all defaults for the core 244 * @name $.jstree.defaults.core 245 */ 246 $.jstree.defaults.core = { 247 /** 248 * data configuration 249 * 250 * 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). 251 * 252 * You can also pass in a HTML string or a JSON array here. 253 * 254 * 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. 255 * 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. 256 * 257 * 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. 258 * 259 * __Examples__ 260 * 261 * // AJAX 262 * $('#tree').jstree({ 263 * 'core' : { 264 * 'data' : { 265 * 'url' : '/get/children/', 266 * 'data' : function (node) { 267 * return { 'id' : node.id }; 268 * } 269 * } 270 * }); 271 * 272 * // direct data 273 * $('#tree').jstree({ 274 * 'core' : { 275 * 'data' : [ 276 * 'Simple root node', 277 * { 278 * 'id' : 'node_2', 279 * 'text' : 'Root node with options', 280 * 'state' : { 'opened' : true, 'selected' : true }, 281 * 'children' : [ { 'text' : 'Child 1' }, 'Child 2'] 282 * } 283 * ] 284 * }); 285 * 286 * // function 287 * $('#tree').jstree({ 288 * 'core' : { 289 * 'data' : function (obj, callback) { 290 * callback.call(this, ['Root 1', 'Root 2']); 291 * } 292 * }); 293 * 294 * @name $.jstree.defaults.core.data 295 */ 296 data : false, 297 /** 298 * configure the various strings used throughout the tree 299 * 300 * You can use an object where the key is the string you need to replace and the value is your replacement. 301 * Another option is to specify a function which will be called with an argument of the needed
301string and should return the replacement. 302 * If left as `false` no replacement is made. 303 * 304 * __Examples__ 305 * 306 * $('#tree').jstree({ 307 * 'core' : { 308 * 'strings' : { 309 * 'Loading...' : 'Please wait ...' 310 * } 311 * } 312 * }); 313 * 314 * @name $.jstree.defaults.core.strings 315 */ 316 strings : false, 317 /** 318 * determines what happens when a user tries to modify the structure of the tree 319 * If left as `false` all operations like create, rename, delete, move or copy are prevented. 320 * You can set this to `true` to allow all interactions or use a function to have better control. 321 * 322 * __Examples__ 323 * 324 * $('#tree').jstree({ 325 * 'core' : { 326 * 'check_callback' : function (operation, node, node_parent, node_position) { 327 * // operation can be 'create_node', 'rename_node', 'delete_node', 'move_node' or 'copy_node' 328 * // in case of 'rename_node' node_position is filled with the new node name 329 * return operation === 'rename_node' ? true : false; 330 * } 331 * } 332 * }); 333 * 334 * @name $.jstree.defaults.core.check_callback 335 */ 336 check_callback : false, 337 /** 338 * a callback called with a single object parameter in the instance's scope when something goes wrong (operation prevented, ajax failed, etc) 339 * @name $.jstree.defaults.core.error 340 */ 341 error : $.noop, 342 /** 343 * the open / close animation duration in milliseconds - set this to `false` to disable the animation (default is `200`) 344 * @name $.jstree.defaults.core.animation 345 */ 346 animation : 200, 347 /** 348 * a boolean indicating if multiple nodes can be selected 349 * @name $.jstree.defaults.core.multiple 350 */ 351 multiple : true, 352 /** 353 * theme configuration object 354 * @name $.jstree.defaults.core.themes 355 */ 356 themes : { 357 /** 358 * the name of the theme to use (if left as `false` the default theme is used) 359 * @name $.jstree.defaults.core.themes.name 360 */ 361 name : false, 362 /** 363 * 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. 364 * @name $.jstree.defaults.core.themes.url 365 */ 366 url : false, 367 /** 368 * the location of all jstree themes - only used if `url` is set to `true` 369 * @name $.jstree.defaults.core.themes.dir 370 */ 371 dir : false, 372 /** 373 * a boolean indicating if connecting dots are shown 374 * @name $.jstree.defaults.core.themes.dots 375 */ 376 dots : true, 377 /** 378 * a boolean indicating if node icons are shown 379 * @name $.jstree.defaults.core.themes.icons 380 */ 381 icons : true, 382 /** 383 * a boolean indicating if the tree background is striped 384 * @name $.jstree.defaults.core.themes.stripes 385 */ 386 stripes : false, 387 /** 388 * a string (or boolean `false`) specifying the theme variant to use (if the theme supports variants) 389 * @name $.jstree.defaults.core.themes.variant 390 */ 391 variant : false, 392 /** 393 * a boolean specifying if a reponsive version of the theme should kick in on smaller screens (if the theme supports it). Defaults to `true`. 394 * @name $.jstree.defaults.core.themes.responsive 395 */ 396 responsive : true 397 }, 398 /** 399 * 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) 400 * @name $.jstree.defaults.core.expand_selected_onload 401 */ 402 expand_selected_onload : true 403 }; 404 $.jstree.core.prototype = { 405 /** 406 * used to decorate an instance with a plugin. Used internally. 407 * @private 408 * @name plugin(deco [, opts]) 409 * @param {String} deco the plugin to decorate with 410 * @param {Object} opts options for the plugin 411 * @return {jsTree} 412 */ 413 plugin : function (deco, opts) { 414 var Child = $.jstree.plugins[deco]; 415 if(Child) { 416 this._data[deco] = {}; 417 Child.prototype = this; 418 return new Child(opts, this); 419 } 420 return this; 421 }, 422 /** 423 * used to decorate an instance with a plugin. Used internally. 424 * @private 425 * @name init(el, optons) 426 * @param {DOMElement|jQuery|String} el the element we are transforming 427 * @param {Object} options options for this instance 428 * @trigger init.jstree, loading.jstree, loaded.jstree, ready.jstree, changed.jstree 429 */ 430 init : function (el, options) { 431 this._model = { 432 data : { 433 '#' : { 434 id : '#', 435 parent : null, 436 parents : [], 437 children : [], 438 children_d : [], 439 state : { loaded : false } 440 } 441 }, 442 changed : [], 443 force_full_redraw : false, 444 redraw_timeout : false, 445 default_state : { 446 loaded : true, 447 opened : false, 448 selected : false, 449 disabled : false 450 } 451 }; 452 453 this.element = $(el).addClass('jstree jstree-' + this._id); 454 this.settings = options; 455 this.element.bind("destroyed", $.proxy(this.teardown, this)); 456 457 this._data.core.ready = false;
458 this._data.core.loaded = false; 459 this._data.core.rtl = (this.element.css("direction") === "rtl"); 460 this.element[this._data.core.rtl ? 'addClass' : 'removeClass']("jstree-rtl"); 461 this.element.attr('role','tree'); 462 463 this.bind(); 464 /** 465 * triggered after all events are bound 466 * @event 467 * @name init.jstree 468 */ 469 this.trigger("init"); 470 471 this._data.core.original_container_html = this.element.find(" > ul > li").clone(true); 472 this._data.core.original_container_html 473 .find("li").addBack() 474 .contents().filter(function() { 475 return this.nodeType === 3 && (!this.nodeValue || /^\s+$/.test(this.nodeValue)); 476 }) 477 .remove(); 478 this.element.html("<"+"ul class='jstree-container-ul'><"+"li class='jstree-initial-node jstree-loading jstree-leaf jstree-last'><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>"); 479 this._data.core.li_height = this.get_container_ul().children("li:eq(0)").height() || 18; 480 /** 481 * triggered after the loading text is shown and before loading starts 482 * @event 483 * @name loading.jstree 484 */ 485 this.trigger("loading"); 486 this.load_node('#'); 487 }, 488 /** 489 * destroy an instance 490 * @name destroy() 491 */ 492 destroy : function () { 493 this.element.unbind("destroyed", this.teardown); 494 this.teardown(); 495 }, 496 /** 497 * part of the destroying of an instance. Used internally. 498 * @private 499 * @name teardown() 500 */ 501 teardown : function () { 502 this.unbind(); 503 this.element 504 .removeClass('jstree') 505 .removeData('jstree') 506 .find("[class^='jstree']") 507 .addBack() 508 .attr("class", function () { return this.className.replace(/jstree[^ ]*|$/ig,''); }); 509 this.element = null; 510 }, 511 /** 512 * bind all events. Used internally. 513 * @private 514 * @name bind() 515 */ 516 bind : function () { 517 this.element 518 .on("dblclick.jstree", function () { 519 if(document.selection && document.selection.empty) { 520 document.selection.empty(); 521 } 522 else { 523 if(window.getSelection) { 524 var sel = window.getSelection(); 525 try { 526 sel.removeAllRanges(); 527 sel.collapse(); 528 } catch (ignore) { } 529 } 530 } 531 }) 532 .on("click.jstree", ".jstree-ocl", $.proxy(function (e) { 533 this.toggle_node(e.target); 534 }, this)) 535 .on("click.jstree", ".jstree-anchor", $.proxy(function (e) { 536 e.preventDefault(); 537 $(e.currentTarget).focus(); 538 this.activate_node(e.currentTarget, e); 539 }, this)) 540 .on('keydown.jstree', '.jstree-anchor', $.proxy(function (e) { 541 var o = null; 542 switch(e.which) { 543 case 13: 544 case 32: 545 e.type = "click"; 546 $(e.currentTarget).trigger(e); 547 break; 548 case 37: 549 e.preventDefault(); 550 if(this.is_open(e.currentTarget)) { 551 this.close_node(e.currentTarget); 552 } 553 else { 554 o = this.get_prev_dom(e.currentTarget); 555 if(o && o.length) { o.children('.jstree-anchor').focus(); } 556 } 557 break; 558 case 38: 559 e.preventDefault(); 560 o = this.get_prev_dom(e.currentTarget); 561 if(o && o.length) { o.children('.jstree-anchor').focus(); } 562 break; 563 case 39: 564 e.preventDefault(); 565 if(this.is_closed(e.currentTarget)) { 566 this.open_node(e.currentTarget, function (o) { this.get_node(o, true).children('.jstree-anchor').focus(); }); 567 } 568 else { 569 o = this.get_next_dom(e.currentTarget); 570 if(o && o.length) { o.children('.jstree-anchor').focus(); } 571 } 572 break; 573 case 40: 574 e.preventDefault(); 575 o = this.get_next_dom(e.currentTarget); 576 if(o && o.length) { o.children('.jstree-anchor').focus(); } 577 break; 578 // delete 579 case 46: 580 e.preventDefault(); 581 o = this.get_node(e.currentTarget); 582 if(o && o.id && o.id !== '#') { 583 o = this.is_selected(o) ? this.get_selected() : o; 584 // this.delete_node(o); 585 } 586 break; 587 // f2 588 case 113: 589 e.preventDefault(); 590 o = this.get_node(e.currentTarget); 591 /*! 592 if(o && o.id && o.id !== '#') { 593 // this.edit(o); 594 } 595 */ 596 break; 597 default: 598 // console.log(e.which); 599 break; 600 } 601 }, this)) 602 .on("load_node.jstree", $.proxy(function (e, data) { 603 if(data.status) { 604 if(data.node.id === '#' && !this._data.core.loaded) { 605 this._data.core.loaded = true; 606 /** 607 * triggered after the root node is loaded for the first time 608 * @event 609 * @name loaded.jstree 610 */ 611 this.trigger("loaded"); 612 } 613 if(!this._data.core.ready && !this.get_container_ul().find('.jstree-loading:eq(0)').length) { 614 this._data.core.ready = true; 615 if(this._data.core.selected.length) { 616 if(this.settings.core.expand_selected_onload) { 617 var tmp = [], i, j; 618 for(i = 0, j = this._data.core.selected.length; i < j; i++) { 619 tmp = tmp.concat(this._model.data[this._data.core.selected[i]].parents); 620 } 621 tmp = $.vakata.array_unique(tmp); 622 for(i = 0, j = tmp.length; i < j; i++) { 623 this.open_node(tmp[i], false, 0); 624 } 625 } 626 this.trigger('changed', { 'action' : 'ready', 'selected' : this._data.core.selected }); 627 } 628 /** 629 * triggered after all nodes are finished loading 630 * @event 631 * @name ready.jstree 632 */ 633 setTimeout($.proxy(function () { this.trigger("ready"); }, this), 0); 634 } 635 } 636 }, this)) 637 // THEME RELATED 638 .on("init.jstree", $.proxy(function () { 639 var s = this.settings.core.themes; 640 this._data.core.themes.dots = s.dots; 641 this._data.core.themes.stripes = s.stripes; 642 this._data.core.themes.icons = s.icons; 643 this.set_theme(s.name || "default", s.url); 644 this.set_theme_variant(s.variant); 645 }, this)) 646 .on("loading.jstree", $.proxy(function () { 647 this[ this._data.core.themes.dots ? "show_dots" : "hide_dots" ](); 648 this[ this._data.core.themes.icons ? "show_icons" : "hide_icons" ](); 649 this[ this._data.core.themes.stripes ? "show_stripes" : "hide_stripes" ](); 650 }, this)) 651 .on('focus.jstree', '.jstree-anchor', $.proxy(function (e) { 652 this.element.find('.jstree-hovered').not(e.currentTarget).mouseleave(); 653 $(e.currentTarget).mouseenter(); 654 }, this)) 655 .on('mouseenter.jstree', '.jstree-anchor', $.proxy(function (e) { 656 this.hover_node(e.currentTarget); 657 }, this)) 658 .on('mouseleave.jstree', '.jstree-anchor', $.proxy(function (e) { 659 this.dehover_node(e.currentTarget); 660 }, this)); 661 }, 662 /** 663 * part of the destroying of an instance. Used internally. 664 * @private 665 * @name unbind() 666 */ 667 unbind : function () { 668 this.element.off('.jstree'); 669 $(document).off('.jstree-' + this._id); 670 }, 671 /** 672 * trigger an event. Used internally. 673 * @private 674 * @name trigger(ev [, data]) 675 * @param {String} ev the name of the event to trigger 676 * @param {Object} data additional data to pass with the event 677 */ 678 trigger : function (ev, data) { 679 if(!data) { 680 data = {}; 681 } 682 data.instance = this; 683 this.element.triggerHandler(ev.replace('.jstree','') + '.jstree', data); 684 }, 685 /** 686 * returns the jQuery extended instance container 687 * @name get_container() 688 * @return {jQuery} 689 */ 690 get_container : function () { 691 return this.element; 692 }, 693 /** 694 * returns the jQuery extended main UL node inside the instance container. Used internally. 695 * @private 696 * @name get_container_ul() 697 * @return {jQuery} 698 */ 699 get_container_ul : function () { 700 return this.element.children("ul:eq(0)"); 701 }, 702 /** 703 * gets string replacements (localization). Used internally. 704 * @private 705 * @name get_string(key) 706 * @param {String} key 707 * @return {String} 708 */ 709 get_string : function (key) { 710 var a = this.settings.core.strings; 711 if($.isFunction(a)) { return a.call(this, key); } 712 if(a && a[key]) { return a[key]; } 713 return key; 714 }, 715 /** 716 * gets the first child of a DOM node. Used internally. 717 * @private 718 * @name _firstChild(dom) 719 * @param {DOMElement} dom 720 * @return {DOMElement} 721 */ 722 _firstChild : function (dom) { 723 dom = dom ? dom.firstChild : null; 724 while(dom !== null && dom.nodeType !== 1) { 725 dom = dom.nextSibling; 726 } 727 return dom; 728 }, 729 /** 730 * gets the next sibling of a DOM node. Used internally. 731 * @private 732 * @name _nextSibling(dom) 733 * @param {DOMElement} dom 734 * @return {DOMElement} 735 */ 736 _nextSibling : function (dom) { 737 dom = dom ? dom.nextSibling : null; 738 while(dom !== null && dom.nodeType !== 1) { 739 dom = dom.nextSibling; 740 } 741 return dom; 742 }, 743 /** 744 * gets the previous sibling of a DOM node. Used internally. 745 * @private 746 * @name _previousSibling(dom) 747 * @param {DOMElement} dom 748 * @return {DOMElement} 749 */ 750 _previousSibling : function (dom) { 751 dom = dom ? dom.previousSibling : null; 752 while(dom !== null && dom.nodeType !== 1) { 753 dom = dom.previousSibling; 754 } 755 return dom; 756 }, 757 /** 758 * 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) 759 * @name get_node(obj [, as_dom]) 760 * @param {mixed} obj 761 * @param {Boolean} as_dom 762 * @return {Object|jQuery} 763 */ 764 get_node : function (obj, as_dom) { 765 if(obj && obj.id) { 766 obj = obj.id; 767 } 768 var dom; 769 try { 770 if(this._model.data[obj]) { 771 obj = this._model.data[obj]; 772 } 773 else if(((dom = $(obj, this.element)).length || (dom = $('#' + obj, this.element)).length) && this._model.data[dom.closest('li').attr('id')]) { 774 obj = this._model.data[dom.closest('li').attr('id')]; 775 } 776 else if((dom = $(obj, this.element)).length && dom.hasClass('jstree')) { 777 obj = this._model.data['#']; 778 } 779 else { 780 return false;
781 } 782 783 if(as_dom) { 784 obj = obj.id === '#' ? this.element : $(document.getElementById(obj.id)); 785 } 786 return obj; 787 } catch (ex) { return false; } 788 }, 789 /** 790 * get the path to a node, either consisting of node texts, or of node IDs, optionally glued together (otherwise an array) 791 * @name get_path(obj [, glue, ids]) 792 * @param {mixed} obj the node 793 * @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 794 * @param {Boolean} ids if set to true build the path using ID, otherwise node text is used 795 * @return {mixed} 796 */ 797 get_path : function (obj, glue, ids) { 798 obj = obj.parents ? obj : this.get_node(obj); 799 if(!obj || obj.id === '#' || !obj.parents) { 800 return false; 801 } 802 var i, j, p = []; 803 p.push(ids ? obj.id : obj.text); 804 for(i = 0, j = obj.parents.length; i < j; i++) { 805 p.push(ids ? obj.parents[i] : this.get_text(obj.parents[i])); 806 } 807 p = p.reverse().slice(1); 808 return glue ? p.join(glue) : p; 809 }, 810 /** 811 * get the next visible node that is below the `obj` node. If `strict` is set to `true` only sibling nodes are returned. 812 * @name get_next_dom(obj [, strict]) 813 * @param {mixed} obj 814 * @param {Boolean} strict 815 * @return {jQuery} 816 */ 817 get_next_dom : function (obj, strict) { 818 var tmp; 819 obj = this.get_node(obj, true); 820 if(obj[0] === this.element[0]) { 821 tmp = this._firstChild(this.get_container_ul()[0]); 822 return tmp ? $(tmp) : false; 823 } 824 if(!obj || !obj.length) { 825 return false; 826 } 827 if(strict) { 828 tmp = this._nextSibling(obj[0]); 829 return tmp ? $(tmp) : false; 830 } 831 if(obj.hasClass("jstree-open")) { 832 tmp = this._firstChild(obj.children('ul')[0]); 833 return tmp ? $(tmp) : false; 834 } 835 if((tmp = this._nextSibling(obj[0])) !== null) { 836 return $(tmp); 837 } 838 return obj.parentsUntil(".jstree","li").next("li").eq(0); 839 }, 840 /** 841 * get the previous visible node that is above the `obj` node. If `strict` is set to `true` only sibling nodes are returned. 842 * @name get_prev_dom(obj [, strict]) 843 * @param {mixed} obj 844 * @param {Boolean} strict 845 * @return {jQuery} 846 */ 847 get_prev_dom : function (obj, strict) { 848 var tmp; 849 obj = this.get_node(obj, true); 850 if(obj[0] === this.element[0]) { 851 tmp = this.get_container_ul()[0].lastChild; 852 return tmp ? $(tmp) : false; 853 } 854 if(!obj || !obj.length) { 855 return false; 856 } 857 if(strict) { 858 tmp = this._previousSibling(obj[0]); 859 return tmp ? $(tmp) : false; 860 } 861 if((tmp = this._previousSibling(obj[0])) !== null) { 862 obj = $(tmp); 863 while(obj.hasClass("jstree-open")) { 864 obj = obj.children("ul:eq(0)").children("li:last"); 865 } 866 return obj; 867 } 868 tmp = obj[0].parentNode.parentNode; 869 return tmp && tmp.tagName === 'LI' ? $(tmp) : false; 870 }, 871 /** 872 * get the parent ID of a node 873 * @name get_parent(obj) 874 * @param {mixed} obj 875 * @return {String} 876 */ 877 get_parent : function (obj) { 878 obj = this.get_node(obj); 879 if(!obj || obj.id === '#') { 880 return false; 881 } 882 return obj.parent; 883 }, 884 /** 885 * get a jQuery collection of all the children of a node (node must be rendered) 886 * @name get_children_dom(obj) 887 * @param {mixed} obj 888 * @return {jQuery} 889 */ 890 get_children_dom : function (obj) { 891 obj = this.get_node(obj, true); 892 if(obj[0] === this.element[0]) { 893 return this.get_container_ul().children("li"); 894 } 895 if(!obj || !obj.length) { 896 return false; 897 } 898 return obj.children("ul").children("li"); 899 }, 900 /** 901 * checks if a node has children 902 * @name is_parent(obj) 903 * @param {mixed} obj 904 * @return {Boolean} 905 */ 906 is_parent : function (obj) { 907 obj = this.get_node(obj); 908 return obj && (obj.state.loaded === false || obj.children.length > 0); 909 }, 910 /** 911 * checks if a node is loaded (its children are available) 912 * @name is_loaded(obj) 913 * @param {mixed} obj 914 * @return {Boolean} 915 */ 916 is_loaded : function (obj) { 917 obj = this.get_node(obj); 918 return obj && obj.state.loaded; 919 }, 920 /** 921 * check if a node is currently loading (fetching children) 922 * @name is_loading(obj) 923 * @param {mixed} obj 924 * @return {Boolean} 925 */ 926 is_loading : function (obj) { 927 obj = this.get_node(obj, true); 928 return obj && obj.hasClass("jstree-loading"); 929 }, 930 /** 931 * check if a node is opened 932 * @name is_open(obj) 933 * @param {mixed} obj 934 * @return {Boolean} 935 */
936 is_open : function (obj) { 937 obj = this.get_node(obj); 938 return obj && obj.state.opened; 939 }, 940 /** 941 * check if a node is in a closed state 942 * @name is_closed(obj) 943 * @param {mixed} obj 944 * @return {Boolean} 945 */ 946 is_closed : function (obj) { 947 obj = this.get_node(obj); 948 return obj && this.is_parent(obj) && !obj.state.opened; 949 }, 950 /** 951 * check if a node has no children 952 * @name is_leaf(obj) 953 * @param {mixed} obj 954 * @return {Boolean} 955 */ 956 is_leaf : function (obj) { 957 return !this.is_parent(obj); 958 }, 959 /** 960 * loads a node (fetches its children using the `core.data` setting). Multiple nodes can be passed to by using an array. 961 * @name load_node(obj [, callback]) 962 * @param {mixed} obj 963 * @param {function} callback a function to be executed once loading is conplete, the function is executed in the instance's scope and receives two arguments - the node and a boolean status 964 * @return {Boolean} 965 * @trigger load_node.jstree 966 */ 967 load_node : function (obj, callback) { 968 var t1, t2; 969 if($.isArray(obj)) { 970 obj = obj.slice(); 971 for(t1 = 0, t2 = obj.length; t1 < t2; t1++) { 972 this.load_node(obj[t1], callback); 973 } 974 return true; 975 } 976 obj = this.get_node(obj); 977 if(!obj) { 978 callback.call(this, obj, false); 979 return false; 980 } 981 this.get_node(obj, true).addClass("jstree-loading"); 982 this._load_node(obj, $.proxy(function (status) { 983 obj.state.loaded = status; 984 this.get_node(obj, true).removeClass("jstree-loading"); 985 /** 986 * triggered after a node is loaded 987 * @event 988 * @name load_node.jstree 989 * @param {Object} node the node that was loading 990 * @param {Boolean} status was the node loaded successfully 991 */ 992 this.trigger('load_node', { "node" : obj, "status" : status }); 993 if(callback) { 994 callback.call(this, obj, status); 995 } 996 }, this)); 997 return true; 998 }, 999 /** 1000 * handles the actual loading of a node. Used only internally. 1001 * @private 1002 * @name _load_node(obj [, callback]) 1003 * @param {mixed} obj 1004 * @param {function} callback a function to be executed once loading is conplete, the function is executed in the instance's scope and receives one argument - a boolean status 1005 * @return {Boolean} 1006 */ 1007 _load_node : function (obj, callback) { 1008 var s = this.settings.core.data, t; 1009 // use original HTML 1010 if(!s) { 1011 return callback.call(this, obj.id === '#' ? this._append_html_data(obj, this._data.core.original_container_html.clone(true)) : false); 1012 } 1013 if($.isFunction(s)) { 1014 return s.call(this, obj, $.proxy(function (d) { 1015 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)); 1016 }, this)); 1017 } 1018 if(typeof s === 'object') { 1019 if(s.url) { 1020 s = $.extend(true, {}, s); 1021 if($.isFunction(s.url)) { 1022 s.url = s.url.call(this, obj); 1023 } 1024 if($.isFunction(s.data)) { 1025 s.data = s.data.call(this, obj); 1026 } 1027 return $.ajax(s) 1028 .done($.proxy(function (d,t,x) { 1029 var type = x.getResponseHeader('Content-Type'); 1030 if(type.indexOf('json') !== -1) { 1031 return callback.call(this, this._append_json_data(obj, d)); 1032 } 1033 if(type.indexOf('html') !== -1) { 1034 return callback.call(this, this._append_html_data(obj, $(d))); 1035 } 1036 }, this)) 1037 .fail($.proxy(function () { 1038 callback.call(this, false); 1039 this._data.core.last_error = { 'error' : 'ajax', 'plugin' : 'core', 'id' : 'core_04', 'reason' : 'Could not load node', 'data' : JSON.stringify(s) }; 1040 this.settings.core.error.call(this, this._data.core.last_error); 1041 }, this)); 1042 } 1043 t = ($.isArray(s) || $.isPlainObject(s)) ? JSON.parse(JSON.stringify(s)) : s; 1044 return callback.call(this, this._append_json_data(obj, t)); 1045 } 1046 if(typeof s === 'string') { 1047 return callback.call(this, this._append_html_data(obj, s)); 1048 } 1049 return callback.call(this, false); 1050 }, 1051 /** 1052 * adds a node to the list of nodes to redraw. Used only internally. 1053 * @private 1054 * @name _node_changed(obj [, callback]) 1055 * @param {mixed} obj 1056 */ 1057 _node_changed : function (obj) { 1058 obj = this.get_node(obj); 1059 if(obj) { 1060 this._model.changed.push(obj.id); 1061 } 1062 }, 1063 /** 1064 * appends HTML content to the tree. Used internally. 1065 * @private 1066 * @name _append_html_data(obj, data) 1067 * @param {mixed} obj the node to append to 1068 * @param {String} data the HTML string to parse and append 1069 * @return {Boolean} 1070 * @trigger model.jstree, changed.jstree 1071 */ 1072 _append_html_data : function (dom, data) { 1073 dom = this.get_node(dom); 1074 dom.children = []; 1075 dom.children_d = []; 1076 var dat = data.is('ul') ? data.children() : data, 1077 par = dom.id, 1078 chd = [], 1079 dpc = [], 1080 m = this._model.data, 1081 p = m[par], 1082 s = this._data.core.selected.length, 1083 tmp, i, j; 1084 dat.each($.proxy(function (i, v) { 1085 tmp = this._parse_model_from_html($(v), par, p.parents.concat()); 1086 if(tmp) { 1087 chd.push(tmp); 1088 dpc.push(tmp); 1089 if(m[tmp].children_d.length) { 1090 dpc = dpc.concat(m[tmp].children_d); 1091 } 1092 } 1093 }, this)); 1094 p.children = chd; 1095 p.children_d = dpc; 1096 for(i = 0, j = p.parents.length; i < j; i++) { 1097 m[p.parents[i]].children_d = m[p.parents[i]].children_d.concat(dpc); 1098 } 1099 /** 1100 * triggered when new data is inserted to the tree model 1101 * @event 1102 * @name model.jstree 1103 * @param {Array} nodes an array of node IDs 1104 * @param {String} parent the parent ID of the nodes 1105 */ 1106 this.trigger('model', { "nodes" : dpc, 'parent' : par }); 1107 if(par !== '#') { 1108 this._node_changed(par); 1109 this.redraw(); 1110 } 1111 else { 1112 this.get_container_ul().children('.jstree-initial-node').remove(); 1113 this.redraw(true); 1114 } 1115 if(this._data.core.selected.length !== s) { 1116 this.trigger('changed', { 'action' : 'model', 'selected' : this._data.core.selected }); 1117 } 1118 return true; 1119 }, 1120 /** 1121 * appends JSON content to the tree. Used internally. 1122 * @private 1123 * @name _append_json_data(obj, data) 1124 * @param {mixed} obj the node to append to 1125 * @param {String} data the JSON object to parse and append 1126 * @return {Boolean} 1127 */ 1128 _append_json_data : function (dom, data) { 1129 dom = this.get_node(dom); 1130 dom.children = []; 1131 dom.children_d = []; 1132 var dat = data, 1133 par = dom.id, 1134 chd = [], 1135 dpc = [], 1136 m = this._model.data, 1137 p = m[par], 1138 s = this._data.core.selected.length, 1139 tmp, i, j; 1140 // *%$@!!! 1141 if(dat.d) { 1142 dat = dat.d; 1143 if(typeof dat === "string") { 1144 dat = JSON.parse(dat); 1145 } 1146 } 1147 if(!$.isArray(dat)) { dat = [dat]; } 1148 if(dat.length && dat[0].id !== undefined && dat[0].parent !== undefined) { 1149 // Flat JSON support (for easy import from DB): 1150 // 1) convert to object (foreach)
vendor: 16,499 bytes, lines 1151-1710
1151 for(i = 0, j = dat.length; i < j; i++) { 1152 if(!dat[i].children) { 1153 dat[i].children = []; 1154 } 1155 m[dat[i].id] = dat[i]; 1156 } 1157 // 2) populate children (foreach) 1158 for(i = 0, j = dat.length; i < j; i++) { 1159 m[dat[i].parent].children.push(dat[i].id); 1160 // populate parent.children_d 1161 p.children_d.push(dat[i].id); 1162 } 1163 // 3) normalize && populate parents and children_d with recursion 1164 for(i = 0, j = p.children.length; i < j; i++) { 1165 tmp = this._parse_model_from_flat_json(m[p.children[i]], par, p.parents.concat()); 1166 dpc.push(tmp); 1167 if(m[tmp].children_d.length) { 1168 dpc = dpc.concat(m[tmp].children_d); 1169 } 1170 } 1171 // ?) three_state selection - p.state.selected && t - (if three_state foreach(dat => ch) -> foreach(parents) if(parent.selected) child.selected = true; 1172 } 1173 else { 1174 for(i = 0, j = dat.length; i < j; i++) { 1175 tmp = this._parse_model_from_json(dat[i], par, p.parents.concat()); 1176 if(tmp) { 1177 chd.push(tmp); 1178 dpc.push(tmp); 1179 if(m[tmp].children_d.length) { 1180 dpc = dpc.concat(m[tmp].children_d); 1181 } 1182 } 1183 } 1184 p.children = chd; 1185 p.children_d = dpc; 1186 for(i = 0, j = p.parents.length; i < j; i++) { 1187 m[p.parents[i]].children_d = m[p.parents[i]].children_d.concat(dpc); 1188 } 1189 } 1190 this.trigger('model', { "nodes" : dpc, 'parent' : par }); 1191 1192 if(par !== '#') { 1193 this._node_changed(par); 1194 this.redraw(); 1195 } 1196 else { 1197 // this.get_container_ul().children('.jstree-initial-node').remove(); 1198 this.redraw(true); 1199 } 1200 if(this._data.core.selected.length !== s) { 1201 this.trigger('changed', { 'action' : 'model', 'selected' : this._data.core.selected }); 1202 } 1203 return true; 1204 }, 1205 /** 1206 * parses a node from a jQuery object and appends them to the in memory tree model. Used internally. 1207 * @private 1208 * @name _parse_model_from_html(d [, p, ps]) 1209 * @param {jQuery} d the jQuery object to parse 1210 * @param {String} p the parent ID 1211 * @param {Array} ps list of all parents 1212 * @return {String} the ID of the object added to the model 1213 */ 1214 _parse_model_from_html : function (d, p, ps) { 1215 if(!ps) { ps = []; } 1216 else { ps = [].concat(ps); } 1217 if(p) { ps.unshift(p); } 1218 var c, e, m = this._model.data, 1219 data = { 1220 id : false, 1221 text : false, 1222 icon : true, 1223 parent : p, 1224 parents : ps, 1225 children : [], 1226 children_d : [], 1227 data : null, 1228 state : { }, 1229 li_attr : { id : false }, 1230 a_attr : { href : '#' }, 1231 original : false 1232 }, i, tmp, tid; 1233 for(i in this._model.default_state) { 1234 if(this._model.default_state.hasOwnProperty(i)) { 1235 data.state[i] = this._model.default_state[i]; 1236 } 1237 } 1238 tmp = $.vakata.attributes(d, true); 1239 $.each(tmp, function (i, v) { 1240 v = $.trim(v); 1241 if(!v.length) { return true; } 1242 data.li_attr[i] = v; 1243 if(i === 'id') { 1244 data.id = v; 1245 } 1246 }); 1247 tmp = d.children('a').eq(0); 1248 if(tmp.length) { 1249 tmp = $.vakata.attributes(tmp, true); 1250 $.each(tmp, function (i, v) { 1251 v = $.trim(v); 1252 if(v.length) { 1253 data.a_attr[i] = v; 1254 } 1255 }); 1256 } 1257 tmp = d.children("a:eq(0)").length ? d.children("a:eq(0)").clone() : d.clone(); 1258 tmp.children("ins, i, ul").remove(); 1259 tmp = tmp.html(); 1260 tmp = $('<div />').html(tmp); 1261 data.text = tmp.html(); 1262 tmp = d.data(); 1263 data.data = tmp ? $.extend(true, {}, tmp) : null; 1264 data.state.opened = d.hasClass('jstree-open'); 1265 data.state.selected = d.children('a').hasClass('jstree-clicked'); 1266 data.state.disabled = d.children('a').hasClass('jstree-disabled'); 1267 if(data.data && data.data.jstree) { 1268 for(i in data.data.jstree) { 1269 if(data.data.jstree.hasOwnProperty(i)) { 1270 data.state[i] = data.data.jstree[i]; 1271 } 1272 } 1273 } 1274 tmp = d.children("a").children(".jstree-themeicon"); 1275 if(tmp.length) { 1276 data.icon = tmp.hasClass('jstree-themeicon-hidden') ? false : tmp.attr('rel'); 1277 } 1278 if(data.state.icon) { 1279 data.icon = data.state.icon; 1280 } 1281 tmp = d.children("ul").children("li"); 1282 do { 1283 tid = 'j' + this._id + '_' + (++this._cnt); 1284 } while(m[tid]); 1285 data.id = data.li_attr.id || tid; 1286 if(tmp.length) { 1287 tmp.each($.proxy(function (i, v) { 1288 c = this._parse_model_from_html($(v), data.id, ps); 1289 e = this._model.data[c]; 1290 data.children.push(c); 1291 if(e.children_d.length) { 1292 data.children_d = data.children_d.concat(e.children_d); 1293 } 1294 }, this)); 1295 data.children_d = data.children_d.concat(data.children); 1296 } 1297 else { 1298 if(d.hasClass('jstree-closed')) { 1299 data.state.loaded = false; 1300 } 1301 } 1302 if(data.li_attr['class']) { 1303 data.li_attr['class'] = data.li_attr['class'].replace('jstree-closed','').replace('jstree-open',''); 1304 } 1305 if(data.a_attr['class']) { 1306 data.a_attr['class'] = data.a_attr['class'].replace('jstree-clicked','').replace('jstree-disabled',''); 1307 } 1308 m[data.id] = data; 1309 if(data.state.selected) { 1310 this._data.core.selected.push(data.id); 1311 } 1312 return data.id; 1313 }, 1314 /** 1315 * 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. 1316 * @private 1317 * @name _parse_model_from_flat_json(d [, p, ps]) 1318 * @param {Object} d the JSON object to parse 1319 * @param {String} p the parent ID 1320 * @param {Array} ps list of all parents 1321 * @return {String} the ID of the object added to the model 1322 */ 1323 _parse_model_from_flat_json : function (d, p, ps) { 1324 if(!ps) { ps = []; } 1325 else { ps = ps.concat(); } 1326 if(p) { ps.unshift(p); } 1327 var tid = d.id, 1328 m = this._model.data, 1329 df = this._model.default_state, 1330 i, j, c, e, 1331 tmp = { 1332 id : tid, 1333 text : d.text || '', 1334 icon : d.icon !== undefined ? d.icon : true, 1335 parent : p, 1336 parents : ps, 1337 children : d.children || [], 1338 children_d : d.children_d || [], 1339 data : d.data, 1340 state : { }, 1341 li_attr : { id : false }, 1342 a_attr : { href : '#' }, 1343 original : false 1344 }; 1345 for(i in df) { 1346 if(df.hasOwnProperty(i)) { 1347 tmp.state[i] = df[i]; 1348 } 1349 } 1350 if(d && d.data && d.data.jstree && d.data.jstree.icon) { 1351 tmp.icon = d.data.jstree.icon; 1352 } 1353 if(d && d.data) { 1354 tmp.data = d.data; 1355 if(d.data.jstree) { 1356 for(i in d.data.jstree) { 1357 if(d.data.jstree.hasOwnProperty(i)) { 1358 tmp.state[i] = d.data.jstree[i]; 1359 } 1360 } 1361 } 1362 } 1363 if(d && typeof d.state === 'object') { 1364 for (i in d.state) { 1365 if(d.state.hasOwnProperty(i)) { 1366 tmp.state[i] = d.state[i]; 1367 } 1368 } 1369 } 1370 if(d && typeof d.li_attr === 'object') { 1371 for (i in d.li_attr) { 1372 if(d.li_attr.hasOwnProperty(i)) { 1373 tmp.li_attr[i] = d.li_attr[i]; 1374 } 1375 } 1376 } 1377 if(!tmp.li_attr.id) { 1378 tmp.li_attr.id = tid; 1379 } 1380 if(d && typeof d.a_attr === 'object') { 1381 for (i in d.a_attr) { 1382 if(d.a_attr.hasOwnProperty(i)) { 1383 tmp.a_attr[i] = d.a_attr[i]; 1384 } 1385 } 1386 } 1387 if(d && d.children && d.children === true) { 1388 tmp.state.loaded = false; 1389 tmp.children = []; 1390 tmp.children_d = []; 1391 } 1392 m[tmp.id] = tmp; 1393 for(i = 0, j = tmp.children.length; i < j; i++) { 1394 c = this._parse_model_from_flat_json(m[tmp.children[i]], tmp.id, ps); 1395 e = m[c]; 1396 tmp.children_d.push(c); 1397 if(e.children_d.length) { 1398 tmp.children_d = tmp.children_d.concat(e.children_d); 1399 } 1400 } 1401 delete d.data; 1402 delete d.children; 1403 m[tmp.id].original = d; 1404 if(tmp.state.selected) { 1405 this._data.core.selected.push(tmp.id); 1406 } 1407 return tmp.id; 1408 }, 1409 /** 1410 * parses a node from a JSON object and appends it to the in memory tree model. Used internally. 1411 * @private 1412 * @name _parse_model_from_json(d [, p, ps]) 1413 * @param {Object} d the JSON object to parse 1414 * @param {String} p the parent ID 1415 * @param {Array} ps list of all parents 1416 * @return {String} the ID of the object added to the model 1417 */ 1418 _parse_model_from_json : function (d, p, ps) { 1419 if(!ps) { ps = []; } 1420 else { ps = ps.concat(); } 1421 if(p) { ps.unshift(p); } 1422 var tid = false, i, j, c, e, m = this._model.data, df = this._model.default_state, tmp; 1423 do { 1424 tid = 'j' + this._id + '_' + (++this._cnt); 1425 } while(m[tid]); 1426 1427 tmp = { 1428 id : false, 1429 text : typeof d === 'string' ? d : '', 1430 icon : typeof d === 'object' && d.icon !== undefined ? d.icon : true, 1431 parent : p, 1432 parents : ps, 1433 children : [], 1434 children_d : [], 1435 data : null, 1436 state : { }, 1437 li_attr : { id : false }, 1438 a_attr : { href : '#' }, 1439 original : false 1440 }; 1441 for(i in df) { 1442 if(df.hasOwnProperty(i)) { 1443 tmp.state[i] = df[i]; 1444 } 1445 } 1446 if(d && d.id) { tmp.id = d.id; } 1447 if(d && d.text) { tmp.text = d.text; } 1448 if(d && d.data && d.data.jstree && d.data.jstree.icon) { 1449 tmp.icon = d.data.jstree.icon; 1450 } 1451 if(d && d.data) { 1452 tmp.data = d.data; 1453 if(d.data.jstree) { 1454 for(i in d.data.jstree) { 1455 if(d.data.jstree.hasOwnProperty(i)) { 1456 tmp.state[i] = d.data.jstree[i]; 1457 } 1458 } 1459 } 1460 } 1461 if(d && typeof d.state === 'object') { 1462 for (i in d.state) { 1463 if(d.state.hasOwnProperty(i)) { 1464 tmp.state[i] = d.state[i]; 1465 } 1466 } 1467 } 1468 if(d && typeof d.li_attr === 'object') { 1469 for (i in d.li_attr) { 1470 if(d.li_attr.hasOwnProperty(i)) { 1471 tmp.li_attr[i] = d.li_attr[i]; 1472 } 1473 } 1474 } 1475 if(tmp.li_attr.id && !tmp.id) { 1476 tmp.id = tmp.li_attr.id; 1477 } 1478 if(!tmp.id) { 1479 tmp.id = tid; 1480 } 1481 if(!tmp.li_attr.id) { 1482 tmp.li_attr.id = tmp.id; 1483 } 1484 if(d && typeof d.a_attr === 'object') { 1485 for (i in d.a_attr) { 1486 if(d.a_attr.hasOwnProperty(i)) { 1487 tmp.a_attr[i] = d.a_attr[i]; 1488 } 1489 } 1490 } 1491 if(d && d.children && d.children.length) { 1492 for(i = 0, j = d.children.length; i < j; i++) { 1493 c = this._parse_model_from_json(d.children[i], tmp.id, ps); 1494 e = m[c]; 1495 tmp.children.push(c); 1496 if(e.children_d.length) { 1497 tmp.children_d = tmp.children_d.concat(e.children_d); 1498 } 1499 } 1500 tmp.children_d = tmp.children_d.concat(tmp.children); 1501 } 1502 if(d && d.children && d.children === true) { 1503 tmp.state.loaded = false; 1504 tmp.children = []; 1505 tmp.children_d = []; 1506 } 1507 delete d.data; 1508 delete d.children; 1509 tmp.original = d; 1510 m[tmp.id] = tmp; 1511 if(tmp.state.selected) { 1512 this._data.core.selected.push(tmp.id); 1513 } 1514 return tmp.id; 1515 }, 1516 /** 1517 * redraws all nodes that need to be redrawn. Used internally. 1518 * @private 1519 * @name _redraw() 1520 * @trigger redraw.jstree 1521 */ 1522 _redraw : function () { 1523 var nodes = this._model.force_full_redraw ? this._model.data['#'].children.concat([]) : this._model.changed.concat([]), 1524 f = document.createElement('UL'), tmp, i, j; 1525 for(i = 0, j = nodes.length; i < j; i++) { 1526 tmp = this.redraw_node(nodes[i], true, this._model.force_full_redraw); 1527 if(tmp && this._model.force_full_redraw) { 1528 f.appendChild(tmp); 1529 } 1530 } 1531 if(this._model.force_full_redraw) { 1532 f.className = this.get_container_ul()[0].className; 1533 this.element.empty().append(f); 1534 //this.get_container_ul()[0].appendChild(f); 1535 } 1536 this._model.force_full_redraw = false; 1537 this._model.changed = []; 1538 /** 1539 * triggered after nodes are redrawn 1540 * @event 1541 * @name redraw.jstree 1542 * @param {array} nodes the redrawn nodes 1543 */ 1544 this.trigger('redraw', { "nodes" : nodes }); 1545 }, 1546 /** 1547 * redraws all nodes that need to be redrawn or optionally - the whole tree 1548 * @name redraw([full]) 1549 * @param {Boolean} full if set to `true` all nodes are redrawn. 1550 */ 1551 redraw : function (full) { 1552 if(full) { 1553 this._model.force_full_redraw = true; 1554 } 1555 //if(this._model.redraw_timeout) { 1556 // clearTimeout(this._model.redraw_timeout); 1557 //} 1558 //this._model.redraw_timeout = setTimeout($.proxy(this._redraw, this),0); 1559 this._redraw(); 1560 }, 1561 /** 1562 * redraws a single node. Used internally. 1563 * @private 1564 * @name redraw_node(node, deep, is_callback) 1565 * @param {mixed} node the node to redraw 1566 * @param {Boolean} deep should child nodes be redrawn too 1567 * @param {Boolean} is_callback is this a recursion call 1568 */ 1569 redraw_node : function (node, deep, is_callback) { 1570 var obj = this.get_node(node), 1571 par = false, 1572 ind = false, 1573 old = false, 1574 i = false, 1575 j = false, 1576 k = false, 1577 c = '', 1578 d = document, 1579 m = this._model.data, 1580 f = false, 1581 s = false; 1582 if(!obj) { return false; } 1583 if(obj.id === '#') { return this.redraw(true); } 1584 deep = deep || obj.children.length === 0; 1585 node = d.getElementById(obj.id); //, this.element); 1586 if(!node) { 1587 deep = true; 1588 //node = d.createElement('LI'); 1589 if(!is_callback) { 1590 par = obj.parent !== '#' ? $('#' + obj.parent, this.element)[0] : null; 1591 if(par !== null && (!par || !m[obj.parent].state.opened)) { 1592 return false; 1593 } 1594 ind = $.inArray(obj.id, par === null ? m['#'].children : m[obj.parent].children); 1595 } 1596 } 1597 else { 1598 node = $(node); 1599 if(!is_callback) { 1600 par = node.parent().parent()[0]; 1601 if(par === this.element[0]) { 1602 par = null; 1603 } 1604 ind = node.index(); 1605 } 1606 // m[obj.id].data = node.data(); // use only node's data, no need to touch jquery storage 1607 if(!deep && obj.children.length && !node.children('ul').length) { 1608 deep = true; 1609 } 1610 if(!deep) { 1611 old = node.children('UL')[0]; 1612 } 1613 s = node.attr('aria-selected'); 1614 f = node.children('.jstree-anchor')[0] === document.activeElement; 1615 node.remove(); 1616 //node = d.createElement('LI'); 1617 //node = node[0]; 1618 } 1619 node = _node.cloneNode(true); 1620 // node is DOM, deep is boolean 1621 1622 c = 'jstree-node '; 1623 for(i in obj.li_attr) { 1624 if(obj.li_attr.hasOwnProperty(i)) { 1625 if(i === 'id') { continue; } 1626 if(i !== 'class') { 1627 node.setAttribute(i, obj.li_attr[i]); 1628 } 1629 else { 1630 c += obj.li_attr[i]; 1631 } 1632 } 1633 } 1634 if(s && s !== "false") { 1635 node.setAttribute('aria-selected', true); 1636 } 1637 if(!obj.children.length && obj.state.loaded) { 1638 c += ' jstree-leaf'; 1639 } 1640 else { 1641 c += obj.state.opened ? ' jstree-open' : ' jstree-closed'; 1642 node.setAttribute('aria-expanded', obj.state.opened); 1643 } 1644 if(obj.parent !== null && m[obj.parent].children[m[obj.parent].children.length - 1] === obj.id) { 1645 c += ' jstree-last'; 1646 } 1647 node.id = obj.id; 1648 node.className = c; 1649 c = ( obj.state.selected ? ' jstree-clicked' : '') + ( obj.state.disabled ? ' jstree-disabled' : ''); 1650 for(j in obj.a_attr) { 1651 if(obj.a_attr.hasOwnProperty(j)) { 1652 if(j === 'href' && obj.a_attr[j] === '#') { continue; } 1653 if(j !== 'class') { 1654 node.childNodes[1].setAttribute(j, obj.a_attr[j]); 1655 } 1656 else { 1657 c += ' ' + obj.a_attr[j]; 1658 } 1659 } 1660 } 1661 if(c.length) { 1662 node.childNodes[1].className = 'jstree-anchor ' + c; 1663 } 1664 if((obj.icon && obj.icon !== true) || obj.icon === false) { 1665 if(obj.icon === false) { 1666 node.childNodes[1].childNodes[0].className += ' jstree-themeicon-hidden'; 1667 } 1668 else if(obj.icon.indexOf('/') === -1 && obj.icon.indexOf('.') === -1) { 1669 node.childNodes[1].childNodes[0].className += ' ' + obj.icon + ' jstree-themeicon-custom'; 1670 } 1671 else { 1672 node.childNodes[1].childNodes[0].style.backgroundImage = 'url('+obj.icon+')'; 1673 node.childNodes[1].childNodes[0].style.backgroundPosition = 'center center'; 1674 node.childNodes[1].childNodes[0].style.backgroundSize = 'auto'; 1675 node.childNodes[1].childNodes[0].className += ' jstree-themeicon-custom'; 1676 } 1677 } 1678 //node.childNodes[1].appendChild(d.createTextNode(obj.text)); 1679 node.childNodes[1].innerHTML += obj.text; 1680 // if(obj.data) { $.data(node, obj.data); } // always work with node's data, no need to touch jquery store 1681 1682 if(deep && obj.children.length && obj.state.opened) { 1683 k = d.createElement('UL'); 1684 k.setAttribute('role', 'group'); 1685 k.className = 'jstree-children'; 1686 for(i = 0, j = obj.children.length; i < j; i++) { 1687 k.appendChild(this.redraw_node(obj.children[i], deep, true)); 1688 } 1689 node.appendChild(k); 1690 } 1691 if(old) { 1692 node.appendChild(old); 1693 } 1694 if(!is_callback) { 1695 // append back using par / ind 1696 if(!par) { 1697 par = this.element[0]; 1698 } 1699 if(!par.getElementsByTagName('UL').length) { 1700 i = d.createElement('UL'); 1701 i.setAttribute('role', 'group'); 1702 i.className = 'jstree-children'; 1703 par.appendChild(i); 1704 par = i; 1705 } 1706 else { 1707 par = par.getElementsByTagName('UL')[0]; 1708 } 1709 1710 if(ind < par.childNodes.length) {
1711 par.insertBefore(node, par.childNodes[ind]); 1712 } 1713 else { 1714 par.appendChild(node); 1715 } 1716 if(f) { 1717 node.childNodes[1].focus(); 1718 } 1719 } 1720 return node; 1721 }, 1722 /** 1723 * opens a node, revaling its children. If the node is not loaded it will be loaded and opened once ready. 1724 * @name open_node(obj [, callback, animation]) 1725 * @param {mixed} obj the node to open 1726 * @param {Function} callback a function to execute once the node is opened 1727 * @param {Number} animation the animation duration in milliseconds when opening the node (overrides the `core.animation` setting). Use `false` for no animation. 1728 * @trigger open_node.jstree, after_open.jstree 1729 */ 1730 open_node : function (obj, callback, animation) { 1731 var t1, t2, d, t; 1732 if($.isArray(obj)) { 1733 obj = obj.slice(); 1734 for(t1 = 0, t2 = obj.length; t1 < t2; t1++) { 1735 this.open_node(obj[t1], callback, animation); 1736 } 1737 return true; 1738 } 1739 obj = this.get_node(obj); 1740 if(!obj || obj.id === '#') { 1741 return false; 1742 } 1743 animation = animation === undefined ? this.settings.core.animation : animation; 1744 if(!this.is_closed(obj)) { 1745 if(callback) { 1746 callback.call(this, obj, false); 1747 } 1748 return false; 1749 } 1750 if(!this.is_loaded(obj)) { 1751 if(this.is_loading(obj)) { 1752 return setTimeout($.proxy(function () { 1753 this.open_node(obj, callback, animation); 1754 }, this), 500); 1755 } 1756 this.load_node(obj, function (o, ok) { 1757 return ok ? this.open_node(o, callback, animation) : (callback ? callback.call(this, o, false) : false); 1758 }); 1759 } 1760 else { 1761 d = this.get_node(obj, true); 1762 t = this; 1763 if(d.length) { 1764 if(obj.children.length && !this._firstChild(d.children('ul')[0])) { 1765 obj.state.opened = true; 1766 this.redraw_node(obj, true); 1767 d = this.get_node(obj, true); 1768 } 1769 if(!animation) { 1770 d[0].className = d[0].className.replace('jstree-closed', 'jstree-open'); 1771 d[0].setAttribute("aria-expanded", true); 1772 } 1773 else { 1774 d 1775 .children("ul").css("display","none").end() 1776 .removeClass("jstree-closed").addClass("jstree-open").attr("aria-expanded", true) 1777 .children("ul").stop(true, true) 1778 .slideDown(animation, function () { 1779 this.style.display = ""; 1780 t.trigger("after_open", { "node" : obj }); 1781 }); 1782 } 1783 } 1784 obj.state.opened = true; 1785 if(callback) { 1786 callback.call(this, obj, true); 1787 } 1788 /** 1789 * triggered when a node is opened (if there is an animation it will not be completed yet) 1790 * @event 1791 * @name open_node.jstree 1792 * @param {Object} node the opened node 1793 */ 1794 this.trigger('open_node', { "node" : obj }); 1795 if(!animation || !d.length) { 1796 /** 1797 * triggered when a node is opened and the animation is complete 1798 * @event 1799 * @name after_open.jstree 1800 * @param {Object} node the opened node 1801 */ 1802 this.trigger("after_open", { "node" : obj }); 1803 } 1804 } 1805 }, 1806 /** 1807 * opens every parent of a node (node should be loaded) 1808 * @name _open_to(obj) 1809 * @param {mixed} obj the node to reveal 1810 * @private 1811 */ 1812 _open_to : function (obj) { 1813 obj = this.get_node(obj); 1814 if(!obj || obj.id === '#') { 1815 return false; 1816 } 1817 var i, j, p = obj.parents; 1818 for(i = 0, j = p.length; i < j; i+=1) { 1819 if(i !== '#') { 1820 this.open_node(p[i], false, 0); 1821 } 1822 } 1823 return $(document.getElementById(obj.id)); 1824 }, 1825 /** 1826 * closes a node, hiding its children 1827 * @name close_node(obj [, animation]) 1828 * @param {mixed} obj the node to close 1829 * @param {Number} animation the animation duration in milliseconds when closing the node (overrides the `core.animation` setting). Use `false` for no animation. 1830 * @trigger close_node.jstree, after_close.jstree 1831 */ 1832 close_node : function (obj, animation) { 1833 var t1, t2, t, d; 1834 if($.isArray(obj)) { 1835 obj = obj.slice(); 1836 for(t1 = 0, t2 = obj.length; t1 < t2; t1++) { 1837 this.close_node(obj[t1], animation); 1838 } 1839 return true; 1840 } 1841 obj = this.get_node(obj); 1842 if(!obj || obj.id === '#') { 1843 return false; 1844 } 1845 animation = animation === undefined ? this.settings.core.animation : animation; 1846 t = this; 1847 d = this.get_node(obj, true); 1848 if(d.length) { 1849 if(!animation) { 1850 d[0].className = d[0].className.replace('jstree-open', 'jstree-closed'); 1851 d.attr("aria-expanded", false).children('ul').remove(); 1852 } 1853 else { 1854 d 1855 .children("ul").attr("style","display:block !important").end() 1856 .removeClass("jstree-open").addClass("jstree-closed").attr("aria-expanded", false)
vendor: 14,300 bytes, lines 1857-2280
1857 .children("ul").stop(true, true).slideUp(animation, function () { 1858 this.style.display = ""; 1859 d.children('ul').remove(); 1860 t.trigger("after_close", { "node" : obj }); 1861 }); 1862 } 1863 } 1864 obj.state.opened = false; 1865 /** 1866 * triggered when a node is closed (if there is an animation it will not be complete yet) 1867 * @event 1868 * @name close_node.jstree 1869 * @param {Object} node the closed node 1870 */ 1871 this.trigger('close_node',{ "node" : obj }); 1872 if(!animation || !d.length) { 1873 /** 1874 * triggered when a node is closed and the animation is complete 1875 * @event 1876 * @name after_close.jstree 1877 * @param {Object} node the closed node 1878 */ 1879 this.trigger("after_close", { "node" : obj }); 1880 } 1881 }, 1882 /** 1883 * toggles a node - closing it if it is open, opening it if it is closed 1884 * @name toggle_node(obj) 1885 * @param {mixed} obj the node to toggle 1886 */ 1887 toggle_node : function (obj) { 1888 var t1, t2; 1889 if($.isArray(obj)) { 1890 obj = obj.slice(); 1891 for(t1 = 0, t2 = obj.length; t1 < t2; t1++) { 1892 this.toggle_node(obj[t1]); 1893 } 1894 return true; 1895 } 1896 if(this.is_closed(obj)) { 1897 return this.open_node(obj); 1898 } 1899 if(this.is_open(obj)) { 1900 return this.close_node(obj); 1901 } 1902 }, 1903 /** 1904 * 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. 1905 * @name open_all([obj, animation, original_obj]) 1906 * @param {mixed} obj the node to open recursively, omit to open all nodes in the tree 1907 * @param {Number} animation the animation duration in milliseconds when opening the nodes, the default is no animation 1908 * @param {jQuery} reference to the node that started the process (internal use) 1909 * @trigger open_all.jstree 1910 */ 1911 open_all : function (obj, animation, original_obj) { 1912 if(!obj) { obj = '#'; } 1913 obj = this.get_node(obj); 1914 if(!obj) { return false; } 1915 var dom = obj.id === '#' ? this.get_container_ul() : this.get_node(obj, true), i, j, _this; 1916 if(!dom.length) { 1917 for(i = 0, j = obj.children_d.length; i < j; i++) { 1918 if(this.is_closed(this._model.data[obj.children_d[i]])) { 1919 this._model.data[obj.children_d[i]].state.opened = true; 1920 } 1921 } 1922 return this.trigger('open_all', { "node" : obj }); 1923 } 1924 original_obj = original_obj || dom; 1925 _this = this; 1926 dom = this.is_closed(obj) ? dom.find('li.jstree-closed').addBack() : dom.find('li.jstree-closed'); 1927 dom.each(function () { 1928 _this.open_node( 1929 this, 1930 function(node, status) { if(status && this.is_parent(node)) { this.open_all(node, animation, original_obj); } }, 1931 animation || 0 1932 ); 1933 }); 1934 if(original_obj.find('li.jstree-closed').length === 0) { 1935 /** 1936 * triggered when an `open_all` call completes 1937 * @event 1938 * @name open_all.jstree 1939 * @param {Object} node the opened node 1940 */ 1941 this.trigger('open_all', { "node" : this.get_node(original_obj) }); 1942 } 1943 }, 1944 /** 1945 * closes all nodes within a node (or the tree), revaling their children 1946 * @name close_all([obj, animation]) 1947 * @param {mixed} obj the node to close recursively, omit to close all nodes in the tree 1948 * @param {Number} animation the animation duration in milliseconds when closing the nodes, the default is no animation 1949 * @trigger close_all.jstree 1950 */ 1951 close_all : function (obj, animation) { 1952 if(!obj) { obj = '#'; } 1953 obj = this.get_node(obj); 1954 if(!obj) { return false; } 1955 var dom = obj.id === '#' ? this.get_container_ul() : this.get_node(obj, true), 1956 _this = this, i, j; 1957 if(!dom.length) { 1958 for(i = 0, j = obj.children_d.length; i < j; i++) { 1959 this._model.data[obj.children_d[i]].state.opened = false; 1960 } 1961 return this.trigger('close_all', { "node" : obj }); 1962 } 1963 dom = this.is_open(obj) ? dom.find('li.jstree-open').addBack() : dom.find('li.jstree-open'); 1964 dom.vakata_reverse().each(function () { _this.close_node(this, animation || 0); }); 1965 /** 1966 * triggered when an `close_all` call completes 1967 * @event 1968 * @name close_all.jstree 1969 * @param {Object} node the closed node 1970 */ 1971 this.trigger('close_all', { "node" : obj }); 1972 }, 1973 /** 1974 * checks if a node is disabled (not selectable) 1975 * @name is_disabled(obj) 1976 * @param {mixed} obj 1977 * @return {Boolean} 1978 */ 1979 is_disabled : function (obj) { 1980 obj = this.get_node(obj); 1981 return obj && obj.state && obj.state.disabled; 1982 }, 1983 /** 1984 * enables a node - so that it can be selected 1985 * @name enable_node(obj) 1986 * @param {mixed} obj the node to enable 1987 * @trigger enable_node.jstree 1988 */ 1989 enable_node : function (obj) { 1990 var t1, t2; 1991 if($.isArray(obj)) { 1992 obj = obj.slice(); 1993 for(t1 = 0, t2 = obj.length; t1 < t2; t1++) { 1994 this.enable_node(obj[t1]); 1995 } 1996 return true; 1997 } 1998 obj = this.get_node(obj); 1999 if(!obj || obj.id === '#') { 2000 return false; 2001 } 2002 obj.state.disabled = false; 2003 this.get_node(obj,true).children('.jstree-anchor').removeClass('jstree-disabled'); 2004 /** 2005 * triggered when an node is enabled 2006 * @event 2007 * @name enable_node.jstree 2008 * @param {Object} node the enabled node 2009 */ 2010 this.trigger('enable_node', { 'node' : obj }); 2011 }, 2012 /** 2013 * disables a node - so that it can not be selected 2014 * @name disable_node(obj) 2015 * @param {mixed} obj the node to disable 2016 * @trigger disable_node.jstree 2017 */ 2018 disable_node : function (obj) { 2019 var t1, t2; 2020 if($.isArray(obj)) { 2021 obj = obj.slice(); 2022 for(t1 = 0, t2 = obj.length; t1 < t2; t1++) { 2023 this.disable_node(obj[t1]); 2024 } 2025 return true; 2026 } 2027 obj = this.get_node(obj); 2028 if(!obj || obj.id === '#') { 2029 return false; 2030 } 2031 obj.state.disabled = true; 2032 this.get_node(obj,true).children('.jstree-anchor').addClass('jstree-disabled'); 2033 /** 2034 * triggered when an node is disabled 2035 * @event 2036 * @name disable_node.jstree 2037 * @param {Object} node the disabled node 2038 */ 2039 this.trigger('disable_node', { 'node' : obj }); 2040 }, 2041 /** 2042 * called when a node is selected by the user. Used internally. 2043 * @private 2044 * @name activate_node(obj, e) 2045 * @param {mixed} obj the node 2046 * @param {Object} e the related event 2047 * @trigger activate_node.jstree 2048 */ 2049 activate_node : function (obj, e) { 2050 if(this.is_disabled(obj)) { 2051 return false; 2052 } 2053 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 ) )) { 2054 if(!this.settings.core.multiple && (e.metaKey || e.ctrlKey || e.shiftKey) && this.is_selected(obj)) { 2055 this.deselect_node(obj, false, false, e); 2056 } 2057 else { 2058 this.deselect_all(true); 2059 this.select_node(obj, false, false, e); 2060 this._data.core.last_clicked = this.get_node(obj); 2061 } 2062 } 2063 else { 2064 if(e.shiftKey) { 2065 var o = this.get_node(obj).id, 2066 l = this._data.core.last_clicked.id, 2067 p = this.get_node(this._data.core.last_clicked.parent).children, 2068 c = false, 2069 i, j; 2070 for(i = 0, j = p.length; i < j; i += 1) { 2071 // separate IFs work whem o and l are the same 2072 if(p[i] === o) { 2073 c = !c; 2074 } 2075 if(p[i] === l) { 2076 c = !c; 2077 } 2078 if(c || p[i] === o || p[i] === l) { 2079 this.select_node(p[i], false, false, e); 2080 } 2081 else { 2082 this.deselect_node(p[i], false, false, e); 2083 } 2084 } 2085 } 2086 else { 2087 if(!this.is_selected(obj)) { 2088 this.select_node(obj, false, false, e); 2089 } 2090 else { 2091 this.deselect_node(obj, false, false, e); 2092 } 2093 } 2094 } 2095 /** 2096 * triggered when an node is clicked or intercated with by the user 2097 * @event 2098 * @name activate_node.jstree 2099 * @param {Object} node 2100 */ 2101 this.trigger('activate_node', { 'node' : this.get_node(obj) }); 2102 }, 2103 /** 2104 * applies the hover state on a node, called when a node is hovered by the user. Used internally. 2105 * @private 2106 * @name hover_node(obj) 2107 * @param {mixed} obj 2108 * @trigger hover_node.jstree 2109 */ 2110 hover_node : function (obj) { 2111 obj = this.get_node(obj, true); 2112 if(!obj || !obj.length || obj.children('.jstree-hovered').length) { 2113 return false; 2114 } 2115 var o = this.element.find('.jstree-hovered'), t = this.element; 2116 if(o && o.length) { this.dehover_node(o); } 2117 2118 obj.children('.jstree-anchor').addClass('jstree-hovered'); 2119 /** 2120 * triggered when an node is hovered 2121 * @event 2122 * @name hover_node.jstree 2123 * @param {Object} node 2124 */ 2125 this.trigger('hover_node', { 'node' : this.get_node(obj) }); 2126 setTimeout(function () { t.attr('aria-activedescendant', obj[0].id); obj.attr('aria-selected', true); }, 0); 2127 }, 2128 /** 2129 * removes the hover state from a nodecalled when a node is no longer hovered by the user. Used internally. 2130 * @private 2131 * @name dehover_node(obj) 2132 * @param {mixed} obj 2133 * @trigger dehover_node.jstree 2134 */ 2135 dehover_node : function (obj) { 2136 obj = this.get_node(obj, true); 2137 if(!obj || !obj.length || !obj.children('.jstree-hovered').length) { 2138 return false; 2139 } 2140 obj.attr('aria-selected', false).children('.jstree-anchor').removeClass('jstree-hovered'); 2141 /** 2142 * triggered when an node is no longer hovered 2143 * @event 2144 * @name dehover_node.jstree 2145 * @param {Object} node 2146 */ 2147 this.trigger('dehover_node', { 'node' : this.get_node(obj) }); 2148 }, 2149 /** 2150 * select a node 2151 * @name select_node(obj [, supress_event, prevent_open]) 2152 * @param {mixed} obj an array can be used to select multiple nodes 2153 * @param {Boolean} supress_event if set to `true` the `changed.jstree` event won't be triggered 2154 * @param {Boolean} prevent_open if set to `true` parents of the selected node won't be opened 2155 * @trigger select_node.jstree, changed.jstree 2156 */ 2157 select_node : function (obj, supress_event, prevent_open, e) { 2158 var dom, t1, t2, th; 2159 if($.isArray(obj)) { 2160 obj = obj.slice(); 2161 for(t1 = 0, t2 = obj.length; t1 < t2; t1++) { 2162 this.select_node(obj[t1], supress_event, prevent_open, e); 2163 } 2164 return true; 2165 } 2166 obj = this.get_node(obj); 2167 if(!obj || obj.id === '#') { 2168 return false; 2169 } 2170 dom = this.get_node(obj, true); 2171 if(!obj.state.selected) { 2172 obj.state.selected = true; 2173 this._data.core.selected.push(obj.id); 2174 if(!prevent_open) { 2175 dom = this._open_to(obj); 2176 } 2177 if(dom && dom.length) { 2178 dom.children('.jstree-anchor').addClass('jstree-clicked'); 2179 } 2180 /** 2181 * triggered when an node is selected 2182 * @event 2183 * @name select_node.jstree 2184 * @param {Object} node 2185 * @param {Array} selected the current selection 2186 * @param {Object} event the event (if any) that triggered this select_node 2187 */ 2188 this.trigger('select_node', { 'node' : obj, 'selected' : this._data.core.selected, 'event' : e }); 2189 if(!supress_event) { 2190 /** 2191 * triggered when selection changes 2192 * @event 2193 * @name changed.jstree 2194 * @param {Object} node 2195 * @param {Object} action the action that caused the selection to change 2196 * @param {Array} selected the current selection 2197 * @param {Object} event the event (if any) that triggered this changed event 2198 */ 2199 this.trigger('changed', { 'action' : 'select_node', 'node' : obj, 'selected' : this._data.core.selected, 'event' : e }); 2200 } 2201 } 2202 }, 2203 /** 2204 * deselect a node 2205 * @name deselect_node(obj [, supress_event]) 2206 * @param {mixed} obj an array can be used to deselect multiple nodes 2207 * @param {Boolean} supress_event if set to `true` the `changed.jstree` event won't be triggered 2208 * @trigger deselect_node.jstree, changed.jstree 2209 */ 2210 deselect_node : function (obj, supress_event, e) { 2211 var t1, t2, dom; 2212 if($.isArray(obj)) { 2213 obj = obj.slice(); 2214 for(t1 = 0, t2 = obj.length; t1 < t2; t1++) { 2215 this.deselect_node(obj[t1], supress_event, e); 2216 } 2217 return true; 2218 } 2219 obj = this.get_node(obj); 2220 if(!obj || obj.id === '#') { 2221 return false; 2222 } 2223 dom = this.get_node(obj, true); 2224 if(obj.state.selected) { 2225 obj.state.selected = false; 2226 this._data.core.selected = $.vakata.array_remove_item(this._data.core.selected, obj.id); 2227 if(dom.length) { 2228 dom.children('.jstree-anchor').removeClass('jstree-clicked'); 2229 } 2230 /** 2231 * triggered when an node is deselected 2232 * @event 2233 * @name deselect_node.jstree 2234 * @param {Object} node 2235 * @param {Array} selected the current selection 2236 * @param {Object} event the event (if any) that triggered this deselect_node 2237 */ 2238 this.trigger('deselect_node', { 'node' : obj, 'selected' : this._data.core.selected, 'event' : e }); 2239 if(!supress_event) { 2240 this.trigger('changed', { 'action' : 'deselect_node', 'node' : obj, 'selected' : this._data.core.selected, 'event' : e }); 2241 } 2242 } 2243 }, 2244 /** 2245 * select all nodes in the tree 2246 * @name select_all([supress_event]) 2247 * @param {Boolean} supress_event if set to `true` the `changed.jstree` event won't be triggered 2248 * @trigger select_all.jstree, changed.jstree 2249 */ 2250 select_all : function (supress_event) { 2251 var tmp = this._data.core.selected.concat([]), i, j; 2252 this._data.core.selected = this._model.data['#'].children_d.concat(); 2253 for(i = 0, j = this._data.core.selected.length; i < j; i++) { 2254 if(this._model.data[this._data.core.selected[i]]) { 2255 this._model.data[this._data.core.selected[i]].state.selected = true; 2256 } 2257 } 2258 this.redraw(true); 2259 /** 2260 * triggered when all nodes are selected 2261 * @event 2262 * @name select_all.jstree 2263 * @param {Array} selected the current selection 2264 */ 2265 this.trigger('select_all', { 'selected' : this._data.core.selected }); 2266 if(!supress_event) { 2267 this.trigger('changed', { 'action' : 'select_all', 'selected' : this._data.core.selected, 'old_selection' : tmp }); 2268 } 2269 }, 2270 /** 2271 * deselect all selected nodes 2272 * @name deselect_all([supress_event]) 2273 * @param {Boolean} supress_event if set to `true` the `changed.jstree` event won't be triggered 2274 * @trigger deselect_all.jstree, changed.jstree 2275 */ 2276 deselect_all : function (supress_event) { 2277 var tmp = this._data.core.selected.concat([]), i, j; 2278 for(i = 0, j = this._data.core.selected.length; i < j; i++) { 2279 if(this._model.data[this._data.core.selected[i]]) { 2280 this._model.data[this._data.core.selected[i]].state.selected = false;
2281 } 2282 } 2283 this._data.core.selected = []; 2284 this.element.find('.jstree-clicked').removeClass('jstree-clicked'); 2285 /** 2286 * triggered when all nodes are deselected 2287 * @event 2288 * @name deselect_all.jstree 2289 * @param {Object} node the previous selection 2290 * @param {Array} selected the current selection 2291 */ 2292 this.trigger('deselect_all', { 'selected' : this._data.core.selected, 'node' : tmp }); 2293 if(!supress_event) { 2294 this.trigger('changed', { 'action' : 'deselect_all', 'selected' : this._data.core.selected, 'old_selection' : tmp }); 2295 } 2296 }, 2297 /** 2298 * checks if a node is selected 2299 * @name is_selected(obj) 2300 * @param {mixed} obj 2301 * @return {Boolean} 2302 */ 2303 is_selected : function (obj) { 2304 obj = this.get_node(obj); 2305 if(!obj || obj.id === '#') { 2306 return false; 2307 } 2308 return obj.state.selected; 2309 }, 2310 /** 2311 * get an array of all selected node IDs 2312 * @name get_selected([full]) 2313 * @param {mixed} full if set to `true` the returned array will consist of the full node objects, otherwise - only IDs will be returned 2314 * @return {Array} 2315 */ 2316 get_selected : function (full) { 2317 return full ? $.map(this._data.core.selected, $.proxy(function (i) { return this.get_node(i); }, this)) : this._data.core.selected; 2318 }, 2319 /** 2320 * gets the current state of the tree so that it can be restored later with `set_state(state)`. Used internally. 2321 * @name get_state() 2322 * @private 2323 * @return {Object} 2324 */ 2325 get_state : function () { 2326 var state = { 2327 'core' : { 2328 'open' : [], 2329 'scroll' : { 2330 'left' : this.element.scrollLeft(), 2331 'top' : this.element.scrollTop() 2332 }, 2333 /*! 2334 'themes' : { 2335 'name' : this.get_theme(), 2336 'icons' : this._data.core.themes.icons, 2337 'dots' : this._data.core.themes.dots 2338 }, 2339 */ 2340 'selected' : [] 2341 } 2342 }, i; 2343 for(i in this._model.data) { 2344 if(this._model.data.hasOwnProperty(i)) { 2345 if(i !== '#') { 2346 if(this._model.data[i].state.opened) { 2347 state.core.open.push(i); 2348 } 2349 if(this._model.data[i].state.selected) { 2350 state.core.selected.push(i); 2351 } 2352 } 2353 } 2354 } 2355 return state; 2356 }, 2357 /** 2358 * sets the state of the tree. Used internally. 2359 * @name set_state(state [, callback]) 2360 * @private 2361 * @param {Object} state the state to restore 2362 * @param {Function} callback an optional function to execute once the state is restored. 2363 * @trigger set_state.jstree 2364 */ 2365 set_state : function (state, callback) { 2366 if(state) { 2367 if(state.core) { 2368 var res, n, t, _this; 2369 if(state.core.open) { 2370 if(!$.isArray(state.core.open)) { 2371 delete state.core.open; 2372 this.set_state(state, callback); 2373 return false; 2374 } 2375 res = true; 2376 n = false; 2377 t = this; 2378 $.each(state.core.open.concat([]), function (i, v) { 2379 n = t.get_node(v); 2380 if(n) { 2381 if(t.is_loaded(v)) { 2382 if(t.is_closed(v)) { 2383 t.open_node(v, false, 0); 2384 } 2385 if(state && state.core && state.core.open) { 2386 $.vakata.array_remove_item(state.core.open, v); 2387 } 2388 } 2389 else { 2390 if(!t.is_loading(v)) { 2391 t.open_node(v, $.proxy(function () { this.set_state(state, callback); }, t), 0); 2392 } 2393 // there will be some async activity - so wait for it 2394 res = false; 2395 } 2396 } 2397 }); 2398 if(res) { 2399 delete state.core.open; 2400 this.set_state(state, callback); 2401 } 2402 return false; 2403 } 2404 if(state.core.scroll) { 2405 if(state.core.scroll && state.core.scroll.left !== undefined) { 2406 this.element.scrollLeft(state.core.scroll.left); 2407 } 2408 if(state.core.scroll && state.core.scroll.top !== undefined) { 2409 this.element.scrollTop(state.core.scroll.top); 2410 } 2411 delete state.core.scroll; 2412 this.set_state(state, callback); 2413 return false; 2414 } 2415 /*! 2416 if(state.core.themes) { 2417 if(state.core.themes.name) { 2418 this.set_theme(state.core.themes.name); 2419 } 2420 if(typeof state.core.themes.dots !== 'undefined') { 2421 this[ state.core.themes.dots ? "show_dots" : "hide_dots" ](); 2422 } 2423 if(typeof state.core.themes.icons !== 'undefined') { 2424 this[ state.core.themes.icons ? "show_icons" : "hide_icons" ](); 2425 } 2426 delete state.core.themes; 2427 delete state.core.open; 2428 this.set_state(state, callback); 2429 return false;
2430 } 2431 */ 2432 if(state.core.selected) { 2433 _this = this; 2434 this.deselect_all(); 2435 $.each(state.core.selected, function (i, v) { 2436 _this.select_node(v); 2437 }); 2438 delete state.core.selected; 2439 this.set_state(state, callback); 2440 return false; 2441 } 2442 if($.isEmptyObject(state.core)) { 2443 delete state.core; 2444 this.set_state(state, callback); 2445 return false; 2446 } 2447 } 2448 if($.isEmptyObject(state)) { 2449 state = null; 2450 if(callback) { callback.call(this); } 2451 /** 2452 * triggered when a `set_state` call completes 2453 * @event 2454 * @name set_state.jstree 2455 */ 2456 this.trigger('set_state'); 2457 return false; 2458 } 2459 return true; 2460 } 2461 return false; 2462 }, 2463 /** 2464 * refreshes the tree - all nodes are reloaded with calls to `load_node`. 2465 * @name refresh() 2466 * @param {Boolean} skip_loading an option to skip showing the loading indicator 2467 * @trigger refresh.jstree 2468 */ 2469 refresh : function (skip_loading) { 2470 this._data.core.state = this.get_state(); 2471 this._cnt = 0; 2472 this._model.data = { 2473 '#' : { 2474 id : '#', 2475 parent : null, 2476 parents : [], 2477 children : [], 2478 children_d : [], 2479 state : { loaded : false } 2480 } 2481 }; 2482 var c = this.get_container_ul()[0].className; 2483 if(!skip_loading) { 2484 this.element.html("<"+"ul class='jstree-container-ul'><"+"li class='jstree-initial-node jstree-loading jstree-leaf jstree-last'><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>"); 2485 } 2486 this.load_node('#', function (o, s) { 2487 if(s) { 2488 this.get_container_ul()[0].className = c; 2489 this.set_state($.extend(true, {}, this._data.core.state), function () { 2490 /** 2491 * triggered when a `refresh` call completes 2492 * @event 2493 * @name refresh.jstree 2494 */ 2495 this.trigger('refresh'); 2496 }); 2497 } 2498 this._data.core.state = null; 2499 }); 2500 }, 2501 /** 2502 * set (change) the ID of a node 2503 * @name set_id(obj, id) 2504 * @param {mixed} obj the node 2505 * @param {String} id the new ID 2506 * @return {Boolean} 2507 */ 2508 set_id : function (obj, id) { 2509 obj = this.get_node(obj); 2510 if(!obj || obj.id === '#') { return false; } 2511 var i, j, m = this._model.data; 2512 // update parents (replace current ID with new one in children and children_d) 2513 m[obj.parent].children[$.inArray(obj.id, m[obj.parent].children)] = id; 2514 for(i = 0, j = obj.parents.length; i < j; i++) { 2515 m[obj.parents[i]].children_d[$.inArray(obj.id, m[obj.parents[i]].children_d)] = id; 2516 } 2517 // update children (replace current ID with new one in parent and parents) 2518 for(i = 0, j = obj.children.length; i < j; i++) { 2519 m[obj.children[i]].parent = id; 2520 } 2521 for(i = 0, j = obj.children_d.length; i < j; i++) { 2522 m[obj.children_d[i]].parents[$.inArray(obj.id, m[obj.children_d[i]].parents)] = id; 2523 } 2524 i = $.inArray(obj.id, this._data.core.selected); 2525 if(i !== -1) { this._data.core.selected[i] = id; } 2526 // update model and obj itself (obj.id, this._model.data[KEY]) 2527 i = this.get_node(obj.id, true); 2528 if(i) { 2529 i.attr('id', id); 2530 } 2531 delete m[obj.id]; 2532 obj.id = id; 2533 m[id] = obj; 2534 return true; 2535 }, 2536 /** 2537 * get the text value of a node 2538 * @name get_text(obj) 2539 * @param {mixed} obj the node 2540 * @return {String} 2541 */ 2542 get_text : function (obj) { 2543 obj = this.get_node(obj); 2544 return (!obj || obj.id === '#') ? false : obj.text; 2545 }, 2546 /** 2547 * set the text value of a node. Used internally, please use `rename_node(obj, val)`. 2548 * @private 2549 * @name set_text(obj, val) 2550 * @param {mixed} obj the node, you can pass an array to set the text on multiple nodes 2551 * @param {String} val the new text value 2552 * @return {Boolean} 2553 * @trigger set_text.jstree 2554 */ 2555 set_text : function (obj, val) { 2556 var t1, t2, dom, tmp; 2557 if($.isArray(obj)) { 2558 obj = obj.slice(); 2559 for(t1 = 0, t2 = obj.length; t1 < t2; t1++) { 2560 this.set_text(obj[t1], val); 2561 } 2562 return true; 2563 } 2564 obj = this.get_node(obj); 2565 if(!obj || obj.id === '#') { return false; } 2566 obj.text = val; 2567 dom = this.get_node(obj, true); 2568 if(dom.length) { 2569 dom = dom.children(".jstree-anchor:eq(0)"); 2570 tmp = dom.children("I").clone(); 2571 dom.html(val).prepend(tmp); 2572 /** 2573 * triggered when a node text value is changed 2574 * @event 2575 * @name set_text.jstree 2576 * @param {Object} obj 2577 * @param {String} text the new value 2578 */ 2579 this.trigger('set_text',{ "obj" : obj, "text" : val }); 2580 } 2581 return true; 2582 }, 2583 /** 2584 * gets a JSON representation of a node (or the whole tree) 2585 * @name get_json([obj, options]) 2586 * @param {mixed} obj 2587 * @param {Object} options 2588 * @param {Boolean} options.no_state do not return state information 2589 * @param {Boolean} options.no_id do not return ID 2590 * @param {Boolean} options.no_children do not include children 2591 * @param {Boolean}
vendor: 21,580 bytes, lines 2591-3177
2591 options.no_data do not include node data 2592 * @param {Boolean} options.flat return flat JSON instead of nested 2593 * @return {Object} 2594 */ 2595 get_json : function (obj, options, flat) { 2596 obj = this.get_node(obj || '#'); 2597 if(!obj) { return false; } 2598 if(options && options.flat && !flat) { flat = []; } 2599 var tmp = { 2600 'id' : obj.id, 2601 'text' : obj.text, 2602 'icon' : this.get_icon(obj), 2603 'li_attr' : obj.li_attr, 2604 'a_attr' : obj.a_attr, 2605 'state' : {}, 2606 'data' : options && options.no_data ? false : obj.data 2607 //( this.get_node(obj, true).length ? this.get_node(obj, true).data() : obj.data ), 2608 }, i, j; 2609 if(options && options.flat) { 2610 tmp.parent = obj.parent; 2611 } 2612 else { 2613 tmp.children = []; 2614 } 2615 if(!options || !options.no_state) { 2616 for(i in obj.state) { 2617 if(obj.state.hasOwnProperty(i)) { 2618 tmp.state[i] = obj.state[i]; 2619 } 2620 } 2621 } 2622 if(options && options.no_id) { 2623 delete tmp.id; 2624 if(tmp.li_attr && tmp.li_attr.id) { 2625 delete tmp.li_attr.id; 2626 } 2627 } 2628 if(options && options.flat && obj.id !== '#') { 2629 flat.push(tmp); 2630 } 2631 if(!options || !options.no_children) { 2632 for(i = 0, j = obj.children.length; i < j; i++) { 2633 if(options && options.flat) { 2634 this.get_json(obj.children[i], options, flat); 2635 } 2636 else { 2637 tmp.children.push(this.get_json(obj.children[i], options)); 2638 } 2639 } 2640 } 2641 return options && options.flat ? flat : (obj.id === '#' ? tmp.children : tmp); 2642 }, 2643 /** 2644 * create a new node (do not confuse with load_node) 2645 * @name create_node([obj, node, pos, callback, is_loaded]) 2646 * @param {mixed} par the parent node 2647 * @param {mixed} node the data for the new node (a valid JSON object, or a simple string with the name) 2648 * @param {mixed} pos the index at which to insert the node, "first" and "last" are also supported, default is "last" 2649 * @param {Function} callback a function to be called once the node is created 2650 * @param {Boolean} is_loaded internal argument indicating if the parent node was succesfully loaded 2651 * @return {String} the ID of the newly create node 2652 * @trigger model.jstree, create_node.jstree 2653 */ 2654 create_node : function (par, node, pos, callback, is_loaded) { 2655 par = this.get_node(par); 2656 if(!par) { return false; } 2657 pos = pos === undefined ? "last" : pos; 2658 if(!pos.toString().match(/^(before|after)$/) && !is_loaded && !this.is_loaded(par)) { 2659 return this.load_node(par, function () { this.create_node(par, node, pos, callback, true); }); 2660 } 2661 if(!node) { node = { "text" : this.get_string('New node') }; } 2662 if(node.text === undefined) { node.text = this.get_string('New node'); } 2663 var tmp, dpc, i, j; 2664 2665 if(par.id === '#') { 2666 if(pos === "before") { pos = "first"; } 2667 if(pos === "after") { pos = "last"; } 2668 } 2669 switch(pos) { 2670 case "before": 2671 tmp = this.get_node(par.parent); 2672 pos = $.inArray(par.id, tmp.children); 2673 par = tmp; 2674 break; 2675 case "after" : 2676 tmp = this.get_node(par.parent); 2677 pos = $.inArray(par.id, tmp.children) + 1; 2678 par = tmp; 2679 break; 2680 case "inside": 2681 case "first": 2682 pos = 0; 2683 break; 2684 case "last": 2685 pos = par.children.length; 2686 break; 2687 default: 2688 if(!pos) { pos = 0; } 2689 break; 2690 } 2691 if(pos > par.children.length) { pos = par.children.length; } 2692 if(!node.id) { node.id = true; } 2693 if(!this.check("create_node", node, par, pos)) { 2694 this.settings.core.error.call(this, this._data.core.last_error); 2695 return false; 2696 } 2697 if(node.id === true) { delete node.id; } 2698 node = this._parse_model_from_json(node, par.id, par.parents.concat()); 2699 if(!node) { return false; } 2700 tmp = this.get_node(node); 2701 dpc = []; 2702 dpc.push(node); 2703 dpc = dpc.concat(tmp.children_d); 2704 this.trigger('model', { "nodes" : dpc, "parent" : par.id }); 2705 2706 par.children_d = par.children_d.concat(dpc); 2707 for(i = 0, j = par.parents.length; i < j; i++) { 2708 this._model.data[par.parents[i]].children_d = this._model.data[par.parents[i]].children_d.concat(dpc); 2709 } 2710 node = tmp; 2711 tmp = []; 2712 for(i = 0, j = par.children.length; i < j; i++) { 2713 tmp[i >= pos ? i+1 : i] = par.children[i]; 2714 } 2715 tmp[pos] = node.id; 2716 par.children = tmp; 2717 2718 this.redraw_node(par, true); 2719 if(callback) { callback.call(this, this.get_node(node)); } 2720 /** 2721 * triggered when a node is created 2722 * @event 2723 * @name create_node.jstree 2724 * @param {Object} node 2725 * @param {String} parent the parent's ID 2726 * @param {Number} position the position of the new node among the parent's children 2727 */ 2728 this.trigger('create_node', { "node" : this.get_node(node), "parent" : par.id, "position" : pos }); 2729 return node.id; 2730 }, 2731 /** 2732 * set the text value of a node 2733 * @name rename_node(obj, val) 2734 * @param {mixed} obj the node, you can pass an array to rename multiple nodes to the same name 2735 * @param {String} val the new text value 2736 * @return {Boolean} 2737 * @trigger rename_node.jstree 2738 */ 2739 rename_node : function (obj, val) { 2740 var t1, t2, old; 2741 if($.isArray(obj)) { 2742 obj = obj.slice(); 2743 for(t1 = 0, t2 = obj.length; t1 < t2; t1++) { 2744 this.rename_node(obj[t1], val); 2745 } 2746 return true; 2747 } 2748 obj = this.get_node(obj); 2749 if(!obj || obj.id === '#') { return false; } 2750 old = obj.text; 2751 if(!this.check("rename_node", obj, this.get_parent(obj), val)) { 2752 this.settings.core.error.call(this, this._data.core.last_error); 2753 return false; 2754 } 2755 this.set_text(obj, val); // .apply(this, Array.prototype.slice.call(arguments)) 2756 /** 2757 * triggered when a node is renamed 2758 * @event 2759 * @name rename_node.jstree 2760 * @param {Object} node 2761 * @param {String} text the new value 2762 * @param {String} old the old value 2763 */ 2764 this.trigger('rename_node', { "node" : obj, "text" : val, "old" : old }); 2765 return true; 2766 }, 2767 /** 2768 * remove a node 2769 * @name delete_node(obj) 2770 * @param {mixed} obj the node, you can pass an array to delete multiple nodes 2771 * @return {Boolean} 2772 * @trigger delete_node.jstree, changed.jstree 2773 */ 2774 delete_node : function (obj) { 2775 var t1, t2, par, pos, tmp, i, j, k, l, c; 2776 if($.isArray(obj)) { 2777 obj = obj.slice(); 2778 for(t1 = 0, t2 = obj.length; t1 < t2; t1++) { 2779 this.delete_node(obj[t1]); 2780 } 2781 return true; 2782 } 2783 obj = this.get_node(obj); 2784 if(!obj || obj.id === '#') { return false; } 2785 par = this.get_node(obj.parent); 2786 pos = $.inArray(obj.id, par.children); 2787 c = false; 2788 if(!this.check("delete_node", obj, par, pos)) { 2789 this.settings.core.error.call(this, this._data.core.last_error); 2790 return false; 2791 } 2792 if(pos !== -1) { 2793 par.children = $.vakata.array_remove(par.children, pos); 2794 } 2795 tmp = obj.children_d.concat([]); 2796 tmp.push(obj.id); 2797 for(k = 0, l = tmp.length; k < l; k++) { 2798 for(i = 0, j = obj.parents.length; i < j; i++) { 2799 pos = $.inArray(tmp[k], this._model.data[obj.parents[i]].children_d); 2800 if(pos !== -1) { 2801 this._model.data[obj.parents[i]].children_d = $.vakata.array_remove(this._model.data[obj.parents[i]].children_d, pos); 2802 } 2803 } 2804 if(this._model.data[tmp[k]].state.selected) { 2805 c = true; 2806 pos = $.inArray(tmp[k], this._data.core.selected); 2807 if(pos !== -1) { 2808 this._data.core.selected = $.vakata.array_remove(this._data.core.selected, pos); 2809 } 2810 } 2811 } 2812 /** 2813 * triggered when a node is deleted 2814 * @event 2815 * @name delete_node.jstree 2816 * @param {Object} node 2817 * @param {String} parent the parent's ID 2818 */ 2819 this.trigger('delete_node', { "node" : obj, "parent" : par.id }); 2820 if(c) { 2821 this.trigger('changed', { 'action' : 'delete_node', 'node' : obj, 'selected' : this._data.core.selected, 'parent' : par.id }); 2822 } 2823 for(k = 0, l = tmp.length; k < l; k++) { 2824 delete this._model.data[tmp[k]]; 2825 } 2826 this.redraw_node(par, true); 2827 return true; 2828 }, 2829 /** 2830 * check if an operation is premitted on the tree. Used internally. 2831 * @private 2832 * @name check(chk, obj, par, pos) 2833 * @param {String} chk the operation to check, can be "create_node", "rename_node", "delete_node", "copy_node" or "move_node" 2834 * @param {mixed} obj the node 2835 * @param {mixed} par the parent 2836 * @param {mixed} pos the position to insert at, or if "rename_node" - the new name 2837 * @return {Boolean} 2838 */ 2839 check : function (chk, obj, par, pos) { 2840 obj = obj && obj.id ? obj : this.get_node(obj); 2841 par = par && par.id ? par : this.get_node(par); 2842 var tmp = chk.match(/^move_node|copy_node|create_node$/i) ? par : obj, 2843 chc = this.settings.core.check_callback; 2844 if(chk === "move_node") { 2845 if(obj.id === par.id || $.inArray(obj.id, par.children) === pos || $.inArray(par.id, obj.children_d) !== -1) { 2846 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 }) }; 2847 return false; 2848 } 2849 } 2850 tmp = this.get_node(tmp, true); 2851 if(tmp.length) { tmp = tmp.data('jstree'); } 2852 if(tmp && tmp.functions && (tmp.functions[chk] === false || tmp.functions[chk] === true)) { 2853 if(tmp.functions[chk] === false) { 2854 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 }) }; 2855 } 2856 return tmp.functions[chk]; 2857 } 2858 if(chc === false || ($.isFunction(chc) && chc.call(this, chk, obj, par, pos) === false) || (chc && chc[chk] === false)) { 2859 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 }) }; 2860 return false; 2861 } 2862 return true; 2863 }, 2864 /** 2865 * get the last error 2866 * @name last_error() 2867 * @return {Object} 2868 */ 2869 last_error : function () { 2870 return this._data.core.last_error; 2871 }, 2872 /** 2873 * move a node to a new parent 2874 * @name move_node(obj, par [, pos, callback, is_loaded]) 2875 * @param {mixed} obj the node to move, pass an array to move multiple nodes 2876 * @param {mixed} par the new parent 2877 * @param {mixed} pos the position to insert at ("first" and "last" are supported, as well as "before" and "after"), defaults to `0` 2878 * @param {function} callback a function to call once the move is completed, receives 3 arguments - the node, the new parent and the position 2879 * @param {Boolean} internal parameter indicating if the parent node has been loaded 2880 * @trigger move_node.jstree 2881 */ 2882 move_node : function (obj, par, pos, callback, is_loaded) { 2883 var t1, t2, old_par, new_par, old_ins, is_multi, dpc, tmp, i, j, k, l, p; 2884 if($.isArray(obj)) { 2885 obj = obj.reverse().slice(); 2886 for(t1 = 0, t2 = obj.length; t1 < t2; t1++) { 2887 this.move_node(obj[t1], par, pos, callback, is_loaded); 2888 } 2889 return true; 2890 } 2891 obj = obj && obj.id ? obj : this.get_node(obj); 2892 par = this.get_node(par); 2893 pos = pos === undefined ? 0 : pos; 2894 2895 if(!par || !obj || obj.id === '#') { return false; } 2896 if(!pos.toString().match(/^(before|after)$/) && !is_loaded && !this.is_loaded(par)) { 2897 return this.load_node(par, function () { this.move_node(obj, par, pos, callback, true); }); 2898 } 2899 2900 old_par = (obj.parent || '#').toString(); 2901 new_par = (!pos.toString().match(/^(before|after)$/) || par.id === '#') ? par : this.get_node(par.parent); 2902 old_ins = this._model.data[obj.id] ? this : $.jstree.reference(obj.id); 2903 is_multi = !old_ins || !old_ins._id || (this._id !== old_ins._id); 2904 if(is_multi) { 2905 if(this.copy_node(obj, par, pos, callback, is_loaded)) { 2906 if(old_ins) { old_ins.delete_node(obj); } 2907 return true; 2908 } 2909 return false; 2910 } 2911 //var m = this._model.data; 2912 if(new_par.id === '#') { 2913 if(pos === "before") { pos = "first"; } 2914 if(pos === "after") { pos = "last"; } 2915 } 2916 switch(pos) { 2917 case "before": 2918 pos = $.inArray(par.id, new_par.children); 2919 break; 2920 case "after" : 2921 pos = $.inArray(par.id, new_par.children) + 1; 2922 break; 2923 case "inside": 2924 case "first": 2925 pos = 0; 2926 break; 2927 case "last": 2928 pos = new_par.children.length; 2929 break; 2930 default: 2931 if(!pos) { pos = 0; } 2932 break; 2933 } 2934 if(pos > new_par.children.length) { pos = new_par.children.length; } 2935 if(!this.check("move_node", obj, new_par, pos)) { 2936 this.settings.core.error.call(this, this._data.core.last_error); 2937 return false; 2938 } 2939 if(obj.parent === new_par.id) { 2940 dpc = new_par.children.concat(); 2941 tmp = $.inArray(obj.id, dpc); 2942 if(tmp !== -1) { 2943 dpc = $.vakata.array_remove(dpc, tmp); 2944 if(pos > tmp) { pos--; } 2945 } 2946 tmp = []; 2947 for(i = 0, j = dpc.length; i < j; i++) { 2948 tmp[i >= pos ? i+1 : i] = dpc[i]; 2949 } 2950 tmp[pos] = obj.id; 2951 new_par.children = tmp; 2952 this._node_changed(new_par.id); 2953 this.redraw(new_par.id === '#'); 2954 } 2955 else { 2956 // clean old parent and up 2957 tmp = obj.children_d.concat(); 2958 tmp.push(obj.id); 2959 for(i = 0, j = obj.parents.length; i < j; i++) { 2960 dpc = []; 2961 p = old_ins._model.data[obj.parents[i]].children_d; 2962 for(k = 0, l = p.length; k < l; k++) { 2963 if($.inArray(p[k], tmp) === -1) { 2964 dpc.push(p[k]); 2965 } 2966 } 2967 old_ins._model.data[obj.parents[i]].children_d = dpc; 2968 } 2969 old_ins._model.data[old_par].children = $.vakata.array_remove_item(old_ins._model.data[old_par].children, obj.id); 2970 2971 // insert into new parent and up 2972 for(i = 0, j = new_par.parents.length; i < j; i++) { 2973 this._model.data[new_par.parents[i]].children_d = this._model.data[new_par.parents[i]].children_d.concat(tmp); 2974 } 2975 dpc = []; 2976 for(i = 0, j = new_par.children.length; i < j; i++) { 2977 dpc[i >= pos ? i+1 : i] = new_par.children[i]; 2978 } 2979 dpc[pos] = obj.id; 2980 new_par.children = dpc; 2981 new_par.children_d.push(obj.id); 2982 new_par.children_d = new_par.children_d.concat(obj.children_d); 2983 2984 // update object 2985 obj.parent = new_par.id; 2986 tmp = new_par.parents.concat(); 2987 tmp.unshift(new_par.id); 2988 p = obj.parents.length; 2989 obj.parents = tmp; 2990 2991 // update object children 2992 tmp = tmp.concat(); 2993 for(i = 0, j = obj.children_d.length; i < j; i++) { 2994 this._model.data[obj.children_d[i]].parents = this._model.data[obj.children_d[i]].parents.slice(0,p*-1); 2995 Array.prototype.push.apply(this._model.data[obj.children_d[i]].parents, tmp); 2996 } 2997 2998 this._node_changed(old_par); 2999 this._node_changed(new_par.id); 3000 this.redraw(old_par === '#' || new_par.id === '#'); 3001 } 3002 if(callback) { callback.call(this, obj, new_par, pos); } 3003 /** 3004 * triggered when a node is moved 3005 * @event 3006 * @name move_node.jstree 3007 * @param {Object} node 3008 * @param {String} parent the parent's ID 3009 * @param {Number} position the position of the node among the parent's children 3010 * @param {String} old_parent the old parent of the node 3011 * @param {Boolean} is_multi do the node and new parent belong to different instances 3012 * @param {jsTree} old_instance the instance the node came from 3013 * @param {jsTree} new_instance the instance of the new parent 3014 */ 3015 this.trigger('move_node', { "node" : obj, "parent" : new_par.id, "position" : pos, "old_parent" : old_par, "is_multi" : is_multi, 'old_instance' : old_ins, 'new_instance' : this }); 3016 return true; 3017 }, 3018 /** 3019 * copy a node to a new parent 3020 * @name copy_node(obj, par [, pos, callback, is_loaded]) 3021 * @param {mixed} obj the node to copy, pass an array to copy multiple nodes 3022 * @param {mixed} par the new parent 3023 * @param {mixed} pos the position to insert at ("first" and "last" are supported, as well as "before" and "after"), defaults to `0` 3024 * @param {function} callback a function to call once the move is completed, receives 3 arguments - the node, the new parent and the position 3025 * @param {Boolean} internal parameter indicating if the parent node has been loaded 3026 * @trigger model.jstree copy_node.jstree 3027 */ 3028 copy_node : function (obj, par, pos, callback, is_loaded) { 3029 var t1, t2, dpc, tmp, i, j, node, old_par, new_par, old_ins, is_multi; 3030 if($.isArray(obj)) { 3031 obj = obj.reverse().slice(); 3032 for(t1 = 0, t2 = obj.length; t1 < t2; t1++) { 3033 this.copy_node(obj[t1], par, pos, callback, is_loaded); 3034 } 3035 return true; 3036 } 3037 obj = obj && obj.id ? obj : this.get_node(obj); 3038 par = this.get_node(par); 3039 pos = pos === undefined ? 0 : pos; 3040 3041 if(!par || !obj || obj.id === '#') { return false; } 3042 if(!pos.toString().match(/^(before|after)$/) && !is_loaded && !this.is_loaded(par)) { 3043 return this.load_node(par, function () { this.copy_node(obj, par, pos, callback, true); }); 3044 } 3045 3046 old_par = (obj.parent || '#').toString(); 3047 new_par = (!pos.toString().match(/^(before|after)$/) || par.id === '#') ? par : this.get_node(par.parent); 3048 old_ins = this._model.data[obj.id] ? this : $.jstree.reference(obj.id); 3049 is_multi = !old_ins || !old_ins._id || (this._id !== old_ins._id); 3050 if(new_par.id === '#') { 3051 if(pos === "before") { pos = "first"; } 3052 if(pos === "after") { pos = "last"; } 3053 } 3054 switch(pos) { 3055 case "before": 3056 pos = $.inArray(par.id, new_par.children); 3057 break; 3058 case "after" : 3059 pos = $.inArray(par.id, new_par.children) + 1; 3060 break; 3061 case "inside": 3062 case "first": 3063 pos = 0; 3064 break; 3065 case "last": 3066 pos = new_par.children.length; 3067 break; 3068 default: 3069 if(!pos) { pos = 0; } 3070 break; 3071 } 3072 if(pos > new_par.children.length) { pos = new_par.children.length; } 3073 if(!this.check("copy_node", obj, new_par, pos)) { 3074 this.settings.core.error.call(this, this._data.core.last_error); 3075 return false; 3076 } 3077 node = old_ins ? old_ins.get_json(obj, { no_id : true, no_data : true, no_state : true }) : obj; 3078 if(!node) { return false; } 3079 if(node.id === true) { delete node.id; } 3080 node = this._parse_model_from_json(node, new_par.id, new_par.parents.concat()); 3081 if(!node) { return false; } 3082 tmp = this.get_node(node); 3083 dpc = []; 3084 dpc.push(node); 3085 dpc = dpc.concat(tmp.children_d); 3086 this.trigger('model', { "nodes" : dpc, "parent" : new_par.id }); 3087 3088 // insert into new parent and up 3089 for(i = 0, j = new_par.parents.length; i < j; i++) { 3090 this._model.data[new_par.parents[i]].children_d = this._model.data[new_par.parents[i]].children_d.concat(dpc); 3091 } 3092 dpc = []; 3093 for(i = 0, j = new_par.children.length; i < j; i++) { 3094 dpc[i >= pos ? i+1 : i] = new_par.children[i]; 3095 } 3096 dpc[pos] = tmp.id; 3097 new_par.children = dpc; 3098 new_par.children_d.push(tmp.id); 3099 new_par.children_d = new_par.children_d.concat(tmp.children_d); 3100 3101 this._node_changed(new_par.id); 3102 this.redraw(new_par.id === '#'); 3103 if(callback) { callback.call(this, tmp, new_par, pos); } 3104 /** 3105 * triggered when a node is copied 3106 * @event 3107 * @name copy_node.jstree 3108 * @param {Object} node the copied node 3109 * @param {Object} original the original node 3110 * @param {String} parent the parent's ID 3111 * @param {Number} position the position of the node among the parent's children 3112 * @param {String} old_parent the old parent of the node 3113 * @param {Boolean} is_multi do the node and new parent belong to different instances 3114 * @param {jsTree} old_instance the instance the node came from 3115 * @param {jsTree} new_instance the instance of the new parent 3116 */ 3117 this.trigger('copy_node', { "node" : tmp, "original" : obj, "parent" : new_par.id, "position" : pos, "old_parent" : old_par, "is_multi" : is_multi, 'old_instance' : old_ins, 'new_instance' : this }); 3118 return tmp.id; 3119 }, 3120 /** 3121 * cut a node (a later call to `paste(obj)` would move the node) 3122 * @name cut(obj) 3123 * @param {mixed} obj multiple objects can be passed using an array 3124 * @trigger cut.jstree 3125 */ 3126 cut : function (obj) { 3127 if(!obj) { obj = this._data.core.selected.concat(); } 3128 if(!$.isArray(obj)) { obj = [obj]; } 3129 if(!obj.length) { return false; } 3130 var tmp = [], o, t1, t2; 3131 for(t1 = 0, t2 = obj.length; t1 < t2; t1++) { 3132 o = this.get_node(obj[t1]); 3133 if(o && o.id && o.id !== '#') { tmp.push(o); } 3134 } 3135 if(!tmp.length) { return false; } 3136 ccp_node = tmp; 3137 ccp_inst = this; 3138 ccp_mode = 'move_node'; 3139 /** 3140 * triggered when nodes are added to the buffer for moving 3141 * @event 3142 * @name cut.jstree 3143 * @param {Array} node 3144 */ 3145 this.trigger('cut', { "node" : obj }); 3146 }, 3147 /** 3148 * copy a node (a later call to `paste(obj)` would copy the node) 3149 * @name copy(obj) 3150 * @param {mixed} obj multiple objects can be passed using an array 3151 * @trigger copy.jstre 3152 */ 3153 copy : function (obj) { 3154 if(!obj) { obj = this._data.core.selected.concat(); } 3155 if(!$.isArray(obj)) { obj = [obj]; } 3156 if(!obj.length) { return false; } 3157 var tmp = [], o, t1, t2; 3158 for(t1 = 0, t2 = obj.length; t1 < t2; t1++) { 3159 o = this.get_node(obj[t1]); 3160 if(o && o.id && o.id !== '#') { tmp.push(o); } 3161 } 3162 if(!tmp.length) { return false; } 3163 ccp_node = tmp; 3164 ccp_inst = this; 3165 ccp_mode = 'copy_node'; 3166 /** 3167 * triggered when nodes are added to the buffer for copying 3168 * @event 3169 * @name copy.jstree 3170 * @param {Array} node 3171 */ 3172 this.trigger('copy', { "node" : obj }); 3173 }, 3174 /** 3175 * get the current buffer (any nodes that are waiting for a paste operation) 3176 * @name get_buffer() 3177 * @return {Object}
3177 an object consisting of `mode` ("copy_node" or "move_node"), `node` (an array of objects) and `inst` (the instance) 3178 */ 3179 get_buffer : function () { 3180 return { 'mode' : ccp_mode, 'node' : ccp_node, 'inst' : ccp_inst }; 3181 }, 3182 /** 3183 * check if there is something in the buffer to paste 3184 * @name can_paste() 3185 * @return {Boolean} 3186 */ 3187 can_paste : function () { 3188 return ccp_mode !== false && ccp_node !== false; // && ccp_inst._model.data[ccp_node]; 3189 }, 3190 /** 3191 * copy or move the previously cut or copied nodes to a new parent 3192 * @name paste(obj) 3193 * @param {mixed} obj the new parent 3194 * @trigger paste.jstree 3195 */ 3196 paste : function (obj) { 3197 obj = this.get_node(obj); 3198 if(!obj || !ccp_mode || !ccp_mode.match(/^(copy_node|move_node)$/) || !ccp_node) { return false; } 3199 if(this[ccp_mode](ccp_node, obj)) { 3200 /** 3201 * triggered when paste is invoked 3202 * @event 3203 * @name paste.jstree 3204 * @param {String} parent the ID of the receiving node 3205 * @param {Array} node the nodes in the buffer 3206 * @param {String} mode the performed operation - "copy_node" or "move_node" 3207 */ 3208 this.trigger('paste', { "parent" : obj.id, "node" : ccp_node, "mode" : ccp_mode }); 3209 } 3210 ccp_node = false; 3211 ccp_mode = false;
vendor: 10,208 bytes, lines 3212-3500
3212 ccp_inst = false; 3213 }, 3214 /** 3215 * put a node in edit mode (input field to rename the node) 3216 * @name edit(obj [, default_text]) 3217 * @param {mixed} obj 3218 * @param {String} default_text the text to populate the input with (if omitted the node text value is used) 3219 */ 3220 edit : function (obj, default_text) { 3221 obj = this._open_to(obj); 3222 if(!obj || !obj.length) { return false; } 3223 var rtl = this._data.core.rtl, 3224 w = this.element.width(), 3225 a = obj.children('.jstree-anchor'), 3226 s = $('<span>'), 3227 /*! 3228 oi = obj.children("i:visible"), 3229 ai = a.children("i:visible"), 3230 w1 = oi.width() * oi.length, 3231 w2 = ai.width() * ai.length, 3232 */ 3233 t = typeof default_text === 'string' ? default_text : this.get_text(obj), 3234 h1 = $("<"+"div />", { css : { "position" : "absolute", "top" : "-200px", "left" : (rtl ? "0px" : "-1000px"), "visibility" : "hidden" } }).appendTo("body"), 3235 h2 = $("<"+"input />", { 3236 "value" : t, 3237 "class" : "jstree-rename-input", 3238 // "size" : t.length, 3239 "css" : { 3240 "padding" : "0", 3241 "border" : "1px solid silver", 3242 "box-sizing" : "border-box", 3243 "display" : "inline-block", 3244 "height" : (this._data.core.li_height) + "px", 3245 "lineHeight" : (this._data.core.li_height) + "px", 3246 "width" : "150px" // will be set a bit further down 3247 }, 3248 "blur" : $.proxy(function () { 3249 var i = s.children(".jstree-rename-input"), 3250 v = i.val(); 3251 if(v === "") { v = t; } 3252 h1.remove(); 3253 s.replaceWith(a); 3254 s.remove(); 3255 this.set_text(obj, t); 3256 if(this.rename_node(obj, v) === false) { 3257 this.set_text(obj, t); // move this up? and fix #483 3258 } 3259 }, this), 3260 "keydown" : function (event) { 3261 var key = event.which; 3262 if(key === 27) { 3263 this.value = t; 3264 } 3265 if(key === 27 || key === 13 || key === 37 || key === 38 || key === 39 || key === 40 || key === 32) { 3266 event.stopImmediatePropagation(); 3267 } 3268 if(key === 27 || key === 13) { 3269 event.preventDefault(); 3270 this.blur(); 3271 } 3272 }, 3273 "click" : function (e) { e.stopImmediatePropagation(); }, 3274 "mousedown" : function (e) { e.stopImmediatePropagation(); }, 3275 "keyup" : function (event) { 3276 h2.width(Math.min(h1.text("pW" + this.value).width(),w)); 3277 }, 3278 "keypress" : function(event) { 3279 if(event.which === 13) { return false; } 3280 } 3281 }), 3282 fn = { 3283 fontFamily : a.css('fontFamily') || '', 3284 fontSize : a.css('fontSize') || '', 3285 fontWeight : a.css('fontWeight') || '', 3286 fontStyle : a.css('fontStyle') || '', 3287 fontStretch : a.css('fontStretch') || '', 3288 fontVariant : a.css('fontVariant') || '', 3289 letterSpacing : a.css('letterSpacing') || '', 3290 wordSpacing : a.css('wordSpacing') || '' 3291 }; 3292 this.set_text(obj, ""); 3293 s.attr('class', a.attr('class')).append(a.contents().clone()).append(h2); 3294 a.replaceWith(s); 3295 h1.css(fn); 3296 h2.css(fn).width(Math.min(h1.text("pW" + h2[0].value).width(),w))[0].select(); 3297 }, 3298 3299 3300 /** 3301 * changes the theme 3302 * @name set_theme(theme_name [, theme_url]) 3303 * @param {String} theme_name the name of the new theme to apply 3304 * @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. 3305 * @trigger set_theme.jstree 3306 */ 3307 set_theme : function (theme_name, theme_url) { 3308 if(!theme_name) { return false; } 3309 if(theme_url === true) { 3310 var dir = this.settings.core.themes.dir; 3311 if(!dir) { dir = $.jstree.path + '/themes'; } 3312 theme_url = dir + '/' + theme_name + '/style.css'; 3313 } 3314 if(theme_url && $.inArray(theme_url, themes_loaded) === -1) { 3315 $('head').append('<'+'link rel="stylesheet" href="' + theme_url + '" type="text/css" />'); 3316 themes_loaded.push(theme_url); 3317 } 3318 if(this._data.core.themes.name) { 3319 this.element.removeClass('jstree-' + this._data.core.themes.name); 3320 } 3321 this._data.core.themes.name = theme_name; 3322 this.element.addClass('jstree-' + theme_name); 3323 this.element[this.settings.core.themes.responsive ? 'addClass' : 'removeClass' ]('jstree-' + theme_name + '-responsive'); 3324 /** 3325 * triggered when a theme is set 3326 * @event 3327 * @name set_theme.jstree 3328 * @param {String} theme the new theme 3329 */ 3330 this.trigger('set_theme', { 'theme' : theme_name }); 3331 }, 3332 /** 3333 * gets the name of the currently applied theme name 3334 * @name get_theme() 3335 * @return {String} 3336 */ 3337 get_theme : function () { return this._data.core.themes.name; }, 3338 /** 3339 * changes the theme variant (if the theme has variants) 3340 * @name set_theme_variant(variant_name) 3341 * @param {String|Boolean} variant_name the variant to apply (if `false` is used the current variant is removed) 3342 */ 3343 set_theme_variant : function (variant_name) { 3344 if(this._data.core.themes.variant) { 3345 this.element.removeClass('jstree-' + this._data.core.themes.name + '-' + this._data.core.themes.variant); 3346 } 3347 this._data.core.themes.variant = variant_name; 3348 if(variant_name) { 3349 this.element.addClass('jstree-' + this._data.core.themes.name + '-' + this._data.core.themes.variant); 3350 } 3351 }, 3352 /** 3353 * gets the name of the currently applied theme variant 3354 * @name get_theme() 3355 * @return {String} 3356 */ 3357 get_theme_variant : function () { return this._data.core.themes.variant; }, 3358 /** 3359 * shows a striped background on the container (if the theme supports it) 3360 * @name show_stripes() 3361 */ 3362 show_stripes : function () { this._data.core.themes.stripes = true; this.get_container_ul().addClass("jstree-striped"); }, 3363 /** 3364 * hides the striped background on the container 3365 * @name hide_stripes() 3366 */ 3367 hide_stripes : function () { this._data.core.themes.stripes = false; this.get_container_ul().removeClass("jstree-striped"); }, 3368 /** 3369 * toggles the striped background on the container 3370 * @name toggle_stripes() 3371 */ 3372 toggle_stripes : function () { if(this._data.core.themes.stripes) { this.hide_stripes(); } else { this.show_stripes(); } }, 3373 /** 3374 * shows the connecting dots (if the theme supports it) 3375 * @name show_dots() 3376 */ 3377 show_dots : function () { this._data.core.themes.dots = true; this.get_container_ul().removeClass("jstree-no-dots"); }, 3378 /** 3379 * hides the connecting dots 3380 * @name hide_dots() 3381 */ 3382 hide_dots : function () { this._data.core.themes.dots = false; this.get_container_ul().addClass("jstree-no-dots"); }, 3383 /** 3384 * toggles the connecting dots 3385 * @name toggle_dots() 3386 */ 3387 toggle_dots : function () { if(this._data.core.themes.dots) { this.hide_dots(); } else { this.show_dots(); } }, 3388 /** 3389 * show the node icons 3390 * @name show_icons() 3391 */ 3392 show_icons : function () { this._data.core.themes.icons = true; this.get_container_ul().removeClass("jstree-no-icons"); }, 3393 /** 3394 * hide the node icons 3395 * @name hide_icons() 3396 */ 3397 hide_icons : function () { this._data.core.themes.icons = false; this.get_container_ul().addClass("jstree-no-icons"); }, 3398 /** 3399 * toggle the node icons 3400 * @name toggle_icons() 3401 */ 3402 toggle_icons : function () { if(this._data.core.themes.icons) { this.hide_icons(); } else { this.show_icons(); } }, 3403 /** 3404 * set the node icon for a node 3405 * @name set_icon(obj, icon) 3406 * @param {mixed} obj 3407 * @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 3408 */ 3409 set_icon : function (obj, icon) { 3410 var t1, t2, dom, old; 3411 if($.isArray(obj)) { 3412 obj = obj.slice(); 3413 for(t1 = 0, t2 = obj.length; t1 < t2; t1++) { 3414 this.set_icon(obj[t1], icon); 3415 } 3416 return true; 3417 } 3418 obj = this.get_node(obj); 3419 if(!obj || obj.id === '#') { return false; } 3420 old = obj.icon; 3421 obj.icon = icon; 3422 dom = this.get_node(obj, true).children(".jstree-anchor").children(".jstree-themeicon"); 3423 if(icon === false) { 3424 this.hide_icon(obj); 3425 } 3426 else if(icon === true) { 3427 dom.removeClass('jstree-themeicon-custom ' + old).css("background","").removeAttr("rel"); 3428 } 3429 else if(icon.indexOf("/") === -1 && icon.indexOf(".") === -1) { 3430 dom.removeClass(old).css("background",""); 3431 dom.addClass(icon + ' jstree-themeicon-custom').attr("rel",icon); 3432 } 3433 else { 3434 dom.removeClass(old).css("background",""); 3435 dom.addClass('jstree-themeicon-custom').css("background", "url('" + icon + "') center center no-repeat").attr("rel",icon); 3436 } 3437 return true; 3438 }, 3439 /** 3440 * get the node icon for a node 3441 * @name get_icon(obj) 3442 * @param {mixed} obj 3443 * @return {String} 3444 */ 3445 get_icon : function (obj) { 3446 obj = this.get_node(obj); 3447 return (!obj || obj.id === '#') ? false : obj.icon; 3448 }, 3449 /** 3450 * hide the icon on an individual node 3451 * @name hide_icon(obj) 3452 * @param {mixed} obj 3453 */ 3454 hide_icon : function (obj) { 3455 var t1, t2; 3456 if($.isArray(obj)) { 3457 obj = obj.slice(); 3458 for(t1 = 0, t2 = obj.length; t1 < t2; t1++) { 3459 this.hide_icon(obj[t1]); 3460 } 3461 return true; 3462 } 3463 obj = this.get_node(obj); 3464 if(!obj || obj === '#') { return false; } 3465 obj.icon = false; 3466 this.get_node(obj, true).children("a").children(".jstree-themeicon").addClass('jstree-themeicon-hidden'); 3467 return true; 3468 }, 3469 /** 3470 * show the icon on an individual node 3471 * @name show_icon(obj) 3472 * @param {mixed} obj 3473 */ 3474 show_icon : function (obj) { 3475 var t1, t2, dom; 3476 if($.isArray(obj)) { 3477 obj = obj.slice(); 3478 for(t1 = 0, t2 = obj.length; t1 < t2; t1++) { 3479 this.show_icon(obj[t1]); 3480 } 3481 return true; 3482 } 3483 obj = this.get_node(obj); 3484 if(!obj || obj === '#') { return false; } 3485 dom = this.get_node(obj, true); 3486 obj.icon = dom.length ? dom.children("a").children(".jstree-themeicon").attr('rel') : true; 3487 if(!obj.icon) { obj.icon = true; } 3488 dom.children("a").children(".jstree-themeicon").removeClass('jstree-themeicon-hidden'); 3489 return true; 3490 } 3491 }; 3492 3493 // helpers 3494 $.vakata = {}; 3495 // reverse 3496 $.fn.vakata_reverse = [].reverse; 3497 // collect attributes 3498 $.vakata.attributes = function(node, with_values) { 3499 node = $(node)[0]; 3500 var attr = with_values ? {}
3500 : []; 3501 if(node && node.attributes) { 3502 $.each(node.attributes, function (i, v) { 3503 if($.inArray(v.nodeName.toLowerCase(),['style','contenteditable','hasfocus','tabindex']) !== -1) { return; } 3504 if(v.nodeValue !== null && $.trim(v.nodeValue) !== '') { 3505 if(with_values) { attr[v.nodeName] = v.nodeValue; } 3506 else { attr.push(v.nodeName); } 3507 } 3508 }); 3509 } 3510 return attr; 3511 }; 3512 $.vakata.array_unique = function(array) { 3513 var a = [], i, j, l; 3514 for(i = 0, l = array.length; i < l; i++) { 3515 for(j = 0; j <= i; j++) { 3516 if(array[i] === array[j]) { 3517 break; 3518 } 3519 } 3520 if(j === i) { a.push(array[i]); } 3521 } 3522 return a; 3523 }; 3524 // remove item from array 3525 $.vakata.array_remove = function(array, from, to) { 3526 var rest = array.slice((to || from) + 1 || array.length); 3527 array.length = from < 0 ? array.length + from : from; 3528 array.push.apply(array, rest); 3529 return array; 3530 }; 3531 // remove item from array 3532 $.vakata.array_remove_item = function(array, item) { 3533 var tmp = $.inArray(item, array); 3534 return tmp !== -1 ? $.vakata.array_remove(array, tmp) : array; 3535 }; 3536 // browser sniffing 3537 (function () { 3538 var browser = {}, 3539 b_match = function(ua) { 3540 ua = ua.toLowerCase(); 3541 3542 var match = /(chrome)[ \/]([\w.]+)/.exec( ua ) || 3543 /(webkit)[ \/]([\w.]+)/.exec( ua ) || 3544 /(opera)(?:.*version|)[ \/]([\w.]+)/.exec( ua ) || 3545 /(msie) ([\w.]+)/.exec( ua ) || 3546 (ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec( ua )) || 3547 []; 3548 return { 3549 browser: match[1] || "", 3550 version: match[2] || "0" 3551 }; 3552 }, 3553 matched = b_match(window.navigator.userAgent); 3554 if(matched.browser) { 3555 browser[ matched.browser ] = true; 3556 browser.version = matched.version; 3557 } 3558 if(browser.chrome) { 3559 browser.webkit = true; 3560 } 3561 else if(browser.webkit) { 3562 browser.safari = true; 3563 } 3564 $.vakata.browser = browser; 3565 }()); 3566 if($.vakata.browser.msie && $.vakata.browser.version < 8) { 3567 $.jstree.defaults.core.animation = 0; 3568 } 3569 3570/** 3571 * ### Checkbox plugin 3572 * 3573 * This plugin renders checkbox icons in front of each node, making multiple selection much easier. 3574 * 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. 3575 */ 3576 3577 var _i = document.createElement('I'); 3578 _i.className = 'jstree-icon jstree-checkbox'; 3579 /** 3580 * stores all defaults for the checkbox plugin 3581 * @name $.jstree.defaults.checkbox 3582 * @plugin checkbox 3583 */ 3584 $.jstree.defaults.checkbox = { 3585 /** 3586 * a boolean indicating if checkboxes should be visible (can be changed at a later time using `show_checkboxes()` and `hide_checkboxes`). Defaults to `true`. 3587 * @name $.jstree.defaults.checkbox.visible 3588 * @plugin checkbox 3589 */ 3590 visible : true, 3591 /** 3592 * a boolean indicating if checkboxes should cascade down and have an undetermined state. Defaults to `true`. 3593 * @name $.jstree.defaults.checkbox.three_state 3594 * @plugin checkbox 3595 */ 3596 three_state : true, 3597 /** 3598 * a boolean indicating if clicking anywhere on the node should act as clicking on the checkbox. Defaults to `true`. 3599 * @name $.jstree.defaults.checkbox.whole_node 3600 * @plugin checkbox 3601 */ 3602 whole_node : true, 3603 /** 3604 * a boolean indicating if the selected style of a node should be kept, or removed. Defaults to `true`. 3605 * @name $.jstree.defaults.checkbox.keep_selected_style 3606 * @plugin checkbox 3607 */ 3608 keep_selected_style : true 3609 }; 3610 $.jstree.plugins.checkbox = function (options, parent) { 3611 this.bind = function () { 3612 parent.bind.call(this); 3613 this._data.checkbox.uto = false; 3614 this.element 3615 .on("init.jstree", $.proxy(function () { 3616 this._data.checkbox.visible = this.settings.checkbox.visible; 3617 if(!this.settings.checkbox.keep_selected_style) { 3618 this.element.addClass('jstree-checkbox-no-clicked'); 3619 } 3620 }, this)) 3621 .on("loading.jstree", $.proxy(function () { 3622 this[ this._data.checkbox.visible ? 'show_checkboxes' : 'hide_checkboxes' ](); 3623 }, this)); 3624 if(this.settings.checkbox.three_state) { 3625 this.element 3626 .on('changed.jstree move_node.jstree copy_node.jstree redraw.jstree open_node.jstree', $.proxy(function () { 3627 if(this._data.checkbox.uto) { clearTimeout(this._data.checkbox.uto); } 3628 this._data.checkbox.uto = setTimeout($.proxy(this._undetermined, this), 50); 3629 }, this)) 3630 .on('model.jstree', $.proxy(function (e, data) { 3631 var m = this._model.data, 3632 p = m[data.parent], 3633 dpc = data.nodes, 3634 chd = [], 3635 c, i, j, k, l, tmp; 3636 3637 // apply down 3638 if(p.state.selected) { 3639 for(i = 0, j = dpc.length; i < j; i++) { 3640 m[dpc[i]].state.selected = true; 3641 } 3642 this._data.core.selected = this._data.core.selected.concat(dpc); 3643 } 3644 else { 3645 for(i = 0, j = dpc.length; i < j; i++) { 3646 if(m[dpc[i]].state.selected) { 3647 for(k = 0, l = m[dpc[i]].children_d.length; k < l; k++) { 3648 m[m[dpc[i]].children_d[k]].state.selected = true; 3649 } 3650 this._data.core.selected = this._data.core.selected.concat(m[dpc[i]].children_d); 3651 } 3652 } 3653 } 3654 3655 // apply up 3656 for(i = 0, j = p.children_d.length; i < j; i++) { 3657 if(!m[p.children_d[i]].children.length) { 3658 chd.push(m[p.children_d[i]].parent); 3659 } 3660 } 3661 chd = $.vakata.array_unique(chd); 3662 for(k = 0, l = chd.length; k < l; k++) { 3663 p = m[chd[k]]; 3664 while(p && p.id !== '#') { 3665 c = 0; 3666 for(i = 0, j = p.children.length; i < j; i++) { 3667 c += m[p.children[i]].state.selected; 3668 } 3669 if(c === j) { 3670 p.state.selected = true; 3671 this._data.core.selected.push(p.id); 3672 tmp = this.get_node(p, true); 3673 if(tmp && tmp.length) { 3674 tmp.children('.jstree-anchor').addClass('jstree-clicked'); 3675 } 3676 } 3677 else { 3678 break; 3679 } 3680 p = this.get_node(p.parent); 3681 } 3682 } 3683 this._data.core.selected = $.vakata.array_unique(this._data.core.selected); 3684 }, this)) 3685 .on('select_node.jstree', $.proxy(function (e, data) { 3686 var obj = data.node, 3687 m = this._model.data, 3688 par = this.get_node(obj.parent), 3689 dom = this.get_node(obj, true), 3690 i, j, c, tmp; 3691 this._data.core.selected = $.vakata.array_unique(this._data.core.selected.concat(obj.children_d)); 3692 for(i = 0, j = obj.children_d.length; i < j; i++) { 3693 m[obj.children_d[i]].state.selected = true; 3694 }
3695 while(par && par.id !== '#') { 3696 c = 0; 3697 for(i = 0, j = par.children.length; i < j; i++) { 3698 c += m[par.children[i]].state.selected; 3699 } 3700 if(c === j) { 3701 par.state.selected = true; 3702 this._data.core.selected.push(par.id); 3703 tmp = this.get_node(par, true); 3704 if(tmp && tmp.length) { 3705 tmp.children('.jstree-anchor').addClass('jstree-clicked'); 3706 } 3707 } 3708 else { 3709 break; 3710 } 3711 par = this.get_node(par.parent); 3712 } 3713 if(dom.length) { 3714 dom.find('.jstree-anchor').addClass('jstree-clicked'); 3715 } 3716 }, this)) 3717 .on('deselect_node.jstree', $.proxy(function (e, data) { 3718 var obj = data.node, 3719 dom = this.get_node(obj, true), 3720 i, j, tmp; 3721 for(i = 0, j = obj.children_d.length; i < j; i++) { 3722 this._model.data[obj.children_d[i]].state.selected = false; 3723 } 3724 for(i = 0, j = obj.parents.length; i < j; i++) { 3725 this._model.data[obj.parents[i]].state.selected = false; 3726 tmp = this.get_node(obj.parents[i], true); 3727 if(tmp && tmp.length) { 3728 tmp.children('.jstree-anchor').removeClass('jstree-clicked'); 3729 } 3730 } 3731 tmp = []; 3732 for(i = 0, j = this._data.core.selected.length; i < j; i++) { 3733 if($.inArray(this._data.core.selected[i], obj.children_d) === -1 && $.inArray(this._data.core.selected[i], obj.parents) === -1) { 3734 tmp.push(this._data.core.selected[i]); 3735 } 3736 } 3737 this._data.core.selected = $.vakata.array_unique(tmp); 3738 if(dom.length) { 3739 dom.find('.jstree-anchor').removeClass('jstree-clicked'); 3740 } 3741 }, this)) 3742 .on('delete_node.jstree', $.proxy(function (e, data) { 3743 var p = this.get_node(data.parent), 3744 m = this._model.data, 3745 i, j, c, tmp; 3746 while(p && p.id !== '#') { 3747 c = 0; 3748 for(i = 0, j = p.children.length; i < j; i++) { 3749 c += m[p.children[i]].state.selected; 3750 } 3751 if(c === j) { 3752 p.state.selected = true; 3753 this._data.core.selected.push(p.id); 3754 tmp = this.get_node(p, true); 3755 if(tmp && tmp.length) { 3756 tmp.children('.jstree-anchor').addClass('jstree-clicked'); 3757 } 3758 } 3759 else { 3760 break; 3761 } 3762 p = this.get_node(p.parent); 3763 } 3764 }, this)) 3765 .on('move_node.jstree', $.proxy(function (e, data) { 3766 var is_multi = data.is_multi, 3767 old_par = data.old_parent, 3768 new_par = this.get_node(data.parent), 3769 m = this._model.data, 3770 p, c, i, j, tmp; 3771 if(!is_multi) { 3772 p = this.get_node(old_par); 3773 while(p && p.id !== '#') { 3774 c = 0; 3775 for(i = 0, j = p.children.length; i < j; i++) { 3776 c += m[p.children[i]].state.selected; 3777 } 3778 if(c === j) { 3779 p.state.selected = true; 3780 this._data.core.selected.push(p.id); 3781 tmp = this.get_node(p, true); 3782 if(tmp && tmp.length) { 3783 tmp.children('.jstree-anchor').addClass('jstree-clicked'); 3784 } 3785 } 3786 else { 3787 break; 3788 } 3789 p = this.get_node(p.parent); 3790 } 3791 } 3792 p = new_par; 3793 while(p && p.id !== '#') { 3794 c = 0; 3795 for(i = 0, j = p.children.length; i < j; i++) { 3796 c += m[p.children[i]].state.selected; 3797 } 3798 if(c === j) { 3799 if(!p.state.selected) { 3800 p.state.selected = true; 3801 this._data.core.selected.push(p.id); 3802 tmp = this.get_node(p, true); 3803 if(tmp && tmp.length) { 3804 tmp.children('.jstree-anchor').addClass('jstree-clicked'); 3805 } 3806 } 3807 } 3808 else { 3809 if(p.state.selected) { 3810 p.state.selected = false; 3811 this._data.core.selected = $.vakata.array_remove_item(this._data.core.selected, p.id); 3812 tmp = this.get_node(p, true); 3813 if(tmp && tmp.length) { 3814 tmp.children('.jstree-anchor').removeClass('jstree-clicked'); 3815 } 3816 } 3817 else { 3818 break; 3819 } 3820 } 3821 p = this.get_node(p.parent); 3822 } 3823 }, this)); 3824 } 3825 }; 3826 /** 3827 * set the undetermined state where and if necessary. Used internally. 3828 * @private 3829 * @name _undetermined() 3830 * @plugin checkbox 3831 */ 3832 this._undetermined = function () { 3833 var i, j, m = this._model.data, s = this._data.core.selected, p = [], t = this; 3834 for(i = 0, j = s.length; i < j; i++) { 3835 if(m[s[i]] && m[s[i]].parents) { 3836 p = p.concat(m[s[i]].parents); 3837 } 3838 } 3839 // attempt for server side undetermined state 3840 this.element.find('.jstree-closed').not(':has(ul)') 3841 .each(function () { 3842 var tmp = t.get_node(this); 3843 if(!tmp.state.loaded && tmp.original && tmp.original.state && tmp.original.state.undetermined && tmp.original.state.undetermined === true) { 3844 p.push(tmp.id); 3845 p = p.concat(tmp.parents); 3846 } 3847 }); 3848 p = $.vakata.array_unique(p); 3849 i = $.inArray('#', p); 3850 if(i !== -1) { 3851 p = $.vakata.array_remove(p, i); 3852 } 3853 3854 this.element.find('.jstree-undetermined').removeClass('jstree-undetermined'); 3855 for(i = 0, j = p.length; i < j; i++) { 3856 if(!m[p[i]].state.selected) { 3857 s = this.get_node(p[i], true); 3858 if(s && s.length) { 3859 s.children('a').children('.jstree-checkbox').addClass('jstree-undetermined'); 3860 } 3861 } 3862 } 3863 }; 3864 this.redraw_node = function(obj, deep, is_callback) { 3865 obj = parent.redraw_node.call(this, obj, deep, is_callback); 3866 if(obj) { 3867 var tmp = obj.getElementsByTagName('A')[0]; 3868 tmp.insertBefore(_i.cloneNode(), tmp.childNodes[0]); 3869 } 3870 if(!is_callback && this.settings.checkbox.three_state) { 3871 if(this._data.checkbox.uto) { clearTimeout(this._data.checkbox.uto); } 3872 this._data.checkbox.uto = setTimeout($.proxy(this._undetermined, this), 50); 3873 } 3874 return obj; 3875 }; 3876 this.activate_node = function (obj, e) { 3877 if(this.settings.checkbox.whole_node || $(e.target).hasClass('jstree-checkbox')) { 3878 e.ctrlKey = true; 3879 } 3880 return parent.activate_node.call(this, obj, e); 3881 }; 3882 /** 3883 * show the node checkbox icons 3884 * @name show_checkboxes() 3885 * @plugin checkbox 3886 */ 3887 this.show_checkboxes = function () { this._data.core.themes.checkboxes = true; this.element.children("ul").removeClass("jstree-no-checkboxes"); }; 3888 /** 3889 * hide the node checkbox icons 3890 * @name hide_checkboxes() 3891 * @plugin checkbox 3892 */ 3893 this.hide_checkboxes = function () { this._data.core.themes.checkboxes = false; this.element.children("ul").addClass("jstree-no-checkboxes"); }; 3894 /** 3895 * toggle the node icons 3896 * @name toggle_checkboxes() 3897 * @plugin checkbox 3898 */ 3899 this.toggle_checkboxes = function () { if(this._data.core.themes.checkboxes) { this.hide_checkboxes(); } else { this.show_checkboxes(); } }; 3900 }; 3901 3902 // include the checkbox plugin by default 3903 // $.jstree.defaults.plugins.push("checkbox"); 3904 3905/** 3906 * ### Contextmenu plugin 3907 * 3908 * Shows a context menu when a node is right-clicked. 3909 */ 3910// TODO: move logic outside of function + check multiple move 3911 3912 /** 3913 * stores all defaults for the contextmenu plugin 3914 * @name $.jstree.defaults.contextmenu 3915 * @plugin contextmenu 3916 */ 3917 $.jstree.defaults.contextmenu = { 3918 /** 3919 * a boolean indicating if the node should be selected when the context menu is invoked on it. Defaults to `true`. 3920 * @name $.jstree.defaults.contextmenu.select_node 3921 * @plugin contextmenu 3922 */ 3923 select_node : true, 3924 /** 3925 * a boolean indicating if the menu should be shown aligned with the node. Defaults to `true`, otherwise the mouse
3925coordinates are used. 3926 * @name $.jstree.defaults.contextmenu.show_at_node 3927 * @plugin contextmenu 3928 */ 3929 show_at_node : true, 3930 /** 3931 * 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). 3932 * 3933 * 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): 3934 * 3935 * * `separator_before` - a boolean indicating if there should be a separator before this item 3936 * * `separator_after` - a boolean indicating if there should be a separator after this item 3937 * * `_disabled` - a boolean indicating if this action should be disabled 3938 * * `label` - a string - the name of the action 3939 * * `action` - a function to be executed if this item is chosen 3940 * * `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 3941 * * `shortcut` - keyCode which will trigger the action if the menu is open (for example `113` for rename, which equals F2) 3942 * * `shortcut_label` - shortcut label (like for example `F2` for rename) 3943 * 3944 * @name $.jstree.defaults.contextmenu.items 3945 * @plugin contextmenu 3946 */ 3947 items : function (o, cb) { // Could be an object directly 3948 return { 3949 "create" : { 3950 "separator_before" : false, 3951 "separator_after" : true, 3952 "_disabled" : false, //(this.check("create_node", data.reference, {}, "last")), 3953 "label" : "Create", 3954 "action" : function (data) { 3955 var inst = $.jstree.reference(data.reference), 3956 obj = inst.get_node(data.reference); 3957 inst.create_node(obj, {}, "last", function (new_node) { 3958 setTimeout(function () { inst.edit(new_node); },0); 3959 }); 3960 } 3961 }, 3962 "rename" : { 3963 "separator_before" : false, 3964 "separator_after" : false, 3965 "_disabled" : false, //(this.check("rename_node", data.reference, this.get_parent(data.reference), "")), 3966 "label" : "Rename", 3967 /* 3968 "shortcut" : 113, 3969 "shortcut_label" : 'F2', 3970 "icon" : "glyphicon glyphicon-leaf", 3971 */ 3972 "action" : function (data) { 3973 var inst = $.jstree.reference(data.reference), 3974 obj = inst.get_node(data.reference); 3975 inst.edit(obj); 3976 } 3977 }, 3978 "remove" : { 3979 "separator_before" : false, 3980 "icon" : false, 3981 "separator_after" : false, 3982 "_disabled" : false, //(this.check("delete_node", data.reference, this.get_parent(data.reference), "")), 3983 "label" : "Delete", 3984 "action" : function (data) { 3985 var inst = $.jstree.reference(data.reference), 3986 obj = inst.get_node(data.reference); 3987 if(inst.is_selected(obj)) { 3988 inst.delete_node(inst.get_selected()); 3989 } 3990 else { 3991 inst.delete_node(obj); 3992 } 3993 } 3994 }, 3995 "ccp" : { 3996 "separator_before" : true, 3997 "icon" : false, 3998 "separator_after" : false, 3999 "label" : "Edit", 4000 "action" : false, 4001 "submenu" : { 4002 "cut" : { 4003 "separator_before" : false, 4004 "separator_after" : false, 4005 "label" : "Cut", 4006 "action" : function (data) { 4007 var inst = $.jstree.reference(data.reference), 4008 obj = inst.get_node(data.reference); 4009 if(inst.is_selected(obj)) { 4010 inst.cut(inst.get_selected()); 4011 } 4012 else { 4013 inst.cut(obj); 4014 } 4015 } 4016 }, 4017 "copy" : { 4018 "separator_before" : false, 4019 "icon" : false, 4020 "separator_after" : false, 4021 "label" : "Copy", 4022 "action" : function (data) { 4023 var inst = $.jstree.reference(data.reference), 4024 obj = inst.get_node(data.reference); 4025 if(inst.is_selected(obj)) { 4026 inst.copy(inst.get_selected()); 4027 } 4028 else { 4029 inst.copy(obj); 4030 } 4031 } 4032 }, 4033 "paste" : { 4034 "separator_before" : false, 4035 "icon" : false, 4036 "_disabled" : function (data) { 4037 return !$.jstree.reference(data.reference).can_paste(); 4038 }, 4039 "separator_after" : false, 4040 "label" : "Paste", 4041 "action" : function (data) { 4042 var inst = $.jstree.reference(data.reference), 4043 obj = inst.get_node(data.reference); 4044 inst.paste(obj); 4045 } 4046 } 4047 } 4048 } 4049 }; 4050 } 4051 }; 4052 4053 $.jstree.plugins.contextmenu = function (options, parent) { 4054 this.bind = function () { 4055 parent.bind.call(this); 4056 4057 this.element 4058 .on("contextmenu.jstree", ".jstree-anchor", $.proxy(function (e) { 4059 e.preventDefault(); 4060 if(!this.is_loading(e.currentTarget)) { 4061 this.show_contextmenu(e.currentTarget, e.pageX, e.pageY, e); 4062 } 4063 }, this)) 4064 .on("click.jstree", ".jstree-anchor", $.proxy(function (e) { 4065 if(this._data.contextmenu.visible) { 4066 $.vakata.context.hide(); 4067 } 4068 }, this)); 4069 /* 4070 if(!('oncontextmenu' in document.body) && ('ontouchstart' in document.body)) { 4071 var el = null, tm = null; 4072 this.element 4073 .on("touchstart", ".jstree-anchor", function (e) { 4074 el = e.currentTarget; 4075 tm = +new Date(); 4076 $(document).one("touchend", function (e) { 4077 e.target = document.elementFromPoint(e.originalEvent.targetTouches[0].pageX - window.pageXOffset, e.originalEvent.targetTouches[0].pageY - window.pageYOffset); 4078 e.currentTarget = e.target; 4079 tm = ((+(new Date())) - tm); 4080 if(e.target === el && tm > 600 && tm < 1000) { 4081 e.preventDefault(); 4082 $(el).trigger('contextmenu', e); 4083 } 4084 el = null; 4085 tm = null; 4086 }); 4087 }); 4088 } 4089 */ 4090 $(document).on("context_hide.vakata", $.proxy(function () { this._data.contextmenu.visible = false; }, this)); 4091 }; 4092 this.teardown = function () { 4093 if(this._data.contextmenu.visible) { 4094 $.vakata.context.hide(); 4095 } 4096 parent.teardown.call(this); 4097 }; 4098 4099 /** 4100 * prepare and show the context menu for a node 4101 * @name show_contextmenu(obj [, x, y]) 4102 * @param {mixed} obj the node 4103 * @param {Number} x the x-coordinate relative to the document to show the menu at 4104 * @param {Number} y the y-coordinate relative to the document to show the menu at 4105 * @param {Object} e the event if available that triggered the contextmenu 4106 * @plugin contextmenu 4107 * @trigger show_contextmenu.jstree 4108 */ 4109 this.show_contextmenu = function (obj, x, y, e) { 4110 obj = this.get_node(obj); 4111 if(!obj || obj.id === '#') { return false; } 4112 var s = this.settings.contextmenu, 4113 d = this.get_node(obj, true), 4114 a = d.children(".jstree-anchor"), 4115 o = false, 4116 i = false;
vendor: 11,150 bytes, lines 4117-4424
4117 if(s.show_at_node || x === undefined || y === undefined) { 4118 o = a.offset(); 4119 x = o.left; 4120 y = o.top + this._data.core.li_height; 4121 } 4122 if(this.settings.contextmenu.select_node && !this.is_selected(obj)) { 4123 this.deselect_all(); 4124 this.select_node(obj, false, false, e); 4125 } 4126 4127 i = s.items; 4128 if($.isFunction(i)) { 4129 i = i.call(this, obj, $.proxy(function (i) { 4130 this._show_contextmenu(obj, x, y, i); 4131 }, this)); 4132 } 4133 if($.isPlainObject(i)) { 4134 this._show_contextmenu(obj, x, y, i); 4135 } 4136 }; 4137 /** 4138 * show the prepared context menu for a node 4139 * @name _show_contextmenu(obj, x, y, i) 4140 * @param {mixed} obj the node 4141 * @param {Number} x the x-coordinate relative to the document to show the menu at 4142 * @param {Number} y the y-coordinate relative to the document to show the menu at 4143 * @param {Number} i the object of items to show 4144 * @plugin contextmenu 4145 * @trigger show_contextmenu.jstree 4146 * @private 4147 */ 4148 this._show_contextmenu = function (obj, x, y, i) { 4149 var d = this.get_node(obj, true), 4150 a = d.children(".jstree-anchor"); 4151 $(document).one("context_show.vakata", $.proxy(function (e, data) { 4152 var cls = 'jstree-contextmenu jstree-' + this.get_theme() + '-contextmenu'; 4153 $(data.element).addClass(cls); 4154 }, this)); 4155 this._data.contextmenu.visible = true; 4156 $.vakata.context.show(a, { 'x' : x, 'y' : y }, i); 4157 /** 4158 * triggered when the contextmenu is shown for a node 4159 * @event 4160 * @name show_contextmenu.jstree 4161 * @param {Object} node the node 4162 * @param {Number} x the x-coordinate of the menu relative to the document 4163 * @param {Number} y the y-coordinate of the menu relative to the document 4164 * @plugin contextmenu 4165 */ 4166 this.trigger('show_contextmenu', { "node" : obj, "x" : x, "y" : y }); 4167 }; 4168 }; 4169 4170 // contextmenu helper 4171 (function ($) { 4172 var right_to_left = false, 4173 vakata_context = { 4174 element : false, 4175 reference : false, 4176 position_x : 0, 4177 position_y : 0, 4178 items : [], 4179 html : "", 4180 is_visible : false 4181 }; 4182 4183 $.vakata.context = { 4184 settings : { 4185 hide_onmouseleave : 0, 4186 icons : true 4187 }, 4188 _trigger : function (event_name) { 4189 $(document).triggerHandler("context_" + event_name + ".vakata", { 4190 "reference" : vakata_context.reference, 4191 "element" : vakata_context.element, 4192 "position" : { 4193 "x" : vakata_context.position_x, 4194 "y" : vakata_context.position_y 4195 } 4196 }); 4197 }, 4198 _execute : function (i) { 4199 i = vakata_context.items[i]; 4200 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, { 4201 "item" : i, 4202 "reference" : vakata_context.reference, 4203 "element" : vakata_context.element, 4204 "position" : { 4205 "x" : vakata_context.position_x, 4206 "y" : vakata_context.position_y 4207 } 4208 }) : false; 4209 }, 4210 _parse : function (o, is_callback) { 4211 if(!o) { return false; } 4212 if(!is_callback) { 4213 vakata_context.html = ""; 4214 vakata_context.items = []; 4215 } 4216 var str = "", 4217 sep = false, 4218 tmp; 4219 4220 if(is_callback) { str += "<"+"ul>"; } 4221 $.each(o, function (i, val) { 4222 if(!val) { return true; } 4223 vakata_context.items.push(val); 4224 if(!sep && val.separator_before) { 4225 str += "<"+"li class='vakata-context-separator'><"+"a href='#' " + ($.vakata.context.settings.icons ? '' : 'style="margin-left:0px;"') + "> <"+"/a><"+"/li>"; 4226 } 4227 sep = false; 4228 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+"' ":'')+">"; 4229 str += "<"+"a href='#' rel='" + (vakata_context.items.length - 1) + "'>"; 4230 if($.vakata.context.settings.icons) { 4231 str += "<"+"i "; 4232 if(val.icon) { 4233 if(val.icon.indexOf("/") !== -1 || val.icon.indexOf(".") !== -1) { str += " style='background:url(\"" + val.icon + "\") center center no-repeat' "; } 4234 else { str += " class='" + val.icon + "' "; } 4235 } 4236 str += "><"+"/i><"+"span class='vakata-contextmenu-sep'> <"+"/span>"; 4237 } 4238 str += val.label + (val.shortcut?' <span class="vakata-contextmenu-shortcut vakata-contextmenu-shortcut-'+val.shortcut+'">'+ (val.shortcut_label || '') +'</span>':'') + "<"+"/a>"; 4239 if(val.submenu) { 4240 tmp = $.vakata.context._parse(val.submenu, true); 4241 if(tmp) { str += tmp; } 4242 } 4243 str += "<"+"/li>"; 4244 if(val.separator_after) { 4245 str += "<"+"li class='vakata-context-separator'><"+"a href='#' " + ($.vakata.context.settings.icons ? '' : 'style="margin-left:0px;"') + "> <"+"/a><"+"/li>"; 4246 sep = true; 4247 } 4248 }); 4249 str = str.replace(/<li class\='vakata-context-separator'\><\/li\>$/,""); 4250 if(is_callback) { str += "</ul>"; } 4251 /** 4252 * triggered on the document when the contextmenu is parsed (HTML is built) 4253 * @event 4254 * @plugin contextmenu 4255 * @name context_parse.vakata 4256 * @param {jQuery} reference the element that was right clicked 4257 * @param {jQuery} element the DOM element of the menu itself 4258 * @param {Object} position the x & y coordinates of the menu 4259 */ 4260 if(!is_callback) { vakata_context.html = str; $.vakata.context._trigger("parse"); } 4261 return str.length > 10 ? str : false; 4262 }, 4263 _show_submenu : function (o) { 4264 o = $(o); 4265 if(!o.length || !o.children("ul").length) { return; } 4266 var e = o.children("ul"), 4267 x = o.offset().left + o.outerWidth(), 4268 y = o.offset().top, 4269 w = e.width(), 4270 h = e.height(), 4271 dw = $(window).width() + $(window).scrollLeft(), 4272 dh = $(window).height() + $(window).scrollTop(); 4273 // може да Ñе ÑпеÑÑи е една пÑовеÑка - дали нÑма нÑкой Ð¾Ñ ÐºÐ»Ð°ÑовеÑе веÑе нагоÑе 4274 if(right_to_left) { 4275 o[x - (w + 10 + o.outerWidth()) < 0 ? "addClass" : "removeClass"]("vakata-context-left"); 4276 } 4277 else { 4278 o[x + w + 10 > dw ? "addClass" : "removeClass"]("vakata-context-right"); 4279 } 4280 if(y + h + 10 > dh) { 4281 e.css("bottom","-1px"); 4282 } 4283 e.show(); 4284 }, 4285 show : function (reference, position, data) { 4286 var o, e, x, y, w, h, dw, dh, cond = true; 4287 if(vakata_context.element && vakata_context.element.length) { 4288 vakata_context.element.width(''); 4289 } 4290 switch(cond) { 4291 case (!position && !reference): 4292 return false; 4293 case (!!position && !!reference): 4294 vakata_context.reference = reference; 4295 vakata_context.position_x = position.x; 4296 vakata_context.position_y = position.y; 4297 break; 4298 case (!position && !!reference): 4299 vakata_context.reference = reference; 4300 o = reference.offset(); 4301 vakata_context.position_x = o.left + reference.outerHeight(); 4302 vakata_context.position_y = o.top; 4303 break; 4304 case (!!position && !reference): 4305 vakata_context.position_x = position.x; 4306 vakata_context.position_y = position.y; 4307 break; 4308 } 4309 if(!!reference && !data && $(reference).data('vakata_contextmenu')) { 4310 data = $(reference).data('vakata_contextmenu'); 4311 } 4312 if($.vakata.context._parse(data)) { 4313 vakata_context.element.html(vakata_context.html); 4314 } 4315 if(vakata_context.items.length) { 4316 e = vakata_context.element; 4317 x = vakata_context.position_x; 4318 y = vakata_context.position_y; 4319 w = e.width(); 4320 h = e.height(); 4321 dw = $(window).width() + $(window).scrollLeft(); 4322 dh = $(window).height() + $(window).scrollTop(); 4323 if(right_to_left) { 4324 x -= e.outerWidth(); 4325 if(x < $(window).scrollLeft() + 20) { 4326 x = $(window).scrollLeft() + 20; 4327 } 4328 } 4329 if(x + w + 20 > dw) { 4330 x = dw - (w + 20); 4331 } 4332 if(y + h + 20 > dh) { 4333 y = dh - (h + 20); 4334 } 4335 4336 vakata_context.element 4337 .css({ "left" : x, "top" : y }) 4338 .show() 4339 .find('a:eq(0)').focus().parent().addClass("vakata-context-hover"); 4340 vakata_context.is_visible = true; 4341 /** 4342 * triggered on the document when the contextmenu is shown 4343 * @event 4344 * @plugin contextmenu 4345 * @name context_show.vakata 4346 * @param {jQuery} reference the element that was right clicked 4347 * @param {jQuery} element the DOM element of the menu itself 4348 * @param {Object} position the x & y coordinates of the menu 4349 */ 4350 $.vakata.context._trigger("show"); 4351 } 4352 }, 4353 hide : function () { 4354 if(vakata_context.is_visible) { 4355 vakata_context.element.hide().find("ul").hide().end().find(':focus').blur(); 4356 vakata_context.is_visible = false; 4357 /** 4358 * triggered on the document when the contextmenu is hidden 4359 * @event 4360 * @plugin contextmenu 4361 * @name context_hide.vakata 4362 * @param {jQuery} reference the element that was right clicked 4363 * @param {jQuery} element the DOM element of the menu itself 4364 * @param {Object} position the x & y coordinates of the menu 4365 */ 4366 $.vakata.context._trigger("hide"); 4367 } 4368 } 4369 }; 4370 $(function () { 4371 right_to_left = $("body").css("direction") === "rtl"; 4372 var to = false; 4373 4374 vakata_context.element = $("<ul class='vakata-context'></ul>"); 4375 vakata_context.element 4376 .on("mouseenter", "li", function (e) { 4377 e.stopImmediatePropagation(); 4378 4379 if($.contains(this, e.relatedTarget)) { 4380 // пÑÐµÐ¼Ð°Ñ Ð½Ð°Ñо заÑади delegate mouseleave по-Ð´Ð¾Ð»Ñ 4381 // $(this).find(".vakata-context-hover").removeClass("vakata-context-hover"); 4382 return; 4383 } 4384 4385 if(to) { clearTimeout(to); } 4386 vakata_context.element.find(".vakata-context-hover").removeClass("vakata-context-hover").end(); 4387 4388 $(this) 4389 .siblings().find("ul").hide().end().end() 4390 .parentsUntil(".vakata-context", "li").addBack().addClass("vakata-context-hover"); 4391 $.vakata.context._show_submenu(this); 4392 }) 4393 // ÑеÑÑово - дали не наÑоваÑва? 4394 .on("mouseleave", "li", function (e) { 4395 if($.contains(this, e.relatedTarget)) { return; } 4396 $(this).find(".vakata-context-hover").addBack().removeClass("vakata-context-hover"); 4397 }) 4398 .on("mouseleave", function (e) { 4399 $(this).find(".vakata-context-hover").removeClass("vakata-context-hover"); 4400 if($.vakata.context.settings.hide_onmouseleave) { 4401 to = setTimeout( 4402 (function (t) { 4403 return function () { $.vakata.context.hide(); }; 4404 }(this)), $.vakata.context.settings.hide_onmouseleave); 4405 } 4406 }) 4407 .on("click", "a", function (e) { 4408 e.preventDefault(); 4409 }) 4410 .on("mouseup", "a", function (e) { 4411 if(!$(this).blur().parent().hasClass("vakata-context-disabled") && $.vakata.context._execute($(this).attr("rel")) !== false) { 4412 $.vakata.context.hide(); 4413 } 4414 }) 4415 .on('keydown', 'a', function (e) { 4416 var o = null; 4417 switch(e.which) { 4418 case 13: 4419 case 32: 4420 e.type = "mouseup"; 4421 e.preventDefault(); 4422 $(e.currentTarget).trigger(e); 4423 break; 4424 case 37:
4425 if(vakata_context.is_visible) { 4426 vakata_context.element.find(".vakata-context-hover").last().parents("li:eq(0)").find("ul").hide().find(".vakata-context-hover").removeClass("vakata-context-hover").end().end().children('a').focus(); 4427 e.stopImmediatePropagation(); 4428 e.preventDefault(); 4429 } 4430 break; 4431 case 38: 4432 if(vakata_context.is_visible) { 4433 o = vakata_context.element.find("ul:visible").addBack().last().children(".vakata-context-hover").removeClass("vakata-context-hover").prevAll("li:not(.vakata-context-separator)").first(); 4434 if(!o.length) { o = vakata_context.element.find("ul:visible").addBack().last().children("li:not(.vakata-context-separator)").last(); } 4435 o.addClass("vakata-context-hover").children('a').focus(); 4436 e.stopImmediatePropagation(); 4437 e.preventDefault(); 4438 } 4439 break; 4440 case 39: 4441 if(vakata_context.is_visible) { 4442 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(); 4443 e.stopImmediatePropagation(); 4444 e.preventDefault(); 4445 } 4446 break; 4447 case 40: 4448 if(vakata_context.is_visible) { 4449 o = vakata_context.element.find("ul:visible").addBack().last().children(".vakata-context-hover").removeClass("vakata-context-hover").nextAll("li:not(.vakata-context-separator)").first(); 4450 if(!o.length) { o = vakata_context.element.find("ul:visible").addBack().last().children("li:not(.vakata-context-separator)").first(); } 4451 o.addClass("vakata-context-hover").children('a').focus(); 4452 e.stopImmediatePropagation(); 4453 e.preventDefault(); 4454 } 4455 break; 4456 case 27: 4457 $.vakata.context.hide(); 4458 e.preventDefault(); 4459 break; 4460 default: 4461 //console.log(e.which); 4462 break; 4463 } 4464 }) 4465 .on('keydown', function (e) { 4466 e.preventDefault(); 4467 var a = vakata_context.element.find('.vakata-contextmenu-shortcut-' + e.which).parent(); 4468 if(a.parent().not('.vakata-context-disabled')) { 4469 a.mouseup(); 4470 } 4471 }) 4472 .appendTo("body"); 4473 4474 $(document) 4475 .on("mousedown", function (e) { 4476 if(vakata_context.is_visible && !$.contains(vakata_context.element[0], e.target)) { $.vakata.context.hide(); } 4477 }) 4478 .on("context_show.vakata", function (e, data) { 4479 vakata_context.element.find("li:has(ul)").children("a").addClass("vakata-context-parent"); 4480 if(right_to_left) { 4481 vakata_context.element.addClass("vakata-context-rtl").css("direction", "rtl"); 4482 } 4483 // also apply a RTL class? 4484 vakata_context.element.find("ul").hide().end(); 4485 }); 4486 }); 4487 }($)); 4488 // $.jstree.defaults.plugins.push("contextmenu"); 4489 4490/** 4491 * ### Drag'n'drop plugin 4492 * 4493 * Enables dragging and dropping of nodes in the tree, resulting in a move or copy operations. 4494 */ 4495 4496 /** 4497 * stores all defaults for the drag'n'drop plugin 4498 * @name $.jstree.defaults.dnd 4499 * @plugin dnd 4500 */ 4501 $.jstree.defaults.dnd = { 4502 /** 4503 * a boolean indicating if a copy should be possible while dragging (by pressint the meta key or Ctrl). Defaults to `true`. 4504 * @name $.jstree.defaults.dnd.copy 4505 * @plugin dnd 4506 */ 4507 copy : true, 4508 /** 4509 * a number indicating how long a node should remain hovered while dragging to be opened. Defaults to `500`. 4510 * @name $.jstree.defaults.dnd.open_timeout 4511 * @plugin dnd 4512 */ 4513 open_timeout : 500, 4514 /** 4515 * a function invoked each time a node is about to be dragged, invoked in the tree's scope and receives the node as an argument - return `false` to prevent dragging 4516 * @name $.jstree.defaults.dnd.is_draggable 4517 * @plugin dnd 4518 */ 4519 is_draggable : true, 4520 /** 4521 * 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` 4522 * @name $.jstree.defaults.dnd.check_while_dragging 4523 * @plugin dnd 4524 */ 4525 check_while_dragging : true 4526 }; 4527 // TODO: now check works by checking for each node individually, how about max_children, unique, etc? 4528 // TODO: drop somewhere else - maybe demo only? 4529 $.jstree.plugins.dnd = function (options, parent) { 4530 this.bind = function () { 4531 parent.bind.call(this); 4532 4533 this.element 4534 .on('mousedown touchstart', '.jstree-anchor', $.proxy(function (e) { 4535 var obj = this.get_node(e.target), 4536 mlt = this.is_selected(obj) ? this.get_selected().length : 1; 4537 if(obj && obj.id && obj.id !== "#" && (e.which === 1 || e.type === "touchstart") && 4538 (this.settings.dnd.is_draggable === true || ($.isFunction(this.settings.dnd.is_draggable) && this.settings.dnd.is_draggable.call(this, obj))) 4539 ) { 4540 this.element.trigger('mousedown.jstree'); 4541 return $.vakata.dnd.start(e, { 'jstree' : true, 'origin' : this, 'obj' : this.get_node(obj,true), 'nodes' : mlt > 1 ? this.get_selected() : [obj.id] }, '<div id="jstree-dnd" class="jstree-' + this.get_theme() + '"><i class="jstree-icon jstree-er"></i>' + (mlt > 1 ? mlt + ' ' + this.get_string('nodes') : this.get_text(e.currentTarget, true)) + '<ins class="jstree-copy" style="display:none;">+</ins></div>'); 4542 } 4543 }, this)); 4544 }; 4545 }; 4546 4547 $(function() { 4548 // bind only once for all instances 4549 var lastmv = false, 4550 laster = false, 4551 opento = false,
4552 marker = $('<div id="jstree-marker"> </div>').hide().appendTo('body'); 4553 4554 $(document) 4555 .bind('dnd_start.vakata', function (e, data) { 4556 lastmv = false; 4557 }) 4558 .bind('dnd_move.vakata', function (e, data) { 4559 if(opento) { clearTimeout(opento); } 4560 if(!data.data.jstree) { return; } 4561 4562 // if we are hovering the marker image do nothing (can happen on "inside" drags) 4563 if(data.event.target.id && data.event.target.id === 'jstree-marker') { 4564 return; 4565 } 4566 4567 var ins = $.jstree.reference(data.event.target), 4568 ref = false, 4569 off = false, 4570 rel = false, 4571 l, t, h, p, i, o, ok, t1, t2, op, ps, pr; 4572 // if we are over an instance 4573 if(ins && ins._data && ins._data.dnd) { 4574 marker.attr('class', 'jstree-' + ins.get_theme()); 4575 data.helper 4576 .children().attr('class', 'jstree-' + ins.get_theme()) 4577 .find('.jstree-copy:eq(0)')[ data.data.origin && data.data.origin.settings.dnd.copy && (data.event.metaKey || data.event.ctrlKey) ? 'show' : 'hide' ](); 4578 4579 4580 // if are hovering the container itself add a new root node 4581 if( (data.event.target === ins.element[0] || data.event.target === ins.get_container_ul()[0]) && ins.get_container_ul().children().length === 0) { 4582 ok = true; 4583 for(t1 = 0, t2 = data.data.nodes.length; t1 < t2; t1++) { 4584 ok = ok && ins.check( (data.data.origin && 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]), '#', 'last'); 4585 if(!ok) { break; } 4586 } 4587 if(ok) { 4588 lastmv = { 'ins' : ins, 'par' : '#', 'pos' : 'last' }; 4589 marker.hide(); 4590 data.helper.find('.jstree-icon:eq(0)').removeClass('jstree-er').addClass('jstree-ok'); 4591 return; 4592 } 4593 } 4594 else { 4595 // if we are hovering a tree node 4596 ref = $(data.event.target).closest('a'); 4597 if(ref && ref.length && ref.parent().is('.jstree-closed, .jstree-open, .jstree-leaf')) { 4598 off = ref.offset(); 4599 rel = data.event.pageY - off.top; 4600 h = ref.height(); 4601 if(rel < h / 3) { 4602 o = ['b', 'i', 'a']; 4603 } 4604 else if(rel > h - h / 3) { 4605 o = ['a', 'i', 'b']; 4606 } 4607 else { 4608 o = rel > h / 2 ? ['i', 'a', 'b'] : ['i', 'b', 'a']; 4609 } 4610 $.each(o, function (j, v) { 4611 switch(v) { 4612 case 'b': 4613 l = off.left - 6; 4614 t = off.top - 5; 4615 p = ins.get_parent(ref); 4616 i = ref.parent().index(); 4617 break; 4618 case 'i': 4619 l = off.left - 2; 4620 t = off.top - 5 + h / 2 + 1; 4621 p = ref.parent(); 4622 i = 0; 4623 break; 4624 case 'a': 4625 l = off.left - 6; 4626 t = off.top - 5 + h; 4627 p = ins.get_parent(ref); 4628 i = ref.parent().index() + 1; 4629 break; 4630 } 4631 /*! 4632 // TODO: moving inside, but the node is not yet loaded? 4633 // the check will work anyway, as when moving the node will be loaded first and checked again 4634 if(v === 'i' && !ins.is_loaded(p)) { } 4635 */ 4636 ok = true; 4637 for(t1 = 0, t2 = data.data.nodes.length; t1 < t2; t1++) { 4638 op = data.data.origin && data.data.origin.settings.dnd.copy && (data.event.metaKey || data.event.ctrlKey) ? "copy_node" : "move_node"; 4639 ps = i; 4640 if(op === "move_node" && v === 'a' && (data.data.origin && data.data.origin === ins) && p === ins.get_parent(data.data.nodes[t1])) { 4641 pr = ins.get_node(p); 4642 if(ps > $.inArray(data.data.nodes[t1], pr.children)) { 4643 ps -= 1; 4644 } 4645 } 4646 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) ); 4647 if(!ok) { 4648 if(ins && ins.last_error) { laster = ins.last_error(); } 4649 break; 4650 } 4651 } 4652 if(ok) { 4653 if(v === 'i' && ref.parent().is('.jstree-closed') && ins.settings.dnd.open_timeout) { 4654 opento = setTimeout((function (x, z) { return function () { x.open_node(z); }; }(ins, ref)), ins.settings.dnd.open_timeout); 4655 } 4656 lastmv = { 'ins' : ins, 'par' : p, 'pos' : i }; 4657 marker.css({ 'left' : l + 'px', 'top' : t + 'px' }).show(); 4658 data.helper.find('.jstree-icon:eq(0)').removeClass('jstree-er').addClass('jstree-ok'); 4659 laster = {}; 4660 o = true; 4661 return false;
vendor: 11,290 bytes, lines 4662-4969
4662 } 4663 }); 4664 if(o === true) { return; } 4665 } 4666 } 4667 } 4668 lastmv = false; 4669 data.helper.find('.jstree-icon').removeClass('jstree-ok').addClass('jstree-er'); 4670 marker.hide(); 4671 }) 4672 .bind('dnd_scroll.vakata', function (e, data) { 4673 if(!data.data.jstree) { return; } 4674 marker.hide(); 4675 lastmv = false; 4676 data.helper.find('.jstree-icon:eq(0)').removeClass('jstree-ok').addClass('jstree-er'); 4677 }) 4678 .bind('dnd_stop.vakata', function (e, data) { 4679 if(opento) { clearTimeout(opento); } 4680 if(!data.data.jstree) { return; } 4681 marker.hide(); 4682 var i, j, nodes = []; 4683 if(lastmv) { 4684 for(i = 0, j = data.data.nodes.length; i < j; i++) { 4685 nodes[i] = data.data.origin ? data.data.origin.get_node(data.data.nodes[i]) : data.data.nodes[i]; 4686 } 4687 lastmv.ins[ data.data.origin && data.data.origin.settings.dnd.copy && (data.event.metaKey || data.event.ctrlKey) ? 'copy_node' : 'move_node' ](nodes, lastmv.par, lastmv.pos); 4688 } 4689 else { 4690 i = $(data.event.target).closest('.jstree'); 4691 if(i.length && laster && laster.error && laster.error === 'check') { 4692 i = i.jstree(true); 4693 if(i) { 4694 i.settings.core.error.call(this, laster); 4695 } 4696 } 4697 } 4698 }) 4699 .bind('keyup keydown', function (e, data) { 4700 data = $.vakata.dnd._get(); 4701 if(data.data && data.data.jstree) { 4702 data.helper.find('.jstree-copy:eq(0)')[ data.data.origin && data.data.origin.settings.dnd.copy && (e.metaKey || e.ctrlKey) ? 'show' : 'hide' ](); 4703 } 4704 }); 4705 }); 4706 4707 // helpers 4708 (function ($) { 4709 $.fn.vakata_reverse = [].reverse; 4710 // private variable 4711 var vakata_dnd = { 4712 element : false, 4713 is_down : false, 4714 is_drag : false, 4715 helper : false, 4716 helper_w: 0, 4717 data : false, 4718 init_x : 0, 4719 init_y : 0, 4720 scroll_l: 0, 4721 scroll_t: 0, 4722 scroll_e: false, 4723 scroll_i: false 4724 }; 4725 $.vakata.dnd = { 4726 settings : { 4727 scroll_speed : 10, 4728 scroll_proximity : 20, 4729 helper_left : 5, 4730 helper_top : 10, 4731 threshold : 5 4732 }, 4733 _trigger : function (event_name, e) { 4734 var data = $.vakata.dnd._get(); 4735 data.event = e; 4736 $(document).triggerHandler("dnd_" + event_name + ".vakata", data); 4737 }, 4738 _get : function () { 4739 return { 4740 "data" : vakata_dnd.data, 4741 "element" : vakata_dnd.element, 4742 "helper" : vakata_dnd.helper 4743 }; 4744 }, 4745 _clean : function () { 4746 if(vakata_dnd.helper) { vakata_dnd.helper.remove(); } 4747 if(vakata_dnd.scroll_i) { clearInterval(vakata_dnd.scroll_i); vakata_dnd.scroll_i = false; } 4748 vakata_dnd = { 4749 element : false, 4750 is_down : false, 4751 is_drag : false, 4752 helper : false, 4753 helper_w: 0, 4754 data : false, 4755 init_x : 0, 4756 init_y : 0, 4757 scroll_l: 0, 4758 scroll_t: 0, 4759 scroll_e: false, 4760 scroll_i: false 4761 }; 4762 $(document).off("mousemove touchmove", $.vakata.dnd.drag); 4763 $(document).off("mouseup touchend", $.vakata.dnd.stop); 4764 }, 4765 _scroll : function (init_only) { 4766 if(!vakata_dnd.scroll_e || (!vakata_dnd.scroll_l && !vakata_dnd.scroll_t)) { 4767 if(vakata_dnd.scroll_i) { clearInterval(vakata_dnd.scroll_i); vakata_dnd.scroll_i = false; } 4768 return false; 4769 } 4770 if(!vakata_dnd.scroll_i) { 4771 vakata_dnd.scroll_i = setInterval($.vakata.dnd._scroll, 100); 4772 return false; 4773 } 4774 if(init_only === true) { return false; } 4775 4776 var i = vakata_dnd.scroll_e.scrollTop(), 4777 j = vakata_dnd.scroll_e.scrollLeft(); 4778 vakata_dnd.scroll_e.scrollTop(i + vakata_dnd.scroll_t * $.vakata.dnd.settings.scroll_speed); 4779 vakata_dnd.scroll_e.scrollLeft(j + vakata_dnd.scroll_l * $.vakata.dnd.settings.scroll_speed); 4780 if(i !== vakata_dnd.scroll_e.scrollTop() || j !== vakata_dnd.scroll_e.scrollLeft()) { 4781 /** 4782 * triggered on the document when a drag causes an element to scroll 4783 * @event 4784 * @plugin dnd 4785 * @name dnd_scroll.vakata 4786 * @param {Mixed} data any data supplied with the call to $.vakata.dnd.start 4787 * @param {DOM} element the DOM element being dragged 4788 * @param {jQuery} helper the helper shown next to the mouse 4789 * @param {jQuery} event the element that is scrolling 4790 */ 4791 $.vakata.dnd._trigger("scroll", vakata_dnd.scroll_e); 4792 } 4793 }, 4794 start : function (e, data, html) { 4795 if(e.type === "touchstart" && e.originalEvent && e.originalEvent.changedTouches && e.originalEvent.changedTouches[0]) { 4796 e.pageX = e.originalEvent.changedTouches[0].pageX; 4797 e.pageY = e.originalEvent.changedTouches[0].pageY; 4798 e.target = document.elementFromPoint(e.originalEvent.changedTouches[0].pageX - window.pageXOffset, e.originalEvent.changedTouches[0].pageY - window.pageYOffset); 4799 } 4800 if(vakata_dnd.is_drag) { $.vakata.dnd.stop({}); } 4801 try { 4802 e.currentTarget.unselectable = "on"; 4803 e.currentTarget.onselectstart = function() { return false; }; 4804 if(e.currentTarget.style) { e.currentTarget.style.MozUserSelect = "none"; } 4805 } catch(ignore) { } 4806 vakata_dnd.init_x = e.pageX; 4807 vakata_dnd.init_y = e.pageY; 4808 vakata_dnd.data = data; 4809 vakata_dnd.is_down = true; 4810 vakata_dnd.element = e.currentTarget; 4811 if(html !== false) { 4812 vakata_dnd.helper = $("<div id='vakata-dnd'></div>").html(html).css({ 4813 "display" : "block", 4814 "margin" : "0", 4815 "padding" : "0", 4816 "position" : "absolute", 4817 "top" : "-2000px", 4818 "lineHeight" : "16px", 4819 "zIndex" : "10000" 4820 }); 4821 } 4822 $(document).bind("mousemove touchmove", $.vakata.dnd.drag); 4823 $(document).bind("mouseup touchend", $.vakata.dnd.stop); 4824 return false; 4825 }, 4826 drag : function (e) { 4827 if(e.type === "touchmove" && e.originalEvent && e.originalEvent.changedTouches && e.originalEvent.changedTouches[0]) { 4828 e.pageX = e.originalEvent.changedTouches[0].pageX; 4829 e.pageY = e.originalEvent.changedTouches[0].pageY; 4830 e.target = document.elementFromPoint(e.originalEvent.changedTouches[0].pageX - window.pageXOffset, e.originalEvent.changedTouches[0].pageY - window.pageYOffset); 4831 } 4832 if(!vakata_dnd.is_down) { return; } 4833 if(!vakata_dnd.is_drag) { 4834 if( 4835 Math.abs(e.pageX - vakata_dnd.init_x) > $.vakata.dnd.settings.threshold || 4836 Math.abs(e.pageY - vakata_dnd.init_y) > $.vakata.dnd.settings.threshold 4837 ) { 4838 if(vakata_dnd.helper) { 4839 vakata_dnd.helper.appendTo("body"); 4840 vakata_dnd.helper_w = vakata_dnd.helper.outerWidth(); 4841 } 4842 vakata_dnd.is_drag = true; 4843 /** 4844 * triggered on the document when a drag starts 4845 * @event 4846 * @plugin dnd 4847 * @name dnd_start.vakata 4848 * @param {Mixed} data any data supplied with the call to $.vakata.dnd.start 4849 * @param {DOM} element the DOM element being dragged 4850 * @param {jQuery} helper the helper shown next to the mouse 4851 * @param {Object} event the event that caused the start (probably mousemove) 4852 */ 4853 $.vakata.dnd._trigger("start", e); 4854 } 4855 else { return; } 4856 } 4857 4858 var d = false, w = false, 4859 dh = false, wh = false, 4860 dw = false, ww = false, 4861 dt = false, dl = false, 4862 ht = false, hl = false; 4863 4864 vakata_dnd.scroll_t = 0; 4865 vakata_dnd.scroll_l = 0; 4866 vakata_dnd.scroll_e = false; 4867 $(e.target) 4868 .parentsUntil("body").addBack().vakata_reverse() 4869 .filter(function () { 4870 return (/^auto|scroll$/).test($(this).css("overflow")) && 4871 (this.scrollHeight > this.offsetHeight || this.scrollWidth > this.offsetWidth); 4872 }) 4873 .each(function () { 4874 var t = $(this), o = t.offset(); 4875 if(this.scrollHeight > this.offsetHeight) { 4876 if(o.top + t.height() - e.pageY < $.vakata.dnd.settings.scroll_proximity) { vakata_dnd.scroll_t = 1; } 4877 if(e.pageY - o.top < $.vakata.dnd.settings.scroll_proximity) { vakata_dnd.scroll_t = -1; } 4878 } 4879 if(this.scrollWidth > this.offsetWidth) { 4880 if(o.left + t.width() - e.pageX < $.vakata.dnd.settings.scroll_proximity) { vakata_dnd.scroll_l = 1; } 4881 if(e.pageX - o.left < $.vakata.dnd.settings.scroll_proximity) { vakata_dnd.scroll_l = -1; } 4882 } 4883 if(vakata_dnd.scroll_t || vakata_dnd.scroll_l) { 4884 vakata_dnd.scroll_e = $(this); 4885 return false; 4886 } 4887 }); 4888 4889 if(!vakata_dnd.scroll_e) { 4890 d = $(document); w = $(window); 4891 dh = d.height(); wh = w.height(); 4892 dw = d.width(); ww = w.width(); 4893 dt = d.scrollTop(); dl = d.scrollLeft(); 4894 if(dh > wh && e.pageY - dt < $.vakata.dnd.settings.scroll_proximity) { vakata_dnd.scroll_t = -1; } 4895 if(dh > wh && wh - (e.pageY - dt) < $.vakata.dnd.settings.scroll_proximity) { vakata_dnd.scroll_t = 1; } 4896 if(dw > ww && e.pageX - dl < $.vakata.dnd.settings.scroll_proximity) { vakata_dnd.scroll_l = -1; } 4897 if(dw > ww && ww - (e.pageX - dl) < $.vakata.dnd.settings.scroll_proximity) { vakata_dnd.scroll_l = 1; } 4898 if(vakata_dnd.scroll_t || vakata_dnd.scroll_l) { 4899 vakata_dnd.scroll_e = d; 4900 } 4901 } 4902 if(vakata_dnd.scroll_e) { $.vakata.dnd._scroll(true); } 4903 4904 if(vakata_dnd.helper) { 4905 ht = parseInt(e.pageY + $.vakata.dnd.settings.helper_top, 10); 4906 hl = parseInt(e.pageX + $.vakata.dnd.settings.helper_left, 10); 4907 if(dh && ht + 25 > dh) { ht = dh - 50; } 4908 if(dw && hl + vakata_dnd.helper_w > dw) { hl = dw - (vakata_dnd.helper_w + 2); } 4909 vakata_dnd.helper.css({ 4910 left : hl + "px", 4911 top : ht + "px" 4912 }); 4913 } 4914 /** 4915 * triggered on the document when a drag is in progress 4916 * @event 4917 * @plugin dnd 4918 * @name dnd_move.vakata 4919 * @param {Mixed} data any data supplied with the call to $.vakata.dnd.start 4920 * @param {DOM} element the DOM element being dragged 4921 * @param {jQuery} helper the helper shown next to the mouse 4922 * @param {Object} event the event that caused this to trigger (most likely mousemove) 4923 */ 4924 $.vakata.dnd._trigger("move", e); 4925 }, 4926 stop : function (e) { 4927 if(e.type === "touchend" && e.originalEvent && e.originalEvent.changedTouches && e.originalEvent.changedTouches[0]) { 4928 e.pageX = e.originalEvent.changedTouches[0].pageX; 4929 e.pageY = e.originalEvent.changedTouches[0].pageY; 4930 e.target = document.elementFromPoint(e.originalEvent.changedTouches[0].pageX - window.pageXOffset, e.originalEvent.changedTouches[0].pageY - window.pageYOffset); 4931 } 4932 if(vakata_dnd.is_drag) { 4933 /** 4934 * triggered on the document when a drag stops (the dragged element is dropped) 4935 * @event 4936 * @plugin dnd 4937 * @name dnd_stop.vakata 4938 * @param {Mixed} data any data supplied with the call to $.vakata.dnd.start 4939 * @param {DOM} element the DOM element being dragged 4940 * @param {jQuery} helper the helper shown next to the mouse 4941 * @param {Object} event the event that caused the stop 4942 */ 4943 $.vakata.dnd._trigger("stop", e); 4944 } 4945 $.vakata.dnd._clean(); 4946 } 4947 }; 4948 }(jQuery)); 4949 4950 // include the dnd plugin by default 4951 // $.jstree.defaults.plugins.push("dnd"); 4952 4953 4954/** 4955 * ### Search plugin 4956 * 4957 * Adds search functionality to jsTree. 4958 */ 4959 4960 /** 4961 * stores all defaults for the search plugin 4962 * @name $.jstree.defaults.search 4963 * @plugin search 4964 */ 4965 $.jstree.defaults.search = { 4966 /** 4967 * a jQuery-like AJAX config, which jstree uses if a server should be queried for results. 4968 * 4969 * A `str` (which is the search string) parameter will be added with the request. The expected result is a JSON array with
4969nodes that need to be opened so that matching nodes will be revealed. 4970 * Leave this setting as `false` to not query the server. 4971 * @name $.jstree.defaults.search.ajax 4972 * @plugin search 4973 */ 4974 ajax : false, 4975 /** 4976 * Indicates if the search should be fuzzy or not (should `chnd3` match `child node 3`). Default is `true`. 4977 * @name $.jstree.defaults.search.fuzzy 4978 * @plugin search 4979 */ 4980 fuzzy : true, 4981 /** 4982 * Indicates if the search should be case sensitive. Default is `false`. 4983 * @name $.jstree.defaults.search.case_sensitive 4984 * @plugin search 4985 */ 4986 case_sensitive : false, 4987 /** 4988 * Indicates if the tree should be filtered to show only matching nodes (keep in mind this can be a heavy on large trees in old browsers). Default is `false`. 4989 * @name $.jstree.defaults.search.show_only_matches 4990 * @plugin search 4991 */ 4992 show_only_matches : false, 4993 /** 4994 * 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`. 4995 * @name $.jstree.defaults.search.close_opened_onclear 4996 * @plugin search 4997 */ 4998 close_opened_onclear : true 4999 }; 5000 5001 $.jstree.plugins.search = function (options, parent) { 5002 this.bind = function () { 5003 parent.bind.call(this); 5004 5005 this._data.search.str = ""; 5006 this._data.search.dom = $(); 5007 this._data.search.res = []; 5008 this._data.search.opn = []; 5009 this._data.search.sln = null; 5010 5011 if(this.settings.search.show_only_matches) { 5012 this.element 5013 .on("search.jstree", function (e, data) { 5014 if(data.nodes.length) { 5015 $(this).find("li").hide().filter('.jstree-last').filter(function() { return this.nextSibling; }).removeClass('jstree-last'); 5016 data.nodes.parentsUntil(".jstree").addBack().show() 5017 .filter("ul").each(function () { $(this).children("li:visible").eq(-1).addClass("jstree-last"); }); 5018 } 5019 }) 5020 .on("clear_search.jstree", function (e, data) { 5021 if(data.nodes.length) { 5022 $(this).find("li").css("display","").filter('.jstree-last').filter(function() { return this.nextSibling; }).removeClass('jstree-last'); 5023 } 5024 }); 5025 } 5026 }; 5027 /** 5028 * used to search the tree nodes for a given string 5029 * @name search(str [, skip_async]) 5030 * @param {String} str the search string 5031 * @param {Boolean} skip_async if set to true server will not be queried even if configured 5032 * @plugin search 5033 * @trigger search.jstree 5034 */ 5035 this.search = function (str, skip_async) { 5036 if(str === false || $.trim(str) === "") { 5037 return this.clear_search(); 5038 } 5039 var s = this.settings.search, 5040 a = s.ajax ? $.extend({}, s.ajax) : false, 5041 f = null, 5042 r = [], 5043 p = [], i, j; 5044 if(this._data.search.res.length) { 5045 this.clear_search(); 5046 } 5047 if(!skip_async && a !== false) { 5048 if(!a.data) { a.data = {}; } 5049 a.data.str = str; 5050 return $.ajax(a) 5051 .fail($.proxy(function () { 5052 this._data.core.last_error = { 'error' : 'ajax', 'plugin' : 'search', 'id' : 'search_01', 'reason' : 'Could not load search parents', 'data' : JSON.stringify(a) }; 5053 this.settings.core.error.call(this, this._data.core.last_error); 5054 }, this)) 5055 .done($.proxy(function (d) { 5056 if(d && d.d) { d = d.d; } 5057 this._data.search.sln = !$.isArray(d) ? [] : d; 5058 this._search_load(str); 5059 }, this)); 5060 } 5061 this._data.search.str = str; 5062 this._data.search.dom = $(); 5063 this._data.search.res = []; 5064 this._data.search.opn = []; 5065 5066 f = new $.vakata.search(str, true, { caseSensitive : s.case_sensitive, fuzzy : s.fuzzy }); 5067 5068 $.each(this._model.data, function (i, v) { 5069 if(v.text && f.search(v.text).isMatch) { 5070 r.push(i); 5071 p = p.concat(v.parents); 5072 } 5073 }); 5074 if(r.length) { 5075 p = $.vakata.array_unique(p); 5076 this._search_open(p); 5077 for(i = 0, j = r.length; i < j; i++) { 5078 f = this.get_node(r[i], true); 5079 if(f) { 5080 this._data.search.dom = this._data.search.dom.add(f); 5081 } 5082 } 5083 this._data.search.res = r; 5084 this._data.search.dom.children(".jstree-anchor").addClass('jstree-search'); 5085 } 5086 /** 5087 * triggered after search is complete 5088 * @event 5089 * @name search.jstree 5090 * @param {jQuery}
vendor: 20,190 bytes, lines 5090-5679
5090 nodes a jQuery collection of matching nodes 5091 * @param {String} str the search string 5092 * @param {Array} res a collection of objects represeing the matching nodes 5093 * @plugin search 5094 */ 5095 this.trigger('search', { nodes : this._data.search.dom, str : str, res : this._data.search.res }); 5096 }; 5097 /** 5098 * used to clear the last search (removes classes and shows all nodes if filtering is on) 5099 * @name clear_search() 5100 * @plugin search 5101 * @trigger clear_search.jstree 5102 */ 5103 this.clear_search = function () { 5104 this._data.search.dom.children(".jstree-anchor").removeClass("jstree-search"); 5105 if(this.settings.search.close_opened_onclear) { 5106 this.close_node(this._data.search.opn, 0); 5107 } 5108 /** 5109 * triggered after search is complete 5110 * @event 5111 * @name clear_search.jstree 5112 * @param {jQuery} nodes a jQuery collection of matching nodes (the result from the last search) 5113 * @param {String} str the search string (the last search string) 5114 * @param {Array} res a collection of objects represeing the matching nodes (the result from the last search) 5115 * @plugin search 5116 */ 5117 this.trigger('clear_search', { 'nodes' : this._data.search.dom, str : this._data.search.str, res : this._data.search.res }); 5118 this._data.search.str = ""; 5119 this._data.search.res = []; 5120 this._data.search.opn = []; 5121 this._data.search.dom = $(); 5122 }; 5123 /** 5124 * opens nodes that need to be opened to reveal the search results. Used only internally. 5125 * @private 5126 * @name _search_open(d) 5127 * @param {Array} d an array of node IDs 5128 * @plugin search 5129 */ 5130 this._search_open = function (d) { 5131 var t = this; 5132 $.each(d.concat([]), function (i, v) { 5133 v = document.getElementById(v); 5134 if(v) { 5135 if(t.is_closed(v)) { 5136 t._data.search.opn.push(v.id); 5137 t.open_node(v, function () { t._search_open(d); }, 0); 5138 } 5139 } 5140 }); 5141 }; 5142 /** 5143 * loads nodes that need to be opened to reveal the search results. Used only internally. 5144 * @private 5145 * @name _search_load(d, str) 5146 * @param {String} str the search string 5147 * @plugin search 5148 */ 5149 this._search_load = function (str) { 5150 var res = true, 5151 t = this, 5152 m = t._model.data; 5153 if($.isArray(this._data.search.sln)) { 5154 if(!this._data.search.sln.length) { 5155 this._data.search.sln = null; 5156 this.search(str, true); 5157 } 5158 else { 5159 $.each(this._data.search.sln, function (i, v) { 5160 if(m[v]) { 5161 $.vakata.array_remove_item(t._data.search.sln, v); 5162 if(!m[v].state.loaded) { 5163 t.load_node(v, function (o, s) { if(s) { t._search_load(str); } }); 5164 res = false; 5165 } 5166 } 5167 }); 5168 if(res) { 5169 this._data.search.sln = []; 5170 this._search_load(str); 5171 } 5172 } 5173 } 5174 }; 5175 }; 5176 5177 // helpers 5178 (function ($) { 5179 // from http://kiro.me/projects/fuse.html 5180 $.vakata.search = function(pattern, txt, options) { 5181 options = options || {}; 5182 if(options.fuzzy !== false) { 5183 options.fuzzy = true; 5184 } 5185 pattern = options.caseSensitive ? pattern : pattern.toLowerCase(); 5186 var MATCH_LOCATION = options.location || 0, 5187 MATCH_DISTANCE = options.distance || 100, 5188 MATCH_THRESHOLD = options.threshold || 0.6, 5189 patternLen = pattern.length, 5190 matchmask, pattern_alphabet, match_bitapScore, search; 5191 if(patternLen > 32) { 5192 options.fuzzy = false; 5193 } 5194 if(options.fuzzy) { 5195 matchmask = 1 << (patternLen - 1); 5196 pattern_alphabet = (function () { 5197 var mask = {}, 5198 i = 0; 5199 for (i = 0; i < patternLen; i++) { 5200 mask[pattern.charAt(i)] = 0; 5201 } 5202 for (i = 0; i < patternLen; i++) { 5203 mask[pattern.charAt(i)] |= 1 << (patternLen - i - 1); 5204 } 5205 return mask; 5206 }()); 5207 match_bitapScore = function (e, x) { 5208 var accuracy = e / patternLen, 5209 proximity = Math.abs(MATCH_LOCATION - x); 5210 if(!MATCH_DISTANCE) { 5211 return proximity ? 1.0 : accuracy; 5212 } 5213 return accuracy + (proximity / MATCH_DISTANCE); 5214 }; 5215 } 5216 search = function (text) { 5217 text = options.caseSensitive ? text : text.toLowerCase(); 5218 if(pattern === text || text.indexOf(pattern) !== -1) { 5219 return { 5220 isMatch: true, 5221 score: 0 5222 }; 5223 } 5224 if(!options.fuzzy) { 5225 return { 5226 isMatch: false, 5227 score: 1 5228 }; 5229 } 5230 var i, j, 5231 textLen = text.length, 5232 scoreThreshold = MATCH_THRESHOLD, 5233 bestLoc = text.indexOf(pattern, MATCH_LOCATION), 5234 binMin, binMid, 5235 binMax = patternLen + textLen, 5236 lastRd, start, finish, rd, charMatch, 5237 score = 1, 5238 locations = []; 5239 if (bestLoc !== -1) { 5240 scoreThreshold = Math.min(match_bitapScore(0, bestLoc), scoreThreshold); 5241 bestLoc = text.lastIndexOf(pattern, MATCH_LOCATION + patternLen); 5242 if (bestLoc !== -1) { 5243 scoreThreshold = Math.min(match_bitapScore(0, bestLoc), scoreThreshold); 5244 } 5245 } 5246 bestLoc = -1; 5247 for (i = 0; i < patternLen; i++) { 5248 binMin = 0; 5249 binMid = binMax; 5250 while (binMin < binMid) { 5251 if (match_bitapScore(i, MATCH_LOCATION + binMid) <= scoreThreshold) { 5252 binMin = binMid; 5253 } else { 5254 binMax = binMid; 5255 } 5256 binMid = Math.floor((binMax - binMin) / 2 + binMin); 5257 } 5258 binMax = binMid; 5259 start = Math.max(1, MATCH_LOCATION - binMid + 1); 5260 finish = Math.min(MATCH_LOCATION + binMid, textLen) + patternLen; 5261 rd = new Array(finish + 2); 5262 rd[finish + 1] = (1 << i) - 1; 5263 for (j = finish; j >= start; j--) { 5264 charMatch = pattern_alphabet[text.charAt(j - 1)]; 5265 if (i === 0) { 5266 rd[j] = ((rd[j + 1] << 1) | 1) & charMatch; 5267 } else { 5268 rd[j] = ((rd[j + 1] << 1) | 1) & charMatch | (((lastRd[j + 1] | lastRd[j]) << 1) | 1) | lastRd[j + 1]; 5269 } 5270 if (rd[j] & matchmask) { 5271 score = match_bitapScore(i, j - 1); 5272 if (score <= scoreThreshold) { 5273 scoreThreshold = score; 5274 bestLoc = j - 1; 5275 locations.push(bestLoc); 5276 if (bestLoc > MATCH_LOCATION) { 5277 start = Math.max(1, 2 * MATCH_LOCATION - bestLoc); 5278 } else { 5279 break; 5280 } 5281 } 5282 } 5283 } 5284 if (match_bitapScore(i + 1, MATCH_LOCATION) > scoreThreshold) { 5285 break; 5286 } 5287 lastRd = rd; 5288 } 5289 return { 5290 isMatch: bestLoc >= 0, 5291 score: score 5292 }; 5293 }; 5294 return txt === true ? { 'search' : search } : search(txt); 5295 }; 5296 }(jQuery)); 5297 5298 // include the search plugin by default 5299 // $.jstree.defaults.plugins.push("search"); 5300 5301/** 5302 * ### Sort plugin 5303 * 5304 * Autmatically sorts all siblings in the tree according to a sorting function. 5305 */ 5306 5307 /** 5308 * the settings function used to sort the nodes. 5309 * It is executed in the tree's context, accepts two nodes as arguments and should return `1` or `-1`. 5310 * @name $.jstree.defaults.sort 5311 * @plugin sort 5312 */ 5313 $.jstree.defaults.sort = function (a, b) { 5314 //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); 5315 return this.get_text(a) > this.get_text(b) ? 1 : -1; 5316 }; 5317 $.jstree.plugins.sort = function (options, parent) { 5318 this.bind = function () { 5319 parent.bind.call(this); 5320 this.element 5321 .on("model.jstree", $.proxy(function (e, data) { 5322 this.sort(data.parent, true); 5323 }, this)) 5324 .on("rename_node.jstree create_node.jstree", $.proxy(function (e, data) { 5325 this.sort(data.parent || data.node.parent, false); 5326 this.redraw_node(data.parent || data.node.parent, true); 5327 }, this)) 5328 .on("move_node.jstree copy_node.jstree", $.proxy(function (e, data) { 5329 this.sort(data.parent, false); 5330 this.redraw_node(data.parent, true); 5331 }, this)); 5332 }; 5333 /** 5334 * used to sort a node's children 5335 * @private 5336 * @name sort(obj [, deep]) 5337 * @param {mixed} obj the node 5338 * @param {Boolean} deep if set to `true` nodes are sorted recursively. 5339 * @plugin sort 5340 * @trigger search.jstree 5341 */ 5342 this.sort = function (obj, deep) { 5343 var i, j; 5344 obj = this.get_node(obj); 5345 if(obj && obj.children && obj.children.length) { 5346 obj.children.sort($.proxy(this.settings.sort, this)); 5347 if(deep) { 5348 for(i = 0, j = obj.children_d.length; i < j; i++) { 5349 this.sort(obj.children_d[i], false); 5350 } 5351 } 5352 } 5353 }; 5354 }; 5355 5356 // include the sort plugin by default 5357 // $.jstree.defaults.plugins.push("sort"); 5358 5359/** 5360 * ### State plugin 5361 * 5362 * Saves the state of the tree (selected nodes, opened nodes) on the user's computer using available options (localStorage, cookies, etc) 5363 */ 5364 5365 var to = false; 5366 /** 5367 * stores all defaults for the state plugin 5368 * @name $.jstree.defaults.state 5369 * @plugin state 5370 */ 5371 $.jstree.defaults.state = { 5372 /** 5373 * A string for the key to use when saving the current tree (change if using multiple trees in your project). Defaults to `jstree`. 5374 * @name $.jstree.defaults.state.key 5375 * @plugin state 5376 */ 5377 key : 'jstree', 5378 /** 5379 * A space separated list of events that trigger a state save. Defaults to `changed.jstree open_node.jstree close_node.jstree`. 5380 * @name $.jstree.defaults.state.events 5381 * @plugin state 5382 */ 5383 events : 'changed.jstree open_node.jstree close_node.jstree', 5384 /** 5385 * Time in milliseconds after which the state will expire. Defaults to 'false' meaning - no expire. 5386 * @name $.jstree.defaults.state.ttl 5387 * @plugin state 5388 */ 5389 ttl : false, 5390 /** 5391 * 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. 5392 * @name $.jstree.defaults.state.filter 5393 * @plugin state 5394 */ 5395 filter : false 5396 }; 5397 $.jstree.plugins.state = function (options, parent) { 5398 this.bind = function () { 5399 parent.bind.call(this); 5400 var bind = $.proxy(function () { 5401 this.element.on(this.settings.state.events, $.proxy(function () { 5402 if(to) { clearTimeout(to); } 5403 to = setTimeout($.proxy(function () { this.save_state(); }, this), 100); 5404 }, this)); 5405 }, this); 5406 this.element 5407 .on("ready.jstree", $.proxy(function (e, data) { 5408 this.element.one("restore_state.jstree", bind); 5409 if(!this.restore_state()) { bind(); } 5410 }, this)); 5411 }; 5412 /** 5413 * save the state 5414 * @name save_state() 5415 * @plugin state 5416 */ 5417 this.save_state = function () { 5418 var st = { 'state' : this.get_state(), 'ttl' : this.settings.state.ttl, 'sec' : +(new Date()) }; 5419 $.vakata.storage.set(this.settings.state.key, JSON.stringify(st)); 5420 }; 5421 /** 5422 * restore the state from the user's computer 5423 * @name restore_state() 5424 * @plugin state 5425 */ 5426 this.restore_state = function () { 5427 var k = $.vakata.storage.get(this.settings.state.key); 5428 if(!!k) { try { k = JSON.parse(k); } catch(ex) { return false; } } 5429 if(!!k && k.ttl && k.sec && +(new Date()) - k.sec > k.ttl) { return false; } 5430 if(!!k && k.state) { k = k.state; } 5431 if(!!k && $.isFunction(this.settings.state.filter)) { k = this.settings.state.filter.call(this, k); } 5432 if(!!k) { 5433 this.element.one("set_state.jstree", function (e, data) { data.instance.trigger('restore_state', { 'state' : $.extend(true, {}, k) }); }); 5434 this.set_state(k); 5435 return true; 5436 } 5437 return false; 5438 }; 5439 /** 5440 * clear the state on the user's computer 5441 * @name clear_state() 5442 * @plugin state 5443 */ 5444 this.clear_state = function () { 5445 return $.vakata.storage.del(this.settings.state.key); 5446 }; 5447 }; 5448 5449 (function ($, undefined) { 5450 $.vakata.storage = { 5451 // simply specifying the functions in FF throws an error 5452 set : function (key, val) { return window.localStorage.setItem(key, val); }, 5453 get : function (key) { return window.localStorage.getItem(key); }, 5454 del : function (key) { return window.localStorage.removeItem(key); } 5455 }; 5456 }(jQuery)); 5457 5458 // include the state plugin by default 5459 // $.jstree.defaults.plugins.push("state"); 5460 5461/** 5462 * ### Types plugin 5463 * 5464 * 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. 5465 */ 5466 5467 /** 5468 * 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). 5469 * 5470 * * `max_children` the maximum number of immediate children this node type can have. Do not specify or set to `-1` for unlimited. 5471 * * `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. 5472 * * `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. 5473 * * `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. 5474 * 5475 * There are two predefined types: 5476 * 5477 * * `#` represents the root of the tree, for example `max_children` would control the maximum number of root nodes. 5478 * * `default` represents the default node - any settings here will be applied to all nodes that do not have a type specified. 5479 * 5480 * @name $.jstree.defaults.types 5481 * @plugin types 5482 */ 5483 $.jstree.defaults.types = { 5484 '#' : {}, 5485 'default' : {} 5486 }; 5487 5488 $.jstree.plugins.types = function (options, parent) { 5489 this.init = function (el, options) { 5490 var i, j; 5491 if(options && options.types && options.types['default']) { 5492 for(i in options.types) { 5493 if(i !== "default" && i !== "#" && options.types.hasOwnProperty(i)) { 5494 for(j in options.types['default']) { 5495 if(options.types['default'].hasOwnProperty(j) && options.types[i][j] === undefined) { 5496 options.types[i][j] = options.types['default'][j]; 5497 } 5498 } 5499 } 5500 } 5501 } 5502 parent.init.call(this, el, options); 5503 this._model.data['#'].type = '#'; 5504 }; 5505 this.bind = function () { 5506 parent.bind.call(this); 5507 this.element 5508 .on('model.jstree', $.proxy(function (e, data) { 5509 var m = this._model.data, 5510 dpc = data.nodes, 5511 t = this.settings.types, 5512 i, j, c = 'default'; 5513 for(i = 0, j = dpc.length; i < j; i++) { 5514 c = 'default'; 5515 if(m[dpc[i]].original && m[dpc[i]].original.type && t[m[dpc[i]].original.type]) { 5516 c = m[dpc[i]].original.type; 5517 } 5518 if(m[dpc[i]].data && m[dpc[i]].data.jstree && m[dpc[i]].data.jstree.type && t[m[dpc[i]].data.jstree.type]) { 5519 c = m[dpc[i]].data.jstree.type; 5520 } 5521 m[dpc[i]].type = c; 5522 if(m[dpc[i]].icon === true && t[c].icon !== undefined) { 5523 m[dpc[i]].icon = t[c].icon; 5524 } 5525 } 5526 }, this)); 5527 }; 5528 this.get_json = function (obj, options, flat) { 5529 var i, j, 5530 m = this._model.data, 5531 opt = options ? $.extend(true, {}, options, {no_id:false}) : {}, 5532 tmp = parent.get_json.call(this, obj, opt, flat); 5533 if(tmp === false) { return false; } 5534 if($.isArray(tmp)) { 5535 for(i = 0, j = tmp.length; i < j; i++) { 5536 tmp[i].type = tmp[i].id && m[tmp[i].id] && m[tmp[i].id].type ? m[tmp[i].id].type : "default"; 5537 if(options && options.no_id) { 5538 delete tmp[i].id; 5539 if(tmp[i].li_attr && tmp[i].li_attr.id) { 5540 delete tmp[i].li_attr.id; 5541 } 5542 } 5543 } 5544 } 5545 else { 5546 tmp.type = tmp.id && m[tmp.id] && m[tmp.id].type ? m[tmp.id].type : "default"; 5547 if(options && options.no_id) { 5548 tmp = this._delete_ids(tmp); 5549 } 5550 } 5551 return tmp; 5552 }; 5553 this._delete_ids = function (tmp) { 5554 if($.isArray(tmp)) { 5555 for(var i = 0, j = tmp.length; i < j; i++) { 5556 tmp[i] = this._delete_ids(tmp[i]); 5557 } 5558 return tmp; 5559 } 5560 delete tmp.id; 5561 if(tmp.li_attr && tmp.li_attr.id) { 5562 delete tmp.li_attr.id; 5563 } 5564 if(tmp.children && $.isArray(tmp.children)) { 5565 tmp.children = this._delete_ids(tmp.children); 5566 } 5567 return tmp; 5568 }; 5569 this.check = function (chk, obj, par, pos) { 5570 if(parent.check.call(this, chk, obj, par, pos) === false) { return false; } 5571 obj = obj && obj.id ? obj : this.get_node(obj); 5572 par = par && par.id ? par : this.get_node(par); 5573 var m = obj && obj.id ? $.jstree.reference(obj.id) : null, tmp, d, i, j; 5574 m = m && m._model && m._model.data ? m._model.data : null; 5575 switch(chk) { 5576 case "create_node": 5577 case "move_node": 5578 case "copy_node": 5579 if(chk !== 'move_node' || $.inArray(obj.id, par.children) === -1) { 5580 tmp = this.get_rules(par); 5581 if(tmp.max_children !== undefined && tmp.max_children !== -1 && tmp.max_children === par.children.length) { 5582 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 }) }; 5583 return false; 5584 } 5585 if(tmp.valid_children !== undefined && tmp.valid_children !== -1 && $.inArray(obj.type, tmp.valid_children) === -1) { 5586 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 }) }; 5587 return false; 5588 } 5589 if(m && obj.children_d && obj.parents) { 5590 d = 0; 5591 for(i = 0, j = obj.children_d.length; i < j; i++) { 5592 d = Math.max(d, m[obj.children_d[i]].parents.length); 5593 } 5594 d = d - obj.parents.length + 1; 5595 } 5596 if(d <= 0 || d === undefined) { d = 1; } 5597 do { 5598 if(tmp.max_depth !== undefined && tmp.max_depth !== -1 && tmp.max_depth < d) { 5599 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 }) }; 5600 return false; 5601 } 5602 par = this.get_node(par.parent); 5603 tmp = this.get_rules(par); 5604 d++; 5605 } while(par); 5606 } 5607 break; 5608 } 5609 return true; 5610 }; 5611 /** 5612 * used to retrieve the type settings object for a node 5613 * @name get_rules(obj) 5614 * @param {mixed} obj the node to find the rules for 5615 * @return {Object} 5616 * @plugin types 5617 */ 5618 this.get_rules = function (obj) { 5619 obj = this.get_node(obj); 5620 if(!obj) { return false; } 5621 var tmp = this.get_type(obj, true); 5622 if(tmp.max_depth === undefined) { tmp.max_depth = -1; } 5623 if(tmp.max_children === undefined) { tmp.max_children = -1; } 5624 if(tmp.valid_children === undefined) { tmp.valid_children = -1; } 5625 return tmp; 5626 }; 5627 /** 5628 * used to retrieve the type string or settings object for a node 5629 * @name get_type(obj [, rules]) 5630 * @param {mixed} obj the node to find the rules for 5631 * @param {Boolean} rules if set to `true` instead of a string the settings object will be returned 5632 * @return {String|Object} 5633 * @plugin types 5634 */ 5635 this.get_type = function (obj, rules) { 5636 obj = this.get_node(obj); 5637 return (!obj) ? false : ( rules ? $.extend({ 'type' : obj.type }, this.settings.types[obj.type]) : obj.type); 5638 }; 5639 /** 5640 * used to change a node's type 5641 * @name set_type(obj, type) 5642 * @param {mixed} obj the node to change 5643 * @param {String} type the new type 5644 * @plugin types 5645 */ 5646 this.set_type = function (obj, type) { 5647 var t, t1, t2, old_type, old_icon; 5648 if($.isArray(obj)) { 5649 obj = obj.slice(); 5650 for(t1 = 0, t2 = obj.length; t1 < t2; t1++) { 5651 this.set_type(obj[t1], type); 5652 } 5653 return true; 5654 } 5655 t = this.settings.types; 5656 obj = this.get_node(obj); 5657 if(!t[type] || !obj) { return false; } 5658 old_type = obj.type; 5659 old_icon = this.get_icon(obj); 5660 obj.type = type; 5661 if(old_icon === true || (t[old_type] && t[old_type].icon && old_icon === t[old_type].icon)) { 5662 this.set_icon(obj, t[type].icon !== undefined ? t[type].icon : true); 5663 } 5664 return true; 5665 }; 5666 }; 5667 // include the types plugin by default 5668 // $.jstree.defaults.plugins.push("types"); 5669 5670/** 5671 * ### Unique plugin 5672 * 5673 * Enforces that no nodes with the same name can coexist as siblings. 5674 */ 5675 5676 $.jstree.plugins.unique = function (options, parent) { 5677 this.check = function (chk, obj, par, pos) { 5678 if(parent.check.call(this, chk, obj, par, pos) === false) { return false; } 5679 obj = obj && obj.id ? obj : this.get_node(obj);
5680 par = par && par.id ? par : this.get_node(par); 5681 if(!par || !par.children) { return true; } 5682 var n = chk === "rename_node" ? pos : obj.text, 5683 c = [], 5684 m = this._model.data, i, j; 5685 for(i = 0, j = par.children.length; i < j; i++) { 5686 c.push(m[par.children[i]].text); 5687 } 5688 switch(chk) { 5689 case "delete_node": 5690 return true; 5691 case "rename_node": 5692 case "copy_node": 5693 i = ($.inArray(n, c) === -1); 5694 if(!i) { 5695 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 }) }; 5696 } 5697 return i; 5698 case "move_node": 5699 i = (obj.parent === par.id || $.inArray(n, c) === -1); 5700 if(!i) { 5701 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 }) }; 5702 } 5703 return i; 5704 } 5705 return true; 5706 }; 5707 }; 5708 5709 // include the unique plugin by default 5710 // $.jstree.defaults.plugins.push("unique"); 5711 5712 5713/** 5714 * ### Wholerow plugin 5715 * 5716 * Makes each node appear block level. Making selection easier. May cause slow down for large trees in old browsers. 5717 */ 5718 5719 var div = document.createElement('DIV'); 5720 div.setAttribute('unselectable','on'); 5721 div.className = 'jstree-wholerow'; 5722 div.innerHTML = ' '; 5723 $.jstree.plugins.wholerow = function (options, parent) { 5724 this.bind = function () { 5725 parent.bind.call(this); 5726 5727 this.element 5728 .on('loading', $.proxy(function () { 5729 div.style.height = this._data.core.li_height + 'px'; 5730 }, this)) 5731 .on('ready.jstree set_state.jstree', $.proxy(function () { 5732 this.hide_dots(); 5733 }, this)) 5734 .on("ready.jstree", $.proxy(function () { 5735 this.get_container_ul().addClass('jstree-wholerow-ul'); 5736 }, this)) 5737 .on("deselect_all.jstree", $.proxy(function (e, data) { 5738 this.element.find('.jstree-wholerow-clicked').removeClass('jstree-wholerow-clicked'); 5739 }, this)) 5740 .on("changed.jstree", $.proxy(function (e, data) { 5741 this.element.find('.jstree-wholerow-clicked').removeClass('jstree-wholerow-clicked'); 5742 var tmp = false, i, j; 5743 for(i = 0, j = data.selected.length; i < j; i++) { 5744 tmp = this.get_node(data.selected[i], true); 5745 if(tmp && tmp.length) { 5746 tmp.children('.jstree-wholerow').addClass('jstree-wholerow-clicked'); 5747 } 5748 } 5749 }, this)) 5750 .on("open_node.jstree", $.proxy(function (e, data) { 5751 this.get_node(data.node, true).find('.jstree-clicked').parent().children('.jstree-wholerow').addClass('jstree-wholerow-clicked'); 5752 }, this)) 5753 .on("hover_node.jstree dehover_node.jstree", $.proxy(function (e, data) { 5754 this.get_node(data.node, true).children('.jstree-wholerow')[e.type === "hover_node"?"addClass":"removeClass"]('jstree-wholerow-hovered'); 5755 }, this)) 5756 .on("contextmenu.jstree", ".jstree-wholerow", $.proxy(function (e) { 5757 e.preventDefault(); 5758 $(e.currentTarget).closest("li").children("a:eq(0)").trigger('contextmenu',e); 5759 }, this)) 5760 .on("click.jstree", ".jstree-wholerow", function (e) { 5761 e.stopImmediatePropagation(); 5762 var tmp = $.Event('click', { metaKey : e.metaKey, ctrlKey : e.ctrlKey, altKey : e.altKey, shiftKey : e.shiftKey }); 5763 $(e.currentTarget).closest("li").children("a:eq(0)").trigger(tmp).focus(); 5764 }) 5765 .on("click.jstree", ".jstree-leaf > .jstree-ocl", $.proxy(function (e) { 5766 e.stopImmediatePropagation(); 5767 var tmp = $.Event('click', { metaKey : e.metaKey, ctrlKey : e.ctrlKey, altKey : e.altKey, shiftKey : e.shiftKey }); 5768 $(e.currentTarget).closest("li").children("a:eq(0)").trigger(tmp).focus(); 5769 }, this)) 5770 .on("mouseover.jstree", ".jstree-wholerow, .jstree-icon", $.proxy(function (e) { 5771 e.stopImmediatePropagation(); 5772 this.hover_node(e.currentTarget); 5773 return false;
vendor: 803 bytes, lines 5774-5799
5774 }, this)) 5775 .on("mouseleave.jstree", ".jstree-node", $.proxy(function (e) { 5776 this.dehover_node(e.currentTarget); 5777 }, this)); 5778 }; 5779 this.teardown = function () { 5780 if(this.settings.wholerow) { 5781 this.element.find(".jstree-wholerow").remove(); 5782 } 5783 parent.teardown.call(this); 5784 }; 5785 this.redraw_node = function(obj, deep, callback) { 5786 obj = parent.redraw_node.call(this, obj, deep, callback); 5787 if(obj) { 5788 var tmp = div.cloneNode(true); 5789 //tmp.style.height = this._data.core.li_height + 'px'; 5790 if($.inArray(obj.id, this._data.core.selected) !== -1) { tmp.className += ' jstree-wholerow-clicked'; } 5791 obj.insertBefore(tmp, obj.childNodes[0]); 5792 } 5793 return obj; 5794 }; 5795 }; 5796 // include the wholerow plugin by default 5797 // $.jstree.defaults.plugins.push("wholerow"); 5798 5799}));
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.