Tap Forms app icon half
Tap Forms Forum text image
Blue gradient background

Exchange tips and ideas with the Tap Forms community

Search Results for 'script'

Viewing 15 results - 2,236 through 2,250 (of 3,049 total)
  • Author
    Search Results
  • #38303
    Brendan
    Keymaster

    I don’t have an exact timeline right now. I’m just trying to fix up a memory issue with running scripts. The memory isn’t being released properly. Hopefully not too much longer.

    #38298
    Sam Moffatt
    Participant

    Based on a reply I added on the time delay post I wrote a while back, I decided to clean up and better document the script.

    There are three functions:

    • delay – simple spin lock delay mechanism for blocking execution for duration milliseconds.
    • rateLimitedDelay – ensure that a script blocks for at least minTime milliseconds since the last execution tracked by key.
    • rateLimitedCallback – non-blocking method to execute callback no more frequently than minTime milliseconds since the last execution tracked by key.

    The first use case is pretty simple: call it and wait until the duration expires. The second one is for ensuring you don’t go through a code path too quickly but if you haven’t called it lately it will immediately call it. The third one is used to optionally execute callback if you haven’t executed it in the last minTime milliseconds which is useful for avoiding spamming log messages or other similar events. It enables you to put in a callback but if it’s recently executed to immediately skip it (it’s not blocking). I built this to support a REST progress interface for ensuring it didn’t post updates more frequently than once every few seconds.

    This script uses a pattern I’m adopting of including a simple test case at the end. If you execute the script directly, it’ll run the test case as a sample of how to run. This means when you import you need to also define a variable called PARENT_SCRIPT which is used to disable the test. Technically you can set PARENT_SCRIPT to any value but I use the format FORM NAME::SCRIPT NAME:

    var PARENT_SCRIPT = 'Products::Update SKUs for Product';
    document.getFormNamed('Script Manager').runScriptNamed('Rate Limiter');
    

    Test case:

    	console.log('Message 1 at ' + new Date());
    	rateLimitedDelay('test');
    	rateLimitedDelay('test');
    	rateLimitedCallback('callback', function() { console.log('Callback 1: ' + new Date()) }); 
    	console.log('Message 2 at ' + new Date());
    	delay(3000);
    	rateLimitedCallback('callback', function() { console.log('Callback 2: ' + new Date()) });
    	rateLimitedDelay('test');
    	console.log('Message 3 at ' + new Date());
    	delay(6000);
    	rateLimitedCallback('callback', function() { console.log('Callback 3: ' + new Date()) });
    	console.log('Message 4 at ' + new Date());
    	rateLimitedDelay('test');
    	console.log('Message 5 at ' + new Date());
    	rateLimitedCallback('callback', function() { console.log('Callback 4: ' + new Date()) });
    

    Here’s the full script:

    // ========== Rate Limiter Start ========== //
    // NAME: Rate Limiter
    // VERSION: 1.0.0
    // CHANGELOG:
    //   1.0.0: Initial release.
    /**
     * Rate Limiter module provides utilities to limit and delay
     * over time.
     */
    if (typeof rateLimiter === 'undefined')
    {
    
    	var rateLimiter = {};
    	
    	/**
    	 * Spin lock delay mechanism.
    	 *
    	 * This will block execution until the time limit.
    	 *
    	 * @param {integer} duration - The length of the delay in milliseconds.
    	 */ 
    	function delay(duration)
    	{
    		let now = new Date();
    		let future = now.getTime() + duration;
    		while((new Date()).getTime() < future) { }
    	}
    	
    	/**
    	 * Blocking rate limited delay mechanism.
    	 *
    	 * This will block a request until a minimum time has been elapsed.
    	 * If based on the last execution of the `key`, `minTime` milliseconds
    	 * have not elapsed, this will block until that time has elapsed.
    	 *
    	 * `key` is shared with rateLimitedCallback.
    	 *
    	 * @param {string}  key - The key to validate the last execution.
    	 * @param {integer} minTime - The minimum amount of time between execution.
    	 */
    	function rateLimitedDelay(key, minTime = 5000)
    	{
    		if (typeof rateLimiter[key] === 'undefined')
    		{
    			rateLimiter[key] = 0;
    		}
    		let now = new Date().getTime();
    		let nextExecution = rateLimiter[key] + minTime;
    		if (now < nextExecution)
    		{
    			delay(nextExecution - now);
    		}
    		rateLimiter[key] = new Date().getTime();
    	}
    
    	/** 
    	 * Non-blocking rate limited callback executor.
    	 *
    	 * This will execute `callback` only if `callback` hasn't been 
    	 * executed as `key` for at least `minTime` milliseconds since
    	 * the last execution. If it has been executed then it will not
    	 * execute this instance.
    	 *
    	 * `key` is shared with `rateLimitedDelay`.
    	 *
    	 * @param {string}   key - The key to validate the last execution.
    	 * @param {function} callback - Callback to execute.
    	 * @param {integer}  minTime - The minimum amount of time between executions.
    	 */
    	function rateLimitedCallback(key, callback, minTime = 5000)
    	{
    		if (typeof rateLimiter[key] === 'undefined')
    		{
    			rateLimiter[key] = 0;
    		}
    		let now = new Date().getTime();
    		let nextExecution = rateLimiter[key] + minTime;
    		if (now > nextExecution)
    		{
    			callback();
    			rateLimiter[key] = new Date().getTime();
    		}
    	}
    }
    
    // Tests
    if (typeof PARENT_SCRIPT === 'undefined')
    {
    	console.log('Message 1 at ' + new Date());
    	rateLimitedDelay('test');
    	rateLimitedDelay('test');
    	rateLimitedCallback('callback', function() { console.log('Callback 1: ' + new Date()) }); 
    	console.log('Message 2 at ' + new Date());
    	delay(3000);
    	rateLimitedCallback('callback', function() { console.log('Callback 2: ' + new Date()) });
    	rateLimitedDelay('test');
    	console.log('Message 3 at ' + new Date());
    	delay(6000);
    	rateLimitedCallback('callback', function() { console.log('Callback 3: ' + new Date()) });
    	console.log('Message 4 at ' + new Date());
    	rateLimitedDelay('test');
    	console.log('Message 5 at ' + new Date());
    	rateLimitedCallback('callback', function() { console.log('Callback 4: ' + new Date()) });	
    }
    // ========== Rate Limiter End ========== //
    
    #38295

    In reply to: Table row index number

    Sam Moffatt
    Participant

    That means there is no way to get a value from a parent record from the table field?

    I’ve been playing with script fields inside table fields and they’re a little quirky. Does the recalculate formulas button properly refresh script/calc fields inside a table? It doesn’t seem like they do. It also took a couple of saves before my script field would properly update to reflect changes.

    If the answer to the first question is yes, then a script like this will resync them:

    var hourly_rate = record.getFieldValue('fld-47aef2ed3cda40d18ff896959a31062c');
    var table = record.getFieldValue('fld-30d04e6103ad4ba8ac1a2149fbfde064');
    
    for(target of table)
    {
    	target.setFieldValue('fld-b7aca38a1e83483bbe94c7b6cf850f18', hourly_rate);
    }

    This is a little heavy because it runs every time a change is made to either field (or for the table, any column of any row) but it does work.

    #38292

    In reply to: Table row index number

    Brendan
    Keymaster

    So there’s something different that happens for Table field sub-fields and sub-records when running a script. When you run the script in the Script Editor, record actually does refer to the parent form’s currently selected record.

    However, when you modify a sub-field value for a sub-record within a Table field in which you have a Script Field, Tap Forms will use the correct Table field’s record in place of the record reference in the script.

    This works because when you type in a value into a sub-field of your Table field, Tap Forms knows what that currently selected record is. The JSContext is updated with the currently selected Table field sub-record and the script is evaluated.

    Hope that clears it up.

    Sorry I missed seeing the reply to this message.

    #38269
    Martin Inchley
    Participant

    OK, I have some progress – though not an answer for why the scripts suddenly stopped working.

    The second of the two scripts above toggles the checkbox if the Payment Date field is filled or emptied. I’ve been treating the checkbox field as a Boolean and assuming that using True/False or 1/0 was immaterial.

    But if I use “1” and “0” for the value to be inserted, rather than “True” or “False”, then the checkbox responds correctly in the LtF field on the linked form.

    I created a new check mark field and used a script to show its value. Before any action, the console reported its value as “False”. But checking it gave a value of “1”. Unchecking it gave “0”. Thereafter it stuck with 1 and 0.

    Since my scripts worked initially on their own form, it seems that these checkbox fields will normally work OK with “True” and “False”. But (so far), they need to be using 1 and 0 to be consistent within my LtF field. I’ve tested them both with and without quote marks around the digit – both work fine.

    #38266
    Martin Inchley
    Participant

    Hm…

    Clicking to another record – in either the main form or the Link-to-Form field – makes no difference.

    I’ve written a script to present the info in the console – still the checkbox stays unchecked. I’ve checked the real status of the checkbox field with a calculation field – it agrees with the script result in the console in showing that the checkbox value is true / 1, even though the checkbox is not displaying that in the Link-to-Form field.

    And then…

    Playing with the LtF field, I removed certain fields from showing. When I saved that, TF crashed and quit (I’m used to that – hopefully the next release will have fixed it). When I re-started and went to the native form (not the linked one with the LtF field), the scripts (in my first post above) for setting the date and toggling the checkbox no longer worked. I did a proper clean re-start – no effect.

    What got them working again was bringing up the console. As soon as I did that, they were fine. Grrrr…

    So I’m less than happy and, I’m afraid, beginning to lose confidence in the system as a solution for a target user who just needs everything to work. Financial details are a sensitive area…

    #38253
    Martin Inchley
    Participant

    I have a checkbox field “Paid”, and a date field “Payment Date”, with their interactions controlled by two script fields. Checking or unchecking “Paid” enters the current date in “Payment Date”, or empties it. Manually entering a date in “Payment Date” checks “Paid”. Deleting the date unchecks “Paid”. Editing the date leaves it alone.

    Everything works fine on their own form. Issues arise when I access them through a Link-to-Form field in another form. Some of the behaviour is consistent, some not. One particular oddity is this:

    Accessing these records via the Link-to-Form field, editing the date field results in the checkbox being unchecked, which shouldn’t happen. But when I go to the native form I find that the checkbox is still checked. Back to the linked form – the box is certainly unchecked there.

    So my investigation of how to get the scripting to work across forms is hampered by this anomaly. Is the box checked or not? I’d appreciate some advice on how to approach this problem.

    Here are the scripts from the control fields, in case they shed any light on things:

    //Responds to the "Paid" checkbox field being toggled, 
    //by setting or emptying the Payment Date field
    
    var paid_id = 'fld-4f9f9ac525964be99ca478bdaee47144';
    var paid = record.getFieldValue(paid_id);
    
    var thisDate = new Date(); // put the current date into <thisdate>
    
    if (paid) {
        
    // Use a special version of "setFieldValue" to prevent triggering the Toggle script
        record.setFieldValue('fld-cf95ec11043d4d2b8dc51d2ab93f177b', thisDate, false);
    } else {
        record.setFieldValue('fld-cf95ec11043d4d2b8dc51d2ab93f177b', "", false);
    }
    //Responds to the "Payment Date" field getting an entry or being emptied, 
    //by toggling the "Paid" checkbox field
    
    var payment_date_id = 'fld-cf95ec11043d4d2b8dc51d2ab93f177b';
    var payment_date = record.getFieldValue(payment_date_id);
    
    if (payment_date) {
        record.setFieldValue('fld-4f9f9ac525964be99ca478bdaee47144', "true", false);
    } else {
        record.setFieldValue('fld-4f9f9ac525964be99ca478bdaee47144', "false", false);
    }

    Any observations very welcome. And, just to note, clicking the refresh button seems to make no difference at any point.

    #38250
    Brendan
    Keymaster

    Can you email me your form template so I can examine it?

    The multi-column list views pick lists really only work properly with single-value Pick Lists. It’s probably something I should look into to see if I can get it to show multiple values at some point.

    There are certain field types that you can’t edit though, such as Calculation fields, Script fields, etc.

    Also, do you have access controls enabled and the form locked? That can prevent editing.

    #38227
    Eddy
    Participant

    Thanks Brendan, for your promt answer!

    I found a way to generate mails from Tapforms to MacOS by using Javascript.

    Like this:

    Mail = Application('com.apple.Mail')
    message = Mail.OutgoingMessage().make()
    message.visible = true
    message.toRecipients.push(Mail.Recipient({ address: "foo.bar@example.com" }))
    message.subject = "Testing JXA"
    message.content = "Foo bar baz"
    
    attachment = Mail.Attachment({ fileName: "/Users/myname/Desktop/test.pdf" })
    message.attachments.push(attachment)

    Or, even better by using the PATH-Command:

    Mail = Application('com.apple.Mail')
    message = Mail.OutgoingMessage().make()
    message.visible = true
    message.toRecipients.push(Mail.Recipient({ address: "foo.bar@example.com" }))
    message.subject = "Testing JXA"
    message.content = "Foo bar baz"
    
    attachment = Mail.Attachment({ fileName: Path("/Users/myname/Desktop/if the file has spaces in it test.pdf") })
    message.attachments.push(attachment)


    The question is now:
    How can I address the file / path inside Tapforms correctly?

    Thanks in advance:

    Eddy

    #38214
    Brendan
    Keymaster

    But also you could go into the Multi-Column List View mode, select all the values from one field, then click in the first cell of another field and paste. All the values will be pasted in order into the other field. Make a backup first in case it doesn’t do what you expect. Or the script that Sam posted.

    There’s also the Fill Down function too which can be used to fill a value from the selected cell into all the selected cell below it.

    #38209
    Sam Moffatt
    Participant

    Use the script editor to grab the two fields ID’s and their values. For your old field, if it has a set of values then you need to translate them to the new value. I’d use a switch statement here on your old field to map it over, something like:

    let oldValue = record.getFieldValue('fld-1234');
    let newValue = record.getFieldValue('fld-4321');
    
    switch(oldValue)
    {
      case 'Swimming':
         record.setFieldValue(newValue + ', swi1', 'fld-4321');
         break;
    }

    Then for your new field, if you have a multipick list then get the current value and then use JavaScript to append “, <new value>”. It’ll look a little weird ofr empty fields but shouldn’t hurt anything. If it is a single value pick list, then you can just reset the field value.

    If you create a form script, you can use form.getRecords to get all of the records and then process them one at a time. A pattern I use is to create a function script that accepts a record field and then label it as currentRecord to avoid getting confused in the scope. Then I can test using a single record or later on put it in a loop.

    #38198
    Martin Inchley
    Participant

    Good spot, Daniel!

    Unfortunately, adding it made no difference.:(

    And the other script that makes the value appear when I trigger it manually makes no difference when I call it from within my Script Field script.

    #38197
    Daniel Leu
    Participant

    I think you are missing a document.saveAllChanges(); at the end of the script.

    Cheers, Daniel

    ---
    See https://lab.danielleu.com/tapformspro/ for scripts and tips&tricks

    #38196
    Martin Inchley
    Participant

    Late night extra extra:

    In this scenario, with the Paid box checked, the Calc field showing the Paid Date field is not empty, but the Paid Date field not actually showing anything – if I run a script to fetch the contents of the Paid Date field, the date suddenly shows up in it!

    Similarly when I’m emptying it – the emptying only shows when I run a script to get the contents (and, worryingly, occasionally not even then).

    Is this likely to be a time-lag in saving the data, or is there perhaps some trigger that’s not firing?

    #38194
    Martin Inchley
    Participant

    Extra evidence:

    Tinkering with this I set up a Calc field to hold 1 if the Payment Date field was not empty, and 0 if it was. After the Script field has been triggered by checking the Paid tick-box, this new Calc field is registering 1, even though the Payment Date field appears to have no content.

    There would appear to be something in it, stirring beneath the waters…

Viewing 15 results - 2,236 through 2,250 (of 3,049 total)