33// Based on a CSS-Tricks Post
44
55var codeInput = {
6+ observedAttributes : [
7+ "value" ,
8+ "placeholder" ,
9+ "lang" ,
10+ "template" ,
11+ "onchange" ,
12+ "onselectionchange"
13+ ] ,
14+ // Attributes to monitor - needs to be global and static
15+
16+ /* Templates */
617 usedTemplates : {
718 } ,
819 defaultTemplate : undefined ,
920 templateQueue : { } , // lists of elements for each unrecognised template
21+
22+ /* Plugins */
1023 plugins : { // Import a plugin from the plugins folder and it will be saved here.
1124 } ,
1225 Plugin : class {
26+ constructor ( ) {
27+ console . log ( "code-input: plugin: Created plugin!" ) ;
28+
29+ // Add attributes
30+ codeInput . observedAttributes = codeInput . observedAttributes . concat ( self . observedAttributes ) ;
31+ }
32+
1333 /* Runs before code is highlighted; Params: codeInput element) */
1434 beforeHighlight ( codeInput ) { }
1535 /* Runs after code is highlighted; Params: codeInput element) */
@@ -22,11 +42,14 @@ var codeInput = {
2242 attributeChanged ( codeInput , name , oldValue , newValue ) { }
2343 observedAttributes = [ ]
2444 } ,
45+
46+ /* Main */
2547 CodeInput : class extends HTMLElement { // Create code input element
2648 constructor ( ) {
2749 super ( ) ; // Element
2850 }
2951
52+ last_events = { } ; // Last events applied; removed when changed so can be added to textarea, etc.
3053
3154 /* Run this event in all plugins with a optional list of arguments */
3255 plugin_evt ( id , args ) {
@@ -146,6 +169,12 @@ var codeInput = {
146169
147170 this . plugin_evt ( "afterElementsAdded" ) ;
148171
172+ // Events
173+ textarea = this . querySelector ( "textarea" ) ;
174+ // Add event listeners, bound so `this` can be referenced
175+ this . transfer_event ( "change" , this . querySelector ( "textarea" ) , null , this . onchange ) ;
176+ this . transfer_event ( "selectionchange" , this . querySelector ( "textarea" ) , null , this . onselectionchange ) ;
177+
149178 /* Add code from value attribute - useful for loading from backend */
150179 this . update ( value , this ) ;
151180 }
@@ -156,20 +185,16 @@ var codeInput = {
156185 this . template = this . get_template ( ) ;
157186 if ( this . template != undefined ) this . setup ( ) ;
158187 }
159- get observedAttributes ( ) {
160- let attrs = [ "value" , "placeholder" , "lang" , "template" ] ; // Attributes to monitor
161-
162- /* Add from plugins */
163- for ( let plugin in this . template . plugins ) {
164- attrs = attrs . concat ( plugin . observedAttributes ) ;
165- }
166- return attrs ;
188+ static get observedAttributes ( ) {
189+ return codeInput . observedAttributes ;
167190 }
168191
169192 attributeChangedCallback ( name , oldValue , newValue ) {
170193 if ( this . isConnected ) {
171194 // This will sometimes be called before the element has been created, so trying to update an attribute causes an error.
172195 // Thanks to Kevin Loughead for pointing this out.
196+
197+ this . plugin_evt ( "attributeChanged" , [ name , oldValue , newValue ] ) ; // Plugin event
173198 switch ( name ) {
174199
175200 case "value" :
@@ -188,38 +213,60 @@ var codeInput = {
188213 else this . classList . remove ( "code-input_pre-element-styled" ) ;
189214 // Syntax Highlight
190215 this . update ( this . value ) ;
216+
217+ break ;
191218
192219 case "lang" :
193220 let code = this . querySelector ( "pre code" ) ;
194- let textarea = this . querySelector ( "textarea" ) ;
221+ let main_textarea = this . querySelector ( "textarea" ) ;
195222
196223 // Case insensitive
197224 oldValue = oldValue . toLowerCase ( ) ;
198225 newValue = newValue . toLowerCase ( ) ;
199226
200227 // Remove old language class and add new
201- console . log ( "REMOVE" , "language-" + oldValue ) ;
228+ console . log ( "code-input: Language: REMOVE" , "language-" + oldValue ) ;
202229 code . classList . remove ( "language-" + oldValue ) ; // From CODE
203230 code . parentElement . classList . remove ( "language-" + oldValue ) ; // From PRE
204231 code . classList . remove ( "language-none" ) ; // Prism
205232 code . parentElement . classList . remove ( "language-none" ) ; // Prism
206233
207234 if ( newValue != undefined && newValue != "" ) {
208235 code . classList . add ( "language-" + newValue ) ;
209- console . log ( "ADD" , "language-" + newValue ) ;
236+ console . log ( "code-input: Language: ADD" , "language-" + newValue ) ;
210237 }
211238
212- if ( textarea . placeholder == oldValue ) textarea . placeholder = newValue ;
239+ if ( main_textarea . placeholder == oldValue ) main_textarea . placeholder = newValue ;
213240
214241 this . update ( this . value ) ;
215-
216- default :
217- this . plugin_evt ( "attributeChanged" , [ name , oldValue , newValue ] ) ; // Plugin event
242+
243+ break ;
244+
245+ // Events
246+ case "onchange" :
247+ this . transfer_event ( "change" , this . querySelector ( "textarea" ) , oldValue , newValue ) ;
248+ break ;
249+ case "onselectionchange" :
250+ this . transfer_event ( "selectionchange" , this . querySelector ( "textarea" ) , oldValue , newValue ) ;
251+ break ;
218252 }
219253 }
220254
221255 }
222256
257+ /* Transfer an event by name from this to an inner element. */
258+ transfer_event ( evt_name , transfer_to , oldValue , newValue ) {
259+ // Doesn't exist
260+ if ( oldValue ) {
261+ transfer_to . removeEventListener ( evt_name , this . last_events [ evt_name ] ) ;
262+ }
263+ if ( newValue ) {
264+ this . last_events [ evt_name ] = this . onchange . bind ( this ) ;
265+ transfer_to . addEventListener ( evt_name , this . last_events [ evt_name ] ) ;
266+ this [ `on${ evt_name } ` ] = undefined ; // Prevent duplicate
267+ }
268+ }
269+
223270 /* Value attribute */
224271 get value ( ) {
225272 return this . getAttribute ( "value" ) ;
@@ -235,6 +282,7 @@ var codeInput = {
235282 return this . setAttribute ( "placeholder" , val ) ;
236283 }
237284 } ,
285+
238286 registerTemplate : function ( template_name , template ) {
239287 // Set default class
240288 codeInput . usedTemplates [ template_name ] = template ;
@@ -245,6 +293,7 @@ var codeInput = {
245293 elem . template = template ;
246294 elem . setup ( ) ;
247295 }
296+ console . log ( `code-input: template: Added existing elements with template ${ template_name } ` ) ;
248297 }
249298 if ( codeInput . defaultTemplate == undefined ) {
250299 codeInput . defaultTemplate = template_name ;
@@ -256,7 +305,9 @@ var codeInput = {
256305 elem . setup ( ) ;
257306 }
258307 }
308+ console . log ( `code-input: template: Set template ${ template_name } as default` ) ;
259309 }
310+ console . log ( `code-input: template: Created template ${ template_name } ` ) ;
260311 } ,
261312 templates : {
262313 custom ( highlight = function ( ) { } , preElementStyled = true , isCode = true , includeCodeInputInHighlightFunc = false , plugins = [ ] ) {
0 commit comments