1- import { PREFIX , COMMANDS } from './consts.js' ;
1+ import { PREFIX , COMMANDS , ROOT_COMMANDS } from './consts.js' ;
2+ import { _disableSource , _enableSource , _finalizeEvent , getCommandWithArgs } from './utils.js' ;
23
34/**
45 * @typedef {Event & {source: Element, command: string} } CommandEvent
@@ -15,105 +16,163 @@ let registrationOpen = true;
1516 * @param {CommandEvent } event The event to handle
1617 */
1718export function handleCommand ( event ) {
18- switch ( event . command ) {
19+ const [ command , ...args ] = event . command . split ( ':' ) ;
20+ switch ( command ) {
21+ case COMMANDS . addClass :
22+ event . target . classList . add ( ...args ) ;
23+ _finalizeEvent ( event ) ;
24+ break ;
25+
26+ case COMMANDS . removeClass :
27+ event . target . classList . remove ( ...args ) ;
28+ _finalizeEvent ( event ) ;
29+ break ;
30+
1931 case COMMANDS . show :
2032 event . target . show ( ) ;
33+ _finalizeEvent ( event ) ;
2134 break ;
2235
2336 case COMMANDS . hide :
2437 event . target . hidden = true ;
38+ _finalizeEvent ( event ) ;
2539 break ;
2640
2741 case COMMANDS . unhide :
2842 event . target . hidden = false ;
43+ _finalizeEvent ( event ) ;
2944 break ;
3045
3146 case COMMANDS . disable :
3247 event . target . disabled = true ;
48+ _finalizeEvent ( event ) ;
3349 break ;
3450
3551 case COMMANDS . enable :
3652 event . target . disabled = false ;
53+ _finalizeEvent ( event ) ;
3754 break ;
3855
3956 case COMMANDS . scrollIntoView :
4057 event . target . scrollIntoView ( {
41- behavior : matchMedia ( '(prefers-reduced-motion: reduce)' ) . matches ? 'instant' : 'smooth' ,
58+ behavior : event . target . dataset . behavior ?? matchMedia ( '(prefers-reduced-motion: reduce)' ) . matches ? 'instant' : 'smooth' ,
59+ block : event . target . dataset . block ?? 'start' ,
60+ container : event . target . dataset . container ?? 'all' ,
61+ inline : event . target . dataset . inline ?? 'nearest' ,
4262 } ) ;
63+
64+ _finalizeEvent ( event ) ;
4365 break ;
4466
4567 case COMMANDS . remove :
4668 event . target . remove ( ) ;
69+ _finalizeEvent ( event ) ;
4770 break ;
4871
4972 case COMMANDS . requestFullscreen :
5073 event . target . requestFullscreen ( ) ;
74+ _finalizeEvent ( event ) ;
5175 break ;
5276
5377 case COMMANDS . exitFullscreen :
5478 if ( event . target . isSameNode ( document . fullscreenElement ) ) {
5579 document . exitFullscreen ( ) ;
80+ _finalizeEvent ( event ) ;
5681 }
5782
5883 break ;
5984
6085 case COMMANDS . toggleFullscreen :
6186 if ( event . target . isSameNode ( document . fullscreenElement ) ) {
6287 document . exitFullscreen ( ) ;
88+ _finalizeEvent ( event ) ;
6389 } else {
6490 event . target . requestFullscreen ( ) ;
91+ _finalizeEvent ( event ) ;
6592 }
6693 break ;
6794
6895 case COMMANDS . showPicker :
6996 event . target . showPicker ( ) ;
97+ _finalizeEvent ( event ) ;
7098 break ;
7199
72100 case COMMANDS . stepUp :
73101 event . target . stepUp ( ) ;
102+ _finalizeEvent ( event ) ;
74103 break ;
75104
76105 case COMMANDS . stepDown :
77106 event . target . stepDown ( ) ;
107+ _finalizeEvent ( event ) ;
78108 break ;
79109
80110 case COMMANDS . openDetails :
81111 event . target . open = true ;
112+ _finalizeEvent ( event ) ;
82113 break ;
83114
84115 case COMMANDS . closeDetails :
85116 event . target . open = false ;
117+ _finalizeEvent ( event ) ;
86118 break ;
87119
88120 case COMMANDS . toggleDetails :
89121 event . target . open = ! event . target . open ;
122+ _finalizeEvent ( event ) ;
90123 break ;
91124
92125 case COMMANDS . playMedia :
93126 event . target . play ( ) ;
127+ _finalizeEvent ( event ) ;
94128 break ;
95129
96130 case COMMANDS . pauseMedia :
97131 event . target . pause ( ) ;
132+ _finalizeEvent ( event ) ;
98133 break ;
99134
100135
101136 case COMMANDS . requestPictureInPicture :
102137 event . target . requestPictureInPicture ( ) ;
138+ _finalizeEvent ( event ) ;
103139 break ;
104140
105141 case COMMANDS . copyText :
106- navigator . clipboard . writeText ( event . target . textContent ) ;
142+ _disableSource ( event ) ;
143+ _finalizeEvent ( event ) ;
144+ navigator . clipboard . writeText ( event . target . textContent ) . finally ( ( ) => _enableSource ( event ) ) ;
107145 break ;
108146
109147 default :
110- if ( registeredCommands . has ( event . command ) ) {
111- const callback = registeredCommands . get ( event . command ) ;
112- callback ( event ) ;
148+ if ( registeredCommands . has ( command ) ) {
149+ _disableSource ( event ) ;
150+ _finalizeEvent ( event ) ;
151+
152+ Promise . try ( registeredCommands . get ( command ) , event , ...args )
153+ . catch ( globalThis . reportError )
154+ . finally ( ( ) => _enableSource ( event ) ) ;
113155 }
114156 }
115157}
116158
159+ const COMMAND_VALUES = Object . values ( COMMANDS ) ;
160+
161+ /**
162+ * Checks if a command is registered
163+ *
164+ * @param {string } command The command to check for
165+ * @returns {boolean } If the command is registered
166+ */
167+ export function hasCommand ( command ) {
168+ const cmd = typeof command === 'string' ? command . split ( ':' ) [ 0 ] : null ;
169+
170+ return typeof cmd === 'string'
171+ && cmd . startsWith ( '--' )
172+ && ( COMMAND_VALUES . includes ( cmd ) || registeredCommands . has ( cmd ) ) ;
173+ }
174+
175+
117176/**
118177 * Adds a `command` listener to the target element
119178 *
@@ -133,7 +192,7 @@ const observer = typeof globalThis.document === 'undefined' ? null : new Mutatio
133192 if ( mutation . type === 'attributes' && mutation . attributeName === 'commandfor' ) {
134193 const target = mutation . target . commandForElement ;
135194
136- if ( target instanceof Element ) {
195+ if ( target instanceof Element && hasCommand ( mutation . target . command ) ) {
137196 target . addEventListener ( 'command' , handleCommand ) ;
138197 }
139198 }
@@ -143,14 +202,14 @@ const observer = typeof globalThis.document === 'undefined' ? null : new Mutatio
143202 if ( node . nodeType === Node . ELEMENT_NODE && node . hasAttribute ( 'commandfor' ) ) {
144203 const el = node . commandForElement ;
145204
146- if ( el instanceof Element ) {
205+ if ( el instanceof Element && hasCommand ( node . command ) ) {
147206 el . addEventListener ( 'command' , handleCommand ) ;
148207 }
149208 } else if ( node . nodeType == Node . ELEMENT_NODE ) {
150- node . querySelectorAll ( '[commandfor]' ) . forEach ( el => {
209+ node . querySelectorAll ( '[commandfor][command] ' ) . forEach ( el => {
151210 const target = el . commandForElement ;
152211
153- if ( target instanceof Element ) {
212+ if ( target instanceof Element && hasCommand ( el . command ) ) {
154213 target . addEventListener ( 'command' , handleCommand ) ;
155214 }
156215 } ) ;
@@ -166,10 +225,10 @@ const observer = typeof globalThis.document === 'undefined' ? null : new Mutatio
166225 * @param {Element|DocumentFragment } target The root for the observer to watch from
167226 */
168227export function observeCommands ( target = document . body ) {
169- target . querySelectorAll ( 'button[commandfor]' ) . forEach ( el => {
228+ target . querySelectorAll ( 'button[command][ commandfor]' ) . forEach ( el => {
170229 const target = el . commandForElement ;
171230
172- if ( target instanceof Element ) {
231+ if ( target instanceof Element && hasCommand ( el . command ) ) {
173232 target . addEventListener ( 'command' , handleCommand ) ;
174233 }
175234 } ) ;
@@ -225,15 +284,23 @@ export function createCommand(callback) {
225284 * @param {(event: CommandEvent) => void } callback The callback to call
226285 * @returns {string } `"command="..."`
227286 */
228- export function command ( callback ) {
229- return `command="${ createCommand ( callback ) } "` ;
287+ export function command ( callback , ... args ) {
288+ return args . length === 0 ? `command="${ createCommand ( callback ) } "` : `command=" ${ createCommand ( callback ) } : ${ args . join ( ':' ) } "`;
230289}
231290
232291/**
233292 * Closes registration of new commands
234293 *
235294 * @returns {boolean }
236295 */
237- export const closeCommandRegistration = ( ) => registrationOpen = false ;
296+ export function closeCommandRegistration ( ) {
297+ if ( registrationOpen ) {
298+ registrationOpen = false ;
299+ return true ;
300+ } else {
301+ return false ;
302+ }
303+ }
238304
239- export { COMMANDS } ;
305+ export { COMMANDS , ROOT_COMMANDS , getCommandWithArgs } ;
306+ export { registerRootCommand , closeCommandRootRegistry , handleRootCommand , initRootCommands } from './root-commands.js' ;
0 commit comments