@@ -10,24 +10,6 @@ define([
1010] , function ( $ ) {
1111 'use strict' ;
1212
13- /**
14- * Recursively adds the 'lastNode' property to the nodes in the nested object.
15- *
16- * @param {Array } nodes
17- * @returns {Array }
18- */
19- function addLastNodeProperty ( nodes ) {
20- return nodes . map ( node => {
21- return node . children ? {
22- ...node ,
23- children : addLastNodeProperty ( node . children )
24- } : {
25- ...node ,
26- lastNode : true
27- } ;
28- } ) ;
29- }
30-
3113 /**
3214 * Main function that creates the jstree
3315 *
@@ -48,142 +30,93 @@ define([
4830 rootId : config . rootId ,
4931 expanded : config . expanded ,
5032 categoryId : config . categoryId ,
51- treeJson : addLastNodeProperty ( config . treeJson )
33+ treeJson : config . treeJson
5234 } ,
53- checkedNodes = [ ] ;
35+ initialSelection = [ ] ;
5436
5537 /**
5638 * Get the jstree element by its ID
5739 */
5840 const treeId = $ ( '#' + options . divId ) ;
5941
6042 /**
61- * Function to check child nodes based on the checkedNodes array
62- *
63- * @param {Object } node
43+ * @return {Element }
6444 */
65- function getCheckedNodeIds ( node ) {
66- if ( node . children_d && node . children_d . length > 0 ) {
67- const selectChildrenNodes = node . children_d . filter ( item => checkedNodes . includes ( item ) ) ;
68-
69- if ( selectChildrenNodes . length > 0 ) {
70- treeId . jstree ( false ) . select_node ( selectChildrenNodes ) ;
71- }
72- }
45+ function getTargetInput ( ) {
46+ return options . jsFormObject . updateElement ;
7347 }
7448
7549 /**
76- * Initialize the jstree with configuration options
77- */
78- treeId . jstree ( {
79- core : {
80- data : options . treeJson ,
81- check_callback : true
82- } ,
83- plugins : [ 'checkbox' ] ,
84- checkbox : {
85- three_state : false
86- }
87- } ) ;
88-
89- /**
90- * Event handler for 'loaded.jstree' event
50+ * Recursively marks nodes which children are not loaded.
51+ *
52+ * @param {Array } nodes
53+ * @returns {Array }
9154 */
92- treeId . on ( 'loaded.jstree' , function ( ) {
55+ function prepareNodes ( nodes ) {
56+ return nodes . map (
57+ function ( node ) {
58+ let obj = { ...node , state : { } } ;
59+
60+ if ( Array . isArray ( obj . children ) ) {
61+ if ( obj . children . length > 0 ) {
62+ obj . children = prepareNodes ( obj . children ) ;
63+ } else {
64+ obj . children = true ;
65+ }
66+ }
9367
94- /**
95- * Get each node in the tree
96- */
97- $ ( treeId . jstree ( ) . get_json ( '#' , {
98- flat : false
99- } ) ) . each ( function ( ) {
100- let node = treeId . jstree ( ) . get_node ( this . id , false ) ;
68+ if ( obj . expanded ) {
69+ obj . state . opened = true ;
70+ }
10171
102- if ( node . original . expanded ) {
103- treeId . jstree ( true ) . open_node ( node ) ;
104- }
72+ if ( initialSelection . includes ( obj . id ) ) {
73+ obj . state . selected = true ;
74+ }
10575
106- if ( options . jsFormObject . updateElement . defaultValue ) {
107- checkedNodes = options . jsFormObject . updateElement . defaultValue . split ( ',' ) ;
76+ return obj ;
10877 }
109- } ) ;
110- } ) ;
111-
112- /**
113- * Event handler for 'load_node.jstree' event
114- */
115- treeId . on ( 'load_node.jstree' , function ( e , data ) {
116- getCheckedNodeIds ( data . node ) ;
117- } ) ;
118-
119- /**
120- * Add lastNode property to child who doesn't have children property
121- *
122- * @param {Object } treeData
123- */
124- function addLastNodeFlag ( treeData ) {
125- if ( treeData . children ) {
126- treeData . children . forEach ( ( child ) => addLastNodeFlag ( child ) ) ;
127- } else {
128- treeData . lastNode = true ;
129- }
78+ ) ;
13079 }
13180
13281 /**
133- * Function to handle the 'success' callback of the AJAX request
82+ * Load the node and execute the callback function
13483 *
135- * @param {Array } response
136- * @param {Object } childNode
137- * @param {Object } data
84+ * @param {Object } node
85+ * @param {Function } callback
13886 */
139- function handleSuccessResponse ( response , childNode , data ) {
140- if ( response . length > 0 ) {
141- response . forEach ( function ( newNode ) {
142- addLastNodeFlag ( newNode ) ;
143-
144- /**
145- * Create the new node and execute node callback
146- */
147- data . instance . create_node ( childNode , newNode , 'last' , function ( node ) {
148- if ( checkedNodes . includes ( node . id ) ) {
149- treeId . jstree ( false ) . select_node ( node . id ) ;
150- }
151- getCheckedNodeIds ( node ) ;
152- } ) ;
87+ function load ( node , callback ) {
88+ let target = getTargetInput ( ) ,
89+ instance = this ;
90+
91+ if ( node . id === $ . jstree . root ) {
92+ callback . call ( instance , prepareNodes ( options . treeJson ) ) ;
93+ } else if ( Array . isArray ( node . children ) && node . children . length === 0 ) {
94+ $ . ajax ( {
95+ url : options . dataUrl ,
96+ data : {
97+ id : node . id ,
98+ selected : target . value
99+ } ,
100+ dataType : 'json' ,
101+ success : function ( response ) {
102+ callback . call ( instance , prepareNodes ( response ) ) ;
103+ } ,
104+ error : function ( jqXHR , status , error ) {
105+ console . log ( status + ': ' + error + '\nResponse text:\n' + jqXHR . responseText ) ;
106+ }
153107 } ) ;
108+ } else {
109+ callback . call ( instance , false ) ;
154110 }
155111 }
156112
157113 /**
158- * Event handler for 'open_node .jstree' event
114+ * Event handler for 'init .jstree' event
159115 */
160- treeId . on ( 'open_node.jstree' , function ( e , data ) {
161- let parentNode = data . node ;
162-
163- if ( parentNode . children . length > 0 ) {
164- let childNode = data . instance . get_node ( parentNode . children , false ) ;
165-
166- /**
167- * Check if the child node has no children (is not yet loaded)
168- */
169- if ( childNode . children && childNode . children . length === 0
170- && childNode . original && ! childNode . original . lastNode ) {
171- $ . ajax ( {
172- url : options . dataUrl ,
173- data : {
174- id : childNode . id ,
175- selected : options . jsFormObject . updateElement . value
176- } ,
177- dataType : 'json' ,
178- success : function ( response ) {
179- handleSuccessResponse ( response , childNode , data ) ;
180- } ,
181- error : function ( jqXHR , status , error ) {
182- console . log ( status + ': ' + error + '\nResponse text:\n' + jqXHR . responseText ) ;
183- }
184- } ) ;
185- }
186- }
116+ treeId . on ( 'init.jstree' , function ( ) {
117+ let target = getTargetInput ( ) ;
118+
119+ initialSelection = target . value ? target . value . split ( ',' ) . map ( id => id . trim ( ) ) : [ ] ;
187120 } ) ;
188121
189122 /**
@@ -193,19 +126,34 @@ define([
193126 if ( data . action === 'ready' ) {
194127 return ;
195128 }
196- const clickedNodeID = data . node . id , currentCheckedNodes = data . instance . get_checked ( ) ;
197-
198- if ( data . action === 'select_node' && ! checkedNodes . includes ( clickedNodeID ) ) {
199- checkedNodes = currentCheckedNodes ;
200- } else if ( data . action === 'deselect_node' ) {
201- checkedNodes = currentCheckedNodes . filter ( ( nodeID ) => nodeID !== clickedNodeID ) ;
202- }
203- checkedNodes . sort ( ( a , b ) => a - b ) ;
204129
205130 /**
206131 * Update the value of the corresponding form element with the checked node IDs
132+ *
133+ * keep the checked nodes that are not in the tree yet,
134+ * and merge them with the currently checked nodes
135+ * then sort the resulted array
207136 */
208- options . jsFormObject . updateElement . value = checkedNodes . join ( ', ' ) ;
137+ let target = getTargetInput ( ) ,
138+ selected = initialSelection
139+ . filter ( node => data . instance . get_node ( node ) === false )
140+ . concat ( data . instance . get_checked ( ) ) ;
141+
142+ target . value = [ ...new Set ( selected ) ] . sort ( ( a , b ) => a - b ) . join ( ',' ) ;
143+ } ) ;
144+
145+ /**
146+ * Initialize the jstree with configuration options
147+ */
148+ treeId . jstree ( {
149+ core : {
150+ data : load ,
151+ check_callback : true
152+ } ,
153+ plugins : [ 'checkbox' ] ,
154+ checkbox : {
155+ three_state : false
156+ }
209157 } ) ;
210158 } ;
211159} ) ;
0 commit comments