Copy link to clipboard
Copied
I was referred to @Peter Kahrel excellent tutorial on ScriptUI located here https://creativepro.com/files/kahrel/indesign/scriptui.html, but I am having a few issues with my scripts:
w.input = w.group.add ('edittext {characters: 10, active: true, justify: "right"}');
I don't see where "characters: 10" is explained. I thought it would limit the input to 10 characters, but it does not seem to, either in his example or in my testing. Is there a way to do this? One of my input fields needs to be 7 characters and one of them needs to be 8 character date in format YYYYMMDD.
win.Panel2.txt2.onChanging = function () {
var valid = /^[\d]+$/.test (win.Panel2.txt2.text);
this.graphics.backgroundColor = this.graphics.newBrush (this.graphics.BrushType.
SOLID_COLOR, valid ? [1, 1, 1, 1] : [1, 0.5, 0.5, 1]);
win.Panel2.ApplyBtn.enabled = valid && win.Panel2.txt2.text.length=7;
}
win.Panel2.txt3.onChanging = function () {
var valid = /^[\d]+$/.test (win.Panel2.txt3.text);
this.graphics.backgroundColor = this.graphics.newBrush (this.graphics.BrushType.
SOLID_COLOR, valid ? [1, 1, 1, 1] : [1, 0.5, 0.5, 1]);
var valid2 = Date.parse(win.Panel2.txt3.text);
win.Panel2.ApplyBtn.enabled = !valid2==NaN;
}
I'll probably have more questions as I get further into my script, and I'll add them to this thread, if that is okay.
(The forum gave me an error about invalid HTML that it removed, but I'm not seeing what it changed).
Thanks in advance!
= is used to assign a value to a variable.
== and === test equality. === is used for strict equality, which means that the two items you compare must be the same type. == is for looser comparisons. == is slower because it tries to coerce the two items into type-equality.
Example:
var a = 5;
var b = '5';
a == b; // => true
a === b // => false
a is a number, b is a string. Strict comparison returns false because the variables are not the same type. Loose comparison returns true, it successfu
...Playing with text then textselection, in that order, may solve the cursor issue in EditText widgets. That's a bit of black magic so I do not guarantee the universality of the following code:
// . . .
// Your UI components (w, w.input, etc)
// . . .
// Predeclare brushes
var gx = w.input.graphics;
var BH =
[
gx.newBrush(gx.BrushType.SOLID_COLOR, [1.0, 0.5, 0.5, 1]), // KO
gx.newBrush(gx.BrushType.SOLID_COLOR, [1.0, 1.0, 1.0, 1]) // OK
];
// Text changing handle
...
@Marc Autret (or others)
Funny how things work - or maybe not if you are religious!!! Anyway, I was getting really frustrated and typed up a long post. I hit preview and walked away from my computer and never posted it. I couldn't see the forest for the trees, but I solved the problem!!!
This will be a long post, but it should help some people.
The main problem I was having had to do with "partially valid" regex. I have enough trouble coming up with "fully valid" regex - although I think I have t
...
Sorry I don't understand your code 😕 I'm afraid I've already detailed everything I can say about my own approach, so the code below basically just repeats the same logic.
Some advice to prevent infinite loops and other errors: Keep your objects encapsulated, only play with text/textselection once (and only if needed!), do not take the risk of reintroducing a changing event while you modify the text, do not resolve again the EditText instance (it is and remains this within t
...Copy link to clipboard
Copied
> Unfortunately, it moves the cursor back to the beginning after it does the replacement
Yes, that's annoying. Not much you can do about it I don't think. Unless @Marc Autret has an idea.
Copy link to clipboard
Copied
Playing with text then textselection, in that order, may solve the cursor issue in EditText widgets. That's a bit of black magic so I do not guarantee the universality of the following code:
// . . .
// Your UI components (w, w.input, etc)
// . . .
// Predeclare brushes
var gx = w.input.graphics;
var BH =
[
gx.newBrush(gx.BrushType.SOLID_COLOR, [1.0, 0.5, 0.5, 1]), // KO
gx.newBrush(gx.BrushType.SOLID_COLOR, [1.0, 1.0, 1.0, 1]) // OK
];
// Text changing handler
w.input.onChanging = function( t)
{
t = this.text;
if( !/^\d{0,7}$/.test(t) )
{
t = t.replace(/\D+/g,'').slice(0,7);
this.text = '';
this.textselection = t;
}
t = 7===t.length;
w.ok.enabled = t;
this.graphics.backgroundColor = BH[+t];
}
// . . .
// Other stuff, w.show()
// . . .
Note 1. — Dispatching new KeyboardEvent instances does not work in ScriptUI in the way you expect it. We tried this strategy for a very long time to work around some bugs, but it only produces logical events without any impact in the UI. Put more briefly, we have never succeeded in emulating keystrokes in a SUI control (although we can positively receive and analyze KB user events through a "keydown" listener).
Note 2. — To my knowledge, the property EditText.graphics.backgroundColor is no longer effective in CC (?) but it may depend on the OS (?) [Still works in CS3-CS6 though.]
Hope that helps.
Best,
Marc
Copy link to clipboard
Copied
@Marc Autret - That works amazingly - exactly what I was asking for. I don't really understand it, but I can make it work. Thank you SO much!!!
Playing with text then textselection, in that order, may solve the cursor issue in EditText widgets. That's a bit of black magic so I do not guarantee the universality of the following code:
Works fine on my system - as long as it works on Windows in FM15 and up, I'm fine. (Actually, as long as it allows input on those systems, I'm okay with it. (And ESTK seems a bit too good at ignoring errors - i.e. many times my code runs (improperly), if I just didn't define something properly. VBA would have thrown an error - but that has plusses also.
Note 2. — To my knowledge, the property EditText.graphics.backgroundColor is no longer effective in CC (?) but it may depend on the OS (?) [Still works in CS3-CS6 though.]
Pity - it is working for me and I really like the effect. I assume CC is Creative Cloud and CS is Creative Suite. How can I know what I am running. It is working for me in Win10 and I see from time to time that Creative Cloud has been updated, so either I have CC but am using something else, or it is working again.
Copy link to clipboard
Copied
Two new questions - and I could experiment, but I figured I'd ask here:
In ScriptUI, I usually see something like:
w.input.OnChanging = function(){
}
Can I call a universal function - i.e.
w.input.OnChanging = DoSomething();
function DoSomething(){
}
Also - my background is VBA - does ScriptUI (or more likely ESTK) support optional variables for functions, and/or does it matter. IOW, I am used to:
Sub DoSomething (variable1 as String, variable2 as String, Optional Variable3 as String = "Ignore Me)
End Sub
called as "Call DoSomething("1", "2") - Not sure if optional Variables have to be at the end or if if I could have 2 optional and call "DoSomething("1", , "3")
From what I can tell - ESTK takes any variables and works and I need to test for validity, for example, I think this works:
function DoSomething(var1, var2, var3){
if (var2.ObjectValid()){
// Var2 was optional and passed
}
else{
// Var 2 was omitted
}
// run code
}
And to call it would I use:
DoSomething(var1, Dummy, var3);
or
DoSomething(var1, ,var3);
or does either one work?
Thanks in advance!
Copy link to clipboard
Copied
Clarifying this for others that might be reading the thread ... Neither one works exactly like I said ...
Universal functions:
w.input.OnChanging = DoSomething();
function DoSomething(){
alert("I'm here!");
}
Gave me weird results - the alert message would display even if I didn't change w.input.
This works, though:
w.input.OnChanging = function(){
DoSomething();
}
function DoSomething(){
alert("I'm here!");
}
https://www.javascripttutorial.net/es6/javascript-default-parameters/ - well-explained but not sure it is valid for ScriptUI, but what I ended up doing was setting the variable to "" or Undefined before passing it to the function and then testing it for validity in the function.
Copy link to clipboard
Copied
w.input.OnChanging = DoSomething();
function DoSomething(){
alert("I'm here!");
}
> Gave me weird results
I'm not surprised: mind your cases -- OnChanging should be onChanging. JavaScript is case-sensitive.
Copy link to clipboard
Copied
I'm not surprised: mind your cases -- OnChanging should be onChanging. JavaScript is case-sensitive.
True - that trips me up all the time - especially with 'if" blocks. Doesn't help that in VBA If is capitalized and if you type it lower-case the editor capitalizes it for you, and in JS, it leaves it capatilized and either says "illegal use of the reserved word else" or "If is undefined."
I should be more careful on the forum as people will copy and paste my results, but I'm pretty sure I copied and pasted "onChanging" in my script and it was running the function without changing the edittext field.
Regardless, it is only two more lines of code to call the function as I did above and that works fne.
Copy link to clipboard
Copied
Perhaps better use visual studio code and add the adobe extendscript debug extension so you have context and autofill. It will also show usefull information about your functions, variables, parameters etc etc
This first video also show how to get the type definitions for autofill, which is really helpful
Copy link to clipboard
Copied
(…) I'm pretty sure I copied and pasted "onChanging" in my script and it was running the function without changing the edittext field.
Regardless, it is only two more lines of code to call the function as I did above and that works fne.
By @Marshall_Brooks
Make sure you assign a function reference to .onChanging:
w.input.onChanging = DoSomething; // Sounds good
is not the same as
w.input.onChanging = DoSomething(); // Sounds wrong (unless a func is returned)
Best,
Marc
Copy link to clipboard
Copied
@Marc Autret - Now I'm confused. How would I pass variables to the function if I leave off the parens?
Also - the issue was with onClick not onChanging, but I would think they would work the same way - but thinking like that can be dangerous ...
Specifically:
win.Panel2.ApplyBtn.onClick = function(){
UpdateVariables(win, doc, var1, var2);
}
function UpdateVariables(win, doc, var1, var2){
alert("I'm here");
}
Works fine.
but:
win.Panel2.ApplyBtn.onClick = UpdateVariables(win, doc, var1, var2);
function UpdateVariables(win, doc, var1, var2){
alert("I'm here");
}
Which looks similar and I thought would work did weird things, like I would get an "I'm here" message when I first ran the script even though I had not clicked the ApplyBtn.
As I understand you, you are saying I should use:
win.Panel2.ApplyBtn.onClick = UpdateVariables;
function UpdateVariables(win, doc, var1, var2){
alert("I'm here");
}
I simplified the function for the forum, but my UpdateVariables function has lines like:
win.Panel2.ApplyBtn.enabled = false;
win.Panel2.CancelBtn.enabled = false;
var varFmt = doc.GetNamedVarFmt (UserFormat1);
I didn't test, but I think your suggestion would give me errors like "win" is undefined. "doc" is undefined, etc.
Copy link to clipboard
Copied
Hi @Marshall_Brooks,
If defined, event handlers (like onClick, onChange, onChanging…) are functions without formal parameters, so you cannot “pass in” explicit arguments. However, since your code seems to use a closure, variables of higher scope (win, doc…) are known from within the body of such functions. Although this is not the optimal strategy in ScriptUI, that's how your code is written so I'm answering in that context.
More agnostically, an event handler has its this context set to the UI component it is attached to, so you can easily recover the parent Window, locally, using `this.window` (property available for any widget).
Note. — Unlike predefined event handlers, pure event listeners have a formal parameter (1st argument) set to the Event instance, but that's another topic.
Best,
Marc
Copy link to clipboard
Copied
@Marc Autret - Excellent reply!!! Not sure I fully understood it, but my code seems to be working now.
Simplified explanation for novices like me is that if you want to pass arguments to the function, you have to call it from within the event function as I did above.
Copy link to clipboard
Copied
@Marc Autret - I have two more questions and then I think I can put this project to bed.
The code above worked great for limiting my input to 7 numbers. For other fields, I have two new requirements and mainly I don't know the regex, although I don't completely understand your code either, but I know enough of it to be dangerous!!!
If you could please provide examples of those, I would greatly appreciate it!!!
Copy link to clipboard
Copied
Hi @Marshall_Brooks,
The key idea when you implement a dynamic validation mechanism on an EditText is to distinguish between partially valid input (all entered characters are OK but the entire pattern is not satisfied yet, there are missing characters) and completely valid input (the expected pattern is fully matched).
Here is the general scheme I used (you can easily adapt it to other regexes, lengths and/or conditions):
w.input.onChanging = function()
{
var t = this.text;
if( ! PARTIALLY_VALID(t) )
{
t = REMOVE_INVALID_PARTS(t);
this.text = '';
this.textselection = t;
}
this.window.ok.enabled = FULLY_VALID(t);
};
Best,
Marc
Copy link to clipboard
Copied
@Marc Autret Thank you - that helps a little. I'm still not fully clear on your code, but I can adapt it and make it work.
Let me re-state the question. Could you send me a good reference on how to set up and test input against regex's.
For example, in the code you sent, I originally had:
if( !/^\d{0,7}$/.test(t) ) { t = t.replace(/\D+/g,'').slice(0,7);
I don't REALLY understand the orange numbers. I Googled something like Regex only allow numbers and found https://stackoverflow.com/questions/27772805/need-a-regex-to-remove-everything-except-numbers and modified some of the answers there. There was also a link to https://stackoverflow.com/questions/22937618/reference-what-does-this-regex-mean/22944075#22944075 - which I haven't really gone through yet.
I don't think I'll get far with Googling Regex to allow Numbers, Dashes, A, and V, (Need to allow J also) but there is a reply at the end to allow a decimal, which probably allows me to answer my initial questions, so I'm thinking I need:
.replace(/[^0-9AVJ-]+/g, '');
But I don't know what the top line should be.
.replace(/[^0-9-/]+/g, '');
But I suspect / is a reserved character and I would need to literal or escape it and I'm not sure how to do that (or what the top line should be).
That takes care of limiting the input, but I also need to validate the results and I'm not sure how to do that.
Without giving too much away ...
And for all dates, 4 more regeexes for 1-digit month and 2-digit day or 2-digit month and one-digit day. I think I can write the regexes for each example, but it would involve select:case examples with 9 cases (in a separate function). I was wondering if there was a simpler way. (Other than saying "USE MM-DD-YYYY with dashes only", which would also be a possibility).
Thank you again for the assistance!!!
Copy link to clipboard
Copied
I can probably get what I need from https://regexone.com/ - but there's a lot to digest ...
Copy link to clipboard
Copied
Getting a grip on your greps is really what you should prioritise. Remember that there are many flavours. You, like the rest of us here, will deal with two different flavours: JavaScript's and InDesign's regular expressions.
JavaScript's regex is pretty basic, all you need to read up is this one:
https://www.oreilly.com/library/view/regular-expression-pocket/9780596514273/
which has a chapter on JavaScript regexes.
For those dates, you can use one regex:
/\d\d[-\/]?\d\d[-\/]?\d\d\d\d/
Copy link to clipboard
Copied
grip on greps - nice word play - I'm not sure what a grep is, other than something I've heard from Linux/Unix. Not something I want to dive into just yet ...
Instead of:
/\d\d[-\/]?\d\d[-\/]?\d\d\d\d/
doesn't it need to be:
/?\d\d[-\/]?\d\d[-\/]\d\d\d\d/
?
If I read the tutorial correctly, what you provided would allow 06-08-202 as valid and 6-8-2022 as invalid.
(But I wouldn't have come up with what you have on my own and it saved me a tremendous amount of time.)
Much appreciated!!!
Copy link to clipboard
Copied
GREP is an acronym, it stands for general regular-expression printer.
Those question marks you added after the slashes – why do you think they're needed?
> If I read the tutorial correctly,
Which tutorial?
> what you provided would allow 06-08-202 as valid and 6-8-2022 as invalid.
No, the other way round. Try it.
Copy link to clipboard
Copied
Which tutorial?
Those question marks you added after the slashes – why do you think they're needed?
https://regexone.com/lesson/optional_characters
But I was mistaken and thought the "?" went BEFORE and not AFTER the optional character.
So I think it needs to be:
/\d\d?[-\/]?\d\d?[-\/]?\d\d\d\d/
This (I think) allows 6-8-2022 or 06-08-2022, or slashes or 06082022 - but also 682022, which should not be allowed, so further, I think it probably needs to be something like:
/\d{8}||\d\d?[-\/]\d\d?[-\/]\d\d\d\d/
And if I want to get really fancy, the first character of the month should be 0 or 1, if present and the first character of the date should be 1, 2, or 3 only - but I'll parse it and test for that afterwards.
GREP does sound related to this, but this is one of those case where right now I want to get this working and not spend time researching special cases, but afterward, I will have time to research it, but won't have a need or justification to use it - LOL!!!
Thank you again for the assistance!!!
Copy link to clipboard
Copied
Haven't gotten to the dates part yet, but I ran into an odd problem.
Previously, I wanted a date in YYYYMMDD format. I found it easier to modify Marc's code into one part that checks if the date is valid and changes the background color and sets the apply button, and one part that handled replacing everything but the numbers. That code looked like this and works:
win.Panel2.txt3.onChanging = function ( t) {
win.Panel2.ApplyBtn.enabled = false;
var valid = IsValidIssueDate(win.Panel2.txt3.text);
this.graphics.backgroundColor = this.graphics.newBrush (this.graphics.BrushType.
SOLID_COLOR, valid ? [1, 1, 1, 1] : [1, 0.5, 0.5, 1]);
win.Panel2.ApplyBtn.enabled = valid;
// Text changing handler
t = this.text;
if( !/^\d{0,8}$/.test(t) ){
t = t.replace(/\D+/g,'').slice(0,8);
this.text = '';
this.textselection = t;
}
}
(There is some more code, but it works with the above.)
For the first question, I need to allow A, V, J, and dashes and certain characters in a certain order, and I wanted it in uppercase.
This works for the color changing and the apply button (I'm not posting the exact regex due to proprietary issues):
win.Panel2.txt1.onChanging = function( t){
win.Panel2.ApplyBtn.enabled = false;
t = this.text
win.Panel2.txt1.text = t.toUpperCase();
t = this.text // Needed to prevent an error if I typed a lower case number.
var valid = / <proprietary code> /.test (t);
this.graphics.backgroundColor = this.graphics.newBrush (this.graphics.BrushType.
SOLID_COLOR, valid ? [1, 1, 1, 1] : [1, 0.5, 0.5, 1]);
win.Panel2.ApplyBtn.enabled = valid;
}
I found this site - https://regex101.com/r/bGkhEg/5 and it recommended this code, which seems to work:
var t="Test Numbers";
if(valid===false){
alert("not valid");
t = t.replace(/[^-0-9AVJ]+/g, '');
alert(t);
}
else{
alert("valid");
}
Then I tried adding this before the applybutton.enabled line:
// Text changing handler
t = this.text;
if(valid===false){
t = t.replace(/[^-0-9AVJ]+/g, '');
this.text = '';
this.textselection = t;
}
Whenever I change a value, I get an error "Stack Overrun" and it copies my code into a new code window (SourceXX).
What did I do incorrectly?
Copy link to clipboard
Copied
For the dates, for testing:
var valid = /^\d{8}$|^\d\d?[-\/]\d\d?[-\/]\d\d\d\d$/.test (t);
Seems to work for all the possibilities. That just verifies it is the correct FORMAT for a date field, i.e. 24-56-2023 would return valid also, but with the right input format, you can parse and verify the date is valid.
Copy link to clipboard
Copied
I made some changes and I have it "Almost" working correctly.
Changes:
The only error I am having is that when it does a replacement, it moves to the beginning of the line instead of the end. I can live with that in this case. (Ideally, I would prefer for the cursor postition to not change - neither to the beginning or the end.
Here is what I have now for the code:
win.Panel2.txt1.onChanging = function( ){
win.Panel2.ApplyBtn.enabled = false;
win.Panel2.CancelBtn.enabled = true;
win.Panel2.txt1.text = win.Panel2.txt1.text.toUpperCase();
// win.Panel2.txt1.text = win.Panel2.txt1.text.replace(/[^-0-9AVJ]+/g, '').slice(0,12);
/*
It works with the slice statement, but since it wraps back to the start, it starts overwriting the valid text. If it stayed at the end it would be fine. I chose to disable it.
*/
win.Panel2.txt1.text = win.Panel2.txt1.text.replace(/[^-0-9AVJ]+/g, '');
/*
win.Panel2.txt1.textselection = win.Panel2.txt1.text;
Out-of-Memory error if this line is enabled, which suggests the stack overrun error was caused by this line also.
*/
var valid = / <proprietary regex> /.test (win.Panel2.txt1.text);
this.graphics.backgroundColor = this.graphics.newBrush (this.graphics.BrushType.
SOLID_COLOR, valid ? [1, 1, 1, 1] : [1, 0.5, 0.5, 1]);
win.Panel2.ApplyBtn.enabled = valid;
}
Copy link to clipboard
Copied
I read your comments more carefully. Added (t) to the on-changing code and adding this after the replacement statement should work:
t=win.Panel2.txt1.text;
win.Panel2.txt1.text = '';
win.Panel2.txt1.textselection = t;
Essentially, what you are doing is saving the contents of the EditText field to a variable, clearing the field, and then pasting the previous contents back into the now-empty field.
It should work, but it's giving me the stack overrun error.
And I get the same stack overrun error if I use:
win.Panel2.txt1.textselection==="";
Although that really shouldn't work either ...
Copy link to clipboard
Copied
But it works fine with your original code in the same script for a 1 to 3 digit number and does NOT give me the stack overrun error:
win.Panel2.txt4.onChanging = function ( t) {
win.Panel2.ApplyBtn.enabled = false;
win.Panel2.CancelBtn.enabled = true;
// Predeclare brushes
var gx = win.Panel2.txt4.graphics;
var BH =
[
gx.newBrush(gx.BrushType.SOLID_COLOR, [1.0, 0.5, 0.5, 1]), // KO
gx.newBrush(gx.BrushType.SOLID_COLOR, [1.0, 1.0, 1.0, 1]) // OK
];
// Text changing handler
t = this.text;
if( !/^\d\d?\d?$/.test(t) )
{
t = t.replace(/\D+/g,'').slice(0,3);
this.text = '';
this.textselection = t;
}
t = 1||2||3===t.length;
win.Panel2.ApplyBtn.enabled = t;
this.graphics.backgroundColor = BH[+t];
}
Any ideas why it works here and not above?