Form Examples
Well-designed Omni Automation actions often prompt and query the user to provide data, or make a selection, choice, or decision as to how the script should proceed. The Form class enables scripts to gather information from the user, by presenting dialogs containing data expressed using standard interface elements: text input fields, date pickers, option menus, and checkboxes.
The following example forms address common scripting scenarios and can be easily added to your Omni Automaton action templates and customized to suit the individual script requirements.
Single Text Input
In this form example, the user is prompted to provide text input. The form’s validation function ensures that the approval button will not be enabled without the user first providing such input.
Basic Single Text Input | ||
01 | var textInputField = new Form.Field.String( | |
02 | "textInput", | |
03 | "Field Label", | |
04 | null | |
05 | ) | |
06 | ||
07 | var inputForm = new Form() | |
08 | inputForm.addField(textInputField) | |
09 | var formPrompt = "Form prompt:" | |
10 | var buttonTitle = "Continue" | |
11 | formPromise = inputForm.show(formPrompt,buttonTitle) | |
12 | ||
13 | inputForm.validate = function(formObject){ | |
14 | inputText = formObject.values['textInput'] | |
15 | return ((!inputText)?false:true) | |
16 | } | |
17 | ||
18 | formPromise.then(function(formObject){ | |
19 | var textValue = formObject.values['textInput'] | |
20 | // PROCESSING STATEMENTS GO HERE | |
21 | }) | |
22 | ||
23 | formPromise.catch(function(err){ | |
24 | console.log("form cancelled", err.message) | |
25 | }) |
URL Input Form
This form presents a single text input field that accepts a text string that can be made into a valid URL object:
URL Input Form (basic check) | ||
01 | // CREATE FORM FOR GATHERING USER INPUT | |
02 | var inputForm = new Form() | |
03 | ||
04 | // CREATE TEXT FIELD | |
05 | var textField = new Form.Field.String( | |
06 | "textInput", | |
07 | null, | |
08 | null | |
09 | ) | |
10 | ||
11 | // ADD THE FIELDS TO THE FORM | |
12 | inputForm.addField(textField) | |
13 | ||
14 | // PRESENT THE FORM TO THE USER | |
15 | var formPrompt = "Enter the full URL:" | |
16 | var formPromise = inputForm.show(formPrompt,"Continue") | |
17 | ||
18 | // VALIDATE THE USER INPUT | |
19 | inputForm.validate = function(formObject){ | |
20 | var textValue = formObject.values["textInput"] | |
21 | return (textValue && URL.fromString(textValue)) ? true : false | |
22 | } | |
23 | ||
24 | // PROCESSING USING THE DATA EXTRACTED FROM THE FORM | |
25 | formPromise.then(function(formObject){ | |
26 | var textValue = formObject.values["textInput"] | |
27 | var url = URL.fromString(textValue) | |
28 | // PROCESSING STATEMENTS GO HERE | |
29 | }) | |
30 | ||
31 | // PROMISE FUNCTION CALLED UPON FORM CANCELLATION | |
32 | formPromise.catch(function(err){ | |
33 | console.log("form cancelled", err.message) | |
34 | }) | |
35 |
To limit the validation to only URLs beginning with specific schema:
URL Input Form (check schema) | ||
01 | // CREATE FORM FOR GATHERING USER INPUT | |
02 | var inputForm = new Form() | |
03 | ||
04 | // CREATE TEXT FIELD | |
05 | var textField = new Form.Field.String( | |
06 | "textInput", | |
07 | null, | |
08 | null | |
09 | ) | |
10 | ||
11 | // ADD THE FIELDS TO THE FORM | |
12 | inputForm.addField(textField) | |
13 | ||
14 | // PRESENT THE FORM TO THE USER | |
15 | var formPrompt = "Enter the full URL:" | |
16 | var formPromise = inputForm.show(formPrompt,"Continue") | |
17 | ||
18 | // VALIDATE THE USER INPUT | |
19 | inputForm.validate = function(formObject){ | |
20 | var textValue = formObject.values["textInput"] | |
21 | var textStatus = (textValue && textValue.length > 0) ? true:false | |
22 | var schema = [ | |
23 | "http://", | |
24 | "https://", | |
25 | "mailto:", | |
26 | "omnioutliner://", | |
27 | "omniplan://", | |
28 | "omnigraffle://", | |
29 | "omnifocus://" | |
30 | ] | |
31 | if (textStatus){ | |
32 | var i; | |
33 | for (i = 0; i < schema.length; i++) { | |
34 | if (textValue.startsWith(schema[i], 0) && URL.fromString(textValue)){return true} | |
35 | } | |
36 | } | |
37 | return false | |
38 | } | |
39 | ||
40 | // PROCESSING USING THE DATA EXTRACTED FROM THE FORM | |
41 | formPromise.then(function(formObject){ | |
42 | var textValue = formObject.values["textInput"] | |
43 | var url = URL.fromString(textValue) | |
44 | // PROCESSING STATEMENTS GO HERE | |
45 | }) | |
46 | ||
47 | // PROMISE FUNCTION CALLED UPON FORM CANCELLATION | |
48 | formPromise.catch(function(err){ | |
49 | console.log("form cancelled", err.message) | |
50 | }) |
Enter an Existing OmniFocus Tag
Here’s an example form containing a single text input field in which the user enters the title of an existing tag.
Enter Title of Existing OmniFocus Tag | ||
01 | try { | |
02 | var tagNames = new Array() | |
03 | tags.apply(tag => tagNames.push(tag.name)) | |
04 | if (tagNames.length === 0){throw Error("No tags in database")} | |
05 | ||
06 | var textInputField = new Form.Field.String( | |
07 | "textInput", | |
08 | "Tag", | |
09 | null | |
10 | ) | |
11 | ||
12 | var inputForm = new Form() | |
13 | inputForm.addField(textInputField) | |
14 | var formPrompt = "Enter the title of an existing tag:" | |
15 | var buttonTitle = "Continue" | |
16 | ||
17 | formPromise = inputForm.show(formPrompt,buttonTitle) | |
18 | ||
19 | inputForm.validate = function(formObject){ | |
20 | var inputText = formObject.values['textInput'] | |
21 | var textStatus = ((!inputText)?false:true) | |
22 | return (textStatus && tagNames.includes(inputText)) ? true : false | |
23 | } | |
24 | ||
25 | formPromise.then(function(formObject){ | |
26 | var tagTitle = formObject.values['textInput'] | |
27 | var targetTag = null | |
28 | tags.apply(function(tag){ | |
29 | if(tag.name == tagTitle){ | |
30 | targetTag = tag | |
31 | return ApplyResult.Stop | |
32 | } | |
33 | }) | |
34 | console.log('tag: ',targetTag) | |
35 | ||
36 | // PROCESS STATEMENTS | |
37 | }) | |
38 | ||
39 | formPromise.catch(function(err){ | |
40 | console.error("form cancelled", err.message) | |
41 | }) | |
42 | } | |
43 | catch (err){ | |
44 | new Alert('SCRIPT ERROR', err.message).show() | |
45 | } |
Text Input with Mandatory Checkbox
In this example form, the dialog cannot be approved without the user providing text and selecting the provided checkbox.
Mandatory Text and Approval | ||
01 | var textInputField = new Form.Field.String( | |
02 | "textInput", | |
03 | "Field Label", | |
04 | null | |
05 | ) | |
06 | ||
07 | var checkSwitchField = new Form.Field.Checkbox( | |
08 | "checkboxSwitch", | |
09 | "I accept the standard terms and conditions", | |
10 | null | |
11 | ) | |
12 | ||
13 | var inputForm = new Form() | |
14 | inputForm.addField(textInputField) | |
15 | inputForm.addField(checkSwitchField) | |
16 | var formPrompt = "Form prompt:" | |
17 | var buttonTitle = "Continue" | |
18 | formPromise = inputForm.show(formPrompt,buttonTitle) | |
19 | ||
20 | inputForm.validate = function(formObject){ | |
21 | var inputText = formObject.values['textInput'] | |
22 | var textStatus = ((!inputText)?false:true) | |
23 | var checkboxStatus = formObject.values['checkboxSwitch'] | |
24 | return (textStatus && checkboxStatus) | |
25 | } | |
26 | ||
27 | formPromise.then(function(formObject){ | |
28 | var textValue = formObject.values['textInput'] | |
29 | console.log('textValue: ',textValue) | |
30 | }) | |
31 | ||
32 | formPromise.catch(function(err){ | |
33 | console.error("form cancelled", err.message) | |
34 | }) |
Multiple Mandatory Text Input
In this example, the user must supply textual input in all of the provided fields before the approval button will be enabled. NOTE: to change a text field to “optional input” simply remove its status variable from the statement on line 38.
Multiple mandatory Text Fields | ||
01 | var textInputField01 = new Form.Field.String( | |
02 | "textInput01", | |
03 | "Field Label 1", | |
04 | null | |
05 | ) | |
06 | ||
07 | var textInputField02 = new Form.Field.String( | |
08 | "textInput02", | |
09 | "Field Label 2", | |
10 | null | |
11 | ) | |
12 | ||
13 | var textInputField03 = new Form.Field.String( | |
14 | "textInput03", | |
15 | "Field Label 3", | |
16 | null | |
17 | ) | |
18 | ||
19 | var inputForm = new Form() | |
20 | inputForm.addField(textInputField01) | |
21 | inputForm.addField(textInputField02) | |
22 | inputForm.addField(textInputField03) | |
23 | var formPrompt = "Form prompt:" | |
24 | var buttonTitle = "Continue" | |
25 | formPromise = inputForm.show(formPrompt,buttonTitle) | |
26 | ||
27 | inputForm.validate = function(formObject){ | |
28 | var inputText01 = formObject.values['textInput01'] | |
29 | var inputText01Status = (!inputText01)?false:true | |
30 | ||
31 | var inputText02 = formObject.values['textInput02'] | |
32 | var inputText02Status = (!inputText02)?false:true | |
33 | ||
34 | var inputText03 = formObject.values['textInput03'] | |
35 | var inputText03Status = (!inputText03)?false:true | |
36 | ||
37 | // ALL CONDITIONS MUST BE TRUE TO VALIDATE | |
38 | var validation = (inputText01Status && inputText02Status && inputText03Status) ? true:false | |
39 | return validation | |
40 | } | |
41 | ||
42 | formPromise.then(function(formObject){ | |
43 | var textValue01 = formObject.values['textInput01'] | |
44 | var textValue02 = formObject.values['textInput02'] | |
45 | var textValue03 = formObject.values['textInput03'] | |
46 | // PROCESSING STATEMENTS GO HERE | |
47 | }) | |
48 | ||
49 | formPromise.catch(function(err){ | |
50 | console.log("form cancelled", err.message) | |
51 | }) |
Text Input is Integer
In this example, the user must enter an integer (whole number):
Input Integer | ||
01 | var textInputField = new Form.Field.String( | |
02 | "textInput", | |
03 | null, | |
04 | null | |
05 | ) | |
06 | ||
07 | var inputForm = new Form() | |
08 | inputForm.addField(textInputField) | |
09 | var formPrompt = "Enter a whole number:" | |
10 | var buttonTitle = "Continue" | |
11 | var formPromise = inputForm.show(formPrompt,buttonTitle) | |
12 | ||
13 | inputForm.validate = function(formObject){ | |
14 | inputText = formObject.values['textInput'] | |
15 | if (!inputText) {return false} | |
16 | var isnum = /^[0-9]+$/i.test(inputText) | |
17 | return isnum | |
18 | } | |
19 | ||
20 | formPromise.then(function(formObject){ | |
21 | var textValue = formObject.values['textInput'] | |
22 | console.log('textValue: ',textValue) | |
23 | }) | |
24 | ||
25 | formPromise.catch(function(err){ | |
26 | console.error("form cancelled", err.message) | |
27 | }) |
Text Input is Integer within Range
In this example, the user must enter an integer (whole number) that falls between the specified range, before the approval button is enabled. You can set the range by changing the values of the first two statements of the form.
Integer within Range | ||
01 | var minValue = 1 | |
02 | var maxValue = 34 | |
03 | ||
04 | var textInputField = new Form.Field.String( | |
05 | "textInput", | |
06 | String("(" + minValue + "-" + maxValue + ")"), | |
07 | null | |
08 | ) | |
09 | ||
10 | var inputForm = new Form() | |
11 | inputForm.addField(textInputField) | |
12 | var formPrompt = "Enter a whole number:" | |
13 | var buttonTitle = "Continue" | |
14 | var formPromise = inputForm.show(formPrompt,buttonTitle) | |
15 | ||
16 | inputForm.validate = function(formObject){ | |
17 | inputText = formObject.values['textInput'] | |
18 | if (!inputText) {return false} | |
19 | var isnum = /^[0-9]+$/i.test(inputText) | |
20 | if (isnum){ | |
21 | var intValue = parseInt(inputText) | |
22 | return ((intValue <= maxValue && intValue >= minValue) ? true:false) | |
23 | } | |
24 | return false | |
25 | } | |
26 | ||
27 | formPromise.then(function(formObject){ | |
28 | var textValue = formObject.values['textInput'] | |
29 | console.log('textValue: ',textValue) | |
30 | }) | |
31 | ||
32 | formPromise.catch(function(err){ | |
33 | console.error("form cancelled", err.message) | |
34 | }) |
Text Input with Only Alpha-Numeric Characters
In this form example, the text input field will only accept alpha numeric characters (a-z A-Z and 0-9).
Alpha-Numeric Input | ||
01 | var textInputField = new Form.Field.String( | |
02 | "textInput", | |
03 | "(a-z,0-9)", | |
04 | null | |
05 | ) | |
06 | ||
07 | var inputForm = new Form() | |
08 | inputForm.addField(textInputField) | |
09 | var formPrompt = "Enter an alpha-numeric string:" | |
10 | var buttonTitle = "Continue" | |
11 | formPromise = inputForm.show(formPrompt,buttonTitle) | |
12 | ||
13 | inputForm.validate = function(formObject){ | |
14 | var textValue = formObject.values['textInput'] | |
15 | if(!textValue){return false} | |
16 | return (/^[a-z0-9]+$/i.test(textValue)) | |
17 | } | |
18 | ||
19 | formPromise.then(function(formObject){ | |
20 | var textValue = formObject.values['textInput'] | |
21 | // PROCESSING STATEMENTS GO HERE | |
22 | }) | |
23 | ||
24 | formPromise.catch(function(err){ | |
25 | console.error("form cancelled", err.message) | |
26 | }) |
Enter Valid eMail Address
In this form example, the approval button will only become enabled when a fully-formed email address is entered in the text input field. For example: bob123@omniapps.uk or carla486@outlinerworld.com
Input Valid eMail Address | ||
01 | var textInputField = new Form.Field.String( | |
02 | "textInput", | |
03 | null, | |
04 | null | |
05 | ) | |
06 | ||
07 | var inputForm = new Form() | |
08 | inputForm.addField(textInputField) | |
09 | var formPrompt = "Enter an eMail address:" | |
10 | var buttonTitle = "Continue" | |
11 | formPromise = inputForm.show(formPrompt,buttonTitle) | |
12 | ||
13 | inputForm.validate = function(formObject){ | |
14 | var textValue = formObject.values['textInput'] | |
15 | if(!textValue){return false} | |
16 | if (/^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/.test(textValue)){ | |
17 | return true | |
18 | } else { | |
19 | throw "ERROR: not a valid eMail address." | |
20 | } | |
21 | } | |
22 | ||
23 | formPromise.then(function(formObject){ | |
24 | var textValue = formObject.values['textInput'] | |
25 | // PROCESSING STATEMENTS GO HERE | |
26 | }) | |
27 | ||
28 | formPromise.catch(function(err){ | |
29 | console.error("form cancelled", err.message) | |
30 | }) |
Basic Options Menu
The following form will display a single options menu:
Basic Options Menu | ||
01 | var menuItems = ["Sunday", | |
02 | var menuIndexes = [0,1,2,3,4,5,6] | |
03 | ||
04 | var menuElement = new Form.Field.Option( | |
05 | "menuElement", | |
06 | null, | |
07 | menuIndexes, | |
08 | menuItems, | |
09 | 0 | |
10 | ) | |
11 | ||
12 | var inputForm = new Form() | |
13 | inputForm.addField(menuElement) | |
14 | var formPrompt = "Choose one of the items:" | |
15 | var buttonTitle = "Continue" | |
16 | formPromise = inputForm.show(formPrompt,buttonTitle) | |
17 | ||
18 | inputForm.validate = function(formObject){ | |
19 | return true | |
20 | } | |
21 | ||
22 | inputForm.then(function(formObject){ | |
23 | var menuIndex = formObject.values['menuElement'] | |
24 | var chosenItem = menuItems[menuIndex] | |
25 | console.log('Chosen item: ',chosenItem) | |
26 | }) | |
27 | ||
28 | inputForm.catch(function(err){ | |
29 | console.error("form cancelled", err.message) | |
30 | }) |
(OmniFocus) Menu of Selected Tasks
In this form example (shown within an enclosing Omni Automation action), a popup menu of the titles of the selected OmniFocus tasks is displayed. When the user selects a task from the menu, its corresponding task object can be processed by the script (line 43).
Action with Form containing Menu of Selected Tasks | ||
01 | /*{ | |
02 | "type": "action", | |
03 | "targets": ["omnifocus"], | |
04 | "author": "Otto Automator", | |
05 | "identifier": "com.omni-automation.of.menu-of-selected-tasks", | |
06 | "version": "1.0", | |
07 | "description": "This action demonstrates how to create a form that displays a menu of the selected tasks of which the user can choose one to process.", | |
08 | "label": "Show Menu of Selected Tasks", | |
09 | "shortLabel": "Selected Tasks" | |
10 | }*/ | |
11 | (() => { | |
12 | var action = new PlugIn.Action(function(selection, sender){ | |
13 | // action code | |
14 | // selection options: tasks, projects, folders, tags | |
15 | ||
16 | var tasks = selection.tasks | |
17 | var taskNames = tasks.map(task => {return task.name}) | |
18 | var taskRefs = new Array() | |
19 | taskNames.forEach(function (task,index){ | |
20 | taskRefs.push(index) | |
21 | }) | |
22 | ||
23 | var selectedTasksMenu = new Form.Field.Option( | |
24 | "selectedTask", | |
25 | null, | |
26 | taskRefs, | |
27 | taskNames, | |
28 | 0 | |
29 | ) | |
30 | ||
31 | var inputForm = new Form() | |
32 | inputForm.addField(selectedTasksMenu) | |
33 | var formPrompt = "Choose one the selected tasks:" | |
34 | var buttonTitle = "Continue" | |
35 | formPromise = inputForm.show(formPrompt,buttonTitle) | |
36 | ||
37 | inputForm.validate = function(formObject){ | |
38 | return true | |
39 | } | |
40 | ||
41 | formPromise.then(function(formObject){ | |
42 | var taskIndex = formObject.values['selectedTask'] | |
43 | console.log('task: ',tasks[taskIndex]) | |
44 | }) | |
45 | ||
46 | formPromise.catch(function(err){ | |
47 | console.error("form cancelled", err.message) | |
48 | }) | |
49 | }); | |
50 | ||
51 | action.validate = function(selection, sender){ | |
52 | // validation code | |
53 | // selection options: tasks, projects, folders, tags | |
54 | return (selection.tasks.length > 0) | |
55 | }; | |
56 | ||
57 | return action; | |
58 | })(); |
(OmniFocus) Menu of Active Inbox Tasks
In this form example (shown within an enclosing Omni Automation action), a popup menu of the titles of the OmniFocus inbox tasks is displayed. When the user selects a task from the menu, its corresponding task object can be processed by the script (line 41).
Action with Form containing Menu of Inbox Tasks | ||
01 | /*{ | |
02 | "type": "action", | |
03 | "targets": ["omnifocus"], | |
04 | "author": "Otto Automator", | |
05 | "identifier": "com.omni-automation.of.menu-of-inbox-tasks", | |
06 | "version": "1.1", | |
07 | "description": "This action demonstrates how to create a form that displays a menu of the active inbox tasks from which the user can choose one to process.", | |
08 | "label": "Choose Inbox Task", | |
09 | "shortLabel": "Choose Inbox Task" | |
10 | }*/ | |
11 | (() => { | |
12 | var action = new PlugIn.Action(function(selection, sender){ | |
13 | // action code | |
14 | ||
15 | var activeTasks = new Array() | |
16 | inbox.apply((task)=>{ | |
17 | if (task.taskStatus === Task.Status.Available){ | |
18 | activeTasks.push(task) | |
19 | } | |
20 | }) | |
21 | ||
22 | activeTasks.sort((a, b) => { | |
23 | var x = a.name.toLowerCase(); | |
24 | var y = b.name.toLowerCase(); | |
25 | if (x < y) {return -1;} | |
26 | if (x > y) {return 1;} | |
27 | return 0; | |
28 | }) | |
29 | ||
30 | var taskNames = activeTasks.map(task => {return task.name}) | |
31 | var taskRefs = new Array() | |
32 | taskNames.forEach(function (task,index){ | |
33 | taskRefs.push(index) | |
34 | }) | |
35 | ||
36 | var inboxTasksMenu = new Form.Field.Option( | |
37 | "inboxTask", | |
38 | null, | |
39 | taskRefs, | |
40 | taskNames, | |
41 | 0 | |
42 | ) | |
43 | ||
44 | var inputForm = new Form() | |
45 | inputForm.addField(inboxTasksMenu) | |
46 | var formPrompt = "Choose one of the inbox tasks:" | |
47 | var buttonTitle = "Continue" | |
48 | formPromise = inputForm.show(formPrompt,buttonTitle) | |
49 | ||
50 | inputForm.validate = function(formObject){ | |
51 | return true | |
52 | } | |
53 | ||
54 | formPromise.then(function(formObject){ | |
55 | var taskIndex = formObject.values['inboxTask'] | |
56 | var task = activeTasks[taskIndex] | |
57 | console.log('task: ',task) | |
58 | var id = task.id.primaryKey | |
59 | URL.fromString('omnifocus:///task/' + id).open() | |
60 | }) | |
61 | ||
62 | formPromise.catch(function(err){ | |
63 | console.error("form cancelled", err.message) | |
64 | }) | |
65 | }); | |
66 | ||
67 | action.validate = function(selection, sender){ | |
68 | // validation code | |
69 | var activeTasks = new Array() | |
70 | inbox.apply((task)=>{ | |
71 | if (task.taskStatus === Task.Status.Available){ | |
72 | activeTasks.push(task) | |
73 | } | |
74 | }) | |
75 | return (activeTasks.length > 0) | |
76 | }; | |
77 | ||
78 | return action; | |
79 | })(); |
(OmniFocus) Menu of Projects with Specific Tag
In the following form example, a selection menu is displayed that lists projects that are tagged with a pre-specified tag.
To use the form, change the name of the target tag from “Camera-Ready” to the title of the tag you wish to specify:
Select Project with Specific Tag | ||
01 | try { | |
02 | var targetTagName = "Camera-Ready" | |
03 | var targetTag = null | |
04 | tags.apply(function(tag){ | |
05 | if(tag.name == targetTagName){ | |
06 | targetTag = tag | |
07 | return ApplyResult.Stop | |
08 | } | |
09 | }) | |
10 | ||
11 | if (!targetTag){ | |
12 | var errMsg1 = "There is no tag titled: ”" + targetTagName + "”" | |
13 | throw new Error(errMsg1) | |
14 | } | |
15 | ||
16 | var projectsWithTag = targetTag.projects | |
17 | if (projectsWithTag.length === 0){ | |
18 | var errMsg2 = "No projects are tagged with: ”" + targetTagName + "”" | |
19 | throw new Error(errMsg2) | |
20 | } | |
21 | ||
22 | projectsWithTag.sort((a, b) => { | |
23 | var x = a.name.toLowerCase() | |
24 | var y = b.name.toLowerCase() | |
25 | if (x < y) {return -1} | |
26 | if (x > y) {return 1} | |
27 | return 0 | |
28 | }) | |
29 | ||
30 | var projectNames = projectsWithTag.map(project => {return project.name}) | |
31 | var projectIndxs = new Array() | |
32 | projectNames.forEach(function(name, index){ | |
33 | projectIndxs.push(index) | |
34 | }) | |
35 | ||
36 | var projectsMenu = new Form.Field.Option( | |
37 | "projectMenu", | |
38 | null, | |
39 | projectIndxs, | |
40 | projectNames, | |
41 | 0 | |
42 | ) | |
43 | ||
44 | var inputForm = new Form() | |
45 | inputForm.addField(projectsMenu) | |
46 | var formPrompt = "Choose a “" + targetTagName + "” project:" | |
47 | var buttonTitle = "Continue" | |
48 | formPromise = inputForm.show(formPrompt,buttonTitle) | |
49 | ||
50 | inputForm.validate = function(formObject){ | |
51 | return true | |
52 | } | |
53 | ||
54 | formPromise.then(function(formObject){ | |
55 | var projectIndex = formObject.values['projectMenu'] | |
56 | var chosenProject = projectsWithTag[projectIndex] | |
57 | console.log(chosenProject) | |
58 | }) | |
59 | ||
60 | formPromise.catch(function(err){ | |
61 | console.error("form cancelled", err.message) | |
62 | }) | |
63 | } | |
64 | catch(err){ | |
65 | new Alert("ERROR", err.message).show() | |
66 | } |
(OmniFocus) Menu of Built-in Perspectives
This plug-in will display a form containing a list of the built-in perspectives from which the user selects the one to be displayed.
NOTE: Since the function code changes the current view of the OmniFocus window, an instance of the Timer class (lines 22-24) is used to delay the code processing until the sheet (macOS) or dialog (iPadOS, iOS) are fully dismissed.
Display Chosen Perspective | ||
01 | var perspectiveMenu = new Form.Field.Option( | |
02 | "perspective", | |
03 | "Perspective", | |
04 | Perspective.BuiltIn.all, | |
05 | null, | |
06 | Perspective.BuiltIn.all[0] | |
07 | ) | |
08 | ||
09 | var inputForm = new Form() | |
10 | inputForm.addField(perspectiveMenu) | |
11 | var formPrompt = "Choose the perspective:" | |
12 | var buttonTitle = "Continue" | |
13 | formPromise = inputForm.show(formPrompt,buttonTitle) | |
14 | ||
15 | inputForm.validate = function(formObject){ | |
16 | return true | |
17 | } | |
18 | ||
19 | formPromise.then(function(formObject){ | |
20 | var chosenPerspective = formObject.values['perspective'] | |
21 | console.log(chosenPerspective) | |
22 | Timer.once(1,function(timer){ | |
23 | document.windows[0].perspective = chosenPerspective | |
24 | }) | |
25 | }) | |
26 | ||
27 | formPromise.catch(function(err){ | |
28 | console.error("form cancelled", err.message) | |
29 | }) |
Enter Date after Today
In the following example form, the user is prompted to provide a date after today.
NOTE: Date fields will accept a variety of shortcut values. Reference the page on Date Input Fields to learn all of the available date referencing options.
Enter Date after Today | ||
01 | today = new Date(new Date().setHours(0,0,0,0)) | |
02 | var tomorrow = new Date(today.setDate(today.getDate() + 1)) | |
03 | ||
04 | dateInputField = new Form.Field.Date( | |
05 | "dateInput", | |
06 | null, | |
07 | null | |
08 | ) | |
09 | ||
10 | inputForm = new Form() | |
11 | inputForm.addField(dateInputField) | |
12 | formPrompt = "Enter a date/time after today:" | |
13 | buttonTitle = "Continue" | |
14 | formPromise = inputForm.show(formPrompt, buttonTitle) | |
15 | ||
16 | inputForm.validate = function(formObject){ | |
17 | dateInput = formObject.values["dateInput"] | |
18 | var status = (dateInput && dateInput >= tomorrow) ? true:false | |
19 | console.log(status) | |
20 | return status | |
21 | } | |
22 | ||
23 | formPromise.then(function(formObject){ | |
24 | dateInput = formObject.values["dateInput"] | |
25 | console.log(dateInput) | |
26 | }) | |
27 | ||
28 | formPromise.catch(function(err){ | |
29 | console.log("form cancelled", err.message) | |
30 | }) |
Select Date from Menus
In the following form example, the user selects a date using the year, month, and day menus:
Select Date from Menus | ||
01 | var locale = "en-us" | |
02 | var monthNames = [] | |
03 | for (i = 0; i < 12; i++) { | |
04 | var objDate = new Date() | |
05 | objDate.setDate(1) | |
06 | objDate.setMonth(i) | |
07 | monthName = objDate.toLocaleString(locale,{month:"long"}) | |
08 | monthNames.push(monthName) | |
09 | } | |
10 | ||
11 | var now = new Date() | |
12 | var currentYear = now.getFullYear() | |
13 | var yearNames = new Array() | |
14 | var yearIndexes = new Array() | |
15 | for (i = 0; i < 4; i++) { | |
16 | yearNames.push(String(currentYear + i)) | |
17 | yearIndexes.push(i) | |
18 | } | |
19 | ||
20 | var dayCount = new Date(currentYear, 1, 0).getDate() | |
21 | var dayIndexes = new Array() | |
22 | var dayIndexStrings = new Array() | |
23 | for (var i = 0; i < dayCount; i++){ | |
24 | dayIndexes.push(i) | |
25 | dayIndexStrings.push(String(i + 1)) | |
26 | } | |
27 | ||
28 | var inputForm = new Form() | |
29 | ||
30 | var yearMenu = new Form.Field.Option( | |
31 | "yearMenu", | |
32 | "Year", | |
33 | yearIndexes, | |
34 | yearNames, | |
35 | 0 | |
36 | ) | |
37 | ||
38 | var currentYearIndex = 0 | |
39 | ||
40 | ||
41 | var monthMenu = new Form.Field.Option( | |
42 | "monthMenu", | |
43 | "Month", | |
44 | [0,1,2,3,4,5,6,7,8,9,10,11], | |
45 | monthNames, | |
46 | 0 | |
47 | ) | |
48 | ||
49 | var currentMonthIndex = 0 | |
50 | ||
51 | var dayMenu = new Form.Field.Option( | |
52 | "dayMenu", | |
53 | "Day", | |
54 | dayIndexes, | |
55 | dayIndexStrings, | |
56 | 0 | |
57 | ) | |
58 | ||
59 | inputForm.addField(yearMenu) | |
60 | inputForm.addField(monthMenu) | |
61 | inputForm.addField(dayMenu) | |
62 | var formPrompt = "Select the date:" | |
63 | var buttonTitle = "OK" | |
64 | var formPromise = inputForm.show(formPrompt, buttonTitle) | |
65 | ||
66 | inputForm.validate = function(formObject){ | |
67 | var yearMenuIndex = formObject.values["yearMenu"] | |
68 | var chosenYear = parseInt(yearNames[yearMenuIndex]) | |
69 | var monthMenuIndex = formObject.values["monthMenu"] | |
70 | ||
71 | if(monthMenuIndex != currentMonthIndex || yearMenuIndex != currentYearIndex){ | |
72 | inputForm.removeField(inputForm.fields[2]) | |
73 | currentMonthIndex = monthMenuIndex | |
74 | currentYearIndex = yearMenuIndex | |
75 | } | |
76 | ||
77 | if (formObject.fields.length == 2){ | |
78 | ||
79 | var dayCount = new Date(chosenYear, monthMenuIndex + 1, 0).getDate() | |
80 | ||
81 | var dayIndexes = new Array() | |
82 | var dayIndexStrings = new Array() | |
83 | for (var i = 0; i < dayCount; i++){ | |
84 | dayIndexes.push(i) | |
85 | dayIndexStrings.push(String(i + 1)) | |
86 | } | |
87 | ||
88 | var dayMenu = new Form.Field.Option( | |
89 | "dayMenu", | |
90 | "Day", | |
91 | dayIndexes, | |
92 | dayIndexStrings, | |
93 | 0 | |
94 | ) | |
95 | ||
96 | inputForm.addField(dayMenu) | |
97 | } | |
98 | ||
99 | return true | |
100 | } | |
101 | ||
102 | ||
103 | formPromise.then(function(formObject){ | |
104 | var yearMenuIndex = formObject.values["yearMenu"] | |
105 | var yearName = yearNames[yearMenuIndex] | |
106 | var monthMenuIndex = formObject.values["monthMenu"] | |
107 | var monthName = monthNames[monthMenuIndex] | |
108 | var dayMenuIndex = formObject.values["dayMenu"] | |
109 | var dayIndexString = dayIndexStrings[dayMenuIndex] | |
110 | var targetDate = new Date(monthName + " " + dayIndexString + " " + yearName) | |
111 | console.log(targetDate) | |
112 | }) | |
113 | ||
114 | formPromise.catch(function(error){ | |
115 | console.log("form cancelled", error.message) | |
116 | }) |
Start and End Dates
The following example presents two date input fields for entering start and end dates:
Start and End Dates | ||
01 | var inputForm = new Form() | |
02 | ||
03 | var startDateField = new Form.Field.Date( | |
04 | "startDate", | |
05 | "Start Date", | |
06 | null | |
07 | ) | |
08 | ||
09 | var endDateField = new Form.Field.Date( | |
10 | "endDate", | |
11 | "End Date", | |
12 | null | |
13 | ) | |
14 | ||
15 | inputForm.addField(startDateField) | |
16 | inputForm.addField(endDateField) | |
17 | ||
18 | var formPrompt = "Enter the start and end dates:" | |
19 | var buttonTitle = "Continue" | |
20 | var formPromise = inputForm.show(formPrompt, buttonTitle) | |
21 | ||
22 | inputForm.validate = function(formObject){ | |
23 | currentDateTime = new Date() | |
24 | startDateObject = formObject.values["startDate"] | |
25 | startDateStatus = (startDateObject && startDateObject > currentDateTime) ? true:false | |
26 | endDateObject = formObject.values["endDate"] | |
27 | endDateStatus = (endDateObject && endDateObject > startDateObject) ? true:false | |
28 | validation = (startDateStatus && endDateStatus) ? true:false | |
29 | return validation | |
30 | } | |
31 | ||
32 | formPromise.then(function(formObject){ | |
33 | var StartDate = formObject.values['startDate'] | |
34 | var EndDate = formObject.values['endDate'] | |
35 | }) | |
36 | ||
37 | formPromise.catch(function(err){ | |
38 | console.log("form cancelled", err.message) | |
39 | }) |
Currency Conversion Interface
The following example demonstrates an interface for gathering data for a currency conversion calculation. (Codes from Calculator.net)
Currency Conversion | ||
01 | var currencyCodes = ["AED", "AFN", "ALL", "AMD", "ANG", "AOA", "ARS", "AUD", "AWG", "AZN", "BAM", "BBD", "BDT", "BGN", "BHD", "BIF", "BMD", "BND", "BOB", "BRL", "BSD", "BTC", "BTN", "BWP", "BYN", "BZD", "CAD", "CDF", "CHF", "CLF", "CLP", "CNH", "CNY", "COP", "CRC", "CUC", "CUP", "CVE", "CZK", "DJF", "DKK", "DOP", "DZD", "EGP", "ERN", "ETB", "EUR", "FJD", "FKP", "GBP", "GEL", "GGP", "GHS", "GIP", "GMD", "GNF", "GTQ", "GYD", "HKD", "HNL", "HRK", "HTG", "HUF", "IDR", "ILS", "IMP", "INR", "IQD", "IRR", "ISK", "JEP", "JMD", "JOD", "JPY", "KES", "KGS", "KHR", "KMF", "KPW", "KRW", "KWD", "KYD", "KZT", "LAK", "LBP", "LKR", "LRD", "LSL", "LYD", "MAD", "MDL", "MGA", "MKD", "MMK", "MNT", "MOP", "MRO", "MRU", "MUR", "MVR", "MWK", "MXN", "MYR", "MZN", "NAD", "NGN", "NIO", "NOK", "NPR", "NZD", "OMR", "PAB", "PEN", "PGK", "PHP", "PKR", "PLN", "PYG", "QAR", "RON", "RSD", "RUB", "RWF", "SAR", "SBD", "SCR", "SDG", "SEK", "SGD", "SHP", "SLL", "SOS", "SRD", "SSP", "STD", "STN", "SVC", "SYP", "SZL", "THB", "TJS", "TMT", "TND", "TOP", "TRY", "TTD", "TWD", "TZS", "UAH", "UGX", "USD", "UYU", "UZS", "VEF", "VES", "VND", "VUV", "WST", "XAF", "XAG", "XAU", "XCD", "XDR", "XOF", "XPD", "XPF", "XPT", "YER", "ZAR", "ZMW", "ZWL"] | |
02 | var currencyStrings = ["United Arab Emirates Dirham", "Afghan Afghani", "Albanian Lek", "Armenian Dram", "Netherlands Antillean Guilder", "Angolan Kwanza", "Argentine Peso", "Australian Dollar", "Aruban Florin", "Azerbaijani Manat", "Bosnia-Herzegovina Convertible Mark", "Barbadian Dollar", "Bangladeshi Taka", "Bulgarian Lev", "Bahraini Dinar", "Burundian Franc", "Bermudan Dollar", "Brunei Dollar", "Bolivian Boliviano", "Brazilian Real", "Bahamian Dollar", "Bitcoin", "Bhutanese Ngultrum", "Botswanan Pula", "Belarusian Ruble", "Belize Dollar", "Canadian Dollar", "Congolese Franc", "Swiss Franc", "Chilean Unit of Account (UF)", "Chilean Peso", "Chinese Yuan (Offshore)", "Chinese Yuan", "Colombian Peso", "Costa Rican Colón", "Cuban Convertible Peso", "Cuban Peso", "Cape Verdean Escudo", "Czech Republic Koruna", "Djiboutian Franc", "Danish Krone", "Dominican Peso", "Algerian Dinar", "Egyptian Pound", "Eritrean Nakfa", "Ethiopian Birr", "Euro", "Fijian Dollar", "Falkland Islands Pound", "British Pound Sterling", "Georgian Lari", "Guernsey Pound", "Ghanaian Cedi", "Gibraltar Pound", "Gambian Dalasi", "Guinean Franc", "Guatemalan Quetzal", "Guyanaese Dollar", "Hong Kong Dollar", "Honduran Lempira", "Croatian Kuna", "Haitian Gourde", "Hungarian Forint", "Indonesian Rupiah", "Israeli New Sheqel", "Manx pound", "Indian Rupee", "Iraqi Dinar", "Iranian Rial", "Icelandic Króna", "Jersey Pound", "Jamaican Dollar", "Jordanian Dinar", "Japanese Yen", "Kenyan Shilling", "Kyrgystani Som", "Cambodian Riel", "Comorian Franc", "North Korean Won", "South Korean Won", "Kuwaiti Dinar", "Cayman Islands Dollar", "Kazakhstani Tenge", "Laotian Kip", "Lebanese Pound", "Sri Lankan Rupee", "Liberian Dollar", "Lesotho Loti", "Libyan Dinar", "Moroccan Dirham", "Moldovan Leu", "Malagasy Ariary", "Macedonian Denar", "Myanma Kyat", "Mongolian Tugrik", "Macanese Pataca", "Mauritanian Ouguiya (pre-2018)", "Mauritanian Ouguiya", "Mauritian Rupee", "Maldivian Rufiyaa", "Malawian Kwacha", "Mexican Peso", "Malaysian Ringgit", "Mozambican Metical", "Namibian Dollar", "Nigerian Naira", "Nicaraguan Córdoba", "Norwegian Krone", "Nepalese Rupee", "New Zealand Dollar", "Omani Rial", "Panamanian Balboa", "Peruvian Nuevo Sol", "Papua New Guinean Kina", "Philippine Peso", "Pakistani Rupee", "Polish Zloty", "Paraguayan Guarani", "Qatari Rial", "Romanian Leu", "Serbian Dinar", "Russian Ruble", "Rwandan Franc", "Saudi Riyal", "Solomon Islands Dollar", "Seychellois Rupee", "Sudanese Pound", "Swedish Krona", "Singapore Dollar", "Saint Helena Pound", "Sierra Leonean Leone", "Somali Shilling", "Surinamese Dollar", "South Sudanese Pound", "São Tomé and Príncipe Dobra (pre-2018)", "São Tomé and Príncipe Dobra", "Salvadoran Colón", "Syrian Pound", "Swazi Lilangeni", "Thai Baht", "Tajikistani Somoni", "Turkmenistani Manat", "Tunisian Dinar", "Tongan Pa anga", "Turkish Lira", "Trinidad and Tobago Dollar", "New Taiwan Dollar", "Tanzanian Shilling", "Ukrainian Hryvnia", "Ugandan Shilling", "United States Dollar", "Uruguayan Peso", "Uzbekistan Som", "Venezuelan Bolívar Fuerte (Old)", "Venezuelan Bolívar Soberano", "Vietnamese Dong", "Vanuatu Vatu", "Samoan Tala", "CFA Franc BEAC", "Silver Ounce", "Gold Ounce", "East Caribbean Dollar", "Special Drawing Rights", "CFA Franc BCEAO", "Palladium Ounce", "CFP Franc", "Platinum Ounce", "Yemeni Rial", "South African Rand", "Zambian Kwacha", "Zimbabwean Dollar"] | |
03 | var menuStrings = ["AED · United Arab Emirates Dirham", "AFN · Afghan Afghani", "ALL · Albanian Lek", "AMD · Armenian Dram", "ANG · Netherlands Antillean Guilder", "AOA · Angolan Kwanza", "ARS · Argentine Peso", "AUD · Australian Dollar", "AWG · Aruban Florin", "AZN · Azerbaijani Manat", "BAM · Bosnia-Herzegovina Convertible Mark", "BBD · Barbadian Dollar", "BDT · Bangladeshi Taka", "BGN · Bulgarian Lev", "BHD · Bahraini Dinar", "BIF · Burundian Franc", "BMD · Bermudan Dollar", "BND · Brunei Dollar", "BOB · Bolivian Boliviano", "BRL · Brazilian Real", "BSD · Bahamian Dollar", "BTC · Bitcoin", "BTN · Bhutanese Ngultrum", "BWP · Botswanan Pula", "BYN · Belarusian Ruble", "BZD · Belize Dollar", "CAD · Canadian Dollar", "CDF · Congolese Franc", "CHF · Swiss Franc", "CLF · Chilean Unit of Account (UF)", "CLP · Chilean Peso", "CNH · Chinese Yuan (Offshore)", "CNY · Chinese Yuan", "COP · Colombian Peso", "CRC · Costa Rican Colón", "CUC · Cuban Convertible Peso", "CUP · Cuban Peso", "CVE · Cape Verdean Escudo", "CZK · Czech Republic Koruna", "DJF · Djiboutian Franc", "DKK · Danish Krone", "DOP · Dominican Peso", "DZD · Algerian Dinar", "EGP · Egyptian Pound", "ERN · Eritrean Nakfa", "ETB · Ethiopian Birr", "EUR · Euro", "FJD · Fijian Dollar", "FKP · Falkland Islands Pound", "GBP · British Pound Sterling", "GEL · Georgian Lari", "GGP · Guernsey Pound", "GHS · Ghanaian Cedi", "GIP · Gibraltar Pound", "GMD · Gambian Dalasi", "GNF · Guinean Franc", "GTQ · Guatemalan Quetzal", "GYD · Guyanaese Dollar", "HKD · Hong Kong Dollar", "HNL · Honduran Lempira", "HRK · Croatian Kuna", "HTG · Haitian Gourde", "HUF · Hungarian Forint", "IDR · Indonesian Rupiah", "ILS · Israeli New Sheqel", "IMP · Manx pound", "INR · Indian Rupee", "IQD · Iraqi Dinar", "IRR · Iranian Rial", "ISK · Icelandic Króna", "JEP · Jersey Pound", "JMD · Jamaican Dollar", "JOD · Jordanian Dinar", "JPY · Japanese Yen", "KES · Kenyan Shilling", "KGS · Kyrgystani Som", "KHR · Cambodian Riel", "KMF · Comorian Franc", "KPW · North Korean Won", "KRW · South Korean Won", "KWD · Kuwaiti Dinar", "KYD · Cayman Islands Dollar", "KZT · Kazakhstani Tenge", "LAK · Laotian Kip", "LBP · Lebanese Pound", "LKR · Sri Lankan Rupee", "LRD · Liberian Dollar", "LSL · Lesotho Loti", "LYD · Libyan Dinar", "MAD · Moroccan Dirham", "MDL · Moldovan Leu", "MGA · Malagasy Ariary", "MKD · Macedonian Denar", "MMK · Myanma Kyat", "MNT · Mongolian Tugrik", "MOP · Macanese Pataca", "MRO · Mauritanian Ouguiya (pre-2018)", "MRU · Mauritanian Ouguiya", "MUR · Mauritian Rupee", "MVR · Maldivian Rufiyaa", "MWK · Malawian Kwacha", "MXN · Mexican Peso", "MYR · Malaysian Ringgit", "MZN · Mozambican Metical", "NAD · Namibian Dollar", "NGN · Nigerian Naira", "NIO · Nicaraguan Córdoba", "NOK · Norwegian Krone", "NPR · Nepalese Rupee", "NZD · New Zealand Dollar", "OMR · Omani Rial", "PAB · Panamanian Balboa", "PEN · Peruvian Nuevo Sol", "PGK · Papua New Guinean Kina", "PHP · Philippine Peso", "PKR · Pakistani Rupee", "PLN · Polish Zloty", "PYG · Paraguayan Guarani", "QAR · Qatari Rial", "RON · Romanian Leu", "RSD · Serbian Dinar", "RUB · Russian Ruble", "RWF · Rwandan Franc", "SAR · Saudi Riyal", "SBD · Solomon Islands Dollar", "SCR · Seychellois Rupee", "SDG · Sudanese Pound", "SEK · Swedish Krona", "SGD · Singapore Dollar", "SHP · Saint Helena Pound", "SLL · Sierra Leonean Leone", "SOS · Somali Shilling", "SRD · Surinamese Dollar", "SSP · South Sudanese Pound", "STD · São Tomé and Príncipe Dobra (pre-2018)", "STN · São Tomé and Príncipe Dobra", "SVC · Salvadoran Colón", "SYP · Syrian Pound", "SZL · Swazi Lilangeni", "THB · Thai Baht", "TJS · Tajikistani Somoni", "TMT · Turkmenistani Manat", "TND · Tunisian Dinar", "TOP · Tongan Pa anga", "TRY · Turkish Lira", "TTD · Trinidad and Tobago Dollar", "TWD · New Taiwan Dollar", "TZS · Tanzanian Shilling", "UAH · Ukrainian Hryvnia", "UGX · Ugandan Shilling", "USD · United States Dollar", "UYU · Uruguayan Peso", "UZS · Uzbekistan Som", "VEF · Venezuelan Bolívar Fuerte (Old)", "VES · Venezuelan Bolívar Soberano", "VND · Vietnamese Dong", "VUV · Vanuatu Vatu", "WST · Samoan Tala", "XAF · CFA Franc BEAC", "XAG · Silver Ounce", "XAU · Gold Ounce", "XCD · East Caribbean Dollar", "XDR · Special Drawing Rights", "XOF · CFA Franc BCEAO", "XPD · Palladium Ounce", "XPF · CFP Franc", "XPT · Platinum Ounce", "YER · Yemeni Rial", "ZAR · South African Rand", "ZMW · Zambian Kwacha", "ZWL · Zimbabwean Dollar"] | |
04 | var menuIndexes = new Array() | |
05 | currencyCodes.forEach((item, index) => {menuIndexes.push(index)}) | |
06 | ||
07 | var defaultFromIndex = menuStrings.indexOf("USD · United States Dollar") | |
08 | var defaultToIndex = menuStrings.indexOf("BTC · Bitcoin") | |
09 | ||
10 | var fromCurrencyMenu = new Form.Field.Option( | |
11 | "fromCurrency", | |
12 | "From", | |
13 | menuIndexes, | |
14 | menuStrings, | |
15 | defaultFromIndex | |
16 | ) | |
17 | ||
18 | var toCurrencyMenu = new Form.Field.Option( | |
19 | "toCurrency", | |
20 | "To", | |
21 | menuIndexes, | |
22 | menuStrings, | |
23 | defaultToIndex | |
24 | ) | |
25 | ||
26 | var textInputField = new Form.Field.String( | |
27 | "conversionAmount", | |
28 | "Amount", | |
29 | null | |
30 | ) | |
31 | ||
32 | var inputForm = new Form() | |
33 | inputForm.addField(fromCurrencyMenu) | |
34 | inputForm.addField(toCurrencyMenu) | |
35 | inputForm.addField(textInputField) | |
36 | var formPrompt = "Convert currency amount:" | |
37 | var buttonTitle = "Continue" | |
38 | var formPromise = inputForm.show(formPrompt,buttonTitle) | |
39 | ||
40 | inputForm.validate = function(formObject){ | |
41 | var conversionAmount = formObject.values['conversionAmount'] | |
42 | if (!conversionAmount){return false} | |
43 | var isnum = /^[0-9]+$/i.test(conversionAmount) | |
44 | if (isnum){ | |
45 | var intValue = parseInt(conversionAmount) | |
46 | return ((intValue > 0) ? true:false) | |
47 | } | |
48 | return false | |
49 | } | |
50 | ||
51 | formPromise.then(function(formObject){ | |
52 | var fromIndex = formObject.values['fromCurrency'] | |
53 | var toIndex = formObject.values['toCurrency'] | |
54 | var fromCode = currencyCodes[fromIndex] | |
55 | var toCode = currencyCodes[toIndex] | |
56 | var conversionAmount = formObject.values['conversionAmount'] | |
57 | ||
58 | // PROCESSING STATEMENTS GO HERE | |
59 | ||
60 | new Alert("Conversion:", String(conversionAmount) + "\n" + fromCode + " --> " + toCode).show() | |
61 | }) | |
62 | ||
63 | formPromise.catch(function(err){ | |
64 | console.error("form cancelled", err.message) | |
65 | }) |
This webpage is in the process of being developed. Any content may change and may not be accurate or complete at this time.