2015-07-16 06:06:02 -07:00
/*globals jQuery, define, exports, require, window, document, postMessage */
( function ( factory ) {
"use strict" ;
if ( typeof define === 'function' && define . amd ) {
define ( [ 'jquery' ] , factory ) ;
}
else if ( typeof exports === 'object' ) {
factory ( require ( 'jquery' ) ) ;
}
else {
factory ( jQuery ) ;
}
} ( function ( $ , undefined ) {
"use strict" ;
/ * !
* jsTree 3.1 . 1
* http : //jstree.com/
*
* Copyright ( c ) 2014 Ivan Bozhanov ( http : //vakata.com)
*
* Licensed same as jquery - under the terms of the MIT License
* http : //www.opensource.org/licenses/mit-license.php
* /
/ * !
* if using jslint please allow for the jQuery global and use following options :
* jslint : browser : true , ass : true , bitwise : true , continue : true , nomen : true , plusplus : true , regexp : true , unparam : true , todo : true , white : true
* /
// prevent another load? maybe there is a better way?
if ( $ . jstree ) {
return ;
}
/ * *
* # # # jsTree core functionality
* /
// internal variables
var instance _counter = 0 ,
ccp _node = false ,
ccp _mode = false ,
ccp _inst = false ,
themes _loaded = [ ] ,
src = $ ( 'script:last' ) . attr ( 'src' ) ,
document = window . document , // local variable is always faster to access then a global
_node = document . createElement ( 'LI' ) , _temp1 , _temp2 ;
_node . setAttribute ( 'role' , 'treeitem' ) ;
_temp1 = document . createElement ( 'I' ) ;
_temp1 . className = 'jstree-icon jstree-ocl' ;
_temp1 . setAttribute ( 'role' , 'presentation' ) ;
_node . appendChild ( _temp1 ) ;
_temp1 = document . createElement ( 'A' ) ;
_temp1 . className = 'jstree-anchor' ;
_temp1 . setAttribute ( 'href' , '#' ) ;
_temp1 . setAttribute ( 'tabindex' , '-1' ) ;
_temp2 = document . createElement ( 'I' ) ;
_temp2 . className = 'jstree-icon jstree-themeicon' ;
_temp2 . setAttribute ( 'role' , 'presentation' ) ;
_temp1 . appendChild ( _temp2 ) ;
_node . appendChild ( _temp1 ) ;
_temp1 = _temp2 = null ;
/ * *
* holds all jstree related functions and variables , including the actual class and methods to create , access and manipulate instances .
* @ name $ . jstree
* /
$ . jstree = {
/ * *
* specifies the jstree version in use
* @ name $ . jstree . version
* /
version : '3.1.1' ,
/ * *
* holds all the default options used when creating new instances
* @ name $ . jstree . defaults
* /
defaults : {
/ * *
* 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 ` [] `
* @ name $ . jstree . defaults . plugins
* /
plugins : [ ]
} ,
/ * *
* stores all loaded jstree plugins ( used internally )
* @ name $ . jstree . plugins
* /
plugins : { } ,
path : src && src . indexOf ( '/' ) !== - 1 ? src . replace ( /\/[^\/]+$/ , '' ) : '' ,
idregex : /[\\:&!^|()\[\]<>@*'+~#";.,=\- \/${}%?`]/g
} ;
/ * *
* creates a jstree instance
* @ name $ . jstree . create ( el [ , options ] )
* @ param { DOMElement | jQuery | String } el the element to create the instance on , can be jQuery extended or a selector
* @ param { Object } options options for this instance ( extends ` $ .jstree.defaults ` )
* @ return { jsTree } the new instance
* /
$ . jstree . create = function ( el , options ) {
var tmp = new $ . jstree . core ( ++ instance _counter ) ,
opt = options ;
options = $ . extend ( true , { } , $ . jstree . defaults , options ) ;
if ( opt && opt . plugins ) {
options . plugins = opt . plugins ;
}
$ . each ( options . plugins , function ( i , k ) {
if ( i !== 'core' ) {
tmp = tmp . plugin ( k , options [ k ] ) ;
}
} ) ;
$ ( el ) . data ( 'jstree' , tmp ) ;
tmp . init ( el , options ) ;
return tmp ;
} ;
/ * *
* remove all traces of jstree from the DOM and destroy all instances
* @ name $ . jstree . destroy ( )
* /
$ . jstree . destroy = function ( ) {
$ ( '.jstree:jstree' ) . jstree ( 'destroy' ) ;
$ ( document ) . off ( '.jstree' ) ;
} ;
/ * *
* the jstree class constructor , used only internally
* @ private
* @ name $ . jstree . core ( id )
* @ param { Number } id this instance ' s index
* /
$ . jstree . core = function ( id ) {
this . _id = id ;
this . _cnt = 0 ;
this . _wrk = null ;
this . _data = {
core : {
themes : {
name : false ,
dots : false ,
icons : false
} ,
selected : [ ] ,
last _error : { } ,
working : false ,
worker _queue : [ ] ,
focused : null
}
} ;
} ;
/ * *
* get a reference to an existing instance
*
* _ _Examples _ _
*
* // provided a container with an ID of "tree", and a nested node with an ID of "branch"
* // all of there will return the same instance
* $ . jstree . reference ( 'tree' ) ;
* $ . jstree . reference ( '#tree' ) ;
* $ . jstree . reference ( $ ( '#tree' ) ) ;
* $ . jstree . reference ( document . getElementByID ( 'tree' ) ) ;
* $ . jstree . reference ( 'branch' ) ;
* $ . jstree . reference ( '#branch' ) ;
* $ . jstree . reference ( $ ( '#branch' ) ) ;
* $ . jstree . reference ( document . getElementByID ( 'branch' ) ) ;
*
* @ name $ . jstree . reference ( needle )
* @ param { DOMElement | jQuery | String } needle
* @ return { jsTree | null } the instance or ` null ` if not found
* /
$ . jstree . reference = function ( needle ) {
var tmp = null ,
obj = null ;
if ( needle && needle . id && ( ! needle . tagName || ! needle . nodeType ) ) { needle = needle . id ; }
if ( ! obj || ! obj . length ) {
try { obj = $ ( needle ) ; } catch ( ignore ) { }
}
if ( ! obj || ! obj . length ) {
try { obj = $ ( '#' + needle . replace ( $ . jstree . idregex , '\\$&' ) ) ; } catch ( ignore ) { }
}
if ( obj && obj . length && ( obj = obj . closest ( '.jstree' ) ) . length && ( obj = obj . data ( 'jstree' ) ) ) {
tmp = obj ;
}
else {
$ ( '.jstree' ) . each ( function ( ) {
var inst = $ ( this ) . data ( 'jstree' ) ;
if ( inst && inst . _model . data [ needle ] ) {
tmp = inst ;
return false ;
}
} ) ;
}
return tmp ;
} ;
/ * *
* Create an instance , get an instance or invoke a command on a instance .
*
* 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 ) .
*
* 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 ) .
*
* If there is an existing instance and ` arg ` is not a string the instance itself is returned ( similar to ` $ .jstree.reference ` ) .
*
* In any other case - nothing is returned and chaining is not broken .
*
* _ _Examples _ _
*
* $ ( '#tree1' ) . jstree ( ) ; // creates an instance
* $ ( '#tree2' ) . jstree ( { plugins : [ ] } ) ; // create an instance with some options
* $ ( '#tree1' ) . jstree ( 'open_node' , '#branch_1' ) ; // call a method on an existing instance, passing additional arguments
* $ ( '#tree2' ) . jstree ( ) ; // get an existing instance (or create an instance)
* $ ( '#tree2' ) . jstree ( true ) ; // get an existing instance (will not create new instance)
* $ ( '#branch_1' ) . jstree ( ) . select _node ( '#branch_1' ) ; // get an instance (using a nested element and call a method)
*
* @ name $ ( ) . jstree ( [ arg ] )
* @ param { String | Object } arg
* @ return { Mixed }
* /
$ . fn . jstree = function ( arg ) {
// check for string argument
var is _method = ( typeof arg === 'string' ) ,
args = Array . prototype . slice . call ( arguments , 1 ) ,
result = null ;
if ( arg === true && ! this . length ) { return false ; }
this . each ( function ( ) {
// get the instance (if there is one) and method (if it exists)
var instance = $ . jstree . reference ( this ) ,
method = is _method && instance ? instance [ arg ] : null ;
// if calling a method, and method is available - execute on the instance
result = is _method && method ?
method . apply ( instance , args ) :
null ;
// if there is no instance and no method is being called - create one
if ( ! instance && ! is _method && ( arg === undefined || $ . isPlainObject ( arg ) ) ) {
$ . jstree . create ( this , arg ) ;
}
// if there is an instance and no method is called - return the instance
if ( ( instance && ! is _method ) || arg === true ) {
result = instance || false ;
}
// if there was a method call which returned a result - break and return the value
if ( result !== null && result !== undefined ) {
return false ;
}
} ) ;
// if there was a method call with a valid return value - return that, otherwise continue the chain
return result !== null && result !== undefined ?
result : this ;
} ;
/ * *
* used to find elements containing an instance
*
* _ _Examples _ _
*
* $ ( 'div:jstree' ) . each ( function ( ) {
* $ ( this ) . jstree ( 'destroy' ) ;
* } ) ;
*
* @ name $ ( ':jstree' )
* @ return { jQuery }
* /
$ . expr [ ':' ] . jstree = $ . expr . createPseudo ( function ( search ) {
return function ( a ) {
return $ ( a ) . hasClass ( 'jstree' ) &&
$ ( a ) . data ( 'jstree' ) !== undefined ;
} ;
} ) ;
/ * *
* stores all defaults for the core
* @ name $ . jstree . defaults . core
* /
$ . jstree . defaults . core = {
/ * *
* data configuration
*
* 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 ) .
*
* You can also pass in a HTML string or a JSON array here .
*
* 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 .
* 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 .
*
* 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 .
*
* _ _Examples _ _
*
* // AJAX
* $ ( '#tree' ) . jstree ( {
* 'core' : {
* 'data' : {
* 'url' : '/get/children/' ,
* 'data' : function ( node ) {
* return { 'id' : node . id } ;
* }
* }
* } ) ;
*
* // direct data
* $ ( '#tree' ) . jstree ( {
* 'core' : {
* 'data' : [
* 'Simple root node' ,
* {
* 'id' : 'node_2' ,
* 'text' : 'Root node with options' ,
* 'state' : { 'opened' : true , 'selected' : true } ,
* 'children' : [ { 'text' : 'Child 1' } , 'Child 2' ]
* }
* ]
* } ) ;
*
* // function
* $ ( '#tree' ) . jstree ( {
* 'core' : {
* 'data' : function ( obj , callback ) {
* callback . call ( this , [ 'Root 1' , 'Root 2' ] ) ;
* }
* } ) ;
*
* @ name $ . jstree . defaults . core . data
* /
data : false ,
/ * *
* configure the various strings used throughout the tree
*
* You can use an object where the key is the string you need to replace and the value is your replacement .
* Another option is to specify a function which will be called with an argument of the needed string and should return the replacement .
* If left as ` false ` no replacement is made .
*
* _ _Examples _ _
*
* $ ( '#tree' ) . jstree ( {
* 'core' : {
* 'strings' : {
* 'Loading ...' : 'Please wait ...'
* }
* }
* } ) ;
*
* @ name $ . jstree . defaults . core . strings
* /
strings : false ,
/ * *
* determines what happens when a user tries to modify the structure of the tree
* If left as ` false ` all operations like create , rename , delete , move or copy are prevented .
* You can set this to ` true ` to allow all interactions or use a function to have better control .
*
* _ _Examples _ _
*
* $ ( '#tree' ) . jstree ( {
* 'core' : {
* 'check_callback' : function ( operation , node , node _parent , node _position , more ) {
* // operation can be 'create_node', 'rename_node', 'delete_node', 'move_node' or 'copy_node'
* // in case of 'rename_node' node_position is filled with the new node name
* return operation === 'rename_node' ? true : false ;
* }
* }
* } ) ;
*
* @ name $ . jstree . defaults . core . check _callback
* /
check _callback : false ,
/ * *
* a callback called with a single object parameter in the instance ' s scope when something goes wrong ( operation prevented , ajax failed , etc )
* @ name $ . jstree . defaults . core . error
* /
error : $ . noop ,
/ * *
* the open / close animation duration in milliseconds - set this to ` false ` to disable the animation ( default is ` 200 ` )
* @ name $ . jstree . defaults . core . animation
* /
animation : 200 ,
/ * *
* a boolean indicating if multiple nodes can be selected
* @ name $ . jstree . defaults . core . multiple
* /
multiple : true ,
/ * *
* theme configuration object
* @ name $ . jstree . defaults . core . themes
* /
themes : {
/ * *
* the name of the theme to use ( if left as ` false ` the default theme is used )
* @ name $ . jstree . defaults . core . themes . name
* /
name : false ,
/ * *
* 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 .
* @ name $ . jstree . defaults . core . themes . url
* /
url : false ,
/ * *
* the location of all jstree themes - only used if ` url ` is set to ` true `
* @ name $ . jstree . defaults . core . themes . dir
* /
dir : false ,
/ * *
* a boolean indicating if connecting dots are shown
* @ name $ . jstree . defaults . core . themes . dots
* /
dots : true ,
/ * *
* a boolean indicating if node icons are shown
* @ name $ . jstree . defaults . core . themes . icons
* /
icons : true ,
/ * *
* a boolean indicating if the tree background is striped
* @ name $ . jstree . defaults . core . themes . stripes
* /
stripes : false ,
/ * *
* a string ( or boolean ` false ` ) specifying the theme variant to use ( if the theme supports variants )
* @ name $ . jstree . defaults . core . themes . variant
* /
variant : false ,
/ * *
* a boolean specifying if a reponsive version of the theme should kick in on smaller screens ( if the theme supports it ) . Defaults to ` false ` .
* @ name $ . jstree . defaults . core . themes . responsive
* /
responsive : false
} ,
/ * *
* 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 )
* @ name $ . jstree . defaults . core . expand _selected _onload
* /
expand _selected _onload : true ,
/ * *
* if left as ` true ` web workers will be used to parse incoming JSON data where possible , so that the UI will not be blocked by large requests . Workers are however about 30 % slower . Defaults to ` true `
* @ name $ . jstree . defaults . core . worker
* /
worker : true ,
/ * *
* Force node text to plain text ( and escape HTML ) . Defaults to ` false `
* @ name $ . jstree . defaults . core . force _text
* /
force _text : false ,
/ * *
* Should the node should be toggled if the text is double clicked . Defaults to ` true `
* @ name $ . jstree . defaults . core . dblclick _toggle
* /
dblclick _toggle : true
} ;
$ . jstree . core . prototype = {
/ * *
* used to decorate an instance with a plugin . Used internally .
* @ private
* @ name plugin ( deco [ , opts ] )
* @ param { String } deco the plugin to decorate with
* @ param { Object } opts options for the plugin
* @ return { jsTree }
* /
plugin : function ( deco , opts ) {
var Child = $ . jstree . plugins [ deco ] ;
if ( Child ) {
this . _data [ deco ] = { } ;
Child . prototype = this ;
return new Child ( opts , this ) ;
}
return this ;
} ,
/ * *
* initialize the instance . Used internally .
* @ private
* @ name init ( el , optons )
* @ param { DOMElement | jQuery | String } el the element we are transforming
* @ param { Object } options options for this instance
* @ trigger init . jstree , loading . jstree , loaded . jstree , ready . jstree , changed . jstree
* /
init : function ( el , options ) {
this . _model = {
data : {
'#' : {
id : '#' ,
parent : null ,
parents : [ ] ,
children : [ ] ,
children _d : [ ] ,
state : { loaded : false }
}
} ,
changed : [ ] ,
force _full _redraw : false ,
redraw _timeout : false ,
default _state : {
loaded : true ,
opened : false ,
selected : false ,
disabled : false
}
} ;
this . element = $ ( el ) . addClass ( 'jstree jstree-' + this . _id ) ;
this . settings = options ;
this . _data . core . ready = false ;
this . _data . core . loaded = false ;
this . _data . core . rtl = ( this . element . css ( "direction" ) === "rtl" ) ;
this . element [ this . _data . core . rtl ? 'addClass' : 'removeClass' ] ( "jstree-rtl" ) ;
this . element . attr ( 'role' , 'tree' ) ;
if ( this . settings . core . multiple ) {
this . element . attr ( 'aria-multiselectable' , true ) ;
}
if ( ! this . element . attr ( 'tabindex' ) ) {
this . element . attr ( 'tabindex' , '0' ) ;
}
this . bind ( ) ;
/ * *
* triggered after all events are bound
* @ event
* @ name init . jstree
* /
this . trigger ( "init" ) ;
this . _data . core . original _container _html = this . element . find ( " > ul > li" ) . clone ( true ) ;
this . _data . core . original _container _html
. find ( "li" ) . addBack ( )
. contents ( ) . filter ( function ( ) {
return this . nodeType === 3 && ( ! this . nodeValue || /^\s+$/ . test ( this . nodeValue ) ) ;
} )
. remove ( ) ;
this . element . html ( "<" + "ul class='jstree-container-ul jstree-children' role='group'><" + "li id='j" + this . _id + "_loading' class='jstree-initial-node jstree-loading jstree-leaf jstree-last' role='tree-item'><i class='jstree-icon jstree-ocl'></i><" + "a class='jstree-anchor' href='#'><i class='jstree-icon jstree-themeicon-hidden'></i>" + this . get _string ( "Loading ..." ) + "</a></li></ul>" ) ;
this . element . attr ( 'aria-activedescendant' , 'j' + this . _id + '_loading' ) ;
this . _data . core . li _height = this . get _container _ul ( ) . children ( "li" ) . first ( ) . height ( ) || 24 ;
/ * *
* triggered after the loading text is shown and before loading starts
* @ event
* @ name loading . jstree
* /
this . trigger ( "loading" ) ;
this . load _node ( '#' ) ;
} ,
/ * *
* destroy an instance
* @ name destroy ( )
* @ param { Boolean } keep _html if not set to ` true ` the container will be emptied , otherwise the current DOM elements will be kept intact
* /
destroy : function ( keep _html ) {
if ( this . _wrk ) {
try {
window . URL . revokeObjectURL ( this . _wrk ) ;
this . _wrk = null ;
}
catch ( ignore ) { }
}
if ( ! keep _html ) { this . element . empty ( ) ; }
this . teardown ( ) ;
} ,
/ * *
* part of the destroying of an instance . Used internally .
* @ private
* @ name teardown ( )
* /
teardown : function ( ) {
this . unbind ( ) ;
this . element
. removeClass ( 'jstree' )
. removeData ( 'jstree' )
. find ( "[class^='jstree']" )
. addBack ( )
. attr ( "class" , function ( ) { return this . className . replace ( /jstree[^ ]*|$/ig , '' ) ; } ) ;
this . element = null ;
} ,
/ * *
* bind all events . Used internally .
* @ private
* @ name bind ( )
* /
bind : function ( ) {
var word = '' ,
tout = null ,
was _click = 0 ;
this . element
. on ( "dblclick.jstree" , function ( ) {
if ( document . selection && document . selection . empty ) {
document . selection . empty ( ) ;
}
else {
if ( window . getSelection ) {
var sel = window . getSelection ( ) ;
try {
sel . removeAllRanges ( ) ;
sel . collapse ( ) ;
} catch ( ignore ) { }
}
}
} )
. on ( "mousedown.jstree" , $ . proxy ( function ( e ) {
if ( e . target === this . element [ 0 ] ) {
e . preventDefault ( ) ; // prevent losing focus when clicking scroll arrows (FF, Chrome)
was _click = + ( new Date ( ) ) ; // ie does not allow to prevent losing focus
}
} , this ) )
. on ( "mousedown.jstree" , ".jstree-ocl" , function ( e ) {
e . preventDefault ( ) ; // prevent any node inside from losing focus when clicking the open/close icon
} )
. on ( "click.jstree" , ".jstree-ocl" , $ . proxy ( function ( e ) {
this . toggle _node ( e . target ) ;
} , this ) )
. on ( "dblclick.jstree" , ".jstree-anchor" , $ . proxy ( function ( e ) {
if ( this . settings . core . dblclick _toggle ) {
this . toggle _node ( e . target ) ;
}
} , this ) )
. on ( "click.jstree" , ".jstree-anchor" , $ . proxy ( function ( e ) {
e . preventDefault ( ) ;
if ( e . currentTarget !== document . activeElement ) { $ ( e . currentTarget ) . focus ( ) ; }
this . activate _node ( e . currentTarget , e ) ;
} , this ) )
. on ( 'keydown.jstree' , '.jstree-anchor' , $ . proxy ( function ( e ) {
if ( e . target . tagName === "INPUT" ) { return true ; }
if ( e . which !== 32 && e . which !== 13 && ( e . shiftKey || e . ctrlKey || e . altKey || e . metaKey ) ) { return true ; }
var o = null ;
if ( this . _data . core . rtl ) {
if ( e . which === 37 ) { e . which = 39 ; }
else if ( e . which === 39 ) { e . which = 37 ; }
}
switch ( e . which ) {
case 32 : // aria defines space only with Ctrl
if ( e . ctrlKey ) {
e . type = "click" ;
$ ( e . currentTarget ) . trigger ( e ) ;
}
break ;
case 13 : // enter
e . type = "click" ;
$ ( e . currentTarget ) . trigger ( e ) ;
break ;
case 37 : // right
e . preventDefault ( ) ;
if ( this . is _open ( e . currentTarget ) ) {
this . close _node ( e . currentTarget ) ;
}
else {
o = this . get _parent ( e . currentTarget ) ;
if ( o && o . id !== '#' ) { this . get _node ( o , true ) . children ( '.jstree-anchor' ) . focus ( ) ; }
}
break ;
case 38 : // up
e . preventDefault ( ) ;
o = this . get _prev _dom ( e . currentTarget ) ;
if ( o && o . length ) { o . children ( '.jstree-anchor' ) . focus ( ) ; }
break ;
case 39 : // left
e . preventDefault ( ) ;
if ( this . is _closed ( e . currentTarget ) ) {
this . open _node ( e . currentTarget , function ( o ) { this . get _node ( o , true ) . children ( '.jstree-anchor' ) . focus ( ) ; } ) ;
}
else if ( this . is _open ( e . currentTarget ) ) {
o = this . get _node ( e . currentTarget , true ) . children ( '.jstree-children' ) [ 0 ] ;
if ( o ) { $ ( this . _firstChild ( o ) ) . children ( '.jstree-anchor' ) . focus ( ) ; }
}
break ;
case 40 : // down
e . preventDefault ( ) ;
o = this . get _next _dom ( e . currentTarget ) ;
if ( o && o . length ) { o . children ( '.jstree-anchor' ) . focus ( ) ; }
break ;
case 106 : // aria defines * on numpad as open_all - not very common
this . open _all ( ) ;
break ;
case 36 : // home
e . preventDefault ( ) ;
o = this . _firstChild ( this . get _container _ul ( ) [ 0 ] ) ;
if ( o ) { $ ( o ) . children ( '.jstree-anchor' ) . filter ( ':visible' ) . focus ( ) ; }
break ;
case 35 : // end
e . preventDefault ( ) ;
this . element . find ( '.jstree-anchor' ) . filter ( ':visible' ) . last ( ) . focus ( ) ;
break ;
/ *
// delete
case 46 :
e . preventDefault ( ) ;
o = this . get _node ( e . currentTarget ) ;
if ( o && o . id && o . id !== '#' ) {
o = this . is _selected ( o ) ? this . get _selected ( ) : o ;
this . delete _node ( o ) ;
}
break ;
// f2
case 113 :
e . preventDefault ( ) ;
o = this . get _node ( e . currentTarget ) ;
if ( o && o . id && o . id !== '#' ) {
// this.edit(o);
}
break ;
default :
// console.log(e.which);
break ;
* /
}
} , this ) )
. on ( "load_node.jstree" , $ . proxy ( function ( e , data ) {
if ( data . status ) {
if ( data . node . id === '#' && ! this . _data . core . loaded ) {
this . _data . core . loaded = true ;
if ( this . _firstChild ( this . get _container _ul ( ) [ 0 ] ) ) {
this . element . attr ( 'aria-activedescendant' , this . _firstChild ( this . get _container _ul ( ) [ 0 ] ) . id ) ;
}
/ * *
* triggered after the root node is loaded for the first time
* @ event
* @ name loaded . jstree
* /
this . trigger ( "loaded" ) ;
}
if ( ! this . _data . core . ready ) {
setTimeout ( $ . proxy ( function ( ) {
if ( this . element && ! this . get _container _ul ( ) . find ( '.jstree-loading' ) . length ) {
this . _data . core . ready = true ;
if ( this . _data . core . selected . length ) {
if ( this . settings . core . expand _selected _onload ) {
var tmp = [ ] , i , j ;
for ( i = 0 , j = this . _data . core . selected . length ; i < j ; i ++ ) {
tmp = tmp . concat ( this . _model . data [ this . _data . core . selected [ i ] ] . parents ) ;
}
tmp = $ . vakata . array _unique ( tmp ) ;
for ( i = 0 , j = tmp . length ; i < j ; i ++ ) {
this . open _node ( tmp [ i ] , false , 0 ) ;
}
}
this . trigger ( 'changed' , { 'action' : 'ready' , 'selected' : this . _data . core . selected } ) ;
}
/ * *
* triggered after all nodes are finished loading
* @ event
* @ name ready . jstree
* /
this . trigger ( "ready" ) ;
}
} , this ) , 0 ) ;
}
}
} , this ) )
// quick searching when the tree is focused
. on ( 'keypress.jstree' , $ . proxy ( function ( e ) {
if ( e . target . tagName === "INPUT" ) { return true ; }
if ( tout ) { clearTimeout ( tout ) ; }
tout = setTimeout ( function ( ) {
word = '' ;
} , 500 ) ;
var chr = String . fromCharCode ( e . which ) . toLowerCase ( ) ,
col = this . element . find ( '.jstree-anchor' ) . filter ( ':visible' ) ,
ind = col . index ( document . activeElement ) || 0 ,
end = false ;
word += chr ;
// match for whole word from current node down (including the current node)
if ( word . length > 1 ) {
col . slice ( ind ) . each ( $ . proxy ( function ( i , v ) {
if ( $ ( v ) . text ( ) . toLowerCase ( ) . indexOf ( word ) === 0 ) {
$ ( v ) . focus ( ) ;
end = true ;
return false ;
}
} , this ) ) ;
if ( end ) { return ; }
// match for whole word from the beginning of the tree
col . slice ( 0 , ind ) . each ( $ . proxy ( function ( i , v ) {
if ( $ ( v ) . text ( ) . toLowerCase ( ) . indexOf ( word ) === 0 ) {
$ ( v ) . focus ( ) ;
end = true ;
return false ;
}
} , this ) ) ;
if ( end ) { return ; }
}
// list nodes that start with that letter (only if word consists of a single char)
if ( new RegExp ( '^' + chr + '+$' ) . test ( word ) ) {
// search for the next node starting with that letter
col . slice ( ind + 1 ) . each ( $ . proxy ( function ( i , v ) {
if ( $ ( v ) . text ( ) . toLowerCase ( ) . charAt ( 0 ) === chr ) {
$ ( v ) . focus ( ) ;
end = true ;
return false ;
}
} , this ) ) ;
if ( end ) { return ; }
// search from the beginning
col . slice ( 0 , ind + 1 ) . each ( $ . proxy ( function ( i , v ) {
if ( $ ( v ) . text ( ) . toLowerCase ( ) . charAt ( 0 ) === chr ) {
$ ( v ) . focus ( ) ;
end = true ;
return false ;
}
} , this ) ) ;
if ( end ) { return ; }
}
} , this ) )
// THEME RELATED
. on ( "init.jstree" , $ . proxy ( function ( ) {
var s = this . settings . core . themes ;
this . _data . core . themes . dots = s . dots ;
this . _data . core . themes . stripes = s . stripes ;
this . _data . core . themes . icons = s . icons ;
this . set _theme ( s . name || "default" , s . url ) ;
this . set _theme _variant ( s . variant ) ;
} , this ) )
. on ( "loading.jstree" , $ . proxy ( function ( ) {
this [ this . _data . core . themes . dots ? "show_dots" : "hide_dots" ] ( ) ;
this [ this . _data . core . themes . icons ? "show_icons" : "hide_icons" ] ( ) ;
this [ this . _data . core . themes . stripes ? "show_stripes" : "hide_stripes" ] ( ) ;
} , this ) )
. on ( 'blur.jstree' , '.jstree-anchor' , $ . proxy ( function ( e ) {
this . _data . core . focused = null ;
$ ( e . currentTarget ) . filter ( '.jstree-hovered' ) . mouseleave ( ) ;
this . element . attr ( 'tabindex' , '0' ) ;
} , this ) )
. on ( 'focus.jstree' , '.jstree-anchor' , $ . proxy ( function ( e ) {
var tmp = this . get _node ( e . currentTarget ) ;
if ( tmp && tmp . id ) {
this . _data . core . focused = tmp . id ;
}
this . element . find ( '.jstree-hovered' ) . not ( e . currentTarget ) . mouseleave ( ) ;
$ ( e . currentTarget ) . mouseenter ( ) ;
this . element . attr ( 'tabindex' , '-1' ) ;
} , this ) )
. on ( 'focus.jstree' , $ . proxy ( function ( ) {
if ( + ( new Date ( ) ) - was _click > 500 && ! this . _data . core . focused ) {
was _click = 0 ;
var act = this . get _node ( this . element . attr ( 'aria-activedescendant' ) , true ) ;
if ( act ) {
act . find ( '> .jstree-anchor' ) . focus ( ) ;
}
}
} , this ) )
. on ( 'mouseenter.jstree' , '.jstree-anchor' , $ . proxy ( function ( e ) {
this . hover _node ( e . currentTarget ) ;
} , this ) )
. on ( 'mouseleave.jstree' , '.jstree-anchor' , $ . proxy ( function ( e ) {
this . dehover _node ( e . currentTarget ) ;
} , this ) ) ;
} ,
/ * *
* part of the destroying of an instance . Used internally .
* @ private
* @ name unbind ( )
* /
unbind : function ( ) {
this . element . off ( '.jstree' ) ;
$ ( document ) . off ( '.jstree-' + this . _id ) ;
} ,
/ * *
* trigger an event . Used internally .
* @ private
* @ name trigger ( ev [ , data ] )
* @ param { String } ev the name of the event to trigger
* @ param { Object } data additional data to pass with the event
* /
trigger : function ( ev , data ) {
if ( ! data ) {
data = { } ;
}
data . instance = this ;
this . element . triggerHandler ( ev . replace ( '.jstree' , '' ) + '.jstree' , data ) ;
} ,
/ * *
* returns the jQuery extended instance container
* @ name get _container ( )
* @ return { jQuery }
* /
get _container : function ( ) {
return this . element ;
} ,
/ * *
* returns the jQuery extended main UL node inside the instance container . Used internally .
* @ private
* @ name get _container _ul ( )
* @ return { jQuery }
* /
get _container _ul : function ( ) {
return this . element . children ( ".jstree-children" ) . first ( ) ;
} ,
/ * *
* gets string replacements ( localization ) . Used internally .
* @ private
* @ name get _string ( key )
* @ param { String } key
* @ return { String }
* /
get _string : function ( key ) {
var a = this . settings . core . strings ;
if ( $ . isFunction ( a ) ) { return a . call ( this , key ) ; }
if ( a && a [ key ] ) { return a [ key ] ; }
return key ;
} ,
/ * *
* gets the first child of a DOM node . Used internally .
* @ private
* @ name _firstChild ( dom )
* @ param { DOMElement } dom
* @ return { DOMElement }
* /
_firstChild : function ( dom ) {
dom = dom ? dom . firstChild : null ;
while ( dom !== null && dom . nodeType !== 1 ) {
dom = dom . nextSibling ;
}
return dom ;
} ,
/ * *
* gets the next sibling of a DOM node . Used internally .
* @ private
* @ name _nextSibling ( dom )
* @ param { DOMElement } dom
* @ return { DOMElement }
* /
_nextSibling : function ( dom ) {
dom = dom ? dom . nextSibling : null ;
while ( dom !== null && dom . nodeType !== 1 ) {
dom = dom . nextSibling ;
}
return dom ;
} ,
/ * *
* gets the previous sibling of a DOM node . Used internally .
* @ private
* @ name _previousSibling ( dom )
* @ param { DOMElement } dom
* @ return { DOMElement }
* /
_previousSibling : function ( dom ) {
dom = dom ? dom . previousSibling : null ;
while ( dom !== null && dom . nodeType !== 1 ) {
dom = dom . previousSibling ;
}
return dom ;
} ,
/ * *
* 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 )
* @ name get _node ( obj [ , as _dom ] )
* @ param { mixed } obj
* @ param { Boolean } as _dom
* @ return { Object | jQuery }
* /
get _node : function ( obj , as _dom ) {
if ( obj && obj . id ) {
obj = obj . id ;
}
var dom ;
try {
if ( this . _model . data [ obj ] ) {
obj = this . _model . data [ obj ] ;
}
else if ( typeof obj === "string" && this . _model . data [ obj . replace ( /^#/ , '' ) ] ) {
obj = this . _model . data [ obj . replace ( /^#/ , '' ) ] ;
}
else if ( typeof obj === "string" && ( dom = $ ( '#' + obj . replace ( $ . jstree . idregex , '\\$&' ) , this . element ) ) . length && this . _model . data [ dom . closest ( '.jstree-node' ) . attr ( 'id' ) ] ) {
obj = this . _model . data [ dom . closest ( '.jstree-node' ) . attr ( 'id' ) ] ;
}
else if ( ( dom = $ ( obj , this . element ) ) . length && this . _model . data [ dom . closest ( '.jstree-node' ) . attr ( 'id' ) ] ) {
obj = this . _model . data [ dom . closest ( '.jstree-node' ) . attr ( 'id' ) ] ;
}
else if ( ( dom = $ ( obj , this . element ) ) . length && dom . hasClass ( 'jstree' ) ) {
obj = this . _model . data [ '#' ] ;
}
else {
return false ;
}
if ( as _dom ) {
obj = obj . id === '#' ? this . element : $ ( '#' + obj . id . replace ( $ . jstree . idregex , '\\$&' ) , this . element ) ;
}
return obj ;
} catch ( ex ) { return false ; }
} ,
/ * *
* get the path to a node , either consisting of node texts , or of node IDs , optionally glued together ( otherwise an array )
* @ name get _path ( obj [ , glue , ids ] )
* @ param { mixed } obj the node
* @ 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
* @ param { Boolean } ids if set to true build the path using ID , otherwise node text is used
* @ return { mixed }
* /
get _path : function ( obj , glue , ids ) {
obj = obj . parents ? obj : this . get _node ( obj ) ;
if ( ! obj || obj . id === '#' || ! obj . parents ) {
return false ;
}
var i , j , p = [ ] ;
p . push ( ids ? obj . id : obj . text ) ;
for ( i = 0 , j = obj . parents . length ; i < j ; i ++ ) {
p . push ( ids ? obj . parents [ i ] : this . get _text ( obj . parents [ i ] ) ) ;
}
p = p . reverse ( ) . slice ( 1 ) ;
return glue ? p . join ( glue ) : p ;
} ,
/ * *
* get the next visible node that is below the ` obj ` node . If ` strict ` is set to ` true ` only sibling nodes are returned .
* @ name get _next _dom ( obj [ , strict ] )
* @ param { mixed } obj
* @ param { Boolean } strict
* @ return { jQuery }
* /
get _next _dom : function ( obj , strict ) {
var tmp ;
obj = this . get _node ( obj , true ) ;
if ( obj [ 0 ] === this . element [ 0 ] ) {
tmp = this . _firstChild ( this . get _container _ul ( ) [ 0 ] ) ;
while ( tmp && tmp . offsetHeight === 0 ) {
tmp = this . _nextSibling ( tmp ) ;
}
return tmp ? $ ( tmp ) : false ;
}
if ( ! obj || ! obj . length ) {
return false ;
}
if ( strict ) {
tmp = obj [ 0 ] ;
do {
tmp = this . _nextSibling ( tmp ) ;
} while ( tmp && tmp . offsetHeight === 0 ) ;
return tmp ? $ ( tmp ) : false ;
}
if ( obj . hasClass ( "jstree-open" ) ) {
tmp = this . _firstChild ( obj . children ( '.jstree-children' ) [ 0 ] ) ;
while ( tmp && tmp . offsetHeight === 0 ) {
tmp = this . _nextSibling ( tmp ) ;
}
if ( tmp !== null ) {
return $ ( tmp ) ;
}
}
tmp = obj [ 0 ] ;
do {
tmp = this . _nextSibling ( tmp ) ;
} while ( tmp && tmp . offsetHeight === 0 ) ;
if ( tmp !== null ) {
return $ ( tmp ) ;
}
return obj . parentsUntil ( ".jstree" , ".jstree-node" ) . nextAll ( ".jstree-node:visible" ) . first ( ) ;
} ,
/ * *
* get the previous visible node that is above the ` obj ` node . If ` strict ` is set to ` true ` only sibling nodes are returned .
* @ name get _prev _dom ( obj [ , strict ] )
* @ param { mixed } obj
* @ param { Boolean } strict
* @ return { jQuery }
* /
get _prev _dom : function ( obj , strict ) {
var tmp ;
obj = this . get _node ( obj , true ) ;
if ( obj [ 0 ] === this . element [ 0 ] ) {
tmp = this . get _container _ul ( ) [ 0 ] . lastChild ;
while ( tmp && tmp . offsetHeight === 0 ) {
tmp = this . _previousSibling ( tmp ) ;
}
return tmp ? $ ( tmp ) : false ;
}
if ( ! obj || ! obj . length ) {
return false ;
}
if ( strict ) {
tmp = obj [ 0 ] ;
do {
tmp = this . _previousSibling ( tmp ) ;
} while ( tmp && tmp . offsetHeight === 0 ) ;
return tmp ? $ ( tmp ) : false ;
}
tmp = obj [ 0 ] ;
do {
tmp = this . _previousSibling ( tmp ) ;
} while ( tmp && tmp . offsetHeight === 0 ) ;
if ( tmp !== null ) {
obj = $ ( tmp ) ;
while ( obj . hasClass ( "jstree-open" ) ) {
obj = obj . children ( ".jstree-children" ) . first ( ) . children ( ".jstree-node:visible:last" ) ;
}
return obj ;
}
tmp = obj [ 0 ] . parentNode . parentNode ;
return tmp && tmp . className && tmp . className . indexOf ( 'jstree-node' ) !== - 1 ? $ ( tmp ) : false ;
} ,
/ * *
* get the parent ID of a node
* @ name get _parent ( obj )
* @ param { mixed } obj
* @ return { String }
* /
get _parent : function ( obj ) {
obj = this . get _node ( obj ) ;
if ( ! obj || obj . id === '#' ) {
return false ;
}
return obj . parent ;
} ,
/ * *
* get a jQuery collection of all the children of a node ( node must be rendered )
* @ name get _children _dom ( obj )
* @ param { mixed } obj
* @ return { jQuery }
* /
get _children _dom : function ( obj ) {
obj = this . get _node ( obj , true ) ;
if ( obj [ 0 ] === this . element [ 0 ] ) {
return this . get _container _ul ( ) . children ( ".jstree-node" ) ;
}
if ( ! obj || ! obj . length ) {
return false ;
}
return obj . children ( ".jstree-children" ) . children ( ".jstree-node" ) ;
} ,
/ * *
* checks if a node has children
* @ name is _parent ( obj )
* @ param { mixed } obj
* @ return { Boolean }
* /
is _parent : function ( obj ) {
obj = this . get _node ( obj ) ;
return obj && ( obj . state . loaded === false || obj . children . length > 0 ) ;
} ,
/ * *
* checks if a node is loaded ( its children are available )
* @ name is _loaded ( obj )
* @ param { mixed } obj
* @ return { Boolean }
* /
is _loaded : function ( obj ) {
obj = this . get _node ( obj ) ;
return obj && obj . state . loaded ;
} ,
/ * *
* check if a node is currently loading ( fetching children )
* @ name is _loading ( obj )
* @ param { mixed } obj
* @ return { Boolean }
* /
is _loading : function ( obj ) {
obj = this . get _node ( obj ) ;
return obj && obj . state && obj . state . loading ;
} ,
/ * *
* check if a node is opened
* @ name is _open ( obj )
* @ param { mixed } obj
* @ return { Boolean }
* /
is _open : function ( obj ) {
obj = this . get _node ( obj ) ;
return obj && obj . state . opened ;
} ,
/ * *
* check if a node is in a closed state
* @ name is _closed ( obj )
* @ param { mixed } obj
* @ return { Boolean }
* /
is _closed : function ( obj ) {
obj = this . get _node ( obj ) ;
return obj && this . is _parent ( obj ) && ! obj . state . opened ;
} ,
/ * *
* check if a node has no children
* @ name is _leaf ( obj )
* @ param { mixed } obj
* @ return { Boolean }
* /
is _leaf : function ( obj ) {
return ! this . is _parent ( obj ) ;
} ,
/ * *
* loads a node ( fetches its children using the ` core.data ` setting ) . Multiple nodes can be passed to by using an array .
* @ name load _node ( obj [ , callback ] )
* @ param { mixed } obj
* @ param { function } callback a function to be executed once loading is complete , the function is executed in the instance ' s scope and receives two arguments - the node and a boolean status
* @ return { Boolean }
* @ trigger load _node . jstree
* /
load _node : function ( obj , callback ) {
var k , l , i , j , c ;
if ( $ . isArray ( obj ) ) {
this . _load _nodes ( obj . slice ( ) , callback ) ;
return true ;
}
obj = this . get _node ( obj ) ;
if ( ! obj ) {
if ( callback ) { callback . call ( this , obj , false ) ; }
return false ;
}
// if(obj.state.loading) { } // the node is already loading - just wait for it to load and invoke callback? but if called implicitly it should be loaded again?
if ( obj . state . loaded ) {
obj . state . loaded = false ;
for ( k = 0 , l = obj . children _d . length ; k < l ; k ++ ) {
for ( i = 0 , j = obj . parents . length ; i < j ; i ++ ) {
this . _model . data [ obj . parents [ i ] ] . children _d = $ . vakata . array _remove _item ( this . _model . data [ obj . parents [ i ] ] . children _d , obj . children _d [ k ] ) ;
}
if ( this . _model . data [ obj . children _d [ k ] ] . state . selected ) {
c = true ;
this . _data . core . selected = $ . vakata . array _remove _item ( this . _data . core . selected , obj . children _d [ k ] ) ;
}
delete this . _model . data [ obj . children _d [ k ] ] ;
}
obj . children = [ ] ;
obj . children _d = [ ] ;
if ( c ) {
this . trigger ( 'changed' , { 'action' : 'load_node' , 'node' : obj , 'selected' : this . _data . core . selected } ) ;
}
}
obj . state . failed = false ;
obj . state . loading = true ;
this . get _node ( obj , true ) . addClass ( "jstree-loading" ) . attr ( 'aria-busy' , true ) ;
this . _load _node ( obj , $ . proxy ( function ( status ) {
obj = this . _model . data [ obj . id ] ;
obj . state . loading = false ;
obj . state . loaded = status ;
obj . state . failed = ! obj . state . loaded ;
var dom = this . get _node ( obj , true ) ;
if ( obj . state . loaded && ! obj . children . length && dom && dom . length && ! dom . hasClass ( 'jstree-leaf' ) ) {
dom . removeClass ( 'jstree-closed jstree-open' ) . addClass ( 'jstree-leaf' ) ;
}
dom . removeClass ( "jstree-loading" ) . attr ( 'aria-busy' , false ) ;
/ * *
* triggered after a node is loaded
* @ event
* @ name load _node . jstree
* @ param { Object } node the node that was loading
* @ param { Boolean } status was the node loaded successfully
* /
this . trigger ( 'load_node' , { "node" : obj , "status" : status } ) ;
if ( callback ) {
callback . call ( this , obj , status ) ;
}
} , this ) ) ;
return true ;
} ,
/ * *
* load an array of nodes ( will also load unavailable nodes as soon as the appear in the structure ) . Used internally .
* @ private
* @ name _load _nodes ( nodes [ , callback ] )
* @ param { array } nodes
* @ param { function } callback a function to be executed once loading is complete , the function is executed in the instance ' s scope and receives one argument - the array passed to _load _nodes
* /
_load _nodes : function ( nodes , callback , is _callback ) {
var r = true ,
c = function ( ) { this . _load _nodes ( nodes , callback , true ) ; } ,
m = this . _model . data , i , j , tmp = [ ] ;
for ( i = 0 , j = nodes . length ; i < j ; i ++ ) {
if ( m [ nodes [ i ] ] && ( ( ! m [ nodes [ i ] ] . state . loaded && ! m [ nodes [ i ] ] . state . failed ) || ! is _callback ) ) {
if ( ! this . is _loading ( nodes [ i ] ) ) {
this . load _node ( nodes [ i ] , c ) ;
}
r = false ;
}
}
if ( r ) {
for ( i = 0 , j = nodes . length ; i < j ; i ++ ) {
if ( m [ nodes [ i ] ] && m [ nodes [ i ] ] . state . loaded ) {
tmp . push ( nodes [ i ] ) ;
}
}
if ( callback && ! callback . done ) {
callback . call ( this , tmp ) ;
callback . done = true ;
}
}
} ,
/ * *
* loads all unloaded nodes
* @ name load _all ( [ obj , callback ] )
* @ param { mixed } obj the node to load recursively , omit to load all nodes in the tree
* @ param { function } callback a function to be executed once loading all the nodes is complete ,
* @ trigger load _all . jstree
* /
load _all : function ( obj , callback ) {
if ( ! obj ) { obj = '#' ; }
obj = this . get _node ( obj ) ;
if ( ! obj ) { return false ; }
var to _load = [ ] ,
m = this . _model . data ,
c = m [ obj . id ] . children _d ,
i , j ;
if ( obj . state && ! obj . state . loaded ) {
to _load . push ( obj . id ) ;
}
for ( i = 0 , j = c . length ; i < j ; i ++ ) {
if ( m [ c [ i ] ] && m [ c [ i ] ] . state && ! m [ c [ i ] ] . state . loaded ) {
to _load . push ( c [ i ] ) ;
}
}
if ( to _load . length ) {
this . _load _nodes ( to _load , function ( ) {
this . load _all ( obj , callback ) ;
} ) ;
}
else {
/ * *
* triggered after a load _all call completes
* @ event
* @ name load _all . jstree
* @ param { Object } node the recursively loaded node
* /
if ( callback ) { callback . call ( this , obj ) ; }
this . trigger ( 'load_all' , { "node" : obj } ) ;
}
} ,
/ * *
* handles the actual loading of a node . Used only internally .
* @ private
* @ name _load _node ( obj [ , callback ] )
* @ param { mixed } obj
* @ param { function } callback a function to be executed once loading is complete , the function is executed in the instance ' s scope and receives one argument - a boolean status
* @ return { Boolean }
* /
_load _node : function ( obj , callback ) {
var s = this . settings . core . data , t ;
// use original HTML
if ( ! s ) {
if ( obj . id === '#' ) {
return this . _append _html _data ( obj , this . _data . core . original _container _html . clone ( true ) , function ( status ) {
callback . call ( this , status ) ;
} ) ;
}
else {
return callback . call ( this , false ) ;
}
// return callback.call(this, obj.id === '#' ? this._append_html_data(obj, this._data.core.original_container_html.clone(true)) : false);
}
if ( $ . isFunction ( s ) ) {
return s . call ( this , obj , $ . proxy ( function ( d ) {
if ( d === false ) {
callback . call ( this , false ) ;
}
this [ typeof d === 'string' ? '_append_html_data' : '_append_json_data' ] ( obj , typeof d === 'string' ? $ ( $ . parseHTML ( d ) ) . filter ( function ( ) { return this . nodeType !== 3 ; } ) : d , function ( status ) {
callback . call ( this , status ) ;
} ) ;
// 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));
} , this ) ) ;
}
if ( typeof s === 'object' ) {
if ( s . url ) {
s = $ . extend ( true , { } , s ) ;
if ( $ . isFunction ( s . url ) ) {
s . url = s . url . call ( this , obj ) ;
}
if ( $ . isFunction ( s . data ) ) {
s . data = s . data . call ( this , obj ) ;
}
return $ . ajax ( s )
. done ( $ . proxy ( function ( d , t , x ) {
var type = x . getResponseHeader ( 'Content-Type' ) ;
if ( ( type && type . indexOf ( 'json' ) !== - 1 ) || typeof d === "object" ) {
return this . _append _json _data ( obj , d , function ( status ) { callback . call ( this , status ) ; } ) ;
//return callback.call(this, this._append_json_data(obj, d));
}
if ( ( type && type . indexOf ( 'html' ) !== - 1 ) || typeof d === "string" ) {
return this . _append _html _data ( obj , $ ( $ . parseHTML ( d ) ) . filter ( function ( ) { return this . nodeType !== 3 ; } ) , function ( status ) { callback . call ( this , status ) ; } ) ;
// return callback.call(this, this._append_html_data(obj, $(d)));
}
this . _data . core . last _error = { 'error' : 'ajax' , 'plugin' : 'core' , 'id' : 'core_04' , 'reason' : 'Could not load node' , 'data' : JSON . stringify ( { 'id' : obj . id , 'xhr' : x } ) } ;
this . settings . core . error . call ( this , this . _data . core . last _error ) ;
return callback . call ( this , false ) ;
} , this ) )
. fail ( $ . proxy ( function ( f ) {
callback . call ( this , false ) ;
this . _data . core . last _error = { 'error' : 'ajax' , 'plugin' : 'core' , 'id' : 'core_04' , 'reason' : 'Could not load node' , 'data' : JSON . stringify ( { 'id' : obj . id , 'xhr' : f } ) } ;
this . settings . core . error . call ( this , this . _data . core . last _error ) ;
} , this ) ) ;
}
t = ( $ . isArray ( s ) || $ . isPlainObject ( s ) ) ? JSON . parse ( JSON . stringify ( s ) ) : s ;
if ( obj . id === '#' ) {
return this . _append _json _data ( obj , t , function ( status ) {
callback . call ( this , status ) ;
} ) ;
}
else {
this . _data . core . last _error = { 'error' : 'nodata' , 'plugin' : 'core' , 'id' : 'core_05' , 'reason' : 'Could not load node' , 'data' : JSON . stringify ( { 'id' : obj . id } ) } ;
this . settings . core . error . call ( this , this . _data . core . last _error ) ;
return callback . call ( this , false ) ;
}
//return callback.call(this, (obj.id === "#" ? this._append_json_data(obj, t) : false) );
}
if ( typeof s === 'string' ) {
if ( obj . id === '#' ) {
return this . _append _html _data ( obj , $ ( $ . parseHTML ( s ) ) . filter ( function ( ) { return this . nodeType !== 3 ; } ) , function ( status ) {
callback . call ( this , status ) ;
} ) ;
}
else {
this . _data . core . last _error = { 'error' : 'nodata' , 'plugin' : 'core' , 'id' : 'core_06' , 'reason' : 'Could not load node' , 'data' : JSON . stringify ( { 'id' : obj . id } ) } ;
this . settings . core . error . call ( this , this . _data . core . last _error ) ;
return callback . call ( this , false ) ;
}
//return callback.call(this, (obj.id === "#" ? this._append_html_data(obj, $(s)) : false) );
}
return callback . call ( this , false ) ;
} ,
/ * *
* adds a node to the list of nodes to redraw . Used only internally .
* @ private
* @ name _node _changed ( obj [ , callback ] )
* @ param { mixed } obj
* /
_node _changed : function ( obj ) {
obj = this . get _node ( obj ) ;
if ( obj ) {
this . _model . changed . push ( obj . id ) ;
}
} ,
/ * *
* appends HTML content to the tree . Used internally .
* @ private
* @ name _append _html _data ( obj , data )
* @ param { mixed } obj the node to append to
* @ param { String } data the HTML string to parse and append
* @ trigger model . jstree , changed . jstree
* /
_append _html _data : function ( dom , data , cb ) {
dom = this . get _node ( dom ) ;
dom . children = [ ] ;
dom . children _d = [ ] ;
var dat = data . is ( 'ul' ) ? data . children ( ) : data ,
par = dom . id ,
chd = [ ] ,
dpc = [ ] ,
m = this . _model . data ,
p = m [ par ] ,
s = this . _data . core . selected . length ,
tmp , i , j ;
dat . each ( $ . proxy ( function ( i , v ) {
tmp = this . _parse _model _from _html ( $ ( v ) , par , p . parents . concat ( ) ) ;
if ( tmp ) {
chd . push ( tmp ) ;
dpc . push ( tmp ) ;
if ( m [ tmp ] . children _d . length ) {
dpc = dpc . concat ( m [ tmp ] . children _d ) ;
}
}
} , this ) ) ;
p . children = chd ;
p . children _d = dpc ;
for ( i = 0 , j = p . parents . length ; i < j ; i ++ ) {
m [ p . parents [ i ] ] . children _d = m [ p . parents [ i ] ] . children _d . concat ( dpc ) ;
}
/ * *
* triggered when new data is inserted to the tree model
* @ event
* @ name model . jstree
* @ param { Array } nodes an array of node IDs
* @ param { String } parent the parent ID of the nodes
* /
this . trigger ( 'model' , { "nodes" : dpc , 'parent' : par } ) ;
if ( par !== '#' ) {
this . _node _changed ( par ) ;
this . redraw ( ) ;
}
else {
this . get _container _ul ( ) . children ( '.jstree-initial-node' ) . remove ( ) ;
this . redraw ( true ) ;
}
if ( this . _data . core . selected . length !== s ) {
this . trigger ( 'changed' , { 'action' : 'model' , 'selected' : this . _data . core . selected } ) ;
}
cb . call ( this , true ) ;
} ,
/ * *
* appends JSON content to the tree . Used internally .
* @ private
* @ name _append _json _data ( obj , data )
* @ param { mixed } obj the node to append to
* @ param { String } data the JSON object to parse and append
* @ param { Boolean } force _processing internal param - do not set
* @ trigger model . jstree , changed . jstree
* /
_append _json _data : function ( dom , data , cb , force _processing ) {
if ( this . element === null ) { return ; }
dom = this . get _node ( dom ) ;
dom . children = [ ] ;
dom . children _d = [ ] ;
// *%$@!!!
if ( data . d ) {
data = data . d ;
if ( typeof data === "string" ) {
data = JSON . parse ( data ) ;
}
}
if ( ! $ . isArray ( data ) ) { data = [ data ] ; }
var w = null ,
args = {
'df' : this . _model . default _state ,
'dat' : data ,
'par' : dom . id ,
'm' : this . _model . data ,
't_id' : this . _id ,
't_cnt' : this . _cnt ,
'sel' : this . _data . core . selected
} ,
func = function ( data , undefined ) {
if ( data . data ) { data = data . data ; }
var dat = data . dat ,
par = data . par ,
chd = [ ] ,
dpc = [ ] ,
add = [ ] ,
df = data . df ,
t _id = data . t _id ,
t _cnt = data . t _cnt ,
m = data . m ,
p = m [ par ] ,
sel = data . sel ,
tmp , i , j , rslt ,
parse _flat = function ( d , p , ps ) {
if ( ! ps ) { ps = [ ] ; }
else { ps = ps . concat ( ) ; }
if ( p ) { ps . unshift ( p ) ; }
var tid = d . id . toString ( ) ,
i , j , c , e ,
tmp = {
id : tid ,
text : d . text || '' ,
icon : d . icon !== undefined ? d . icon : true ,
parent : p ,
parents : ps ,
children : d . children || [ ] ,
children _d : d . children _d || [ ] ,
data : d . data ,
state : { } ,
li _attr : { id : false } ,
a _attr : { href : '#' } ,
original : false
} ;
for ( i in df ) {
if ( df . hasOwnProperty ( i ) ) {
tmp . state [ i ] = df [ i ] ;
}
}
if ( d && d . data && d . data . jstree && d . data . jstree . icon ) {
tmp . icon = d . data . jstree . icon ;
}
if ( tmp . icon === undefined || tmp . icon === null || tmp . icon === "" ) {
tmp . icon = true ;
}
if ( d && d . data ) {
tmp . data = d . data ;
if ( d . data . jstree ) {
for ( i in d . data . jstree ) {
if ( d . data . jstree . hasOwnProperty ( i ) ) {
tmp . state [ i ] = d . data . jstree [ i ] ;
}
}
}
}
if ( d && typeof d . state === 'object' ) {
for ( i in d . state ) {
if ( d . state . hasOwnProperty ( i ) ) {
tmp . state [ i ] = d . state [ i ] ;
}
}
}
if ( d && typeof d . li _attr === 'object' ) {
for ( i in d . li _attr ) {
if ( d . li _attr . hasOwnProperty ( i ) ) {
tmp . li _attr [ i ] = d . li _attr [ i ] ;
}
}
}
if ( ! tmp . li _attr . id ) {
tmp . li _attr . id = tid ;
}
if ( d && typeof d . a _attr === 'object' ) {
for ( i in d . a _attr ) {
if ( d . a _attr . hasOwnProperty ( i ) ) {
tmp . a _attr [ i ] = d . a _attr [ i ] ;
}
}
}
if ( d && d . children && d . children === true ) {
tmp . state . loaded = false ;
tmp . children = [ ] ;
tmp . children _d = [ ] ;
}
m [ tmp . id ] = tmp ;
for ( i = 0 , j = tmp . children . length ; i < j ; i ++ ) {
c = parse _flat ( m [ tmp . children [ i ] ] , tmp . id , ps ) ;
e = m [ c ] ;
tmp . children _d . push ( c ) ;
if ( e . children _d . length ) {
tmp . children _d = tmp . children _d . concat ( e . children _d ) ;
}
}
delete d . data ;
delete d . children ;
m [ tmp . id ] . original = d ;
if ( tmp . state . selected ) {
add . push ( tmp . id ) ;
}
return tmp . id ;
} ,
parse _nest = function ( d , p , ps ) {
if ( ! ps ) { ps = [ ] ; }
else { ps = ps . concat ( ) ; }
if ( p ) { ps . unshift ( p ) ; }
var tid = false , i , j , c , e , tmp ;
do {
tid = 'j' + t _id + '_' + ( ++ t _cnt ) ;
} while ( m [ tid ] ) ;
tmp = {
id : false ,
text : typeof d === 'string' ? d : '' ,
icon : typeof d === 'object' && d . icon !== undefined ? d . icon : true ,
parent : p ,
parents : ps ,
children : [ ] ,
children _d : [ ] ,
data : null ,
state : { } ,
li _attr : { id : false } ,
a _attr : { href : '#' } ,
original : false
} ;
for ( i in df ) {
if ( df . hasOwnProperty ( i ) ) {
tmp . state [ i ] = df [ i ] ;
}
}
if ( d && d . id ) { tmp . id = d . id . toString ( ) ; }
if ( d && d . text ) { tmp . text = d . text ; }
if ( d && d . data && d . data . jstree && d . data . jstree . icon ) {
tmp . icon = d . data . jstree . icon ;
}
if ( tmp . icon === undefined || tmp . icon === null || tmp . icon === "" ) {
tmp . icon = true ;
}
if ( d && d . data ) {
tmp . data = d . data ;
if ( d . data . jstree ) {
for ( i in d . data . jstree ) {
if ( d . data . jstree . hasOwnProperty ( i ) ) {
tmp . state [ i ] = d . data . jstree [ i ] ;
}
}
}
}
if ( d && typeof d . state === 'object' ) {
for ( i in d . state ) {
if ( d . state . hasOwnProperty ( i ) ) {
tmp . state [ i ] = d . state [ i ] ;
}
}
}
if ( d && typeof d . li _attr === 'object' ) {
for ( i in d . li _attr ) {
if ( d . li _attr . hasOwnProperty ( i ) ) {
tmp . li _attr [ i ] = d . li _attr [ i ] ;
}
}
}
if ( tmp . li _attr . id && ! tmp . id ) {
tmp . id = tmp . li _attr . id . toString ( ) ;
}
if ( ! tmp . id ) {
tmp . id = tid ;
}
if ( ! tmp . li _attr . id ) {
tmp . li _attr . id = tmp . id ;
}
if ( d && typeof d . a _attr === 'object' ) {
for ( i in d . a _attr ) {
if ( d . a _attr . hasOwnProperty ( i ) ) {
tmp . a _attr [ i ] = d . a _attr [ i ] ;
}
}
}
if ( d && d . children && d . children . length ) {
for ( i = 0 , j = d . children . length ; i < j ; i ++ ) {
c = parse _nest ( d . children [ i ] , tmp . id , ps ) ;
e = m [ c ] ;
tmp . children . push ( c ) ;
if ( e . children _d . length ) {
tmp . children _d = tmp . children _d . concat ( e . children _d ) ;
}
}
tmp . children _d = tmp . children _d . concat ( tmp . children ) ;
}
if ( d && d . children && d . children === true ) {
tmp . state . loaded = false ;
tmp . children = [ ] ;
tmp . children _d = [ ] ;
}
delete d . data ;
delete d . children ;
tmp . original = d ;
m [ tmp . id ] = tmp ;
if ( tmp . state . selected ) {
add . push ( tmp . id ) ;
}
return tmp . id ;
} ;
if ( dat . length && dat [ 0 ] . id !== undefined && dat [ 0 ] . parent !== undefined ) {
// Flat JSON support (for easy import from DB):
// 1) convert to object (foreach)
for ( i = 0 , j = dat . length ; i < j ; i ++ ) {
if ( ! dat [ i ] . children ) {
dat [ i ] . children = [ ] ;
}
m [ dat [ i ] . id . toString ( ) ] = dat [ i ] ;
}
// 2) populate children (foreach)
for ( i = 0 , j = dat . length ; i < j ; i ++ ) {
m [ dat [ i ] . parent . toString ( ) ] . children . push ( dat [ i ] . id . toString ( ) ) ;
// populate parent.children_d
p . children _d . push ( dat [ i ] . id . toString ( ) ) ;
}
// 3) normalize && populate parents and children_d with recursion
for ( i = 0 , j = p . children . length ; i < j ; i ++ ) {
tmp = parse _flat ( m [ p . children [ i ] ] , par , p . parents . concat ( ) ) ;
dpc . push ( tmp ) ;
if ( m [ tmp ] . children _d . length ) {
dpc = dpc . concat ( m [ tmp ] . children _d ) ;
}
}
for ( i = 0 , j = p . parents . length ; i < j ; i ++ ) {
m [ p . parents [ i ] ] . children _d = m [ p . parents [ i ] ] . children _d . concat ( dpc ) ;
}
// ?) three_state selection - p.state.selected && t - (if three_state foreach(dat => ch) -> foreach(parents) if(parent.selected) child.selected = true;
rslt = {
'cnt' : t _cnt ,
'mod' : m ,
'sel' : sel ,
'par' : par ,
'dpc' : dpc ,
'add' : add
} ;
}
else {
for ( i = 0 , j = dat . length ; i < j ; i ++ ) {
tmp = parse _nest ( dat [ i ] , par , p . parents . concat ( ) ) ;
if ( tmp ) {
chd . push ( tmp ) ;
dpc . push ( tmp ) ;
if ( m [ tmp ] . children _d . length ) {
dpc = dpc . concat ( m [ tmp ] . children _d ) ;
}
}
}
p . children = chd ;
p . children _d = dpc ;
for ( i = 0 , j = p . parents . length ; i < j ; i ++ ) {
m [ p . parents [ i ] ] . children _d = m [ p . parents [ i ] ] . children _d . concat ( dpc ) ;
}
rslt = {
'cnt' : t _cnt ,
'mod' : m ,
'sel' : sel ,
'par' : par ,
'dpc' : dpc ,
'add' : add
} ;
}
if ( typeof window === 'undefined' || typeof window . document === 'undefined' ) {
postMessage ( rslt ) ;
}
else {
return rslt ;
}
} ,
rslt = function ( rslt , worker ) {
if ( this . element === null ) { return ; }
this . _cnt = rslt . cnt ;
this . _model . data = rslt . mod ; // breaks the reference in load_node - careful
if ( worker ) {
var i , j , a = rslt . add , r = rslt . sel , s = this . _data . core . selected . slice ( ) , m = this . _model . data ;
// if selection was changed while calculating in worker
if ( r . length !== s . length || $ . vakata . array _unique ( r . concat ( s ) ) . length !== r . length ) {
// deselect nodes that are no longer selected
for ( i = 0 , j = r . length ; i < j ; i ++ ) {
if ( $ . inArray ( r [ i ] , a ) === - 1 && $ . inArray ( r [ i ] , s ) === - 1 ) {
m [ r [ i ] ] . state . selected = false ;
}
}
// select nodes that were selected in the mean time
for ( i = 0 , j = s . length ; i < j ; i ++ ) {
if ( $ . inArray ( s [ i ] , r ) === - 1 ) {
m [ s [ i ] ] . state . selected = true ;
}
}
}
}
if ( rslt . add . length ) {
this . _data . core . selected = this . _data . core . selected . concat ( rslt . add ) ;
}
this . trigger ( 'model' , { "nodes" : rslt . dpc , 'parent' : rslt . par } ) ;
if ( rslt . par !== '#' ) {
this . _node _changed ( rslt . par ) ;
this . redraw ( ) ;
}
else {
// this.get_container_ul().children('.jstree-initial-node').remove();
this . redraw ( true ) ;
}
if ( rslt . add . length ) {
this . trigger ( 'changed' , { 'action' : 'model' , 'selected' : this . _data . core . selected } ) ;
}
cb . call ( this , true ) ;
} ;
if ( this . settings . core . worker && window . Blob && window . URL && window . Worker ) {
try {
if ( this . _wrk === null ) {
this . _wrk = window . URL . createObjectURL (
new window . Blob (
[ 'self.onmessage = ' + func . toString ( ) ] ,
{ type : "text/javascript" }
)
) ;
}
if ( ! this . _data . core . working || force _processing ) {
this . _data . core . working = true ;
w = new window . Worker ( this . _wrk ) ;
w . onmessage = $ . proxy ( function ( e ) {
rslt . call ( this , e . data , true ) ;
try { w . terminate ( ) ; w = null ; } catch ( ignore ) { }
if ( this . _data . core . worker _queue . length ) {
this . _append _json _data . apply ( this , this . _data . core . worker _queue . shift ( ) ) ;
}
else {
this . _data . core . working = false ;
}
} , this ) ;
if ( ! args . par ) {
if ( this . _data . core . worker _queue . length ) {
this . _append _json _data . apply ( this , this . _data . core . worker _queue . shift ( ) ) ;
}
else {
this . _data . core . working = false ;
}
}
else {
w . postMessage ( args ) ;
}
}
else {
this . _data . core . worker _queue . push ( [ dom , data , cb , true ] ) ;
}
}
catch ( e ) {
rslt . call ( this , func ( args ) , false ) ;
if ( this . _data . core . worker _queue . length ) {
this . _append _json _data . apply ( this , this . _data . core . worker _queue . shift ( ) ) ;
}
else {
this . _data . core . working = false ;
}
}
}
else {
rslt . call ( this , func ( args ) , false ) ;
}
} ,
/ * *
* parses a node from a jQuery object and appends them to the in memory tree model . Used internally .
* @ private
* @ name _parse _model _from _html ( d [ , p , ps ] )
* @ param { jQuery } d the jQuery object to parse
* @ param { String } p the parent ID
* @ param { Array } ps list of all parents
* @ return { String } the ID of the object added to the model
* /
_parse _model _from _html : function ( d , p , ps ) {
if ( ! ps ) { ps = [ ] ; }
else { ps = [ ] . concat ( ps ) ; }
if ( p ) { ps . unshift ( p ) ; }
var c , e , m = this . _model . data ,
data = {
id : false ,
text : false ,
icon : true ,
parent : p ,
parents : ps ,
children : [ ] ,
children _d : [ ] ,
data : null ,
state : { } ,
li _attr : { id : false } ,
a _attr : { href : '#' } ,
original : false
} , i , tmp , tid ;
for ( i in this . _model . default _state ) {
if ( this . _model . default _state . hasOwnProperty ( i ) ) {
data . state [ i ] = this . _model . default _state [ i ] ;
}
}
tmp = $ . vakata . attributes ( d , true ) ;
$ . each ( tmp , function ( i , v ) {
v = $ . trim ( v ) ;
if ( ! v . length ) { return true ; }
data . li _attr [ i ] = v ;
if ( i === 'id' ) {
data . id = v . toString ( ) ;
}
} ) ;
tmp = d . children ( 'a' ) . first ( ) ;
if ( tmp . length ) {
tmp = $ . vakata . attributes ( tmp , true ) ;
$ . each ( tmp , function ( i , v ) {
v = $ . trim ( v ) ;
if ( v . length ) {
data . a _attr [ i ] = v ;
}
} ) ;
}
tmp = d . children ( "a" ) . first ( ) . length ? d . children ( "a" ) . first ( ) . clone ( ) : d . clone ( ) ;
tmp . children ( "ins, i, ul" ) . remove ( ) ;
tmp = tmp . html ( ) ;
tmp = $ ( '<div />' ) . html ( tmp ) ;
data . text = this . settings . core . force _text ? tmp . text ( ) : tmp . html ( ) ;
tmp = d . data ( ) ;
data . data = tmp ? $ . extend ( true , { } , tmp ) : null ;
data . state . opened = d . hasClass ( 'jstree-open' ) ;
data . state . selected = d . children ( 'a' ) . hasClass ( 'jstree-clicked' ) ;
data . state . disabled = d . children ( 'a' ) . hasClass ( 'jstree-disabled' ) ;
if ( data . data && data . data . jstree ) {
for ( i in data . data . jstree ) {
if ( data . data . jstree . hasOwnProperty ( i ) ) {
data . state [ i ] = data . data . jstree [ i ] ;
}
}
}
tmp = d . children ( "a" ) . children ( ".jstree-themeicon" ) ;
if ( tmp . length ) {
data . icon = tmp . hasClass ( 'jstree-themeicon-hidden' ) ? false : tmp . attr ( 'rel' ) ;
}
if ( data . state . icon !== undefined ) {
data . icon = data . state . icon ;
}
if ( data . icon === undefined || data . icon === null || data . icon === "" ) {
data . icon = true ;
}
tmp = d . children ( "ul" ) . children ( "li" ) ;
do {
tid = 'j' + this . _id + '_' + ( ++ this . _cnt ) ;
} while ( m [ tid ] ) ;
data . id = data . li _attr . id ? data . li _attr . id . toString ( ) : tid ;
if ( tmp . length ) {
tmp . each ( $ . proxy ( function ( i , v ) {
c = this . _parse _model _from _html ( $ ( v ) , data . id , ps ) ;
e = this . _model . data [ c ] ;
data . children . push ( c ) ;
if ( e . children _d . length ) {
data . children _d = data . children _d . concat ( e . children _d ) ;
}
} , this ) ) ;
data . children _d = data . children _d . concat ( data . children ) ;
}
else {
if ( d . hasClass ( 'jstree-closed' ) ) {
data . state . loaded = false ;
}
}
if ( data . li _attr [ 'class' ] ) {
data . li _attr [ 'class' ] = data . li _attr [ 'class' ] . replace ( 'jstree-closed' , '' ) . replace ( 'jstree-open' , '' ) ;
}
if ( data . a _attr [ 'class' ] ) {
data . a _attr [ 'class' ] = data . a _attr [ 'class' ] . replace ( 'jstree-clicked' , '' ) . replace ( 'jstree-disabled' , '' ) ;
}
m [ data . id ] = data ;
if ( data . state . selected ) {
this . _data . core . selected . push ( data . id ) ;
}
return data . id ;
} ,
/ * *
* 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 .
* @ private
* @ name _parse _model _from _flat _json ( d [ , p , ps ] )
* @ param { Object } d the JSON object to parse
* @ param { String } p the parent ID
* @ param { Array } ps list of all parents
* @ return { String } the ID of the object added to the model
* /
_parse _model _from _flat _json : function ( d , p , ps ) {
if ( ! ps ) { ps = [ ] ; }
else { ps = ps . concat ( ) ; }
if ( p ) { ps . unshift ( p ) ; }
var tid = d . id . toString ( ) ,
m = this . _model . data ,
df = this . _model . default _state ,
i , j , c , e ,
tmp = {
id : tid ,
text : d . text || '' ,
icon : d . icon !== undefined ? d . icon : true ,
parent : p ,
parents : ps ,
children : d . children || [ ] ,
children _d : d . children _d || [ ] ,
data : d . data ,
state : { } ,
li _attr : { id : false } ,
a _attr : { href : '#' } ,
original : false
} ;
for ( i in df ) {
if ( df . hasOwnProperty ( i ) ) {
tmp . state [ i ] = df [ i ] ;
}
}
if ( d && d . data && d . data . jstree && d . data . jstree . icon ) {
tmp . icon = d . data . jstree . icon ;
}
if ( tmp . icon === undefined || tmp . icon === null || tmp . icon === "" ) {
tmp . icon = true ;
}
if ( d && d . data ) {
tmp . data = d . data ;
if ( d . data . jstree ) {
for ( i in d . data . jstree ) {
if ( d . data . jstree . hasOwnProperty ( i ) ) {
tmp . state [ i ] = d . data . jstree [ i ] ;
}
}
}
}
if ( d && typeof d . state === 'object' ) {
for ( i in d . state ) {
if ( d . state . hasOwnProperty ( i ) ) {
tmp . state [ i ] = d . state [ i ] ;
}
}
}
if ( d && typeof d . li _attr === 'object' ) {
for ( i in d . li _attr ) {
if ( d . li _attr . hasOwnProperty ( i ) ) {
tmp . li _attr [ i ] = d . li _attr [ i ] ;
}
}
}
if ( ! tmp . li _attr . id ) {
tmp . li _attr . id = tid ;
}
if ( d && typeof d . a _attr === 'object' ) {
for ( i in d . a _attr ) {
if ( d . a _attr . hasOwnProperty ( i ) ) {
tmp . a _attr [ i ] = d . a _attr [ i ] ;
}
}
}
if ( d && d . children && d . children === true ) {
tmp . state . loaded = false ;
tmp . children = [ ] ;
tmp . children _d = [ ] ;
}
m [ tmp . id ] = tmp ;
for ( i = 0 , j = tmp . children . length ; i < j ; i ++ ) {
c = this . _parse _model _from _flat _json ( m [ tmp . children [ i ] ] , tmp . id , ps ) ;
e = m [ c ] ;
tmp . children _d . push ( c ) ;
if ( e . children _d . length ) {
tmp . children _d = tmp . children _d . concat ( e . children _d ) ;
}
}
delete d . data ;
delete d . children ;
m [ tmp . id ] . original = d ;
if ( tmp . state . selected ) {
this . _data . core . selected . push ( tmp . id ) ;
}
return tmp . id ;
} ,
/ * *
* parses a node from a JSON object and appends it to the in memory tree model . Used internally .
* @ private
* @ name _parse _model _from _json ( d [ , p , ps ] )
* @ param { Object } d the JSON object to parse
* @ param { String } p the parent ID
* @ param { Array } ps list of all parents
* @ return { String } the ID of the object added to the model
* /
_parse _model _from _json : function ( d , p , ps ) {
if ( ! ps ) { ps = [ ] ; }
else { ps = ps . concat ( ) ; }
if ( p ) { ps . unshift ( p ) ; }
var tid = false , i , j , c , e , m = this . _model . data , df = this . _model . default _state , tmp ;
do {
tid = 'j' + this . _id + '_' + ( ++ this . _cnt ) ;
} while ( m [ tid ] ) ;
tmp = {
id : false ,
text : typeof d === 'string' ? d : '' ,
icon : typeof d === 'object' && d . icon !== undefined ? d . icon : true ,
parent : p ,
parents : ps ,
children : [ ] ,
children _d : [ ] ,
data : null ,
state : { } ,
li _attr : { id : false } ,
a _attr : { href : '#' } ,
original : false
} ;
for ( i in df ) {
if ( df . hasOwnProperty ( i ) ) {
tmp . state [ i ] = df [ i ] ;
}
}
if ( d && d . id ) { tmp . id = d . id . toString ( ) ; }
if ( d && d . text ) { tmp . text = d . text ; }
if ( d && d . data && d . data . jstree && d . data . jstree . icon ) {
tmp . icon = d . data . jstree . icon ;
}
if ( tmp . icon === undefined || tmp . icon === null || tmp . icon === "" ) {
tmp . icon = true ;
}
if ( d && d . data ) {
tmp . data = d . data ;
if ( d . data . jstree ) {
for ( i in d . data . jstree ) {
if ( d . data . jstree . hasOwnProperty ( i ) ) {
tmp . state [ i ] = d . data . jstree [ i ] ;
}
}
}
}
if ( d && typeof d . state === 'object' ) {
for ( i in d . state ) {
if ( d . state . hasOwnProperty ( i ) ) {
tmp . state [ i ] = d . state [ i ] ;
}
}
}
if ( d && typeof d . li _attr === 'object' ) {
for ( i in d . li _attr ) {
if ( d . li _attr . hasOwnProperty ( i ) ) {
tmp . li _attr [ i ] = d . li _attr [ i ] ;
}
}
}
if ( tmp . li _attr . id && ! tmp . id ) {
tmp . id = tmp . li _attr . id . toString ( ) ;
}
if ( ! tmp . id ) {
tmp . id = tid ;
}
if ( ! tmp . li _attr . id ) {
tmp . li _attr . id = tmp . id ;
}
if ( d && typeof d . a _attr === 'object' ) {
for ( i in d . a _attr ) {
if ( d . a _attr . hasOwnProperty ( i ) ) {
tmp . a _attr [ i ] = d . a _attr [ i ] ;
}
}
}
if ( d && d . children && d . children . length ) {
for ( i = 0 , j = d . children . length ; i < j ; i ++ ) {
c = this . _parse _model _from _json ( d . children [ i ] , tmp . id , ps ) ;
e = m [ c ] ;
tmp . children . push ( c ) ;
if ( e . children _d . length ) {
tmp . children _d = tmp . children _d . concat ( e . children _d ) ;
}
}
tmp . children _d = tmp . children _d . concat ( tmp . children ) ;
}
if ( d && d . children && d . children === true ) {
tmp . state . loaded = false ;
tmp . children = [ ] ;
tmp . children _d = [ ] ;
}
delete d . data ;
delete d . children ;
tmp . original = d ;
m [ tmp . id ] = tmp ;
if ( tmp . state . selected ) {
this . _data . core . selected . push ( tmp . id ) ;
}
return tmp . id ;
} ,
/ * *
* redraws all nodes that need to be redrawn . Used internally .
* @ private
* @ name _redraw ( )
* @ trigger redraw . jstree
* /
_redraw : function ( ) {
var nodes = this . _model . force _full _redraw ? this . _model . data [ '#' ] . children . concat ( [ ] ) : this . _model . changed . concat ( [ ] ) ,
f = document . createElement ( 'UL' ) , tmp , i , j , fe = this . _data . core . focused ;
for ( i = 0 , j = nodes . length ; i < j ; i ++ ) {
tmp = this . redraw _node ( nodes [ i ] , true , this . _model . force _full _redraw ) ;
if ( tmp && this . _model . force _full _redraw ) {
f . appendChild ( tmp ) ;
}
}
if ( this . _model . force _full _redraw ) {
f . className = this . get _container _ul ( ) [ 0 ] . className ;
f . setAttribute ( 'role' , 'group' ) ;
this . element . empty ( ) . append ( f ) ;
//this.get_container_ul()[0].appendChild(f);
}
if ( fe !== null ) {
tmp = this . get _node ( fe , true ) ;
if ( tmp && tmp . length && tmp . children ( '.jstree-anchor' ) [ 0 ] !== document . activeElement ) {
tmp . children ( '.jstree-anchor' ) . focus ( ) ;
}
else {
this . _data . core . focused = null ;
}
}
this . _model . force _full _redraw = false ;
this . _model . changed = [ ] ;
/ * *
* triggered after nodes are redrawn
* @ event
* @ name redraw . jstree
* @ param { array } nodes the redrawn nodes
* /
this . trigger ( 'redraw' , { "nodes" : nodes } ) ;
} ,
/ * *
* redraws all nodes that need to be redrawn or optionally - the whole tree
* @ name redraw ( [ full ] )
* @ param { Boolean } full if set to ` true ` all nodes are redrawn .
* /
redraw : function ( full ) {
if ( full ) {
this . _model . force _full _redraw = true ;
}
//if(this._model.redraw_timeout) {
// clearTimeout(this._model.redraw_timeout);
//}
//this._model.redraw_timeout = setTimeout($.proxy(this._redraw, this),0);
this . _redraw ( ) ;
} ,
/ * *
* redraws a single node ' s children . Used internally .
* @ private
* @ name draw _children ( node )
* @ param { mixed } node the node whose children will be redrawn
* /
draw _children : function ( node ) {
var obj = this . get _node ( node ) ,
i = false ,
j = false ,
k = false ,
d = document ;
if ( ! obj ) { return false ; }
if ( obj . id === '#' ) { return this . redraw ( true ) ; }
node = this . get _node ( node , true ) ;
if ( ! node || ! node . length ) { return false ; } // TODO: quick toggle
node . children ( '.jstree-children' ) . remove ( ) ;
node = node [ 0 ] ;
if ( obj . children . length && obj . state . loaded ) {
k = d . createElement ( 'UL' ) ;
k . setAttribute ( 'role' , 'group' ) ;
k . className = 'jstree-children' ;
for ( i = 0 , j = obj . children . length ; i < j ; i ++ ) {
k . appendChild ( this . redraw _node ( obj . children [ i ] , true , true ) ) ;
}
node . appendChild ( k ) ;
}
} ,
/ * *
* redraws a single node . Used internally .
* @ private
* @ name redraw _node ( node , deep , is _callback , force _render )
* @ param { mixed } node the node to redraw
* @ param { Boolean } deep should child nodes be redrawn too
* @ param { Boolean } is _callback is this a recursion call
* @ param { Boolean } force _render should children of closed parents be drawn anyway
* /
redraw _node : function ( node , deep , is _callback , force _render ) {
var obj = this . get _node ( node ) ,
par = false ,
ind = false ,
old = false ,
i = false ,
j = false ,
k = false ,
c = '' ,
d = document ,
m = this . _model . data ,
f = false ,
s = false ,
tmp = null ,
t = 0 ,
l = 0 ;
if ( ! obj ) { return false ; }
if ( obj . id === '#' ) { return this . redraw ( true ) ; }
deep = deep || obj . children . length === 0 ;
node = ! document . querySelector ? document . getElementById ( obj . id ) : this . element [ 0 ] . querySelector ( '#' + ( "0123456789" . indexOf ( obj . id [ 0 ] ) !== - 1 ? '\\3' + obj . id [ 0 ] + ' ' + obj . id . substr ( 1 ) . replace ( $ . jstree . idregex , '\\$&' ) : obj . id . replace ( $ . jstree . idregex , '\\$&' ) ) ) ; //, this.element);
if ( ! node ) {
deep = true ;
//node = d.createElement('LI');
if ( ! is _callback ) {
par = obj . parent !== '#' ? $ ( '#' + obj . parent . replace ( $ . jstree . idregex , '\\$&' ) , this . element ) [ 0 ] : null ;
if ( par !== null && ( ! par || ! m [ obj . parent ] . state . opened ) ) {
return false ;
}
ind = $ . inArray ( obj . id , par === null ? m [ '#' ] . children : m [ obj . parent ] . children ) ;
}
}
else {
node = $ ( node ) ;
if ( ! is _callback ) {
par = node . parent ( ) . parent ( ) [ 0 ] ;
if ( par === this . element [ 0 ] ) {
par = null ;
}
ind = node . index ( ) ;
}
// m[obj.id].data = node.data(); // use only node's data, no need to touch jquery storage
if ( ! deep && obj . children . length && ! node . children ( '.jstree-children' ) . length ) {
deep = true ;
}
if ( ! deep ) {
old = node . children ( '.jstree-children' ) [ 0 ] ;
}
f = node . children ( '.jstree-anchor' ) [ 0 ] === document . activeElement ;
node . remove ( ) ;
//node = d.createElement('LI');
//node = node[0];
}
node = _node . cloneNode ( true ) ;
// node is DOM, deep is boolean
c = 'jstree-node ' ;
for ( i in obj . li _attr ) {
if ( obj . li _attr . hasOwnProperty ( i ) ) {
if ( i === 'id' ) { continue ; }
if ( i !== 'class' ) {
node . setAttribute ( i , obj . li _attr [ i ] ) ;
}
else {
c += obj . li _attr [ i ] ;
}
}
}
if ( ! obj . a _attr . id ) {
obj . a _attr . id = obj . id + '_anchor' ;
}
node . setAttribute ( 'aria-selected' , ! ! obj . state . selected ) ;
node . setAttribute ( 'aria-level' , obj . parents . length ) ;
node . setAttribute ( 'aria-labelledby' , obj . a _attr . id ) ;
if ( obj . state . disabled ) {
node . setAttribute ( 'aria-disabled' , true ) ;
}
if ( obj . state . loaded && ! obj . children . length ) {
c += ' jstree-leaf' ;
}
else {
c += obj . state . opened && obj . state . loaded ? ' jstree-open' : ' jstree-closed' ;
node . setAttribute ( 'aria-expanded' , ( obj . state . opened && obj . state . loaded ) ) ;
}
if ( obj . parent !== null && m [ obj . parent ] . children [ m [ obj . parent ] . children . length - 1 ] === obj . id ) {
c += ' jstree-last' ;
}
node . id = obj . id ;
node . className = c ;
c = ( obj . state . selected ? ' jstree-clicked' : '' ) + ( obj . state . disabled ? ' jstree-disabled' : '' ) ;
for ( j in obj . a _attr ) {
if ( obj . a _attr . hasOwnProperty ( j ) ) {
if ( j === 'href' && obj . a _attr [ j ] === '#' ) { continue ; }
if ( j !== 'class' ) {
node . childNodes [ 1 ] . setAttribute ( j , obj . a _attr [ j ] ) ;
}
else {
c += ' ' + obj . a _attr [ j ] ;
}
}
}
if ( c . length ) {
node . childNodes [ 1 ] . className = 'jstree-anchor ' + c ;
}
if ( ( obj . icon && obj . icon !== true ) || obj . icon === false ) {
if ( obj . icon === false ) {
node . childNodes [ 1 ] . childNodes [ 0 ] . className += ' jstree-themeicon-hidden' ;
}
else if ( obj . icon . indexOf ( '/' ) === - 1 && obj . icon . indexOf ( '.' ) === - 1 ) {
node . childNodes [ 1 ] . childNodes [ 0 ] . className += ' ' + obj . icon + ' jstree-themeicon-custom' ;
}
else {
node . childNodes [ 1 ] . childNodes [ 0 ] . style . backgroundImage = 'url(' + obj . icon + ')' ;
node . childNodes [ 1 ] . childNodes [ 0 ] . style . backgroundPosition = 'center center' ;
node . childNodes [ 1 ] . childNodes [ 0 ] . style . backgroundSize = 'auto' ;
node . childNodes [ 1 ] . childNodes [ 0 ] . className += ' jstree-themeicon-custom' ;
}
}
if ( this . settings . core . force _text ) {
node . childNodes [ 1 ] . appendChild ( d . createTextNode ( obj . text ) ) ;
}
else {
node . childNodes [ 1 ] . innerHTML += obj . text ;
}
if ( deep && obj . children . length && ( obj . state . opened || force _render ) && obj . state . loaded ) {
k = d . createElement ( 'UL' ) ;
k . setAttribute ( 'role' , 'group' ) ;
k . className = 'jstree-children' ;
for ( i = 0 , j = obj . children . length ; i < j ; i ++ ) {
k . appendChild ( this . redraw _node ( obj . children [ i ] , deep , true ) ) ;
}
node . appendChild ( k ) ;
}
if ( old ) {
node . appendChild ( old ) ;
}
if ( ! is _callback ) {
// append back using par / ind
if ( ! par ) {
par = this . element [ 0 ] ;
}
for ( i = 0 , j = par . childNodes . length ; i < j ; i ++ ) {
if ( par . childNodes [ i ] && par . childNodes [ i ] . className && par . childNodes [ i ] . className . indexOf ( 'jstree-children' ) !== - 1 ) {
tmp = par . childNodes [ i ] ;
break ;
}
}
if ( ! tmp ) {
tmp = d . createElement ( 'UL' ) ;
tmp . setAttribute ( 'role' , 'group' ) ;
tmp . className = 'jstree-children' ;
par . appendChild ( tmp ) ;
}
par = tmp ;
if ( ind < par . childNodes . length ) {
par . insertBefore ( node , par . childNodes [ ind ] ) ;
}
else {
par . appendChild ( node ) ;
}
if ( f ) {
t = this . element [ 0 ] . scrollTop ;
l = this . element [ 0 ] . scrollLeft ;
node . childNodes [ 1 ] . focus ( ) ;
this . element [ 0 ] . scrollTop = t ;
this . element [ 0 ] . scrollLeft = l ;
}
}
if ( obj . state . opened && ! obj . state . loaded ) {
obj . state . opened = false ;
setTimeout ( $ . proxy ( function ( ) {
this . open _node ( obj . id , false , 0 ) ;
} , this ) , 0 ) ;
}
return node ;
} ,
/ * *
* opens a node , revaling its children . If the node is not loaded it will be loaded and opened once ready .
* @ name open _node ( obj [ , callback , animation ] )
* @ param { mixed } obj the node to open
* @ param { Function } callback a function to execute once the node is opened
* @ param { Number } animation the animation duration in milliseconds when opening the node ( overrides the ` core.animation ` setting ) . Use ` false ` for no animation .
* @ trigger open _node . jstree , after _open . jstree , before _open . jstree
* /
open _node : function ( obj , callback , animation ) {
var t1 , t2 , d , t ;
if ( $ . isArray ( obj ) ) {
obj = obj . slice ( ) ;
for ( t1 = 0 , t2 = obj . length ; t1 < t2 ; t1 ++ ) {
this . open _node ( obj [ t1 ] , callback , animation ) ;
}
return true ;
}
obj = this . get _node ( obj ) ;
if ( ! obj || obj . id === '#' ) {
return false ;
}
animation = animation === undefined ? this . settings . core . animation : animation ;
if ( ! this . is _closed ( obj ) ) {
if ( callback ) {
callback . call ( this , obj , false ) ;
}
return false ;
}
if ( ! this . is _loaded ( obj ) ) {
if ( this . is _loading ( obj ) ) {
return setTimeout ( $ . proxy ( function ( ) {
this . open _node ( obj , callback , animation ) ;
} , this ) , 500 ) ;
}
this . load _node ( obj , function ( o , ok ) {
return ok ? this . open _node ( o , callback , animation ) : ( callback ? callback . call ( this , o , false ) : false ) ;
} ) ;
}
else {
d = this . get _node ( obj , true ) ;
t = this ;
if ( d . length ) {
if ( animation && d . children ( ".jstree-children" ) . length ) {
2016-02-24 10:43:06 -07:00
//d.children(".jstree-children").stop(true, true);
2015-07-16 06:06:02 -07:00
}
if ( obj . children . length && ! this . _firstChild ( d . children ( '.jstree-children' ) [ 0 ] ) ) {
this . draw _children ( obj ) ;
//d = this.get_node(obj, true);
}
if ( ! animation ) {
this . trigger ( 'before_open' , { "node" : obj } ) ;
d [ 0 ] . className = d [ 0 ] . className . replace ( 'jstree-closed' , 'jstree-open' ) ;
d [ 0 ] . setAttribute ( "aria-expanded" , true ) ;
}
else {
this . trigger ( 'before_open' , { "node" : obj } ) ;
d
. children ( ".jstree-children" ) . css ( "display" , "none" ) . end ( )
. removeClass ( "jstree-closed" ) . addClass ( "jstree-open" ) . attr ( "aria-expanded" , true )
2016-02-24 10:43:06 -07:00
. children ( ".jstree-children" ) . show ( ) ;
t . trigger ( "after_open" , { "node" : obj } ) ;
2015-07-16 06:06:02 -07:00
}
}
obj . state . opened = true ;
if ( callback ) {
callback . call ( this , obj , true ) ;
}
if ( ! d . length ) {
/ * *
* triggered when a node is about to be opened ( if the node is supposed to be in the DOM , it will be , but it won ' t be visible yet )
* @ event
* @ name before _open . jstree
* @ param { Object } node the opened node
* /
this . trigger ( 'before_open' , { "node" : obj } ) ;
}
/ * *
* triggered when a node is opened ( if there is an animation it will not be completed yet )
* @ event
* @ name open _node . jstree
* @ param { Object } node the opened node
* /
this . trigger ( 'open_node' , { "node" : obj } ) ;
if ( ! animation || ! d . length ) {
/ * *
* triggered when a node is opened and the animation is complete
* @ event
* @ name after _open . jstree
* @ param { Object } node the opened node
* /
this . trigger ( "after_open" , { "node" : obj } ) ;
}
}
} ,
/ * *
* opens every parent of a node ( node should be loaded )
* @ name _open _to ( obj )
* @ param { mixed } obj the node to reveal
* @ private
* /
_open _to : function ( obj ) {
obj = this . get _node ( obj ) ;
if ( ! obj || obj . id === '#' ) {
return false ;
}
var i , j , p = obj . parents ;
for ( i = 0 , j = p . length ; i < j ; i += 1 ) {
if ( i !== '#' ) {
this . open _node ( p [ i ] , false , 0 ) ;
}
}
return $ ( '#' + obj . id . replace ( $ . jstree . idregex , '\\$&' ) , this . element ) ;
} ,
/ * *
* closes a node , hiding its children
* @ name close _node ( obj [ , animation ] )
* @ param { mixed } obj the node to close
* @ param { Number } animation the animation duration in milliseconds when closing the node ( overrides the ` core.animation ` setting ) . Use ` false ` for no animation .
* @ trigger close _node . jstree , after _close . jstree
* /
close _node : function ( obj , animation ) {
var t1 , t2 , t , d ;
if ( $ . isArray ( obj ) ) {
obj = obj . slice ( ) ;
for ( t1 = 0 , t2 = obj . length ; t1 < t2 ; t1 ++ ) {
this . close _node ( obj [ t1 ] , animation ) ;
}
return true ;
}
obj = this . get _node ( obj ) ;
if ( ! obj || obj . id === '#' ) {
return false ;
}
if ( this . is _closed ( obj ) ) {
return false ;
}
animation = animation === undefined ? this . settings . core . animation : animation ;
t = this ;
d = this . get _node ( obj , true ) ;
if ( d . length ) {
if ( ! animation ) {
d [ 0 ] . className = d [ 0 ] . className . replace ( 'jstree-open' , 'jstree-closed' ) ;
d . attr ( "aria-expanded" , false ) . children ( '.jstree-children' ) . remove ( ) ;
}
else {
d
. children ( ".jstree-children" ) . attr ( "style" , "display:block !important" ) . end ( )
. removeClass ( "jstree-open" ) . addClass ( "jstree-closed" ) . attr ( "aria-expanded" , false )
2016-02-24 10:43:06 -07:00
. children ( ".jstree-children" ) . hide ( ) ;
d . children ( '.jstree-children' ) . remove ( ) ;
t . trigger ( "after_close" , { "node" : obj } ) ;
2015-07-16 06:06:02 -07:00
}
}
obj . state . opened = false ;
/ * *
* triggered when a node is closed ( if there is an animation it will not be complete yet )
* @ event
* @ name close _node . jstree
* @ param { Object } node the closed node
* /
this . trigger ( 'close_node' , { "node" : obj } ) ;
if ( ! animation || ! d . length ) {
/ * *
* triggered when a node is closed and the animation is complete
* @ event
* @ name after _close . jstree
* @ param { Object } node the closed node
* /
this . trigger ( "after_close" , { "node" : obj } ) ;
}
} ,
/ * *
* toggles a node - closing it if it is open , opening it if it is closed
* @ name toggle _node ( obj )
* @ param { mixed } obj the node to toggle
* /
toggle _node : function ( obj ) {
var t1 , t2 ;
if ( $ . isArray ( obj ) ) {
obj = obj . slice ( ) ;
for ( t1 = 0 , t2 = obj . length ; t1 < t2 ; t1 ++ ) {
this . toggle _node ( obj [ t1 ] ) ;
}
return true ;
}
if ( this . is _closed ( obj ) ) {
return this . open _node ( obj ) ;
}
if ( this . is _open ( obj ) ) {
return this . close _node ( obj ) ;
}
} ,
/ * *
* 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 .
* @ name open _all ( [ obj , animation , original _obj ] )
* @ param { mixed } obj the node to open recursively , omit to open all nodes in the tree
* @ param { Number } animation the animation duration in milliseconds when opening the nodes , the default is no animation
* @ param { jQuery } reference to the node that started the process ( internal use )
* @ trigger open _all . jstree
* /
open _all : function ( obj , animation , original _obj ) {
if ( ! obj ) { obj = '#' ; }
obj = this . get _node ( obj ) ;
if ( ! obj ) { return false ; }
var dom = obj . id === '#' ? this . get _container _ul ( ) : this . get _node ( obj , true ) , i , j , _this ;
if ( ! dom . length ) {
for ( i = 0 , j = obj . children _d . length ; i < j ; i ++ ) {
if ( this . is _closed ( this . _model . data [ obj . children _d [ i ] ] ) ) {
this . _model . data [ obj . children _d [ i ] ] . state . opened = true ;
}
}
return this . trigger ( 'open_all' , { "node" : obj } ) ;
}
original _obj = original _obj || dom ;
_this = this ;
dom = this . is _closed ( obj ) ? dom . find ( '.jstree-closed' ) . addBack ( ) : dom . find ( '.jstree-closed' ) ;
dom . each ( function ( ) {
_this . open _node (
this ,
function ( node , status ) { if ( status && this . is _parent ( node ) ) { this . open _all ( node , animation , original _obj ) ; } } ,
animation || 0
) ;
} ) ;
if ( original _obj . find ( '.jstree-closed' ) . length === 0 ) {
/ * *
* triggered when an ` open_all ` call completes
* @ event
* @ name open _all . jstree
* @ param { Object } node the opened node
* /
this . trigger ( 'open_all' , { "node" : this . get _node ( original _obj ) } ) ;
}
} ,
/ * *
* closes all nodes within a node ( or the tree ) , revaling their children
* @ name close _all ( [ obj , animation ] )
* @ param { mixed } obj the node to close recursively , omit to close all nodes in the tree
* @ param { Number } animation the animation duration in milliseconds when closing the nodes , the default is no animation
* @ trigger close _all . jstree
* /
close _all : function ( obj , animation ) {
if ( ! obj ) { obj = '#' ; }
obj = this . get _node ( obj ) ;
if ( ! obj ) { return false ; }
var dom = obj . id === '#' ? this . get _container _ul ( ) : this . get _node ( obj , true ) ,
_this = this , i , j ;
if ( ! dom . length ) {
for ( i = 0 , j = obj . children _d . length ; i < j ; i ++ ) {
this . _model . data [ obj . children _d [ i ] ] . state . opened = false ;
}
return this . trigger ( 'close_all' , { "node" : obj } ) ;
}
dom = this . is _open ( obj ) ? dom . find ( '.jstree-open' ) . addBack ( ) : dom . find ( '.jstree-open' ) ;
$ ( dom . get ( ) . reverse ( ) ) . each ( function ( ) { _this . close _node ( this , animation || 0 ) ; } ) ;
/ * *
* triggered when an ` close_all ` call completes
* @ event
* @ name close _all . jstree
* @ param { Object } node the closed node
* /
this . trigger ( 'close_all' , { "node" : obj } ) ;
} ,
/ * *
* checks if a node is disabled ( not selectable )
* @ name is _disabled ( obj )
* @ param { mixed } obj
* @ return { Boolean }
* /
is _disabled : function ( obj ) {
obj = this . get _node ( obj ) ;
return obj && obj . state && obj . state . disabled ;
} ,
/ * *
* enables a node - so that it can be selected
* @ name enable _node ( obj )
* @ param { mixed } obj the node to enable
* @ trigger enable _node . jstree
* /
enable _node : function ( obj ) {
var t1 , t2 ;
if ( $ . isArray ( obj ) ) {
obj = obj . slice ( ) ;
for ( t1 = 0 , t2 = obj . length ; t1 < t2 ; t1 ++ ) {
this . enable _node ( obj [ t1 ] ) ;
}
return true ;
}
obj = this . get _node ( obj ) ;
if ( ! obj || obj . id === '#' ) {
return false ;
}
obj . state . disabled = false ;
this . get _node ( obj , true ) . children ( '.jstree-anchor' ) . removeClass ( 'jstree-disabled' ) . attr ( 'aria-disabled' , false ) ;
/ * *
* triggered when an node is enabled
* @ event
* @ name enable _node . jstree
* @ param { Object } node the enabled node
* /
this . trigger ( 'enable_node' , { 'node' : obj } ) ;
} ,
/ * *
* disables a node - so that it can not be selected
* @ name disable _node ( obj )
* @ param { mixed } obj the node to disable
* @ trigger disable _node . jstree
* /
disable _node : function ( obj ) {
var t1 , t2 ;
if ( $ . isArray ( obj ) ) {
obj = obj . slice ( ) ;
for ( t1 = 0 , t2 = obj . length ; t1 < t2 ; t1 ++ ) {
this . disable _node ( obj [ t1 ] ) ;
}
return true ;
}
obj = this . get _node ( obj ) ;
if ( ! obj || obj . id === '#' ) {
return false ;
}
obj . state . disabled = true ;
this . get _node ( obj , true ) . children ( '.jstree-anchor' ) . addClass ( 'jstree-disabled' ) . attr ( 'aria-disabled' , true ) ;
/ * *
* triggered when an node is disabled
* @ event
* @ name disable _node . jstree
* @ param { Object } node the disabled node
* /
this . trigger ( 'disable_node' , { 'node' : obj } ) ;
} ,
/ * *
* called when a node is selected by the user . Used internally .
* @ private
* @ name activate _node ( obj , e )
* @ param { mixed } obj the node
* @ param { Object } e the related event
* @ trigger activate _node . jstree , changed . jstree
* /
activate _node : function ( obj , e ) {
if ( this . is _disabled ( obj ) ) {
return false ;
}
// ensure last_clicked is still in the DOM, make it fresh (maybe it was moved?) and make sure it is still selected, if not - make last_clicked the last selected node
this . _data . core . last _clicked = this . _data . core . last _clicked && this . _data . core . last _clicked . id !== undefined ? this . get _node ( this . _data . core . last _clicked . id ) : null ;
if ( this . _data . core . last _clicked && ! this . _data . core . last _clicked . state . selected ) { this . _data . core . last _clicked = null ; }
if ( ! this . _data . core . last _clicked && this . _data . core . selected . length ) { this . _data . core . last _clicked = this . get _node ( this . _data . core . selected [ this . _data . core . selected . length - 1 ] ) ; }
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 ) ) ) {
if ( ! this . settings . core . multiple && ( e . metaKey || e . ctrlKey || e . shiftKey ) && this . is _selected ( obj ) ) {
this . deselect _node ( obj , false , e ) ;
}
else {
this . deselect _all ( true ) ;
this . select _node ( obj , false , false , e ) ;
this . _data . core . last _clicked = this . get _node ( obj ) ;
}
}
else {
if ( e . shiftKey ) {
var o = this . get _node ( obj ) . id ,
l = this . _data . core . last _clicked . id ,
p = this . get _node ( this . _data . core . last _clicked . parent ) . children ,
c = false ,
i , j ;
for ( i = 0 , j = p . length ; i < j ; i += 1 ) {
// separate IFs work whem o and l are the same
if ( p [ i ] === o ) {
c = ! c ;
}
if ( p [ i ] === l ) {
c = ! c ;
}
if ( ! this . is _disabled ( p [ i ] ) && ( c || p [ i ] === o || p [ i ] === l ) ) {
this . select _node ( p [ i ] , true , false , e ) ;
}
else {
this . deselect _node ( p [ i ] , true , e ) ;
}
}
this . trigger ( 'changed' , { 'action' : 'select_node' , 'node' : this . get _node ( obj ) , 'selected' : this . _data . core . selected , 'event' : e } ) ;
}
else {
if ( ! this . is _selected ( obj ) ) {
this . select _node ( obj , false , false , e ) ;
}
else {
this . deselect _node ( obj , false , e ) ;
}
}
}
/ * *
* triggered when an node is clicked or intercated with by the user
* @ event
* @ name activate _node . jstree
* @ param { Object } node
* /
this . trigger ( 'activate_node' , { 'node' : this . get _node ( obj ) } ) ;
} ,
/ * *
* applies the hover state on a node , called when a node is hovered by the user . Used internally .
* @ private
* @ name hover _node ( obj )
* @ param { mixed } obj
* @ trigger hover _node . jstree
* /
hover _node : function ( obj ) {
obj = this . get _node ( obj , true ) ;
if ( ! obj || ! obj . length || obj . children ( '.jstree-hovered' ) . length ) {
return false ;
}
var o = this . element . find ( '.jstree-hovered' ) , t = this . element ;
if ( o && o . length ) { this . dehover _node ( o ) ; }
obj . children ( '.jstree-anchor' ) . addClass ( 'jstree-hovered' ) ;
/ * *
* triggered when an node is hovered
* @ event
* @ name hover _node . jstree
* @ param { Object } node
* /
this . trigger ( 'hover_node' , { 'node' : this . get _node ( obj ) } ) ;
setTimeout ( function ( ) { t . attr ( 'aria-activedescendant' , obj [ 0 ] . id ) ; } , 0 ) ;
} ,
/ * *
* removes the hover state from a nodecalled when a node is no longer hovered by the user . Used internally .
* @ private
* @ name dehover _node ( obj )
* @ param { mixed } obj
* @ trigger dehover _node . jstree
* /
dehover _node : function ( obj ) {
obj = this . get _node ( obj , true ) ;
if ( ! obj || ! obj . length || ! obj . children ( '.jstree-hovered' ) . length ) {
return false ;
}
obj . children ( '.jstree-anchor' ) . removeClass ( 'jstree-hovered' ) ;
/ * *
* triggered when an node is no longer hovered
* @ event
* @ name dehover _node . jstree
* @ param { Object } node
* /
this . trigger ( 'dehover_node' , { 'node' : this . get _node ( obj ) } ) ;
} ,
/ * *
* select a node
* @ name select _node ( obj [ , supress _event , prevent _open ] )
* @ param { mixed } obj an array can be used to select multiple nodes
* @ param { Boolean } supress _event if set to ` true ` the ` changed.jstree ` event won ' t be triggered
* @ param { Boolean } prevent _open if set to ` true ` parents of the selected node won ' t be opened
* @ trigger select _node . jstree , changed . jstree
* /
select _node : function ( obj , supress _event , prevent _open , e ) {
var dom , t1 , t2 , th ;
if ( $ . isArray ( obj ) ) {
obj = obj . slice ( ) ;
for ( t1 = 0 , t2 = obj . length ; t1 < t2 ; t1 ++ ) {
this . select _node ( obj [ t1 ] , supress _event , prevent _open , e ) ;
}
return true ;
}
obj = this . get _node ( obj ) ;
if ( ! obj || obj . id === '#' ) {
return false ;
}
dom = this . get _node ( obj , true ) ;
if ( ! obj . state . selected ) {
obj . state . selected = true ;
this . _data . core . selected . push ( obj . id ) ;
if ( ! prevent _open ) {
dom = this . _open _to ( obj ) ;
}
if ( dom && dom . length ) {
dom . attr ( 'aria-selected' , true ) . children ( '.jstree-anchor' ) . addClass ( 'jstree-clicked' ) ;
}
/ * *
* triggered when an node is selected
* @ event
* @ name select _node . jstree
* @ param { Object } node
* @ param { Array } selected the current selection
* @ param { Object } event the event ( if any ) that triggered this select _node
* /
this . trigger ( 'select_node' , { 'node' : obj , 'selected' : this . _data . core . selected , 'event' : e } ) ;
if ( ! supress _event ) {
/ * *
* triggered when selection changes
* @ event
* @ name changed . jstree
* @ param { Object } node
* @ param { Object } action the action that caused the selection to change
* @ param { Array } selected the current selection
* @ param { Object } event the event ( if any ) that triggered this changed event
* /
this . trigger ( 'changed' , { 'action' : 'select_node' , 'node' : obj , 'selected' : this . _data . core . selected , 'event' : e } ) ;
}
}
} ,
/ * *
* deselect a node
* @ name deselect _node ( obj [ , supress _event ] )
* @ param { mixed } obj an array can be used to deselect multiple nodes
* @ param { Boolean } supress _event if set to ` true ` the ` changed.jstree ` event won ' t be triggered
* @ trigger deselect _node . jstree , changed . jstree
* /
deselect _node : function ( obj , supress _event , e ) {
var t1 , t2 , dom ;
if ( $ . isArray ( obj ) ) {
obj = obj . slice ( ) ;
for ( t1 = 0 , t2 = obj . length ; t1 < t2 ; t1 ++ ) {
this . deselect _node ( obj [ t1 ] , supress _event , e ) ;
}
return true ;
}
obj = this . get _node ( obj ) ;
if ( ! obj || obj . id === '#' ) {
return false ;
}
dom = this . get _node ( obj , true ) ;
if ( obj . state . selected ) {
obj . state . selected = false ;
this . _data . core . selected = $ . vakata . array _remove _item ( this . _data . core . selected , obj . id ) ;
if ( dom . length ) {
dom . attr ( 'aria-selected' , false ) . children ( '.jstree-anchor' ) . removeClass ( 'jstree-clicked' ) ;
}
/ * *
* triggered when an node is deselected
* @ event
* @ name deselect _node . jstree
* @ param { Object } node
* @ param { Array } selected the current selection
* @ param { Object } event the event ( if any ) that triggered this deselect _node
* /
this . trigger ( 'deselect_node' , { 'node' : obj , 'selected' : this . _data . core . selected , 'event' : e } ) ;
if ( ! supress _event ) {
this . trigger ( 'changed' , { 'action' : 'deselect_node' , 'node' : obj , 'selected' : this . _data . core . selected , 'event' : e } ) ;
}
}
} ,
/ * *
* select all nodes in the tree
* @ name select _all ( [ supress _event ] )
* @ param { Boolean } supress _event if set to ` true ` the ` changed.jstree ` event won ' t be triggered
* @ trigger select _all . jstree , changed . jstree
* /
select _all : function ( supress _event ) {
var tmp = this . _data . core . selected . concat ( [ ] ) , i , j ;
this . _data . core . selected = this . _model . data [ '#' ] . children _d . concat ( ) ;
for ( i = 0 , j = this . _data . core . selected . length ; i < j ; i ++ ) {
if ( this . _model . data [ this . _data . core . selected [ i ] ] ) {
this . _model . data [ this . _data . core . selected [ i ] ] . state . selected = true ;
}
}
this . redraw ( true ) ;
/ * *
* triggered when all nodes are selected
* @ event
* @ name select _all . jstree
* @ param { Array } selected the current selection
* /
this . trigger ( 'select_all' , { 'selected' : this . _data . core . selected } ) ;
if ( ! supress _event ) {
this . trigger ( 'changed' , { 'action' : 'select_all' , 'selected' : this . _data . core . selected , 'old_selection' : tmp } ) ;
}
} ,
/ * *
* deselect all selected nodes
* @ name deselect _all ( [ supress _event ] )
* @ param { Boolean } supress _event if set to ` true ` the ` changed.jstree ` event won ' t be triggered
* @ trigger deselect _all . jstree , changed . jstree
* /
deselect _all : function ( supress _event ) {
var tmp = this . _data . core . selected . concat ( [ ] ) , i , j ;
for ( i = 0 , j = this . _data . core . selected . length ; i < j ; i ++ ) {
if ( this . _model . data [ this . _data . core . selected [ i ] ] ) {
this . _model . data [ this . _data . core . selected [ i ] ] . state . selected = false ;
}
}
this . _data . core . selected = [ ] ;
this . element . find ( '.jstree-clicked' ) . removeClass ( 'jstree-clicked' ) . parent ( ) . attr ( 'aria-selected' , false ) ;
/ * *
* triggered when all nodes are deselected
* @ event
* @ name deselect _all . jstree
* @ param { Object } node the previous selection
* @ param { Array } selected the current selection
* /
this . trigger ( 'deselect_all' , { 'selected' : this . _data . core . selected , 'node' : tmp } ) ;
if ( ! supress _event ) {
this . trigger ( 'changed' , { 'action' : 'deselect_all' , 'selected' : this . _data . core . selected , 'old_selection' : tmp } ) ;
}
} ,
/ * *
* checks if a node is selected
* @ name is _selected ( obj )
* @ param { mixed } obj
* @ return { Boolean }
* /
is _selected : function ( obj ) {
obj = this . get _node ( obj ) ;
if ( ! obj || obj . id === '#' ) {
return false ;
}
return obj . state . selected ;
} ,
/ * *
* get an array of all selected nodes
* @ name get _selected ( [ full ] )
* @ param { mixed } full if set to ` true ` the returned array will consist of the full node objects , otherwise - only IDs will be returned
* @ return { Array }
* /
get _selected : function ( full ) {
return full ? $ . map ( this . _data . core . selected , $ . proxy ( function ( i ) { return this . get _node ( i ) ; } , this ) ) : this . _data . core . selected . slice ( ) ;
} ,
/ * *
* get an array of all top level selected nodes ( ignoring children of selected nodes )
* @ name get _top _selected ( [ full ] )
* @ param { mixed } full if set to ` true ` the returned array will consist of the full node objects , otherwise - only IDs will be returned
* @ return { Array }
* /
get _top _selected : function ( full ) {
var tmp = this . get _selected ( true ) ,
obj = { } , i , j , k , l ;
for ( i = 0 , j = tmp . length ; i < j ; i ++ ) {
obj [ tmp [ i ] . id ] = tmp [ i ] ;
}
for ( i = 0 , j = tmp . length ; i < j ; i ++ ) {
for ( k = 0 , l = tmp [ i ] . children _d . length ; k < l ; k ++ ) {
if ( obj [ tmp [ i ] . children _d [ k ] ] ) {
delete obj [ tmp [ i ] . children _d [ k ] ] ;
}
}
}
tmp = [ ] ;
for ( i in obj ) {
if ( obj . hasOwnProperty ( i ) ) {
tmp . push ( i ) ;
}
}
return full ? $ . map ( tmp , $ . proxy ( function ( i ) { return this . get _node ( i ) ; } , this ) ) : tmp ;
} ,
/ * *
* get an array of all bottom level selected nodes ( ignoring selected parents )
* @ name get _bottom _selected ( [ full ] )
* @ param { mixed } full if set to ` true ` the returned array will consist of the full node objects , otherwise - only IDs will be returned
* @ return { Array }
* /
get _bottom _selected : function ( full ) {
var tmp = this . get _selected ( true ) ,
obj = [ ] , i , j ;
for ( i = 0 , j = tmp . length ; i < j ; i ++ ) {
if ( ! tmp [ i ] . children . length ) {
obj . push ( tmp [ i ] . id ) ;
}
}
return full ? $ . map ( obj , $ . proxy ( function ( i ) { return this . get _node ( i ) ; } , this ) ) : obj ;
} ,
/ * *
* gets the current state of the tree so that it can be restored later with ` set_state(state) ` . Used internally .
* @ name get _state ( )
* @ private
* @ return { Object }
* /
get _state : function ( ) {
var state = {
'core' : {
'open' : [ ] ,
'scroll' : {
'left' : this . element . scrollLeft ( ) ,
'top' : this . element . scrollTop ( )
} ,
/ * !
'themes' : {
'name' : this . get _theme ( ) ,
'icons' : this . _data . core . themes . icons ,
'dots' : this . _data . core . themes . dots
} ,
* /
'selected' : [ ]
}
} , i ;
for ( i in this . _model . data ) {
if ( this . _model . data . hasOwnProperty ( i ) ) {
if ( i !== '#' ) {
if ( this . _model . data [ i ] . state . opened ) {
state . core . open . push ( i ) ;
}
if ( this . _model . data [ i ] . state . selected ) {
state . core . selected . push ( i ) ;
}
}
}
}
return state ;
} ,
/ * *
* sets the state of the tree . Used internally .
* @ name set _state ( state [ , callback ] )
* @ private
* @ param { Object } state the state to restore
* @ param { Function } callback an optional function to execute once the state is restored .
* @ trigger set _state . jstree
* /
set _state : function ( state , callback ) {
if ( state ) {
if ( state . core ) {
var res , n , t , _this , i ;
if ( state . core . open ) {
if ( ! $ . isArray ( state . core . open ) || ! state . core . open . length ) {
delete state . core . open ;
this . set _state ( state , callback ) ;
}
else {
this . _load _nodes ( state . core . open , function ( nodes ) {
this . open _node ( nodes , false , 0 ) ;
delete state . core . open ;
this . set _state ( state , callback ) ;
} , true ) ;
}
return false ;
}
if ( state . core . scroll ) {
if ( state . core . scroll && state . core . scroll . left !== undefined ) {
this . element . scrollLeft ( state . core . scroll . left ) ;
}
if ( state . core . scroll && state . core . scroll . top !== undefined ) {
this . element . scrollTop ( state . core . scroll . top ) ;
}
delete state . core . scroll ;
this . set _state ( state , callback ) ;
return false ;
}
if ( state . core . selected ) {
_this = this ;
this . deselect _all ( ) ;
$ . each ( state . core . selected , function ( i , v ) {
_this . select _node ( v , false , true ) ;
} ) ;
delete state . core . selected ;
this . set _state ( state , callback ) ;
return false ;
}
for ( i in state ) {
if ( state . hasOwnProperty ( i ) && i !== "core" && $ . inArray ( i , this . settings . plugins ) === - 1 ) {
delete state [ i ] ;
}
}
if ( $ . isEmptyObject ( state . core ) ) {
delete state . core ;
this . set _state ( state , callback ) ;
return false ;
}
}
if ( $ . isEmptyObject ( state ) ) {
state = null ;
if ( callback ) { callback . call ( this ) ; }
/ * *
* triggered when a ` set_state ` call completes
* @ event
* @ name set _state . jstree
* /
this . trigger ( 'set_state' ) ;
return false ;
}
return true ;
}
return false ;
} ,
/ * *
* refreshes the tree - all nodes are reloaded with calls to ` load_node ` .
* @ name refresh ( )
* @ param { Boolean } skip _loading an option to skip showing the loading indicator
* @ param { Mixed } forget _state if set to ` true ` state will not be reapplied , if set to a function ( receiving the current state as argument ) the result of that function will be used as state
* @ trigger refresh . jstree
* /
refresh : function ( skip _loading , forget _state ) {
this . _data . core . state = forget _state === true ? { } : this . get _state ( ) ;
if ( forget _state && $ . isFunction ( forget _state ) ) { this . _data . core . state = forget _state . call ( this , this . _data . core . state ) ; }
this . _cnt = 0 ;
this . _model . data = {
'#' : {
id : '#' ,
parent : null ,
parents : [ ] ,
children : [ ] ,
children _d : [ ] ,
state : { loaded : false }
}
} ;
var c = this . get _container _ul ( ) [ 0 ] . className ;
if ( ! skip _loading ) {
this . element . html ( "<" + "ul class='" + c + "' role='group'><" + "li class='jstree-initial-node jstree-loading jstree-leaf jstree-last' role='treeitem' id='j" + this . _id + "_loading'><i class='jstree-icon jstree-ocl'></i><" + "a class='jstree-anchor' href='#'><i class='jstree-icon jstree-themeicon-hidden'></i>" + this . get _string ( "Loading ..." ) + "</a></li></ul>" ) ;
this . element . attr ( 'aria-activedescendant' , 'j' + this . _id + '_loading' ) ;
}
this . load _node ( '#' , function ( o , s ) {
if ( s ) {
this . get _container _ul ( ) [ 0 ] . className = c ;
if ( this . _firstChild ( this . get _container _ul ( ) [ 0 ] ) ) {
this . element . attr ( 'aria-activedescendant' , this . _firstChild ( this . get _container _ul ( ) [ 0 ] ) . id ) ;
}
this . set _state ( $ . extend ( true , { } , this . _data . core . state ) , function ( ) {
/ * *
* triggered when a ` refresh ` call completes
* @ event
* @ name refresh . jstree
* /
this . trigger ( 'refresh' ) ;
} ) ;
}
this . _data . core . state = null ;
} ) ;
} ,
/ * *
* refreshes a node in the tree ( reload its children ) all opened nodes inside that node are reloaded with calls to ` load_node ` .
* @ name refresh _node ( obj )
* @ param { mixed } obj the node
* @ trigger refresh _node . jstree
* /
refresh _node : function ( obj ) {
obj = this . get _node ( obj ) ;
if ( ! obj || obj . id === '#' ) { return false ; }
var opened = [ ] , to _load = [ ] , s = this . _data . core . selected . concat ( [ ] ) ;
to _load . push ( obj . id ) ;
if ( obj . state . opened === true ) { opened . push ( obj . id ) ; }
this . get _node ( obj , true ) . find ( '.jstree-open' ) . each ( function ( ) { opened . push ( this . id ) ; } ) ;
this . _load _nodes ( to _load , $ . proxy ( function ( nodes ) {
this . open _node ( opened , false , 0 ) ;
this . select _node ( this . _data . core . selected ) ;
/ * *
* triggered when a node is refreshed
* @ event
* @ name refresh _node . jstree
* @ param { Object } node - the refreshed node
* @ param { Array } nodes - an array of the IDs of the nodes that were reloaded
* /
this . trigger ( 'refresh_node' , { 'node' : obj , 'nodes' : nodes } ) ;
} , this ) ) ;
} ,
/ * *
* set ( change ) the ID of a node
* @ name set _id ( obj , id )
* @ param { mixed } obj the node
* @ param { String } id the new ID
* @ return { Boolean }
* /
set _id : function ( obj , id ) {
obj = this . get _node ( obj ) ;
if ( ! obj || obj . id === '#' ) { return false ; }
var i , j , m = this . _model . data ;
id = id . toString ( ) ;
// update parents (replace current ID with new one in children and children_d)
m [ obj . parent ] . children [ $ . inArray ( obj . id , m [ obj . parent ] . children ) ] = id ;
for ( i = 0 , j = obj . parents . length ; i < j ; i ++ ) {
m [ obj . parents [ i ] ] . children _d [ $ . inArray ( obj . id , m [ obj . parents [ i ] ] . children _d ) ] = id ;
}
// update children (replace current ID with new one in parent and parents)
for ( i = 0 , j = obj . children . length ; i < j ; i ++ ) {
m [ obj . children [ i ] ] . parent = id ;
}
for ( i = 0 , j = obj . children _d . length ; i < j ; i ++ ) {
m [ obj . children _d [ i ] ] . parents [ $ . inArray ( obj . id , m [ obj . children _d [ i ] ] . parents ) ] = id ;
}
i = $ . inArray ( obj . id , this . _data . core . selected ) ;
if ( i !== - 1 ) { this . _data . core . selected [ i ] = id ; }
// update model and obj itself (obj.id, this._model.data[KEY])
i = this . get _node ( obj . id , true ) ;
if ( i ) {
i . attr ( 'id' , id ) . children ( '.jstree-anchor' ) . attr ( 'id' , id + '_anchor' ) . end ( ) . attr ( 'aria-labelledby' , id + '_anchor' ) ;
if ( this . element . attr ( 'aria-activedescendant' ) === obj . id ) {
this . element . attr ( 'aria-activedescendant' , id ) ;
}
}
delete m [ obj . id ] ;
obj . id = id ;
obj . li _attr . id = id ;
m [ id ] = obj ;
return true ;
} ,
/ * *
* get the text value of a node
* @ name get _text ( obj )
* @ param { mixed } obj the node
* @ return { String }
* /
get _text : function ( obj ) {
obj = this . get _node ( obj ) ;
return ( ! obj || obj . id === '#' ) ? false : obj . text ;
} ,
/ * *
* set the text value of a node . Used internally , please use ` rename_node(obj, val) ` .
* @ private
* @ name set _text ( obj , val )
* @ param { mixed } obj the node , you can pass an array to set the text on multiple nodes
* @ param { String } val the new text value
* @ return { Boolean }
* @ trigger set _text . jstree
* /
set _text : function ( obj , val ) {
var t1 , t2 ;
if ( $ . isArray ( obj ) ) {
obj = obj . slice ( ) ;
for ( t1 = 0 , t2 = obj . length ; t1 < t2 ; t1 ++ ) {
this . set _text ( obj [ t1 ] , val ) ;
}
return true ;
}
obj = this . get _node ( obj ) ;
if ( ! obj || obj . id === '#' ) { return false ; }
obj . text = val ;
if ( this . get _node ( obj , true ) . length ) {
this . redraw _node ( obj . id ) ;
}
/ * *
* triggered when a node text value is changed
* @ event
* @ name set _text . jstree
* @ param { Object } obj
* @ param { String } text the new value
* /
this . trigger ( 'set_text' , { "obj" : obj , "text" : val } ) ;
return true ;
} ,
/ * *
* gets a JSON representation of a node ( or the whole tree )
* @ name get _json ( [ obj , options ] )
* @ param { mixed } obj
* @ param { Object } options
* @ param { Boolean } options . no _state do not return state information
* @ param { Boolean } options . no _id do not return ID
* @ param { Boolean } options . no _children do not include children
* @ param { Boolean } options . no _data do not include node data
* @ param { Boolean } options . flat return flat JSON instead of nested
* @ return { Object }
* /
get _json : function ( obj , options , flat ) {
obj = this . get _node ( obj || '#' ) ;
if ( ! obj ) { return false ; }
if ( options && options . flat && ! flat ) { flat = [ ] ; }
var tmp = {
'id' : obj . id ,
'text' : obj . text ,
'icon' : this . get _icon ( obj ) ,
'li_attr' : $ . extend ( true , { } , obj . li _attr ) ,
'a_attr' : $ . extend ( true , { } , obj . a _attr ) ,
'state' : { } ,
'data' : options && options . no _data ? false : $ . extend ( true , { } , obj . data )
//( this.get_node(obj, true).length ? this.get_node(obj, true).data() : obj.data ),
} , i , j ;
if ( options && options . flat ) {
tmp . parent = obj . parent ;
}
else {
tmp . children = [ ] ;
}
if ( ! options || ! options . no _state ) {
for ( i in obj . state ) {
if ( obj . state . hasOwnProperty ( i ) ) {
tmp . state [ i ] = obj . state [ i ] ;
}
}
}
if ( options && options . no _id ) {
delete tmp . id ;
if ( tmp . li _attr && tmp . li _attr . id ) {
delete tmp . li _attr . id ;
}
if ( tmp . a _attr && tmp . a _attr . id ) {
delete tmp . a _attr . id ;
}
}
if ( options && options . flat && obj . id !== '#' ) {
flat . push ( tmp ) ;
}
if ( ! options || ! options . no _children ) {
for ( i = 0 , j = obj . children . length ; i < j ; i ++ ) {
if ( options && options . flat ) {
this . get _json ( obj . children [ i ] , options , flat ) ;
}
else {
tmp . children . push ( this . get _json ( obj . children [ i ] , options ) ) ;
}
}
}
return options && options . flat ? flat : ( obj . id === '#' ? tmp . children : tmp ) ;
} ,
/ * *
* create a new node ( do not confuse with load _node )
* @ name create _node ( [ obj , node , pos , callback , is _loaded ] )
* @ param { mixed } par the parent node ( to create a root node use either "#" ( string ) or ` null ` )
* @ param { mixed } node the data for the new node ( a valid JSON object , or a simple string with the name )
* @ param { mixed } pos the index at which to insert the node , "first" and "last" are also supported , default is "last"
* @ param { Function } callback a function to be called once the node is created
* @ param { Boolean } is _loaded internal argument indicating if the parent node was succesfully loaded
* @ return { String } the ID of the newly create node
* @ trigger model . jstree , create _node . jstree
* /
create _node : function ( par , node , pos , callback , is _loaded ) {
if ( par === null ) { par = "#" ; }
par = this . get _node ( par ) ;
if ( ! par ) { return false ; }
pos = pos === undefined ? "last" : pos ;
if ( ! pos . toString ( ) . match ( /^(before|after)$/ ) && ! is _loaded && ! this . is _loaded ( par ) ) {
return this . load _node ( par , function ( ) { this . create _node ( par , node , pos , callback , true ) ; } ) ;
}
if ( ! node ) { node = { "text" : this . get _string ( 'New node' ) } ; }
if ( typeof node === "string" ) { node = { "text" : node } ; }
if ( node . text === undefined ) { node . text = this . get _string ( 'New node' ) ; }
var tmp , dpc , i , j ;
if ( par . id === '#' ) {
if ( pos === "before" ) { pos = "first" ; }
if ( pos === "after" ) { pos = "last" ; }
}
switch ( pos ) {
case "before" :
tmp = this . get _node ( par . parent ) ;
pos = $ . inArray ( par . id , tmp . children ) ;
par = tmp ;
break ;
case "after" :
tmp = this . get _node ( par . parent ) ;
pos = $ . inArray ( par . id , tmp . children ) + 1 ;
par = tmp ;
break ;
case "inside" :
case "first" :
pos = 0 ;
break ;
case "last" :
pos = par . children . length ;
break ;
default :
if ( ! pos ) { pos = 0 ; }
break ;
}
if ( pos > par . children . length ) { pos = par . children . length ; }
if ( ! node . id ) { node . id = true ; }
if ( ! this . check ( "create_node" , node , par , pos ) ) {
this . settings . core . error . call ( this , this . _data . core . last _error ) ;
return false ;
}
if ( node . id === true ) { delete node . id ; }
node = this . _parse _model _from _json ( node , par . id , par . parents . concat ( ) ) ;
if ( ! node ) { return false ; }
tmp = this . get _node ( node ) ;
dpc = [ ] ;
dpc . push ( node ) ;
dpc = dpc . concat ( tmp . children _d ) ;
this . trigger ( 'model' , { "nodes" : dpc , "parent" : par . id } ) ;
par . children _d = par . children _d . concat ( dpc ) ;
for ( i = 0 , j = par . parents . length ; i < j ; i ++ ) {
this . _model . data [ par . parents [ i ] ] . children _d = this . _model . data [ par . parents [ i ] ] . children _d . concat ( dpc ) ;
}
node = tmp ;
tmp = [ ] ;
for ( i = 0 , j = par . children . length ; i < j ; i ++ ) {
tmp [ i >= pos ? i + 1 : i ] = par . children [ i ] ;
}
tmp [ pos ] = node . id ;
par . children = tmp ;
this . redraw _node ( par , true ) ;
if ( callback ) { callback . call ( this , this . get _node ( node ) ) ; }
/ * *
* triggered when a node is created
* @ event
* @ name create _node . jstree
* @ param { Object } node
* @ param { String } parent the parent ' s ID
* @ param { Number } position the position of the new node among the parent ' s children
* /
this . trigger ( 'create_node' , { "node" : this . get _node ( node ) , "parent" : par . id , "position" : pos } ) ;
return node . id ;
} ,
/ * *
* set the text value of a node
* @ name rename _node ( obj , val )
* @ param { mixed } obj the node , you can pass an array to rename multiple nodes to the same name
* @ param { String } val the new text value
* @ return { Boolean }
* @ trigger rename _node . jstree
* /
rename _node : function ( obj , val ) {
var t1 , t2 , old ;
if ( $ . isArray ( obj ) ) {
obj = obj . slice ( ) ;
for ( t1 = 0 , t2 = obj . length ; t1 < t2 ; t1 ++ ) {
this . rename _node ( obj [ t1 ] , val ) ;
}
return true ;
}
obj = this . get _node ( obj ) ;
if ( ! obj || obj . id === '#' ) { return false ; }
old = obj . text ;
if ( ! this . check ( "rename_node" , obj , this . get _parent ( obj ) , val ) ) {
this . settings . core . error . call ( this , this . _data . core . last _error ) ;
return false ;
}
this . set _text ( obj , val ) ; // .apply(this, Array.prototype.slice.call(arguments))
/ * *
* triggered when a node is renamed
* @ event
* @ name rename _node . jstree
* @ param { Object } node
* @ param { String } text the new value
* @ param { String } old the old value
* /
this . trigger ( 'rename_node' , { "node" : obj , "text" : val , "old" : old } ) ;
return true ;
} ,
/ * *
* remove a node
* @ name delete _node ( obj )
* @ param { mixed } obj the node , you can pass an array to delete multiple nodes
* @ return { Boolean }
* @ trigger delete _node . jstree , changed . jstree
* /
delete _node : function ( obj ) {
var t1 , t2 , par , pos , tmp , i , j , k , l , c ;
if ( $ . isArray ( obj ) ) {
obj = obj . slice ( ) ;
for ( t1 = 0 , t2 = obj . length ; t1 < t2 ; t1 ++ ) {
this . delete _node ( obj [ t1 ] ) ;
}
return true ;
}
obj = this . get _node ( obj ) ;
if ( ! obj || obj . id === '#' ) { return false ; }
par = this . get _node ( obj . parent ) ;
pos = $ . inArray ( obj . id , par . children ) ;
c = false ;
if ( ! this . check ( "delete_node" , obj , par , pos ) ) {
this . settings . core . error . call ( this , this . _data . core . last _error ) ;
return false ;
}
if ( pos !== - 1 ) {
par . children = $ . vakata . array _remove ( par . children , pos ) ;
}
tmp = obj . children _d . concat ( [ ] ) ;
tmp . push ( obj . id ) ;
for ( k = 0 , l = tmp . length ; k < l ; k ++ ) {
for ( i = 0 , j = obj . parents . length ; i < j ; i ++ ) {
pos = $ . inArray ( tmp [ k ] , this . _model . data [ obj . parents [ i ] ] . children _d ) ;
if ( pos !== - 1 ) {
this . _model . data [ obj . parents [ i ] ] . children _d = $ . vakata . array _remove ( this . _model . data [ obj . parents [ i ] ] . children _d , pos ) ;
}
}
if ( this . _model . data [ tmp [ k ] ] . state . selected ) {
c = true ;
pos = $ . inArray ( tmp [ k ] , this . _data . core . selected ) ;
if ( pos !== - 1 ) {
this . _data . core . selected = $ . vakata . array _remove ( this . _data . core . selected , pos ) ;
}
}
}
/ * *
* triggered when a node is deleted
* @ event
* @ name delete _node . jstree
* @ param { Object } node
* @ param { String } parent the parent ' s ID
* /
this . trigger ( 'delete_node' , { "node" : obj , "parent" : par . id } ) ;
if ( c ) {
this . trigger ( 'changed' , { 'action' : 'delete_node' , 'node' : obj , 'selected' : this . _data . core . selected , 'parent' : par . id } ) ;
}
for ( k = 0 , l = tmp . length ; k < l ; k ++ ) {
delete this . _model . data [ tmp [ k ] ] ;
}
this . redraw _node ( par , true ) ;
return true ;
} ,
/ * *
* check if an operation is premitted on the tree . Used internally .
* @ private
* @ name check ( chk , obj , par , pos )
* @ param { String } chk the operation to check , can be "create_node" , "rename_node" , "delete_node" , "copy_node" or "move_node"
* @ param { mixed } obj the node
* @ param { mixed } par the parent
* @ param { mixed } pos the position to insert at , or if "rename_node" - the new name
* @ param { mixed } more some various additional information , for example if a "move_node" operations is triggered by DND this will be the hovered node
* @ return { Boolean }
* /
check : function ( chk , obj , par , pos , more ) {
obj = obj && obj . id ? obj : this . get _node ( obj ) ;
par = par && par . id ? par : this . get _node ( par ) ;
var tmp = chk . match ( /^move_node|copy_node|create_node$/i ) ? par : obj ,
chc = this . settings . core . check _callback ;
if ( chk === "move_node" || chk === "copy_node" ) {
if ( ( ! more || ! more . is _multi ) && ( obj . id === par . id || $ . inArray ( obj . id , par . children ) === pos || $ . inArray ( par . id , obj . children _d ) !== - 1 ) ) {
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 } ) } ;
return false ;
}
}
if ( tmp && tmp . data ) { tmp = tmp . data ; }
if ( tmp && tmp . functions && ( tmp . functions [ chk ] === false || tmp . functions [ chk ] === true ) ) {
if ( tmp . functions [ chk ] === false ) {
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 } ) } ;
}
return tmp . functions [ chk ] ;
}
if ( chc === false || ( $ . isFunction ( chc ) && chc . call ( this , chk , obj , par , pos , more ) === false ) || ( chc && chc [ chk ] === false ) ) {
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 } ) } ;
return false ;
}
return true ;
} ,
/ * *
* get the last error
* @ name last _error ( )
* @ return { Object }
* /
last _error : function ( ) {
return this . _data . core . last _error ;
} ,
/ * *
* move a node to a new parent
* @ name move _node ( obj , par [ , pos , callback , is _loaded ] )
* @ param { mixed } obj the node to move , pass an array to move multiple nodes
* @ param { mixed } par the new parent
* @ param { mixed } pos the position to insert at ( besides integer values , "first" and "last" are supported , as well as "before" and "after" ) , defaults to integer ` 0 `
* @ param { function } callback a function to call once the move is completed , receives 3 arguments - the node , the new parent and the position
* @ param { Boolean } is _loaded internal parameter indicating if the parent node has been loaded
* @ param { Boolean } skip _redraw internal parameter indicating if the tree should be redrawn
* @ param { Boolean } instance internal parameter indicating if the node comes from another instance
* @ trigger move _node . jstree
* /
move _node : function ( obj , par , pos , callback , is _loaded , skip _redraw , origin ) {
var t1 , t2 , old _par , old _pos , new _par , old _ins , is _multi , dpc , tmp , i , j , k , l , p ;
par = this . get _node ( par ) ;
pos = pos === undefined ? 0 : pos ;
if ( ! par ) { return false ; }
if ( ! pos . toString ( ) . match ( /^(before|after)$/ ) && ! is _loaded && ! this . is _loaded ( par ) ) {
return this . load _node ( par , function ( ) { this . move _node ( obj , par , pos , callback , true , false , origin ) ; } ) ;
}
if ( $ . isArray ( obj ) ) {
if ( obj . length === 1 ) {
obj = obj [ 0 ] ;
}
else {
//obj = obj.slice();
for ( t1 = 0 , t2 = obj . length ; t1 < t2 ; t1 ++ ) {
if ( ( tmp = this . move _node ( obj [ t1 ] , par , pos , callback , is _loaded , false , origin ) ) ) {
par = tmp ;
pos = "after" ;
}
}
this . redraw ( ) ;
return true ;
}
}
obj = obj && obj . id ? obj : this . get _node ( obj ) ;
if ( ! obj || obj . id === '#' ) { return false ; }
old _par = ( obj . parent || '#' ) . toString ( ) ;
new _par = ( ! pos . toString ( ) . match ( /^(before|after)$/ ) || par . id === '#' ) ? par : this . get _node ( par . parent ) ;
old _ins = origin ? origin : ( this . _model . data [ obj . id ] ? this : $ . jstree . reference ( obj . id ) ) ;
is _multi = ! old _ins || ! old _ins . _id || ( this . _id !== old _ins . _id ) ;
old _pos = old _ins && old _ins . _id && old _par && old _ins . _model . data [ old _par ] && old _ins . _model . data [ old _par ] . children ? $ . inArray ( obj . id , old _ins . _model . data [ old _par ] . children ) : - 1 ;
if ( old _ins && old _ins . _id ) {
obj = old _ins . _model . data [ obj . id ] ;
}
if ( is _multi ) {
if ( ( tmp = this . copy _node ( obj , par , pos , callback , is _loaded , false , origin ) ) ) {
if ( old _ins ) { old _ins . delete _node ( obj ) ; }
return tmp ;
}
return false ;
}
//var m = this._model.data;
if ( par . id === '#' ) {
if ( pos === "before" ) { pos = "first" ; }
if ( pos === "after" ) { pos = "last" ; }
}
switch ( pos ) {
case "before" :
pos = $ . inArray ( par . id , new _par . children ) ;
break ;
case "after" :
pos = $ . inArray ( par . id , new _par . children ) + 1 ;
break ;
case "inside" :
case "first" :
pos = 0 ;
break ;
case "last" :
pos = new _par . children . length ;
break ;
default :
if ( ! pos ) { pos = 0 ; }
break ;
}
if ( pos > new _par . children . length ) { pos = new _par . children . length ; }
if ( ! this . check ( "move_node" , obj , new _par , pos , { 'core' : true , 'origin' : origin , 'is_multi' : ( old _ins && old _ins . _id && old _ins . _id !== this . _id ) , 'is_foreign' : ( ! old _ins || ! old _ins . _id ) } ) ) {
this . settings . core . error . call ( this , this . _data . core . last _error ) ;
return false ;
}
if ( obj . parent === new _par . id ) {
dpc = new _par . children . concat ( ) ;
tmp = $ . inArray ( obj . id , dpc ) ;
if ( tmp !== - 1 ) {
dpc = $ . vakata . array _remove ( dpc , tmp ) ;
if ( pos > tmp ) { pos -- ; }
}
tmp = [ ] ;
for ( i = 0 , j = dpc . length ; i < j ; i ++ ) {
tmp [ i >= pos ? i + 1 : i ] = dpc [ i ] ;
}
tmp [ pos ] = obj . id ;
new _par . children = tmp ;
this . _node _changed ( new _par . id ) ;
this . redraw ( new _par . id === '#' ) ;
}
else {
// clean old parent and up
tmp = obj . children _d . concat ( ) ;
tmp . push ( obj . id ) ;
for ( i = 0 , j = obj . parents . length ; i < j ; i ++ ) {
dpc = [ ] ;
p = old _ins . _model . data [ obj . parents [ i ] ] . children _d ;
for ( k = 0 , l = p . length ; k < l ; k ++ ) {
if ( $ . inArray ( p [ k ] , tmp ) === - 1 ) {
dpc . push ( p [ k ] ) ;
}
}
old _ins . _model . data [ obj . parents [ i ] ] . children _d = dpc ;
}
old _ins . _model . data [ old _par ] . children = $ . vakata . array _remove _item ( old _ins . _model . data [ old _par ] . children , obj . id ) ;
// insert into new parent and up
for ( i = 0 , j = new _par . parents . length ; i < j ; i ++ ) {
this . _model . data [ new _par . parents [ i ] ] . children _d = this . _model . data [ new _par . parents [ i ] ] . children _d . concat ( tmp ) ;
}
dpc = [ ] ;
for ( i = 0 , j = new _par . children . length ; i < j ; i ++ ) {
dpc [ i >= pos ? i + 1 : i ] = new _par . children [ i ] ;
}
dpc [ pos ] = obj . id ;
new _par . children = dpc ;
new _par . children _d . push ( obj . id ) ;
new _par . children _d = new _par . children _d . concat ( obj . children _d ) ;
// update object
obj . parent = new _par . id ;
tmp = new _par . parents . concat ( ) ;
tmp . unshift ( new _par . id ) ;
p = obj . parents . length ;
obj . parents = tmp ;
// update object children
tmp = tmp . concat ( ) ;
for ( i = 0 , j = obj . children _d . length ; i < j ; i ++ ) {
this . _model . data [ obj . children _d [ i ] ] . parents = this . _model . data [ obj . children _d [ i ] ] . parents . slice ( 0 , p * - 1 ) ;
Array . prototype . push . apply ( this . _model . data [ obj . children _d [ i ] ] . parents , tmp ) ;
}
if ( old _par === '#' || new _par . id === '#' ) {
this . _model . force _full _redraw = true ;
}
if ( ! this . _model . force _full _redraw ) {
this . _node _changed ( old _par ) ;
this . _node _changed ( new _par . id ) ;
}
if ( ! skip _redraw ) {
this . redraw ( ) ;
}
}
if ( callback ) { callback . call ( this , obj , new _par , pos ) ; }
/ * *
* triggered when a node is moved
* @ event
* @ name move _node . jstree
* @ param { Object } node
* @ param { String } parent the parent ' s ID
* @ param { Number } position the position of the node among the parent ' s children
* @ param { String } old _parent the old parent of the node
* @ param { Number } old _position the old position of the node
* @ param { Boolean } is _multi do the node and new parent belong to different instances
* @ param { jsTree } old _instance the instance the node came from
* @ param { jsTree } new _instance the instance of the new parent
* /
this . trigger ( 'move_node' , { "node" : obj , "parent" : new _par . id , "position" : pos , "old_parent" : old _par , "old_position" : old _pos , 'is_multi' : ( old _ins && old _ins . _id && old _ins . _id !== this . _id ) , 'is_foreign' : ( ! old _ins || ! old _ins . _id ) , 'old_instance' : old _ins , 'new_instance' : this } ) ;
return obj . id ;
} ,
/ * *
* copy a node to a new parent
* @ name copy _node ( obj , par [ , pos , callback , is _loaded ] )
* @ param { mixed } obj the node to copy , pass an array to copy multiple nodes
* @ param { mixed } par the new parent
* @ param { mixed } pos the position to insert at ( besides integer values , "first" and "last" are supported , as well as "before" and "after" ) , defaults to integer ` 0 `
* @ param { function } callback a function to call once the move is completed , receives 3 arguments - the node , the new parent and the position
* @ param { Boolean } is _loaded internal parameter indicating if the parent node has been loaded
* @ param { Boolean } skip _redraw internal parameter indicating if the tree should be redrawn
* @ param { Boolean } instance internal parameter indicating if the node comes from another instance
* @ trigger model . jstree copy _node . jstree
* /
copy _node : function ( obj , par , pos , callback , is _loaded , skip _redraw , origin ) {
var t1 , t2 , dpc , tmp , i , j , node , old _par , new _par , old _ins , is _multi ;
par = this . get _node ( par ) ;
pos = pos === undefined ? 0 : pos ;
if ( ! par ) { return false ; }
if ( ! pos . toString ( ) . match ( /^(before|after)$/ ) && ! is _loaded && ! this . is _loaded ( par ) ) {
return this . load _node ( par , function ( ) { this . copy _node ( obj , par , pos , callback , true , false , origin ) ; } ) ;
}
if ( $ . isArray ( obj ) ) {
if ( obj . length === 1 ) {
obj = obj [ 0 ] ;
}
else {
//obj = obj.slice();
for ( t1 = 0 , t2 = obj . length ; t1 < t2 ; t1 ++ ) {
if ( ( tmp = this . copy _node ( obj [ t1 ] , par , pos , callback , is _loaded , true , origin ) ) ) {
par = tmp ;
pos = "after" ;
}
}
this . redraw ( ) ;
return true ;
}
}
obj = obj && obj . id ? obj : this . get _node ( obj ) ;
if ( ! obj || obj . id === '#' ) { return false ; }
old _par = ( obj . parent || '#' ) . toString ( ) ;
new _par = ( ! pos . toString ( ) . match ( /^(before|after)$/ ) || par . id === '#' ) ? par : this . get _node ( par . parent ) ;
old _ins = origin ? origin : ( this . _model . data [ obj . id ] ? this : $ . jstree . reference ( obj . id ) ) ;
is _multi = ! old _ins || ! old _ins . _id || ( this . _id !== old _ins . _id ) ;
if ( old _ins && old _ins . _id ) {
obj = old _ins . _model . data [ obj . id ] ;
}
if ( par . id === '#' ) {
if ( pos === "before" ) { pos = "first" ; }
if ( pos === "after" ) { pos = "last" ; }
}
switch ( pos ) {
case "before" :
pos = $ . inArray ( par . id , new _par . children ) ;
break ;
case "after" :
pos = $ . inArray ( par . id , new _par . children ) + 1 ;
break ;
case "inside" :
case "first" :
pos = 0 ;
break ;
case "last" :
pos = new _par . children . length ;
break ;
default :
if ( ! pos ) { pos = 0 ; }
break ;
}
if ( pos > new _par . children . length ) { pos = new _par . children . length ; }
if ( ! this . check ( "copy_node" , obj , new _par , pos , { 'core' : true , 'origin' : origin , 'is_multi' : ( old _ins && old _ins . _id && old _ins . _id !== this . _id ) , 'is_foreign' : ( ! old _ins || ! old _ins . _id ) } ) ) {
this . settings . core . error . call ( this , this . _data . core . last _error ) ;
return false ;
}
node = old _ins ? old _ins . get _json ( obj , { no _id : true , no _data : true , no _state : true } ) : obj ;
if ( ! node ) { return false ; }
if ( node . id === true ) { delete node . id ; }
node = this . _parse _model _from _json ( node , new _par . id , new _par . parents . concat ( ) ) ;
if ( ! node ) { return false ; }
tmp = this . get _node ( node ) ;
if ( obj && obj . state && obj . state . loaded === false ) { tmp . state . loaded = false ; }
dpc = [ ] ;
dpc . push ( node ) ;
dpc = dpc . concat ( tmp . children _d ) ;
this . trigger ( 'model' , { "nodes" : dpc , "parent" : new _par . id } ) ;
// insert into new parent and up
for ( i = 0 , j = new _par . parents . length ; i < j ; i ++ ) {
this . _model . data [ new _par . parents [ i ] ] . children _d = this . _model . data [ new _par . parents [ i ] ] . children _d . concat ( dpc ) ;
}
dpc = [ ] ;
for ( i = 0 , j = new _par . children . length ; i < j ; i ++ ) {
dpc [ i >= pos ? i + 1 : i ] = new _par . children [ i ] ;
}
dpc [ pos ] = tmp . id ;
new _par . children = dpc ;
new _par . children _d . push ( tmp . id ) ;
new _par . children _d = new _par . children _d . concat ( tmp . children _d ) ;
if ( new _par . id === '#' ) {
this . _model . force _full _redraw = true ;
}
if ( ! this . _model . force _full _redraw ) {
this . _node _changed ( new _par . id ) ;
}
if ( ! skip _redraw ) {
this . redraw ( new _par . id === '#' ) ;
}
if ( callback ) { callback . call ( this , tmp , new _par , pos ) ; }
/ * *
* triggered when a node is copied
* @ event
* @ name copy _node . jstree
* @ param { Object } node the copied node
* @ param { Object } original the original node
* @ param { String } parent the parent ' s ID
* @ param { Number } position the position of the node among the parent ' s children
* @ param { String } old _parent the old parent of the node
* @ param { Number } old _position the position of the original node
* @ param { Boolean } is _multi do the node and new parent belong to different instances
* @ param { jsTree } old _instance the instance the node came from
* @ param { jsTree } new _instance the instance of the new parent
* /
this . trigger ( 'copy_node' , { "node" : tmp , "original" : obj , "parent" : new _par . id , "position" : pos , "old_parent" : old _par , "old_position" : old _ins && old _ins . _id && old _par && old _ins . _model . data [ old _par ] && old _ins . _model . data [ old _par ] . children ? $ . inArray ( obj . id , old _ins . _model . data [ old _par ] . children ) : - 1 , 'is_multi' : ( old _ins && old _ins . _id && old _ins . _id !== this . _id ) , 'is_foreign' : ( ! old _ins || ! old _ins . _id ) , 'old_instance' : old _ins , 'new_instance' : this } ) ;
return tmp . id ;
} ,
/ * *
* cut a node ( a later call to ` paste(obj) ` would move the node )
* @ name cut ( obj )
* @ param { mixed } obj multiple objects can be passed using an array
* @ trigger cut . jstree
* /
cut : function ( obj ) {
if ( ! obj ) { obj = this . _data . core . selected . concat ( ) ; }
if ( ! $ . isArray ( obj ) ) { obj = [ obj ] ; }
if ( ! obj . length ) { return false ; }
var tmp = [ ] , o , t1 , t2 ;
for ( t1 = 0 , t2 = obj . length ; t1 < t2 ; t1 ++ ) {
o = this . get _node ( obj [ t1 ] ) ;
if ( o && o . id && o . id !== '#' ) { tmp . push ( o ) ; }
}
if ( ! tmp . length ) { return false ; }
ccp _node = tmp ;
ccp _inst = this ;
ccp _mode = 'move_node' ;
/ * *
* triggered when nodes are added to the buffer for moving
* @ event
* @ name cut . jstree
* @ param { Array } node
* /
this . trigger ( 'cut' , { "node" : obj } ) ;
} ,
/ * *
* copy a node ( a later call to ` paste(obj) ` would copy the node )
* @ name copy ( obj )
* @ param { mixed } obj multiple objects can be passed using an array
* @ trigger copy . jstree
* /
copy : function ( obj ) {
if ( ! obj ) { obj = this . _data . core . selected . concat ( ) ; }
if ( ! $ . isArray ( obj ) ) { obj = [ obj ] ; }
if ( ! obj . length ) { return false ; }
var tmp = [ ] , o , t1 , t2 ;
for ( t1 = 0 , t2 = obj . length ; t1 < t2 ; t1 ++ ) {
o = this . get _node ( obj [ t1 ] ) ;
if ( o && o . id && o . id !== '#' ) { tmp . push ( o ) ; }
}
if ( ! tmp . length ) { return false ; }
ccp _node = tmp ;
ccp _inst = this ;
ccp _mode = 'copy_node' ;
/ * *
* triggered when nodes are added to the buffer for copying
* @ event
* @ name copy . jstree
* @ param { Array } node
* /
this . trigger ( 'copy' , { "node" : obj } ) ;
} ,
/ * *
* get the current buffer ( any nodes that are waiting for a paste operation )
* @ name get _buffer ( )
* @ return { Object } an object consisting of ` mode ` ( "copy_node" or "move_node" ) , ` node ` ( an array of objects ) and ` inst ` ( the instance )
* /
get _buffer : function ( ) {
return { 'mode' : ccp _mode , 'node' : ccp _node , 'inst' : ccp _inst } ;
} ,
/ * *
* check if there is something in the buffer to paste
* @ name can _paste ( )
* @ return { Boolean }
* /
can _paste : function ( ) {
return ccp _mode !== false && ccp _node !== false ; // && ccp_inst._model.data[ccp_node];
} ,
/ * *
* copy or move the previously cut or copied nodes to a new parent
* @ name paste ( obj [ , pos ] )
* @ param { mixed } obj the new parent
* @ param { mixed } pos the position to insert at ( besides integer , "first" and "last" are supported ) , defaults to integer ` 0 `
* @ trigger paste . jstree
* /
paste : function ( obj , pos ) {
obj = this . get _node ( obj ) ;
if ( ! obj || ! ccp _mode || ! ccp _mode . match ( /^(copy_node|move_node)$/ ) || ! ccp _node ) { return false ; }
if ( this [ ccp _mode ] ( ccp _node , obj , pos , false , false , false , ccp _inst ) ) {
/ * *
* triggered when paste is invoked
* @ event
* @ name paste . jstree
* @ param { String } parent the ID of the receiving node
* @ param { Array } node the nodes in the buffer
* @ param { String } mode the performed operation - "copy_node" or "move_node"
* /
this . trigger ( 'paste' , { "parent" : obj . id , "node" : ccp _node , "mode" : ccp _mode } ) ;
}
ccp _node = false ;
ccp _mode = false ;
ccp _inst = false ;
} ,
/ * *
* clear the buffer of previously copied or cut nodes
* @ name clear _buffer ( )
* @ trigger clear _buffer . jstree
* /
clear _buffer : function ( ) {
ccp _node = false ;
ccp _mode = false ;
ccp _inst = false ;
/ * *
* triggered when the copy / cut buffer is cleared
* @ event
* @ name clear _buffer . jstree
* /
this . trigger ( 'clear_buffer' ) ;
} ,
/ * *
* put a node in edit mode ( input field to rename the node )
* @ name edit ( obj [ , default _text , callback ] )
* @ param { mixed } obj
* @ param { String } default _text the text to populate the input with ( if omitted or set to a non - string value the node ' s text value is used )
* @ param { Function } callback a function to be called once the text box is blurred , it is called in the instance 's scope and receives the node and a status parameter - true if the rename is successful, false otherwise. You can access the node' s title using . text
* /
edit : function ( obj , default _text , callback ) {
var rtl , w , a , s , t , h1 , h2 , fn , tmp ;
obj = this . get _node ( obj ) ;
if ( ! obj ) { return false ; }
if ( this . settings . core . check _callback === false ) {
this . _data . core . last _error = { 'error' : 'check' , 'plugin' : 'core' , 'id' : 'core_07' , 'reason' : 'Could not edit node because of check_callback' } ;
this . settings . core . error . call ( this , this . _data . core . last _error ) ;
return false ;
}
tmp = obj ;
default _text = typeof default _text === 'string' ? default _text : obj . text ;
this . set _text ( obj , "" ) ;
obj = this . _open _to ( obj ) ;
tmp . text = default _text ;
rtl = this . _data . core . rtl ;
w = this . element . width ( ) ;
a = obj . children ( '.jstree-anchor' ) ;
s = $ ( '<span>' ) ;
/ * !
oi = obj . children ( "i:visible" ) ,
ai = a . children ( "i:visible" ) ,
w1 = oi . width ( ) * oi . length ,
w2 = ai . width ( ) * ai . length ,
* /
t = default _text ;
h1 = $ ( "<" + "div />" , { css : { "position" : "absolute" , "top" : "-200px" , "left" : ( rtl ? "0px" : "-1000px" ) , "visibility" : "hidden" } } ) . appendTo ( "body" ) ;
h2 = $ ( "<" + "input />" , {
"value" : t ,
"class" : "jstree-rename-input" ,
// "size" : t.length,
"css" : {
"padding" : "0" ,
"border" : "1px solid silver" ,
"box-sizing" : "border-box" ,
"display" : "inline-block" ,
"height" : ( this . _data . core . li _height ) + "px" ,
"lineHeight" : ( this . _data . core . li _height ) + "px" ,
"width" : "150px" // will be set a bit further down
} ,
"blur" : $ . proxy ( function ( ) {
var i = s . children ( ".jstree-rename-input" ) ,
v = i . val ( ) ,
f = this . settings . core . force _text ,
nv ;
if ( v === "" ) { v = t ; }
h1 . remove ( ) ;
s . replaceWith ( a ) ;
s . remove ( ) ;
t = f ? t : $ ( '<div></div>' ) . append ( $ . parseHTML ( t ) ) . html ( ) ;
this . set _text ( obj , t ) ;
nv = ! ! this . rename _node ( obj , f ? $ ( '<div></div>' ) . text ( v ) . text ( ) : $ ( '<div></div>' ) . append ( $ . parseHTML ( v ) ) . html ( ) ) ;
if ( ! nv ) {
this . set _text ( obj , t ) ; // move this up? and fix #483
}
if ( callback ) {
callback . call ( this , tmp , nv ) ;
}
} , this ) ,
"keydown" : function ( event ) {
var key = event . which ;
if ( key === 27 ) {
this . value = t ;
}
if ( key === 27 || key === 13 || key === 37 || key === 38 || key === 39 || key === 40 || key === 32 ) {
event . stopImmediatePropagation ( ) ;
}
if ( key === 27 || key === 13 ) {
event . preventDefault ( ) ;
this . blur ( ) ;
}
} ,
"click" : function ( e ) { e . stopImmediatePropagation ( ) ; } ,
"mousedown" : function ( e ) { e . stopImmediatePropagation ( ) ; } ,
"keyup" : function ( event ) {
h2 . width ( Math . min ( h1 . text ( "pW" + this . value ) . width ( ) , w ) ) ;
} ,
"keypress" : function ( event ) {
if ( event . which === 13 ) { return false ; }
}
} ) ;
fn = {
fontFamily : a . css ( 'fontFamily' ) || '' ,
fontSize : a . css ( 'fontSize' ) || '' ,
fontWeight : a . css ( 'fontWeight' ) || '' ,
fontStyle : a . css ( 'fontStyle' ) || '' ,
fontStretch : a . css ( 'fontStretch' ) || '' ,
fontVariant : a . css ( 'fontVariant' ) || '' ,
letterSpacing : a . css ( 'letterSpacing' ) || '' ,
wordSpacing : a . css ( 'wordSpacing' ) || ''
} ;
s . attr ( 'class' , a . attr ( 'class' ) ) . append ( a . contents ( ) . clone ( ) ) . append ( h2 ) ;
a . replaceWith ( s ) ;
h1 . css ( fn ) ;
h2 . css ( fn ) . width ( Math . min ( h1 . text ( "pW" + h2 [ 0 ] . value ) . width ( ) , w ) ) [ 0 ] . select ( ) ;
} ,
/ * *
* changes the theme
* @ name set _theme ( theme _name [ , theme _url ] )
* @ param { String } theme _name the name of the new theme to apply
* @ 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 .
* @ trigger set _theme . jstree
* /
set _theme : function ( theme _name , theme _url ) {
if ( ! theme _name ) { return false ; }
if ( theme _url === true ) {
var dir = this . settings . core . themes . dir ;
if ( ! dir ) { dir = $ . jstree . path + '/themes' ; }
theme _url = dir + '/' + theme _name + '/style.css' ;
}
if ( theme _url && $ . inArray ( theme _url , themes _loaded ) === - 1 ) {
$ ( 'head' ) . append ( '<' + 'link rel="stylesheet" href="' + theme _url + '" type="text/css" />' ) ;
themes _loaded . push ( theme _url ) ;
}
if ( this . _data . core . themes . name ) {
this . element . removeClass ( 'jstree-' + this . _data . core . themes . name ) ;
}
this . _data . core . themes . name = theme _name ;
this . element . addClass ( 'jstree-' + theme _name ) ;
this . element [ this . settings . core . themes . responsive ? 'addClass' : 'removeClass' ] ( 'jstree-' + theme _name + '-responsive' ) ;
/ * *
* triggered when a theme is set
* @ event
* @ name set _theme . jstree
* @ param { String } theme the new theme
* /
this . trigger ( 'set_theme' , { 'theme' : theme _name } ) ;
} ,
/ * *
* gets the name of the currently applied theme name
* @ name get _theme ( )
* @ return { String }
* /
get _theme : function ( ) { return this . _data . core . themes . name ; } ,
/ * *
* changes the theme variant ( if the theme has variants )
* @ name set _theme _variant ( variant _name )
* @ param { String | Boolean } variant _name the variant to apply ( if ` false ` is used the current variant is removed )
* /
set _theme _variant : function ( variant _name ) {
if ( this . _data . core . themes . variant ) {
this . element . removeClass ( 'jstree-' + this . _data . core . themes . name + '-' + this . _data . core . themes . variant ) ;
}
this . _data . core . themes . variant = variant _name ;
if ( variant _name ) {
this . element . addClass ( 'jstree-' + this . _data . core . themes . name + '-' + this . _data . core . themes . variant ) ;
}
} ,
/ * *
* gets the name of the currently applied theme variant
* @ name get _theme ( )
* @ return { String }
* /
get _theme _variant : function ( ) { return this . _data . core . themes . variant ; } ,
/ * *
* shows a striped background on the container ( if the theme supports it )
* @ name show _stripes ( )
* /
show _stripes : function ( ) { this . _data . core . themes . stripes = true ; this . get _container _ul ( ) . addClass ( "jstree-striped" ) ; } ,
/ * *
* hides the striped background on the container
* @ name hide _stripes ( )
* /
hide _stripes : function ( ) { this . _data . core . themes . stripes = false ; this . get _container _ul ( ) . removeClass ( "jstree-striped" ) ; } ,
/ * *
* toggles the striped background on the container
* @ name toggle _stripes ( )
* /
toggle _stripes : function ( ) { if ( this . _data . core . themes . stripes ) { this . hide _stripes ( ) ; } else { this . show _stripes ( ) ; } } ,
/ * *
* shows the connecting dots ( if the theme supports it )
* @ name show _dots ( )
* /
show _dots : function ( ) { this . _data . core . themes . dots = true ; this . get _container _ul ( ) . removeClass ( "jstree-no-dots" ) ; } ,
/ * *
* hides the connecting dots
* @ name hide _dots ( )
* /
hide _dots : function ( ) { this . _data . core . themes . dots = false ; this . get _container _ul ( ) . addClass ( "jstree-no-dots" ) ; } ,
/ * *
* toggles the connecting dots
* @ name toggle _dots ( )
* /
toggle _dots : function ( ) { if ( this . _data . core . themes . dots ) { this . hide _dots ( ) ; } else { this . show _dots ( ) ; } } ,
/ * *
* show the node icons
* @ name show _icons ( )
* /
show _icons : function ( ) { this . _data . core . themes . icons = true ; this . get _container _ul ( ) . removeClass ( "jstree-no-icons" ) ; } ,
/ * *
* hide the node icons
* @ name hide _icons ( )
* /
hide _icons : function ( ) { this . _data . core . themes . icons = false ; this . get _container _ul ( ) . addClass ( "jstree-no-icons" ) ; } ,
/ * *
* toggle the node icons
* @ name toggle _icons ( )
* /
toggle _icons : function ( ) { if ( this . _data . core . themes . icons ) { this . hide _icons ( ) ; } else { this . show _icons ( ) ; } } ,
/ * *
* set the node icon for a node
* @ name set _icon ( obj , icon )
* @ param { mixed } obj
* @ 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
* /
set _icon : function ( obj , icon ) {
var t1 , t2 , dom , old ;
if ( $ . isArray ( obj ) ) {
obj = obj . slice ( ) ;
for ( t1 = 0 , t2 = obj . length ; t1 < t2 ; t1 ++ ) {
this . set _icon ( obj [ t1 ] , icon ) ;
}
return true ;
}
obj = this . get _node ( obj ) ;
if ( ! obj || obj . id === '#' ) { return false ; }
old = obj . icon ;
obj . icon = icon === true || icon === null || icon === undefined || icon === '' ? true : icon ;
dom = this . get _node ( obj , true ) . children ( ".jstree-anchor" ) . children ( ".jstree-themeicon" ) ;
if ( icon === false ) {
this . hide _icon ( obj ) ;
}
else if ( icon === true || icon === null || icon === undefined || icon === '' ) {
dom . removeClass ( 'jstree-themeicon-custom ' + old ) . css ( "background" , "" ) . removeAttr ( "rel" ) ;
if ( old === false ) { this . show _icon ( obj ) ; }
}
else if ( icon . indexOf ( "/" ) === - 1 && icon . indexOf ( "." ) === - 1 ) {
dom . removeClass ( old ) . css ( "background" , "" ) ;
dom . addClass ( icon + ' jstree-themeicon-custom' ) . attr ( "rel" , icon ) ;
if ( old === false ) { this . show _icon ( obj ) ; }
}
else {
dom . removeClass ( old ) . css ( "background" , "" ) ;
dom . addClass ( 'jstree-themeicon-custom' ) . css ( "background" , "url('" + icon + "') center center no-repeat" ) . attr ( "rel" , icon ) ;
if ( old === false ) { this . show _icon ( obj ) ; }
}
return true ;
} ,
/ * *
* get the node icon for a node
* @ name get _icon ( obj )
* @ param { mixed } obj
* @ return { String }
* /
get _icon : function ( obj ) {
obj = this . get _node ( obj ) ;
return ( ! obj || obj . id === '#' ) ? false : obj . icon ;
} ,
/ * *
* hide the icon on an individual node
* @ name hide _icon ( obj )
* @ param { mixed } obj
* /
hide _icon : function ( obj ) {
var t1 , t2 ;
if ( $ . isArray ( obj ) ) {
obj = obj . slice ( ) ;
for ( t1 = 0 , t2 = obj . length ; t1 < t2 ; t1 ++ ) {
this . hide _icon ( obj [ t1 ] ) ;
}
return true ;
}
obj = this . get _node ( obj ) ;
if ( ! obj || obj === '#' ) { return false ; }
obj . icon = false ;
this . get _node ( obj , true ) . children ( ".jstree-anchor" ) . children ( ".jstree-themeicon" ) . addClass ( 'jstree-themeicon-hidden' ) ;
return true ;
} ,
/ * *
* show the icon on an individual node
* @ name show _icon ( obj )
* @ param { mixed } obj
* /
show _icon : function ( obj ) {
var t1 , t2 , dom ;
if ( $ . isArray ( obj ) ) {
obj = obj . slice ( ) ;
for ( t1 = 0 , t2 = obj . length ; t1 < t2 ; t1 ++ ) {
this . show _icon ( obj [ t1 ] ) ;
}
return true ;
}
obj = this . get _node ( obj ) ;
if ( ! obj || obj === '#' ) { return false ; }
dom = this . get _node ( obj , true ) ;
obj . icon = dom . length ? dom . children ( ".jstree-anchor" ) . children ( ".jstree-themeicon" ) . attr ( 'rel' ) : true ;
if ( ! obj . icon ) { obj . icon = true ; }
dom . children ( ".jstree-anchor" ) . children ( ".jstree-themeicon" ) . removeClass ( 'jstree-themeicon-hidden' ) ;
return true ;
}
} ;
// helpers
$ . vakata = { } ;
// collect attributes
$ . vakata . attributes = function ( node , with _values ) {
node = $ ( node ) [ 0 ] ;
var attr = with _values ? { } : [ ] ;
if ( node && node . attributes ) {
$ . each ( node . attributes , function ( i , v ) {
if ( $ . inArray ( v . name . toLowerCase ( ) , [ 'style' , 'contenteditable' , 'hasfocus' , 'tabindex' ] ) !== - 1 ) { return ; }
if ( v . value !== null && $ . trim ( v . value ) !== '' ) {
if ( with _values ) { attr [ v . name ] = v . value ; }
else { attr . push ( v . name ) ; }
}
} ) ;
}
return attr ;
} ;
$ . vakata . array _unique = function ( array ) {
var a = [ ] , i , j , l , o = { } ;
for ( i = 0 , l = array . length ; i < l ; i ++ ) {
if ( o [ array [ i ] ] === undefined ) {
a . push ( array [ i ] ) ;
o [ array [ i ] ] = true ;
}
}
return a ;
} ;
// remove item from array
$ . vakata . array _remove = function ( array , from , to ) {
var rest = array . slice ( ( to || from ) + 1 || array . length ) ;
array . length = from < 0 ? array . length + from : from ;
array . push . apply ( array , rest ) ;
return array ;
} ;
// remove item from array
$ . vakata . array _remove _item = function ( array , item ) {
var tmp = $ . inArray ( item , array ) ;
return tmp !== - 1 ? $ . vakata . array _remove ( array , tmp ) : array ;
} ;
/ * *
* # # # Checkbox plugin
*
* This plugin renders checkbox icons in front of each node , making multiple selection much easier .
* 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 .
* /
var _i = document . createElement ( 'I' ) ;
_i . className = 'jstree-icon jstree-checkbox' ;
_i . setAttribute ( 'role' , 'presentation' ) ;
/ * *
* stores all defaults for the checkbox plugin
* @ name $ . jstree . defaults . checkbox
* @ plugin checkbox
* /
$ . jstree . defaults . checkbox = {
/ * *
* a boolean indicating if checkboxes should be visible ( can be changed at a later time using ` show_checkboxes() ` and ` hide_checkboxes ` ) . Defaults to ` true ` .
* @ name $ . jstree . defaults . checkbox . visible
* @ plugin checkbox
* /
visible : true ,
/ * *
* a boolean indicating if checkboxes should cascade down and have an undetermined state . Defaults to ` true ` .
* @ name $ . jstree . defaults . checkbox . three _state
* @ plugin checkbox
* /
three _state : true ,
/ * *
* a boolean indicating if clicking anywhere on the node should act as clicking on the checkbox . Defaults to ` true ` .
* @ name $ . jstree . defaults . checkbox . whole _node
* @ plugin checkbox
* /
whole _node : true ,
/ * *
* a boolean indicating if the selected style of a node should be kept , or removed . Defaults to ` true ` .
* @ name $ . jstree . defaults . checkbox . keep _selected _style
* @ plugin checkbox
* /
keep _selected _style : true ,
/ * *
* This setting controls how cascading and undetermined nodes are applied .
* If 'up' is in the string - cascading up is enabled , if 'down' is in the string - cascading down is enabled , if 'undetermined' is in the string - undetermined nodes will be used .
* If ` three_state ` is set to ` true ` this setting is automatically set to 'up+down+undetermined' . Defaults to '' .
* @ name $ . jstree . defaults . checkbox . cascade
* @ plugin checkbox
* /
cascade : '' ,
/ * *
* This setting controls if checkbox are bound to the general tree selection or to an internal array maintained by the checkbox plugin . Defaults to ` true ` , only set to ` false ` if you know exactly what you are doing .
* @ name $ . jstree . defaults . checkbox . tie _selection
* @ plugin checkbox
* /
tie _selection : true
} ;
$ . jstree . plugins . checkbox = function ( options , parent ) {
this . bind = function ( ) {
parent . bind . call ( this ) ;
this . _data . checkbox . uto = false ;
this . _data . checkbox . selected = [ ] ;
if ( this . settings . checkbox . three _state ) {
this . settings . checkbox . cascade = 'up+down+undetermined' ;
}
this . element
. on ( "init.jstree" , $ . proxy ( function ( ) {
this . _data . checkbox . visible = this . settings . checkbox . visible ;
if ( ! this . settings . checkbox . keep _selected _style ) {
this . element . addClass ( 'jstree-checkbox-no-clicked' ) ;
}
if ( this . settings . checkbox . tie _selection ) {
this . element . addClass ( 'jstree-checkbox-selection' ) ;
}
} , this ) )
. on ( "loading.jstree" , $ . proxy ( function ( ) {
this [ this . _data . checkbox . visible ? 'show_checkboxes' : 'hide_checkboxes' ] ( ) ;
} , this ) ) ;
if ( this . settings . checkbox . cascade . indexOf ( 'undetermined' ) !== - 1 ) {
this . element
. on ( 'changed.jstree uncheck_node.jstree check_node.jstree uncheck_all.jstree check_all.jstree move_node.jstree copy_node.jstree redraw.jstree open_node.jstree' , $ . proxy ( function ( ) {
// only if undetermined is in setting
if ( this . _data . checkbox . uto ) { clearTimeout ( this . _data . checkbox . uto ) ; }
this . _data . checkbox . uto = setTimeout ( $ . proxy ( this . _undetermined , this ) , 50 ) ;
} , this ) ) ;
}
if ( ! this . settings . checkbox . tie _selection ) {
this . element
. on ( 'model.jstree' , $ . proxy ( function ( e , data ) {
var m = this . _model . data ,
p = m [ data . parent ] ,
dpc = data . nodes ,
i , j ;
for ( i = 0 , j = dpc . length ; i < j ; i ++ ) {
m [ dpc [ i ] ] . state . checked = ( m [ dpc [ i ] ] . original && m [ dpc [ i ] ] . original . state && m [ dpc [ i ] ] . original . state . checked ) ;
if ( m [ dpc [ i ] ] . state . checked ) {
this . _data . checkbox . selected . push ( dpc [ i ] ) ;
}
}
} , this ) ) ;
}
if ( this . settings . checkbox . cascade . indexOf ( 'up' ) !== - 1 || this . settings . checkbox . cascade . indexOf ( 'down' ) !== - 1 ) {
this . element
. on ( 'model.jstree' , $ . proxy ( function ( e , data ) {
var m = this . _model . data ,
p = m [ data . parent ] ,
dpc = data . nodes ,
chd = [ ] ,
c , i , j , k , l , tmp , s = this . settings . checkbox . cascade , t = this . settings . checkbox . tie _selection ;
if ( s . indexOf ( 'down' ) !== - 1 ) {
// apply down
if ( p . state [ t ? 'selected' : 'checked' ] ) {
for ( i = 0 , j = dpc . length ; i < j ; i ++ ) {
m [ dpc [ i ] ] . state [ t ? 'selected' : 'checked' ] = true ;
}
this . _data [ t ? 'core' : 'checkbox' ] . selected = this . _data [ t ? 'core' : 'checkbox' ] . selected . concat ( dpc ) ;
}
else {
for ( i = 0 , j = dpc . length ; i < j ; i ++ ) {
if ( m [ dpc [ i ] ] . state [ t ? 'selected' : 'checked' ] ) {
for ( k = 0 , l = m [ dpc [ i ] ] . children _d . length ; k < l ; k ++ ) {
m [ m [ dpc [ i ] ] . children _d [ k ] ] . state [ t ? 'selected' : 'checked' ] = true ;
}
this . _data [ t ? 'core' : 'checkbox' ] . selected = this . _data [ t ? 'core' : 'checkbox' ] . selected . concat ( m [ dpc [ i ] ] . children _d ) ;
}
}
}
}
if ( s . indexOf ( 'up' ) !== - 1 ) {
// apply up
for ( i = 0 , j = p . children _d . length ; i < j ; i ++ ) {
if ( ! m [ p . children _d [ i ] ] . children . length ) {
chd . push ( m [ p . children _d [ i ] ] . parent ) ;
}
}
chd = $ . vakata . array _unique ( chd ) ;
for ( k = 0 , l = chd . length ; k < l ; k ++ ) {
p = m [ chd [ k ] ] ;
while ( p && p . id !== '#' ) {
c = 0 ;
for ( i = 0 , j = p . children . length ; i < j ; i ++ ) {
c += m [ p . children [ i ] ] . state [ t ? 'selected' : 'checked' ] ;
}
if ( c === j ) {
p . state [ t ? 'selected' : 'checked' ] = true ;
this . _data [ t ? 'core' : 'checkbox' ] . selected . push ( p . id ) ;
tmp = this . get _node ( p , true ) ;
if ( tmp && tmp . length ) {
tmp . attr ( 'aria-selected' , true ) . children ( '.jstree-anchor' ) . addClass ( t ? 'jstree-clicked' : 'jstree-checked' ) ;
}
}
else {
break ;
}
p = this . get _node ( p . parent ) ;
}
}
}
this . _data [ t ? 'core' : 'checkbox' ] . selected = $ . vakata . array _unique ( this . _data [ t ? 'core' : 'checkbox' ] . selected ) ;
} , this ) )
. on ( this . settings . checkbox . tie _selection ? 'select_node.jstree' : 'check_node.jstree' , $ . proxy ( function ( e , data ) {
var obj = data . node ,
m = this . _model . data ,
par = this . get _node ( obj . parent ) ,
dom = this . get _node ( obj , true ) ,
i , j , c , tmp , s = this . settings . checkbox . cascade , t = this . settings . checkbox . tie _selection ;
// apply down
if ( s . indexOf ( 'down' ) !== - 1 ) {
this . _data [ t ? 'core' : 'checkbox' ] . selected = $ . vakata . array _unique ( this . _data [ t ? 'core' : 'checkbox' ] . selected . concat ( obj . children _d ) ) ;
for ( i = 0 , j = obj . children _d . length ; i < j ; i ++ ) {
tmp = m [ obj . children _d [ i ] ] ;
tmp . state [ t ? 'selected' : 'checked' ] = true ;
if ( tmp && tmp . original && tmp . original . state && tmp . original . state . undetermined ) {
tmp . original . state . undetermined = false ;
}
}
}
// apply up
if ( s . indexOf ( 'up' ) !== - 1 ) {
while ( par && par . id !== '#' ) {
c = 0 ;
for ( i = 0 , j = par . children . length ; i < j ; i ++ ) {
c += m [ par . children [ i ] ] . state [ t ? 'selected' : 'checked' ] ;
}
if ( c === j ) {
par . state [ t ? 'selected' : 'checked' ] = true ;
this . _data [ t ? 'core' : 'checkbox' ] . selected . push ( par . id ) ;
tmp = this . get _node ( par , true ) ;
if ( tmp && tmp . length ) {
tmp . attr ( 'aria-selected' , true ) . children ( '.jstree-anchor' ) . addClass ( t ? 'jstree-clicked' : 'jstree-checked' ) ;
}
}
else {
break ;
}
par = this . get _node ( par . parent ) ;
}
}
// apply down (process .children separately?)
if ( s . indexOf ( 'down' ) !== - 1 && dom . length ) {
dom . find ( '.jstree-anchor' ) . addClass ( t ? 'jstree-clicked' : 'jstree-checked' ) . parent ( ) . attr ( 'aria-selected' , true ) ;
}
} , this ) )
. on ( this . settings . checkbox . tie _selection ? 'deselect_all.jstree' : 'uncheck_all.jstree' , $ . proxy ( function ( e , data ) {
var obj = this . get _node ( '#' ) ,
m = this . _model . data ,
i , j , tmp ;
for ( i = 0 , j = obj . children _d . length ; i < j ; i ++ ) {
tmp = m [ obj . children _d [ i ] ] ;
if ( tmp && tmp . original && tmp . original . state && tmp . original . state . undetermined ) {
tmp . original . state . undetermined = false ;
}
}
} , this ) )
. on ( this . settings . checkbox . tie _selection ? 'deselect_node.jstree' : 'uncheck_node.jstree' , $ . proxy ( function ( e , data ) {
var obj = data . node ,
dom = this . get _node ( obj , true ) ,
i , j , tmp , s = this . settings . checkbox . cascade , t = this . settings . checkbox . tie _selection ;
if ( obj && obj . original && obj . original . state && obj . original . state . undetermined ) {
obj . original . state . undetermined = false ;
}
// apply down
if ( s . indexOf ( 'down' ) !== - 1 ) {
for ( i = 0 , j = obj . children _d . length ; i < j ; i ++ ) {
tmp = this . _model . data [ obj . children _d [ i ] ] ;
tmp . state [ t ? 'selected' : 'checked' ] = false ;
if ( tmp && tmp . original && tmp . original . state && tmp . original . state . undetermined ) {
tmp . original . state . undetermined = false ;
}
}
}
// apply up
if ( s . indexOf ( 'up' ) !== - 1 ) {
for ( i = 0 , j = obj . parents . length ; i < j ; i ++ ) {
tmp = this . _model . data [ obj . parents [ i ] ] ;
tmp . state [ t ? 'selected' : 'checked' ] = false ;
if ( tmp && tmp . original && tmp . original . state && tmp . original . state . undetermined ) {
tmp . original . state . undetermined = false ;
}
tmp = this . get _node ( obj . parents [ i ] , true ) ;
if ( tmp && tmp . length ) {
tmp . attr ( 'aria-selected' , false ) . children ( '.jstree-anchor' ) . removeClass ( t ? 'jstree-clicked' : 'jstree-checked' ) ;
}
}
}
tmp = [ ] ;
for ( i = 0 , j = this . _data [ t ? 'core' : 'checkbox' ] . selected . length ; i < j ; i ++ ) {
// apply down + apply up
if (
( s . indexOf ( 'down' ) === - 1 || $ . inArray ( this . _data [ t ? 'core' : 'checkbox' ] . selected [ i ] , obj . children _d ) === - 1 ) &&
( s . indexOf ( 'up' ) === - 1 || $ . inArray ( this . _data [ t ? 'core' : 'checkbox' ] . selected [ i ] , obj . parents ) === - 1 )
) {
tmp . push ( this . _data [ t ? 'core' : 'checkbox' ] . selected [ i ] ) ;
}
}
this . _data [ t ? 'core' : 'checkbox' ] . selected = $ . vakata . array _unique ( tmp ) ;
// apply down (process .children separately?)
if ( s . indexOf ( 'down' ) !== - 1 && dom . length ) {
dom . find ( '.jstree-anchor' ) . removeClass ( t ? 'jstree-clicked' : 'jstree-checked' ) . parent ( ) . attr ( 'aria-selected' , false ) ;
}
} , this ) ) ;
}
if ( this . settings . checkbox . cascade . indexOf ( 'up' ) !== - 1 ) {
this . element
. on ( 'delete_node.jstree' , $ . proxy ( function ( e , data ) {
// apply up (whole handler)
var p = this . get _node ( data . parent ) ,
m = this . _model . data ,
i , j , c , tmp , t = this . settings . checkbox . tie _selection ;
while ( p && p . id !== '#' ) {
c = 0 ;
for ( i = 0 , j = p . children . length ; i < j ; i ++ ) {
c += m [ p . children [ i ] ] . state [ t ? 'selected' : 'checked' ] ;
}
if ( c === j ) {
p . state [ t ? 'selected' : 'checked' ] = true ;
this . _data [ t ? 'core' : 'checkbox' ] . selected . push ( p . id ) ;
tmp = this . get _node ( p , true ) ;
if ( tmp && tmp . length ) {
tmp . attr ( 'aria-selected' , true ) . children ( '.jstree-anchor' ) . addClass ( t ? 'jstree-clicked' : 'jstree-checked' ) ;
}
}
else {
break ;
}
p = this . get _node ( p . parent ) ;
}
} , this ) )
. on ( 'move_node.jstree' , $ . proxy ( function ( e , data ) {
// apply up (whole handler)
var is _multi = data . is _multi ,
old _par = data . old _parent ,
new _par = this . get _node ( data . parent ) ,
m = this . _model . data ,
p , c , i , j , tmp , t = this . settings . checkbox . tie _selection ;
if ( ! is _multi ) {
p = this . get _node ( old _par ) ;
while ( p && p . id !== '#' ) {
c = 0 ;
for ( i = 0 , j = p . children . length ; i < j ; i ++ ) {
c += m [ p . children [ i ] ] . state [ t ? 'selected' : 'checked' ] ;
}
if ( c === j ) {
p . state [ t ? 'selected' : 'checked' ] = true ;
this . _data [ t ? 'core' : 'checkbox' ] . selected . push ( p . id ) ;
tmp = this . get _node ( p , true ) ;
if ( tmp && tmp . length ) {
tmp . attr ( 'aria-selected' , true ) . children ( '.jstree-anchor' ) . addClass ( t ? 'jstree-clicked' : 'jstree-checked' ) ;
}
}
else {
break ;
}
p = this . get _node ( p . parent ) ;
}
}
p = new _par ;
while ( p && p . id !== '#' ) {
c = 0 ;
for ( i = 0 , j = p . children . length ; i < j ; i ++ ) {
c += m [ p . children [ i ] ] . state [ t ? 'selected' : 'checked' ] ;
}
if ( c === j ) {
if ( ! p . state [ t ? 'selected' : 'checked' ] ) {
p . state [ t ? 'selected' : 'checked' ] = true ;
this . _data [ t ? 'core' : 'checkbox' ] . selected . push ( p . id ) ;
tmp = this . get _node ( p , true ) ;
if ( tmp && tmp . length ) {
tmp . attr ( 'aria-selected' , true ) . children ( '.jstree-anchor' ) . addClass ( t ? 'jstree-clicked' : 'jstree-checked' ) ;
}
}
}
else {
if ( p . state [ t ? 'selected' : 'checked' ] ) {
p . state [ t ? 'selected' : 'checked' ] = false ;
this . _data [ t ? 'core' : 'checkbox' ] . selected = $ . vakata . array _remove _item ( this . _data [ t ? 'core' : 'checkbox' ] . selected , p . id ) ;
tmp = this . get _node ( p , true ) ;
if ( tmp && tmp . length ) {
tmp . attr ( 'aria-selected' , false ) . children ( '.jstree-anchor' ) . removeClass ( t ? 'jstree-clicked' : 'jstree-checked' ) ;
}
}
else {
break ;
}
}
p = this . get _node ( p . parent ) ;
}
} , this ) ) ;
}
} ;
/ * *
* set the undetermined state where and if necessary . Used internally .
* @ private
* @ name _undetermined ( )
* @ plugin checkbox
* /
this . _undetermined = function ( ) {
if ( this . element === null ) { return ; }
var i , j , k , l , o = { } , m = this . _model . data , t = this . settings . checkbox . tie _selection , s = this . _data [ t ? 'core' : 'checkbox' ] . selected , p = [ ] , tt = this ;
for ( i = 0 , j = s . length ; i < j ; i ++ ) {
if ( m [ s [ i ] ] && m [ s [ i ] ] . parents ) {
for ( k = 0 , l = m [ s [ i ] ] . parents . length ; k < l ; k ++ ) {
if ( o [ m [ s [ i ] ] . parents [ k ] ] === undefined && m [ s [ i ] ] . parents [ k ] !== '#' ) {
o [ m [ s [ i ] ] . parents [ k ] ] = true ;
p . push ( m [ s [ i ] ] . parents [ k ] ) ;
}
}
}
}
// attempt for server side undetermined state
this . element . find ( '.jstree-closed' ) . not ( ':has(.jstree-children)' )
. each ( function ( ) {
var tmp = tt . get _node ( this ) , tmp2 ;
if ( ! tmp . state . loaded ) {
if ( tmp . original && tmp . original . state && tmp . original . state . undetermined && tmp . original . state . undetermined === true ) {
if ( o [ tmp . id ] === undefined && tmp . id !== '#' ) {
o [ tmp . id ] = true ;
p . push ( tmp . id ) ;
}
for ( k = 0 , l = tmp . parents . length ; k < l ; k ++ ) {
if ( o [ tmp . parents [ k ] ] === undefined && tmp . parents [ k ] !== '#' ) {
o [ tmp . parents [ k ] ] = true ;
p . push ( tmp . parents [ k ] ) ;
}
}
}
}
else {
for ( i = 0 , j = tmp . children _d . length ; i < j ; i ++ ) {
tmp2 = m [ tmp . children _d [ i ] ] ;
if ( ! tmp2 . state . loaded && tmp2 . original && tmp2 . original . state && tmp2 . original . state . undetermined && tmp2 . original . state . undetermined === true ) {
if ( o [ tmp2 . id ] === undefined && tmp2 . id !== '#' ) {
o [ tmp2 . id ] = true ;
p . push ( tmp2 . id ) ;
}
for ( k = 0 , l = tmp2 . parents . length ; k < l ; k ++ ) {
if ( o [ tmp2 . parents [ k ] ] === undefined && tmp2 . parents [ k ] !== '#' ) {
o [ tmp2 . parents [ k ] ] = true ;
p . push ( tmp2 . parents [ k ] ) ;
}
}
}
}
}
} ) ;
this . element . find ( '.jstree-undetermined' ) . removeClass ( 'jstree-undetermined' ) ;
for ( i = 0 , j = p . length ; i < j ; i ++ ) {
if ( ! m [ p [ i ] ] . state [ t ? 'selected' : 'checked' ] ) {
s = this . get _node ( p [ i ] , true ) ;
if ( s && s . length ) {
s . children ( '.jstree-anchor' ) . children ( '.jstree-checkbox' ) . addClass ( 'jstree-undetermined' ) ;
}
}
}
} ;
this . redraw _node = function ( obj , deep , is _callback , force _render ) {
obj = parent . redraw _node . apply ( this , arguments ) ;
if ( obj ) {
var i , j , tmp = null ;
for ( i = 0 , j = obj . childNodes . length ; i < j ; i ++ ) {
if ( obj . childNodes [ i ] && obj . childNodes [ i ] . className && obj . childNodes [ i ] . className . indexOf ( "jstree-anchor" ) !== - 1 ) {
tmp = obj . childNodes [ i ] ;
break ;
}
}
if ( tmp ) {
if ( ! this . settings . checkbox . tie _selection && this . _model . data [ obj . id ] . state . checked ) { tmp . className += ' jstree-checked' ; }
tmp . insertBefore ( _i . cloneNode ( false ) , tmp . childNodes [ 0 ] ) ;
}
}
if ( ! is _callback && this . settings . checkbox . cascade . indexOf ( 'undetermined' ) !== - 1 ) {
if ( this . _data . checkbox . uto ) { clearTimeout ( this . _data . checkbox . uto ) ; }
this . _data . checkbox . uto = setTimeout ( $ . proxy ( this . _undetermined , this ) , 50 ) ;
}
return obj ;
} ;
/ * *
* show the node checkbox icons
* @ name show _checkboxes ( )
* @ plugin checkbox
* /
this . show _checkboxes = function ( ) { this . _data . core . themes . checkboxes = true ; this . get _container _ul ( ) . removeClass ( "jstree-no-checkboxes" ) ; } ;
/ * *
* hide the node checkbox icons
* @ name hide _checkboxes ( )
* @ plugin checkbox
* /
this . hide _checkboxes = function ( ) { this . _data . core . themes . checkboxes = false ; this . get _container _ul ( ) . addClass ( "jstree-no-checkboxes" ) ; } ;
/ * *
* toggle the node icons
* @ name toggle _checkboxes ( )
* @ plugin checkbox
* /
this . toggle _checkboxes = function ( ) { if ( this . _data . core . themes . checkboxes ) { this . hide _checkboxes ( ) ; } else { this . show _checkboxes ( ) ; } } ;
/ * *
* checks if a node is in an undetermined state
* @ name is _undetermined ( obj )
* @ param { mixed } obj
* @ return { Boolean }
* /
this . is _undetermined = function ( obj ) {
obj = this . get _node ( obj ) ;
var s = this . settings . checkbox . cascade , i , j , t = this . settings . checkbox . tie _selection , d = this . _data [ t ? 'core' : 'checkbox' ] . selected , m = this . _model . data ;
if ( ! obj || obj . state [ t ? 'selected' : 'checked' ] === true || s . indexOf ( 'undetermined' ) === - 1 || ( s . indexOf ( 'down' ) === - 1 && s . indexOf ( 'up' ) === - 1 ) ) {
return false ;
}
if ( ! obj . state . loaded && obj . original . state . undetermined === true ) {
return true ;
}
for ( i = 0 , j = obj . children _d . length ; i < j ; i ++ ) {
if ( $ . inArray ( obj . children _d [ i ] , d ) !== - 1 || ( ! m [ obj . children _d [ i ] ] . state . loaded && m [ obj . children _d [ i ] ] . original . state . undetermined ) ) {
return true ;
}
}
return false ;
} ;
this . activate _node = function ( obj , e ) {
if ( this . settings . checkbox . tie _selection && ( this . settings . checkbox . whole _node || $ ( e . target ) . hasClass ( 'jstree-checkbox' ) ) ) {
e . ctrlKey = true ;
}
if ( this . settings . checkbox . tie _selection || ( ! this . settings . checkbox . whole _node && ! $ ( e . target ) . hasClass ( 'jstree-checkbox' ) ) ) {
return parent . activate _node . call ( this , obj , e ) ;
}
if ( this . is _disabled ( obj ) ) {
return false ;
}
if ( this . is _checked ( obj ) ) {
this . uncheck _node ( obj , e ) ;
}
else {
this . check _node ( obj , e ) ;
}
this . trigger ( 'activate_node' , { 'node' : this . get _node ( obj ) } ) ;
} ;
/ * *
* check a node ( only if tie _selection in checkbox settings is false , otherwise select _node will be called internally )
* @ name check _node ( obj )
* @ param { mixed } obj an array can be used to check multiple nodes
* @ trigger check _node . jstree
* @ plugin checkbox
* /
this . check _node = function ( obj , e ) {
if ( this . settings . checkbox . tie _selection ) { return this . select _node ( obj , false , true , e ) ; }
var dom , t1 , t2 , th ;
if ( $ . isArray ( obj ) ) {
obj = obj . slice ( ) ;
for ( t1 = 0 , t2 = obj . length ; t1 < t2 ; t1 ++ ) {
this . check _node ( obj [ t1 ] , e ) ;
}
return true ;
}
obj = this . get _node ( obj ) ;
if ( ! obj || obj . id === '#' ) {
return false ;
}
dom = this . get _node ( obj , true ) ;
if ( ! obj . state . checked ) {
obj . state . checked = true ;
this . _data . checkbox . selected . push ( obj . id ) ;
if ( dom && dom . length ) {
dom . children ( '.jstree-anchor' ) . addClass ( 'jstree-checked' ) ;
}
/ * *
* triggered when an node is checked ( only if tie _selection in checkbox settings is false )
* @ event
* @ name check _node . jstree
* @ param { Object } node
* @ param { Array } selected the current selection
* @ param { Object } event the event ( if any ) that triggered this check _node
* @ plugin checkbox
* /
this . trigger ( 'check_node' , { 'node' : obj , 'selected' : this . _data . checkbox . selected , 'event' : e } ) ;
}
} ;
/ * *
* uncheck a node ( only if tie _selection in checkbox settings is false , otherwise deselect _node will be called internally )
* @ name uncheck _node ( obj )
* @ param { mixed } obj an array can be used to uncheck multiple nodes
* @ trigger uncheck _node . jstree
* @ plugin checkbox
* /
this . uncheck _node = function ( obj , e ) {
if ( this . settings . checkbox . tie _selection ) { return this . deselect _node ( obj , false , e ) ; }
var t1 , t2 , dom ;
if ( $ . isArray ( obj ) ) {
obj = obj . slice ( ) ;
for ( t1 = 0 , t2 = obj . length ; t1 < t2 ; t1 ++ ) {
this . uncheck _node ( obj [ t1 ] , e ) ;
}
return true ;
}
obj = this . get _node ( obj ) ;
if ( ! obj || obj . id === '#' ) {
return false ;
}
dom = this . get _node ( obj , true ) ;
if ( obj . state . checked ) {
obj . state . checked = false ;
this . _data . checkbox . selected = $ . vakata . array _remove _item ( this . _data . checkbox . selected , obj . id ) ;
if ( dom . length ) {
dom . children ( '.jstree-anchor' ) . removeClass ( 'jstree-checked' ) ;
}
/ * *
* triggered when an node is unchecked ( only if tie _selection in checkbox settings is false )
* @ event
* @ name uncheck _node . jstree
* @ param { Object } node
* @ param { Array } selected the current selection
* @ param { Object } event the event ( if any ) that triggered this uncheck _node
* @ plugin checkbox
* /
this . trigger ( 'uncheck_node' , { 'node' : obj , 'selected' : this . _data . checkbox . selected , 'event' : e } ) ;
}
} ;
/ * *
* checks all nodes in the tree ( only if tie _selection in checkbox settings is false , otherwise select _all will be called internally )
* @ name check _all ( )
* @ trigger check _all . jstree , changed . jstree
* @ plugin checkbox
* /
this . check _all = function ( ) {
if ( this . settings . checkbox . tie _selection ) { return this . select _all ( ) ; }
var tmp = this . _data . checkbox . selected . concat ( [ ] ) , i , j ;
this . _data . checkbox . selected = this . _model . data [ '#' ] . children _d . concat ( ) ;
for ( i = 0 , j = this . _data . checkbox . selected . length ; i < j ; i ++ ) {
if ( this . _model . data [ this . _data . checkbox . selected [ i ] ] ) {
this . _model . data [ this . _data . checkbox . selected [ i ] ] . state . checked = true ;
}
}
this . redraw ( true ) ;
/ * *
* triggered when all nodes are checked ( only if tie _selection in checkbox settings is false )
* @ event
* @ name check _all . jstree
* @ param { Array } selected the current selection
* @ plugin checkbox
* /
this . trigger ( 'check_all' , { 'selected' : this . _data . checkbox . selected } ) ;
} ;
/ * *
* uncheck all checked nodes ( only if tie _selection in checkbox settings is false , otherwise deselect _all will be called internally )
* @ name uncheck _all ( )
* @ trigger uncheck _all . jstree
* @ plugin checkbox
* /
this . uncheck _all = function ( ) {
if ( this . settings . checkbox . tie _selection ) { return this . deselect _all ( ) ; }
var tmp = this . _data . checkbox . selected . concat ( [ ] ) , i , j ;
for ( i = 0 , j = this . _data . checkbox . selected . length ; i < j ; i ++ ) {
if ( this . _model . data [ this . _data . checkbox . selected [ i ] ] ) {
this . _model . data [ this . _data . checkbox . selected [ i ] ] . state . checked = false ;
}
}
this . _data . checkbox . selected = [ ] ;
this . element . find ( '.jstree-checked' ) . removeClass ( 'jstree-checked' ) ;
/ * *
* triggered when all nodes are unchecked ( only if tie _selection in checkbox settings is false )
* @ event
* @ name uncheck _all . jstree
* @ param { Object } node the previous selection
* @ param { Array } selected the current selection
* @ plugin checkbox
* /
this . trigger ( 'uncheck_all' , { 'selected' : this . _data . checkbox . selected , 'node' : tmp } ) ;
} ;
/ * *
* checks if a node is checked ( if tie _selection is on in the settings this function will return the same as is _selected )
* @ name is _checked ( obj )
* @ param { mixed } obj
* @ return { Boolean }
* @ plugin checkbox
* /
this . is _checked = function ( obj ) {
if ( this . settings . checkbox . tie _selection ) { return this . is _selected ( obj ) ; }
obj = this . get _node ( obj ) ;
if ( ! obj || obj . id === '#' ) { return false ; }
return obj . state . checked ;
} ;
/ * *
* get an array of all checked nodes ( if tie _selection is on in the settings this function will return the same as get _selected )
* @ name get _checked ( [ full ] )
* @ param { mixed } full if set to ` true ` the returned array will consist of the full node objects , otherwise - only IDs will be returned
* @ return { Array }
* @ plugin checkbox
* /
this . get _checked = function ( full ) {
if ( this . settings . checkbox . tie _selection ) { return this . get _selected ( full ) ; }
return full ? $ . map ( this . _data . checkbox . selected , $ . proxy ( function ( i ) { return this . get _node ( i ) ; } , this ) ) : this . _data . checkbox . selected ;
} ;
/ * *
* get an array of all top level checked nodes ( ignoring children of checked nodes ) ( if tie _selection is on in the settings this function will return the same as get _top _selected )
* @ name get _top _checked ( [ full ] )
* @ param { mixed } full if set to ` true ` the returned array will consist of the full node objects , otherwise - only IDs will be returned
* @ return { Array }
* @ plugin checkbox
* /
this . get _top _checked = function ( full ) {
if ( this . settings . checkbox . tie _selection ) { return this . get _top _selected ( full ) ; }
var tmp = this . get _checked ( true ) ,
obj = { } , i , j , k , l ;
for ( i = 0 , j = tmp . length ; i < j ; i ++ ) {
obj [ tmp [ i ] . id ] = tmp [ i ] ;
}
for ( i = 0 , j = tmp . length ; i < j ; i ++ ) {
for ( k = 0 , l = tmp [ i ] . children _d . length ; k < l ; k ++ ) {
if ( obj [ tmp [ i ] . children _d [ k ] ] ) {
delete obj [ tmp [ i ] . children _d [ k ] ] ;
}
}
}
tmp = [ ] ;
for ( i in obj ) {
if ( obj . hasOwnProperty ( i ) ) {
tmp . push ( i ) ;
}
}
return full ? $ . map ( tmp , $ . proxy ( function ( i ) { return this . get _node ( i ) ; } , this ) ) : tmp ;
} ;
/ * *
* get an array of all bottom level checked nodes ( ignoring selected parents ) ( if tie _selection is on in the settings this function will return the same as get _bottom _selected )
* @ name get _bottom _checked ( [ full ] )
* @ param { mixed } full if set to ` true ` the returned array will consist of the full node objects , otherwise - only IDs will be returned
* @ return { Array }
* @ plugin checkbox
* /
this . get _bottom _checked = function ( full ) {
if ( this . settings . checkbox . tie _selection ) { return this . get _bottom _selected ( full ) ; }
var tmp = this . get _checked ( true ) ,
obj = [ ] , i , j ;
for ( i = 0 , j = tmp . length ; i < j ; i ++ ) {
if ( ! tmp [ i ] . children . length ) {
obj . push ( tmp [ i ] . id ) ;
}
}
return full ? $ . map ( obj , $ . proxy ( function ( i ) { return this . get _node ( i ) ; } , this ) ) : obj ;
} ;
this . load _node = function ( obj , callback ) {
var k , l , i , j , c , tmp ;
if ( ! $ . isArray ( obj ) && ! this . settings . checkbox . tie _selection ) {
tmp = this . get _node ( obj ) ;
if ( tmp && tmp . state . loaded ) {
for ( k = 0 , l = tmp . children _d . length ; k < l ; k ++ ) {
if ( this . _model . data [ tmp . children _d [ k ] ] . state . checked ) {
c = true ;
this . _data . checkbox . selected = $ . vakata . array _remove _item ( this . _data . checkbox . selected , tmp . children _d [ k ] ) ;
}
}
}
}
return parent . load _node . apply ( this , arguments ) ;
} ;
this . get _state = function ( ) {
var state = parent . get _state . apply ( this , arguments ) ;
if ( this . settings . checkbox . tie _selection ) { return state ; }
state . checkbox = this . _data . checkbox . selected . slice ( ) ;
return state ;
} ;
this . set _state = function ( state , callback ) {
var res = parent . set _state . apply ( this , arguments ) ;
if ( res && state . checkbox ) {
if ( ! this . settings . checkbox . tie _selection ) {
this . uncheck _all ( ) ;
var _this = this ;
$ . each ( state . checkbox , function ( i , v ) {
_this . check _node ( v ) ;
} ) ;
}
delete state . checkbox ;
this . set _state ( state , callback ) ;
return false ;
}
return res ;
} ;
} ;
// include the checkbox plugin by default
// $.jstree.defaults.plugins.push("checkbox");
/ * *
* # # # Contextmenu plugin
*
* Shows a context menu when a node is right - clicked .
* /
/ * *
* stores all defaults for the contextmenu plugin
* @ name $ . jstree . defaults . contextmenu
* @ plugin contextmenu
* /
$ . jstree . defaults . contextmenu = {
/ * *
* a boolean indicating if the node should be selected when the context menu is invoked on it . Defaults to ` true ` .
* @ name $ . jstree . defaults . contextmenu . select _node
* @ plugin contextmenu
* /
select _node : true ,
/ * *
* a boolean indicating if the menu should be shown aligned with the node . Defaults to ` true ` , otherwise the mouse coordinates are used .
* @ name $ . jstree . defaults . contextmenu . show _at _node
* @ plugin contextmenu
* /
show _at _node : true ,
/ * *
* 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 ) .
*
* 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 ) :
*
* * ` separator_before ` - a boolean indicating if there should be a separator before this item
* * ` separator_after ` - a boolean indicating if there should be a separator after this item
* * ` _disabled ` - a boolean indicating if this action should be disabled
* * ` label ` - a string - the name of the action ( could be a function returning a string )
* * ` action ` - a function to be executed if this item is chosen
* * ` 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
* * ` shortcut ` - keyCode which will trigger the action if the menu is open ( for example ` 113 ` for rename , which equals F2 )
* * ` shortcut_label ` - shortcut label ( like for example ` F2 ` for rename )
*
* @ name $ . jstree . defaults . contextmenu . items
* @ plugin contextmenu
* /
items : function ( o , cb ) { // Could be an object directly
return {
"create" : {
"separator_before" : false ,
"separator_after" : true ,
"_disabled" : false , //(this.check("create_node", data.reference, {}, "last")),
"label" : "Create" ,
"action" : function ( data ) {
var inst = $ . jstree . reference ( data . reference ) ,
obj = inst . get _node ( data . reference ) ;
inst . create _node ( obj , { } , "last" , function ( new _node ) {
setTimeout ( function ( ) { inst . edit ( new _node ) ; } , 0 ) ;
} ) ;
}
} ,
"rename" : {
"separator_before" : false ,
"separator_after" : false ,
"_disabled" : false , //(this.check("rename_node", data.reference, this.get_parent(data.reference), "")),
"label" : "Rename" ,
/ *
"shortcut" : 113 ,
"shortcut_label" : 'F2' ,
"icon" : "glyphicon glyphicon-leaf" ,
* /
"action" : function ( data ) {
var inst = $ . jstree . reference ( data . reference ) ,
obj = inst . get _node ( data . reference ) ;
inst . edit ( obj ) ;
}
} ,
"remove" : {
"separator_before" : false ,
"icon" : false ,
"separator_after" : false ,
"_disabled" : false , //(this.check("delete_node", data.reference, this.get_parent(data.reference), "")),
"label" : "Delete" ,
"action" : function ( data ) {
var inst = $ . jstree . reference ( data . reference ) ,
obj = inst . get _node ( data . reference ) ;
if ( inst . is _selected ( obj ) ) {
inst . delete _node ( inst . get _selected ( ) ) ;
}
else {
inst . delete _node ( obj ) ;
}
}
} ,
"ccp" : {
"separator_before" : true ,
"icon" : false ,
"separator_after" : false ,
"label" : "Edit" ,
"action" : false ,
"submenu" : {
"cut" : {
"separator_before" : false ,
"separator_after" : false ,
"label" : "Cut" ,
"action" : function ( data ) {
var inst = $ . jstree . reference ( data . reference ) ,
obj = inst . get _node ( data . reference ) ;
if ( inst . is _selected ( obj ) ) {
inst . cut ( inst . get _top _selected ( ) ) ;
}
else {
inst . cut ( obj ) ;
}
}
} ,
"copy" : {
"separator_before" : false ,
"icon" : false ,
"separator_after" : false ,
"label" : "Copy" ,
"action" : function ( data ) {
var inst = $ . jstree . reference ( data . reference ) ,
obj = inst . get _node ( data . reference ) ;
if ( inst . is _selected ( obj ) ) {
inst . copy ( inst . get _top _selected ( ) ) ;
}
else {
inst . copy ( obj ) ;
}
}
} ,
"paste" : {
"separator_before" : false ,
"icon" : false ,
"_disabled" : function ( data ) {
return ! $ . jstree . reference ( data . reference ) . can _paste ( ) ;
} ,
"separator_after" : false ,
"label" : "Paste" ,
"action" : function ( data ) {
var inst = $ . jstree . reference ( data . reference ) ,
obj = inst . get _node ( data . reference ) ;
inst . paste ( obj ) ;
}
}
}
}
} ;
}
} ;
$ . jstree . plugins . contextmenu = function ( options , parent ) {
this . bind = function ( ) {
parent . bind . call ( this ) ;
var last _ts = 0 , cto = null , ex , ey ;
this . element
. on ( "contextmenu.jstree" , ".jstree-anchor" , $ . proxy ( function ( e , data ) {
e . preventDefault ( ) ;
last _ts = e . ctrlKey ? + new Date ( ) : 0 ;
if ( data || cto ) {
last _ts = ( + new Date ( ) ) + 10000 ;
}
if ( cto ) {
clearTimeout ( cto ) ;
}
if ( ! this . is _loading ( e . currentTarget ) ) {
this . show _contextmenu ( e . currentTarget , e . pageX , e . pageY , e ) ;
}
} , this ) )
. on ( "click.jstree" , ".jstree-anchor" , $ . proxy ( function ( e ) {
if ( this . _data . contextmenu . visible && ( ! last _ts || ( + new Date ( ) ) - last _ts > 250 ) ) { // work around safari & macOS ctrl+click
$ . vakata . context . hide ( ) ;
}
last _ts = 0 ;
} , this ) )
. on ( "touchstart.jstree" , ".jstree-anchor" , function ( e ) {
if ( ! e . originalEvent || ! e . originalEvent . changedTouches || ! e . originalEvent . changedTouches [ 0 ] ) {
return ;
}
ex = e . pageX ;
ey = e . pageY ;
cto = setTimeout ( function ( ) {
$ ( e . currentTarget ) . trigger ( 'contextmenu' , true ) ;
} , 750 ) ;
} )
. on ( 'touchmove.vakata.jstree' , function ( e ) {
if ( cto && e . originalEvent && e . originalEvent . changedTouches && e . originalEvent . changedTouches [ 0 ] && ( Math . abs ( ex - e . pageX ) > 50 || Math . abs ( ey - e . pageY ) > 50 ) ) {
clearTimeout ( cto ) ;
}
} )
. on ( 'touchend.vakata.jstree' , function ( e ) {
if ( cto ) {
clearTimeout ( cto ) ;
}
} ) ;
/ *
if ( ! ( 'oncontextmenu' in document . body ) && ( 'ontouchstart' in document . body ) ) {
var el = null , tm = null ;
this . element
. on ( "touchstart" , ".jstree-anchor" , function ( e ) {
el = e . currentTarget ;
tm = + new Date ( ) ;
$ ( document ) . one ( "touchend" , function ( e ) {
e . target = document . elementFromPoint ( e . originalEvent . targetTouches [ 0 ] . pageX - window . pageXOffset , e . originalEvent . targetTouches [ 0 ] . pageY - window . pageYOffset ) ;
e . currentTarget = e . target ;
tm = ( ( + ( new Date ( ) ) ) - tm ) ;
if ( e . target === el && tm > 600 && tm < 1000 ) {
e . preventDefault ( ) ;
$ ( el ) . trigger ( 'contextmenu' , e ) ;
}
el = null ;
tm = null ;
} ) ;
} ) ;
}
* /
$ ( document ) . on ( "context_hide.vakata.jstree" , $ . proxy ( function ( ) { this . _data . contextmenu . visible = false ; } , this ) ) ;
} ;
this . teardown = function ( ) {
if ( this . _data . contextmenu . visible ) {
$ . vakata . context . hide ( ) ;
}
parent . teardown . call ( this ) ;
} ;
/ * *
* prepare and show the context menu for a node
* @ name show _contextmenu ( obj [ , x , y ] )
* @ param { mixed } obj the node
* @ param { Number } x the x - coordinate relative to the document to show the menu at
* @ param { Number } y the y - coordinate relative to the document to show the menu at
* @ param { Object } e the event if available that triggered the contextmenu
* @ plugin contextmenu
* @ trigger show _contextmenu . jstree
* /
this . show _contextmenu = function ( obj , x , y , e ) {
obj = this . get _node ( obj ) ;
if ( ! obj || obj . id === '#' ) { return false ; }
var s = this . settings . contextmenu ,
d = this . get _node ( obj , true ) ,
a = d . children ( ".jstree-anchor" ) ,
o = false ,
i = false ;
if ( s . show _at _node || x === undefined || y === undefined ) {
o = a . offset ( ) ;
x = o . left ;
y = o . top + this . _data . core . li _height ;
}
if ( this . settings . contextmenu . select _node && ! this . is _selected ( obj ) ) {
this . activate _node ( obj , e ) ;
}
i = s . items ;
if ( $ . isFunction ( i ) ) {
i = i . call ( this , obj , $ . proxy ( function ( i ) {
this . _show _contextmenu ( obj , x , y , i ) ;
} , this ) ) ;
}
if ( $ . isPlainObject ( i ) ) {
this . _show _contextmenu ( obj , x , y , i ) ;
}
} ;
/ * *
* show the prepared context menu for a node
* @ name _show _contextmenu ( obj , x , y , i )
* @ param { mixed } obj the node
* @ param { Number } x the x - coordinate relative to the document to show the menu at
* @ param { Number } y the y - coordinate relative to the document to show the menu at
* @ param { Number } i the object of items to show
* @ plugin contextmenu
* @ trigger show _contextmenu . jstree
* @ private
* /
this . _show _contextmenu = function ( obj , x , y , i ) {
var d = this . get _node ( obj , true ) ,
a = d . children ( ".jstree-anchor" ) ;
$ ( document ) . one ( "context_show.vakata.jstree" , $ . proxy ( function ( e , data ) {
var cls = 'jstree-contextmenu jstree-' + this . get _theme ( ) + '-contextmenu' ;
$ ( data . element ) . addClass ( cls ) ;
} , this ) ) ;
this . _data . contextmenu . visible = true ;
$ . vakata . context . show ( a , { 'x' : x , 'y' : y } , i ) ;
/ * *
* triggered when the contextmenu is shown for a node
* @ event
* @ name show _contextmenu . jstree
* @ param { Object } node the node
* @ param { Number } x the x - coordinate of the menu relative to the document
* @ param { Number } y the y - coordinate of the menu relative to the document
* @ plugin contextmenu
* /
this . trigger ( 'show_contextmenu' , { "node" : obj , "x" : x , "y" : y } ) ;
} ;
} ;
// contextmenu helper
( function ( $ ) {
var right _to _left = false ,
vakata _context = {
element : false ,
reference : false ,
position _x : 0 ,
position _y : 0 ,
items : [ ] ,
html : "" ,
is _visible : false
} ;
$ . vakata . context = {
settings : {
hide _onmouseleave : 0 ,
icons : true
} ,
_trigger : function ( event _name ) {
$ ( document ) . triggerHandler ( "context_" + event _name + ".vakata" , {
"reference" : vakata _context . reference ,
"element" : vakata _context . element ,
"position" : {
"x" : vakata _context . position _x ,
"y" : vakata _context . position _y
}
} ) ;
} ,
_execute : function ( i ) {
i = vakata _context . items [ i ] ;
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 , {
"item" : i ,
"reference" : vakata _context . reference ,
"element" : vakata _context . element ,
"position" : {
"x" : vakata _context . position _x ,
"y" : vakata _context . position _y
}
} ) : false ;
} ,
_parse : function ( o , is _callback ) {
if ( ! o ) { return false ; }
if ( ! is _callback ) {
vakata _context . html = "" ;
vakata _context . items = [ ] ;
}
var str = "" ,
sep = false ,
tmp ;
if ( is _callback ) { str += "<" + "ul>" ; }
$ . each ( o , function ( i , val ) {
if ( ! val ) { return true ; }
vakata _context . items . push ( val ) ;
if ( ! sep && val . separator _before ) {
str += "<" + "li class='vakata-context-separator'><" + "a href='#' " + ( $ . vakata . context . settings . icons ? '' : 'style="margin-left:0px;"' ) + "> <" + "/a><" + "/li>" ;
}
sep = false ;
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 + "' " : '' ) + ">" ;
str += "<" + "a href='#' rel='" + ( vakata _context . items . length - 1 ) + "'>" ;
if ( $ . vakata . context . settings . icons ) {
str += "<" + "i " ;
if ( val . icon ) {
if ( val . icon . indexOf ( "/" ) !== - 1 || val . icon . indexOf ( "." ) !== - 1 ) { str += " style='background:url(\"" + val . icon + "\") center center no-repeat' " ; }
else { str += " class='" + val . icon + "' " ; }
}
str += "><" + "/i><" + "span class='vakata-contextmenu-sep'> <" + "/span>" ;
}
str += ( $ . isFunction ( val . label ) ? val . label ( { "item" : i , "reference" : vakata _context . reference , "element" : vakata _context . element } ) : val . label ) + ( val . shortcut ? ' <span class="vakata-contextmenu-shortcut vakata-contextmenu-shortcut-' + val . shortcut + '">' + ( val . shortcut _label || '' ) + '</span>' : '' ) + "<" + "/a>" ;
if ( val . submenu ) {
tmp = $ . vakata . context . _parse ( val . submenu , true ) ;
if ( tmp ) { str += tmp ; }
}
str += "<" + "/li>" ;
if ( val . separator _after ) {
str += "<" + "li class='vakata-context-separator'><" + "a href='#' " + ( $ . vakata . context . settings . icons ? '' : 'style="margin-left:0px;"' ) + "> <" + "/a><" + "/li>" ;
sep = true ;
}
} ) ;
str = str . replace ( /<li class\='vakata-context-separator'\><\/li\>$/ , "" ) ;
if ( is _callback ) { str += "</ul>" ; }
/ * *
* triggered on the document when the contextmenu is parsed ( HTML is built )
* @ event
* @ plugin contextmenu
* @ name context _parse . vakata
* @ param { jQuery } reference the element that was right clicked
* @ param { jQuery } element the DOM element of the menu itself
* @ param { Object } position the x & y coordinates of the menu
* /
if ( ! is _callback ) { vakata _context . html = str ; $ . vakata . context . _trigger ( "parse" ) ; }
return str . length > 10 ? str : false ;
} ,
_show _submenu : function ( o ) {
o = $ ( o ) ;
if ( ! o . length || ! o . children ( "ul" ) . length ) { return ; }
var e = o . children ( "ul" ) ,
x = o . offset ( ) . left + o . outerWidth ( ) ,
y = o . offset ( ) . top ,
w = e . width ( ) ,
h = e . height ( ) ,
dw = $ ( window ) . width ( ) + $ ( window ) . scrollLeft ( ) ,
dh = $ ( window ) . height ( ) + $ ( window ) . scrollTop ( ) ;
// може да с е спести е една проверка - дали няма някой от класовете вече нагоре
if ( right _to _left ) {
o [ x - ( w + 10 + o . outerWidth ( ) ) < 0 ? "addClass" : "removeClass" ] ( "vakata-context-left" ) ;
}
else {
o [ x + w + 10 > dw ? "addClass" : "removeClass" ] ( "vakata-context-right" ) ;
}
if ( y + h + 10 > dh ) {
e . css ( "bottom" , "-1px" ) ;
}
e . show ( ) ;
} ,
show : function ( reference , position , data ) {
var o , e , x , y , w , h , dw , dh , cond = true ;
if ( vakata _context . element && vakata _context . element . length ) {
vakata _context . element . width ( '' ) ;
}
switch ( cond ) {
case ( ! position && ! reference ) :
return false ;
case ( ! ! position && ! ! reference ) :
vakata _context . reference = reference ;
vakata _context . position _x = position . x ;
vakata _context . position _y = position . y ;
break ;
case ( ! position && ! ! reference ) :
vakata _context . reference = reference ;
o = reference . offset ( ) ;
vakata _context . position _x = o . left + reference . outerHeight ( ) ;
vakata _context . position _y = o . top ;
break ;
case ( ! ! position && ! reference ) :
vakata _context . position _x = position . x ;
vakata _context . position _y = position . y ;
break ;
}
if ( ! ! reference && ! data && $ ( reference ) . data ( 'vakata_contextmenu' ) ) {
data = $ ( reference ) . data ( 'vakata_contextmenu' ) ;
}
if ( $ . vakata . context . _parse ( data ) ) {
vakata _context . element . html ( vakata _context . html ) ;
}
if ( vakata _context . items . length ) {
vakata _context . element . appendTo ( "body" ) ;
e = vakata _context . element ;
x = vakata _context . position _x ;
y = vakata _context . position _y ;
w = e . width ( ) ;
h = e . height ( ) ;
dw = $ ( window ) . width ( ) + $ ( window ) . scrollLeft ( ) ;
dh = $ ( window ) . height ( ) + $ ( window ) . scrollTop ( ) ;
if ( right _to _left ) {
x -= ( e . outerWidth ( ) - $ ( reference ) . outerWidth ( ) ) ;
if ( x < $ ( window ) . scrollLeft ( ) + 20 ) {
x = $ ( window ) . scrollLeft ( ) + 20 ;
}
}
if ( x + w + 20 > dw ) {
x = dw - ( w + 20 ) ;
}
if ( y + h + 20 > dh ) {
y = dh - ( h + 20 ) ;
}
vakata _context . element
. css ( { "left" : x , "top" : y } )
. show ( )
. find ( 'a' ) . first ( ) . focus ( ) . parent ( ) . addClass ( "vakata-context-hover" ) ;
vakata _context . is _visible = true ;
/ * *
* triggered on the document when the contextmenu is shown
* @ event
* @ plugin contextmenu
* @ name context _show . vakata
* @ param { jQuery } reference the element that was right clicked
* @ param { jQuery } element the DOM element of the menu itself
* @ param { Object } position the x & y coordinates of the menu
* /
$ . vakata . context . _trigger ( "show" ) ;
}
} ,
hide : function ( ) {
if ( vakata _context . is _visible ) {
vakata _context . element . hide ( ) . find ( "ul" ) . hide ( ) . end ( ) . find ( ':focus' ) . blur ( ) . end ( ) . detach ( ) ;
vakata _context . is _visible = false ;
/ * *
* triggered on the document when the contextmenu is hidden
* @ event
* @ plugin contextmenu
* @ name context _hide . vakata
* @ param { jQuery } reference the element that was right clicked
* @ param { jQuery } element the DOM element of the menu itself
* @ param { Object } position the x & y coordinates of the menu
* /
$ . vakata . context . _trigger ( "hide" ) ;
}
}
} ;
$ ( function ( ) {
right _to _left = $ ( "body" ) . css ( "direction" ) === "rtl" ;
var to = false ;
vakata _context . element = $ ( "<ul class='vakata-context'></ul>" ) ;
vakata _context . element
. on ( "mouseenter" , "li" , function ( e ) {
e . stopImmediatePropagation ( ) ;
if ( $ . contains ( this , e . relatedTarget ) ) {
// премахнато заради delegate mouseleave по-долу
// $(this).find(".vakata-context-hover").removeClass("vakata-context-hover");
return ;
}
if ( to ) { clearTimeout ( to ) ; }
vakata _context . element . find ( ".vakata-context-hover" ) . removeClass ( "vakata-context-hover" ) . end ( ) ;
$ ( this )
. siblings ( ) . find ( "ul" ) . hide ( ) . end ( ) . end ( )
. parentsUntil ( ".vakata-context" , "li" ) . addBack ( ) . addClass ( "vakata-context-hover" ) ;
$ . vakata . context . _show _submenu ( this ) ;
} )
// тестово - дали не натоварва?
. on ( "mouseleave" , "li" , function ( e ) {
if ( $ . contains ( this , e . relatedTarget ) ) { return ; }
$ ( this ) . find ( ".vakata-context-hover" ) . addBack ( ) . removeClass ( "vakata-context-hover" ) ;
} )
. on ( "mouseleave" , function ( e ) {
$ ( this ) . find ( ".vakata-context-hover" ) . removeClass ( "vakata-context-hover" ) ;
if ( $ . vakata . context . settings . hide _onmouseleave ) {
to = setTimeout (
( function ( t ) {
return function ( ) { $ . vakata . context . hide ( ) ; } ;
} ( this ) ) , $ . vakata . context . settings . hide _onmouseleave ) ;
}
} )
. on ( "click" , "a" , function ( e ) {
e . preventDefault ( ) ;
//})
//.on("mouseup", "a", function (e) {
if ( ! $ ( this ) . blur ( ) . parent ( ) . hasClass ( "vakata-context-disabled" ) && $ . vakata . context . _execute ( $ ( this ) . attr ( "rel" ) ) !== false ) {
$ . vakata . context . hide ( ) ;
}
} )
. on ( 'keydown' , 'a' , function ( e ) {
var o = null ;
switch ( e . which ) {
case 13 :
case 32 :
e . type = "mouseup" ;
e . preventDefault ( ) ;
$ ( e . currentTarget ) . trigger ( e ) ;
break ;
case 37 :
if ( vakata _context . is _visible ) {
vakata _context . element . find ( ".vakata-context-hover" ) . last ( ) . closest ( "li" ) . first ( ) . find ( "ul" ) . hide ( ) . find ( ".vakata-context-hover" ) . removeClass ( "vakata-context-hover" ) . end ( ) . end ( ) . children ( 'a' ) . focus ( ) ;
e . stopImmediatePropagation ( ) ;
e . preventDefault ( ) ;
}
break ;
case 38 :
if ( vakata _context . is _visible ) {
o = vakata _context . element . find ( "ul:visible" ) . addBack ( ) . last ( ) . children ( ".vakata-context-hover" ) . removeClass ( "vakata-context-hover" ) . prevAll ( "li:not(.vakata-context-separator)" ) . first ( ) ;
if ( ! o . length ) { o = vakata _context . element . find ( "ul:visible" ) . addBack ( ) . last ( ) . children ( "li:not(.vakata-context-separator)" ) . last ( ) ; }
o . addClass ( "vakata-context-hover" ) . children ( 'a' ) . focus ( ) ;
e . stopImmediatePropagation ( ) ;
e . preventDefault ( ) ;
}
break ;
case 39 :
if ( vakata _context . is _visible ) {
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 ( ) ;
e . stopImmediatePropagation ( ) ;
e . preventDefault ( ) ;
}
break ;
case 40 :
if ( vakata _context . is _visible ) {
o = vakata _context . element . find ( "ul:visible" ) . addBack ( ) . last ( ) . children ( ".vakata-context-hover" ) . removeClass ( "vakata-context-hover" ) . nextAll ( "li:not(.vakata-context-separator)" ) . first ( ) ;
if ( ! o . length ) { o = vakata _context . element . find ( "ul:visible" ) . addBack ( ) . last ( ) . children ( "li:not(.vakata-context-separator)" ) . first ( ) ; }
o . addClass ( "vakata-context-hover" ) . children ( 'a' ) . focus ( ) ;
e . stopImmediatePropagation ( ) ;
e . preventDefault ( ) ;
}
break ;
case 27 :
$ . vakata . context . hide ( ) ;
e . preventDefault ( ) ;
break ;
default :
//console.log(e.which);
break ;
}
} )
. on ( 'keydown' , function ( e ) {
e . preventDefault ( ) ;
var a = vakata _context . element . find ( '.vakata-contextmenu-shortcut-' + e . which ) . parent ( ) ;
if ( a . parent ( ) . not ( '.vakata-context-disabled' ) ) {
a . click ( ) ;
}
} ) ;
$ ( document )
. on ( "mousedown.vakata.jstree" , function ( e ) {
if ( vakata _context . is _visible && ! $ . contains ( vakata _context . element [ 0 ] , e . target ) ) {
$ . vakata . context . hide ( ) ;
}
} )
. on ( "context_show.vakata.jstree" , function ( e , data ) {
vakata _context . element . find ( "li:has(ul)" ) . children ( "a" ) . addClass ( "vakata-context-parent" ) ;
if ( right _to _left ) {
vakata _context . element . addClass ( "vakata-context-rtl" ) . css ( "direction" , "rtl" ) ;
}
// also apply a RTL class?
vakata _context . element . find ( "ul" ) . hide ( ) . end ( ) ;
} ) ;
} ) ;
} ( $ ) ) ;
// $.jstree.defaults.plugins.push("contextmenu");
/ * *
* # # # Drag 'n' drop plugin
*
* Enables dragging and dropping of nodes in the tree , resulting in a move or copy operations .
* /
/ * *
* stores all defaults for the drag 'n' drop plugin
* @ name $ . jstree . defaults . dnd
* @ plugin dnd
* /
$ . jstree . defaults . dnd = {
/ * *
* a boolean indicating if a copy should be possible while dragging ( by pressint the meta key or Ctrl ) . Defaults to ` true ` .
* @ name $ . jstree . defaults . dnd . copy
* @ plugin dnd
* /
copy : true ,
/ * *
* a number indicating how long a node should remain hovered while dragging to be opened . Defaults to ` 500 ` .
* @ name $ . jstree . defaults . dnd . open _timeout
* @ plugin dnd
* /
open _timeout : 500 ,
/ * *
* a function invoked each time a node is about to be dragged , invoked in the tree ' s scope and receives the nodes about to be dragged as an argument ( array ) - return ` false ` to prevent dragging
* @ name $ . jstree . defaults . dnd . is _draggable
* @ plugin dnd
* /
is _draggable : true ,
/ * *
* 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 `
* @ name $ . jstree . defaults . dnd . check _while _dragging
* @ plugin dnd
* /
check _while _dragging : true ,
/ * *
* a boolean indicating if nodes from this tree should only be copied with dnd ( as opposed to moved ) , default is ` false `
* @ name $ . jstree . defaults . dnd . always _copy
* @ plugin dnd
* /
always _copy : false ,
/ * *
* when dropping a node "inside" , this setting indicates the position the node should go to - it can be an integer or a string : "first" ( same as 0 ) or "last" , default is ` 0 `
* @ name $ . jstree . defaults . dnd . inside _pos
* @ plugin dnd
* /
inside _pos : 0 ,
/ * *
* when starting the drag on a node that is selected this setting controls if all selected nodes are dragged or only the single node , default is ` true ` , which means all selected nodes are dragged when the drag is started on a selected node
* @ name $ . jstree . defaults . dnd . drag _selection
* @ plugin dnd
* /
drag _selection : true ,
/ * *
* controls whether dnd works on touch devices . If left as boolean true dnd will work the same as in desktop browsers , which in some cases may impair scrolling . If set to boolean false dnd will not work on touch devices . There is a special third option - string "selected" which means only selected nodes can be dragged on touch devices .
* @ name $ . jstree . defaults . dnd . touch
* @ plugin dnd
* /
touch : true ,
/ * *
* controls whether items can be dropped anywhere on the node , not just on the anchor , by default only the node anchor is a valid drop target . Works best with the wholerow plugin . If enabled on mobile depending on the interface it might be hard for the user to cancel the drop , since the whole tree container will be a valid drop target .
* @ name $ . jstree . defaults . dnd . large _drop _target
* @ plugin dnd
* /
large _drop _target : false ,
/ * *
* controls whether a drag can be initiated from any part of the node and not just the text / icon part , works best with the wholerow plugin . Keep in mind it can cause problems with tree scrolling on mobile depending on the interface - in that case set the touch option to "selected" .
* @ name $ . jstree . defaults . dnd . large _drag _target
* @ plugin dnd
* /
large _drag _target : false
} ;
// TODO: now check works by checking for each node individually, how about max_children, unique, etc?
$ . jstree . plugins . dnd = function ( options , parent ) {
this . bind = function ( ) {
parent . bind . call ( this ) ;
this . element
. on ( 'mousedown.jstree touchstart.jstree' , this . settings . dnd . large _drag _target ? '.jstree-node' : '.jstree-anchor' , $ . proxy ( function ( e ) {
if ( this . settings . dnd . large _drag _target && $ ( e . target ) . closest ( '.jstree-node' ) [ 0 ] !== e . currentTarget ) {
return true ;
}
if ( e . type === "touchstart" && ( ! this . settings . dnd . touch || ( this . settings . dnd . touch === 'selected' && ! $ ( e . currentTarget ) . closest ( '.jstree-node' ) . children ( '.jstree-anchor' ) . hasClass ( 'jstree-clicked' ) ) ) ) {
return true ;
}
var obj = this . get _node ( e . target ) ,
mlt = this . is _selected ( obj ) && this . settings . dnd . drag _selection ? this . get _top _selected ( ) . length : 1 ,
txt = ( mlt > 1 ? mlt + ' ' + this . get _string ( 'nodes' ) : this . get _text ( e . currentTarget ) ) ;
if ( this . settings . core . force _text ) {
txt = $ . vakata . html . escape ( txt ) ;
}
if ( obj && obj . id && obj . id !== "#" && ( e . which === 1 || e . type === "touchstart" ) &&
( this . settings . dnd . is _draggable === true || ( $ . isFunction ( this . settings . dnd . is _draggable ) && this . settings . dnd . is _draggable . call ( this , ( mlt > 1 ? this . get _top _selected ( true ) : [ obj ] ) ) ) )
) {
this . element . trigger ( 'mousedown.jstree' ) ;
return $ . vakata . dnd . start ( e , { 'jstree' : true , 'origin' : this , 'obj' : this . get _node ( obj , true ) , 'nodes' : mlt > 1 ? this . get _top _selected ( ) : [ obj . id ] } , '<div id="jstree-dnd" class="jstree-' + this . get _theme ( ) + ' jstree-' + this . get _theme ( ) + '-' + this . get _theme _variant ( ) + ' ' + ( this . settings . core . themes . responsive ? ' jstree-dnd-responsive' : '' ) + '"><i class="jstree-icon jstree-er"></i>' + txt + '<ins class="jstree-copy" style="display:none;">+</ins></div>' ) ;
}
} , this ) ) ;
} ;
} ;
$ ( function ( ) {
// bind only once for all instances
var lastmv = false ,
laster = false ,
opento = false ,
marker = $ ( '<div id="jstree-marker"> </div>' ) . hide ( ) ; //.appendTo('body');
$ ( document )
. on ( 'dnd_start.vakata.jstree' , function ( e , data ) {
lastmv = false ;
if ( ! data || ! data . data || ! data . data . jstree ) { return ; }
marker . appendTo ( 'body' ) ; //.show();
} )
. on ( 'dnd_move.vakata.jstree' , function ( e , data ) {
if ( opento ) { clearTimeout ( opento ) ; }
if ( ! data || ! data . data || ! data . data . jstree ) { return ; }
// if we are hovering the marker image do nothing (can happen on "inside" drags)
if ( data . event . target . id && data . event . target . id === 'jstree-marker' ) {
return ;
}
var ins = $ . jstree . reference ( data . event . target ) ,
ref = false ,
off = false ,
rel = false ,
tmp , l , t , h , p , i , o , ok , t1 , t2 , op , ps , pr , ip , tm ;
// if we are over an instance
if ( ins && ins . _data && ins . _data . dnd ) {
marker . attr ( 'class' , 'jstree-' + ins . get _theme ( ) + ( ins . settings . core . themes . responsive ? ' jstree-dnd-responsive' : '' ) ) ;
data . helper
. children ( ) . attr ( 'class' , 'jstree-' + ins . get _theme ( ) + ' jstree-' + ins . get _theme ( ) + '-' + ins . get _theme _variant ( ) + ' ' + ( ins . settings . core . themes . responsive ? ' jstree-dnd-responsive' : '' ) )
. find ( '.jstree-copy' ) . first ( ) [ data . data . origin && ( data . data . origin . settings . dnd . always _copy || ( data . data . origin . settings . dnd . copy && ( data . event . metaKey || data . event . ctrlKey ) ) ) ? 'show' : 'hide' ] ( ) ;
// if are hovering the container itself add a new root node
if ( ( data . event . target === ins . element [ 0 ] || data . event . target === ins . get _container _ul ( ) [ 0 ] ) && ins . get _container _ul ( ) . children ( ) . length === 0 ) {
ok = true ;
for ( t1 = 0 , t2 = data . data . nodes . length ; t1 < t2 ; t1 ++ ) {
ok = ok && ins . check ( ( data . data . origin && ( data . data . origin . settings . dnd . always _copy || ( data . data . origin . settings . dnd . copy && ( data . event . metaKey || data . event . ctrlKey ) ) ) ? "copy_node" : "move_node" ) , ( data . data . origin && data . data . origin !== ins ? data . data . origin . get _node ( data . data . nodes [ t1 ] ) : data . data . nodes [ t1 ] ) , '#' , 'last' , { 'dnd' : true , 'ref' : ins . get _node ( '#' ) , 'pos' : 'i' , 'origin' : data . data . origin , 'is_multi' : ( data . data . origin && data . data . origin !== ins ) , 'is_foreign' : ( ! data . data . origin ) } ) ;
if ( ! ok ) { break ; }
}
if ( ok ) {
lastmv = { 'ins' : ins , 'par' : '#' , 'pos' : 'last' } ;
marker . hide ( ) ;
data . helper . find ( '.jstree-icon' ) . first ( ) . removeClass ( 'jstree-er' ) . addClass ( 'jstree-ok' ) ;
return ;
}
}
else {
// if we are hovering a tree node
ref = ins . settings . dnd . large _drop _target ? $ ( data . event . target ) . closest ( '.jstree-node' ) . children ( '.jstree-anchor' ) : $ ( data . event . target ) . closest ( '.jstree-anchor' ) ;
if ( ref && ref . length && ref . parent ( ) . is ( '.jstree-closed, .jstree-open, .jstree-leaf' ) ) {
off = ref . offset ( ) ;
rel = data . event . pageY - off . top ;
h = ref . outerHeight ( ) ;
if ( rel < h / 3 ) {
o = [ 'b' , 'i' , 'a' ] ;
}
else if ( rel > h - h / 3 ) {
o = [ 'a' , 'i' , 'b' ] ;
}
else {
o = rel > h / 2 ? [ 'i' , 'a' , 'b' ] : [ 'i' , 'b' , 'a' ] ;
}
$ . each ( o , function ( j , v ) {
switch ( v ) {
case 'b' :
l = off . left - 6 ;
t = off . top ;
p = ins . get _parent ( ref ) ;
i = ref . parent ( ) . index ( ) ;
break ;
case 'i' :
ip = ins . settings . dnd . inside _pos ;
tm = ins . get _node ( ref . parent ( ) ) ;
l = off . left - 2 ;
t = off . top + h / 2 + 1 ;
p = tm . id ;
i = ip === 'first' ? 0 : ( ip === 'last' ? tm . children . length : Math . min ( ip , tm . children . length ) ) ;
break ;
case 'a' :
l = off . left - 6 ;
t = off . top + h ;
p = ins . get _parent ( ref ) ;
i = ref . parent ( ) . index ( ) + 1 ;
break ;
}
ok = true ;
for ( t1 = 0 , t2 = data . data . nodes . length ; t1 < t2 ; t1 ++ ) {
op = data . data . origin && ( data . data . origin . settings . dnd . always _copy || ( data . data . origin . settings . dnd . copy && ( data . event . metaKey || data . event . ctrlKey ) ) ) ? "copy_node" : "move_node" ;
ps = i ;
if ( op === "move_node" && v === 'a' && ( data . data . origin && data . data . origin === ins ) && p === ins . get _parent ( data . data . nodes [ t1 ] ) ) {
pr = ins . get _node ( p ) ;
if ( ps > $ . inArray ( data . data . nodes [ t1 ] , pr . children ) ) {
ps -= 1 ;
}
}
ok = ok && ( ( ins && ins . settings && ins . settings . dnd && ins . settings . dnd . check _while _dragging === false ) || ins . check ( op , ( data . data . origin && data . data . origin !== ins ? data . data . origin . get _node ( data . data . nodes [ t1 ] ) : data . data . nodes [ t1 ] ) , p , ps , { 'dnd' : true , 'ref' : ins . get _node ( ref . parent ( ) ) , 'pos' : v , 'origin' : data . data . origin , 'is_multi' : ( data . data . origin && data . data . origin !== ins ) , 'is_foreign' : ( ! data . data . origin ) } ) ) ;
if ( ! ok ) {
if ( ins && ins . last _error ) { laster = ins . last _error ( ) ; }
break ;
}
}
if ( v === 'i' && ref . parent ( ) . is ( '.jstree-closed' ) && ins . settings . dnd . open _timeout ) {
opento = setTimeout ( ( function ( x , z ) { return function ( ) { x . open _node ( z ) ; } ; } ( ins , ref ) ) , ins . settings . dnd . open _timeout ) ;
}
if ( ok ) {
lastmv = { 'ins' : ins , 'par' : p , 'pos' : v === 'i' && ip === 'last' && i === 0 && ! ins . is _loaded ( tm ) ? 'last' : i } ;
marker . css ( { 'left' : l + 'px' , 'top' : t + 'px' } ) . show ( ) ;
data . helper . find ( '.jstree-icon' ) . first ( ) . removeClass ( 'jstree-er' ) . addClass ( 'jstree-ok' ) ;
laster = { } ;
o = true ;
return false ;
}
} ) ;
if ( o === true ) { return ; }
}
}
}
lastmv = false ;
data . helper . find ( '.jstree-icon' ) . removeClass ( 'jstree-ok' ) . addClass ( 'jstree-er' ) ;
marker . hide ( ) ;
} )
. on ( 'dnd_scroll.vakata.jstree' , function ( e , data ) {
if ( ! data || ! data . data || ! data . data . jstree ) { return ; }
marker . hide ( ) ;
lastmv = false ;
data . helper . find ( '.jstree-icon' ) . first ( ) . removeClass ( 'jstree-ok' ) . addClass ( 'jstree-er' ) ;
} )
. on ( 'dnd_stop.vakata.jstree' , function ( e , data ) {
if ( opento ) { clearTimeout ( opento ) ; }
if ( ! data || ! data . data || ! data . data . jstree ) { return ; }
marker . hide ( ) . detach ( ) ;
var i , j , nodes = [ ] ;
if ( lastmv ) {
for ( i = 0 , j = data . data . nodes . length ; i < j ; i ++ ) {
nodes [ i ] = data . data . origin ? data . data . origin . get _node ( data . data . nodes [ i ] ) : data . data . nodes [ i ] ;
}
lastmv . ins [ data . data . origin && ( data . data . origin . settings . dnd . always _copy || ( data . data . origin . settings . dnd . copy && ( data . event . metaKey || data . event . ctrlKey ) ) ) ? 'copy_node' : 'move_node' ] ( nodes , lastmv . par , lastmv . pos , false , false , false , data . data . origin ) ;
}
else {
i = $ ( data . event . target ) . closest ( '.jstree' ) ;
if ( i . length && laster && laster . error && laster . error === 'check' ) {
i = i . jstree ( true ) ;
if ( i ) {
i . settings . core . error . call ( this , laster ) ;
}
}
}
} )
. on ( 'keyup.jstree keydown.jstree' , function ( e , data ) {
data = $ . vakata . dnd . _get ( ) ;
if ( data && data . data && data . data . jstree ) {
data . helper . find ( '.jstree-copy' ) . first ( ) [ data . data . origin && ( data . data . origin . settings . dnd . always _copy || ( data . data . origin . settings . dnd . copy && ( e . metaKey || e . ctrlKey ) ) ) ? 'show' : 'hide' ] ( ) ;
}
} ) ;
} ) ;
// helpers
( function ( $ ) {
$ . vakata . html = {
div : $ ( '<div />' ) ,
escape : function ( str ) {
return $ . vakata . html . div . text ( str ) . html ( ) ;
} ,
strip : function ( str ) {
return $ . vakata . html . div . empty ( ) . append ( $ . parseHTML ( str ) ) . text ( ) ;
}
} ;
// private variable
var vakata _dnd = {
element : false ,
target : false ,
is _down : false ,
is _drag : false ,
helper : false ,
helper _w : 0 ,
data : false ,
init _x : 0 ,
init _y : 0 ,
scroll _l : 0 ,
scroll _t : 0 ,
scroll _e : false ,
scroll _i : false ,
is _touch : false
} ;
$ . vakata . dnd = {
settings : {
scroll _speed : 10 ,
scroll _proximity : 20 ,
helper _left : 5 ,
helper _top : 10 ,
threshold : 5 ,
threshold _touch : 50
} ,
_trigger : function ( event _name , e ) {
var data = $ . vakata . dnd . _get ( ) ;
data . event = e ;
$ ( document ) . triggerHandler ( "dnd_" + event _name + ".vakata" , data ) ;
} ,
_get : function ( ) {
return {
"data" : vakata _dnd . data ,
"element" : vakata _dnd . element ,
"helper" : vakata _dnd . helper
} ;
} ,
_clean : function ( ) {
if ( vakata _dnd . helper ) { vakata _dnd . helper . remove ( ) ; }
if ( vakata _dnd . scroll _i ) { clearInterval ( vakata _dnd . scroll _i ) ; vakata _dnd . scroll _i = false ; }
vakata _dnd = {
element : false ,
target : false ,
is _down : false ,
is _drag : false ,
helper : false ,
helper _w : 0 ,
data : false ,
init _x : 0 ,
init _y : 0 ,
scroll _l : 0 ,
scroll _t : 0 ,
scroll _e : false ,
scroll _i : false ,
is _touch : false
} ;
$ ( document ) . off ( "mousemove.vakata.jstree touchmove.vakata.jstree" , $ . vakata . dnd . drag ) ;
$ ( document ) . off ( "mouseup.vakata.jstree touchend.vakata.jstree" , $ . vakata . dnd . stop ) ;
} ,
_scroll : function ( init _only ) {
if ( ! vakata _dnd . scroll _e || ( ! vakata _dnd . scroll _l && ! vakata _dnd . scroll _t ) ) {
if ( vakata _dnd . scroll _i ) { clearInterval ( vakata _dnd . scroll _i ) ; vakata _dnd . scroll _i = false ; }
return false ;
}
if ( ! vakata _dnd . scroll _i ) {
vakata _dnd . scroll _i = setInterval ( $ . vakata . dnd . _scroll , 100 ) ;
return false ;
}
if ( init _only === true ) { return false ; }
var i = vakata _dnd . scroll _e . scrollTop ( ) ,
j = vakata _dnd . scroll _e . scrollLeft ( ) ;
vakata _dnd . scroll _e . scrollTop ( i + vakata _dnd . scroll _t * $ . vakata . dnd . settings . scroll _speed ) ;
vakata _dnd . scroll _e . scrollLeft ( j + vakata _dnd . scroll _l * $ . vakata . dnd . settings . scroll _speed ) ;
if ( i !== vakata _dnd . scroll _e . scrollTop ( ) || j !== vakata _dnd . scroll _e . scrollLeft ( ) ) {
/ * *
* triggered on the document when a drag causes an element to scroll
* @ event
* @ plugin dnd
* @ name dnd _scroll . vakata
* @ param { Mixed } data any data supplied with the call to $ . vakata . dnd . start
* @ param { DOM } element the DOM element being dragged
* @ param { jQuery } helper the helper shown next to the mouse
* @ param { jQuery } event the element that is scrolling
* /
$ . vakata . dnd . _trigger ( "scroll" , vakata _dnd . scroll _e ) ;
}
} ,
start : function ( e , data , html ) {
if ( e . type === "touchstart" && e . originalEvent && e . originalEvent . changedTouches && e . originalEvent . changedTouches [ 0 ] ) {
e . pageX = e . originalEvent . changedTouches [ 0 ] . pageX ;
e . pageY = e . originalEvent . changedTouches [ 0 ] . pageY ;
e . target = document . elementFromPoint ( e . originalEvent . changedTouches [ 0 ] . pageX - window . pageXOffset , e . originalEvent . changedTouches [ 0 ] . pageY - window . pageYOffset ) ;
}
if ( vakata _dnd . is _drag ) { $ . vakata . dnd . stop ( { } ) ; }
try {
e . currentTarget . unselectable = "on" ;
e . currentTarget . onselectstart = function ( ) { return false ; } ;
if ( e . currentTarget . style ) { e . currentTarget . style . MozUserSelect = "none" ; }
} catch ( ignore ) { }
vakata _dnd . init _x = e . pageX ;
vakata _dnd . init _y = e . pageY ;
vakata _dnd . data = data ;
vakata _dnd . is _down = true ;
vakata _dnd . element = e . currentTarget ;
vakata _dnd . target = e . target ;
vakata _dnd . is _touch = e . type === "touchstart" ;
if ( html !== false ) {
vakata _dnd . helper = $ ( "<div id='vakata-dnd'></div>" ) . html ( html ) . css ( {
"display" : "block" ,
"margin" : "0" ,
"padding" : "0" ,
"position" : "absolute" ,
"top" : "-2000px" ,
"lineHeight" : "16px" ,
"zIndex" : "10000"
} ) ;
}
$ ( document ) . on ( "mousemove.vakata.jstree touchmove.vakata.jstree" , $ . vakata . dnd . drag ) ;
$ ( document ) . on ( "mouseup.vakata.jstree touchend.vakata.jstree" , $ . vakata . dnd . stop ) ;
return false ;
} ,
drag : function ( e ) {
if ( e . type === "touchmove" && e . originalEvent && e . originalEvent . changedTouches && e . originalEvent . changedTouches [ 0 ] ) {
e . pageX = e . originalEvent . changedTouches [ 0 ] . pageX ;
e . pageY = e . originalEvent . changedTouches [ 0 ] . pageY ;
e . target = document . elementFromPoint ( e . originalEvent . changedTouches [ 0 ] . pageX - window . pageXOffset , e . originalEvent . changedTouches [ 0 ] . pageY - window . pageYOffset ) ;
}
if ( ! vakata _dnd . is _down ) { return ; }
if ( ! vakata _dnd . is _drag ) {
if (
Math . abs ( e . pageX - vakata _dnd . init _x ) > ( vakata _dnd . is _touch ? $ . vakata . dnd . settings . threshold _touch : $ . vakata . dnd . settings . threshold ) ||
Math . abs ( e . pageY - vakata _dnd . init _y ) > ( vakata _dnd . is _touch ? $ . vakata . dnd . settings . threshold _touch : $ . vakata . dnd . settings . threshold )
) {
if ( vakata _dnd . helper ) {
vakata _dnd . helper . appendTo ( "body" ) ;
vakata _dnd . helper _w = vakata _dnd . helper . outerWidth ( ) ;
}
vakata _dnd . is _drag = true ;
/ * *
* triggered on the document when a drag starts
* @ event
* @ plugin dnd
* @ name dnd _start . vakata
* @ param { Mixed } data any data supplied with the call to $ . vakata . dnd . start
* @ param { DOM } element the DOM element being dragged
* @ param { jQuery } helper the helper shown next to the mouse
* @ param { Object } event the event that caused the start ( probably mousemove )
* /
$ . vakata . dnd . _trigger ( "start" , e ) ;
}
else { return ; }
}
var d = false , w = false ,
dh = false , wh = false ,
dw = false , ww = false ,
dt = false , dl = false ,
ht = false , hl = false ;
vakata _dnd . scroll _t = 0 ;
vakata _dnd . scroll _l = 0 ;
vakata _dnd . scroll _e = false ;
$ ( $ ( e . target ) . parentsUntil ( "body" ) . addBack ( ) . get ( ) . reverse ( ) )
. filter ( function ( ) {
return ( /^auto|scroll$/ ) . test ( $ ( this ) . css ( "overflow" ) ) &&
( this . scrollHeight > this . offsetHeight || this . scrollWidth > this . offsetWidth ) ;
} )
. each ( function ( ) {
var t = $ ( this ) , o = t . offset ( ) ;
if ( this . scrollHeight > this . offsetHeight ) {
if ( o . top + t . height ( ) - e . pageY < $ . vakata . dnd . settings . scroll _proximity ) { vakata _dnd . scroll _t = 1 ; }
if ( e . pageY - o . top < $ . vakata . dnd . settings . scroll _proximity ) { vakata _dnd . scroll _t = - 1 ; }
}
if ( this . scrollWidth > this . offsetWidth ) {
if ( o . left + t . width ( ) - e . pageX < $ . vakata . dnd . settings . scroll _proximity ) { vakata _dnd . scroll _l = 1 ; }
if ( e . pageX - o . left < $ . vakata . dnd . settings . scroll _proximity ) { vakata _dnd . scroll _l = - 1 ; }
}
if ( vakata _dnd . scroll _t || vakata _dnd . scroll _l ) {
vakata _dnd . scroll _e = $ ( this ) ;
return false ;
}
} ) ;
if ( ! vakata _dnd . scroll _e ) {
d = $ ( document ) ; w = $ ( window ) ;
dh = d . height ( ) ; wh = w . height ( ) ;
dw = d . width ( ) ; ww = w . width ( ) ;
dt = d . scrollTop ( ) ; dl = d . scrollLeft ( ) ;
if ( dh > wh && e . pageY - dt < $ . vakata . dnd . settings . scroll _proximity ) { vakata _dnd . scroll _t = - 1 ; }
if ( dh > wh && wh - ( e . pageY - dt ) < $ . vakata . dnd . settings . scroll _proximity ) { vakata _dnd . scroll _t = 1 ; }
if ( dw > ww && e . pageX - dl < $ . vakata . dnd . settings . scroll _proximity ) { vakata _dnd . scroll _l = - 1 ; }
if ( dw > ww && ww - ( e . pageX - dl ) < $ . vakata . dnd . settings . scroll _proximity ) { vakata _dnd . scroll _l = 1 ; }
if ( vakata _dnd . scroll _t || vakata _dnd . scroll _l ) {
vakata _dnd . scroll _e = d ;
}
}
if ( vakata _dnd . scroll _e ) { $ . vakata . dnd . _scroll ( true ) ; }
if ( vakata _dnd . helper ) {
ht = parseInt ( e . pageY + $ . vakata . dnd . settings . helper _top , 10 ) ;
hl = parseInt ( e . pageX + $ . vakata . dnd . settings . helper _left , 10 ) ;
if ( dh && ht + 25 > dh ) { ht = dh - 50 ; }
if ( dw && hl + vakata _dnd . helper _w > dw ) { hl = dw - ( vakata _dnd . helper _w + 2 ) ; }
vakata _dnd . helper . css ( {
left : hl + "px" ,
top : ht + "px"
} ) ;
}
/ * *
* triggered on the document when a drag is in progress
* @ event
* @ plugin dnd
* @ name dnd _move . vakata
* @ param { Mixed } data any data supplied with the call to $ . vakata . dnd . start
* @ param { DOM } element the DOM element being dragged
* @ param { jQuery } helper the helper shown next to the mouse
* @ param { Object } event the event that caused this to trigger ( most likely mousemove )
* /
$ . vakata . dnd . _trigger ( "move" , e ) ;
return false ;
} ,
stop : function ( e ) {
if ( e . type === "touchend" && e . originalEvent && e . originalEvent . changedTouches && e . originalEvent . changedTouches [ 0 ] ) {
e . pageX = e . originalEvent . changedTouches [ 0 ] . pageX ;
e . pageY = e . originalEvent . changedTouches [ 0 ] . pageY ;
e . target = document . elementFromPoint ( e . originalEvent . changedTouches [ 0 ] . pageX - window . pageXOffset , e . originalEvent . changedTouches [ 0 ] . pageY - window . pageYOffset ) ;
}
if ( vakata _dnd . is _drag ) {
/ * *
* triggered on the document when a drag stops ( the dragged element is dropped )
* @ event
* @ plugin dnd
* @ name dnd _stop . vakata
* @ param { Mixed } data any data supplied with the call to $ . vakata . dnd . start
* @ param { DOM } element the DOM element being dragged
* @ param { jQuery } helper the helper shown next to the mouse
* @ param { Object } event the event that caused the stop
* /
$ . vakata . dnd . _trigger ( "stop" , e ) ;
}
else {
if ( e . type === "touchend" && e . target === vakata _dnd . target ) {
var to = setTimeout ( function ( ) { $ ( e . target ) . click ( ) ; } , 100 ) ;
$ ( e . target ) . one ( 'click' , function ( ) { if ( to ) { clearTimeout ( to ) ; } } ) ;
}
}
$ . vakata . dnd . _clean ( ) ;
return false ;
}
} ;
} ( $ ) ) ;
// include the dnd plugin by default
// $.jstree.defaults.plugins.push("dnd");
/ * *
* # # # Massload plugin
*
* Adds massload functionality to jsTree , so that multiple nodes can be loaded in a single request ( only useful with lazy loading ) .
* /
/ * *
* massload configuration
*
* It is possible to set this to a standard jQuery - like AJAX config .
* In addition to the standard jQuery ajax options here you can supply functions for ` data ` and ` url ` , the functions will be run in the current instance ' s scope and a param will be passed indicating which node IDs need to be loaded , the return value of those functions will be used .
*
* You can also set this to a function , that function will receive the node IDs being loaded as argument and a second param which is a function ( callback ) which should be called with the result .
*
* Both the AJAX and the function approach rely on the same return value - an object where the keys are the node IDs , and the value is the children of that node as an array .
*
* {
* "id1" : [ { "text" : "Child of ID1" , "id" : "c1" } , { "text" : "Another child of ID1" , "id" : "c2" } ] ,
* "id2" : [ { "text" : "Child of ID2" , "id" : "c3" } ]
* }
*
* @ name $ . jstree . defaults . massload
* @ plugin massload
* /
$ . jstree . defaults . massload = null ;
$ . jstree . plugins . massload = function ( options , parent ) {
this . init = function ( el , options ) {
parent . init . call ( this , el , options ) ;
this . _data . massload = { } ;
} ;
this . _load _nodes = function ( nodes , callback , is _callback ) {
var s = this . settings . massload ;
if ( is _callback && ! $ . isEmptyObject ( this . _data . massload ) ) {
return parent . _load _nodes . call ( this , nodes , callback , is _callback ) ;
}
if ( $ . isFunction ( s ) ) {
return s . call ( this , nodes , $ . proxy ( function ( data ) {
if ( data ) {
for ( var i in data ) {
if ( data . hasOwnProperty ( i ) ) {
this . _data . massload [ i ] = data [ i ] ;
}
}
}
parent . _load _nodes . call ( this , nodes , callback , is _callback ) ;
} , this ) ) ;
}
if ( typeof s === 'object' && s && s . url ) {
s = $ . extend ( true , { } , s ) ;
if ( $ . isFunction ( s . url ) ) {
s . url = s . url . call ( this , nodes ) ;
}
if ( $ . isFunction ( s . data ) ) {
s . data = s . data . call ( this , nodes ) ;
}
return $ . ajax ( s )
. done ( $ . proxy ( function ( data , t , x ) {
if ( data ) {
for ( var i in data ) {
if ( data . hasOwnProperty ( i ) ) {
this . _data . massload [ i ] = data [ i ] ;
}
}
}
parent . _load _nodes . call ( this , nodes , callback , is _callback ) ;
} , this ) )
. fail ( $ . proxy ( function ( f ) {
parent . _load _nodes . call ( this , nodes , callback , is _callback ) ;
} , this ) ) ;
}
return parent . _load _nodes . call ( this , nodes , callback , is _callback ) ;
} ;
this . _load _node = function ( obj , callback ) {
var d = this . _data . massload [ obj . id ] ;
if ( d ) {
return this [ typeof d === 'string' ? '_append_html_data' : '_append_json_data' ] ( obj , typeof d === 'string' ? $ ( $ . parseHTML ( d ) ) . filter ( function ( ) { return this . nodeType !== 3 ; } ) : d , function ( status ) {
callback . call ( this , status ) ;
delete this . _data . massload [ obj . id ] ;
} ) ;
}
return parent . _load _node . call ( this , obj , callback ) ;
} ;
} ;
/ * *
* # # # Search plugin
*
* Adds search functionality to jsTree .
* /
/ * *
* stores all defaults for the search plugin
* @ name $ . jstree . defaults . search
* @ plugin search
* /
$ . jstree . defaults . search = {
/ * *
* a jQuery - like AJAX config , which jstree uses if a server should be queried for results .
*
* A ` str ` ( which is the search string ) parameter will be added with the request , an optional ` inside ` parameter will be added if the search is limited to a node id . The expected result is a JSON array with nodes that need to be opened so that matching nodes will be revealed .
* Leave this setting as ` false ` to not query the server . You can also set this to a function , which will be invoked in the instance ' s scope and receive 3 parameters - the search string , the callback to call with the array of nodes to load , and the optional node ID to limit the search to
* @ name $ . jstree . defaults . search . ajax
* @ plugin search
* /
ajax : false ,
/ * *
* Indicates if the search should be fuzzy or not ( should ` chnd3 ` match ` child node 3 ` ) . Default is ` false ` .
* @ name $ . jstree . defaults . search . fuzzy
* @ plugin search
* /
fuzzy : false ,
/ * *
* Indicates if the search should be case sensitive . Default is ` false ` .
* @ name $ . jstree . defaults . search . case _sensitive
* @ plugin search
* /
case _sensitive : false ,
/ * *
* Indicates if the tree should be filtered ( by default ) to show only matching nodes ( keep in mind this can be a heavy on large trees in old browsers ) .
* This setting can be changed at runtime when calling the search method . Default is ` false ` .
* @ name $ . jstree . defaults . search . show _only _matches
* @ plugin search
* /
show _only _matches : false ,
/ * *
* Indicates if the children of matched element are shown ( when show _only _matches is true )
* This setting can be changed at runtime when calling the search method . Default is ` false ` .
* @ name $ . jstree . defaults . search . show _only _matches _children
* @ plugin search
* /
show _only _matches _children : false ,
/ * *
* 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 ` .
* @ name $ . jstree . defaults . search . close _opened _onclear
* @ plugin search
* /
close _opened _onclear : true ,
/ * *
* Indicates if only leaf nodes should be included in search results . Default is ` false ` .
* @ name $ . jstree . defaults . search . search _leaves _only
* @ plugin search
* /
search _leaves _only : false ,
/ * *
* If set to a function it wil be called in the instance ' s scope with two arguments - search string and node ( where node will be every node in the structure , so use with caution ) .
* If the function returns a truthy value the node will be considered a match ( it might not be displayed if search _only _leaves is set to true and the node is not a leaf ) . Default is ` false ` .
* @ name $ . jstree . defaults . search . search _callback
* @ plugin search
* /
search _callback : false
} ;
$ . jstree . plugins . search = function ( options , parent ) {
this . bind = function ( ) {
parent . bind . call ( this ) ;
this . _data . search . str = "" ;
this . _data . search . dom = $ ( ) ;
this . _data . search . res = [ ] ;
this . _data . search . opn = [ ] ;
this . _data . search . som = false ;
this . _data . search . smc = false ;
this . element
. on ( 'before_open.jstree' , $ . proxy ( function ( e , data ) {
var i , j , f , r = this . _data . search . res , s = [ ] , o = $ ( ) ;
if ( r && r . length ) {
this . _data . search . dom = $ ( this . element [ 0 ] . querySelectorAll ( '#' + $ . map ( r , function ( v ) { return "0123456789" . indexOf ( v [ 0 ] ) !== - 1 ? '\\3' + v [ 0 ] + ' ' + v . substr ( 1 ) . replace ( $ . jstree . idregex , '\\$&' ) : v . replace ( $ . jstree . idregex , '\\$&' ) ; } ) . join ( ', #' ) ) ) ;
this . _data . search . dom . children ( ".jstree-anchor" ) . addClass ( 'jstree-search' ) ;
if ( this . _data . search . som && this . _data . search . res . length ) {
for ( i = 0 , j = r . length ; i < j ; i ++ ) {
s = s . concat ( this . get _node ( r [ i ] ) . parents ) ;
}
s = $ . vakata . array _remove _item ( $ . vakata . array _unique ( s ) , '#' ) ;
o = s . length ? $ ( this . element [ 0 ] . querySelectorAll ( '#' + $ . map ( s , function ( v ) { return "0123456789" . indexOf ( v [ 0 ] ) !== - 1 ? '\\3' + v [ 0 ] + ' ' + v . substr ( 1 ) . replace ( $ . jstree . idregex , '\\$&' ) : v . replace ( $ . jstree . idregex , '\\$&' ) ; } ) . join ( ', #' ) ) ) : $ ( ) ;
this . element . find ( ".jstree-node" ) . hide ( ) . filter ( '.jstree-last' ) . filter ( function ( ) { return this . nextSibling ; } ) . removeClass ( 'jstree-last' ) ;
o = o . add ( this . _data . search . dom ) ;
if ( this . _data . search . smc ) {
this . _data . search . dom . children ( ".jstree-children" ) . find ( ".jstree-node" ) . show ( ) ;
}
o . parentsUntil ( ".jstree" ) . addBack ( ) . show ( )
. filter ( ".jstree-children" ) . each ( function ( ) { $ ( this ) . children ( ".jstree-node:visible" ) . eq ( - 1 ) . addClass ( "jstree-last" ) ; } ) ;
}
}
} , this ) )
. on ( "search.jstree" , $ . proxy ( function ( e , data ) {
if ( this . _data . search . som ) {
if ( data . nodes . length ) {
this . element . find ( ".jstree-node" ) . hide ( ) . filter ( '.jstree-last' ) . filter ( function ( ) { return this . nextSibling ; } ) . removeClass ( 'jstree-last' ) ;
if ( this . _data . search . smc ) {
data . nodes . children ( ".jstree-children" ) . find ( ".jstree-node" ) . show ( ) ;
}
data . nodes . parentsUntil ( ".jstree" ) . addBack ( ) . show ( )
. filter ( ".jstree-children" ) . each ( function ( ) { $ ( this ) . children ( ".jstree-node:visible" ) . eq ( - 1 ) . addClass ( "jstree-last" ) ; } ) ;
}
}
} , this ) )
. on ( "clear_search.jstree" , $ . proxy ( function ( e , data ) {
if ( this . _data . search . som && data . nodes . length ) {
this . element . find ( ".jstree-node" ) . css ( "display" , "" ) . filter ( '.jstree-last' ) . filter ( function ( ) { return this . nextSibling ; } ) . removeClass ( 'jstree-last' ) ;
}
} , this ) ) ;
} ;
/ * *
* used to search the tree nodes for a given string
* @ name search ( str [ , skip _async ] )
* @ param { String } str the search string
* @ param { Boolean } skip _async if set to true server will not be queried even if configured
* @ param { Boolean } show _only _matches if set to true only matching nodes will be shown ( keep in mind this can be very slow on large trees or old browsers )
* @ param { mixed } inside an optional node to whose children to limit the search
* @ param { Boolean } append if set to true the results of this search are appended to the previous search
* @ plugin search
* @ trigger search . jstree
* /
this . search = function ( str , skip _async , show _only _matches , inside , append , show _only _matches _children ) {
if ( str === false || $ . trim ( str . toString ( ) ) === "" ) {
return this . clear _search ( ) ;
}
inside = this . get _node ( inside ) ;
inside = inside && inside . id ? inside . id : null ;
str = str . toString ( ) ;
var s = this . settings . search ,
a = s . ajax ? s . ajax : false ,
m = this . _model . data ,
f = null ,
r = [ ] ,
p = [ ] , i , j ;
if ( this . _data . search . res . length && ! append ) {
this . clear _search ( ) ;
}
if ( show _only _matches === undefined ) {
show _only _matches = s . show _only _matches ;
}
if ( show _only _matches _children === undefined ) {
show _only _matches _children = s . show _only _matches _children ;
}
if ( ! skip _async && a !== false ) {
if ( $ . isFunction ( a ) ) {
return a . call ( this , str , $ . proxy ( function ( d ) {
if ( d && d . d ) { d = d . d ; }
this . _load _nodes ( ! $ . isArray ( d ) ? [ ] : $ . vakata . array _unique ( d ) , function ( ) {
this . search ( str , true , show _only _matches , inside , append ) ;
} , true ) ;
} , this ) , inside ) ;
}
else {
a = $ . extend ( { } , a ) ;
if ( ! a . data ) { a . data = { } ; }
a . data . str = str ;
if ( inside ) {
a . data . inside = inside ;
}
return $ . ajax ( a )
. fail ( $ . proxy ( function ( ) {
this . _data . core . last _error = { 'error' : 'ajax' , 'plugin' : 'search' , 'id' : 'search_01' , 'reason' : 'Could not load search parents' , 'data' : JSON . stringify ( a ) } ;
this . settings . core . error . call ( this , this . _data . core . last _error ) ;
} , this ) )
. done ( $ . proxy ( function ( d ) {
if ( d && d . d ) { d = d . d ; }
this . _load _nodes ( ! $ . isArray ( d ) ? [ ] : $ . vakata . array _unique ( d ) , function ( ) {
this . search ( str , true , show _only _matches , inside , append ) ;
} , true ) ;
} , this ) ) ;
}
}
if ( ! append ) {
this . _data . search . str = str ;
this . _data . search . dom = $ ( ) ;
this . _data . search . res = [ ] ;
this . _data . search . opn = [ ] ;
this . _data . search . som = show _only _matches ;
this . _data . search . smc = show _only _matches _children ;
}
f = new $ . vakata . search ( str , true , { caseSensitive : s . case _sensitive , fuzzy : s . fuzzy } ) ;
$ . each ( m [ inside ? inside : '#' ] . children _d , function ( ii , i ) {
var v = m [ i ] ;
if ( v . text && ( ( s . search _callback && s . search _callback . call ( this , str , v ) ) || ( ! s . search _callback && f . search ( v . text ) . isMatch ) ) && ( ! s . search _leaves _only || ( v . state . loaded && v . children . length === 0 ) ) ) {
r . push ( i ) ;
p = p . concat ( v . parents ) ;
}
} ) ;
if ( r . length ) {
p = $ . vakata . array _unique ( p ) ;
this . _search _open ( p ) ;
if ( ! append ) {
this . _data . search . dom = $ ( this . element [ 0 ] . querySelectorAll ( '#' + $ . map ( r , function ( v ) { return "0123456789" . indexOf ( v [ 0 ] ) !== - 1 ? '\\3' + v [ 0 ] + ' ' + v . substr ( 1 ) . replace ( $ . jstree . idregex , '\\$&' ) : v . replace ( $ . jstree . idregex , '\\$&' ) ; } ) . join ( ', #' ) ) ) ;
this . _data . search . res = r ;
}
else {
this . _data . search . dom = this . _data . search . dom . add ( $ ( this . element [ 0 ] . querySelectorAll ( '#' + $ . map ( r , function ( v ) { return "0123456789" . indexOf ( v [ 0 ] ) !== - 1 ? '\\3' + v [ 0 ] + ' ' + v . substr ( 1 ) . replace ( $ . jstree . idregex , '\\$&' ) : v . replace ( $ . jstree . idregex , '\\$&' ) ; } ) . join ( ', #' ) ) ) ) ;
this . _data . search . res = $ . vakata . array _unique ( this . _data . search . res . concat ( r ) ) ;
}
this . _data . search . dom . children ( ".jstree-anchor" ) . addClass ( 'jstree-search' ) ;
}
/ * *
* triggered after search is complete
* @ event
* @ name search . jstree
* @ param { jQuery } nodes a jQuery collection of matching nodes
* @ param { String } str the search string
* @ param { Array } res a collection of objects represeing the matching nodes
* @ plugin search
* /
this . trigger ( 'search' , { nodes : this . _data . search . dom , str : str , res : this . _data . search . res , show _only _matches : show _only _matches } ) ;
} ;
/ * *
* used to clear the last search ( removes classes and shows all nodes if filtering is on )
* @ name clear _search ( )
* @ plugin search
* @ trigger clear _search . jstree
* /
this . clear _search = function ( ) {
this . _data . search . dom . children ( ".jstree-anchor" ) . removeClass ( "jstree-search" ) ;
if ( this . settings . search . close _opened _onclear ) {
this . close _node ( this . _data . search . opn , 0 ) ;
}
/ * *
* triggered after search is complete
* @ event
* @ name clear _search . jstree
* @ param { jQuery } nodes a jQuery collection of matching nodes ( the result from the last search )
* @ param { String } str the search string ( the last search string )
* @ param { Array } res a collection of objects represeing the matching nodes ( the result from the last search )
* @ plugin search
* /
this . trigger ( 'clear_search' , { 'nodes' : this . _data . search . dom , str : this . _data . search . str , res : this . _data . search . res } ) ;
this . _data . search . str = "" ;
this . _data . search . res = [ ] ;
this . _data . search . opn = [ ] ;
this . _data . search . dom = $ ( ) ;
} ;
/ * *
* opens nodes that need to be opened to reveal the search results . Used only internally .
* @ private
* @ name _search _open ( d )
* @ param { Array } d an array of node IDs
* @ plugin search
* /
this . _search _open = function ( d ) {
var t = this ;
$ . each ( d . concat ( [ ] ) , function ( i , v ) {
if ( v === "#" ) { return true ; }
try { v = $ ( '#' + v . replace ( $ . jstree . idregex , '\\$&' ) , t . element ) ; } catch ( ignore ) { }
if ( v && v . length ) {
if ( t . is _closed ( v ) ) {
t . _data . search . opn . push ( v [ 0 ] . id ) ;
t . open _node ( v , function ( ) { t . _search _open ( d ) ; } , 0 ) ;
}
}
} ) ;
} ;
} ;
// helpers
( function ( $ ) {
// from http://kiro.me/projects/fuse.html
$ . vakata . search = function ( pattern , txt , options ) {
options = options || { } ;
options = $ . extend ( { } , $ . vakata . search . defaults , options ) ;
if ( options . fuzzy !== false ) {
options . fuzzy = true ;
}
pattern = options . caseSensitive ? pattern : pattern . toLowerCase ( ) ;
var MATCH _LOCATION = options . location ,
MATCH _DISTANCE = options . distance ,
MATCH _THRESHOLD = options . threshold ,
patternLen = pattern . length ,
matchmask , pattern _alphabet , match _bitapScore , search ;
if ( patternLen > 32 ) {
options . fuzzy = false ;
}
if ( options . fuzzy ) {
matchmask = 1 << ( patternLen - 1 ) ;
pattern _alphabet = ( function ( ) {
var mask = { } ,
i = 0 ;
for ( i = 0 ; i < patternLen ; i ++ ) {
mask [ pattern . charAt ( i ) ] = 0 ;
}
for ( i = 0 ; i < patternLen ; i ++ ) {
mask [ pattern . charAt ( i ) ] |= 1 << ( patternLen - i - 1 ) ;
}
return mask ;
} ( ) ) ;
match _bitapScore = function ( e , x ) {
var accuracy = e / patternLen ,
proximity = Math . abs ( MATCH _LOCATION - x ) ;
if ( ! MATCH _DISTANCE ) {
return proximity ? 1.0 : accuracy ;
}
return accuracy + ( proximity / MATCH _DISTANCE ) ;
} ;
}
search = function ( text ) {
text = options . caseSensitive ? text : text . toLowerCase ( ) ;
if ( pattern === text || text . indexOf ( pattern ) !== - 1 ) {
return {
isMatch : true ,
score : 0
} ;
}
if ( ! options . fuzzy ) {
return {
isMatch : false ,
score : 1
} ;
}
var i , j ,
textLen = text . length ,
scoreThreshold = MATCH _THRESHOLD ,
bestLoc = text . indexOf ( pattern , MATCH _LOCATION ) ,
binMin , binMid ,
binMax = patternLen + textLen ,
lastRd , start , finish , rd , charMatch ,
score = 1 ,
locations = [ ] ;
if ( bestLoc !== - 1 ) {
scoreThreshold = Math . min ( match _bitapScore ( 0 , bestLoc ) , scoreThreshold ) ;
bestLoc = text . lastIndexOf ( pattern , MATCH _LOCATION + patternLen ) ;
if ( bestLoc !== - 1 ) {
scoreThreshold = Math . min ( match _bitapScore ( 0 , bestLoc ) , scoreThreshold ) ;
}
}
bestLoc = - 1 ;
for ( i = 0 ; i < patternLen ; i ++ ) {
binMin = 0 ;
binMid = binMax ;
while ( binMin < binMid ) {
if ( match _bitapScore ( i , MATCH _LOCATION + binMid ) <= scoreThreshold ) {
binMin = binMid ;
} else {
binMax = binMid ;
}
binMid = Math . floor ( ( binMax - binMin ) / 2 + binMin ) ;
}
binMax = binMid ;
start = Math . max ( 1 , MATCH _LOCATION - binMid + 1 ) ;
finish = Math . min ( MATCH _LOCATION + binMid , textLen ) + patternLen ;
rd = new Array ( finish + 2 ) ;
rd [ finish + 1 ] = ( 1 << i ) - 1 ;
for ( j = finish ; j >= start ; j -- ) {
charMatch = pattern _alphabet [ text . charAt ( j - 1 ) ] ;
if ( i === 0 ) {
rd [ j ] = ( ( rd [ j + 1 ] << 1 ) | 1 ) & charMatch ;
} else {
rd [ j ] = ( ( rd [ j + 1 ] << 1 ) | 1 ) & charMatch | ( ( ( lastRd [ j + 1 ] | lastRd [ j ] ) << 1 ) | 1 ) | lastRd [ j + 1 ] ;
}
if ( rd [ j ] & matchmask ) {
score = match _bitapScore ( i , j - 1 ) ;
if ( score <= scoreThreshold ) {
scoreThreshold = score ;
bestLoc = j - 1 ;
locations . push ( bestLoc ) ;
if ( bestLoc > MATCH _LOCATION ) {
start = Math . max ( 1 , 2 * MATCH _LOCATION - bestLoc ) ;
} else {
break ;
}
}
}
}
if ( match _bitapScore ( i + 1 , MATCH _LOCATION ) > scoreThreshold ) {
break ;
}
lastRd = rd ;
}
return {
isMatch : bestLoc >= 0 ,
score : score
} ;
} ;
return txt === true ? { 'search' : search } : search ( txt ) ;
} ;
$ . vakata . search . defaults = {
location : 0 ,
distance : 100 ,
threshold : 0.6 ,
fuzzy : false ,
caseSensitive : false
} ;
} ( $ ) ) ;
// include the search plugin by default
// $.jstree.defaults.plugins.push("search");
/ * *
* # # # Sort plugin
*
* Automatically sorts all siblings in the tree according to a sorting function .
* /
/ * *
* the settings function used to sort the nodes .
* It is executed in the tree ' s context , accepts two nodes as arguments and should return ` 1 ` or ` -1 ` .
* @ name $ . jstree . defaults . sort
* @ plugin sort
* /
$ . jstree . defaults . sort = function ( a , b ) {
//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);
return this . get _text ( a ) > this . get _text ( b ) ? 1 : - 1 ;
} ;
$ . jstree . plugins . sort = function ( options , parent ) {
this . bind = function ( ) {
parent . bind . call ( this ) ;
this . element
. on ( "model.jstree" , $ . proxy ( function ( e , data ) {
this . sort ( data . parent , true ) ;
} , this ) )
. on ( "rename_node.jstree create_node.jstree" , $ . proxy ( function ( e , data ) {
this . sort ( data . parent || data . node . parent , false ) ;
this . redraw _node ( data . parent || data . node . parent , true ) ;
} , this ) )
. on ( "move_node.jstree copy_node.jstree" , $ . proxy ( function ( e , data ) {
this . sort ( data . parent , false ) ;
this . redraw _node ( data . parent , true ) ;
} , this ) ) ;
} ;
/ * *
* used to sort a node ' s children
* @ private
* @ name sort ( obj [ , deep ] )
* @ param { mixed } obj the node
* @ param { Boolean } deep if set to ` true ` nodes are sorted recursively .
* @ plugin sort
* @ trigger search . jstree
* /
this . sort = function ( obj , deep ) {
var i , j ;
obj = this . get _node ( obj ) ;
if ( obj && obj . children && obj . children . length ) {
obj . children . sort ( $ . proxy ( this . settings . sort , this ) ) ;
if ( deep ) {
for ( i = 0 , j = obj . children _d . length ; i < j ; i ++ ) {
this . sort ( obj . children _d [ i ] , false ) ;
}
}
}
} ;
} ;
// include the sort plugin by default
// $.jstree.defaults.plugins.push("sort");
/ * *
* # # # State plugin
*
* Saves the state of the tree ( selected nodes , opened nodes ) on the user ' s computer using available options ( localStorage , cookies , etc )
* /
var to = false ;
/ * *
* stores all defaults for the state plugin
* @ name $ . jstree . defaults . state
* @ plugin state
* /
$ . jstree . defaults . state = {
/ * *
* A string for the key to use when saving the current tree ( change if using multiple trees in your project ) . Defaults to ` jstree ` .
* @ name $ . jstree . defaults . state . key
* @ plugin state
* /
key : 'jstree' ,
/ * *
* A space separated list of events that trigger a state save . Defaults to ` changed.jstree open_node.jstree close_node.jstree ` .
* @ name $ . jstree . defaults . state . events
* @ plugin state
* /
events : 'changed.jstree open_node.jstree close_node.jstree check_node.jstree uncheck_node.jstree' ,
/ * *
* Time in milliseconds after which the state will expire . Defaults to 'false' meaning - no expire .
* @ name $ . jstree . defaults . state . ttl
* @ plugin state
* /
ttl : false ,
/ * *
* 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 .
* @ name $ . jstree . defaults . state . filter
* @ plugin state
* /
filter : false
} ;
$ . jstree . plugins . state = function ( options , parent ) {
this . bind = function ( ) {
parent . bind . call ( this ) ;
var bind = $ . proxy ( function ( ) {
this . element . on ( this . settings . state . events , $ . proxy ( function ( ) {
if ( to ) { clearTimeout ( to ) ; }
to = setTimeout ( $ . proxy ( function ( ) { this . save _state ( ) ; } , this ) , 100 ) ;
} , this ) ) ;
/ * *
* triggered when the state plugin is finished restoring the state ( and immediately after ready if there is no state to restore ) .
* @ event
* @ name state _ready . jstree
* @ plugin state
* /
this . trigger ( 'state_ready' ) ;
} , this ) ;
this . element
. on ( "ready.jstree" , $ . proxy ( function ( e , data ) {
this . element . one ( "restore_state.jstree" , bind ) ;
if ( ! this . restore _state ( ) ) { bind ( ) ; }
} , this ) ) ;
} ;
/ * *
* save the state
* @ name save _state ( )
* @ plugin state
* /
this . save _state = function ( ) {
var st = { 'state' : this . get _state ( ) , 'ttl' : this . settings . state . ttl , 'sec' : + ( new Date ( ) ) } ;
$ . vakata . storage . set ( this . settings . state . key , JSON . stringify ( st ) ) ;
} ;
/ * *
* restore the state from the user ' s computer
* @ name restore _state ( )
* @ plugin state
* /
this . restore _state = function ( ) {
var k = $ . vakata . storage . get ( this . settings . state . key ) ;
if ( ! ! k ) { try { k = JSON . parse ( k ) ; } catch ( ex ) { return false ; } }
if ( ! ! k && k . ttl && k . sec && + ( new Date ( ) ) - k . sec > k . ttl ) { return false ; }
if ( ! ! k && k . state ) { k = k . state ; }
if ( ! ! k && $ . isFunction ( this . settings . state . filter ) ) { k = this . settings . state . filter . call ( this , k ) ; }
if ( ! ! k ) {
this . element . one ( "set_state.jstree" , function ( e , data ) { data . instance . trigger ( 'restore_state' , { 'state' : $ . extend ( true , { } , k ) } ) ; } ) ;
this . set _state ( k ) ;
return true ;
}
return false ;
} ;
/ * *
* clear the state on the user ' s computer
* @ name clear _state ( )
* @ plugin state
* /
this . clear _state = function ( ) {
return $ . vakata . storage . del ( this . settings . state . key ) ;
} ;
} ;
( function ( $ , undefined ) {
$ . vakata . storage = {
// simply specifying the functions in FF throws an error
set : function ( key , val ) { return window . localStorage . setItem ( key , val ) ; } ,
get : function ( key ) { return window . localStorage . getItem ( key ) ; } ,
del : function ( key ) { return window . localStorage . removeItem ( key ) ; }
} ;
} ( $ ) ) ;
// include the state plugin by default
// $.jstree.defaults.plugins.push("state");
/ * *
* # # # Types plugin
*
* 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 .
* /
/ * *
* 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 ) .
*
* * ` max_children ` the maximum number of immediate children this node type can have . Do not specify or set to ` -1 ` for unlimited .
* * ` 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 .
* * ` 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 .
* * ` 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 .
*
* There are two predefined types :
*
* * ` # ` represents the root of the tree , for example ` max_children ` would control the maximum number of root nodes .
* * ` default ` represents the default node - any settings here will be applied to all nodes that do not have a type specified .
*
* @ name $ . jstree . defaults . types
* @ plugin types
* /
$ . jstree . defaults . types = {
'#' : { } ,
'default' : { }
} ;
$ . jstree . plugins . types = function ( options , parent ) {
this . init = function ( el , options ) {
var i , j ;
if ( options && options . types && options . types [ 'default' ] ) {
for ( i in options . types ) {
if ( i !== "default" && i !== "#" && options . types . hasOwnProperty ( i ) ) {
for ( j in options . types [ 'default' ] ) {
if ( options . types [ 'default' ] . hasOwnProperty ( j ) && options . types [ i ] [ j ] === undefined ) {
options . types [ i ] [ j ] = options . types [ 'default' ] [ j ] ;
}
}
}
}
}
parent . init . call ( this , el , options ) ;
this . _model . data [ '#' ] . type = '#' ;
} ;
this . refresh = function ( skip _loading , forget _state ) {
parent . refresh . call ( this , skip _loading , forget _state ) ;
this . _model . data [ '#' ] . type = '#' ;
} ;
this . bind = function ( ) {
this . element
. on ( 'model.jstree' , $ . proxy ( function ( e , data ) {
var m = this . _model . data ,
dpc = data . nodes ,
t = this . settings . types ,
i , j , c = 'default' ;
for ( i = 0 , j = dpc . length ; i < j ; i ++ ) {
c = 'default' ;
if ( m [ dpc [ i ] ] . original && m [ dpc [ i ] ] . original . type && t [ m [ dpc [ i ] ] . original . type ] ) {
c = m [ dpc [ i ] ] . original . type ;
}
if ( m [ dpc [ i ] ] . data && m [ dpc [ i ] ] . data . jstree && m [ dpc [ i ] ] . data . jstree . type && t [ m [ dpc [ i ] ] . data . jstree . type ] ) {
c = m [ dpc [ i ] ] . data . jstree . type ;
}
m [ dpc [ i ] ] . type = c ;
if ( m [ dpc [ i ] ] . icon === true && t [ c ] . icon !== undefined ) {
m [ dpc [ i ] ] . icon = t [ c ] . icon ;
}
}
m [ '#' ] . type = '#' ;
} , this ) ) ;
parent . bind . call ( this ) ;
} ;
this . get _json = function ( obj , options , flat ) {
var i , j ,
m = this . _model . data ,
opt = options ? $ . extend ( true , { } , options , { no _id : false } ) : { } ,
tmp = parent . get _json . call ( this , obj , opt , flat ) ;
if ( tmp === false ) { return false ; }
if ( $ . isArray ( tmp ) ) {
for ( i = 0 , j = tmp . length ; i < j ; i ++ ) {
tmp [ i ] . type = tmp [ i ] . id && m [ tmp [ i ] . id ] && m [ tmp [ i ] . id ] . type ? m [ tmp [ i ] . id ] . type : "default" ;
if ( options && options . no _id ) {
delete tmp [ i ] . id ;
if ( tmp [ i ] . li _attr && tmp [ i ] . li _attr . id ) {
delete tmp [ i ] . li _attr . id ;
}
if ( tmp [ i ] . a _attr && tmp [ i ] . a _attr . id ) {
delete tmp [ i ] . a _attr . id ;
}
}
}
}
else {
tmp . type = tmp . id && m [ tmp . id ] && m [ tmp . id ] . type ? m [ tmp . id ] . type : "default" ;
if ( options && options . no _id ) {
tmp = this . _delete _ids ( tmp ) ;
}
}
return tmp ;
} ;
this . _delete _ids = function ( tmp ) {
if ( $ . isArray ( tmp ) ) {
for ( var i = 0 , j = tmp . length ; i < j ; i ++ ) {
tmp [ i ] = this . _delete _ids ( tmp [ i ] ) ;
}
return tmp ;
}
delete tmp . id ;
if ( tmp . li _attr && tmp . li _attr . id ) {
delete tmp . li _attr . id ;
}
if ( tmp . a _attr && tmp . a _attr . id ) {
delete tmp . a _attr . id ;
}
if ( tmp . children && $ . isArray ( tmp . children ) ) {
tmp . children = this . _delete _ids ( tmp . children ) ;
}
return tmp ;
} ;
this . check = function ( chk , obj , par , pos , more ) {
if ( parent . check . call ( this , chk , obj , par , pos , more ) === false ) { return false ; }
obj = obj && obj . id ? obj : this . get _node ( obj ) ;
par = par && par . id ? par : this . get _node ( par ) ;
var m = obj && obj . id ? ( more && more . origin ? more . origin : $ . jstree . reference ( obj . id ) ) : null , tmp , d , i , j ;
m = m && m . _model && m . _model . data ? m . _model . data : null ;
switch ( chk ) {
case "create_node" :
case "move_node" :
case "copy_node" :
if ( chk !== 'move_node' || $ . inArray ( obj . id , par . children ) === - 1 ) {
tmp = this . get _rules ( par ) ;
if ( tmp . max _children !== undefined && tmp . max _children !== - 1 && tmp . max _children === par . children . length ) {
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 } ) } ;
return false ;
}
if ( tmp . valid _children !== undefined && tmp . valid _children !== - 1 && $ . inArray ( ( obj . type || 'default' ) , tmp . valid _children ) === - 1 ) {
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 } ) } ;
return false ;
}
if ( m && obj . children _d && obj . parents ) {
d = 0 ;
for ( i = 0 , j = obj . children _d . length ; i < j ; i ++ ) {
d = Math . max ( d , m [ obj . children _d [ i ] ] . parents . length ) ;
}
d = d - obj . parents . length + 1 ;
}
if ( d <= 0 || d === undefined ) { d = 1 ; }
do {
if ( tmp . max _depth !== undefined && tmp . max _depth !== - 1 && tmp . max _depth < d ) {
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 } ) } ;
return false ;
}
par = this . get _node ( par . parent ) ;
tmp = this . get _rules ( par ) ;
d ++ ;
} while ( par ) ;
}
break ;
}
return true ;
} ;
/ * *
* used to retrieve the type settings object for a node
* @ name get _rules ( obj )
* @ param { mixed } obj the node to find the rules for
* @ return { Object }
* @ plugin types
* /
this . get _rules = function ( obj ) {
obj = this . get _node ( obj ) ;
if ( ! obj ) { return false ; }
var tmp = this . get _type ( obj , true ) ;
if ( tmp . max _depth === undefined ) { tmp . max _depth = - 1 ; }
if ( tmp . max _children === undefined ) { tmp . max _children = - 1 ; }
if ( tmp . valid _children === undefined ) { tmp . valid _children = - 1 ; }
return tmp ;
} ;
/ * *
* used to retrieve the type string or settings object for a node
* @ name get _type ( obj [ , rules ] )
* @ param { mixed } obj the node to find the rules for
* @ param { Boolean } rules if set to ` true ` instead of a string the settings object will be returned
* @ return { String | Object }
* @ plugin types
* /
this . get _type = function ( obj , rules ) {
obj = this . get _node ( obj ) ;
return ( ! obj ) ? false : ( rules ? $ . extend ( { 'type' : obj . type } , this . settings . types [ obj . type ] ) : obj . type ) ;
} ;
/ * *
* used to change a node ' s type
* @ name set _type ( obj , type )
* @ param { mixed } obj the node to change
* @ param { String } type the new type
* @ plugin types
* /
this . set _type = function ( obj , type ) {
var t , t1 , t2 , old _type , old _icon ;
if ( $ . isArray ( obj ) ) {
obj = obj . slice ( ) ;
for ( t1 = 0 , t2 = obj . length ; t1 < t2 ; t1 ++ ) {
this . set _type ( obj [ t1 ] , type ) ;
}
return true ;
}
t = this . settings . types ;
obj = this . get _node ( obj ) ;
if ( ! t [ type ] || ! obj ) { return false ; }
old _type = obj . type ;
old _icon = this . get _icon ( obj ) ;
obj . type = type ;
if ( old _icon === true || ( t [ old _type ] && t [ old _type ] . icon !== undefined && old _icon === t [ old _type ] . icon ) ) {
this . set _icon ( obj , t [ type ] . icon !== undefined ? t [ type ] . icon : true ) ;
}
return true ;
} ;
} ;
// include the types plugin by default
// $.jstree.defaults.plugins.push("types");
/ * *
* # # # Unique plugin
*
* Enforces that no nodes with the same name can coexist as siblings .
* /
/ * *
* stores all defaults for the unique plugin
* @ name $ . jstree . defaults . unique
* @ plugin unique
* /
$ . jstree . defaults . unique = {
/ * *
* Indicates if the comparison should be case sensitive . Default is ` false ` .
* @ name $ . jstree . defaults . unique . case _sensitive
* @ plugin unique
* /
case _sensitive : false ,
/ * *
* A callback executed in the instance ' s scope when a new node is created and the name is already taken , the two arguments are the conflicting name and the counter . The default will produce results like ` New node (2) ` .
* @ name $ . jstree . defaults . unique . duplicate
* @ plugin unique
* /
duplicate : function ( name , counter ) {
return name + ' (' + counter + ')' ;
}
} ;
$ . jstree . plugins . unique = function ( options , parent ) {
this . check = function ( chk , obj , par , pos , more ) {
if ( parent . check . call ( this , chk , obj , par , pos , more ) === false ) { return false ; }
obj = obj && obj . id ? obj : this . get _node ( obj ) ;
par = par && par . id ? par : this . get _node ( par ) ;
if ( ! par || ! par . children ) { return true ; }
var n = chk === "rename_node" ? pos : obj . text ,
c = [ ] ,
s = this . settings . unique . case _sensitive ,
m = this . _model . data , i , j ;
for ( i = 0 , j = par . children . length ; i < j ; i ++ ) {
c . push ( s ? m [ par . children [ i ] ] . text : m [ par . children [ i ] ] . text . toLowerCase ( ) ) ;
}
if ( ! s ) { n = n . toLowerCase ( ) ; }
switch ( chk ) {
case "delete_node" :
return true ;
case "rename_node" :
i = ( $ . inArray ( n , c ) === - 1 || ( obj . text && obj . text [ s ? 'toString' : 'toLowerCase' ] ( ) === n ) ) ;
if ( ! i ) {
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 } ) } ;
}
return i ;
case "create_node" :
i = ( $ . inArray ( n , c ) === - 1 ) ;
if ( ! i ) {
this . _data . core . last _error = { 'error' : 'check' , 'plugin' : 'unique' , 'id' : 'unique_04' , 'reason' : 'Child with name ' + n + ' already exists. Preventing: ' + chk , 'data' : JSON . stringify ( { 'chk' : chk , 'pos' : pos , 'obj' : obj && obj . id ? obj . id : false , 'par' : par && par . id ? par . id : false } ) } ;
}
return i ;
case "copy_node" :
i = ( $ . inArray ( n , c ) === - 1 ) ;
if ( ! i ) {
this . _data . core . last _error = { 'error' : 'check' , 'plugin' : 'unique' , 'id' : 'unique_02' , 'reason' : 'Child with name ' + n + ' already exists. Preventing: ' + chk , 'data' : JSON . stringify ( { 'chk' : chk , 'pos' : pos , 'obj' : obj && obj . id ? obj . id : false , 'par' : par && par . id ? par . id : false } ) } ;
}
return i ;
case "move_node" :
i = ( ( obj . parent === par . id && ( ! more || ! more . is _multi ) ) || $ . inArray ( n , c ) === - 1 ) ;
if ( ! i ) {
this . _data . core . last _error = { 'error' : 'check' , 'plugin' : 'unique' , 'id' : 'unique_03' , 'reason' : 'Child with name ' + n + ' already exists. Preventing: ' + chk , 'data' : JSON . stringify ( { 'chk' : chk , 'pos' : pos , 'obj' : obj && obj . id ? obj . id : false , 'par' : par && par . id ? par . id : false } ) } ;
}
return i ;
}
return true ;
} ;
this . create _node = function ( par , node , pos , callback , is _loaded ) {
if ( ! node || node . text === undefined ) {
if ( par === null ) {
par = "#" ;
}
par = this . get _node ( par ) ;
if ( ! par ) {
return parent . create _node . call ( this , par , node , pos , callback , is _loaded ) ;
}
pos = pos === undefined ? "last" : pos ;
if ( ! pos . toString ( ) . match ( /^(before|after)$/ ) && ! is _loaded && ! this . is _loaded ( par ) ) {
return parent . create _node . call ( this , par , node , pos , callback , is _loaded ) ;
}
if ( ! node ) { node = { } ; }
var tmp , n , dpc , i , j , m = this . _model . data , s = this . settings . unique . case _sensitive , cb = this . settings . unique . duplicate ;
n = tmp = this . get _string ( 'New node' ) ;
dpc = [ ] ;
for ( i = 0 , j = par . children . length ; i < j ; i ++ ) {
dpc . push ( s ? m [ par . children [ i ] ] . text : m [ par . children [ i ] ] . text . toLowerCase ( ) ) ;
}
i = 1 ;
while ( $ . inArray ( s ? n : n . toLowerCase ( ) , dpc ) !== - 1 ) {
n = cb . call ( this , tmp , ( ++ i ) ) . toString ( ) ;
}
node . text = n ;
}
return parent . create _node . call ( this , par , node , pos , callback , is _loaded ) ;
} ;
} ;
// include the unique plugin by default
// $.jstree.defaults.plugins.push("unique");
/ * *
* # # # Wholerow plugin
*
* Makes each node appear block level . Making selection easier . May cause slow down for large trees in old browsers .
* /
var div = document . createElement ( 'DIV' ) ;
div . setAttribute ( 'unselectable' , 'on' ) ;
div . setAttribute ( 'role' , 'presentation' ) ;
div . className = 'jstree-wholerow' ;
div . innerHTML = ' ' ;
$ . jstree . plugins . wholerow = function ( options , parent ) {
this . bind = function ( ) {
parent . bind . call ( this ) ;
this . element
. on ( 'ready.jstree set_state.jstree' , $ . proxy ( function ( ) {
this . hide _dots ( ) ;
} , this ) )
. on ( "init.jstree loading.jstree ready.jstree" , $ . proxy ( function ( ) {
//div.style.height = this._data.core.li_height + 'px';
this . get _container _ul ( ) . addClass ( 'jstree-wholerow-ul' ) ;
} , this ) )
. on ( "deselect_all.jstree" , $ . proxy ( function ( e , data ) {
this . element . find ( '.jstree-wholerow-clicked' ) . removeClass ( 'jstree-wholerow-clicked' ) ;
} , this ) )
. on ( "changed.jstree" , $ . proxy ( function ( e , data ) {
this . element . find ( '.jstree-wholerow-clicked' ) . removeClass ( 'jstree-wholerow-clicked' ) ;
var tmp = false , i , j ;
for ( i = 0 , j = data . selected . length ; i < j ; i ++ ) {
tmp = this . get _node ( data . selected [ i ] , true ) ;
if ( tmp && tmp . length ) {
tmp . children ( '.jstree-wholerow' ) . addClass ( 'jstree-wholerow-clicked' ) ;
}
}
} , this ) )
. on ( "open_node.jstree" , $ . proxy ( function ( e , data ) {
this . get _node ( data . node , true ) . find ( '.jstree-clicked' ) . parent ( ) . children ( '.jstree-wholerow' ) . addClass ( 'jstree-wholerow-clicked' ) ;
} , this ) )
. on ( "hover_node.jstree dehover_node.jstree" , $ . proxy ( function ( e , data ) {
if ( e . type === "hover_node" && this . is _disabled ( data . node ) ) { return ; }
this . get _node ( data . node , true ) . children ( '.jstree-wholerow' ) [ e . type === "hover_node" ? "addClass" : "removeClass" ] ( 'jstree-wholerow-hovered' ) ;
} , this ) )
. on ( "contextmenu.jstree" , ".jstree-wholerow" , $ . proxy ( function ( e ) {
e . preventDefault ( ) ;
var tmp = $ . Event ( 'contextmenu' , { metaKey : e . metaKey , ctrlKey : e . ctrlKey , altKey : e . altKey , shiftKey : e . shiftKey , pageX : e . pageX , pageY : e . pageY } ) ;
$ ( e . currentTarget ) . closest ( ".jstree-node" ) . children ( ".jstree-anchor" ) . first ( ) . trigger ( tmp ) ;
} , this ) )
/ * !
. on ( "mousedown.jstree touchstart.jstree" , ".jstree-wholerow" , function ( e ) {
if ( e . target === e . currentTarget ) {
var a = $ ( e . currentTarget ) . closest ( ".jstree-node" ) . children ( ".jstree-anchor" ) ;
e . target = a [ 0 ] ;
a . trigger ( e ) ;
}
} )
* /
. on ( "click.jstree" , ".jstree-wholerow" , function ( e ) {
e . stopImmediatePropagation ( ) ;
var tmp = $ . Event ( 'click' , { metaKey : e . metaKey , ctrlKey : e . ctrlKey , altKey : e . altKey , shiftKey : e . shiftKey } ) ;
$ ( e . currentTarget ) . closest ( ".jstree-node" ) . children ( ".jstree-anchor" ) . first ( ) . trigger ( tmp ) . focus ( ) ;
} )
. on ( "click.jstree" , ".jstree-leaf > .jstree-ocl" , $ . proxy ( function ( e ) {
e . stopImmediatePropagation ( ) ;
var tmp = $ . Event ( 'click' , { metaKey : e . metaKey , ctrlKey : e . ctrlKey , altKey : e . altKey , shiftKey : e . shiftKey } ) ;
$ ( e . currentTarget ) . closest ( ".jstree-node" ) . children ( ".jstree-anchor" ) . first ( ) . trigger ( tmp ) . focus ( ) ;
} , this ) )
. on ( "mouseover.jstree" , ".jstree-wholerow, .jstree-icon" , $ . proxy ( function ( e ) {
e . stopImmediatePropagation ( ) ;
if ( ! this . is _disabled ( e . currentTarget ) ) {
this . hover _node ( e . currentTarget ) ;
}
return false ;
} , this ) )
. on ( "mouseleave.jstree" , ".jstree-node" , $ . proxy ( function ( e ) {
this . dehover _node ( e . currentTarget ) ;
} , this ) ) ;
} ;
this . teardown = function ( ) {
if ( this . settings . wholerow ) {
this . element . find ( ".jstree-wholerow" ) . remove ( ) ;
}
parent . teardown . call ( this ) ;
} ;
this . redraw _node = function ( obj , deep , callback , force _render ) {
obj = parent . redraw _node . apply ( this , arguments ) ;
if ( obj ) {
var tmp = div . cloneNode ( true ) ;
//tmp.style.height = this._data.core.li_height + 'px';
if ( $ . inArray ( obj . id , this . _data . core . selected ) !== - 1 ) { tmp . className += ' jstree-wholerow-clicked' ; }
if ( this . _data . core . focused && this . _data . core . focused === obj . id ) { tmp . className += ' jstree-wholerow-hovered' ; }
obj . insertBefore ( tmp , obj . childNodes [ 0 ] ) ;
}
return obj ;
} ;
} ;
// include the wholerow plugin by default
// $.jstree.defaults.plugins.push("wholerow");
if ( document . registerElement && Object && Object . create ) {
var proto = Object . create ( HTMLElement . prototype ) ;
proto . createdCallback = function ( ) {
var c = { core : { } , plugins : [ ] } , i ;
for ( i in $ . jstree . plugins ) {
if ( $ . jstree . plugins . hasOwnProperty ( i ) && this . attributes [ i ] ) {
c . plugins . push ( i ) ;
if ( this . getAttribute ( i ) && JSON . parse ( this . getAttribute ( i ) ) ) {
c [ i ] = JSON . parse ( this . getAttribute ( i ) ) ;
}
}
}
for ( i in $ . jstree . defaults . core ) {
if ( $ . jstree . defaults . core . hasOwnProperty ( i ) && this . attributes [ i ] ) {
c . core [ i ] = JSON . parse ( this . getAttribute ( i ) ) || this . getAttribute ( i ) ;
}
}
$ ( this ) . jstree ( c ) ;
} ;
// proto.attributeChangedCallback = function (name, previous, value) { };
try {
document . registerElement ( "vakata-jstree" , { prototype : proto } ) ;
} catch ( ignore ) { }
}
} ) ) ;