@command / create-command
You can create your own Active CSS commands and then use them like CSS commands in your config.
@command
Syntax
@command (action command name) {= JavaScript code =}
This is just a nicer-looking syntax added in version 2.2.0 for creating a command, but there are no plans to remove create-command. "@command" converts to "create-command" during config load and sets the command up immediately. See below for details on how to use the create-command action command.
Syntax example:
@command reshape {=
function random(min, max) {
return Math.floor(Math.random() * (max - min + 1) + min);
}
vars polly; // This is a Active CSS command (not JavaScript), to allow Active CSS variables inside JS.
polly = [];
for (let n = 0; n < 8; n++) {
polly[n] = random(0, 100) + 'px';
}
=}
create-command
Syntax
create-command: (action command name) {= JavaScript code =} [after (time)][, (command name)...];
action command name = name of the action command to create. Eg. add-blue-color, or scroll-bottom.
JavaScript code = Javascript functionality contained with the Active CSS JavaScript wrappers "{=" and "=}".
This command allows you to create Active CSS commands dynamically within the event flow.
You would normally use create-command in the body:init event, inside a draw event declaration, or in a shadow DOM initialization event. But it doesn't particularly matter where you choose to use it, as create-command will only create a command once. It will not re-create the action command if it already exists. No variable substitution is allowed in the command. You can only use raw JavaScript - no Active CSS. It will run in the context of the scope that it is run. Any variables run in the action command should be available in the scope it is run in. So if you are writing a shadow DOM component, the variables are expected to either be already set up in that scope, or you are going to set them up in that scope as part of the command itself.
Creating a function for the action command
The function itself that will perform your action command is written in regular JavaScript, as if you were writing it in an external JavaScript file. But you don't need to declare the function, or any parameters with JavaScript. You have a bunch of variables already set up that you can use, which should provide you with everything you need to set the context of the action command.
To illustrate this, if you were recreating the media-control command with exactly the same parameters as the built-in command, and you wanted to call it "my-amazing-media-control", it might look like the following example in the config. The function below uses variables that will be available in your function. These are variables that you would need to reference in order to perform the action on the target selector, and other variables that are set as part of the event flow. The target selector element is referenced by the variable "targetSelector", which would be the media object, like a video tag or an audio tag. The action command parameters are called the action value, and that is found in the string variable "actionValue":
body:init {
create-command: my-amazing-media-control {=
let arr = actionValue.split(' '); // manipulate the action value string.
if (arr[1]) { // if there is more than one "parameter" in the action value.
arr[1] = arr[1]._ACSSRepQuo(); // helper function to remove quotes from the second parameter. You can roll-your-own if you prefer.
switch (arr[0]) {
case 'load':
targetSelector.setAttribute('src', arr[1]);
targetSelector.load();
break;
case 'seek':
targetSelector.currentTime = parseFloat(arr[1]);
break;
case 'volume':
targetSelector.volume = parseFloat(arr[1]); // Value between 0 and 1.
}
return;
}
switch (arr[0]) {
case 'play':
targetSelector.play();
break;
case 'pause':
targetSelector.pause();
}
=};
}
Don't forget the semi-colon at the end of the create-command declaration after the "=}" or the create-command won't run!
Also, you are permitted to use comments with "//", as in the above example, because this is native JavaScript. You cannot use "//" in regular config though... always use "/* ... */" when putting comments into regular config.
Back to the example above. You would use the command like this - exactly like the built-in media-control command:
#playVideoButton:click {
#videoPlayer {
my-amazing-media-control: play;
}
}
How is the action value handled?
The action value is the right-side of the colon ":" in the action command.
Eg. "blue" in the command "color: blue;".
The action value will appear in your function in the variable "actionValue". This will be a string. It is up to you how you handle it, with regular JavaScript string manipulation, etc. There is no set method of handling action value contents or "parameters" in Active CSS. Each command could potentially handle the action value differently.
How are comma delimited action values handled?
Comma-delimited values are multiple action values for the same command.
Eg. In the command "style: color blue, border 1px solid #222, margin 20px;", the values are "color blue", "border 1px solid #222" and "margin 20px".
You don't have to do anything special in your command for this, as this is built-in functionality for Active CSS. You will only ever see one action value in your function. Comma-delimited action values are simply multiple re-runs of the same function. Your function will run for each comma-delimited value, and so the actionValue variable will never contain a comma-delimited list - it will only contain the value it is working on at the time.
What about the "after" and "every" instructions for delayed or intervaled action?
Again, this is built-in functionality for Active CSS. All "after" and "every" instructions will be removed by the time your function is run. You will not see anything in the actionValue that looks like "after 1s" or "every 1s after stack". These will have been removed by the time Active CSS runs your action command.
How do I reference the event selector or the target selector?
If you are writing a command that will apply directly to a target selector, or if no target selector is present and so therefore you want to reference the event selector, then you will want to use the "targetSelector" variable in every case. If there is no target selector mentioned in the config where the command is run, the event selector is the target selector. So by referencing the "targetSelector" variable as your main focus, it will handle both cases.
There will only ever be one target selector object that reaches your function. This is important to grasp. Your function will get run multiple times if you are referencing multiple targets. For example if you put your action command into a target selector of ".redDivs", then your command will get run on each element that matches the ".redDivs" selector. You do not need to check for multiple targets in your function. The targetSelector will only ever be a reference to one element.
If you do need the event selector, which may be a rare occurrence in the course of creating commands, then this is available in the variable "eventSelector". This will be the element object that received the event. But you would normally use targetSelector to handle event selectors, as mentioned above, for general commands.
Available variables within the command function
Commonly used variables are in bold - it's recommended you just read those. At least be aware of the other variables names so you don't accidentally name your own variables the same.
actionName | The name of the action command, such as "make-divs-blue". actionPosition The position in the action value, 0, 1, etc. - you can call more than one function if you comma-delimit them. |
actionValue | The fully evaluated action value that applies to this command call. All variable substitution has occurred by this point. actionValueUnEval The un-evaluated action value that called the function. There may be others through the use of comma-delimiting, but there is only one that applies per function call. "actionValuesUnEval" contains the full list if you ever need to reference all comma-delimited values. |
carriedEventObject | If your action command is called from an afterAjax type of event, this should contain the element from the event that triggered the ajax call. This is a bit niche - you probably wouldn't use this often. |
activeID | The internal reference to the target selector assigned. You probably won't need to use it, but it is here just in case. |
doc | The frame object where the target selector can be found. Always use this instead of "document" to stay in the same window frame as the target selector. Don't use "document" unless you are absolutely sure you will always be referencing the main frame. Target selectors can be in iframes, and the shadow DOM, so use doc which will either be set to document, an iframe or a shadow DOM reference, as applicable. |
e | The event object. Like "click", "mouseover", etc. It is the actual JavaScript event object, which is commonly referred to as "e" in functions, so we've used the same name here. |
eventName | The name of the event, like "click". This is a string. |
configFile | The name of the config file where this action command was run. |
actionFunc | The name of the actual function that Active CSS has generated internally for this command, such as "_aMakeDivsBlue". |
configLine |
The line in the config file where this action command was run. |
eventSelector |
The event selector element itself, that received the event. This will be an element-type of object. |
actionValuesUnEval |
All the comma delimited un-evaluated action values of the action command. This contains all the action values, so be careful that this is what you want to use. You might only want the un-evaluated action value for this specific command - if so, use actionValueUnEval. |
targetSelectorName |
The name of the target selector this function was called from. If it contains "&" it means the target selector was the event selector. |
conditionals |
A space delimited list of any conditionals that were passed. |
eventSelectorName |
The name of the event selector. This is a string. |
rulesArray |
An array of all the action commands and un-evaluated values in the target selector declaration. This will be concatenated from all the rules that are apply to the target selector, which may be physically in different places in the config, but will be all grouped together here. |
targetSelector |
The target selector element itself. This is always a reference to one element. See notes above on referencing the event selector and the target selector. |
selectorRef |
The target selector internal reference string. |
compDoc |
The "document" or node of the shadow DOM or scoped component, if appropriate. component The name of the component, if appropriate |
_loopVars, _loopRef, _activeVarScope, scopedVars |
Internal variables containing data to manage user variables. It isn't recommended to mess with these. |
o |
This is the same o that appears in the func command. This contains most of the above variables. The reason the variables above exist is that they may be more easily understood in that form than in the o variable. But you can solely use the o variable if you prefer. The values are the same. Do not change o variable contents though - eg. it may have adverse effects like break the event reporting in DevTools, or if you erase the main event object then you might get things going strange. |
Multiple "create-command"s in the same declaration
This is done in the same way as any other Active CSS command, by using a comma. The create-command action command follows the same rules as all the other Active CSS commands.
Eg.:
body:init {
create-command: my-new-command-a {=
... javascript javascript...
=},
my-new-command-b {=
... javascript javascript...
=};
}
Helper functions
_ACSSRepQuo()
This removes the double quotes from a string and unescapes any double quotes within.
Eg.
let str = actionValue._ACSSRepQuo();
Getting it to work in browser extension code
So you are building a browser extension, and you try to use create-command, and you get an error saying JavaScript cannot be evaluated dynamically due to browser policies. What do you do? You would need to call an external function with the func command, or get an action command approved for use in the core.