@@ -14,6 +14,7 @@ const Taxonomy = (function() {
1414 '#a6761d' ,
1515 '#7f7f7f'
1616 ] ;
17+ let div = { } ;
1718 let tagTypes = { } ;
1819
1920 function updateColor ( word , color ) {
@@ -25,14 +26,29 @@ const Taxonomy = (function() {
2526 }
2627 } ;
2728
28- let div = { } ;
29+ function updateTagColor ( tag , color ) {
30+ tagTypes [ tag ] . forEach ( word => updateColor ( word , color ) ) ;
31+ } ;
2932
3033 class Taxonomy {
3134 constructor ( id ) {
3235 this . tree = { } ;
3336 div = document . getElementById ( 'taxonomy' ) ;
3437 }
3538
39+ draw ( taxonomy , words ) {
40+ if ( taxonomy ) {
41+ this . buildTree ( taxonomy ) ;
42+
43+ if ( words ) {
44+ this . buildTagTypes ( words ) ;
45+ }
46+
47+ this . populateTaxonomy ( ) ;
48+ this . attachHandlers ( ) ;
49+ }
50+ }
51+
3652 buildTagTypes ( words ) {
3753 tagTypes = { } ;
3854 words . forEach ( word => {
@@ -95,113 +111,175 @@ const Taxonomy = (function() {
95111 } ;
96112 }
97113
114+ // populate modal window with list of taxonomic classes
98115 populateTaxonomy ( ) {
99- let keys = Object . keys ( tagTypes ) ;
100-
101- // populate taxonomy
102116 div . innerHTML = '<span id="toggle-taxonomy">Filter unused labels</span>' ;
103117
118+ // build list of inputs in DOM
104119 let ul = document . createElement ( 'ul' ) ;
105120 div . appendChild ( ul ) ;
106121
107- function createLi ( el , ul ) {
122+ let nli = 1 ; // number of list items
123+ function createLi ( node , parent ) {
108124 let li = document . createElement ( 'li' ) ;
109- el . el = li ;
110125
111126 // create checkbox
112127 let cbox = document . createElement ( 'input' ) ;
113128 cbox . setAttribute ( 'type' , 'checkbox' ) ;
114- li . appendChild ( cbox ) ;
129+ cbox . id = 'cb-' + nli ;
115130
116- // text span
117- li . appendChild ( document . createTextNode ( el . val ) ) ;
131+ // create label for checkbox
132+ let label = document . createElement ( 'label' ) ;
133+ label . setAttribute ( 'for' , cbox . id ) ;
134+ label . textContent = node . val ;
135+ node . cbox = cbox ;
118136
119137 // create color picker input
120138 let picker = document . createElement ( 'input' ) ;
121- picker . className = 'jscolor' ;
139+ picker . className = 'colorpicker' ;
140+ picker . value = '#000000' ;
141+ picker . setAttribute ( 'disabled' , true ) ;
142+ picker . node = node ;
143+ node . picker = picker ;
122144
123- // set initial value
124- let i = keys . indexOf ( el . val ) ;
125- if ( i > - 1 ) {
126- cbox . checked = true ;
127- picker . value = colors [ i ] || '#000000' ;
128-
129- // propagate color to colorless ancestors
130- let parent = el . parent ;
131- while ( parent && parent . el && ! parent . el . querySelector ( 'input.jscolor' ) . value ) {
132- parent . el . querySelector ( 'input.jscolor' ) . value = picker . value ;
133- parent = parent . parent ;
134- }
135- }
136- picker . setAttribute ( 'disabled' , ! cbox . checked ) ;
145+ li . appendChild ( cbox ) ;
146+ li . appendChild ( label ) ;
137147 li . appendChild ( picker ) ;
148+ parent . appendChild ( li ) ;
149+ ++ nli ;
138150
139- // recursively update picker colors
140- function updateChildColors ( ) {
141- let color = this . value ;
142- console . log ( 'color' , color ) ;
143- function recurse ( el ) {
144- if ( tagTypes [ el . val ] ) {
145- tagTypes [ el . val ] . forEach ( word => updateColor ( word , color ) ) ;
146- }
147- if ( el . children ) {
148- el . children . forEach ( recurse ) ;
149- }
150- }
151- recurse ( el ) ;
151+ if ( node . children ) {
152+ let childUl = document . createElement ( 'ul' ) ;
153+ li . appendChild ( childUl ) ;
154+
155+ node . children . forEach ( child => {
156+ createLi ( child , childUl ) ;
157+ } )
152158 }
159+ }
153160
154- // attach listeners
155- cbox . onclick = function ( ) {
156- // TODO: update children colors on click
157- if ( this . checked ) {
158- // enable current picker and disable children inputs
159- picker . removeAttribute ( 'disabled' ) ;
160- li . querySelectorAll ( 'input' ) . forEach ( input => {
161- if ( input . parentNode !== li ) {
162- input . setAttribute ( 'disabled' , true ) ;
163- }
164- } ) ;
165- updateChildColors . bind ( picker ) ;
166- }
167- else {
168- // enable children inputs
169- picker . setAttribute ( 'disabled' , true ) ;
170- let checkboxes = li . querySelectorAll ( 'input[type="checkbox"]' ) ;
171- let pickers = li . querySelectorAll ( 'input.jscolor' ) ;
172- checkboxes . forEach ( ( cbox , i ) => {
173- cbox . removeAttribute ( 'disabled' ) ;
174- pickers [ i ] . setAttribute ( 'disabled' , ! cbox . checked ) ;
175- } ) ;
176- updateChildColors ( ) ;
161+ this . tree . hierarchy . forEach ( node => {
162+ createLi ( node , ul ) ;
163+ } ) ;
164+ }
165+
166+ // bind events to data
167+ attachHandlers ( ) {
168+ // initialize colorpicker
169+ let self = this ;
170+ jsColorPicker ( 'input.colorpicker' , {
171+ customBG : '#000' ,
172+ init : function ( elm , colors ) {
173+ elm . style . backgroundColor = elm . value || '#000' ;
174+ elm . style . color = colors . rgbaMixCustom . luminance > 0.22 ? '#222' : '#ddd' ;
175+ } ,
176+ renderCallback : function ( colors , mode ) {
177+ /* ---- code taken from jsColor.js ---- */
178+ var options = this ,
179+ input = options . input ,
180+ patch = options . patch ,
181+ RGB = colors . RND . rgb ,
182+ HSL = colors . RND . hsl ,
183+ AHEX = options . isIE8 ? ( colors . alpha < 0.16 ? '0' : '' ) +
184+ ( Math . round ( colors . alpha * 100 ) ) . toString ( 16 ) . toUpperCase ( ) + colors . HEX : '' ,
185+ RGBInnerText = RGB . r + ', ' + RGB . g + ', ' + RGB . b ,
186+ RGBAText = 'rgba(' + RGBInnerText + ', ' + colors . alpha + ')' ,
187+ isAlpha = colors . alpha !== 1 && ! options . isIE8 ,
188+ colorMode = input . getAttribute ( 'data-colorMode' ) ;
189+
190+ patch . style . cssText =
191+ 'color:' + ( colors . rgbaMixCustom . luminance > 0.22 ? '#222' : '#ddd' ) + ';' + // Black...???
192+ 'background-color:' + RGBAText + ';' +
193+ 'filter:' + ( options . isIE8 ? 'progid:DXImageTransform.Microsoft.gradient(' + // IE<9
194+ 'startColorstr=#' + AHEX + ',' + 'endColorstr=#' + AHEX + ')' : '' ) ;
195+
196+ input . value = ( colorMode === 'HEX' && ! isAlpha ? '#' + ( options . isIE8 ? AHEX : colors . HEX ) :
197+ colorMode === 'rgb' || ( colorMode === 'HEX' && isAlpha ) ?
198+ ( ! isAlpha ? 'rgb(' + RGBInnerText + ')' : RGBAText ) :
199+ ( 'hsl' + ( isAlpha ? 'a(' : '(' ) + HSL . h + ', ' + HSL . s + '%, ' + HSL . l + '%' +
200+ ( isAlpha ? ', ' + colors . alpha : '' ) + ')' )
201+ ) ;
202+
203+ if ( options . displayCallback ) {
204+ options . displayCallback ( colors , mode , options ) ;
177205 }
206+
207+ /* -- manually invoke callback -- */
208+ self . setColor ( this . input . node ) ;
178209 }
179- picker . onchange = updateChildColors ;
210+ } ) ;
180211
181- if ( el . children ) {
182- let childUl = document . createElement ( 'ul' ) ;
183- li . appendChild ( childUl ) ;
184- el . children . forEach ( child => createLi ( child , childUl ) ) ;
212+ const keys = Object . keys ( tagTypes ) ;
213+ this . tree . flat . forEach ( node => {
214+ // disable/enable color picking on a node
215+ node . cbox . onclick = ( ) => this . onCheckboxClick ( node ) ;
216+
217+ // check if tag type exists in document
218+ if ( tagTypes [ node . val ] ) {
219+ this . setColor ( node , colors [ keys . indexOf ( node . val ) ] ) ;
220+ node . cbox . click ( ) ;
221+ }
222+ } ) ;
223+
224+ // update colors of existing data
225+ Object . keys ( tagTypes ) . forEach ( ( tag , i ) => updateTagColor ( tag , colors [ i ] ) ) ;
226+ }
227+
228+ /* handle event when checkbox state changes */
229+ onCheckboxClick ( node ) {
230+ if ( node . cbox . checked ) {
231+ // activate node
232+ // propagate color to all descendants
233+ node . picker . removeAttribute ( 'disabled' ) ;
234+ this . setColor ( node ) ;
235+ } else {
236+ // deactivate node
237+ // undo color propagation to all descendants
238+ node . picker . setAttribute ( 'disabled' , true ) ;
239+ if ( node . parent ) {
240+ this . setColor ( node , node . parent . picker . value ) ;
241+ } else {
242+ this . setColor ( node , '#000000' ) ;
185243 }
186- ul . appendChild ( li ) ;
187244 }
188- this . tree . hierarchy . forEach ( el => createLi ( el , ul ) ) ;
189- jscolor . installByClassName ( 'jscolor' ) ;
245+ }
246+
247+ /* change the color of a node */
248+ setColor ( node , color ) {
249+ let picker = node . picker ;
190250
191- document . getElementById ( 'toggle-taxonomy' ) . onclick = function ( ) {
192- if ( ul . className === 'filtered' ) {
193- ul . className = '' ;
194- this . innerHTML = 'Show all labels' ;
251+ // manually set color
252+ if ( color ) {
253+ const c = new Colors ( { color : color } ) ;
254+ const labelColor = c . colors . RGBLuminance > 0.22 ? '#222' : '#ddd' ;
255+ picker . value = color ;
256+ picker . style . cssText = `color:${ labelColor } ; background-color:${ color } ;` ;
257+ }
258+
259+ if ( tagTypes [ node . val ] ) {
260+ updateTagColor ( node . val , picker . value ) ;
261+ }
262+
263+ // set color of input and descendant inputs
264+ function inheritColor ( child ) {
265+ if ( ! child . cbox . checked ) {
266+ // set color and style
267+ child . picker . value = picker . value ;
268+ child . picker . style . cssText = picker . style . cssText || child . picker . style . cssText ;
269+ if ( tagTypes [ child . val ] ) {
270+ updateTagColor ( child . val , picker . value ) ;
195271 }
196- else {
197- ul . className = 'filtered' ;
198- this . innerHTML = 'Filter unused labels' ;
272+
273+ // recursively propagate value to all children
274+ if ( child . children ) {
275+ child . children . forEach ( inheritColor ) ;
199276 }
277+ }
200278 }
201279
202- keys . forEach ( ( tag , i ) => {
203- tagTypes [ tag ] . forEach ( word => updateColor ( word , colors [ i ] ) ) ;
204- } ) ;
280+ if ( node . children ) {
281+ node . children . forEach ( inheritColor ) ;
282+ }
205283 }
206284
207285 remove ( object ) {
0 commit comments