├── .gitignore ├── Standards.fmp12 ├── Standards.fp7 ├── Standards13.fmp12 ├── Functions ├── WebviewerURL.fmfn ├── #Name-Value │ ├── FM-Parameters.fmp12 │ ├── DDR │ │ └── Summary.xml │ ├── #ArrayGet.fmfn │ ├── #Array.fmfn │ ├── #ListGet.fmfn │ ├── #ArrayFromList.fmfn │ ├── #AssignGlobal.fmfn │ ├── #GetScriptResult.fmfn │ ├── #GetScriptParameter.fmfn │ ├── VerifyVariablesNotEmpty.fmfn │ ├── #Assign.fmfn │ ├── #Get.fmfn │ ├── #AssignScriptResults.fmfn │ ├── #AssignScriptParameters.fmfn │ ├── #List.fmfn │ ├── #GetNameList.fmfn │ ├── ScriptOptionalParameterList.fmfn │ ├── ScriptRequiredParameterList.fmfn │ ├── #Filter.fmfn │ ├── #Remove.fmfn │ └── #.fmfn ├── FMPExtension.fmfn ├── ParseLines.fmfn ├── WebviewerTimezone.fmfn ├── UUID │ ├── UUID.fmfn │ ├── UUIDGetTimestamp.fmfn │ ├── UUIDRandom.fmfn │ ├── UUIDGetNICAddress.fmfn │ ├── UUIDGetAsRFC4122.fmfn │ ├── UUIDGetAsUUID.fmfn │ ├── UUIDTimestamp.fmfn │ └── UUIDTimeDevice.fmfn ├── DateFormatISO8601.fmfn ├── GetDevice.fmfn ├── ValueExists.fmfn ├── FormatPlural.fmfn ├── FileExtension.fmfn ├── TimeSpan.fmfn ├── HTMLCSS.fmfn ├── LayoutObjects.fmfn ├── GetAsOrdinal.fmfn ├── VariableToggle.fmfn ├── DeveloperModifierKeys.fmfn ├── DatabaseFieldNames.fmfn ├── ListWrap.fmfn ├── Objects │ ├── ObjectLayoutNumber.fmfn │ ├── ObjectTableName.fmfn │ ├── ObjectLayoutName.fmfn │ ├── ObjectTableID.fmfn │ ├── ObjectLayoutID.fmfn │ ├── ObjectValueListID.fmfn │ ├── ObjectValueListName.fmfn │ ├── ObjectScriptID.fmfn │ ├── ObjectScriptName.fmfn │ ├── ObjectFieldName.fmfn │ └── ObjectFieldID.fmfn ├── ValueShuffle.fmfn ├── GetFieldNameAsGet.fmfn ├── SQLFieldName.fmfn ├── SQLTableName.fmfn ├── LayoutPosition.fmfn ├── WebviewerHTMLData.fmfn ├── HTMLWrapTag.fmfn ├── ActiveFQFN.fmfn ├── VariableCheckbox.fmfn ├── GetFieldNameVariable.fmfn ├── TrimMore.fmfn ├── ValueFilter.fmfn ├── Errors │ ├── ErrorFound.fmfn │ ├── ErrorFmpGetLast.fmfn │ ├── ErrorApp.fmfn │ ├── Error.fmfn │ └── LogData.fmfn ├── BOM.fmfn ├── TimeUnits.fmfn ├── PortalRowToggleValue.fmfn ├── ValuePosition.fmfn ├── Triggers │ ├── TriggersAreActive.fmfn │ ├── TriggersReset.fmfn │ ├── TriggersDisable.fmfn │ └── TriggersEnable.fmfn ├── ASOpenFolder.fmfn ├── ISO639-1.fmfn ├── UISortListDirection.fmfn ├── UISortPortalDirection.fmfn ├── HTMLDocument.fmfn ├── FileOSPath.fmfn ├── WindowActiveTabs.fmfn ├── Developer.fmfn ├── LayoutVariables.fmfn ├── WindowProperties.fmfn ├── IsNumber.fmfn ├── SetError.fmfn ├── Dirname.fmfn ├── PluginExists.fmfn ├── MergeVariables.fmfn ├── UISortPortalToggle.fmfn ├── UISortListToggle.fmfn ├── DeveloperProperties.fmfn ├── DeveloperPropertiesDisplay.fmfn ├── FilterASCII.fmfn ├── VariableList.fmfn ├── UISortPortalIndicator.fmfn ├── Define.fmfn ├── FMPURL.fmfn ├── BusinessDays.fmfn ├── LayoutObjectHierarchy.fmfn ├── UISortListIndicator.fmfn ├── ChecksumAdler32.fmfn ├── KeyboardModifiers.fmfn ├── ForEach.fmfn ├── WindowCenter.fmfn ├── S3Url.fmfn ├── ValueToggle.fmfn ├── GetBaseNAsNumber.fmfn ├── GetNumberAsBaseN.fmfn ├── BusinessHolidays.fmfn ├── Debug.fmfn ├── DeveloperWebview.fmfn └── ObjectID.fmfn ├── README.markdown ├── ScriptMaster └── RegEx.java └── Standards Diffs └── standard.css /.gitignore: -------------------------------------------------------------------------------- 1 | *.fp7 2 | Import.log 3 | .DS_Store 4 | -------------------------------------------------------------------------------- /Standards.fmp12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/petrowsky/fmpstandards/HEAD/Standards.fmp12 -------------------------------------------------------------------------------- /Standards.fp7: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/petrowsky/fmpstandards/HEAD/Standards.fp7 -------------------------------------------------------------------------------- /Standards13.fmp12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/petrowsky/fmpstandards/HEAD/Standards13.fmp12 -------------------------------------------------------------------------------- /Functions/WebviewerURL.fmfn: -------------------------------------------------------------------------------- 1 | // need to turn it into a function 2 | "fmp://localhost/" & get ( filename ) & "?script=scriptname" -------------------------------------------------------------------------------- /Functions/#Name-Value/FM-Parameters.fmp12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/petrowsky/fmpstandards/HEAD/Functions/#Name-Value/FM-Parameters.fmp12 -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | # Archived repository 2 | 3 | This is not the authoritative repository for filemakerstandards. 4 | 5 | Please use [filemakerstandards/fmpstandards](https://github.com/filemakerstandards/fmpstandards) if you are going to fork and contribute. 6 | -------------------------------------------------------------------------------- /Functions/FMPExtension.fmfn: -------------------------------------------------------------------------------- 1 | Let ( [ 2 | ~pathParts = Substitute ( Get ( FilePath ) ; "/" ; ¶ ); 3 | ~filename = GetValue ( ~pathParts ; ValueCount ( ~pathParts ) ); 4 | ~dotCount = PatternCount ( ~filename ; "." ) 5 | ]; 6 | Middle ( ~filename ; Position ( ~filename ; "." ; 1 ; ~dotCount ) ; 10 ) 7 | ) -------------------------------------------------------------------------------- /Functions/ParseLines.fmfn: -------------------------------------------------------------------------------- 1 | // Function used to parse out labeled lines like a header 2 | 3 | Let ( [ 4 | ~content = content & ¶; 5 | ~match = label; 6 | ~start = Position ( ~content ; ~match ; 1 ; 1 ) + Length ( ~match ); 7 | ~end = Position ( ~content ; ¶ ; ~start ; 1 ) 8 | ]; 9 | Middle ( ~content ; ~start ; ~end - ~start ) 10 | ) 11 | -------------------------------------------------------------------------------- /Functions/WebviewerTimezone.fmfn: -------------------------------------------------------------------------------- 1 | "data:text/html, 2 | 3 | 11 | 12 | 13 |
14 | " -------------------------------------------------------------------------------- /ScriptMaster/RegEx.java: -------------------------------------------------------------------------------- 1 | // Needs documentation about use 2 | import java.util.regex.*;String regex = pattern; 3 | Pattern pattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL); 4 | Matcher matcher = pattern.matcher(text); 5 | List result = new LinkedList(); 6 | while (matcher.find()) { result.add( matcher.group(1) );} 7 | return result; -------------------------------------------------------------------------------- /Functions/UUID/UUID.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * This custom function is a reserved keyword function and can be used as a wrapper (like an alias) 3 | * or reference to any other custom function used to generate a UUID (Universally unique identifier) 4 | * http://en.wikipedia.org/wiki/Uuid 5 | * http://filemakerstandards.org/pages/viewpage.action?pageId=557138 6 | */ 7 | 8 | UUIDTimeNIC // change this to another UUID function if desired -------------------------------------------------------------------------------- /Functions/DateFormatISO8601.fmfn: -------------------------------------------------------------------------------- 1 | Let ( [ 2 | ~today = Get ( CurrentDate ) 3 | ]; 4 | Year ( ~today ) 5 | & "-" 6 | & Right ( "00" & Month ( ~today ) ; 2 ) 7 | & "-" 8 | & Right ( "00" & Day ( ~today ) ; 2 ) 9 | ) 10 | 11 | & " " & 12 | 13 | Let ( [ 14 | ~time = Get ( CurrentTime ) 15 | ]; 16 | Right ( "00" & Hour ( ~time ) ; 2 ) 17 | & ":" 18 | & Right ( "00" & Minute ( ~time ) ; 2 ) 19 | & ":" 20 | & Right ( "00" & Seconds ( ~time ) ; 2 ) 21 | ) -------------------------------------------------------------------------------- /Functions/GetDevice.fmfn: -------------------------------------------------------------------------------- 1 | Let ( [ 2 | ~devices = List ( "Computer" ; "iPad" ; "iPhone"); 3 | ~version = Get ( ApplicationVersion ); 4 | ]; 5 | calculation 6 | ) 7 | 8 | Let ( [ 9 | ~version = Get ( ApplicationVersion ); 10 | ~device = Left ( ~version ; Position ( ~version ; " " ; 1 ; 1 ) -1 ) 11 | ]; 12 | Case ( 13 | ~device = "Go_iPad"; 14 | "iPad"; 15 | 16 | ~device = "Go"; 17 | "iPhone"; 18 | 19 | "Computer" 20 | ) 21 | ) -------------------------------------------------------------------------------- /Functions/ValueExists.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================================== 3 | * ValueExists ( value ; valueList ) 4 | * 5 | * PURPOSE: 6 | * Return if a value exists within a return delimited list 7 | * 8 | * RETURNS: 9 | * (boolean) True if it exists, False if not 10 | * 11 | * DEPENDENCIES: 12 | * none 13 | * 14 | * NOTES: 15 | * none 16 | * 17 | * HISTORY: 18 | * 2011-01-13 - Petrowsky - Added to custom functions 19 | * ===================================================== 20 | * 21 | */ 22 | 23 | PatternCount ( ¶& valueList &¶ ; ¶& value &¶ ) ≥ 1 24 | -------------------------------------------------------------------------------- /Functions/FormatPlural.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================================== 3 | * FormatPlural ( number ; singular ; plural ) 4 | * 5 | * PURPOSE: 6 | * Provide a method to return the singular or plural version 7 | * of a count of something 8 | * 9 | * RETURNS: 10 | * (string) Either the supplied singular or plural string 11 | * 12 | * DEPENDENCIES: 13 | * none 14 | * 15 | * NOTES: 16 | * none 17 | * 18 | * HISTORY: 19 | * 4/14/11 - Petrowsky - Added 20 | * ===================================================== 21 | * 22 | */ 23 | 24 | Case ( 25 | number = 0; 26 | plural; 27 | 28 | number = 1; 29 | singular; 30 | 31 | plural 32 | ) 33 | -------------------------------------------------------------------------------- /Functions/FileExtension.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================== 3 | * FileExtension ( path ) 4 | * 5 | * RETURNS: 6 | * The extension of the provided path. 7 | * 8 | * PARAMETERS: 9 | * path: Any valid file path with the /filename.extension at the end. 10 | * 11 | * EXAMPLES: 12 | * FileExtension ( "fmnet:/dev.server/MyDatabase.fmp12" ) // returns fmp12 13 | * FileExtension ( "/Volumes/some/path/to/file.svg" ) // returns svg 14 | * 15 | * DEPENDENCIES: none 16 | * 17 | * HISTORY: 18 | * CREATED on 2016-08-04. 19 | * 20 | * REFERENCES: 21 | * none 22 | * ===================================== 23 | */ 24 | 25 | RightWords ( Substitute ( path ; "." ; ¶ ) ; 1 ) 26 | -------------------------------------------------------------------------------- /Functions/TimeSpan.fmfn: -------------------------------------------------------------------------------- 1 | // © 2006 Winfried Huslik, www.fmdiff.com 2 | 3 | Let ( [ 4 | neg = Case ( date1 > date2 ; -1 ; 1 ) ; 5 | d1 = Case ( neg < 0 ; date2 ; date1 ) ; 6 | d2 = Case ( neg < 0 ; date1 ; date2 ) ; 7 | 8 | d = Mod ( Day ( d2 ) - Day ( d1 ) ; Day ( 9 | Date ( Month ( d1 ) + 1 ; 0; Year ( d1 ) ) ) ) ; 10 | 11 | m = Mod ( Month ( d2 ) - Month ( d1 ) - 12 | ( Day ( d2 ) < Day ( d1 ) ) ; 12 ) ; 13 | 14 | y = Year ( d2 ) - Year ( d1 ) - ( ( Month ( d2 ) - 15 | ( Day ( d2 ) < Day ( d1 ) ) ) < Month ( d1 ) ) 16 | ]; 17 | y * neg & ¶ & 18 | m * neg & ¶ & 19 | d * neg & ¶ & 20 | y & " years, " & m & " months, and " & d & " days" & ¶ 21 | ) 22 | -------------------------------------------------------------------------------- /Functions/HTMLCSS.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================================== 3 | * HTMLCSS ( css ) 4 | * 5 | * PURPOSE: 6 | * Simply wrap the "; 35 | ) 36 | -------------------------------------------------------------------------------- /Functions/LayoutObjects.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================== 3 | * LayoutObjects ( layout ) 4 | * 5 | * PURPOSE: 6 | * Get a return delimited list of all layout objects from 7 | * a specified layout 8 | * 9 | * RETURNS: 10 | * (string) Return delimited array layout objects 11 | * 12 | * PARAMETERS: 13 | * none 14 | * 15 | * EXAMPLES: 16 | * none 17 | * 18 | * DEPENDENCIES: 19 | * none 20 | * 21 | * NOTES: 22 | * This is a helper function 23 | * 24 | * HISTORY: 25 | * MODIFIED on 2011-07-08 by matt@filemakermagazine.com - added 26 | * 27 | * REFERENCES: 28 | * none 29 | * ===================================== 30 | */ 31 | 32 | Substitute ( LayoutObjectNames ( Get( FileName ) ; layout ) ; ["<¶" ; ""] ; ["¶>" ; ""] ) 33 | -------------------------------------------------------------------------------- /Functions/#Name-Value/DDR/Summary.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /Functions/GetAsOrdinal.fmfn: -------------------------------------------------------------------------------- 1 | /* 2 | * ===================================================== 3 | * GetAsOrdinal ( number ) 4 | * 5 | * RETURNS: 6 | * (string) Ordinal formatted number 7 | * DEPENDENCIES: 8 | * none 9 | * NOTES: 10 | * Adapted from http://www.briandunning.com/cf/737 11 | * ===================================================== 12 | * 13 | */ 14 | 15 | 16 | Let ( [ 17 | ~style = "Superscript"; // Can be made null to remove formatting 18 | ~digit = Int( GetAsNumber( number ) ); 19 | ~tens = Mod ( Abs( ~digit ); 100 ) 20 | ]; 21 | 22 | ~digit & TextStyleAdd ( Choose ( Min ( 4 ; Mod ( ~tens ; 10 ) ) * ( ( ~tens < 11 ) or ( ~tens > 13 ) ); "th"; "st"; "nd"; "rd"; "th" ); Evaluate ( ~style ) ) 23 | 24 | ) 25 | -------------------------------------------------------------------------------- /Functions/VariableToggle.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================================== 3 | * VariableToggle ( variable ) 4 | * 5 | * PURPOSE: 6 | * Toggle a Boolean value within a $$GLOBAL.VARIABLE 7 | * 8 | * RETURNS: 9 | * (boolean) True or False based on the success of the toggle 10 | * 11 | * DEPENDENCIES: 12 | * none 13 | * 14 | * NOTES: 15 | * This is a wrapper function according to 16 | * 17 | * RELEASE: 18 | * 2012-06-04 19 | * ===================================================== 20 | * 21 | */ 22 | 23 | 24 | Let ( [ 25 | ~evaluateString = "Let ( " & variable & " = " & not GetAsBoolean ( Evaluate ( variable ) ) & "; True )" 26 | ]; 27 | If ( IsValidExpression ( ~evaluateString ); 28 | Evaluate ( ~evaluateString ); 29 | False 30 | ) 31 | ) 32 | -------------------------------------------------------------------------------- /Functions/DeveloperModifierKeys.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ======================================================= 3 | * DeveloperModifierKeys 4 | * 5 | * PURPOSE: 6 | * Returns the active modifier keys on the condition that a [Full Access] user 7 | * is holding them. 8 | * 9 | * RETURNS: 10 | * The result of Get ( ActiveModifierKeys ) if the account has [Full Access] 11 | * privileges. Returns Null "" otherwise. 12 | * 13 | * PARAMETERS: none 14 | * 15 | * DEPENDENCIES: 16 | * Developer (custom function) 17 | * 18 | * HISTORY: 19 | * MODIFIED on 2010-12-02 by Jeremy Bante of The Support Group 20 | * (jbante@supportgroup.com) to use this header comment format. 21 | * ======================================================= 22 | */ 23 | 24 | If ( Developer ; Get ( ActiveModifierKeys ) ) -------------------------------------------------------------------------------- /Functions/DatabaseFieldNames.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================== 3 | * DatabaseFieldNames ( occurrenceName ) 4 | * 5 | * PURPOSE: 6 | * Return a list of fields from the target table occurrence 7 | * 8 | * RETURNS: 9 | * (list) Return delimeted list of field names 10 | * 11 | * PARAMETERS: 12 | * occurrenceName: a valid table occurrence name 13 | * 14 | * EXAMPLES: 15 | * none 16 | * 17 | * DEPENDENCIES: 18 | * none 19 | * 20 | * NOTE: 21 | * none 22 | * 23 | * HISTORY: 24 | * CREATED on 2014-07-24 by Matt Petrowsky (matt@filemakermagazine.com) 25 | * 26 | * REFERENCES: 27 | * none 28 | * ===================================== 29 | */ 30 | ExecuteSQL ( "SELECT FieldName FROM FileMaker_Fields WHERE TableName = '" & occurrenceName & "' ORDER BY FieldName" ; "" ; "" ) -------------------------------------------------------------------------------- /Functions/ListWrap.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================== 3 | * ListWrap ( theList ; prefix ; suffix ) 4 | * 5 | * RETURNS: 6 | * A formatted list with the prefix and 7 | * suffix applied to the inbound list. 8 | * 9 | * PARAMETERS: 10 | * theList = (text) a return delimited list of values 11 | * prefix = (text) a string of characters used for the prefix 12 | * prefix = (text) a string of characters used for the suffix 13 | * 14 | * DEPENDENCIES: 15 | * none 16 | * 17 | * HISTORY: 18 | * CREATED on 2014-MAY-07 Matt Petrowsky matt@isoproductions.com 19 | * 20 | * REFERENCES: 21 | * none 22 | * ===================================== 23 | */ 24 | Let ( [ 25 | ~newList = Substitute ( prefix & theList ; ¶ ; suffix & ¶ & prefix ) 26 | ]; 27 | Left ( ~newList ; Length ( ~newList ) - Length ( prefix ) - 1 ) // account for ¶ 28 | ) -------------------------------------------------------------------------------- /Functions/#Name-Value/#ArrayGet.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | ******************************************************************************* 3 | * #ArrayGet ( parameters ; name ; index ) 4 | * 5 | * PURPOSE: 6 | * Serves as syntactic sugar for retrieving array values from Let notation. 7 | * 8 | * RETURNS: 9 | * A named value from a string of name-value pairs in Let notation. 10 | * 11 | * PARAMETERS: 12 | * parameters: A string of name-value pairs in Let notation. 13 | * name: The name to retrieve the value of. 14 | * index: A numeric index for a repeating variable. 15 | * 16 | * DEPENDENCIES: 17 | * #Get ( parameters ; name ) 18 | * 19 | * HISTORY: 20 | * CREATED on 2013-07-27 by Jeremy Bante . 21 | ******************************************************************************* 22 | */ 23 | 24 | #Get ( 25 | parameters ; 26 | name & If ( index ≠ 1 or IsEmpty ( name ) ; "[" & index & "]" ) 27 | ) -------------------------------------------------------------------------------- /Functions/#Name-Value/#Array.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | ******************************************************************************* 3 | * #Array ( name ; index ; value ) 4 | * 5 | * PURPOSE: 6 | * Serves as syntactic sugar for creating array values in Let notation. 7 | * 8 | * RETURNS: 9 | * A name-value pair in Let notation. 10 | * 11 | * PARAMETERS: 12 | * name: The name for the returned name-value pair. name can be any value 13 | * that would be a valid Let() variable name. 14 | * index: A numeric index for a repeating variable. 15 | * value: The value for the returned name-value pair. 16 | * 17 | * DEPENDENCIES: 18 | * # ( name ; value ) 19 | * 20 | * HISTORY: 21 | * CREATED on 2013-07-26 by Jeremy Bante . 22 | ******************************************************************************* 23 | */ 24 | 25 | # ( 26 | name & If ( index ≠ 1 or IsEmpty ( name ) ; "[" & index & "]" ) ; 27 | value 28 | ) -------------------------------------------------------------------------------- /Functions/Objects/ObjectLayoutNumber.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================== 3 | * ObjectLayoutNumber ( file ; id ) 4 | * 5 | * RETURNS: 6 | * The current number of a FileMaker layout, based on its ID. 7 | * 8 | * PARAMETERS: 9 | * id: FileMaker's internal ID for the layout. 10 | * file: The name of the FileMaker file containing the layout. 11 | * 12 | * DEPENDENCIES: none 13 | * 14 | * HISTORY: 15 | * CREATED on 2012-09-05 by Jeremy Bante . 16 | * INSPIRED by custom functions by Fabrice Nordman. 17 | * 18 | * REFERENCES: 19 | * https://github.com/filemakerstandards/fmpstandards/blob/master/Functions/Objects/ObjectLayoutNumber.fmfn 20 | * ===================================== 21 | */ 22 | 23 | Let ( [ 24 | ~idList = ¶ & LayoutIDs ( file ) & ¶; 25 | ~value = 26 | Position ( ~idList ; ¶ & id & ¶ ; 1 ; 1 ); 27 | ~value = PatternCount ( Left ( ~idList ; ~value ) ; ¶ ) 28 | ]; 29 | ~value 30 | ) -------------------------------------------------------------------------------- /Functions/ValueShuffle.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ValueShuffle ( valueList ) 3 | * 4 | * RETURNS: 5 | * The contents of valueList in random order. 6 | * 7 | * PARAMETERS: 8 | * valueList: A ¶-delimited list of values to shuffle 9 | * 10 | * RELEASE: 2011-03-31 11 | */ 12 | 13 | Let ( [ 14 | ~valueCount = ValueCount ( valueList ); 15 | ~selection = 16 | If ( ~valueCount > 1; 17 | Max ( Ceiling ( Random * ~valueCount ) ; 1 ); 18 | /* Else */ 1 19 | ); 20 | $~shuffledList = 21 | List ( 22 | $~shuffledList; 23 | GetValue ( valueList ; ~selection ) 24 | ); 25 | ~leftList = LeftValues ( valueList ; ~selection - 1 ); 26 | ~rightList = RightValues ( valueList ; ~valueCount - ~selection ) 27 | ]; 28 | If ( ~valueCount > 1; 29 | ValueShuffle ( ~leftList & ~rightList ); 30 | 31 | /* Else */ 32 | Let ( [ 33 | ~shuffledList = $~shuffledList; 34 | $~shuffledList = "" // purge variable 35 | ]; 36 | ~shuffledList 37 | ) 38 | ) 39 | ) -------------------------------------------------------------------------------- /Functions/Objects/ObjectTableName.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================== 3 | * ObjectTableName ( id ; file ) 4 | * 5 | * RETURNS: 6 | * The current name of a FileMaker script, based on its ID. 7 | * 8 | * PARAMETERS: 9 | * id: FileMaker's internal ID for the table. 10 | * file: The name of the FileMaker file containing the table. 11 | * 12 | * DEPENDENCIES: none 13 | * 14 | * HISTORY: 15 | * CREATED on 2012-01-26 by Jeremy Bante . 16 | * INSPIRED by custom functions by Fabrice Nordman. 17 | * 18 | * REFERENCES: 19 | * https://github.com/filemakerstandards/fmpstandards/blob/master/Functions/Objects/ObjectTableName.fmfn 20 | * ===================================== 21 | */ 22 | 23 | Let ( [ 24 | ~idList = ¶ & TableIDs ( file ) & ¶; 25 | ~value = 26 | Position ( ~idList ; ¶ & id & ¶ ; 1 ; 1 ); 27 | ~value = PatternCount ( Left ( ~idList ; ~value ) ; ¶ ) 28 | ]; 29 | GetValue ( TableNames ( file ) ; ~value ) 30 | ) -------------------------------------------------------------------------------- /Functions/Objects/ObjectLayoutName.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================== 3 | * ObjectLayoutName ( id ; file ) 4 | * 5 | * RETURNS: 6 | * The current name of a FileMaker layout, based on its ID. 7 | * 8 | * PARAMETERS: 9 | * id: FileMaker's internal ID for the layout. 10 | * file: The name of the FileMaker file containing the layout. 11 | * 12 | * DEPENDENCIES: none 13 | * 14 | * HISTORY: 15 | * CREATED on 2012-01-26 by Jeremy Bante . 16 | * INSPIRED by custom functions by Fabrice Nordman. 17 | * 18 | * REFERENCES: 19 | * https://github.com/filemakerstandards/fmpstandards/blob/master/Functions/Objects/ObjectLayoutName.fmfn 20 | * ===================================== 21 | */ 22 | 23 | Let ( [ 24 | ~idList = ¶ & LayoutIDs ( file ) & ¶; 25 | ~value = 26 | Position ( ~idList ; ¶ & id & ¶ ; 1 ; 1 ); 27 | ~value = PatternCount ( Left ( ~idList ; ~value ) ; ¶ ) 28 | ]; 29 | GetValue ( LayoutNames ( file ) ; ~value ) 30 | ) -------------------------------------------------------------------------------- /Functions/GetFieldNameAsGet.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================================== 3 | * GetFieldNameAsGet ( field ) 4 | * 5 | * PURPOSE: 6 | * To be used within an auto-enter calculation 7 | * in order to evaluate the name of the field 8 | * as a Get () parameter 9 | * 10 | * PARAMETERS: 11 | * @field = (reference) a field 12 | * RETURNS: 13 | * (variable) the content of a variable with 14 | * the same name as the field. 15 | * DEPENDENCIES: 16 | * none 17 | * NOTES: 18 | * This function has a very specific use 19 | * ===================================================== 20 | * 21 | */ 22 | 23 | Let ( [ 24 | var.field = GetFieldName ( field ); 25 | var.parts = Substitute ( var.field ; "::" ; ¶ ); 26 | var.result = Evaluate ( "Get ( " & GetValue ( var.parts ; 2 ) & " )" ) 27 | ]; 28 | If ( var.result = "?" ; False ; var.result ) 29 | ) 30 | -------------------------------------------------------------------------------- /Functions/SQLFieldName.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================== 3 | * FieldName ( fieldRef ) 4 | * 5 | * PURPOSE: 6 | * Convenience function for working with 7 | * abstracted field references within code. 8 | * 9 | * RETURNS: 10 | * (string) name of just the field portion from a field reference 11 | * 12 | * PARAMETERS: 13 | * fieldRef = Absolute field reference 14 | * 15 | * EXAMPLES: 16 | * TableName ( FunctionalArea » Tablename::fieldName ) = "FunctionalArea » Tablename" 17 | * 18 | * DEPENDENCIES: 19 | * none 20 | * 21 | * NOTES: 22 | * This is simply a helper function to avoid having excessive amounts of 23 | * similar code wtihin a FileMaker solution. 24 | * 25 | * HISTORY: 26 | * MODIFIED on 2012-05-11 by matt@filemakermagazine.com - added 27 | * 28 | * REFERENCES: 29 | * (see TableName custom function) 30 | * ===================================== 31 | */ 32 | 33 | GetValue ( Substitute ( GetFieldName ( fieldRef ) ; "::" ; ¶ ) ; 2 ) 34 | -------------------------------------------------------------------------------- /Functions/SQLTableName.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================== 3 | * TableName ( fieldRef ) 4 | * 5 | * PURPOSE: 6 | * Convenience function for working with 7 | * abstracted table references within code. 8 | * 9 | * RETURNS: 10 | * (string) name of just the table portion from a field reference 11 | * 12 | * PARAMETERS: 13 | * fieldRef = Absolute field reference 14 | * 15 | * EXAMPLES: 16 | * TableName ( FunctionalArea » Tablename::fieldName ) = "FunctionalArea » Tablename" 17 | * 18 | * DEPENDENCIES: 19 | * none 20 | * 21 | * NOTES: 22 | * This is simply a helper function to avoid having excessive amounts of 23 | * similar code wtihin a FileMaker solution. 24 | * 25 | * HISTORY: 26 | * MODIFIED on 2012-05-11 by matt@filemakermagazine.com - added 27 | * 28 | * REFERENCES: 29 | * (see FieldName custom function) 30 | * ===================================== 31 | */ 32 | 33 | GetValue ( Substitute ( GetFieldName ( fieldRef ) ; "::" ; ¶ ) ; 1 ) 34 | -------------------------------------------------------------------------------- /Functions/LayoutPosition.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================== 3 | * LayoutPosition ( layoutId ) 4 | * 5 | * PURPOSE: 6 | * Return the numeric position of a layout. 7 | * 8 | * RETURNS: 9 | * (number) Layout number 10 | * 11 | * PARAMETERS: 12 | * layoutId: an internal layout id as derived from LayoutIDs function 13 | * 14 | * EXAMPLES: 15 | * LayoutPosition ( 24 ) = 10 where layout id 24 is the 10th layout out of 16 | * all layouts in the solution. 17 | * 18 | * DEPENDENCIES: 19 | * none 20 | * 21 | * NOTES: 22 | * None 23 | * 24 | * HISTORY: 25 | * CREATED on 2014-06-04 by matt@filemakermagazine.com 26 | * 27 | * REFERENCES: 28 | * none 29 | * ===================================== 30 | */ 31 | 32 | Let ( [ 33 | ~valueReplaced = Substitute ( ¶& LayoutIDs ( Get ( FileName ) ) &¶ ; ¶& id &¶ ; "¶•¶" ) 34 | ]; 35 | Position ( 36 | Substitute ( Filter ( ~valueReplaced ; "•¶" ) ; [ ¶ ; "|" ] ); 37 | "•" ; 38 | 1 ; 39 | 1 40 | ) - 1 41 | ) 42 | -------------------------------------------------------------------------------- /Functions/Objects/ObjectTableID.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================== 3 | * ObjectTableID ( table ; file ) 4 | * 5 | * RETURNS: 6 | * FileMaker's internal ID for a table. 7 | * 8 | * PARAMETERS: 9 | * table: Table Occurrence name 10 | * file: The name of the FileMaker file containing the table. 11 | * 12 | * DEPENDENCIES: none 13 | * 14 | * HISTORY: 15 | * MODIFIED on 2012-01-26 by Jeremy Bante to accept the file parameter. 16 | * CREATED on 2011-06-01 by Jeremy Bante . 17 | * INSPIRED by custom functions by Fabrice Nordman. 18 | * 19 | * REFERENCES: 20 | * https://github.com/filemakerstandards/fmpstandards/blob/master/Functions/Objects/ObjectTableID.fmfn 21 | * ===================================== 22 | */ 23 | 24 | Let ( [ 25 | ~nameList = ¶ & TableNames ( file ) & ¶; 26 | ~value = 27 | Position ( ~nameList ; ¶ & table & ¶ ; 1 ; 1 ); 28 | ~value = PatternCount ( Left ( ~nameList ; ~value ) ; ¶ ) 29 | ]; 30 | GetValue ( TableIDs ( file ) ; ~value ) 31 | ) -------------------------------------------------------------------------------- /Functions/WebviewerHTMLData.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================================== 3 | * WebviewerHTMLData ( type ; head ; css ; body ) 4 | * 5 | * PURPOSE: 6 | * Apply the data:text/html setting to any valid 7 | * HTML which will be displayed using a web viewer. 8 | * 9 | * RETURNS: 10 | * (string) local html content for a web viewer 11 | * 12 | * PARAMETERS: 13 | * type = (enumeration) any of the valid html document types or none 14 | * head = (string) any content which may be used within the section 15 | * css = (string) valid css (see the HTMLCSS function) 16 | * body = (string) HTML body content 17 | * 18 | * DEPENDENCIES: 19 | * HTMLDocument 20 | * 21 | * NOTES: 22 | * All parameters are simply forwarded to HTMLDocument 23 | * 24 | * HISTORY: 25 | * 2011-04-04 26 | * 27 | * REFERENCES: 28 | * none 29 | * ===================================================== 30 | * 31 | */ 32 | 33 | "data:text/html," & 34 | HTMLDocument ( type ; head ; css ; body ) 35 | -------------------------------------------------------------------------------- /Functions/Objects/ObjectLayoutID.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================== 3 | * ObjectLayoutID ( name ; file ) 4 | * 5 | * RETURNS: 6 | * FileMaker's internal ID for a layout. 7 | * 8 | * PARAMETERS: 9 | * name: The name of the layout 10 | * file: The name of the FileMaker file containing the layout. 11 | * 12 | * DEPENDENCIES: none 13 | * 14 | * HISTORY: 15 | * MODIFIED on 2012-01-26 by Jeremy Bante to accept the file parameter. 16 | * CREATED on 2011-06-01 by Jeremy Bante . 17 | * INSPIRED by custom functions by Fabrice Nordman. 18 | * 19 | * REFERENCES: 20 | * https://github.com/filemakerstandards/fmpstandards/blob/master/Functions/Objects/ObjectLayoutID.fmfn 21 | * ===================================== 22 | */ 23 | 24 | Let ( [ 25 | ~nameList = ¶ & LayoutNames ( file ) & ¶; 26 | ~value = 27 | Position ( ~nameList ; ¶ & name & ¶ ; 1 ; 1 ); 28 | ~value = PatternCount ( Left ( ~nameList ; ~value ) ; ¶ ) 29 | ]; 30 | GetValue ( LayoutIDs ( file ) ; ~value ) 31 | ) -------------------------------------------------------------------------------- /Functions/Objects/ObjectValueListID.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================== 3 | * ObjectValueListID ( name ; file ) 4 | * 5 | * RETURNS: 6 | * FileMaker's internal ID for a value list. 7 | * 8 | * PARAMETERS: 9 | * name: The name of the value list 10 | * file: The name of the FileMaker file containing the valueList. 11 | * 12 | * DEPENDENCIES: none 13 | * 14 | * HISTORY: 15 | * CREATED on 2012-04-13 by Jeremy Bante . 16 | * INSPIRED by custom functions by Fabrice Nordman. 17 | * 18 | * REFERENCES: 19 | * https://github.com/filemakerstandards/fmpstandards/blob/master/Functions/Objects/ObjectValueListID.fmfn 20 | * ===================================== 21 | */ 22 | 23 | Let ( [ 24 | ~nameList = ¶ & ValueListNames ( file ) & ¶; 25 | ~value = 26 | Position ( ~nameList ; ¶ & name & ¶ ; 1 ; 1 ); 27 | ~value = PatternCount ( Left ( ~nameList ; ~value ) ; ¶ ); 28 | ~id = GetValue ( ValueListIDs ( file ) ; ~value ) 29 | ]; 30 | If ( IsEmpty ( ~id ) ; -1 ; /* Else */ ~id ) 31 | ) -------------------------------------------------------------------------------- /Functions/HTMLWrapTag.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================================== 3 | * HTMLWrapTag ( values ; tag ; attributes ) 4 | * 5 | * PURPOSE: 6 | * Wrap a HTML/XML tag around the supplied values 7 | * or singular value. 8 | * 9 | * RETURNS: 10 | * (string) data 11 | * 12 | * PARAMETERS: 13 | * values = (string) either a list or singular value 14 | * tag = (string) name of the tagg to wrap 15 | * 16 | * DEPENDENCIES: 17 | * none 18 | * 19 | * NOTES: 20 | * none 21 | * 22 | * HISTORY: 23 | * 2011-04-04 24 | * 25 | * REFERENCES: 26 | * none 27 | * ===================================================== 28 | * 29 | */ 30 | 31 | Let ([ 32 | 33 | ~isList = ValueCount( values ) > 1; 34 | ~opentag = "<" & tag & " " & attributes & ">"; 35 | ~closetag = Replace( Substitute ( ~opentag ; " " & attributes ; "" ); 1; 1; ". 16 | * INSPIRED by custom functions by Fabrice Nordman. 17 | * 18 | * REFERENCES: 19 | * https://github.com/filemakerstandards/fmpstandards/blob/master/Functions/Objects/ObjectValueListName.fmfn 20 | * ===================================== 21 | */ 22 | 23 | Let ( [ 24 | ~idList = ¶ & ValueListIDs ( file ) & ¶; 25 | ~value = 26 | Position ( ~idList ; ¶ & id & ¶ ; 1 ; 1 ); 27 | ~value = PatternCount ( Left ( ~idList ; ~value ) ; ¶ ); 28 | ~name = GetValue ( ValueListNames ( file ) ; ~value ) 29 | ]; 30 | If ( IsEmpty ( ~name ) ; "?" ; /* Else */ ~name ) 31 | ) -------------------------------------------------------------------------------- /Functions/ActiveFQFN.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================== 3 | * ActiveFQFN ( ) 4 | * 5 | * PURPOSE: 6 | * Convenience function for working with 7 | * absolute field name references. 8 | * 9 | * RETURNS: 10 | * (string) fully qualified field name reference 11 | * 12 | * PARAMETERS: 13 | * none 14 | * 15 | * EXAMPLES: 16 | * Set Variable [$field; Value:ActiveFQFN] 17 | * # Do other things here 18 | * Set Field By Name [$field; "Result"] 19 | * 20 | * DEPENDENCIES: 21 | * none 22 | * 23 | * NOTES: 24 | * This is a helper function to avoid having excessive amounts of 25 | * similar code wtihin a FileMaker solution. 26 | * 27 | * HISTORY: 28 | * MODIFIED on 2014-06-17 by matt@filemakermagazine.com - adding repetition 29 | * MODIFIED on 2012-05-11 by matt@filemakermagazine.com - added 30 | * 31 | * REFERENCES: 32 | * none 33 | * ===================================== 34 | */ 35 | 36 | Get ( ActiveFieldTableName ) & "::" & Get ( ActiveFieldName ) 37 | & If ( Get ( ActiveRepetitionNumber ) > 1 ; "[" & Get ( ActiveRepetitionNumber ) & "]" ) 38 | -------------------------------------------------------------------------------- /Functions/VariableCheckbox.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================== 3 | * VariableCheckbox ( globalVariable ) 4 | * 5 | * PURPOSE: 6 | * Toggle a global variable on and off to show a checkmark character 7 | * 8 | * RETURNS: 9 | * (boolean) True or False based on the globalVariable existing or not 10 | * 11 | * PARAMETERS: 12 | * globalVariable = (string) the name of the global variable 13 | * 14 | * EXAMPLES: 15 | * When used as a script parameter pointing to a script named Refresh[] 16 | * VariableCheckbox ( "$$FILTER.PORTAL.ON" ) 17 | * 18 | * DEPENDENCIES: 19 | * none 20 | * 21 | * NOTES: 22 | * This is used as a technique to avoid creating global fields 23 | * 24 | * HISTORY: 25 | * MODIFIED on 2011-05-12 by matt@filemakermagazine.com - created 26 | * 27 | * REFERENCES: 28 | * none 29 | * ===================================== 30 | */ 31 | 32 | 33 | If ( IsEmpty ( Evaluate ( globalVariable ) ); 34 | Evaluate ( "Let ( " & globalVariable & " = \"√\" ; True )" ); 35 | Evaluate ( "Let ( " & globalVariable & " = \"\" ; False )" ) 36 | ) 37 | -------------------------------------------------------------------------------- /Functions/GetFieldNameVariable.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================================== 3 | * GetFieldNameVariable ( field ; prefix ) 4 | * 5 | * PURPOSE: 6 | * To be used within an auto-enter calculation 7 | * when you want to extract the name of 8 | * the current field and capture a variable 9 | * of the same name. 10 | * 11 | * PARAMETERS: 12 | * @field = (reference) a field 13 | * @prefix = (enum) either "$" or "$$" 14 | * RETURNS: 15 | * (variable) the content of a variable with 16 | * the same name as the field. 17 | * DEPENDENCIES: 18 | * none 19 | * NOTES: 20 | * This function has a very specific use 21 | * ===================================================== 22 | * 23 | */ 24 | 25 | Let ( [ 26 | var.field = GetFieldName ( field ); 27 | var.parts = Substitute ( var.field ; "::" ; ¶ ); 28 | var.result = Evaluate ( prefix & GetValue ( var.parts ; 2 ) ) 29 | ]; 30 | If ( var.result = "?" ; False ; var.result ) 31 | ) 32 | -------------------------------------------------------------------------------- /Functions/TrimMore.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================== 3 | * TrimMore ( content ) 4 | * 5 | * PURPOSE: 6 | * Remove leading and trailing carriage returns, 7 | * line feeds, vertical tabs, and spaces. 8 | * 9 | * RETURNS: 10 | * The supplied content less the above mentioned chars. 11 | * 12 | * PARAMETERS: 13 | * content: the supplied content to be trimmed 14 | * 15 | * EXAMPLES: 16 | * TrimMore ( Char ( 9 ) & Char ( 10 ) & Char ( 11 ) & "¶ Line 1¶Line 2¶¶¶¶¶¶¶ " ) //= Line 1¶Line 2 17 | * TrimMore ( LeftValues ( List ( "One" ; "Two" ; "Three" ) ; 2 ) ) // removes the trailing return 18 | * 19 | * DEPENDENCIES: none 20 | * 21 | * RELEASE: 22 | * 2013-05-20 23 | * 24 | * REFERENCES: 25 | * http://fmfunctions.com/fid/355 26 | * Fabrice Nordman (http://www.fmfunctions.com/mid/37) 27 | * 28 | * Notes: 29 | * Char ( 9 ) = Tab, Char ( 10 ) = new line (line feed) 30 | * Char ( 11 ) = vertical tab, Char ( 13 ) = ¶ carriage return 31 | * Char ( 32 ) = space 32 | * ===================================== 33 | */ 34 | 35 | MiddleWords ( content ; 1 ; Length ( content )) 36 | -------------------------------------------------------------------------------- /Functions/Objects/ObjectScriptID.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================== 3 | * ObjectScriptID ( name ; file ) 4 | * 5 | * RETURNS: 6 | * FileMaker's internal ID for a script. 7 | * 8 | * PARAMETERS: 9 | * name: The name of the script 10 | * file: The name of the FileMaker file containing the script. 11 | * 12 | * DEPENDENCIES: none 13 | * 14 | * HISTORY: 15 | * MODIFIED on 2012-02-20 by Jeremy Bante to return 16 | * an error value (-1) for unknown scripts. 17 | * CREATED on 2012-01-26 by Jeremy Bante . 18 | * INSPIRED by custom functions by Fabrice Nordman. 19 | * 20 | * REFERENCES: 21 | * https://github.com/filemakerstandards/fmpstandards/blob/master/Functions/Objects/ObjectScriptID.fmfn 22 | * ===================================== 23 | */ 24 | 25 | Let ( [ 26 | ~nameList = ¶ & ScriptNames ( file ) & ¶; 27 | ~value = 28 | Position ( ~nameList ; ¶ & name & ¶ ; 1 ; 1 ); 29 | ~value = PatternCount ( Left ( ~nameList ; ~value ) ; ¶ ); 30 | ~id = GetValue ( ScriptIDs ( file ) ; ~value ) 31 | ]; 32 | If ( IsEmpty ( ~id ) ; -1 ; /* Else */ ~id ) 33 | ) -------------------------------------------------------------------------------- /Functions/ValueFilter.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================== 3 | * ValueFilter ( match ; valueList ) 4 | * 5 | * PURPOSE: 6 | * Extract a return delimited list of values based 7 | * on the supplied match value. 8 | * 9 | * RETURNS: 10 | * (string) Return delimited array of matching string values 11 | * 12 | * PARAMETERS: 13 | * match = (string) the target match value 14 | * valueList = (string) array of values 15 | * 16 | * EXAMPLES: 17 | * none 18 | * 19 | * DEPENDENCIES: 20 | * CustomLilst() 21 | * 22 | * NOTES: 23 | * This is a simple straight match function. It does not 24 | * find values based on Regular Expressions. 25 | * 26 | * HISTORY: 27 | * MODIFIED on 2011-07-08 by matt@filemakermagazine.com - added 28 | * 29 | * REFERENCES: 30 | * none 31 | * ===================================== 32 | */ 33 | 34 | Let( [ 35 | $~valueList = valueList; 36 | $~match = match; 37 | ~function = "Let ( ~value = GetValue( $~valueList ; [n] ) ; If ( PatternCount ( ~value ; $~match ) ; ~value ) )" 38 | ]; 39 | 40 | CustomList ( 1 ; ValueCount ( $~valueList ) ; ~function ) 41 | 42 | ) 43 | -------------------------------------------------------------------------------- /Functions/Errors/ErrorFound.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================== 3 | * ErrorFound ( theErrorData ) 4 | * 5 | * RETURNS: 6 | * ErrorCode contained in theErrorData as a Boolean. In other words, 7 | * if any error occurred at all, return True (1), otherwise return 8 | * False (0). 9 | * 10 | * PARAMETERS: 11 | * theErrorData = (let notation) output of Error custom function 12 | * 13 | * DEPENDENCIES: 14 | * Custom Functions: #Get 15 | * 16 | * HISTORY: 17 | * MODIFIED on 2013-NOV-22 by Daniel Smith dansmith65@gmail.com 18 | * - renamed to ErrorFound, update documentation 19 | * MODIFIED on 2013-OCT-11 by Daniel Smith dansmith65@gmail.com 20 | * - renamed from IsError to #IsError 21 | * - update documentation in script header 22 | * MODIFIED on 2012-NOV-21 by Daniel Smith dansmith65@gmail.com 23 | * - don't access $error variable directly 24 | * CREATED on 2012-NOV-20 Daniel Smith dansmith65@gmail.com 25 | * 26 | * REFERENCES: 27 | * http://filemakerstandards.org/x/AoA-/ 28 | * ===================================== 29 | */ 30 | 31 | GetAsBoolean ( 32 | #Get ( theErrorData ; "errorCode" ) 33 | ) 34 | -------------------------------------------------------------------------------- /Functions/Objects/ObjectScriptName.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================== 3 | * ObjectScriptName ( id ; file ) 4 | * 5 | * RETURNS: 6 | * The current name of a FileMaker script, based on its ID. 7 | * 8 | * PARAMETERS: 9 | * id: FileMaker's internal ID for the script. 10 | * file: The name of the FileMaker file containing the script. 11 | * 12 | * DEPENDENCIES: none 13 | * 14 | * HISTORY: 15 | * MODIFIED on 2012-02-20 by Jeremy Bante to return 16 | * descriptive text for bad IDs. 17 | * CREATED on 2012-01-26 by Jeremy Bante . 18 | * INSPIRED by custom functions by Fabrice Nordman. 19 | * 20 | * REFERENCES: 21 | * https://github.com/filemakerstandards/fmpstandards/blob/master/Functions/Objects/ObjectScriptName.fmfn 22 | * ===================================== 23 | */ 24 | 25 | Let ( [ 26 | ~idList = ¶ & ScriptIDs ( file ) & ¶; 27 | ~value = 28 | Position ( ~idList ; ¶ & id & ¶ ; 1 ; 1 ); 29 | ~value = PatternCount ( Left ( ~idList ; ~value ) ; ¶ ); 30 | ~name = GetValue ( ScriptNames ( file ) ; ~value ) 31 | ]; 32 | If ( IsEmpty ( ~name ) ; "?" ; /* Else */ ~name ) 33 | ) -------------------------------------------------------------------------------- /Functions/Objects/ObjectFieldName.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================== 3 | * ObjectFieldName ( id ; table ; file ) 4 | * 5 | * RETURNS: 6 | * The current name of a FileMaker field, based on its ID. 7 | * 8 | * PARAMETERS: 9 | * file: The name of the FileMaker file containing the field. 10 | * theFieldID: FileMaker's internal ID for the field. 11 | * 12 | * DEPENDENCIES: none 13 | * 14 | * NOTE: 15 | * If there is a layout in the file with the same name as the field's 16 | * table, the field must be on that layout for this function to work. 17 | * 18 | * HISTORY: 19 | * CREATED on 2012-01-26 by Jeremy Bante . 20 | * INSPIRED by custom functions by Fabrice Nordman. 21 | * 22 | * REFERENCES: 23 | * https://github.com/filemakerstandards/fmpstandards/blob/master/Functions/Objects/ObjectFieldName.fmfn 24 | * ===================================== 25 | */ 26 | 27 | Let ( [ 28 | ~idList = ¶ & FieldIDs ( file ; table ) & ¶; 29 | ~value = 30 | Position ( ~idList ; ¶ & id & ¶ ; 1 ; 1 ); 31 | ~value = PatternCount ( Left ( ~idList ; ~value ) ; "¶" ) 32 | ]; 33 | GetValue ( FieldNames ( file ; table ) ; ~value ) 34 | ) -------------------------------------------------------------------------------- /Functions/BOM.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================== 3 | * BOM ( number ) 4 | * 5 | * PURPOSE: 6 | * Return the specified number of BOM (Byte Order Mark) characters 7 | * 8 | * RETURNS: 9 | * String of the (number) of BOM chars 10 | * 11 | * PARAMETERS: 12 | * number: a numerical value of the requested BOM chars 13 | * 14 | * EXAMPLES: 15 | * BOM ( 10 ) & $value // = 10 BOM chars preceeding the $value 16 | * 17 | * DEPENDENCIES: none 18 | * 19 | * NOTE: 20 | * This function can be used to force a sorting order by 21 | * prepending a number of BOM chars to a given value. You MUST 22 | * set the indexing language to UNICODE for the field being sorted 23 | * on. Also note that equality operations will fail if you forget 24 | * about prepended BOM chars. Use Substitute ( text ; Char(65279) ; "") 25 | * 26 | * HISTORY: 27 | * CREATED on 2012-09-18 by Matt Petrowsky (matt@filemakermagazine.com) 28 | * INSPIRED by Marcelo Piñeyro of Soliant consulting. 29 | * 30 | * REFERENCES: 31 | * http://en.wikipedia.org/wiki/Byte_order_mark 32 | * ===================================== 33 | */ 34 | If ( number > 0 ; Char(65279) & BOM ( number - 1 ) ) -------------------------------------------------------------------------------- /Functions/TimeUnits.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================== 3 | * TimeUnits ( amount ; unit ) 4 | * 5 | * PURPOSE: 6 | * Return a numerical amount of seconds based on the 7 | * supplied amount in the units specified 8 | * 9 | * RETURNS: 10 | * (number) seconds 11 | * 12 | * PARAMETERS: 13 | * amount = (number) 14 | * unit = (enum) minutes, hours, days, months, years 15 | * 16 | * EXAMPLES: 17 | * TimeUnits ( 5 ; "days" ) = 432000 18 | * 19 | * DEPENDENCIES: 20 | * none 21 | * 22 | * NOTES: 23 | * Because of variations in months and years, these return values 24 | * are approximate. If you need exact differences between date values 25 | * you'll need to use FileMaker's date functions. 26 | * 27 | * HISTORY: 28 | * MODIFIED on 2011-09-15 by matt@filemakermagazine.com - added 29 | * 30 | * REFERENCES: 31 | * none 32 | * ===================================== 33 | */ 34 | 35 | Let( [ 36 | ~seconds = Choose ( 37 | Int ( Position ( "minutes.hours...days....months..years..."; unit; 1; 1 ) / 8 ); 38 | 60 ; 3600 ; 86400 ; 2629743.83 ; 31556926 ) // minutes, hours, days, months, years (in seconds) 39 | ]; 40 | ~seconds * amount 41 | ) 42 | -------------------------------------------------------------------------------- /Functions/PortalRowToggleValue.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================================== 3 | * PortalRowToggleValue ( keyField ; rowField ) 4 | * 5 | * PURPOSE: 6 | * Add or remove a portal row field value 7 | * from a target key field (global or normal). 8 | * PARAMETERS: 9 | * keyField = global or normal field reference 10 | * rowField = field reference from a portal row 11 | * RETURNS: 12 | * (string) Return delimited list of values 13 | * with the value either added or removed. 14 | * DEPENDENCIES: 15 | * none 16 | * NOTES: 17 | * HISTORY: 1/18/11 - Matt Petrowsky 18 | * ===================================================== 19 | * 20 | */ 21 | 22 | Let ( [ 23 | ~keyField = keyField; 24 | ~rowValue = GetNthRecord ( rowField ; Get ( ActivePortalRowNumber ) ) 25 | ]; 26 | If ( IsEmpty ( FilterValues ( ~keyField ; ~rowValue ) ); // it doesn't exist 27 | 28 | List ( ~keyField; ~rowValue ); // add it, otherwise... 29 | 30 | Substitute( "¶¶" & ~keyField & "¶¶"; // remove it 31 | [ ¶ & ~rowValue & ¶ ; ¶ ]; 32 | ["¶¶¶"; ""]; 33 | ["¶¶"; ""] 34 | ) 35 | ) 36 | ) 37 | -------------------------------------------------------------------------------- /Functions/#Name-Value/#ListGet.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================== 3 | * #ListGet ( listOfValues ; valueNumber ) 4 | * 5 | * PURPOSE: 6 | * Retrieve a value from a list created with the #List ( value ) function. 7 | * Preserve the data type and all special characters of the original value. 8 | * 9 | * RETURNS: 10 | * Original value before encoding with the original data type preserved. 11 | * 12 | * PARAMETERS: 13 | * listOfValues: Return delimeted and encoded values 14 | * valueNumber: The position of the value to retrieve 15 | * (1 being the first value) 16 | * 17 | * EXAMPLE: 18 | * #ListGet ( #List ( "value1" ) & #List ( "value2" ) ; 1 ) = "value1" 19 | * #ListGet ( #List ( "" ) & #List ( "value2" ) ; 2 ) = "value2" 20 | * #ListGet ( #List ( "value1" ) & #List ( "value2" ) ; 3 ) = "" 21 | * #ListGet ( "This is not a valid #List string" ; 1 ) = "" 22 | * 23 | * DEPENDENCIES: none 24 | * 25 | * HISTORY: 26 | * CREATED on 2012-NOV-26 by Daniel Smith dansmith65@gmail.com 27 | * ===================================== 28 | */ 29 | 30 | Let ( [ 31 | ~value = GetValue ( listOfValues ; valueNumber ) 32 | ] ; 33 | 34 | If ( 35 | IsValidExpression ( ~value ) ; 36 | Evaluate ( ~value ) 37 | ) 38 | 39 | ) -------------------------------------------------------------------------------- /Functions/ValuePosition.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================================== 3 | * ValuePosition ( value ; valueList ) 4 | * 5 | * PURPOSE: 6 | * Return the numerical position of a value within a list 7 | * 8 | * RETURNS: 9 | * (number) position of value 10 | * 11 | * DEPENDENCIES: 12 | * none 13 | * 14 | * NOTES: 15 | * This function assumes valueList contains unique values 16 | * otherwise the first position will be returned 17 | * 18 | * HISTORY: 19 | * 4/5/11 - Petrowsky - Added to custom functions 20 | * ===================================================== 21 | * 22 | */ 23 | 24 | Let ( [ 25 | ~valueReplaced = Substitute ( ¶& valueList &¶ ; ¶& value &¶ ; "¶•¶" ) 26 | ]; 27 | Position ( 28 | Substitute ( Filter ( ~valueReplaced ; "•¶" ); 29 | [ ¶ ; "|" ] ); 30 | "•" ; 1 ; 1 ) - 1 31 | ) 32 | 33 | // Alternatives: @todo performance testing 34 | // PatternCount ( Left ( ¶ & valueList ; Position ( ¶ & valueList ; ¶ & value & ¶; 1; 1 ) ) ; ¶) 35 | // ValueCount ( Left ( ¶ & valueList & ¶ ; Position ( ¶ & valueList & ¶ ; ¶ & value ; 1; 1 ) ) ) 36 | /* 37 | Let ( [ 38 | v = ¶ & _listOfValues & ¶ ; 39 | p = Position ( v ; ¶ & _value & ¶ ; 1 ; 1 ) 40 | ] ; 41 | 42 | ValueCount ( Left ( v ; p ) ) 43 | ) 44 | */ 45 | -------------------------------------------------------------------------------- /Functions/Triggers/TriggersAreActive.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================== 3 | * TriggersAreActive 4 | * https://github.com/jbante/FileMaker-Techniques/blob/master/CustomFunctions/Triggers/TriggersAreActive.fmfn 5 | * 6 | * PURPOSE: 7 | * TriggersAreActive checks a global variable to identify whether or not 8 | * script triggers should be allowed to run. This function is best used in 9 | * concert with the TriggersDisable, TriggersEnable, and TriggersReset 10 | * functions. 11 | * 12 | * RETURNS: 13 | * False (0) if triggers should be suppressed; True (1) otherwise. 14 | * 15 | * PARAMETERS: none 16 | * 17 | * REFERENCED VARIABLES: 18 | * $$~DISABLETRIGGERS (deprecated) 19 | * $$~TRIGGERS_DISABLE 20 | * 21 | * DEPENDENCIES: none 22 | * 23 | * EXAMPLE (triggered script): 24 | * If [not TriggersAreActive] 25 | * Exit Script [True // allow triggering event to proceed] 26 | * End If 27 | * 28 | * RELEASE: 2013-04-08 29 | * 30 | * REFERENCES: 31 | * Supressible Triggered Scripts Best Practice: http://filemakerstandards.org/display/bp/Suppressible+Triggered+Scripts 32 | * ===================================== 33 | */ 34 | 35 | not ( 36 | $$~TRIGGERS_DISABLE 37 | or $$~DISABLETRIGGERS 38 | ) -------------------------------------------------------------------------------- /Functions/#Name-Value/#ArrayFromList.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | ******************************************************************************* 3 | * #ArrayFromList ( name ; valueList ) 4 | * 5 | * PURPOSE: 6 | * Converts a return-delimited list to a Let notation array. 7 | * 8 | * RETURNS: 9 | * A series of name-value pairs in Let notation. 10 | * 11 | * PARAMETERS: 12 | * name: The name for the returned name-value pair. name can be any value 13 | * that would be a valid Let() variable name. 14 | * valueList: A return-delimited list of values 15 | * 16 | * DEPENDENCIES: 17 | * # ( name ; value ) 18 | * #Array ( name ; index ; value ) 19 | * 20 | * HISTORY: 21 | * CREATED on 2013-07-26 by Jeremy Bante . 22 | ******************************************************************************* 23 | */ 24 | 25 | If ( IsEmpty ( $~#Array.valueCount ) ; 26 | Let ( $~#Array.valueCount = ValueCount ( valueList ) ; "" ) 27 | ) 28 | & Let ( $~#Array.i = $~#Array.i + 1 ; "" ) 29 | & If ( $~#Array.i ≤ $~#Array.valueCount ; 30 | #Array ( name ; $~#Array.i ; GetValue ( valueList ; $~#Array.i ) ) 31 | ) 32 | & If ( $~#Array.i < $~#Array.valueCount ; 33 | #ArrayFromList ( name ; valueList ) ; 34 | /* Else */ Let ( [ $~#Array.valueCount = "" ; $~#Array.i = "" ] ; "" ) 35 | ) -------------------------------------------------------------------------------- /Functions/ASOpenFolder.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================================== 3 | * ASOpenFolder ( path ; name ) 4 | * 5 | * PURPOSE: 6 | * Create AppleScript code which will open a folder within the 7 | * Finder. If the folder does not exist, it will be created. 8 | * PARAMETERS: 9 | * path = a valid POSIX path 10 | * name = name of the target folder 11 | * RETURNS: 12 | * (string) AppleScript code 13 | * DEPENDENCIES: 14 | * none 15 | * NOTES: 16 | * This calculation contains the shell code needed to make a folder on OS X 17 | * "if [ ! -d $path ];then mkdir $path; open $path; else open $path; fi" 18 | * HISTORY: 19 | * 12/29/11 - Matt Petrowsky - updated 20 | * ===================================================== 21 | * 22 | */ 23 | 24 | Let( [ 25 | // name = "folder"; 26 | // path = "/Users/matt/Desktop/"; 27 | ~mypath = path & name; 28 | ~qpath = Quote ( "$path" ); // quoted shell variable 29 | ~sh = "if [ ! -d " & ~qpath & " ];then mkdir " & ~qpath & "; open " & ~qpath & "; else open " & ~qpath & "; fi"; 30 | ~cmd = "path=" & Quote ( ~mypath ) & "; " & ~sh // raw shell script command 31 | ]; 32 | // this is applescript code - it should be called via the Perform AppleScript step 33 | "do shell script " & "\"" & Substitute ( ~cmd ; "\"" ; "\\\"" ) & "\"" 34 | 35 | ) 36 | -------------------------------------------------------------------------------- /Functions/ISO639-1.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================== 3 | * ISO639–1 4 | * 5 | * PURPOSE: 6 | * Return the ISO639–1 equivalent of FileMaker's Get ( ApplicationLanguage ) 7 | * 8 | * RETURNS: 9 | * A two character string according to ISO639–1 10 | * 11 | * PARAMETERS: 12 | * none 13 | * 14 | * EXAMPLES: 15 | * ISO639–1 // = "en" when Get ( ApplicationLanguage ) is "English" 16 | * 17 | * DEPENDENCIES: none 18 | * 19 | * NOTE: 20 | * This function can be used for localization 21 | * 22 | * HISTORY: 23 | * CREATED on 2012-09-18 by Matt Petrowsky (matt@filemakermagazine.com) 24 | * 25 | * REFERENCES: 26 | * http://en.wikipedia.org/wiki/List_of_ISO_639-1_codes 27 | * ===================================== 28 | */ 29 | Let ( [ 30 | ~lang = Get ( ApplicationLanguage ) 31 | ]; 32 | Case ( 33 | ~lang = "Dutch"; 34 | "nl"; 35 | 36 | ~lang = "English"; 37 | "en"; 38 | 39 | ~lang = "French"; 40 | "fr"; 41 | 42 | ~lang = "German"; 43 | "de"; 44 | 45 | ~lang = "Italian"; 46 | "it"; 47 | 48 | ~lang = "Japanese"; 49 | "ja"; 50 | 51 | ~lang = "Simplified Chinese"; 52 | "zh"; 53 | 54 | ~lang = "Spanish"; 55 | "es"; 56 | 57 | ~lang = "Swedish"; 58 | "sv"; 59 | 60 | ~lang = "Traditional Chinese"; 61 | "zh"; 62 | 63 | "en" // default language for solution 64 | ) 65 | ) -------------------------------------------------------------------------------- /Functions/Triggers/TriggersReset.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================== 3 | * TriggersReset 4 | * https://github.com/jbante/FileMaker-Techniques/blob/master/CustomFunctions/Triggers/TriggersReset.fmfn 5 | * 6 | * PURPOSE: 7 | * TriggersReset sets global variables to indicate that script triggers 8 | * should be allowed to run, regardless of any persisting instructions to 9 | * the contrary from any script. This may be useful to recover from a 10 | * situation where a script forgot to call TriggersEnable at the end of 11 | * operation. In order to be suppressed, a script called via trigger should 12 | * use the TriggersAreActive function to decide whether or not to run. 13 | * 14 | * RETURNS: "" (null) 15 | * 16 | * PARAMETERS: none 17 | * 18 | * REFERENCED VARIABLES: 19 | * $$~DISABLETRIGGERS (deprecated) 20 | * $$~TRIGGERS_DISABLE 21 | * $$~TRIGGERS_SCRIPTS 22 | * $~triggers_disableKey 23 | * 24 | * DEPENDENCIES: none 25 | * 26 | * RELEASE: 2013-04-08 27 | * 28 | * REFERENCES: 29 | * Supressible Triggered Scripts Best Practice: http://filemakerstandards.org/display/bp/Suppressible+Triggered+Scripts 30 | * ===================================== 31 | */ 32 | 33 | Let ( [ 34 | $$~TRIGGERS_SCRIPTS = "" ; 35 | $$~TRIGGERS_DISABLE = "" ; 36 | $$~DISABLETRIGGERS = "" ; 37 | $~triggers_disableKey = "" 38 | ] ; 39 | "" 40 | ) -------------------------------------------------------------------------------- /Functions/Objects/ObjectFieldID.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================== 3 | * ObjectFieldID ( field ; file ) 4 | * 5 | * RETURNS: 6 | * FileMaker's internal ID for a field. 7 | * 8 | * PARAMETERS: 9 | * field: The field (not literal field name) to retrieve the ID of. 10 | * file: The name of the FileMaker file containing the field. 11 | * 12 | * DEPENDENCIES: none 13 | * 14 | * NOTE: 15 | * If there is a layout in the file with the same name as the field's 16 | * table, the field must be on that layout for this function to work. 17 | * 18 | * HISTORY: 19 | * MODIFIED on 2012-01-26 by Jeremy Bante to accept the file parameter. 20 | * CREATED on 2011-06-01 by Jeremy Bante . 21 | * INSPIRED by custom functions by Fabrice Nordman. 22 | * 23 | * REFERENCES: 24 | * https://github.com/filemakerstandards/fmpstandards/blob/master/Functions/Objects/ObjectFieldID.fmfn 25 | * ===================================== 26 | */ 27 | 28 | Let ( [ 29 | ~fieldName = Substitute ( GetFieldName ( field ) ; "::" ; "¶" ); 30 | ~tableName = GetValue ( ~fieldName ; 1 ); 31 | ~fieldName = GetValue ( ~fieldName ; 2 ); 32 | 33 | ~nameList = ¶ & FieldNames ( file ; ~tableName ) & ¶; 34 | ~value = 35 | Position ( ~nameList ; ¶ & ~fieldName & ¶ ; 1 ; 1 ); 36 | ~value = PatternCount ( Left ( ~nameList ; ~value ) ; "¶" ) 37 | ]; 38 | GetValue ( FieldIDs ( file ; ~tableName ) ; ~value ) 39 | ) -------------------------------------------------------------------------------- /Functions/#Name-Value/#AssignGlobal.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================== 3 | * #AssignGlobal ( parameters ) 4 | * 5 | * PURPOSE: 6 | * Parses a Let dictionary of name-value parameters into a series of 7 | * globally scoped $$VARIABLES. 8 | * 9 | * RETURNS: 10 | * FileMaker's error code from any errors encountered during evaluation. 11 | * 12 | * PARAMETERS: 13 | * parameters: A Let format dictionary, such as produced by the 14 | * # ( name ; value ) function. 15 | * 16 | * DEPENDENCIES: none 17 | * 18 | * NOTES: 19 | * Notes go here 20 | * 21 | * HISTORY: 22 | * MODIFIED on 2015-11-18 by Matt Petrowsky to support weak formatted 23 | * return delimited input. 24 | * CREATED on 2012-12-04 by Jeremy Bante . 25 | * ===================================== 26 | */ 27 | 28 | Let ( [ 29 | ~variables = Case ( 30 | not PatternCount ( parameters ; ";¶" ) 31 | and not IsEmpty ( parameters ); 32 | Substitute ( parameters ; ¶ ; ";¶" ) & ";"; 33 | 34 | parameters 35 | ); 36 | ~error = 37 | EvaluationError ( Evaluate ( 38 | "Let ( [" 39 | & Substitute ( // convert $local to $$GLOBAL variables 40 | ¶ & ~variables; 41 | [ "¶$$" ; "¶$" ]; // ... without renaming existing $$GLOBALS 42 | [ "¶$" ; "¶$$" ] 43 | ) 44 | & "¶~ = \"\" ]; ~ )" 45 | ) ) 46 | ]; 47 | ~error = 0 // indicate success or failure of Evaluate() 48 | or Let ( $#AssignGlobal.error = ~error ; False ) // only runs on error 49 | ) 50 | -------------------------------------------------------------------------------- /Functions/#Name-Value/#GetScriptResult.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================== 3 | * #GetScriptResult ( name ) 4 | * 5 | * WARNING: 6 | * This function is deprecated in favor of the more transparent equivalent: 7 | * #Get ( Get ( ScriptResult ) ; name ). This function is still supported 8 | * for backwards compatibility. 9 | * 10 | * PURPOSE: 11 | * Retrieves values from a name-value pair list created with the # ( name ; 12 | * value ) function in the script result. This function does not set any 13 | * variables, making it non-destructive, in contrast with the 14 | * #AssignScriptResults function, which can over-write variables. This 15 | * function is equivalent to #Get ( Get ( ScriptResult ) ; name ). 16 | * 17 | * RETURNS: 18 | * The value from the name-value pair specified by the name parameter. If 19 | * such a value is not in the script result, the function returns Null. 20 | * 21 | * PARAMETERS: 22 | * name: The name of the name-value pair from which to retrieve the value. 23 | * 24 | * DEPENDENCIES: 25 | * #Get ( parameters ; name ) 26 | * 27 | * HISTORY: 28 | * MODIFIED on 2012-12-07 by Jeremy Bante to use 29 | * newer supported functions. 30 | * 31 | * REFERENCES: 32 | * Script Parameter Interface Best Practice: http://filemakerstandards.org/pages/viewpage.action?pageId=557462 33 | * ===================================== 34 | */ 35 | 36 | #Get ( Get ( ScriptResult ) ; name ) -------------------------------------------------------------------------------- /Functions/UISortListDirection.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================== 3 | * UISortListDirection ( field ) 4 | * 5 | * PURPOSE: 6 | * Return the human readable value of "Ascending" or "Descending" 7 | * based on the value stored within a reserved global variable 8 | * (see UISortToggle and UISortIndicator) 9 | * 10 | * RETURNS: 11 | * (string) either "Ascending" or "Descending" 12 | * 13 | * PARAMETERS: 14 | * field: A field reference 15 | * 16 | * EXAMPLES: 17 | * When used within a script If[] step 18 | * If [ UISortListdirection ( FunctionalArea » Tablename::fieldName ) = "Ascending" ] // = True if the same field 19 | * 20 | * DEPENDENCIES: 21 | * none 22 | * 23 | * RELEASE: 24 | * 2011-04-20 25 | * 26 | * REFERENCES: 27 | * none 28 | * ===================================== 29 | */ 30 | 31 | Let ( [ 32 | ~layoutID = GetValue ( LayoutIDs ( Get ( FileName ) ) ; Get ( LayoutNumber ) ); 33 | ~fieldName = GetFieldName ( field ); 34 | ~sortVarPrefix = "$$SORT.LAYOUT." & ~layoutID; 35 | ~sortVarField = ~sortVarPrefix & ".LIST.FIELD"; 36 | ~sortVarDirection = ~sortVarPrefix & ".LIST.DIRECTION"; 37 | ~sortCurrentField = GetAsText ( Evaluate ( ~sortVarField ) ); 38 | ~isSameSortField = ~sortCurrentField = ~fieldName; 39 | ~sortDirection = Evaluate ( ~sortVarDirection ) 40 | ]; 41 | Case ( 42 | ~isSameSortField 43 | and ~sortDirection; 44 | "Ascending"; 45 | 46 | ~isSameSortField 47 | and not ~sortDirection; 48 | "Descending"; 49 | 50 | "" 51 | ) 52 | ) -------------------------------------------------------------------------------- /Functions/#Name-Value/#GetScriptParameter.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================== 3 | * #GetScriptParameter ( name ) 4 | * 5 | * WARNING: 6 | * This function is deprecated in favor of the more transparent equivalent: 7 | * #Get ( Get ( ScriptParameter ) ; name ). This function is still 8 | * supported for backwards compatibility. 9 | * 10 | * PURPOSE: 11 | * Retrieves values from a name-value pair list created with the 12 | * # ( name ; value ) function in the script parameter. This function does 13 | * not set any variables, making it non-destructive, in contrast with the 14 | * #AssignScriptParameters function, which can over-write variables. This 15 | * function is equivalent to #Get ( Get ( ScriptParameter ) ; name ). 16 | * 17 | * RETURNS: 18 | * The value from the name-value pair specified by the name parameter. If 19 | * such a value is not in the script parameter, the function returns Null. 20 | * 21 | * PARAMETERS: 22 | * name: The name of the name-value pair from which to retrieve the value. 23 | * 24 | * DEPENDENCIES: 25 | * #Get ( parameters ; name ) 26 | * 27 | * RELEASE: 28 | * MODIFIED on 2012-12-07 by Jeremy Bante to use 29 | * newer supported functions. 30 | * 31 | * REFERENCES: 32 | * Script Parameter Interface Best Practice: http://filemakerstandards.org/pages/viewpage.action?pageId=557462 33 | * ===================================== 34 | */ 35 | 36 | #Get ( Get ( ScriptParameter ) ; name ) -------------------------------------------------------------------------------- /Functions/UISortPortalDirection.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================== 3 | * UISortPortalDirection ( field ; object ) 4 | * 5 | * PURPOSE: 6 | * Return the human readable value of "Ascending" or "Descending" 7 | * based on the value stored within a reserved global variable 8 | * (see UISortPortalToggle and UISortPortalIndicator) 9 | * 10 | * RETURNS: 11 | * (string) either "Ascending" or "Descending" 12 | * 13 | * PARAMETERS: 14 | * field: A field reference 15 | * object: Name of the object or unique id 16 | * 17 | * EXAMPLES: 18 | * When used within a script If[] step 19 | * If [ UISortPortaldirection ( FunctionalArea » Tablename::fieldName ; "portal.name" ) = "Ascending" ] // = True if the same field 20 | * 21 | * DEPENDENCIES: 22 | * none 23 | * 24 | * RELEASE: 25 | * 2011-04-20 26 | * 27 | * REFERENCES: 28 | * none 29 | * ===================================== 30 | */ 31 | 32 | Let ( [ 33 | ~fieldName = GetFieldName ( field ); 34 | ~sortVarPrefix = "$$SORT.PORTAL." & Upper ( object ); 35 | ~sortVarField = ~sortVarPrefix & ".FIELD"; 36 | ~sortVarDirection = ~sortVarPrefix & ".DIRECTION"; 37 | ~sortCurrentField = GetAsText ( Evaluate ( ~sortVarField ) ); 38 | ~isSameSortField = ~sortCurrentField = ~fieldName; 39 | ~sortDirection = Evaluate ( ~sortVarDirection ) 40 | ]; 41 | Case ( 42 | ~isSameSortField 43 | and ~sortDirection; 44 | "Ascending"; 45 | 46 | ~isSameSortField 47 | and not ~sortDirection; 48 | "Descending"; 49 | 50 | "" 51 | ) 52 | ) -------------------------------------------------------------------------------- /Functions/HTMLDocument.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================================== 3 | * HTMLDocument ( type ; head ; css ; body ) 4 | * 5 | * PURPOSE: 6 | * Create valid HTML output which can be either exported or 7 | * used within a web viewer for data display. 8 | * 9 | * RETURNS: 10 | * (string) valid HTML output 11 | * 12 | * PARAMETERS: 13 | * type = (enumeration) any of the valid html document types or none 14 | * head = (string) any content which may be used within the section 15 | * css = (string) valid css (see the HTMLCSS function) 16 | * body = (string) HTML body content 17 | * 18 | * DEPENDENCIES: 19 | * none 20 | * 21 | * NOTES: 22 | * Passing in a null value for type will force quirks mode or browser default. 23 | * This is useful to know because using a "border = 0" on the will force 24 | * IE on Windows to not show its view port border. Apply this via a css tag. 25 | * 26 | * HISTORY: 27 | * 2011-04-04 28 | * 29 | * REFERENCES: 30 | * none 31 | * ===================================================== 32 | * 33 | */ 34 | 35 | List( 36 | Case( 37 | type = "html4t"; 38 | ""; 39 | 40 | type = "xhtmlt"; 41 | ""; 42 | 43 | "" // default is no document type (see notes above) 44 | ); 45 | ""; 46 | ""; 47 | head; 48 | css; 49 | ""; 50 | ""; 51 | body; 52 | ""; 53 | "" 54 | ) 55 | -------------------------------------------------------------------------------- /Functions/UUID/UUIDGetTimestamp.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ====================================== 3 | * UUIDGetTimestamp ( theID ) 4 | * 5 | * PURPOSE: 6 | * Extracts the creation timestamp from a numeric UUID created by this 7 | * family of UUID functions. The returned value can be converted to a more 8 | * conventional format with the GetAsTimestamp function. 9 | * 10 | * RETURNS: 11 | * A number representing the number of seconds since 0001-01-01 00:00:00 12 | * when the UUID passed as a parameter to the function was created. Returns 13 | * Null ("") if the UUID is in a format that does not contain a retrievable 14 | * timestamp. 15 | * 16 | * PARAMETERS: 17 | * theID: The UUID to extract a timestamp from. 18 | * 19 | * DEPENDENCIES: none 20 | * 21 | * RELEASE: 2011-04-06 22 | * 23 | * REFERENCES: 24 | * Key values Best Practice: http://filemakerstandards.org/pages/viewpage.action?pageId=557138 25 | * Ray Cologon's uID functions: http://www.nightwing.com.au/FileMaker/demos9/demo910.html 26 | * ====================================== 27 | */ 28 | Let( [ 29 | ~idAsNumber = GetAsNumber ( theID ); 30 | ~length = Length ( ~idAsNumber ); 31 | ~version = Left ( ~idAsNumber ; ~length - 40 ) 32 | ]; 33 | If ( ~version < 3; 34 | GetAsTimestamp ( GetAsNumber ( 35 | Middle ( ~idAsNumber ; ~length - 38 ; 12 ) //seconds 36 | & "." 37 | & Middle ( ~idAsNumber ; ~length - 26 ; 7 ) //sub-seconds 38 | ) ) 39 | 40 | /* Else, UUID does not have a timestamp; return Null */ 41 | ) 42 | ) -------------------------------------------------------------------------------- /Functions/FileOSPath.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================================== 3 | * FileOSPath ( path ; escaped ) 4 | * 5 | * PURPOSE: 6 | * Provide an OS specific wrapper around FileMaker's 7 | * own path functions. 8 | * 9 | * RETURNS: 10 | * (string) OS valid path 11 | * 12 | * EXAMPLES: 13 | * FileOSPath ( Get ( FilePath ) ; True ) = "/Volumes/HardDriveName/Users/someone/Folder\ with\ spaces/FileName.fmp12" 14 | * 15 | * DEPENDENCIES: 16 | * none 17 | * 18 | * NOTES: 19 | * This is useful when used with plugins or scripting 20 | * languages which need a valid OS path. 21 | * @todo add in windows escaping 22 | * 23 | * HISTORY: 24 | * RELEASED: 2013-08-21 by matt@filemakermagazine.com 25 | * 26 | * REFERENCES: 27 | * https://github.com/filemakerstandards/fmpstandards/blob/master/Functions/FileOSPath.fmfn 28 | * ===================================================== 29 | * 30 | */ 31 | 32 | Let( [ 33 | ~newPath = If ( Left( path; 5) = "file:"; 34 | Substitute ( 35 | Middle( path; 6; 1000000 ); 36 | [ "¶file:" ; ¶ ] 37 | ); 38 | // default 39 | path 40 | ); 41 | ~macPath = "/Volumes" & Substitute( ~newPath; ¶; "¶/Volumes"); 42 | ~winPath = Substitute( Middle( Substitute ( ~newPath ; "/" ; "\\" ); 2; 1000000 ); "¶\\"; "¶"); 43 | ~goPath = ~newPath 44 | ]; 45 | Choose ( Abs ( Get( SystemPlatform ) ) - 1 ; 46 | // Macintosh 47 | If ( escaped; Substitute( ~macPath; [" "; "\ "]; [","; "\,"] ); ~macPath ); 48 | 49 | // Windows 50 | ~winPath; 51 | 52 | // iOS 53 | ~goPath 54 | ) 55 | ) 56 | -------------------------------------------------------------------------------- /Functions/WindowActiveTabs.fmfn: -------------------------------------------------------------------------------- 1 | /* 2 | * ===================================================== 3 | * WindowActiveTabs () 4 | * https://github.com/petrowsky/fmpstandards/blob/master/Functions/WindowActiveTabs.fmfn 5 | * 6 | * PARAMETERS: none 7 | * RETURNS: 8 | * (list) List of all active frontmost tabs 9 | * DEPENDENCIES: 10 | * none 11 | * NOTES: 12 | * Original by Koen Van Hulle (SHpartners.com) 13 | * ===================================================== 14 | */ 15 | 16 | Let ( [ 17 | 18 | $~i = $~i +1; // increment counter 19 | $~objects = LayoutObjectNames ( Get( FileName ); Get ( LayoutName ) ); 20 | ~tabToCheck = GetValue ( $~objects ; $~i ); 21 | ~parentTab = GetLayoutObjectAttribute ( ~tabToCheck; "enclosingObject" ); 22 | ~isFront = GetLayoutObjectAttribute ( ~tabToCheck ; "isFrontTabPanel" ); 23 | ~isCurrentTab = 24 | Case( 25 | IsEmpty ( ~tabToCheck ); // there's no tab name 26 | False; 27 | 28 | ~isFront 29 | and IsEmpty ( ~parentTab ); // does not have a parent 30 | True; 31 | 32 | ~isFront 33 | and PatternCount ( ¶ & $~tabList & ¶; ¶ & ~parentTab & ¶ ); // parent is already in $~tabList 34 | True; 35 | 36 | /*Else*/ 37 | False 38 | ); 39 | $~tabList = List ( $~tabList ; If ( ~isCurrentTab ; ~tabToCheck ) ) 40 | 41 | ]; 42 | 43 | If ( not IsEmpty ( ~tabToCheck ); 44 | WindowActiveTabs; 45 | /*Else*/ 46 | $~tabList 47 | & Let ( [ 48 | // purge variables 49 | $~i = ""; 50 | $~objects = ""; 51 | $~tabList = "" 52 | ]; 53 | "" // null 54 | ) 55 | ) 56 | 57 | ) 58 | -------------------------------------------------------------------------------- /Functions/Developer.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================================== 3 | * Developer ( ) 4 | * https://github.com/petrowsky/fmpstandards/blob/master/Functions/Developer.fmfn 5 | * 6 | * PARAMETERS: 7 | * none 8 | * 9 | * RETURNS: 10 | * (bool) True or False based on proper 11 | * evaluation 12 | * 13 | * DEPENDENCIES: 14 | * none 15 | * 16 | * NOTES: 17 | * Because Filemaker Changed Get ( PrivilegeSetName ) 18 | * Evaluate () must be used 19 | * 20 | * RELEASE: 21 | * 2011-02-23 22 | * ===================================================== 23 | * 24 | */ 25 | 26 | Let ( [ 27 | ~developers = List ( "" ; "" ); // Add names of the accounts which are valid developer accounts 28 | ~version = Get ( ApplicationVersion ); 29 | ~isGo = PatternCount ( ~version; "Go" ) = True; 30 | ~versionNumber = GetAsNumber ( Substitute ( ~version ; "," ; "." ) ); // account for foreign versions using comma 31 | // Add "developer" extended privilege to groups which are valid developer groups 32 | ~extendedPrivileges = 33 | If ( ~isGo or ~versionNumber ≥ 11 ; 34 | Evaluate ( "Get ( AccountExtendedPrivileges )" ) ; 35 | /*else*/ 36 | Evaluate ( "Get ( ExtendedPrivileges )" ) 37 | ) 38 | ]; 39 | PatternCount ( ¶& ~developers &¶ ; ¶& Get ( AccountName ) &¶ ) ≥ 1 40 | or PatternCount ( ¶& ~extendedPrivileges &¶ ; ¶& "developer" &¶ ) ≥ 1 41 | or 42 | If ( ~isGo or ~versionNumber ≥ 11 ; 43 | Evaluate ( "Get ( AccountPrivilegeSetName )" ) = "[Full Access]" ; 44 | /*else*/ 45 | Evaluate ( "Get ( PrivilegeSetName )" ) = "[Full Access]" 46 | ) 47 | ) 48 | -------------------------------------------------------------------------------- /Functions/LayoutVariables.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================== 3 | * LayoutVariables ( layout ) 4 | * 5 | * PURPOSE: 6 | * Extract all layout objects named as a $variable 7 | * in order to declare those objects and their 8 | * content as valid $variables. 9 | * 10 | * RETURNS: 11 | * (string) Internal portion of a Let() for use with #Assign() 12 | * 13 | * PARAMETERS: 14 | * layout = the name of the target layout 15 | * 16 | * EXAMPLES: 17 | * Name a given field as $field.variable then call 18 | * LayoutVariables ( Get ( Layoutname ) ) // returns $field.variable = "field content" 19 | * 20 | * DEPENDENCIES: 21 | * ValueFilter(), LayoutObjects(), CustomList() 22 | * 23 | * NOTES: 24 | * This function can be used to get information from 25 | * fields named as $variables in a very quick fashion. 26 | * 27 | * HISTORY: 28 | * MODIFIED on 2011-07-08 by matt@filemakermagazine.com - added 29 | * 30 | * REFERENCES: 31 | * none 32 | * ===================================== 33 | */ 34 | 35 | 36 | Let( [ 37 | $~layoutVariables = ValueFilter ( "$" ; LayoutObjects ( layout ) ); // will find both local and global variables 38 | ~function = "Let ( ~value = GetValue ( $~layoutVariables ; [n] ); 39 | If ( Left ( ~value ; 1 ) = \"$\"; 40 | Substitute ( ~value; 41 | [ ~value ; ~value & \" = \" & Quote ( GetLayoutObjectAttribute ( ~value ; \"content\" ) ) ] 42 | ) 43 | ) 44 | ) 45 | " 46 | ]; 47 | // wrap semi-colons on ends of lines 48 | Substitute ( CustomList ( 1 ; ValueCount ( $~layoutVariables ); ~function ) ; [ ¶ ; ";¶" ] ) 49 | ) 50 | -------------------------------------------------------------------------------- /Functions/WindowProperties.fmfn: -------------------------------------------------------------------------------- 1 | /* 2 | * ===================================================== 3 | * WindowProperties ( ) 4 | * 5 | * RETURNS: 6 | * Let () formatted list of global variables with prefix of 'window' 7 | * 8 | * PARAMETERS: 9 | * none 10 | * 11 | * EXAMPLES: 12 | * Substitute ( WindowProperties ; "$$" ; "$" ) //= locally scoped $window[Value] variables 13 | * Substitute ( WindowProperties ; "$$" ; "$parent" ) //= locally scoped $parentwindow[Value] variables 14 | * 15 | * RETURNS: 16 | * formatted global let variables 17 | * 18 | * DEPENDENCIES: 19 | * none 20 | * 21 | * NOTES: 22 | * See WindowCenter companion function. It will center a window within a parent 23 | * window when $parentWindow[Value] variables are set. 24 | * ===================================================== 25 | * 26 | */ 27 | 28 | Substitute ( 29 | List ( 30 | "$$windowName = " & Quote ( Get ( WindowName ) ); 31 | "$$windowLayout = " & Quote ( Get ( LayoutName ) ); 32 | "$$windowLayoutID = " & GetValue ( LayoutIDs ( Get ( FileName ) ) ; Get ( LayoutNumber ) ); 33 | "$$windowTop = " & Get ( WindowTop ); 34 | "$$windowLeft = " & Get ( WindowLeft ); 35 | "$$windowWidth = " & Get ( WindowWidth ); 36 | "$$windowHeight = " & Get ( WindowHeight ); 37 | "$$windowViewAs = " & Get ( LayoutViewState ); 38 | "$$windowMode = " & Get ( WindowMode ); 39 | "$$windowStatusArea = " & Get ( StatusAreaState ); 40 | "$$windowZoomLevel = " & Get ( WindowZoomLevel ); 41 | "$$windowTextRuler = " & Get ( TextRulerVisible ); 42 | ); 43 | [ ¶ ; ";¶" ] // adding semi colons to each line 44 | ) 45 | -------------------------------------------------------------------------------- /Functions/IsNumber.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================================== 3 | * IsNumber ( value ) 4 | * 5 | * PURPOSE: 6 | * Determine if the supplied value is numeric 7 | * PARAMETERS: 8 | * value = (string or number) 9 | * RETURNS: 10 | * (boolean) whether value is numeric or not 11 | * DEPENDENCIES: 12 | * none 13 | * NOTES: 14 | * Adapted from the following function 15 | * http://fmfunctions.com/functions_display_record.php?functionId=161 16 | * ===================================================== 17 | * 18 | */ 19 | 20 | Let ( [ 21 | ~decimalSeparator = Middle ( 3/2 ; 2 ; 1 ); // do some division to get separator 22 | ~cleanedValue = Substitute ( value ; 23 | [ "e+" ; "" ]; 24 | [ "E+" ; "" ]; 25 | [ "e-" ; "" ]; 26 | [ "E-" ; "" ] 27 | ); 28 | ~filteredValue = Filter ( ~cleanedValue ; "1234567890-" & ~decimalSeparator ); 29 | ~decimalCount = PatternCount ( value ; ~decimalSeparator ) 30 | ]; 31 | Exact ( ~filteredValue ; ~cleanedValue ) 32 | and ( Left ( value ; 1 ) ≠ "0" 33 | or value = "0" 34 | or Left ( value ; 2 ) = "-0" 35 | or Left ( value ; 2 ) = "0" & ~decimalSeparator 36 | ) 37 | and ~decimalCount ≤ 1 38 | and value ≠ "-" 39 | ) 40 | 41 | /* 42 | // Unit tests 43 | List( 44 | "These should be true"; 45 | IsNumber ( 0 ); 46 | IsNumber ( -0 ); 47 | IsNumber ( 0.1 ); 48 | IsNumber ( 1 ); 49 | IsNumber ( 1.2 ); 50 | IsNumber ( -1.2 ); 51 | IsNumber ( 1.234e+124 ); 52 | IsNumber ( -1.234e+124 ); 53 | IsNumber ( 10^50 ); 54 | "These should be false"; 55 | IsNumber ( "abc" ); 56 | IsNumber ( "1.2.3" ); 57 | IsNumber ( "0001" ); 58 | ) 59 | */ 60 | -------------------------------------------------------------------------------- /Functions/SetError.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================== 3 | * SetError ( type ; num ; dialog ; capture ; halt ) 4 | * 5 | * PURPOSE: 6 | * Explicitly set an error. Useful when setting an application 7 | * error or getting a plugin or custom function error 8 | * 9 | * RETURNS: 10 | * (array) A $variable array of $error variables 11 | * 12 | * PARAMETERS: 13 | * type = (enumeration) fmp, app, plugin, func 14 | * num = (number) the numerical value for that error type 15 | * dialog = (bool) whether the error should show a dialog 16 | * capture = (bool) whether the error should be captured 17 | * halt = (bool) whether the error should cause a script halt 18 | * 19 | * EXAMPLES: 20 | * (see Error function) 21 | * 22 | * DEPENDENCIES: 23 | * Error(), ErrorData(), ErrorString() 24 | * 25 | * NOTES: 26 | * See the Error function for information about error overrides 27 | * 28 | * HISTORY: 29 | * MODIFIED on 2011-03-20 by matt@filemakermagazine.com - reformatted 30 | * 31 | * REFERENCES: 32 | * none 33 | * ===================================== 34 | */ 35 | 36 | Error & 37 | Substitute ( // add semi-colons for proper Let() formatting 38 | List ( 39 | "$errorState = " & True; // explicitly set the error to true 40 | "$errorType = " & Quote ( type ); // enumeration app, plugin, func 41 | "$errorNum = " & num; // error number 42 | "$errorString = " & Quote ( ErrorString ( type ; num ) ); // override for errorString - put your errors in ErrorString 43 | "$errorDialog = " & dialog; 44 | "$errorCapture = " & capture; 45 | "$errorHalt = " & halt; 46 | ); 47 | [ ¶ ; ";¶" ] 48 | ) 49 | -------------------------------------------------------------------------------- /Functions/Dirname.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================================== 3 | * Dirname ( path ) 4 | * 5 | * PURPOSE: 6 | * Return the parent folder of the supplied path 7 | * PARAMETERS: 8 | * path = (string) 9 | * RETURNS: 10 | * (string) path to parent folder 11 | * DEPENDENCIES: 12 | * none 13 | * NOTES: 14 | * This function does not query to operating system 15 | * to determine whether the path is a file or folder 16 | * it can only use information within the path - such 17 | * as the existence of a trailing path separator 18 | * ===================================================== 19 | * 20 | */ 21 | 22 | Let ( [ 23 | ~separatorMac = PatternCount ( path ; "/" ); 24 | ~separatorWin = PatternCount ( path ; "\\" ); 25 | ~separator = If ( ~separatorMac > 0 ; "/" ; "\\"); 26 | ~lastCharIsSeparator = Right ( path ; 1 ) = ~separator; 27 | ~pathParts = Substitute ( path ; ~separator ; ¶ ); 28 | ~partCount = ValueCount ( ~pathParts ) 29 | ]; 30 | 31 | Substitute ( LeftValues ( ~pathParts ; If ( ~lastCharIsSeparator ; ~partCount ; ~partCount -1 ) - 1 ) ; ¶ ; ~separator ) 32 | 33 | ) 34 | 35 | /* 36 | //Unit tests 37 | List ( 38 | Dirname ( "/Volumes/Macintosh HD/Users/User Name/Library/Application Support/FileMaker/Extensions/" ); 39 | Dirname ( "C:\Documents and Settings\User Name\Local Settings\Application Data\FileMaker\Extensions\\" ); 40 | Dirname ( "C:\Users\User Name\AppData\Local\FileMaker\Extensions" ); 41 | Dirname ( "file:Folder/Parent/Path" ); 42 | Dirname ( "file:Folder/Parent/Path/" ); 43 | Dirname ( "file:/LightSpeed/Users/matt/Dropbox/Transfer/FMP Standards/Standards.fp7" ); 44 | ) 45 | */ 46 | -------------------------------------------------------------------------------- /Functions/PluginExists.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================================== 3 | * PluginExists ( name ) 4 | * 5 | * PURPOSE: 6 | * Return a return delimited list of the values returned by 7 | * Get ( InstalledFMPlugins ) for one specific plugin name. 8 | * 9 | * RETURNS: 10 | * (string) Return delimted list of name, version & status 11 | * or False if the plugin does not exist 12 | * 13 | * EXAMPLES: 14 | * PluginExists ( "SomePlugin" ) = "SomePlugin¶1.12¶Enabled" 15 | * PluginExists ( "MissingPlugin" ) = False 16 | * 17 | * DEPENDENCIES: 18 | * none 19 | * 20 | * NOTES: 21 | * This is convenience function for extracting the values from 22 | * the internal Get ( InstalledFMPlugins ) function 23 | * 24 | * HISTORY: 25 | * MODIFIED: 2013-12-19 added trailing return to list for plugins listed last. 26 | * MODIFIED: 2013-08-21 added reference to repo. 27 | * RELEASED: 2012-06-19 by matt@filemakermagazine.com 28 | * 29 | * REFERENCES: 30 | * https://github.com/filemakerstandards/fmpstandards/blob/master/Functions/PluginExists.fmfn 31 | * ===================================================== 32 | * 33 | */ 34 | 35 | Let ( [ 36 | ~pluginList = Get ( InstalledFMPlugins ) & ¶; 37 | ~pluginExists = PatternCount ( ~pluginList ; name & ";" ); 38 | ~pluginStringStart = Position ( ~pluginList ; name ; 1 ; 1 ); 39 | ~pluginStringEnd = Position ( ~pluginList ; ¶ ; ~pluginStringStart ; 1 ); 40 | ~pluginString = Middle ( ~pluginList ; ~pluginStringStart ; ~pluginStringEnd - ~pluginStringStart ); 41 | ~pluginParts = Substitute ( ~pluginString ; ";" ; ¶ ) 42 | ]; 43 | If ( ~pluginExists ; ~pluginParts ; False ) 44 | ) 45 | -------------------------------------------------------------------------------- /Functions/MergeVariables.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================== 3 | * MergeVariables ( content ; variables ) 4 | * 5 | * PURPOSE: 6 | * Replace all merge <<$variables>> with 7 | * data found in those layout objects named 8 | * as valid $variables. 9 | * 10 | * RETURNS: 11 | * (string) Merged content 12 | * 13 | * PARAMETERS: 14 | * content = A string of text containing <<$variables>> 15 | * variables = Return delimited string of $variable = assignments (see LayoutVariables function) 16 | * 17 | * EXAMPLES: 18 | * MergeVariables ( "This contains <<$merge>> data" ; LayoutVariables ( Get ( LayoutName ) ) ) 19 | * // where a field on the layout is simply named $merge - result: "This contains awesome data" 20 | * // where the layout field named $merge contains "awesome" 21 | * 22 | * DEPENDENCIES: 23 | * #Assign() 24 | * 25 | * NOTES: 26 | * This function is designed to be used with the LayoutVariables() function. 27 | * The exposed nature of the Evaluate() could be a security consideration. 28 | * Historical credit given to Mike Duncan http://www.briandunning.com/cf/438 29 | * 30 | * HISTORY: 31 | * MODIFIED on 2011-07-08 by matt@filemakermagazine.com - added 32 | * 33 | * REFERENCES: 34 | * none 35 | * ===================================== 36 | */ 37 | 38 | If ( #Assign ( variables ); 39 | 40 | Let ( [ 41 | ~mergeString = Substitute ( Quote ( content ) ; ["<<" ; "\" & "] ; [">>" ; " & \""] ) 42 | ]; 43 | If ( IsValidExpression ( ~mergeString ); 44 | Evaluate ( ~mergeString ); 45 | False // merge content could not be evaluated 46 | ) 47 | ); 48 | 49 | False // variables could not be created 50 | ) 51 | -------------------------------------------------------------------------------- /Functions/UISortPortalToggle.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================== 3 | * UISortPortalToggle ( field ; object ) 4 | * 5 | * PURPOSE: 6 | * Assign a reserved set of global variables to values used 7 | * by a sorting script (see UISortPortalDirection and UISortPortalIndicator) 8 | * Reserved global variables are classed to $$SORT.PORTAL.[object].* where 9 | * [id] is the internal ID of the current layout. 10 | * 11 | * RETURNS: 12 | * (boolean) The result of a successful assignment of variables 13 | * 14 | * PARAMETERS: 15 | * field: A field reference 16 | * 17 | * EXAMPLES: 18 | * When used as a script parameter on a button defined to run a sort script 19 | * UISortPortalToggle ( FunctionalArea » Tablename::fieldName ; "portal.name" ) // = Assignment of $$SORT.PORTAL.[object].* variables 20 | * 21 | * DEPENDENCIES: 22 | * #Assign() 23 | * 24 | * RELEASE: 25 | * 2011-04-20 26 | * 27 | * REFERENCES: 28 | * none 29 | * ===================================== 30 | */ 31 | 32 | Let ( [ 33 | ~fieldName = GetFieldName ( field ); 34 | ~sortVarPrefix = "$$SORT.PORTAL." & Upper ( object ); 35 | ~sortVarField = ~sortVarPrefix & ".FIELD"; 36 | ~sortVarDirection = ~sortVarPrefix & ".DIRECTION"; 37 | ~sortCurrentField = GetAsText ( Evaluate ( ~sortVarField ) ); 38 | ~isNewFieldSelection = ~sortCurrentField ≠ ~fieldName; 39 | ~sortNewDirection = If ( ~isNewFieldSelection ; 1 ; Evaluate ( ~sortVarDirection ) xor True ); 40 | ~variables = List ( 41 | ~sortVarField & " = " & Quote ( ~fieldName ) & ";"; 42 | ~sortVarDirection & " = " & ~sortNewDirection; 43 | ) 44 | ]; 45 | If ( #Assign ( ~variables ) ; Evaluate ( ~sortVarField ) ; False ) 46 | ) -------------------------------------------------------------------------------- /Functions/UISortListToggle.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================== 3 | * UISortListToggle ( field ) 4 | * 5 | * PURPOSE: 6 | * Assign a reserved set of global variables to values used 7 | * by a sorting script (see UISortDirection and UISortIndicator) 8 | * Reserved global variables are classed to $$SORT.LAYOUT.[id].* where 9 | * [id] is the internal ID of the current layout. 10 | * 11 | * RETURNS: 12 | * (boolean) The result of a successful assignment of variables 13 | * 14 | * PARAMETERS: 15 | * field: A field reference 16 | * 17 | * EXAMPLES: 18 | * When used as a script parameter on a button defined to run a sort script 19 | * UISortToggle ( FunctionalArea » Tablename::fieldName ) // = Assignment of $$SORT.LAYOUT.[ID].* variables 20 | * 21 | * DEPENDENCIES: 22 | * #Assign() 23 | * 24 | * RELEASE: 25 | * 2011-04-20 26 | * 27 | * REFERENCES: 28 | * none 29 | * ===================================== 30 | */ 31 | 32 | Let ( [ 33 | ~layoutID = GetValue ( LayoutIDs ( Get ( FileName ) ) ; Get ( LayoutNumber ) ); 34 | ~fieldName = GetFieldName ( field ); 35 | ~sortVarPrefix = "$$SORT.LAYOUT." & ~layoutID; 36 | ~sortVarField = ~sortVarPrefix & ".LIST.FIELD"; 37 | ~sortVarDirection = ~sortVarPrefix & ".LIST.DIRECTION"; 38 | ~sortCurrentField = GetAsText ( Evaluate ( ~sortVarField ) ); 39 | ~isNewFieldSelection = ~sortCurrentField ≠ ~fieldName; 40 | ~sortNewDirection = If ( ~isNewFieldSelection or not Get ( SortState ) ; 1 ; Evaluate ( ~sortVarDirection ) xor True ); 41 | ~variables = List ( 42 | ~sortVarField & " = " & Quote ( ~fieldName ) & ";"; 43 | ~sortVarDirection & " = " & ~sortNewDirection; 44 | ) 45 | ]; 46 | #Assign ( ~variables ) 47 | ) -------------------------------------------------------------------------------- /Functions/#Name-Value/VerifyVariablesNotEmpty.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================== 3 | * VerifyVariablesNotEmpty ( nameList ) 4 | * 5 | * RETURNS: 6 | * True (1) if a locally scoped $variable matching each value in nameList 7 | * is not empty; False (0) otherwise. 8 | * 9 | * PARAMETERS: 10 | * nameList: A return-limited list of names to check. Names do not need to 11 | * include $ prefixes. 12 | * 13 | * DEPENDENCIES: none 14 | * 15 | * NOTES: 16 | * Names containing "$" or "$$" prefixes will be treated as identical to 17 | * names not beginning with those prefixes — this function only checks for 18 | * local $variables. 19 | * 20 | * HISTORY: 21 | * MODIFIED on 2013-07-10 by Jeremy Bante to correct 22 | * a bug with support for trailing returns. 23 | * MODIFIED on 2013-07-05 by Jeremy Bante to support 24 | * one trailing return in the nameList parameter. 25 | * CREATED on 2012-12-07 by Jeremy Bante . 26 | * ===================================== 27 | */ 28 | 29 | If ( IsEmpty ( nameList ) ; 30 | True ; 31 | /* Else */ 32 | Let ( [ 33 | ~start = Get ( UUID ) ; 34 | ~end = Get ( UUID ) ; 35 | nameList = // normalize nameList so all values begin with "$" 36 | Substitute ( 37 | ~start & ¶ & nameList & ~end ; 38 | [ ¶ & ~end ; "" ] ; 39 | [ ~end ; "" ] ; 40 | [ "¶$$" ; "¶" ] ; 41 | [ "¶$" ; "¶" ] ; 42 | [ "¶" ; "¶$" ] ; 43 | [ ~start & ¶ ; "" ] 44 | ) ; 45 | ~testExpression = 46 | "not IsEmpty ( " 47 | & Substitute ( nameList ; ¶ ; " )¶and not IsEmpty ( " ) 48 | & " ) " 49 | ] ; 50 | IsValidExpression ( ~testExpression ) 51 | and Evaluate ( ~testExpression ) 52 | ) 53 | ) -------------------------------------------------------------------------------- /Functions/DeveloperProperties.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================================== 3 | * DeveloperProperties ( hide ) 4 | * 5 | * PURPOSE: 6 | * Set reserved namespace variables of 7 | * $$ CURRENT.WHATEVER to contain useful 8 | * developer information for the purpose 9 | * of viewing within the Data Viewer. 10 | * Typically used within conditional 11 | * formatting on layout text label. 12 | * PARAMETERS: 13 | * @hide = (boolean) True or False 14 | * RETURNS: 15 | * (boolean) True or False 16 | * DEPENDENCIES: 17 | * Developer(), ObjectID(), LayoutObjectHierarchy() 18 | * NOTES: 19 | * Note the use of the space after the $$ 20 | * this causes these global variables to 21 | * be sorted at the top of the Data Viewer 22 | * ===================================================== 23 | * 24 | */ 25 | 26 | If ( Developer; // only set global variables for developers to prevent snooping 27 | Let ( [ 28 | $$ CURRENT.TABLE = Get ( LayoutTableName ); 29 | $$ CURRENT.LAYOUT = Get ( LayoutName ); 30 | $$ CURRENT.FIELD = Get ( ActiveFieldName ); 31 | $$ CURRENT.FIELDTABLE = Get ( ActiveFieldTableName ); 32 | $$ CURRENT.OBJECTS = LayoutObjectHierarchy ( "" ); 33 | $$ CURRENT.OBJECTIDS = 34 | "Layout ID = " & ObjectID ( Get ( LayoutName ) ; "Layout" ; "" ; "" ) 35 | & " | " & 36 | "Table ID = " & ObjectID ( Get ( LayoutTableName ) ; "Table" ; "" ; "" ); 37 | $$ CURRENT.FILEPATH = Get ( FilePath ) 38 | ]; 39 | // This result is intended to be used on conditional formatting 40 | // when the text is set to hide using the same color as the background 41 | If ( not hide ; False ; True ) 42 | ); 43 | 44 | True // Always return true for non developer when used on conditional formatting 45 | ) 46 | -------------------------------------------------------------------------------- /Functions/DeveloperPropertiesDisplay.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================================== 3 | * DeveloperPropertiesDisplay ( ) 4 | * 5 | * PURPOSE: 6 | * Used to display the information created 7 | * in the DeveloperProperties function 8 | * PARAMETERS: 9 | * none 10 | * RETURNS: 11 | * (string) Whether or not there is a calculation error 12 | * DEPENDENCIES: 13 | * Developer(), DeveloperProperties() 14 | * NOTES: 15 | * ===================================================== 16 | * 17 | */ 18 | 19 | If ( Developer; 20 | Let ( [ 21 | var.prefix = " " 22 | ]; 23 | List ( 24 | "TABLE OCCURRENCE NAME"; 25 | var.prefix & Substitute ( 26 | $$ CURRENT.TABLE; 27 | [ ¶ ; ¶& var.prefix ] 28 | ); 29 | "------------------"; 30 | "LAYOUT NAME"; 31 | var.prefix & Substitute ( 32 | $$ CURRENT.LAYOUT; 33 | [ ¶ ; ¶& var.prefix ] 34 | ); 35 | "------------------"; 36 | "FIELD"; 37 | var.prefix & Substitute ( 38 | $$ CURRENT.FIELD; 39 | [ ¶ ; ¶& var.prefix ] 40 | ); 41 | "------------------"; 42 | "FIELD'S TABLE"; 43 | var.prefix & Substitute ( 44 | $$ CURRENT.FIELDTABLE; 45 | [ ¶ ; ¶& var.prefix ] 46 | ); 47 | "------------------"; 48 | "OBJECT NAMES"; 49 | var.prefix & Substitute ( 50 | $$ CURRENT.OBJECTS; 51 | [ ¶ ; ¶& var.prefix ] 52 | ); 53 | "------------------"; 54 | "OBJECT IDS"; 55 | var.prefix & Substitute ( 56 | $$ CURRENT.OBJECTIDS; 57 | [ ¶ ; ¶& var.prefix ] 58 | ); 59 | "------------------"; 60 | "FILE PATH"; 61 | var.prefix & Substitute ( 62 | $$ CURRENT.FILEPATH; 63 | [ ¶ ; ¶& var.prefix ] 64 | ); 65 | ) 66 | ) 67 | ) 68 | -------------------------------------------------------------------------------- /Functions/#Name-Value/#Assign.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================== 3 | * #Assign ( parameters ) 4 | * 5 | * PURPOSE: 6 | * Parses a Let dictionary of name-value parameters into a series of 7 | * locally scoped $variables. 8 | * 9 | * RETURNS: 10 | * True (1) when parameters is a valid set of name-value pairs (which 11 | * includes the empty string); False (0) otherwise. 12 | * If False (0), the error code will be saved to: $#Assign.error 13 | * 14 | * PARAMETERS: 15 | * parameters: A Let format dictionary, such as produced by the 16 | * # ( name ; value ) function. 17 | * 18 | * DEPENDENCIES: none 19 | * 20 | * NOTES: 21 | * Notes go here 22 | * 23 | * HISTORY: 24 | * MODIFIED on 2015-10-27 by Matt Petrowsky to support weak formatted 25 | * return delimited input. 26 | * MODIFIED on 2013-03-15 by Daniel Smith to 27 | * update documentation to match functions actual return value. 28 | * MODIFIED on 2012-11-28 by Jeremy Bante to support 29 | * "$" prefix values as default. 30 | * CREATED on 2012-11-11 by Jeremy Bante . 31 | * ===================================== 32 | */ 33 | 34 | Let ( [ 35 | ~variables = Case ( 36 | not PatternCount ( parameters ; ";¶" ) 37 | and not IsEmpty ( parameters ); 38 | Substitute ( parameters ; ¶ ; ";¶" ) & ";"; 39 | 40 | parameters 41 | ); 42 | ~error = 43 | EvaluationError ( Evaluate ( 44 | "Let ( [¶" 45 | & Substitute ( // convert global variables to local variables 46 | ¶ & ~variables; 47 | "¶$$"; 48 | "¶$" 49 | ) 50 | & "¶~ = \"\" ]; ~ )" 51 | ) ) 52 | ]; 53 | ~error = 0 // indicate success or failure of Evaluate() 54 | or Let ( $#Assign.error = ~error ; False ) // only runs on error 55 | ) 56 | -------------------------------------------------------------------------------- /Functions/FilterASCII.fmfn: -------------------------------------------------------------------------------- 1 | /* 2 | FilterASCII function 3 | 4 | Author 5 | *COMMENT Visual Realisation 6 | 7 | Format 8 | FilterASCII ( text ; extended ) 9 | 10 | Parameters 11 | text - any text expression or text field 12 | extended - 0 or False, 1 or True 13 | 14 | Data type returned 15 | text 16 | 17 | Description 18 | Removes characters from text, retaining only printable characters included in the American Standard Code for Information Interchange (US-ASCII) character set. 19 | 20 | The following characters are passed by the filter: 21 | • Horizontal Tab [9]; 22 | • Carriage Return [13]; 23 | • Space [32]; 24 | • Characters [33]..[126]. 25 | 26 | When the extended parameter is True(1), characters in the extended ASCII character set range [128]..[255] are also passed. 27 | 28 | March 27, 2011 29 | */ 30 | 31 | Let ( 32 | filterText = 33 | GetAsURLEncoded ( text ) 34 | & 35 | Char ( 125001240012300096000940009200062000600003700034000930009100035000630004700044000360004300061000380006400058000590004100040000390004200033000320001300009 ) 36 | 37 | & Case ( extended ; 38 | Char ( 191001900018900188001870018600185001840018300182001810018000179001780017700176001750017400173001720017100170001690016800167001660016500164001630016200161001600015900158001570015600155001540015300152001510015000149001480014700146001450014400143001420014100140001390013800137001360013500134001330013200131001300012900128 ) 39 | & 40 | Char ( 41 | 255002540025300252002510025000249002480024700246002450024400243002420024100240002390023800237002360023500234002330023200231002300022900228002270022600225002240022300222002210022000219002180021700216002150021400213002120021100210002090020800207002060020500204002030020200201002000019900198001970019600195001940019300192 ) 42 | ) 43 | ; 44 | Filter ( text ; filterText ) 45 | ) 46 | -------------------------------------------------------------------------------- /Functions/#Name-Value/#Get.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================== 3 | * #Get ( parameters ; name ) 4 | * 5 | * RETURNS: 6 | * A named value from a string of name-value pairs. 7 | * 8 | * PARAMETERS: 9 | * parameters: A string of name-value pairs 10 | * name: The name to retrieve the value of 11 | * 12 | * DEPENDENCIES: none 13 | * 14 | * NOTES: 15 | * When a name is defined more than once in parameters, the last value is 16 | * returned. 17 | * 18 | * HISTORY: 19 | * MODIFIED on 2013-01-08 by Jeremy Bante to handle 20 | * leading $$ and $ in the name parameter the same as the 21 | * # ( name ; value ) function, based on a comment by Dan Smith. 22 | * CREATED on 2012-12-05 by Jeremy Bante . 23 | * ===================================== 24 | */ 25 | 26 | Let ( [ 27 | parameters = ¶ & parameters & ¶; 28 | name = // strip any leading $$ or $ for backwards compatibility 29 | Substitute ( ¶ & name ; [ "¶$$" ; "" ] ; [ "¶$" ; "" ] ; [ ¶ ; "" ] ) ; 30 | 31 | ~length = Length ( parameters ); 32 | ~namePosition = // position of last occurrence of name 33 | Position ( parameters ; "¶$" & name & " = " ; ~length ; -1 ); 34 | ~namePosition = // check for $$global name for backwards compatibility 35 | If ( ~namePosition > 0; 36 | ~namePosition; 37 | /* Else */ 38 | Position ( parameters ; "¶$$" & name & " = " ; ~length ; -1 ) 39 | ); 40 | 41 | ~valueStart = Position ( parameters ; " = " ; ~namePosition ; 1 ) + 3; 42 | ~valueEnd = Position ( parameters ; ";¶" ; ~namePosition ; 1 ); 43 | ~value = Middle ( parameters ; ~valueStart ; ~valueEnd - ~valueStart ) 44 | ]; 45 | // check that ~value exists and is valid 46 | If ( ~namePosition > 0 and IsValidExpression ( ~value ); 47 | Evaluate ( ~value ) 48 | ) 49 | ) -------------------------------------------------------------------------------- /Functions/VariableList.fmfn: -------------------------------------------------------------------------------- 1 | /* 2 | * ===================================================== 3 | * VariableList ( keys; values; scope ) 4 | * 5 | * RETURNS: 6 | * (string) key/value pairs as $key = value in 7 | * positional order 8 | * 9 | * PARAMETERS: 10 | * @keys = List() of key names 11 | * @values = List() of values 12 | * @scope = (enum) global, local 13 | * 14 | * DEPENDENCIES: 15 | * none 16 | * 17 | * PURPOSE: 18 | * Takes two compliment set of lists 19 | * and turns them into FMP defined variables 20 | * 21 | * NOTES: 22 | * none 23 | * ===================================================== 24 | * 25 | */ 26 | 27 | Let ( [ 28 | ~singleValue = ValueCount ( keys ) = 1 and ValueCount ( values ) = 1; // only one key/value pair 29 | ~isValid = ValueCount ( keys ) = ValueCount ( values ); // both lists have the same number of values 30 | ~notEmpty = not IsEmpty( keys ) and not IsEmpty ( values ); // lists aren't empty 31 | ~value = GetValue ( values ; 1 ); 32 | ~valueIsNumber = Length ( GetAsNumber ( Filter ( ~value; "0123456789.-" ) ) ) = Length ( ~value ); 33 | ~value = Case ( 34 | ~valueIsNumber; 35 | ~value; 36 | 37 | Quote ( ~value ) // default for all strings 38 | ); 39 | ~string = If ( scope = "global" ; "$$" ; "$" ) & GetValue ( keys ; 1 ) & " = " & ~value & If ( ValueCount( keys ) = 1; "" ; ";¶" ) 40 | ]; 41 | Case ( 42 | ~singleValue; 43 | ~string; 44 | 45 | ~isValid and ~notEmpty; 46 | ~string & 47 | VariableList ( MiddleValues ( keys ; 2 ; 1000000 ) ; MiddleValues ( values ; 2 ; 1000000 ) ; scope ); 48 | 49 | // default 50 | "ERROR » Lists don't match in length" 51 | ) 52 | ) 53 | 54 | /* 55 | // Testing code 56 | ~list ( 57 | List( 58 | "foo"; 59 | "bar" 60 | ) 61 | ; 62 | List( 63 | "single value"; 64 | "multiple¶values" 65 | ) 66 | ; "local" ) 67 | */ 68 | -------------------------------------------------------------------------------- /Functions/UISortPortalIndicator.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================== 3 | * UISortPortalIndicator ( field ; object ; tabs ) 4 | * 5 | * PURPOSE: 6 | * Return a an arrow character if the specified field 7 | * is the same field being used by UISortPortalToggle. 8 | * A directional toggle arrow is returned in order to 9 | * use conditional formatting on column headers. 10 | * (see UISortPortalToggle and UISortPortalDirection) 11 | * 12 | * RETURNS: 13 | * (string) Up or down arrow based on the contents $$SORT.PORTAL.[object].FIELD 14 | * 15 | * PARAMETERS: 16 | * field: A field reference 17 | * object: Name of the object or id unique to store values 18 | * tabs: A numerical value of how many tabs to prefix 19 | * 20 | * EXAMPLES: 21 | * When used on conditional formatting for a button attached to a sort script 22 | * UISortPortalIndicator ( FunctionalArea » Tablename::fieldName ; "portal.name" ; 2 ) // = True if the same field 23 | * 24 | * DEPENDENCIES: 25 | * none 26 | * 27 | * RELEASE: 28 | * 2011-04-20 29 | * 30 | * REFERENCES: 31 | * none 32 | * ===================================== 33 | */ 34 | 35 | Let ( [ 36 | ~fieldName = GetFieldName ( field ); 37 | ~sortVarPrefix = "$$SORT.PORTAL." & Upper ( object ); 38 | ~sortVarField = ~sortVarPrefix & ".FIELD"; 39 | ~sortVarDirection = ~sortVarPrefix & ".DIRECTION"; 40 | ~sortCurrentField = GetAsText ( Evaluate ( ~sortVarField ) ); 41 | ~isSameSortField = ~sortCurrentField = ~fieldName; 42 | ~sortDirection = Evaluate ( ~sortVarDirection ); 43 | ~sortArrow = TextFont ( Choose ( ~sortDirection; char ( 9660 ); char ( 9650 ) ) ; If ( Get( SystemPlatform ) = -2 ; "Arial" ; "Apple Symbol Regular" ) ); 44 | ~sortIndicator = Left ( " " ; tabs ) & ~sortArrow 45 | ]; 46 | Case ( 47 | ~isSameSortField; 48 | ~sortIndicator; 49 | 50 | "" 51 | ) 52 | ) 53 | -------------------------------------------------------------------------------- /Functions/#Name-Value/#AssignScriptResults.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================== 3 | * #AssignScriptResults 4 | * 5 | * WARNING: 6 | * This function is deprecated in favor of the more transparent equivalent: 7 | * #Assign ( Get ( ScriptResult ) ). This function is still supported for 8 | * backwards compatibility. 9 | * 10 | * PURPOSE: 11 | * #AssignScriptResults uses the current script result (treated as if 12 | * generated by the # function) to declare and populate a series of 13 | * locally-scoped script variables. This function is equivalent to 14 | * #Assign ( Get ( ScriptResult ) ). 15 | * 16 | * RETURNS: 17 | * True (1) when the script result is a valid set of name-value pairs 18 | * (which includes the empty string); False (0) otherwise. 19 | * 20 | * PARAMETERS: none 21 | * 22 | * DEPENDENCIES: 23 | * #Assign ( parameters ) 24 | * 25 | * NOTE: 26 | * To be accessible outside this function, variable declarations in the 27 | * script result must use the $- or $$-prefixed notation for FileMaker 28 | * variables. This function does not handle that for you. 29 | * 30 | * HISTORY: 31 | * MODIFIED on 2012-12-07 by Jeremy Bante to use 32 | * newer supported functions. 33 | * MODIFIED on 2010-10-04 by Jeremy Bante to return 34 | * explicit True or False values indicating successful evaluation of the 35 | * script result, and to fit syntax inspired by a comment on 36 | * standards.filemakermagazine.com by Richard Dyce. 37 | * CREATED on 2010-08-25 by Jeremy Bante . 38 | * INSPIRED by an example in the FileMaker help documentation. 39 | * 40 | * REFERENCES: 41 | * Script Parameter Interface Best Practice: http://filemakerstandards.org/pages/viewpage.action?pageId=557462 42 | * ===================================== 43 | */ 44 | 45 | #Assign ( Get ( ScriptResult ) ) -------------------------------------------------------------------------------- /Functions/Define.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================== 3 | * Define ( name ; value ; store ) 4 | * 5 | * RETURNS: 6 | * True or False based on creation and storage of a global variable. 7 | * 8 | * PARAMETERS: 9 | * name: (string) The name for the global variable. 10 | * value: (mixed) The value to stored in the global variable. 11 | * store: (fm var) Either a $var or $$VAR in which the name of the variable is stored. 12 | * 13 | * EXAMPLE: 14 | * Define ( "foo" ; "bar" "$$VARIABLES" ) // $$FOO = "bar" and $$VARIABLES contains FOO 15 | * 16 | * DEPENDENCIES: 17 | * none 18 | * 19 | * HISTORY: 20 | * UPDATED on 2014-10-07 by Matt Petrowsky . 21 | * 22 | * REFERENCES: 23 | * none 24 | * ===================================== 25 | */ 26 | 27 | Let ( [ 28 | // preserve data type by using a variable 29 | $~value = value 30 | ]; 31 | Let ( [ 32 | ~globalName = Upper ( Substitute ( name ; "$" ; "" ) ); 33 | ~globalVar = "$$" & ~globalName; 34 | ~evaluateString = "Let ( " & ~globalVar & " = $~value; True )" 35 | ]; 36 | If ( IsValidExpression ( ~evaluateString ); 37 | // Insert the name of the variable into the designated store ( a variable itself ) 38 | Let ( [ 39 | ~variableNames = Evaluate ( store ); 40 | ~valueExists = not IsEmpty ( FilterValues ( ~variableNames ; ~globalName ) ); 41 | ~storeString = "Let ( " & store & " = " & Quote ( If ( ~valueExists ; ~variableNames ; List ( ~variableNames ; ~globalName ) ) ) & "; True )" 42 | ]; 43 | // Create the variable IF IT DOES NOT EXIST - do not overwrite with this function if it does 44 | Let ( ~setVariable = If ( IsEmpty ( Evaluate ( ~globalVar ) ) ; Evaluate ( ~evaluateString ) ); True ) 45 | and not EvaluationError ( Evaluate ( ~storeString ) ) 46 | ); 47 | // default 48 | Let ( $Define.error = List ( "Error: " & EvaluationError ( Evaluate ( ~evaluateString ) ) ; ~evaluateString ) ; False ) 49 | ) 50 | ) 51 | ) 52 | -------------------------------------------------------------------------------- /Functions/Errors/ErrorFmpGetLast.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================== 3 | * ErrorFmpGetLast ( theErrorInfo ) 4 | * 5 | * RETURNS: 6 | * Let Notation containing information about the last FileMaker 7 | * error and the environment it occurred in. 8 | * 9 | * PARAMETERS: 10 | * theErrorInfo = (text, optional) some text describing the particular 11 | * instance of this error. This should help a developer locate where 12 | * in a script the error occured. 13 | * (e.g., "create new invoice record") 14 | * 15 | * DEPENDENCIES: 16 | * Custom Functions: ErrorFmp 17 | * 18 | * HISTORY: 19 | * MODIFIED on 2013-NOV-25 by Daniel Smith dansmith65@gmail.com 20 | * - make ErrorFmp a dependency 21 | * MODIFIED on 2013-NOV-22 by Daniel Smith dansmith65@gmail.com 22 | * - renamed to ErrorFmpGetLast, update documentation 23 | * MODIFIED on 2013-OCT-11 by Daniel Smith dansmith65@gmail.com 24 | * - renamed from ErrorDataFmp ( ~errorCode ) to #GetLastError ( theErrorInfo ) 25 | * - add dependency on custom function: #Error 26 | * MODIFIED on 2013-JAN-25 by Daniel Smith dansmith65@gmail.com 27 | * - rename from ErrorDataFmpWithCode to ErrorDataFmp 28 | * MODIFIED on 2012-DEC-15 by Daniel Smith dansmith65@gmail.com 29 | * - rename from ErrorDataFmp to ErrorDataFmpWithCode 30 | * - add parameter 31 | * MODIFIED on 2012-NOV-22 by Daniel Smith dansmith65@gmail.com 32 | * - rename to: ErrorDataFmp 33 | * MODIFIED on 2012-NOV-21 by Daniel Smith dansmith65@gmail.com 34 | * - don't access $error variable directly 35 | * CREATED on 2012-NOV-20 Daniel Smith dansmith65@gmail.com 36 | * INSPIRED by ErrorString and ErrorData by matt@filemakermagazine.com 37 | * https://github.com/filemakerstandards/fmpstandards/tree/master/Functions 38 | * 39 | * REFERENCES: 40 | * http://www.filemaker.com/help/html/error_codes.html 41 | * http://filemakerstandards.org/x/AoA-/ 42 | * ===================================== 43 | */ 44 | 45 | ErrorFmp ( Get ( LastError ) ; theErrorInfo ) 46 | -------------------------------------------------------------------------------- /Functions/FMPURL.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================== 3 | * FMPURL ( IPAddress ) 4 | * 5 | * PURPOSE: 6 | * Generate a solution specific fmp:// url 7 | * 8 | * RETURNS: 9 | * (string) fmp://THE.SERVER.IP.ADDRESS/FileName?script= 10 | * 11 | * PARAMETERS: 12 | * IPAddress: The target IP address of the FileMaker server 13 | * 14 | * EXAMPLES: 15 | * FMPURL ( "192.168.1.2" ) // = fmp://192.168.1.2/FileName?script= 16 | * 17 | * DEPENDENCIES: none 18 | * 19 | * NOTE: 20 | * Inbound IPAddress is not validated for correctness 21 | * * ~serverIPAddress defaults to first listed if IPAddress is blank 22 | * 23 | * HISTORY: 24 | * UPDATED on 2015-06-30 by Matt Petrowsky to account for 25 | * http://help.filemaker.com/app/answers/detail/a_id/11358 26 | * UPDATED on 2014-04-07 by Matt Petrowsky to account for 13.0v2 fmp:// url changes 27 | * http://help.filemaker.com/app/answers/detail/a_id/13313 28 | * CREATED on 2012-10-04 by Matt Petrowsky (matt@filemakermagazine.com). 29 | * 30 | * REFERENCES: 31 | * none 32 | * ===================================== 33 | */ 34 | 35 | Let ( [ 36 | ~fmpVersion = Getasnumber ( Get ( ApplicationVersion ) ); 37 | ~currentHost = Get ( HostIPAddress ); // returned by client - server returns localhost 38 | ~serverIPAddress = If ( IsEmpty ( IPAddress ) ; GetValue ( Get ( SystemIPAddress ) ; 1 ) ; IPAddress ); // * 39 | ~isMobile = Get ( SystemPlatform ) = 3; 40 | ~isServer = ~currentHost = "127.0.0.1"; 41 | ~isLocal = IsEmpty ( ~currentHost ) 42 | ]; 43 | "fmp://" & Case ( 44 | ~isMobile and ~isLocal; // when the db is local to a mobile device 45 | "%7e"; // using encoded tilde 46 | 47 | ~isServer; 48 | ~serverIPAddress; // server side scripts would use "127.0.0.1" - which isn't good 49 | 50 | ~isLocal and ~fmpVersion ≥ 13.02; 51 | "%24"; // using encoded dollar sign 52 | 53 | ~isLocal; 54 | GetValue ( Get ( SystemIPAddress ) ; 1 ); // assume first ip for local dev 55 | 56 | ~currentHost 57 | ) 58 | & "/" & GetAsURLEncoded ( Get ( FileName ) ) & "?script=" 59 | ) 60 | -------------------------------------------------------------------------------- /Functions/BusinessDays.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================== 3 | * BusinessDays ( startDate ; endDate ) 4 | * 5 | * RETURNS: 6 | * The number of business days falling between startDate and endDate, 7 | * inclusive, omitting any days in holidayList that fall on weekdays. 8 | * 9 | * PARAMETERS: 10 | * startDate: A date 11 | * endDate: A date, endDate ≥ startDate 12 | * 13 | * DEPENDENCIES: 14 | * BusinessHolidays ( calendarYear ) 15 | * 16 | * RELEASE: 2011-10-19 17 | * ===================================== 18 | */ 19 | 20 | Let ( [ 21 | // compare calendar years, in case range spans multiple years 22 | ~startYear = Year ( startDate ); 23 | ~endYear = Year ( endDate ); 24 | ~splitDate = Date ( 1 ; 1 ; ~startYear + 1 ); // first 1 Jan after startDate 25 | ~endDate = If ( ~endYear > ~startYear ; ~splitDate - 1 ; endDate ); 26 | 27 | ~startDay = DayOfWeek ( startDate ); 28 | ~endDay = DayOfWeek ( ~endDate ); 29 | 30 | ~startSaturday = startDate - ~startDay + 7; 31 | ~endSaturday = ~endDate - ~endDay; 32 | ~fullWeeks = Div ( ~endSaturday - ~startSaturday + 1 ; 7 ); 33 | 34 | ~leadingWeekDays = 35 | Choose ( ~startDay - 1 ; 5 ; 5 ; 4 ; 3 ; 2 ; 1 ; 0 ); 36 | ~trailingWeekDays = 37 | Choose ( ~endDay - 1 ; 0 ; 1 ; 2 ; 3 ; 4 ; 5 ; 5 ); 38 | 39 | ~holidayCalculation = 40 | "Let ( [ ~d = GetAsDate (\"" 41 | & BusinessHolidays ( ~startYear ) 42 | & "\" ) ; ~n = DayOfWeek ( ~d ) ] ; ~d ≥ GetAsDate ( startDate ) and ~d ≤ GetAsDate ( endDate ) and ~n > 1 and ~n < 7 )"; 43 | ~holidayCalculation = // Add 1 for each weekday holiday within range 44 | Substitute ( ~holidayCalculation; 45 | [ "¶" ; "\" ) ; ~n = DayOfWeek ( ~d ) ] ; ~d ≥ GetAsDate ( startDate ) and ~d ≤ GetAsDate ( endDate ) and ~n > 1 and ~n < 7 ) + Let ( [ ~d = GetAsDate ( \"" ]; 46 | [ "startDate" ; Quote ( startDate ) ]; 47 | [ "endDate" ; Quote ( ~endDate ) ] 48 | ); 49 | ~holidays = Evaluate ( ~holidayCalculation ) 50 | ]; 51 | 5 * ~fullWeeks + ~leadingWeekDays + ~trailingWeekDays - ~holidays 52 | + If ( ~endYear > ~startYear ; BusinessDays ( ~splitDate ; endDate ) ; 0 ) 53 | ) -------------------------------------------------------------------------------- /Functions/LayoutObjectHierarchy.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================================== 3 | * LayoutObjectHierarchy ( content ) 4 | * 5 | * PURPOSE: 6 | * Display indented version of LayoutObjectNames 7 | * PARAMETERS: 8 | * @content = LayoutObjectNames 9 | * RETURNS: 10 | * (list) Indented version of LayoutObjectNames 11 | * DEPENDENCIES: 12 | * none 13 | * NOTES: 14 | * This is a special use function for showing 15 | * a developer the hierarchy of Layout objects 16 | * ===================================================== 17 | * 18 | */ 19 | 20 | Let ( [ 21 | content = If ( IsEmpty ( content ) ; LayoutObjectNames ( Get ( FileName ) ; Get ( LayoutName ) ) ; content ); 22 | var.topRow = GetValue ( content ; 1 ); 23 | var.remainingData = RightValues ( content ; ValueCount ( content ) -1 ); 24 | var.prefixChar = "» "; 25 | var.indentChars = " "; 26 | var.isIndent = If ( var.topRow = "<" ; True ); 27 | var.isOutdent = If ( var.topRow = ">" ; True ); 28 | $$FUNCTION.LAYOUTOBJECTHIERARCHY = Case ( 29 | var.isIndent; 30 | Left ( var.indentChars ; Length ( $$FUNCTION.LAYOUTOBJECTHIERARCHY ) +1 ); 31 | 32 | var.isOutdent; 33 | Left ( var.indentChars ; Length ( $$FUNCTION.LAYOUTOBJECTHIERARCHY ) -1 ); 34 | 35 | $$FUNCTION.LAYOUTOBJECTHIERARCHY 36 | ); 37 | var.useRow = not ( var.isIndent or var.isOutdent ); 38 | var.resultRow = If ( Length ( $$FUNCTION.LAYOUTOBJECTHIERARCHY ) ≥ 1 ; 39 | $$FUNCTION.LAYOUTOBJECTHIERARCHY & var.prefixChar ) & var.topRow; 40 | $$FUNCTION.LAYOUTOBJECTHIERARCHY[2] = List ( $$FUNCTION.LAYOUTOBJECTHIERARCHY[2] ; If ( var.useRow ; var.resultRow ; "" ) ) 41 | ]; 42 | If ( ValueCount ( var.remainingData ) = 1 or IsEmpty ( content ); 43 | // Return result and reset global variables being used 44 | $$FUNCTION.LAYOUTOBJECTHIERARCHY[2] & Let ( [ $$FUNCTION.LAYOUTOBJECTHIERARCHY = "" ; $$FUNCTION.LAYOUTOBJECTHIERARCHY[2] = "" ]; "" ); // final result 45 | // Recursion (not using this as result because of stack inversion) 46 | LayoutObjectHierarchy ( var.remainingData ) 47 | ) 48 | ) 49 | -------------------------------------------------------------------------------- /Functions/UISortListIndicator.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================== 3 | * UISortListIndicator ( field ; tabs ) 4 | * 5 | * PURPOSE: 6 | * Return a boolean value if the specified field 7 | * is the same field being used by UISortToggle and 8 | * the sort status is sorted. $$SORT.LAYOUT.[id].INDICATOR 9 | * is set to contain a directional toggle arrow for use 10 | * within a global merge variable. 11 | * (see UISortToggle and UISortDirection) 12 | * 13 | * RETURNS: 14 | * (boolean) true or false based on the contents $$SORT.LAYOUT.[id].FIELD 15 | * 16 | * PARAMETERS: 17 | * field: A field reference 18 | * tabs: A numerical value of how many tab to prefix 19 | * 20 | * EXAMPLES: 21 | * When used on conditional formatting for a button attached to a sort script 22 | * UISortListIndicator ( FunctionalArea » Tablename::fieldName ; 2 ) // = True if the same field 23 | * 24 | * DEPENDENCIES: 25 | * none 26 | * 27 | * RELEASE: 28 | * 2011-04-20 29 | * 30 | * REFERENCES: 31 | * none 32 | * ===================================== 33 | */ 34 | 35 | Let ( [ 36 | ~layoutID = GetValue ( LayoutIDs ( Get ( FileName ) ) ; Get ( LayoutNumber ) ); 37 | ~fieldName = GetFieldName ( field ); 38 | ~sortVarPrefix = "$$SORT.LAYOUT." & ~layoutID; 39 | ~sortVarField = ~sortVarPrefix & ".LIST.FIELD"; 40 | ~sortVarDirection = ~sortVarPrefix & ".LIST.DIRECTION"; 41 | ~sortVarIndicator = ~sortVarPrefix & ".LIST.INDICATOR"; 42 | ~sortCurrentField = GetAsText ( Evaluate ( ~sortVarField ) ); 43 | ~isSameSortField = ~sortCurrentField = ~fieldName; 44 | ~sortDirection = Evaluate ( ~sortVarDirection ); 45 | ~sortArrow = TextFont ( Choose ( ~sortDirection; char ( 9660 ); char ( 9650 ) ) ; If ( Get( SystemPlatform ) = -2 ; "Arial" ; "Apple Symbol Regular" ) ); 46 | ~sortIndicator = Left ( " " ; tabs ) & ~sortArrow 47 | ]; 48 | Case ( 49 | Get ( SortState ) = 0 50 | or Get ( SortState ) = 2; 51 | Evaluate ( "Let ( " & ~sortVarIndicator & " = " & Quote ( "" ) & "; False )"); 52 | 53 | ~isSameSortField and Get ( SortState ); 54 | Evaluate ( "Let ( " & ~sortVarIndicator & " = " & Quote ( ~sortIndicator ) & "; True )"); 55 | ) 56 | ) -------------------------------------------------------------------------------- /Functions/ChecksumAdler32.fmfn: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * ChecksumAdler32 ( asciiText ) 3 | * Computes a 32 bit checksum for a string of text using the Adler-32 algorithm. 4 | * 5 | * @parameter asciiText: Text with character codes between 0 and 255. 6 | * 7 | * @return A number representing a 32-bit checksum 8 | * 9 | * @history 2014-01-05 - Jeremy Bante - Created 10 | * 11 | * @see http://en.wikipedia.org/wiki/Adler-32 12 | * @see http://www.ietf.org/rfc/rfc1950.txt 13 | ******************************************************************************/ 14 | 15 | Case ( 16 | /* Step 0, set-up */ 17 | not $~adler.step ; 18 | Let ( [ 19 | // initialize checksum values 20 | $~adler.a = 1 ; 21 | $~adler.b = 0 ; 22 | $~adler.i = 1 ; 23 | $~adler.byte = Code ( Middle ( asciiText ; $~adler.i ; 1 ) ) ; 24 | $~adler.step = 25 | Case ( 26 | IsEmpty ( $~adler.byte ) ; 2 ; // end 27 | $~adler.byte > 255 ; -1 ; // out of range 28 | /* Else */ 1 29 | ) 30 | ] ; 31 | ChecksumAdler32 ( asciiText ) 32 | ) ; 33 | 34 | /* Step 1, accumulate checksum */ 35 | $~adler.step = 1 ; 36 | Let ( [ 37 | $~adler.a = Mod ( $~adler.a + $~adler.byte ; 65521 ) ; 38 | $~adler.b = Mod ( $~adler.b + $~adler.a ; 65521 ) ; 39 | $~adler.i = $~adler.i + 1 ; 40 | $~adler.byte = Code ( Middle ( asciiText ; $~adler.i ; 1 ) ) ; 41 | $~adler.step = 42 | Case ( 43 | IsEmpty ( $~adler.byte ) ; 2 ; // end 44 | $~adler.byte > 255 ; -1 ; // out of range 45 | /* Else */ 1 46 | ) 47 | ] ; 48 | ChecksumAdler32 ( asciiText ) 49 | ) ; 50 | 51 | /* Step 2, clean-up and return result */ 52 | $~adler.step = 2 or $~adler.step = -1 ; 53 | Let ( [ 54 | ~result = 55 | Case ( 56 | $~adler.step = -1 ; 57 | "ERROR: Character out of range (" 58 | & $~adler.byte 59 | & ")" ; 60 | 61 | /* Else */ 62 | $~adler.b * 65536 + $~adler.a 63 | ) ; 64 | 65 | // purge variables 66 | $~adler.a = "" ; 67 | $~adler.b = "" ; 68 | $~adler.byte = "" ; 69 | $~adler.i = "" ; 70 | $~adler.step = "" 71 | ] ; 72 | ~result 73 | ) 74 | ) -------------------------------------------------------------------------------- /Functions/KeyboardModifiers.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================================== 3 | * KeyboardModifiers ( keysList ; ignoreCaps ) 4 | * 5 | * PURPOSE: 6 | * Trap for human readable list of 7 | * modifier keys being held down. 8 | * 9 | * PARAMETERS: 10 | * keysList (enumeration) List() of "keys" or string 11 | * (e.g. List("Command"; "Alt") or "command-alt") 12 | * ignoreCaps (boolean) True or False to ignore caps key 13 | * 14 | * RETURNS: 15 | * (boolean) based on modifier keys being held down 16 | * 17 | * DEPENDENCIES: 18 | * none 19 | * 20 | * EXAMPLE: 21 | * If [KeyboardModifiers ( "command-shift" ; True )] 22 | * # Perform steps here 23 | * End If 24 | * 25 | * RELEASE: 2010-10-14 26 | * 2013-07-02 Updated to proper filemakerstandards.org style guide 27 | * 28 | * NOTES: 29 | * Adapted from http://www.briandunning.com/cf/473 30 | * by Peter Wagemans, SHpartners 31 | * ===================================================== 32 | * 33 | */ 34 | 35 | Let ( [ 36 | // Get the modifier keys 37 | 38 | ~keys = Get ( ActiveModifierKeys ); 39 | 40 | // Convert keys to their bit values (see note at bottom) 41 | 42 | ~command = Mod ( Int ( ~keys / 16 ) ; 2 ); 43 | ~alt = Mod ( Int ( ~keys / 8 ) ; 2 ); 44 | ~control = Mod ( Int ( ~keys / 4 ) ; 2 ); 45 | ~capslock = Mod ( Int ( ~keys / 2 ) ; 2 ); 46 | ~shift = Mod ( ~keys ; 2 ) 47 | 48 | ]; 49 | // Result (boolean) 50 | 51 | ( ~command xor not PatternCount ( keysList ; "command" ) ) 52 | and 53 | ( ~alt xor not ( PatternCount ( keysList ; "alt" ) or PatternCount ( keysList ; "option" ) ) ) 54 | and 55 | ( ~control xor not ( PatternCount ( keysList ; "ctrl" ) or PatternCount ( keysList ; "control") ) ) 56 | and 57 | If ( not ignoreCaps ; 58 | ( ~capslock xor not PatternCount ( keysList ; "capslock" ) ) ; 59 | True 60 | ) 61 | and 62 | ( ~shift xor not PatternCount ( keysList ; "shift" ) ) 63 | and 64 | ( ( ~keys = 0 ) xor ( keysList ≠ "" ) ) 65 | ) 66 | 67 | /* 68 | The Mod() function is used to determine 69 | if the key is within the result of 70 | Get ( ActiveModifierKeys ) 71 | 72 | 16 8 4 2 1 73 | -------------- 74 | 0 0 1 0 1 75 | | | | | | 76 | | | | | Shift 77 | | | | Caps Lock 78 | | | Ctrl 79 | | Alt/Option 80 | Apple Command Key 81 | */ 82 | -------------------------------------------------------------------------------- /Functions/ForEach.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================== 3 | * ForEach ( valueList ; expression ) 4 | * 5 | * PURPOSE: 6 | * ForEach repeatedly applies a calculation to each value in a ¶-delimited 7 | * list. For each value in valueList, evaluates expression, substituting 8 | * the value for each "~n" in expression. Expressions may also reference 9 | * the value index (line number) being evaluated with "$~i". This is 10 | * analogous to Map functionality in functional programming languages. 11 | * 12 | * NOTE: 13 | * This is the recursive version of this function. It is slower, but 14 | * capable of processing larger lists. 15 | * 16 | * RETURNS: 17 | * A ¶-delimited list of the results of evaluating expression on valueList. 18 | * 19 | * EXAMPLE: 20 | * ForEach ( 21 | * List ( "1 One" ; "2 Two" ; "3 Three" ); 22 | * "Left ( ~n ; 1 ) + $~i" 23 | * ) // = "2¶4¶6" 24 | * 25 | * PARAMETERS: 26 | * valueList: A ¶-delimited list of inputs to expression 27 | * expression: A calculation to evaluate 28 | * 29 | * DEPENDENCIES: none 30 | * 31 | * RELEASE: 2012-01-06 32 | * 33 | * REFERENCES: 34 | * http://en.wikipedia.org/wiki/Map_(higher-order_function) 35 | * ===================================== 36 | */ 37 | 38 | Case ( 39 | /* Step 0, initialize routine */ 40 | not $~map.step; 41 | Let ( [ 42 | $~map.valueCount = ValueCount ( valueList ); 43 | $~map.step = 1 44 | ]; 45 | ForEach ( valueList ; expression ) 46 | ); 47 | 48 | /* Step 1, evaluate expression */ 49 | $~map.step = 1; 50 | Let ( [ 51 | $~i = $~i + 1; 52 | ~value = 53 | Evaluate ( 54 | "Let ( ~n = " 55 | & Quote ( GetValue ( valueList ; $~i ) ) 56 | & " ; " 57 | & expression 58 | & " )" 59 | ); 60 | $~map.resultList = 61 | If ( $~i > 1 ; $~map.resultList & ¶ ) 62 | & ~value; 63 | $~map.step = 64 | If ( $~i < $~map.valueCount; 65 | $~map.step; 66 | /* Else */ $~map.step + 1 67 | ) 68 | ]; 69 | ForEach ( valueList ; expression ) 70 | ); 71 | 72 | /* Step 2, clean-up and return result */ 73 | $~map.step = 2; 74 | Let ( [ 75 | ~resultList = $~map.resultList; 76 | 77 | // Purge variables 78 | $~i = ""; 79 | $~map.resultList = ""; 80 | $~map.step = ""; 81 | $~map.valueCount = "" 82 | ]; 83 | ~resultList 84 | ) 85 | ) -------------------------------------------------------------------------------- /Functions/WindowCenter.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================================== 3 | * WindowCenter ( dimension ) 4 | * 5 | * PARAMETERS: 6 | * dimension: (enumeration) Vertical, Horizontal 7 | * 8 | * RETURNS: 9 | * (int) Screen position value based on dimension 10 | * supplied 11 | 12 | * DEPENDENCIES: 13 | * none 14 | 15 | * NOTES: 16 | * Using a locally scoped variable within your 17 | * script will alter what this function returns 18 | * $parentWindowHeight for window height of parent 19 | * $parentWindowWidth for window width of parent 20 | * $parentWindowTop for window top of parent 21 | * $parentWindowLeft for window left of parent 22 | * $childWindowWidth MUST BE SET! when centering on parent 23 | * $childWindowHeight MUST BE SET! when centering on parent 24 | * ===================================================== 25 | * 26 | */ 27 | 28 | Let ( [ 29 | ~vertical = PatternCount ( dimension ; "vert" ) or dimension = "y" or dimension = "top"; 30 | ~horizontal = PatternCount ( dimension ; "horiz" ) or dimension = "x" or dimension = "left"; 31 | ~parentIsSet = not IsEmpty ( $parentWindowTop ) 32 | and not IsEmpty ( $parentWindowLeft ) 33 | and not IsEmpty ( $parentWindowWidth ) 34 | and not IsEmpty ( $parentWindowHeight ); 35 | ~parentOnScreen = ~parentIsSet 36 | and ( $parentWindowTop + $parentWindowHeight ≤ Get ( WindowDesktopHeight ) 37 | and $parentWindowLeft + $parentWindowWidth ≤ Get ( WindowDesktopWidth ) ); 38 | ~parentHeight = If ( not ~parentIsSet or not ~parentOnScreen; 39 | Get ( WindowDesktopHeight ); 40 | /*else*/ 41 | $parentWindowHeight 42 | ); 43 | ~parentWidth = If ( not ~parentIsSet or not ~parentOnScreen; 44 | Get ( WindowDesktopWidth ); 45 | /*else*/ 46 | $parentWindowWidth 47 | ); 48 | ~childHeight = If ( IsEmpty ( $childWindowHeight ); 49 | Get ( WindowHeight ); 50 | /*else*/ 51 | $childWindowHeight 52 | ); 53 | ~childWidth = If ( IsEmpty ( $childWindowWidth ); 54 | Get ( WindowWidth ); 55 | /*else*/ 56 | $childWindowWidth 57 | ) 58 | ]; 59 | Case ( 60 | ~vertical; 61 | Int ( ( ~parentHeight / 2) - ( ~childHeight / 2 ) 62 | + If ( ~parentOnScreen ; $parentWindowTop ) ); 63 | ~horizontal; 64 | Int ( ( ~parentWidth / 2) - ( ~childWidth / 2 ) 65 | + If ( ~parentOnScreen ; $parentWindowLeft ) ) 66 | ) 67 | ) 68 | -------------------------------------------------------------------------------- /Functions/Triggers/TriggersDisable.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================== 3 | * TriggersDisable 4 | * https://github.com/jbante/FileMaker-Techniques/blob/master/CustomFunctions/Triggers/TriggersDisable.fmfn 5 | * 6 | * PURPOSE: 7 | * TriggersDisable sets global variables to indicate that the current 8 | * script is suppressing triggers. This script must be called from the 9 | * context of a script; otherwise, it will not suppress triggers, and it 10 | * will return False. In order to be suppressed, a script called via 11 | * trigger should use the TriggersAreActive function to decide whether or 12 | * not to run. 13 | * 14 | * RETURNS: 15 | * True (1) if called from the context of a script, and therefore triggers 16 | * have been suppressed; False (0) otherwise. 17 | * 18 | * PARAMETERS: none 19 | * 20 | * REFERENCED VARIABLES: 21 | * $$~DISABLETRIGGERS (deprecated) 22 | * $$~TRIGGERS_DISABLE 23 | * $$~TRIGGERS_SCRIPTS 24 | * $~triggers_disableKey 25 | * 26 | * DEPENDENCIES: none 27 | * 28 | * EXAMPLE (script): 29 | * Set Variable [$!; Value:TriggersDisable] 30 | * # Do something 31 | * Set Variable [$!; Value:TriggersEnable] 32 | * 33 | * NOTES: 34 | * This function should *always* be used in pairs with the TriggersEnable 35 | * function. 36 | * 37 | * RELEASE: 2013-04-08 38 | * 39 | * REFERENCES: 40 | * Supressible Triggered Scripts Best Practice: http://filemakerstandards.org/display/bp/Suppressible+Triggered+Scripts 41 | * ===================================== 42 | */ 43 | 44 | Let ( [ 45 | ~scriptName = Get ( ScriptName ) ; 46 | ~scriptIsRunning = not IsEmpty ( Get ( ScriptName ) ) ; 47 | ~disableIsRunning = not IsEmpty ( $~triggers_disableKey ) ; 48 | $~triggers_disableKey = 49 | Case ( 50 | ~disableIsRunning ; $~triggers_disableKey ; 51 | ~scriptIsRunning ; Get ( UUID ) 52 | ) ; 53 | $$~TRIGGERS_SCRIPTS = 54 | If ( ~scriptIsRunning and not ~disableIsRunning ; 55 | ~scriptName 56 | & Char ( 9 ) // tab 57 | & $~triggers_disableKey 58 | & ¶ 59 | & $$~TRIGGERS_SCRIPTS ; 60 | /* Else */ 61 | $$~TRIGGERS_SCRIPTS 62 | ) ; 63 | $$~TRIGGERS_DISABLE = 64 | If ( ~scriptIsRunning ; True ; /* Else */ $$~TRIGGERS_DISABLE ) ; 65 | $$~DISABLETRIGGERS = $$~TRIGGERS_DISABLE 66 | ] ; 67 | ~scriptIsRunning 68 | ) -------------------------------------------------------------------------------- /Functions/Triggers/TriggersEnable.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================== 3 | * TriggersEnable 4 | * https://github.com/jbante/FileMaker-Techniques/blob/master/CustomFunctions/Triggers/TriggersEnable.fmfn 5 | * 6 | * PURPOSE: 7 | * TriggersEnable sets global variables to indicate that the current script 8 | * is no longer suppressing triggers. This function will also turn off 9 | * trigger suppression altogether if there are no other scripts suppressing 10 | * triggers. In order to be suppressed, a script called via trigger should 11 | * use the TriggersAreActive function to decide whether or not to run. 12 | * 13 | * RETURNS: 14 | * True (1) if TriggersEnable successfully removed the current script from 15 | * the list of scripts suppressing triggers; False (0) otherwise. 16 | * 17 | * PARAMETERS: none 18 | * 19 | * REFERENCED VARIABLES: 20 | * $$~DISABLETRIGGERS (deprecated) 21 | * $$~TRIGGERS_DISABLE 22 | * $$~TRIGGERS_SCRIPTS 23 | * $~triggers_disableKey 24 | * 25 | * DEPENDENCIES: none 26 | * 27 | * EXAMPLE (script): 28 | * Set Variable [$!; Value:TriggersDisable] 29 | * # Do something 30 | * Set Variable [$!; Value:TriggersEnable] 31 | * 32 | * NOTES: 33 | * This function should *always* be used in pairs with the TriggersDisable 34 | * function. 35 | * 36 | * RELEASE: 2013-04-08 37 | * 38 | * REFERENCES: 39 | * Supressible Triggered Scripts Best Practice: http://filemakerstandards.org/display/bp/Suppressible+Triggered+Scripts 40 | * ===================================== 41 | */ 42 | 43 | If ( not IsEmpty ( $~triggers_disableKey ) ; 44 | Let ( [ 45 | ~scriptsLength = Length ( $$~TRIGGERS_SCRIPTS ) ; 46 | ~scriptPosition = 47 | Position ( 48 | $$~TRIGGERS_SCRIPTS ; 49 | Char ( 9 ) /* tab */ & $~triggers_disableKey & ¶ ; 50 | ~scriptsLength ; 51 | -1 52 | ) ; 53 | ~breakPosition = 54 | If ( ~scriptPosition > 0 ; 55 | Position ( $$~TRIGGERS_SCRIPTS ; ¶ ; ~scriptPosition ; 1 ) ; 56 | /* Else */ 0 57 | ) ; 58 | $$~TRIGGERS_SCRIPTS = 59 | Right ( $$~TRIGGERS_SCRIPTS ; ~scriptsLength - ~breakPosition ) ; 60 | $$~TRIGGERS_DISABLE = 61 | If ( not IsEmpty ( $$~TRIGGERS_SCRIPTS ) and $$~TRIGGERS_DISABLE ; 62 | True ; 63 | /* Else */ "" 64 | ) ; 65 | $$~DISABLETRIGGERS = $$~TRIGGERS_DISABLE ; 66 | $~triggers_disableKey = "" 67 | ] ; 68 | "" 69 | ) 70 | ) -------------------------------------------------------------------------------- /Functions/#Name-Value/#AssignScriptParameters.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================== 3 | * #AssignScriptParameters 4 | * 5 | * WARNING: 6 | * This function is deprecated in favor of the more transparent 7 | * combinations of other functions. This function is still supported for 8 | * backwards compatibility. 9 | * 10 | * PURPOSE: 11 | * #AssignScriptParameters uses the current script parameter (treated as if 12 | * generated by the # function) to declare and populate a series of 13 | * locally-scoped script variables. #AssignScriptParameters also uses the 14 | * current script name to determine whether or not all required named 15 | * parameters are populated (≠ Null). This function is NOT equivalent to 16 | * #Assign ( Get ( ScriptParameter ) ). 17 | * 18 | * RETURNS: 19 | * True (1) when the script parameter is a valid set of name-value pairs 20 | * (which includes the empty string) and all parameters defined as required 21 | * according to the script name are non-null; False (0) otherwise. 22 | * 23 | * PARAMETERS: none 24 | * 25 | * DEPENDENCIES: 26 | * #Assign ( parameters ) 27 | * VerifyVariablesNotEmpty ( nameList ) 28 | * ScriptRequiredParameterList ( scriptNameToParse ) 29 | * 30 | * NOTES: 31 | * To be accessible outside this function, variable declarations in the 32 | * script parameter must use the $ or $$ prefixed notation for FileMaker 33 | * variables. This function does not handle that for you. If your script 34 | * name does not use parenthesis "()" to indicate required or optional 35 | * parameters then all inbound parameters will be available upon successful 36 | * evaluation. 37 | * 38 | * HISTORY: 39 | * MODIFIED on 2012-12-07 by Jeremy Bante to use 40 | * newer supported functions. 41 | * MODIFIED on 2010-10-04 by jeremy@kyologic.com to return explicit True or 42 | * False values indicating successful evaluation of the script parameter, 43 | * and to fit syntax inspired by a comment on 44 | * standards.filemakermagazine.com by Richard Dyce. 45 | * CREATED on 2010-03-13 by Jeremy Bante . 46 | * INSPIRED by an example in the FileMaker help documentation. 47 | * 48 | * REFERENCES: 49 | * Script Parameter Interface Best Practice 50 | * http://filemakerstandards.org/pages/viewpage.action?pageId=557462 51 | * ===================================== 52 | */ 53 | 54 | #Assign ( Get ( ScriptParameter ) ) 55 | and VerifyVariablesNotEmpty ( ScriptRequiredParameterList ( "" ) ) -------------------------------------------------------------------------------- /Functions/Errors/ErrorApp.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================== 3 | * ErrorApp ( theErrorCode ; theErrorInfo ) 4 | * 5 | * RETURNS: 6 | * Let Notation containing information about the specified 7 | * solution-specific error and the environment it occurred in. 8 | * 9 | * PARAMETERS: 10 | * theErrorCode = (number) code for an Application error 11 | * theErrorInfo = (text, optional) some text describing the particular 12 | * instance of this error. This should help a developer locate where 13 | * in a script the error occured. 14 | * (e.g., "create new invoice record") 15 | * 16 | * DEPENDENCIES: 17 | * Custom Functions: #, Error 18 | * 19 | * NOTE: 20 | * This function should be modified by each developer/solution to map your 21 | * own error codes to error descriptions. Sections that should be modified 22 | * are marked with a comment starting with "TODO". 23 | * 24 | * HISTORY: 25 | * MODIFIED on 2013-NOV-22 by Daniel Smith dansmith65@gmail.com 26 | * - renamed to ErrorApp, update documentation 27 | * MODIFIED on 2013-OCT-11 by Daniel Smith dansmith65@gmail.com 28 | * - change from ErrorDataApp ( theErrorCode ) 29 | * to #AppError ( theErrorCode ; theErrorInfo ) 30 | * - add dependency on custom function: #Error 31 | * MODIFIED on 2012-NOV-22 by Daniel Smith dansmith65@gmail.com 32 | * - rename to: ErrorDataApp 33 | * MODIFIED on 2012-NOV-21 by Daniel Smith dansmith65@gmail.com 34 | * - don't access $error variable directly 35 | * CREATED on 2012-NOV-20 Daniel Smith dansmith65@gmail.com 36 | * INSPIRED by ErrorString and ErrorData by matt@filemakermagazine.com 37 | * https://github.com/filemakerstandards/fmpstandards/tree/master/Functions 38 | * 39 | * REFERENCES: 40 | * http://filemakerstandards.org/x/AoA-/ 41 | * ===================================== 42 | */ 43 | 44 | Let ( [ 45 | // Get human-readable error string associated with the error code 46 | ~errorDescription = 47 | Case ( 48 | // TODO: the next four lines are an example only, they should be modified before this function is used 49 | theErrorCode = -1 ; "Unknown error" ; 50 | theErrorCode = 0 ; "No error" ; 51 | theErrorCode = 1 ; "User canceled action" ; 52 | "Unknown error code" 53 | ) 54 | ] ; 55 | Error ( "App" ; theErrorCode ; ~errorDescription ; theErrorInfo ) 56 | // add additional info useful for debugging specific errors 57 | // TODO: the following Case an example only, it should be modified before this function is used 58 | & Case ( 59 | theErrorCode = 3 ; 60 | # ( "WindowMode" ; Get ( WindowMode ) ) ; 61 | /* else */ "" 62 | ) 63 | ) 64 | -------------------------------------------------------------------------------- /Functions/UUID/UUIDRandom.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ====================================== 3 | * UUIDRandom 4 | * 5 | * PURPOSE: 6 | * Creates a numeric universally unique identifier suitable for use as a primary 7 | * key in FileMaker number fields. The UUID is composed almost entirely of 8 | * random digits. This may be useful in some situations to preserve the 9 | * anonymity of the conditions that generated the UUID. The values returned by 10 | * this and related functions have a one-to-one correspondence with UUIDs 11 | * following the RFC 4122 standard - all values in the format generated by this 12 | * function can be converted to RFC 4122, and visa-versa. 13 | * 14 | * RETURNS: 15 | * A 41-digit delimited number of the form: 16 | * v-r-nnnnnnnnnnnn-nnnnnnn-nnnnn@nnnnnnnnnnnnnnn 17 | * The sections of the UUID correspond to: 18 | * v: A UUID version (type) number 19 | * r: A variant code reserved by the RFC 4122 standard 20 | * n: Random digits, within certain bounds for compatibility with RFC 4122 21 | * 22 | * PARAMETERS: none 23 | * 24 | * DEPENDENCIES: none 25 | * 26 | * NOTES: 27 | * For most practical purposes, FileMaker will ignore the text delimiters in 28 | * values returned by this function. 29 | * 30 | * These functions generates UUIDs that are meaningfully sortable. Values sort 31 | * by version, then creation order, then node (creation device). 32 | * 33 | * This function creates a (decimal) numeric UUID, which has performance 34 | * advantages for file size and find speed in FileMaker. The value can be 35 | * converted to RFC 4122 canonical form (hexadecimal) with the 36 | * UUIDGetAsRFC4122 function. 37 | * 38 | * RELEASE: 2011-02-02 39 | * 40 | * REFERENCES: 41 | * Key values Best Practice: http://filemakerstandards.org/pages/viewpage.action?pageId=557386 42 | * RFC 4122 standard UUID specification: http://tools.ietf.org/html/rfc4122 43 | * UUIDGetAsRFC4122: http://www.briandunning.com/cf/1221 44 | * UUIDGetAsUUID: http://www.briandunning.com/cf/1273 45 | * UUIDGetNICAddress: http://www.briandunning.com/cf/1222 46 | * UUIDGetSeconds: http://www.briandunning.com/cf/1223 47 | * UUIDNew: http://www.briandunning.com/cf/1220 48 | * UUIDRandom: http://www.briandunning.com/cf/1246 49 | * ====================================== 50 | */ 51 | 52 | // v-r-mmm mmm mmm mmm-sss ssss-ccccc-nnnnn nnnnn nnnnn 53 | "4-2-" // version and reserved variant code 54 | & Right ( "000000000000" & Floor ( Random * 165208454460 ) ; 12 ) & "-" 55 | & Right ( Random ; 7 ) & "-" 56 | & Right ( "00000" & Floor ( Random * 16384 ) ; 5 ) & "-" 57 | & Right ( "000000000000000" & Floor ( Random * 281474976710656 ) ; 15 ) -------------------------------------------------------------------------------- /Functions/#Name-Value/#List.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================== 3 | * #List ( value ) 4 | * 5 | * RETURNS: 6 | * Encoded value with trailing return and data type preserved. 7 | * 8 | * PARAMETERS: 9 | * value: The value to encode. 10 | * 11 | * EXAMPLE: 12 | * #List ( "value1" ) 13 | * & #List ( 14 | * #List ( "subValue" ) 15 | * & #List ( "subValue2" ) 16 | * ) 17 | * 18 | * DEPENDENCIES: none 19 | * 20 | * HISTORY: 21 | * MODIFIED on 2014-06-06 by Jeremy Bante to fix an 22 | * issue where long sequences of digits could be interpreted as timestamps. 23 | * MODIFIED on 2014-05-26 by Jeremy Bante to detect 24 | * type using a different method suggested by Arnold Kegebein. 25 | * MODIFIED on 2013-12-24 by Jeremy Bante to 26 | * specially encode newline characters in values, and to update type 27 | * detection to match the # function. 28 | * MODIFIED on 2013-MAY-09 by Daniel Smith dansmith65@gmail.com to encode 29 | * a value like the current version of # ( name ; value ) custom function 30 | * CREATED on 2012-NOV-26 by Daniel Smith dansmith65@gmail.com 31 | * ===================================== 32 | */ 33 | 34 | Let ( [ 35 | ~plusOneText = GetAsText ( value + 1 ) ; 36 | ~number = GetAsNumber ( value ) ; 37 | ~value = 38 | Case ( 39 | value = "" or value = "?" or ~number = "?" ; 40 | Quote ( value ) ; 41 | 42 | GetAsText ( GetAsTimestamp ( value ) + 1 ) = ~plusOneText ; 43 | "GetAsTimestamp ( " & Quote ( value ) & " )" ; 44 | 45 | GetAsText ( GetAsTime ( value ) + 1 ) = ~plusOneText ; 46 | "GetAsTime ( " & Quote ( value ) & " )" ; 47 | 48 | GetAsText ( GetAsDate ( value ) + 1 ) = ~plusOneText ; 49 | "GetAsDate ( " & Quote ( value ) & " )" ; 50 | 51 | value ≠ ~number ; 52 | Substitute ( Quote ( value ) ; 53 | [ Char ( 10 ) ; "\" & Char ( 10 ) & \"" ] ; 54 | [ Char ( 8232 ) ; "\" & Char ( 8232 ) & \"" ] ; 55 | [ Char ( 8233 ) ; "\" & Char ( 8233 ) & \"" ] 56 | ) ; 57 | 58 | /* Else */ 59 | ~number 60 | ) 61 | ] ; 62 | Case ( 63 | IsValidExpression ( ~value ) 64 | /** 65 | * the following Evaluate statement causes the function to calculate 66 | * ~30% slower, so it may be best to comment it in a production 67 | * environment and only use it for development/testing 68 | */ 69 | // and Evaluate ( ~value ) = value 70 | ; 71 | ~value & ¶ ; 72 | 73 | //else, ~value is not a valid expression... 74 | "/* Error " 75 | & 1200 // Generic calculation error 76 | & " value: " 77 | & Quote ( 78 | Substitute ( // escape comment character sequences 79 | value ; 80 | [ "*/" ; "\*\/" ] ; 81 | [ "/*" ; "\/\*" ] 82 | ) 83 | ) 84 | & " */" 85 | & ¶ 86 | ) 87 | ) -------------------------------------------------------------------------------- /Functions/Errors/Error.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================== 3 | * Error ( theErrorType ; theErrorCode ; theErrorDescription ; theErrorInfo ) 4 | * 5 | * RETURNS: 6 | * Let Notation containing information about the error and the environment 7 | * it occurred in. 8 | * 9 | * PARAMETERS: 10 | * theErrorType = (text) origin of the error 11 | * (e.g., "Fmp", "App", "Plugin: ScriptMaster", "mFM: JSON", etc.) 12 | * theErrorCode = (number) code for the error 13 | * theErrorDescription = (text) description of the error, preferably human 14 | * readable and decipherable 15 | * theErrorInfo = (text, optional) some text describing the particular 16 | * instance of this error. This should help a developer locate where 17 | * in a script the error occured. 18 | * (e.g., "create new invoice record") 19 | * 20 | * DEPENDENCIES: 21 | * Custom Functions: # 22 | * 23 | * NOTE: 24 | * This function contains a recommended set of environmental data, but you 25 | * may choose to add or remove name/value pairs from this function as you 26 | * see fit for your solution. All error generating custom functions 27 | * call this function, so it is the central place to define default 28 | * environmental data collected when an error occurs. 29 | * 30 | * Environmental data that is likely to change from the time the error occurs 31 | * to the time log data is collected should be included in this function. 32 | * 33 | * 34 | * HISTORY: 35 | * MODIFIED on 2013-NOV-22 by Daniel Smith dansmith65@gmail.com 36 | * - change name to Error, update documentation 37 | * CREATED on 2013-OCT-11 by Daniel Smith dansmith65@gmail.com 38 | * 39 | * REFERENCES: 40 | * http://filemakerstandards.org/x/AoA-/ 41 | * ===================================== 42 | */ 43 | 44 | // information about the error 45 | // these should NOT be modified, they should be consistent from one database to the next 46 | # ( "errorType" ; theErrorType ) 47 | & # ( "errorCode" ; theErrorCode ) 48 | & # ( "errorDescription" ; theErrorDescription ) 49 | & If ( not IsEmpty ( theErrorInfo ) and GetAsBoolean ( theErrorCode ) ; 50 | # ( "errorInfo" ; theErrorInfo ) 51 | ) 52 | 53 | 54 | // information about the environment 55 | // these values should be adjusted as needed for the database 56 | & If ( 57 | GetAsBoolean ( theErrorCode ) ; 58 | "" 59 | & # ( "AllowAbortState" ; Get ( AllowAbortState ) ) 60 | & # ( "CurrentHostTimestamp" ; Get ( CurrentHostTimeStamp ) ) 61 | & # ( "ErrorCaptureState" ; Get ( ErrorCaptureState ) ) 62 | & # ( "LastODBCError" ; Get ( LastODBCError ) ) 63 | & # ( "ScriptName" ; Get ( ScriptName ) ) 64 | & # ( "ScriptParameter" ; Get ( ScriptParameter ) ) 65 | & # ( "ScriptResult" ; Get ( ScriptResult ) ) 66 | & # ( "UserCount" ; Get ( UserCount ) ) 67 | ) 68 | -------------------------------------------------------------------------------- /Functions/#Name-Value/#GetNameList.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================== 3 | * #GetNameList ( parameters ) 4 | * 5 | * RETURNS: 6 | * A return-delimited list of names from the name-value pairs in parameters. 7 | * 8 | * PARAMETERS: 9 | * parameters: A string of serialized name-value pair data in Let notation. 10 | * 11 | * EXAMPLE: 12 | * #GetNameList ( 13 | * # ( "name" ; "value" ) 14 | * & # ( "foo" ; "bar" ); 15 | * ) = List ( "name" ; "foo" ) 16 | * 17 | * DEPENDENCIES: none 18 | * 19 | * HISTORY: 20 | * MODIFIED on 2013-12-24 by Jeremy Bante to improve 21 | * efficiency. 22 | * MODIFIED on 2013-12-23 by John Jones john.christopher@alumni.virginia.edu 23 | * to use Position/Middle. 24 | * CREATED on 2013-01-24 by Daniel Smith dansmith65@gmail.com 25 | * ===================================== 26 | */ 27 | 28 | Case ( 29 | /* Step 0, set-up */ 30 | not $#GetNameList.step ; 31 | Let ( [ 32 | ~empty = IsEmpty ( Trim ( parameters ) ) ; 33 | parameters = 34 | Substitute ( 35 | ¶ & parameters ; 36 | [ "¶$" ; ¶ ] ; // remove "$" prefix 37 | [ "¶$" ; ¶ ] ; // remove "$$" prefix 38 | [ "¶¶" ; ¶ ] // remove empty values 39 | ) ; 40 | $#GetNameList.length = Length ( parameters ) ; 41 | $#GetNameList.step = If ( ~empty ; 2 ; /* Else */ 1 ) 42 | ] ; 43 | #GetNameList ( parameters ) 44 | ) ; 45 | 46 | /* Step 1, check each parameter */ 47 | $#GetNameList.step = 1 ; 48 | Let ( [ 49 | ~start = $#GetNameList.end + 1 ; 50 | $#GetNameList.end = Position ( parameters ; ¶ ; ~start ; 1 ) ; 51 | ~endOfList = $#GetNameList.end = 0 ; 52 | ~length = 53 | If ( ~endOfList ; 54 | $#GetNameList.length + 1 ; 55 | /* Else */ $#GetNameList.end 56 | ) 57 | - ~start ; 58 | ~pair = Middle ( parameters ; ~start ; ~length ) ; 59 | ~name = Left ( ~pair ; Position ( ~pair ; " = " ; 1 ; 1 ) - 1 ) ; 60 | ~include = // only include ~name if it isn't already in the result 61 | Position ( 62 | ¶ & $#GetNameList.result & ¶ ; 63 | ¶ & ~name & ¶ ; 64 | 1 ; 65 | 1 66 | ) = 0 ; 67 | $#GetNameList.result = 68 | List ( 69 | $#GetNameList.result ; 70 | If ( ~include ; ~name ) 71 | ) ; 72 | 73 | ~endOfList = 74 | ~endOfList 75 | or $#GetNameList.end ≥ $#GetNameList.length ; 76 | $#GetNameList.step = 77 | If ( ~endOfList ; 78 | $#GetNameList.step + 1 ; 79 | /* Else */ $#GetNameList.step 80 | ) 81 | ] ; 82 | #GetNameList ( parameters ) 83 | ) ; 84 | 85 | /* Step 2, clean-up and return result */ 86 | $#GetNameList.step = 2 ; 87 | Let ( [ 88 | ~result = $#GetNameList.result ; 89 | 90 | // purge variables 91 | $#GetNameList.end = "" ; 92 | $#GetNameList.length = "" ; 93 | $#GetNameList.result = "" ; 94 | $#GetNameList.step = "" 95 | ] ; 96 | ~result 97 | ) 98 | ) -------------------------------------------------------------------------------- /Functions/S3Url.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================== 3 | * S3Url ( accessKey ; secretKey ; bucket ; file ; expiresTimestamp ) 4 | * 5 | * PURPOSE: 6 | * Generate a valid S3 expirable download link. 7 | * 8 | * RETURNS: 9 | * (string) valid Amazon AWS S3 url 10 | * 11 | * PARAMETERS: 12 | * accessKey: a valid AWS access key 13 | * secretKey: a valid AWS secret key 14 | * bucket: name of an existing bucket 15 | * file: the path to a file within the bucket 16 | * expiresTimestamp: a valid FileMaker timestamp value, typically in the future 17 | * 18 | * EXAMPLES: 19 | * S3Url ( "1F8XQWPN96WHFY16MV02" ; "sadfi98dfsd834d96dDF/G324+dw" ; "filemakerthemes" ; "Protected.zip" ; Get ( CurrentTimeStamp ) + ( 3600 * 24 ) ) 20 | * // = https://filemakerthemes.s3.amazonaws.com/Protected.zip?AWSAccessKeyId=1F8XQWPN96WHFY16MV02&Expires=1427605250&Signature=ClAZEC3daVdoMEJwdlU2WEjA%2Fsc%3D 21 | * 22 | * DEPENDENCIES: 23 | * 24 | * PLUGIN: BaseElements 25 | * 26 | * NOTE: 27 | * Your accessKey identifies your account and it must be associated 28 | * to the root full access of your AWS account or an IAM user with a 29 | * minimum of the AmazonS3ReadOnlyAccess policy associated. 30 | * HTTPS warning: If your bucket name includes periods, the url may 31 | * generate a certificate warning within browsers. 32 | * 33 | * HISTORY: 34 | * CREATED on 2015-03-25 by Matt Petrowsky (matt@filemakermagazine.com) 35 | * 36 | * REFERENCES: 37 | * https://aws.amazon.com 38 | * http://docs.aws.amazon.com/AmazonS3/latest/dev/RESTAuthentication.html 39 | * ===================================== 40 | */ 41 | 42 | Let ( [ 43 | ~utcOffset = Get ( CurrentTimeStamp ) - Floor ( Get ( CurrentTimeUTCMilliseconds ) / 1000 ); 44 | 45 | ~endpoint = "s3.amazonaws.com"; 46 | ~file = GetAsURLEncoded ( file ); 47 | ~path = bucket & "/" & ~file; 48 | ~expires = If ( IsEmpty ( expiresTimestamp ) ; Get ( CurrentTimeStamp ) + ( 3600 * 24 ) ; expiresTimestamp ); // default to +1 day 49 | ~expiresUTC = GetAsNumber ( ~expires ) - GetAsNumber ( GetAsTimestamp ( Date ( 1 ; 1 ; 1970 ) ) ) - ~utcOffset; 50 | 51 | ~request = "GET¶¶¶" & ~expiresUTC & "¶/" &~path; 52 | ~request = Substitute ( ~request ; ¶ ; Char ( 10 ) ); // because we need valid returns 53 | 54 | ~signature = BE_HMAC ( ~request ; secretKey ; BE_MessageDigestAlgorithm_SHA1 ; BE_Encoding_Base64 ); 55 | 56 | ~url = "https://" & bucket & "." & ~endpoint & "/" & ~file; 57 | ~url = ~url & "?AWSAccessKeyId=" & accessKey; 58 | ~url = ~url & "&Expires=" & ~expiresUTC; 59 | ~url = ~url & "&Signature=" & GetAsURLEncoded ( ~signature ) 60 | 61 | ]; 62 | ~url 63 | 64 | /* // Debug info 65 | List ( 66 | ~request; 67 | ~signature; 68 | ~url; 69 | ) 70 | */ 71 | ) 72 | 73 | /* 74 | This function requires the BaseElements plugin in order to 75 | create the HMAC digest 76 | */ 77 | -------------------------------------------------------------------------------- /Functions/ValueToggle.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================================== 3 | * ValueToggle ( value ; valueList ) 4 | * 5 | * PURPOSE: 6 | * Add or remove a value from a return delimited list of data 7 | * RETURNS: 8 | * (string) Return delimited list of values 9 | * DEPENDENCIES: 10 | * none 11 | * NOTES: 12 | * none 13 | * HISTORY: 14 | * 1/13/11 - Petrowsky - Modified to filemakerstandards.org format 15 | * ===================================================== 16 | * 17 | */ 18 | 19 | Let ( [ 20 | 21 | ~newList = Substitute ( If ( Left ( valuelist; 1 ) ≠ ¶; "^") & valuelist & "^"; ¶; "^^" ); // List minus all returns 22 | ~match = "^" & value & "^" // Value to match 23 | 24 | ]; 25 | 26 | Case ( 27 | PatternCount ( ~newList; ~match ); 28 | Let ( [ 29 | ~removed = Substitute ( ~newList; ~match; "" ); // Kill matching values 30 | ~newList = Substitute ( ~removed; "^^"; ¶ ); // Put returns back in 31 | ~newList = Middle ( ~newList; 2; 100000 ); // Trim leading ^ 32 | ~newList = Left ( ~newList; Length ( ~newList ) - 1 ); // Trim trailing ^ 33 | ~cleaned = Substitute ( ~newList; ¶; "" ); // Remove returns for trimming 34 | ~firstChar = Position ( ~newList; Left ( ~cleaned; 1 ); 0; 1 ); // Position of firt non cleaned char 35 | ~lastChar = Position ( ~newList; Right ( ~cleaned; 1 ); Length ( ~newList ); -1 ) // Position of last non cleaned char 36 | ]; 37 | Middle ( ~newList; ~firstChar; ~lastChar - ~firstChar + 1 ) 38 | ); 39 | 40 | If ( valuelist ≠ ""; valuelist & ¶ ) & value // default 41 | ) 42 | 43 | ) 44 | 45 | /* 46 | * Unit tests 47 | */ 48 | 49 | /* 50 | Let ( 51 | _ = "--------------------"; 52 | 53 | List ( 54 | List ( "At beginning ( one )"; _; list.addRemove ( "one"; List ( "one"; "two"; "three" ) ); _ ); 55 | List ( "At ending ( three )"; _; list.addRemove ( "three"; List ( "one"; "two"; "three" ) ); _ ); 56 | List ( "With multiple spread out ( one )"; _; list.addRemove ( "one"; List ( "one"; "two"; "one"; "three" ) ); _ ); 57 | List ( "With multiple at beginning ( one )"; _; list.addRemove ( "one"; List ( "one"; "one"; "two"; "three" ) ); _ ); 58 | List ( "With multiple at ending ( three )"; _; list.addRemove ( "three"; List ( "one"; "two"; "three"; "three" ) ); _ ); 59 | List ( "With multiple in middle ( two )"; _; list.addRemove ( "two"; List ( "one"; "two"; "two"; "three" ) ); _ ); 60 | List ( "With even returns at start ( two )"; _; list.addRemove ( "two"; "¶¶¶¶one¶two¶three" ); _ ); 61 | List ( "With odd returns at start ( two )"; _; list.addRemove ( "two"; "¶¶¶one¶two¶three" ); _ ); 62 | List ( "With even returns at end ( two )"; _; list.addRemove ( "two"; "one¶two¶three¶¶¶¶" ); _ ); 63 | List ( "With odd returns at end ( two )"; _; list.addRemove ( "two"; "one¶two¶three¶¶¶" ); _ ); 64 | List ( "Without value ( one )"; _; list.addRemove ( "one"; List ( "two"; "three" ) ); _ ); 65 | ) 66 | ) 67 | */ 68 | -------------------------------------------------------------------------------- /Functions/#Name-Value/ScriptOptionalParameterList.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================== 3 | * ScriptOptionalParameterList ( scriptNameToParse ) 4 | * 5 | * RETURNS: 6 | * A return-delimited list of optional script parameters according to the 7 | * script name and the FileMakerStandards.org convention for listing 8 | * parameters in script names. 9 | * 10 | * PARAMETERS: 11 | * scriptNameToParse: A script name. Defaults to Get ( ScriptName ) when 12 | * left empty. 13 | * 14 | * DEPENDENCIES: none 15 | * 16 | * HISTORY: 17 | * MODIFIED on 2013-06-29 by Jeremy Bante to append 18 | * a trailing return to non-empty results to support direct concatenation 19 | * with other lists. 20 | * CREATED on 2012-12-07 by Jeremy Bante . 21 | * 22 | * REFERENCES: 23 | * http://filemakerstandards.org/display/cs/Script+naming 24 | * ===================================== 25 | */ 26 | 27 | Case ( 28 | /* Step 0, set-up */ 29 | not $~parse.step ; 30 | Let ( [ 31 | scriptNameToParse = 32 | If ( IsEmpty ( scriptNameToParse ) ; 33 | Get ( ScriptName ) ; 34 | /* Else */ scriptNameToParse 35 | ); 36 | ~length = Length ( scriptNameToParse ) ; 37 | ~start = Position ( scriptNameToParse ; "{" ; ~length ; -1 ) + 1 ; 38 | ~endOptional = Position ( scriptNameToParse ; "}" ; ~start ; 1 ) ; 39 | ~endOptional = 40 | If ( ~endOptional = 0 ; ~length ; /* Else */ ~endOptional ) ; 41 | ~endRequired = Position ( scriptNameToParse ; ")" ; ~start ; 1 ) ; 42 | ~endRequired = 43 | If ( ~endRequired = 0 ; ~length ; /* Else */ ~endRequired ) ; 44 | ~end = Min ( ~endOptional ; ~endRequired ) ; 45 | parameters = 46 | If ( ~start = 1 ; // opening "{" not found 47 | "" ; 48 | /* Else */ 49 | Middle ( scriptNameToParse ; ~start ; ~end - ~start ) 50 | ) ; 51 | parameters = Substitute ( parameters ; ";" ; ¶ ) ; 52 | $~parse.parameterCount = ValueCount ( parameters ) ; 53 | 54 | $~parse.step = If ( IsEmpty ( parameters ) ; 2 ; /* Else */ 1 ) 55 | ] ; 56 | ScriptOptionalParameterList ( parameters ) 57 | ) ; 58 | 59 | /* Step 1, trim whitespace */ 60 | $~parse.step = 1 ; 61 | Let ( [ 62 | $~parse.i = $~parse.i + 1 ; 63 | parameter = Trim ( GetValue ( scriptNameToParse ; $~parse.i ) ) ; 64 | $~parse.result = List ( $~parse.result ; parameter ) ; 65 | $~parse.step = 66 | If ( $~parse.i < $~parse.parameterCount ; 67 | $~parse.step ; 68 | /* Else */ $~parse.step + 1 69 | ) 70 | ] ; 71 | ScriptOptionalParameterList ( scriptNameToParse ) 72 | ) ; 73 | 74 | /* Step 2, clean-up and return result */ 75 | $~parse.step = 2 ; 76 | Let ( [ 77 | ~result = 78 | If ( not IsEmpty ( $~parse.result ) ; 79 | $~parse.result & ¶ 80 | ) ; 81 | 82 | // purge variables 83 | $~parse.i = "" ; 84 | $~parse.parameterCount = "" ; 85 | $~parse.result = "" ; 86 | $~parse.step = "" 87 | ] ; 88 | ~result 89 | ) 90 | ) -------------------------------------------------------------------------------- /Functions/#Name-Value/ScriptRequiredParameterList.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================== 3 | * ScriptRequiredParameterList ( scriptNameToParse ) 4 | * 5 | * RETURNS: 6 | * A return-delimited list of required script parameters according to the 7 | * script name and the FileMakerStandards.org convention for listing 8 | * parameters in script names. 9 | * 10 | * PARAMETERS: 11 | * scriptNameToParse: A script name. Defaults to Get ( ScriptName ) when 12 | * left empty. 13 | * 14 | * DEPENDENCIES: none 15 | * 16 | * HISTORY: 17 | * MODIFIED on 2013-06-29 by Jeremy Bante to append 18 | * a trailing return to non-empty results to support direct concatenation 19 | * with other lists. 20 | * CREATED on 2012-12-07 by Jeremy Bante . 21 | * 22 | * REFERENCES: 23 | * http://filemakerstandards.org/display/cs/Script+naming 24 | * ===================================== 25 | */ 26 | 27 | Case ( 28 | /* Step 0, set-up */ 29 | not $~parse.step ; 30 | Let ( [ 31 | scriptNameToParse = 32 | If ( IsEmpty ( scriptNameToParse ) ; 33 | Get ( ScriptName ) ; 34 | /* Else */ scriptNameToParse 35 | ) ; 36 | ~length = Length ( scriptNameToParse ) ; 37 | ~start = Position ( scriptNameToParse ; "(" ; ~length ; -1 ) + 1 ; 38 | ~endOptional = Position ( scriptNameToParse ; "{" ; ~start ; 1 ) ; 39 | ~endOptional = 40 | If ( ~endOptional = 0 ; ~length ; /* Else */ ~endOptional ) ; 41 | ~endRequired = Position ( scriptNameToParse ; ")" ; ~start ; 1 ) ; 42 | ~endRequired = 43 | If ( ~endRequired = 0 ; ~length ; /* Else */ ~endRequired ) ; 44 | ~end = Min ( ~endOptional ; ~endRequired ) ; 45 | parameters = 46 | If ( ~start = 1 ; // opening "(" not found 47 | "" ; 48 | /* Else */ 49 | Middle ( scriptNameToParse ; ~start ; ~end - ~start ) 50 | ) ; 51 | parameters = Substitute ( parameters ; ";" ; ¶ ) ; 52 | $~parse.parameterCount = ValueCount ( parameters ) ; 53 | 54 | $~parse.step = If ( IsEmpty ( parameters ) ; 2 ; /* Else */ 1 ) 55 | ]; 56 | ScriptRequiredParameterList ( parameters ) 57 | ) ; 58 | 59 | /* Step 1, trim whitespace */ 60 | $~parse.step = 1 ; 61 | Let ( [ 62 | $~parse.i = $~parse.i + 1 ; 63 | parameter = Trim ( GetValue ( scriptNameToParse ; $~parse.i ) ) ; 64 | $~parse.result = List ( $~parse.result ; parameter ) ; 65 | $~parse.step = 66 | If ( $~parse.i < $~parse.parameterCount ; 67 | $~parse.step ; 68 | /* Else */ $~parse.step + 1 69 | ) 70 | ] ; 71 | ScriptRequiredParameterList ( scriptNameToParse ) 72 | ) ; 73 | 74 | /* Step 2, clean-up and return result */ 75 | $~parse.step = 2 ; 76 | Let ( [ 77 | ~result = 78 | If ( not IsEmpty ( $~parse.result ) ; 79 | $~parse.result & ¶ 80 | ) ; 81 | 82 | // purge variables 83 | $~parse.i = "" ; 84 | $~parse.parameterCount = "" ; 85 | $~parse.result = "" ; 86 | $~parse.step = "" 87 | ] ; 88 | ~result 89 | ) 90 | ) -------------------------------------------------------------------------------- /Functions/UUID/UUIDGetNICAddress.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ====================================== 3 | * UUIDGetNICAddress ( theID ) 4 | * https://github.com/petrowsky/fmpstandards/blob/master/Functions/UUIDGetNICAddress.fmfn 5 | * 6 | * PURPOSE: 7 | * Extracts the hexadecimal NIC address from a numeric (not hexadecimal) 8 | * UUID created by this family of functions. 9 | * 10 | * RETURNS: 11 | * A string of the form nn:nn:nn:nn:nn:nn representing the hexadecimal NIC 12 | * address of the machine that created the UUID passed as a parameter. 13 | * Returns Null ("") if the UUID does not contain a retrievable NIC 14 | * address. 15 | * 16 | * PARAMETERS: 17 | * theID: The UUID to extract a NIC address from 18 | * 19 | * DEPENDENCIES: none 20 | * 21 | * RELEASE: 2011-02-25 22 | * 23 | * REFERENCES: 24 | * Key values Best Practice: http://filemakerstandards.org/display/bp/Key+values 25 | * Ray Cologon's uID functions: http://www.nightwing.com.au/FileMaker/demos9/demo910.html 26 | * ====================================== 27 | */ 28 | 29 | Case ( 30 | /*Pull node field from UUID, and set-up conversion loop*/ 31 | not $~uuid.step; 32 | Let ( [ 33 | ~idAsNumber = GetAsNumber ( theID ); 34 | ~version = Left ( ~idAsNumber ; Length ( ~idAsNumber ) - 40 ); 35 | $~nicAddress = Right ( ~idAsNumber ; 15 ); 36 | ~isMulticast = Mod ( Div ( $~nicAddress ; 1099511627776 ) ; 2 ); 37 | $~hexNICAddress = ""; 38 | $~base16 = "0123456789abcdef"; 39 | $~uuid.step = 40 | If ( 41 | ~version < 3 //NIC is valid 42 | and $~nicAddress ≤ 281474976710656 43 | and not ~isMulticast; 44 | 1; 45 | /*else*/ 46 | -1 //error 47 | ) 48 | ]; 49 | UUIDGetNICAddress ( "" ) 50 | ); 51 | 52 | /*Convert NIC address from base 10 to base 16*/ 53 | $~uuid.step = 1; 54 | Let ( [ 55 | $~hexNICAddress = 56 | Middle ( $~base16 ; Mod ( $~nicAddress ; 16 ) + 1 ; 1 ) & 57 | $~hexNICAddress; 58 | $~nicAddress = Div ( $~nicAddress ; 16 ); 59 | $~uuid.step = If ( $~nicAddress ; $~uuid.step ; /*else*/ $~uuid.step + 1 ) 60 | ]; 61 | UUIDGetNICAddress ( "" ) 62 | ); 63 | 64 | /*Insert colons and return result*/ 65 | $~uuid.step = 2; 66 | Let ( [ 67 | ~nicAddress = Right ( "000000000000" & $~hexNICAddress ; 12 ); 68 | ~nicAddress = 69 | Middle ( ~nicAddress ; 1 ; 2 ) & ":" 70 | & Middle ( ~nicAddress ; 3 ; 2 ) & ":" 71 | & Middle ( ~nicAddress ; 5 ; 2 ) & ":" 72 | & Middle ( ~nicAddress ; 7 ; 2 ) & ":" 73 | & Middle ( ~nicAddress ; 9 ; 2 ) & ":" 74 | & Middle ( ~nicAddress ; 11 ; 2 ); 75 | 76 | //purge variables 77 | $~nicAddress = ""; 78 | $~hexNICAddress = ""; 79 | $~base16 = ""; 80 | $~i = ""; 81 | $~uuid.step = "" 82 | ]; 83 | ~nicAddress 84 | ); 85 | 86 | /*Return error value*/ 87 | $~uuid.step = -1; 88 | Let ( [ 89 | //purge variables 90 | $~nicAddress = ""; 91 | $~hexNICAddress = ""; 92 | $~base16 = ""; 93 | $~i = ""; 94 | $~uuid.step = "" 95 | ]; 96 | "" //Null 97 | ) 98 | ) -------------------------------------------------------------------------------- /Functions/#Name-Value/#Filter.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================== 3 | * #Filter ( parameters ; filterParameters ) 4 | * 5 | * RETURNS: 6 | * A list of Let notation name-value pairs from parameters with names in 7 | * filterParameters. Any pairs with names not in filterParameters will not 8 | * be included. 9 | * 10 | * PARAMETERS: 11 | * parameters: A string of serialized name-value pair data in Let notation. 12 | * filterParameters: A return-delimited list of parameter names to include 13 | * in the result. 14 | * 15 | * EXAMPLE: 16 | * #Assign ( #Filter ( 17 | * # ( "name" ; "value" ) 18 | * & # ( "foo" ; "bar" ); 19 | * List ( "name" ; "otherName" ) 20 | * ) ) 21 | * // variable $name assigned "value"; $foo and $otherName are unaffected 22 | * 23 | * DEPENDENCIES: none 24 | * 25 | * HISTORY: 26 | * MODIFIED on 2013-12-24 by Jeremy Bante to improve 27 | * efficiency. 28 | * MODIFIED on 2013-12-23 by John Jones 29 | * to use Position/Middle. Modified to add trailing return if it doesn't 30 | * exist. 31 | * CREATED on 2012-11-28 by Jeremy Bante . 32 | * ===================================== 33 | */ 34 | 35 | Case ( 36 | /* Step 0, set-up */ 37 | not $#Filter.step ; 38 | Let ( [ 39 | ~empty = IsEmpty ( parameters ) or IsEmpty ( filterParameters ) ; 40 | filterParameters = // add "$" prefix for matching 41 | Substitute ( ¶ & filterParameters ; ¶ ; "¶$" ) ; 42 | filterParameters = // add "$$" prefix for backwards-compatibility 43 | filterParameters 44 | & Substitute ( filterParameters ; ¶ ; "¶$" ) 45 | & ¶ ; 46 | $#Filter.length = Length ( parameters ) ; 47 | $#Filter.step = If ( ~empty ; 2 ; /* Else */ 1 ) 48 | ] ; 49 | #Filter ( parameters ; filterParameters ) 50 | ) ; 51 | 52 | /* Step 1, check each parameter */ 53 | $#Filter.step = 1 ; 54 | Let ( [ 55 | ~start = $#Filter.end + 1 ; 56 | $#Filter.end = Position ( parameters ; ¶ ; ~start ; 1 ) ; 57 | ~endOfList = $#Filter.end = 0 ; 58 | ~length = 59 | If ( ~endOfList ; 60 | $#Filter.length + 1 ; 61 | /* Else */ $#Filter.end 62 | ) 63 | - ~start ; 64 | ~pair = Middle ( parameters ; ~start ; ~length ) ; 65 | ~name = Left ( ~pair ; Position ( ~pair ; " = " ; 1 ; 1 ) - 1 ) ; 66 | ~include = // only include ~pair if ~name is in filterParameters 67 | Position ( filterParameters ; ¶ & ~name & ¶ ; 1 ; 1 ) > 0 ; 68 | $#Filter.result = 69 | $#Filter.result 70 | & If ( ~include ; ~pair & ¶ ) ; 71 | 72 | ~endOfList = ~endOfList or $#Filter.end ≥ $#Filter.length ; 73 | $#Filter.step = 74 | If ( ~endOfList ; $#Filter.step + 1 ; /* Else */ $#Filter.step ) 75 | ] ; 76 | #Filter ( parameters ; filterParameters ) 77 | ) ; 78 | 79 | /* Step 2, clean-up and return result */ 80 | $#Filter.step = 2 ; 81 | Let ( [ 82 | ~result = $#Filter.result ; 83 | 84 | // purge variables 85 | $#Filter.end = "" ; 86 | $#Filter.length = "" ; 87 | $#Filter.result = "" ; 88 | $#Filter.step = "" 89 | ] ; 90 | ~result 91 | ) 92 | ) -------------------------------------------------------------------------------- /Functions/GetBaseNAsNumber.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================== 3 | * GetBaseNAsNumber ( numberText ; base ) 4 | * 5 | * PURPOSE: 6 | * Converts numberText from the specified base to a FileMaker (decimal) 7 | * number. Results from converting fractional portions of numbers may be 8 | * approximate rather than exact. 9 | * 10 | * RETURNS: 11 | * The numeric (base 10) value of numberText 12 | * 13 | * PARAMETERS: 14 | * numberText: A text string representing a number in the specified base. 15 | * base: The radix of numberText. 2 ≤ base ≤ 36. 16 | * 17 | * RELEASE: 2011-11-30 18 | * ===================================== 19 | */ 20 | 21 | Case ( 22 | /* Step 0, set-up */ 23 | not $~radix.step; 24 | Let ( [ 25 | ~base = Int ( base ); 26 | ~numberText = 27 | Filter ( 28 | Lower ( numberText ); 29 | Left ( ".0123456789abcdefghijklmnopqrstuvwxyz" ; ~base + 1 ) 30 | ); 31 | 32 | $~radix.isNegative = Left ( numberText ; 1 ) = "-"; // PRE filter value 33 | ~length = Length ( ~numberText ); // POST filter value 34 | ~dotPosition = Position ( ~numberText ; "." ; 1 ; 1 ); 35 | $~radix.power = // initialize to highest power of numberText 36 | If ( ~dotPosition; 37 | ~dotPosition - 2; 38 | /* Else */ ~length - 1 39 | ); 40 | $~radix.powerMin = 41 | If ( ~dotPosition ; $~radix.power - ~length + 2 ; /* Else */ 0 ); 42 | $~radix.round = // constrain result to analogous significant digits 43 | Ceiling ( Ln ( ~base ^ Abs ( $~radix.powerMin ) ) / Ln ( 10 ) ); 44 | 45 | $~radix.step = 46 | If ( IsEmpty ( ~numberText ) or ~base < 2 or ~base > 36; 47 | -1; // error, skip to end 48 | /* Else */ 1 49 | ) 50 | ]; 51 | GetBaseNAsNumber ( ~numberText ; ~base ) 52 | ); 53 | 54 | /* Step 1, convert digits */ 55 | $~radix.step = 1; 56 | Let ( [ 57 | ~digit = Left ( numberText ; 1 ); 58 | ~digit = 59 | Position ( 60 | "0123456789abcdefghijklmnopqrstuvwxyz"; 61 | ~digit; 62 | 1; 63 | 1 64 | ) 65 | - 1; 66 | ~isDot = ~digit = -1; 67 | ~numberText = Right ( numberText ; Length ( numberText ) - 1 ); 68 | 69 | $~radix.decimal = 70 | $~radix.decimal + 71 | If ( not ~isDot ; ~digit * ( base ^ $~radix.power ) ); 72 | $~radix.power = $~radix.power - ( not ~isDot ); // decrement by 1 73 | $~radix.step = 74 | If ( $~radix.power ≥ $~radix.powerMin; 75 | $~radix.step; 76 | /* Else */ $~radix.step + 1 77 | ) 78 | ]; 79 | GetBaseNAsNumber ( ~numberText ; base ) 80 | ); 81 | 82 | /* Step 2, clean-up and return result */ 83 | $~radix.step = 2 or $~radix.step = -1; 84 | Let ( [ 85 | ~error = $~radix.step = -1; 86 | ~result = 87 | If ( $~radix.isNegative ; "-" ) 88 | & Round ( $~radix.decimal ; $~radix.round ); 89 | 90 | // Purge variables 91 | $~radix.decimal = ""; 92 | $~radix.isNegative = ""; 93 | $~radix.power = ""; 94 | $~radix.powerMin = ""; 95 | $~radix.round = ""; 96 | $~radix.step = "" 97 | ]; 98 | If ( ~error ; "?" ; /* Else */ ~result ) 99 | ) 100 | ) -------------------------------------------------------------------------------- /Functions/#Name-Value/#Remove.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================== 3 | * #Remove ( parameters ; removeParameters ) 4 | * 5 | * RETURNS: 6 | * A #-format dictionary based on parameters, but with all values named in 7 | * removeParameters removed. 8 | * 9 | * PARAMETERS: 10 | * parameters: A string of name-value pairs 11 | * removeParameters: The names of name-value pairs to remove. 12 | * 13 | * DEPENDENCIES: none 14 | * 15 | * NOTES: 16 | * This is a recursive function. 17 | * 18 | * HISTORY: 19 | * MODIFIED on 2013-12-24 by Jeremy Bante to improve 20 | * efficiency. 21 | * MODIFIED on 2013-12-23 by John Jones 22 | * to use Middle/Position instead of 23 | * GetValue. 24 | * MODIFIED on 2013-03-15 by Daniel Smith to add 25 | * trailing return if it doesn't exist. 26 | * MODIFIED on 2013-01-08 by Jeremy Bante to extend 27 | * the function to remove multiple names from a return-delimited list. 28 | * CREATED on 2012-12-20 by Daniel Smith 29 | * ===================================== 30 | */ 31 | 32 | Case ( 33 | /* Step 0, set-up */ 34 | not $#Remove.step ; 35 | Let ( [ 36 | ~empty = IsEmpty ( parameters ) or IsEmpty ( removeParameters ) ; 37 | removeParameters = // add "$" prefix for matching 38 | Substitute ( ¶ & removeParameters ; ¶ ; "¶$" ) ; 39 | removeParameters = // add "$$" prefix for backwards-compatibility 40 | removeParameters 41 | & Substitute ( removeParameters ; ¶ ; "¶$" ) 42 | & ¶ ; 43 | $#Remove.length = Length ( parameters ) ; 44 | $#Remove.step = If ( ~empty ; -1 ; /* Else */ 1 ) 45 | ] ; 46 | #Remove ( parameters ; removeParameters ) 47 | ) ; 48 | 49 | /* Step 1, check each parameter */ 50 | $#Remove.step = 1 ; 51 | Let ( [ 52 | ~start = $#Remove.end + 1 ; 53 | $#Remove.end = Position ( parameters ; ¶ ; ~start ; 1 ) ; 54 | ~endOfList = $#Remove.end = 0 ; 55 | ~length = 56 | If ( ~endOfList ; 57 | $#Remove.length + 1 ; 58 | /* Else */ $#Remove.end 59 | ) 60 | - ~start ; 61 | ~pair = Middle ( parameters ; ~start ; ~length ) ; 62 | ~name = Left ( ~pair ; Position ( ~pair ; " = " ; 1 ; 1 ) - 1 ) ; 63 | ~include = // only include if ~name is not in removeParameters 64 | not IsEmpty ( ~name ) 65 | and Position ( removeParameters ; ¶ & ~name & ¶ ; 1 ; 1 ) = 0 ; 66 | $#Remove.result = 67 | $#Remove.result 68 | & If ( ~include ; ~pair & ¶ ) ; 69 | 70 | ~endOfList = ~endOfList or $#Remove.end ≥ $#Remove.length ; 71 | $#Remove.step = 72 | If ( ~endOfList ; $#Remove.step + 1 ; /* Else */ $#Remove.step ) 73 | ] ; 74 | #Remove ( parameters ; removeParameters ) 75 | ) ; 76 | 77 | /* Step 2, clean-up and return result */ 78 | $#Remove.step = 2 or $#Remove.step = -1 ; 79 | Let ( [ 80 | ~empty = $#Remove.step = -1 ; 81 | ~result = If ( ~empty ; parameters ; /* Else */ $#Remove.result ) ; 82 | 83 | // purge variables 84 | $#Remove.end = "" ; 85 | $#Remove.length = "" ; 86 | $#Remove.result = "" ; 87 | $#Remove.step = "" 88 | ] ; 89 | ~result 90 | ) 91 | ) -------------------------------------------------------------------------------- /Functions/Errors/LogData.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================== 3 | * LogData ( logLevel ) 4 | * 5 | * RETURNS: 6 | * (string) Let format dictionary which describes the current environment. 7 | * 8 | * PARAMETERS: 9 | * logLevel = (numeric) 0 thru [n] 10 | * 11 | * EXAMPLE: 12 | * LogData ( LogWarning ) 13 | * LogData ( $logLevelDebug ) 14 | * // Where your own custom function LogWarning 15 | * // returns the constant of 1 or a variable 16 | * // named $logLevelDebug returns a value of 3. 17 | * 18 | * DEPENDENCIES: 19 | * Custom Functions: # 20 | * 21 | * NOTES: 22 | * This function should be modified to capture any relevant values for 23 | * your solution. This includes any plugin or solution specific data desired. 24 | * The level/amount of data returned increases numerically. You can add 25 | * as much data as needed. You can also define the log levels by using 26 | * separate custom functions with the prefix of Log* - such as LogWarning, 27 | * LogError, LogCritical, LogDebug, etc. 28 | * See: http://en.wikipedia.org/wiki/Syslog for logging references. 29 | * 30 | * HISTORY: 31 | * MODIFIED on 2014-JAN-11 matt@filemakermagazine.com to use numeric logLevel 32 | * MODIFIED on 2012-NOV-21 by Daniel Smith dansmith65@gmail.com 33 | * - don't access $error variable directly 34 | * CREATED on 2012-NOV-21 Daniel Smith dansmith65@gmail.com 35 | * INSPIRED by ErrorData by matt@filemakermagazine.com 36 | * https://github.com/filemakerstandards/fmpstandards/tree/master/Functions 37 | * ===================================== 38 | */ 39 | 40 | // ALL LOG LEVELS 41 | # ( "LogLevel" ; logLevel ) 42 | & # ( "AccountName" ; Get ( AccountName ) ) 43 | & # ( "ApplicationVersion" ; Get ( ApplicationVersion ) ) 44 | & # ( "CurrentHostTimestamp" ; Get ( CurrentHostTimestamp ) ) 45 | & # ( "FileName" ; Get ( FileName ) ) 46 | & # ( "ScriptName" ; Get ( ScriptName ) ) 47 | & # ( "ScriptParameter" ; Get ( ScriptParameter ) ) 48 | & # ( "ScriptResult" ; Get ( ScriptResult ) ) 49 | & # ( "SystemPlatform" ; Get ( SystemPlatform ) ) 50 | & # ( "WindowName" ; Get ( WindowName ) ) 51 | 52 | // LEVEL 1 53 | & If ( logLevel ≤ 1 ; 54 | # ( "AllowAbortState" ; Get ( AllowAbortState ) ) 55 | & # ( "ErrorCaptureState" ; Get ( ErrorCaptureState ) ) 56 | & # ( "FilePath" ; Get ( FilePath ) ) 57 | & # ( "LayoutAccess" ; Get ( LayoutAccess ) ) 58 | & # ( "LayoutName" ; Get ( LayoutName ) ) 59 | & # ( "LayoutTableName" ; Get ( LayoutTableName ) ) 60 | & # ( "LayoutViewState" ; Get ( LayoutViewState ) ) 61 | & # ( "MultiUserState" ; Get ( MultiUserState ) ) 62 | & # ( "RecordAccess" ; Get ( RecordAccess ) ) 63 | & # ( "RecordID" ; Get ( RecordID ) ) 64 | & # ( "RecordOpenCount" ; Get ( RecordOpenCount ) ) 65 | & # ( "RecordOpenState" ; Get ( RecordOpenState ) ) 66 | & # ( "SortState" ; Get ( SortState ) ) 67 | & # ( "StatusAreaState" ; Get ( StatusAreaState ) ) 68 | & # ( "SystemVersion" ; Get ( SystemVersion ) ) 69 | & # ( "UserCount" ; Get ( UserCount ) ) 70 | & # ( "UserName" ; Get ( UserName ) ) 71 | & # ( "WindowMode" ; Get ( WindowMode ) ) 72 | & # ( "WindowStyle" ; Get ( WindowStyle ) ) 73 | & # ( "WindowVisible" ; Get ( WindowVisible ) ) 74 | & If (not isempty( Get ( ActiveFieldName ) ); 75 | # ( "ActiveFieldName" ; Get ( ActiveFieldName ) ) 76 | & # ( "ActiveFieldTableName" ; Get ( ActiveFieldTableName ) ) 77 | ) 78 | & If ( not isempty( Get ( ActiveLayoutObjectName ) ); 79 | # ( "ActiveLayoutObjectName" ; Get ( ActiveLayoutObjectName ) ) 80 | ) 81 | ) 82 | 83 | 84 | -------------------------------------------------------------------------------- /Functions/GetNumberAsBaseN.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================== 3 | * GetNumberAsBaseN ( number ; base ) 4 | * 5 | * PURPOSE: 6 | * Encodes numbers in any base between 2 and 36. Results from converting 7 | * fractional portions of numbers may be approximate rather than exact. 8 | * 9 | * RETURNS: 10 | * A string representing the number in the specified base. 11 | * 12 | * PARAMETERS: 13 | * number: The FileMaker (decimal) number to encode 14 | * base: The integer base (radix) of the encoding. 2 ≤ base ≤ 36. 15 | * 16 | * DEPENDENCIES: none 17 | * 18 | * RELEASE: 2011-11-30 19 | * ===================================== 20 | */ 21 | 22 | Case ( 23 | /* Step 0, set-up */ 24 | not $~radix.step; 25 | Let ( [ 26 | ~number = Abs ( GetAsNumber ( number ) ); 27 | ~base = Int ( base ); 28 | 29 | $~radix.isNegative = number < 0; 30 | $~radix.integer = Int ( ~number ); 31 | $~radix.fraction = Mod ( ~number ; 1 ); 32 | 33 | // Constrain encoding of fraction to analogous significant digits 34 | $~radix.fractionBound = 35 | Ceiling ( 36 | Ln ( 10 ^ ( Length ( $~radix.fraction ) - 1 ) ) 37 | / Ln ( ~base ) 38 | ); 39 | 40 | $~radix.step = 41 | If ( ~base < 2 or ~base > 36; 42 | -1; // error, skip to end 43 | /* Else */ 1 44 | ) 45 | ]; 46 | GetNumberAsBaseN ( "" ; ~base ) 47 | ); 48 | 49 | /* Step 1, encode integer portion of number */ 50 | $~radix.step = 1; 51 | Let ( [ 52 | ~digit = Mod ( $~radix.integer ; base ); 53 | ~digit = 54 | Middle ( 55 | "0123456789abcdefghijklmnopqrstuvwxyz"; 56 | ~digit + 1; 57 | 1 58 | ); 59 | $~radix.integer = Div ( $~radix.integer ; base ); 60 | $~radix.integerResult = ~digit & $~radix.integerResult; 61 | 62 | $~radix.step = 63 | Case ( 64 | $~radix.integer ; $~radix.step; // not done encoding integer 65 | $~radix.fraction ; $~radix.step + 1; 66 | /* Else */ 3 // skip to end 67 | ) 68 | ]; 69 | GetNumberAsBaseN ( "" ; base ) 70 | ); 71 | 72 | /* Step 2, encode fractional portion of number */ 73 | $~radix.step = 2; 74 | Let ( [ 75 | $~radix.i = $~radix.i - 1; 76 | ~digit = Div ( $~radix.fraction ; base ^ $~radix.i ); 77 | ~digit = 78 | Middle ( 79 | "0123456789abcdefghijklmnopqrstuvwxyz"; 80 | ~digit + 1; 81 | 1 82 | ); 83 | $~radix.fraction = Mod ( $~radix.fraction ; base ^ $~radix.i ); 84 | $~radix.fractionResult = $~radix.fractionResult & ~digit; 85 | 86 | $~radix.step = 87 | If ( $~radix.fraction and -$~radix.i < $~radix.fractionBound; 88 | $~radix.step; // not done encoding fraction 89 | /* Else */ $~radix.step + 1 90 | ) 91 | ]; 92 | GetNumberAsBaseN ( "" ; base ) 93 | ); 94 | 95 | /* Step 3, clean-up and return result */ 96 | $~radix.step = 3 or $~radix.step = -1; 97 | Let ( [ 98 | ~error = $~radix.step = -1; 99 | ~result = 100 | If ( $~radix.isNegative ; "-" ) 101 | & If ( $~radix.integerResult ; $~radix.integerResult ) 102 | & If ( $~radix.fractionResult ; "." & $~radix.fractionResult ); 103 | 104 | // Purge variables 105 | $~radix.fraction = ""; 106 | $~radix.fractionBound = ""; 107 | $~radix.fractionResult = ""; 108 | $~radix.i = ""; 109 | $~radix.integer = ""; 110 | $~radix.integerResult = ""; 111 | $~radix.isNegative = ""; 112 | $~radix.step = "" 113 | ]; 114 | If ( ~error ; "?" ; /* Else */ ~result ) 115 | ); 116 | ) -------------------------------------------------------------------------------- /Standards Diffs/standard.css: -------------------------------------------------------------------------------- 1 | body{font-size: 75%;padding: 0px 2% 0px 0px;line-height:1.4em;font-family:Verdana,Arial,Helvetica,sans-serif;} 2 | @media print{body{font-size:8pt;padding:0 0 0 48px;}} 3 | .boxHead,.boxSect,.boxCont,.boxFoot,.boxErr,.boxWarn,.boxLeg{padding-left:2%;border:1px solid #777;} 4 | .boxHead,.boxSect,.boxCont,.boxFoot,.boxErr,.boxWarn,.boxLeg,.lf{width:100%;} 5 | .boxCont,.boxErr,.boxWarn{border-top:none;} 6 | .boxCont{background-color:#cadcee;} 7 | .boxHead,.boxSect,.boxFoot{background-color:#ace;} 8 | .boxErr{background-color:#f63;} 9 | .boxLeg{background-color:#fff;border:none} 10 | .boxWarn{background-color:#fc0;} 11 | .boxSect,.boxFoot,.boxLeg{margin-top:1.4em;} 12 | .boxHead{-moz-border-radius-topleft:10px;-moz-border-radius-topright:10px;-webkit-border-top-left-radius:10px;-webkit-border-top-right-radius:10px;} 13 | .boxFoot{-moz-border-radius-bottomleft:10px;-moz-border-radius-bottomright:10px;-webkit-border-bottom-left-radius:10px;-webkit-border-bottom-right-radius:10px;} 14 | .txtHead{font-size:1.4em;line-height:1.4em;} 15 | .txtHead,.txtTit,.txtName,.l0,.lf,.boxFoot{font-weight:bold;} 16 | .l1,.l2{border-top:1px solid #ccc;} 17 | .l1{background-color:#fff;} 18 | .l2{background-color:#eee;} 19 | .lf{background-color:#090;} 20 | .italic{font-style:italic;} 21 | .c4711{float:right;width:48%;display:inline-block;vertical-align:top;} 22 | .c2{width:2%;} 23 | .c3{width:3%;} 24 | .c4{width:4%;} 25 | .c5,.c5e{width:5%;} 26 | .c6,.c6e{width:6%;} 27 | .c5e,.c6e{font-weight:bold;color:red} 28 | .c8{width:8%;} 29 | .c9{width:9%;} 30 | .c11{width:11%;} 31 | .c13{width:13%;} 32 | .c16,.c16s{width:16%;} 33 | .c16s,.id{color:#666;font-size:0.8em;font-weight:normal;} 34 | .c18{width:18%;} 35 | .c20{width:22%;} 36 | .c28{width:28%;} 37 | .c32{width:32%;} 38 | .c33{width:33%;background-color:#AAE;} 39 | .c40{width:41%;} 40 | .c50{width:52%;} 41 | .c70{width:70%;} 42 | .c80{width:80%;} 43 | .c97{width:96%;} 44 | .inl{padding-left:0.5em} 45 | .in1{padding-left:1em;} 46 | .in2{padding-left:2em;} 47 | .in3{padding-left:3em;} 48 | .in4{padding-left:4em;} 49 | .in5{padding-left:5em;} 50 | .in6{padding-left:6em;} 51 | .in7{padding-left:7em;} 52 | .in8{padding-left:8em;} 53 | .idel{background-color:#ff9f7f;} 54 | .iins{background-color:#C6E9C6;} 55 | .imod{background-color:#ffdf7f;} 56 | /*#C6E9C6.bkmod{background-color:#eee;}*/ 57 | .add,.bl,.c0,.c2,.c3,.c4,.c5,.c5e,.c6,.c6e,.c8,.c9,.c11,.c13,.c16,.c16s,.c18,.c20,.c28,.c32,.c33,.c40,.c40r,.c50,.c70,.c80,.c97,.mod,.rem,.uparr,.id,.idel,.iins,.imod{vertical-align:top;display:inline-block;} 58 | .id{float:right;} 59 | /*.boxSect,.boxCont,.boxErr,.boxWarn,.boxFoot,*/.add,.rem,.mod,.uparr,.bl,.c0,.l1,.l2,.c16,.c16s,.c32,.c33,.c50,.c40r,.c70,.c80,.c97{position:relative;} 60 | a{text-decoration:none;} 61 | a:hover[href] { background-color: #fcf61a; } 62 | .add:before{content:url('data:image/gif;base64,R0lGODlhDAAMAKIAADtgNhQgEmOgWic/I3a/a0+AR57/jwAAACH5BAAAAAAALAAAAAAMAAwAAAMdaLrcSsSxUuSiVmE3egjdwEAEAJDSZqmSIGQwnAAAOw==');} 63 | .mod:before{content:url('data:image/gif;base64,R0lGODlhDAAMAKIAACUgE9q/bT84IJWCS+/ReGBUMAAAAP/ffyH5BAAAAAAALAAAAAAMAAwAAAMaeLrc/jC2AcB4QYwAyiFF4BCCYRSEpK5seyQAOw==');} 64 | .rem:before{content:url('data:image/gif;base64,R0lGODlhDAAMAJEAAL94YD8oIP+ffwAAACH5BAAAAAAALAAAAAAMAAwAAAIPlI+py+0boowI2Guf3lwXADs=');} 65 | .uparr:before{content:url('data:image/gif;base64,R0lGODlhDAAMAKIDADtH+W+F9Ju676rM7jM++gAAAAAAAAAAACH5BAEAAAMALAAAAAAMAAwAAAMjOLrc/iMEOAQAAgZCphMclzVASADNZnZMuaKKurLgHAqyHSQAOw==');} 66 | .add,.rem,.mod,.uparr{width:12px;height:12px;top:1px;margin-right:2px;} 67 | html:not([dummy]) .css21{display:none;} 68 | /*body:nth-of-type(1) .css21{display:none;}*/ 69 | -------------------------------------------------------------------------------- /Functions/BusinessHolidays.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================== 3 | * BusinessHolidays ( calendarYear ) 4 | * 5 | * RETURNS: 6 | * A ¶-delimited list of the dates of observed holidays in calendarYear 7 | * 8 | * PARAMETERS: 9 | * calendarYear 10 | * 11 | * DEPENDENCIES: none 12 | * 13 | * NOTES: 14 | * This version of this function includes observed U.S. federal holidays. 15 | * It should be modified to include any holidays observed by the users of 16 | * each particular system. 17 | * 18 | * RELEASE: 2011-10-18 19 | * ===================================== 20 | */ 21 | 22 | List ( 23 | // New Years Day 24 | Let ( [ 25 | ~date = Date ( 1 ; 1 ; calendarYear ); 26 | ~day = DayOfWeek ( ~date ) 27 | ]; 28 | Case ( 29 | ~day = 1 ; ~date + 1; // following Monday 30 | ~day = 7 ; ""; // previous Friday (in previous year) 31 | /* Else */ ~date 32 | ) 33 | ); 34 | 35 | // Martin Luther King, Jr. Day (3rd Monday in January) 36 | Date ( 1 ; 15 ; calendarYear ) // earliest possible date 37 | + Choose ( DayOfWeek ( Date ( 1 ; 1 ; calendarYear ) ) - 1; 38 | 1 ; 0 ; 6 ; 5 ; 4 ; 3 ; 2 // adjust for day of week of month start 39 | ); 40 | 41 | // Washington's Birthday (3rd Monday in February) 42 | Date ( 2 ; 15 ; calendarYear ) // earliest possible date 43 | + Choose ( DayOfWeek ( Date ( 2 ; 1 ; calendarYear ) ) - 1; 44 | 1 ; 0 ; 6 ; 5 ; 4 ; 3 ; 2 // adjust for day of week of month start 45 | ); 46 | 47 | // Memorial Day (last Monday in May) 48 | Date ( 5 ; 25 ; calendarYear ) // earliest possible date 49 | + Choose ( DayOfWeek ( Date ( 5 ; 31 ; calendarYear ) ) - 1; 50 | 0 ; 6 ; 5 ; 4 ; 3 ; 2 ; 1 // adjust for day of week of month end 51 | ); 52 | 53 | // U.S. Independence Day (4 July) 54 | Let ( [ 55 | ~date = Date ( 7 ; 4 ; calendarYear ); 56 | ~day = DayOfWeek ( ~date ) 57 | ]; 58 | Case ( 59 | ~day = 1 ; ~date + 1; // following Monday 60 | ~day = 7 ; ~date - 1; // previous Friday 61 | /* Else */ ~date 62 | ) 63 | ); 64 | 65 | // Labor Day (1st Monday in September) 66 | Date ( 9 ; 1 ; calendarYear ) // earliest possible date 67 | + Choose ( DayOfWeek ( Date ( 9 ; 1 ; calendarYear ) ) - 1; 68 | 1 ; 0 ; 6 ; 5 ; 4 ; 3 ; 2 // adjust for day of week of month start 69 | ); 70 | 71 | // Columbus Day (2nd Monday in October) 72 | Date ( 10 ; 8 ; calendarYear ) // earliest possible date 73 | + Choose ( DayOfWeek ( Date ( 10 ; 1 ; calendarYear ) ) - 1; 74 | 1 ; 0 ; 6 ; 5 ; 4 ; 3 ; 2 // adjust for day of week of month start 75 | ); 76 | 77 | // Veterans Day (11 November) 78 | Let ( [ 79 | ~date = Date ( 11 ; 11 ; calendarYear ); 80 | ~day = DayOfWeek ( ~date ) 81 | ]; 82 | Case ( 83 | ~day = 1 ; ~date + 1; // following Monday 84 | ~day = 7 ; ~date - 1; // previous Friday 85 | /* Else */ ~date 86 | ) 87 | ); 88 | 89 | // Thanksgiving Day (4th Thursday in November) 90 | Date ( 11 ; 22 ; calendarYear ) // earliest possible date 91 | + Choose ( DayOfWeek ( Date ( 11 ; 1 ; calendarYear ) ) - 1; 92 | 4 ; 3 ; 2 ; 1 ; 0 ; 6 ; 5 // adjust for day of week of month start 93 | ); 94 | 95 | // Christmas Day (25 December) 96 | Let ( [ 97 | ~date = Date ( 12 ; 25 ; calendarYear ); 98 | ~day = DayOfWeek ( ~date ) 99 | ]; 100 | Case ( 101 | ~day = 1 ; ~date + 1; // following Monday 102 | ~day = 7 ; ~date - 1; // previous Friday 103 | /* Else */ ~date 104 | ) 105 | ); 106 | 107 | // New Years Day (observed from next year) 108 | Let ( [ 109 | ~date = Date ( 1 ; 1 ; calendarYear + 1 ) 110 | ]; 111 | If ( DayOfWeek ( ~date ) = 7 ; ~date - 1 /* Else, Null */ ) 112 | ) 113 | ) -------------------------------------------------------------------------------- /Functions/UUID/UUIDGetAsRFC4122.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ====================================== 3 | * UUIDGetAsRFC4122 ( theID ) 4 | * 5 | * PURPOSE: 6 | * Converts a numeric UUID created by this family of functions to the canonical 7 | * hexadecimal (base 16) representation for the RFC 4122 standard. 8 | * 9 | * RETURNS: 10 | * A text string representing an RFC 4122 universally unique identifier. 11 | * 12 | * PARAMETERS: 13 | * theID: The numeric UUID to convert 14 | * 15 | * DEPENDENCIES: none 16 | * 17 | * RELEASE: 2011-07-22 18 | * 19 | * REFERENCES: 20 | * Key values Best Practice: http://filemakerstandards.org/pages/viewpage.action?pageId=557386 21 | * RFC 4122 UUID standard specification: http://tools.ietf.org/html/rfc4122 22 | * Tom Robinson's UUID function: http://www.briandunning.com/cf/969 23 | * UUIDGetAsRFC4122: http://www.briandunning.com/cf/1221 24 | * UUIDGetAsUUID: http://www.briandunning.com/cf/1273 25 | * UUIDGetNICAddress: http://www.briandunning.com/cf/1222 26 | * UUIDGetSeconds: http://www.briandunning.com/cf/1223 27 | * UUIDNew: http://www.briandunning.com/cf/1220 28 | * UUIDRandom: http://www.briandunning.com/cf/1246 29 | * ====================================== 30 | */ 31 | 32 | Case ( 33 | /*Extract and arrange UUID components*/ 34 | not $~uuid.step; 35 | Let ( [ 36 | ~idAsNumber = GetAsNumber ( theID ); 37 | ~idLength = Length ( ~idAsNumber ); 38 | 39 | // extract components of FileMaker-centric format 40 | ~version = Left ( ~idAsNumber ; ~idLength - 40 ); 41 | ~reserved = Middle ( ~idAsNumber ; ~idLength - 39 ; 1 ); 42 | ~timestamp = Middle ( ~idAsNumber ; ~idLength - 38 ; 12 ); 43 | // move epoch from 0001-01-01T00:00:00 to 1582-10-15T00:00:00 44 | ~timestamp = Max ( ~timestamp - 49916304000 ; 0 ); 45 | ~timestamp = ~timestamp & Middle ( ~idAsNumber ; ~idLength - 26 ; 7 ); 46 | ~clockSequence = Middle ( ~idAsNumber ; ~idLength - 19 ; 5 ); 47 | ~node = Right ( ~idAsNumber ; 15 ); 48 | 49 | // convert to RFC 4122 components 50 | ~time_low = Mod ( ~timestamp ; 2^32 ); //32 bits 51 | ~time_mid = Mod ( Div ( ~timestamp ; 2^32 ) ; 2^16 ); //next 16 bits 52 | ~time_hi = Mod ( Div ( ~timestamp ; 2^48 ) ; 2^12 ); //next 12 bits 53 | ~time_hi_and_version = ~version * (2^12) + ~time_hi; 54 | ~clock_seq = Mod ( ~clockSequence ; 2^15 ); 55 | ~clock_seq_and_reserved = 56 | Case ( 57 | ~reserved < 2; //1 bit 58 | ~reserved * 2^15 + ~clock_seq; 59 | 60 | ~reserved < 4; //2 bits 61 | ~reserved * 2^14 + Mod ( ~clock_seq ; 2^14 ); 62 | 63 | /*else, ~reserved is 3 bits*/ 64 | Mod ( ~reserved ; 8 ) * 2^13 + Mod ( ~clock_seq ; 2^13 ) 65 | ); 66 | $~uuid = 67 | ~time_low * (2^96) 68 | + ~time_mid * (2^80) 69 | + ~time_hi_and_version * (2^64) 70 | + ~clock_seq_and_reserved * (2^48) 71 | + ~node; 72 | 73 | //set-up conversion from base 10 to base 16 74 | $~hex = ""; 75 | $~uuid.step = 1 76 | ]; 77 | UUIDGetAsRFC4122 ( "" ) 78 | ); 79 | 80 | /*Convert to base 16*/ 81 | $~uuid.step = 1; 82 | Let ( [ 83 | ~hexDigit = Mod ( $~uuid ; 16 ); 84 | $~hex = Middle ( "0123456789abcdef" ; ~hexDigit + 1 ; 1 ) & $~hex; 85 | $~uuid = Div ( $~uuid ; 16 ); 86 | $~uuid.step = If ( $~uuid ; $~uuid.step ; /*else*/ $~uuid.step + 1 ) 87 | ]; 88 | UUIDGetAsRFC4122 ( "" ) 89 | ); 90 | 91 | /*Return result*/ 92 | $~uuid.step = 2; 93 | Let ( [ 94 | ~hex = $~hex; 95 | ~hex = 96 | Left ( ~hex ; 8 ) & "-" 97 | & Middle ( ~hex ; 9 ; 4 ) & "-" 98 | & Middle ( ~hex ; 13 ; 4 ) & "-" 99 | & Middle ( ~hex ; 17 ; 4 ) & "-" 100 | & Right ( ~hex ; 12 ); 101 | 102 | //purge variables 103 | $~hex = ""; 104 | $~uuid.step = ""; 105 | $~uuid = "" 106 | ]; 107 | ~hex 108 | ) 109 | ) -------------------------------------------------------------------------------- /Functions/UUID/UUIDGetAsUUID.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ====================================== 3 | * UUIDGetAsUUID ( rfc4122 ) 4 | * 5 | * PURPOSE: 6 | * Converts a UUID in the canonical RFC 4122 hexadecimal format to a numeric 7 | * format with better performance characteristics as a primary key in FileMaker 8 | * records. 9 | * 10 | * RETURNS: 11 | * A 41-digit delimited number of the form: 12 | * v-r-mmmmmmmmmmmm-sssssss-ccccc-nnnnnnnnnnnnnnn 13 | * With version 1 and 2 UUIDs, the sections of the UUID correspond to: 14 | * v: The UUID version (type) number 15 | * r: A variant code reserved by the RFC 4122 standard 16 | * m: The creation timestamp (seconds since 0001-01-01T00:00:00) 17 | * s: The sub-second portion of the original UUID's timestamp 18 | * c: The "clock sequence" 19 | * n: The NIC address ("node") of the device that created the UUID 20 | * 21 | * PARAMETERS: 22 | * rfc4122: The hexadecimal UUID to convert 23 | * 24 | * DEPENDENCIES: none 25 | * 26 | * RELEASE: 2011-07-22 27 | * 28 | * REFERENCES: 29 | * Key values Best Practice: http://filemakerstandards.org/pages/viewpage.action?pageId=557386 30 | * RFC 4122 UUID standard specification: http://tools.ietf.org/html/rfc4122 31 | * UUIDGetAsRFC4122: http://www.briandunning.com/cf/1221 32 | * UUIDGetAsUUID: http://www.briandunning.com/cf/1273 33 | * UUIDGetNICAddress: http://www.briandunning.com/cf/1222 34 | * UUIDGetSeconds: http://www.briandunning.com/cf/1223 35 | * UUIDNew: http://www.briandunning.com/cf/1220 36 | * UUIDRandom: http://www.briandunning.com/cf/1246 37 | * ====================================== 38 | */ 39 | 40 | Case ( 41 | /*Set-up conversion from base 16 to base 10*/ 42 | not $~uuid.step; 43 | Let ( [ 44 | $~uuid.step = 1 45 | ]; 46 | UUIDGetAsUUID ( 47 | Right ( Filter ( Lower ( rfc4122 ) ; "0123456789abcdef" ) ; 32 ) 48 | ) 49 | ); 50 | 51 | /*Convert to base 10*/ 52 | $~uuid.step = 1; 53 | Let ( [ 54 | $~uuid.i = $~uuid.i + 1; 55 | ~hexDigit = Middle ( rfc4122 ; $~uuid.i ; 1 ); 56 | $~decimal = 57 | $~decimal * 16 58 | + ( Position ( "0123456789abcdef" ; ~hexDigit ; 1 ; 1 ) - 1 ); 59 | $~uuid.step = 60 | If ( $~uuid.i < 32 ; $~uuid.step ; /*else*/ $~uuid.step + 1 ) 61 | ]; 62 | UUIDGetAsUUID ( rfc4122 ) 63 | ); 64 | 65 | /*Format and return result*/ 66 | $~uuid.step = 2; 67 | Let ( [ 68 | //extract RFC 4122 components 69 | ~node = Mod ( $~decimal ; 2^48 ); 70 | ~clock_seq_and_reserved = Mod ( Div ( $~decimal ; 2^48 ) ; 2^16 ); 71 | ~reserved = 72 | Case ( 73 | ~clock_seq_and_reserved < 2^15 ; 0; 74 | ~clock_seq_and_reserved < 2^15 + 2^14 ; 2; 75 | ~clock_seq_and_reserved < 2^15 + 2^14 + 2^13 ; 6; 76 | /*else*/ 7 77 | ); 78 | ~clock_seq = 79 | Case ( 80 | ~reserved = 0 ; ~clock_seq_and_reserved; 81 | ~reserved = 2 ; Mod ( ~clock_seq_and_reserved ; 2^14 ); 82 | ~reserved ≥ 6 ; Mod ( ~clock_seq_and_reserved ; 2^13 ); 83 | ); 84 | ~time_hi_and_version = Mod ( Div ( $~decimal ; 2^64 ) ; 2^16 ); 85 | ~time_hi = Mod ( ~time_hi_and_version ; 2^12 ); 86 | ~version = Div ( ~time_hi_and_version ; 2^12 ); 87 | ~time_mid = Mod ( Div ( $~decimal ; 2^80 ) ; 2^16 ); 88 | ~time_low = Mod ( Div ( $~decimal ; 2^96 ) ; 2^32 ); 89 | 90 | //convert to FileMaker-centric format 91 | ~timestamp = 92 | ~time_hi * 2^48 93 | + ~time_mid * 2^32 94 | + ~time_low; 95 | ~serialNumber = Right ( ~timestamp ; 7 ); 96 | //move epoch from 0001-01-01T00:00:00 to 1582-10-15T00:00:00 97 | ~timestamp = Div ( ~timestamp ; 10000000 ) + 49916304000; 98 | 99 | //purge variables 100 | $~decimal = ""; 101 | $~uuid.i = ""; 102 | $~uuid.step = "" 103 | ]; 104 | //format result 105 | ~version & "-" 106 | & ~reserved & "-" 107 | & Right ( "000000000000" & ~timestamp ; 12 ) 108 | & "-" 109 | & Right ( "0000000" & ~serialNumber ; 7 ) & "-" 110 | & Right ( "00000" & ~clock_seq ; 5 ) 111 | & "-" 112 | & Right ( "000000000000000" & ~node ; 15 ) 113 | ) 114 | ) -------------------------------------------------------------------------------- /Functions/UUID/UUIDTimestamp.fmfn: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * UUIDTimestamp 3 | * 4 | * Creates a universally unique identifier suitable for use as a primary key in 5 | * FileMaker number fields. The UUID is an encoding of the creation timestamp 6 | * (UTC), a serial number, and random numbers. This may be useful in some 7 | * situations to preserve the anonymity of the device that generated the UUID. 8 | * The values returned by this and related functions have a one-to-one 9 | * correspondence with UUIDs following the RFC 4122 standard all values in the 10 | * format generated by this function can be converted to RFC 4122, and vice 11 | * versa. 12 | * 13 | * This function generates UUIDs that are meaningfully sortable. Values sort by 14 | * version, then approximate creation order. Because of the leading sort by 15 | * version, random (version 4) values (where the creation order cannot be 16 | * determined by the values) will cluster separately from timestamp-node 17 | * (version 1) values. 18 | * 19 | * The value can be converted to RFC 4122 canonical form (hexadecimal) with the 20 | * UUIDGetAsRFC4122 function. The creation timestamp can be extracted with the 21 | * UUIDGetTimestamp function. 22 | * 23 | * @return A 41-digit number of the form: 24 | * vrttttttttttttssssssscccccnnnnnnnnnnnnnnn 25 | * The sections of the UUID correspond to: 26 | * v: A UUID version (type) number 27 | * r: A variant code reserved by the RFC 4122 standard 28 | * t: The creation timestamp (seconds since 0001-01-01T00:00:00) 29 | * s: A serial number, reset for each second 30 | * c: A session key, randomly generated for each session 31 | * n: The randomly-generated "node" for a session 32 | * 33 | * @global $$~UUID.DATA[2] 34 | * 35 | * @history 2012-02-25 - Jeremy Bante - Created 36 | * @history 2013-12-13 - Jeremy Bante - Generate ~serial 37 | * number values that result in easier-to-read timestamps from the 38 | * UUIDGetTimestamp function. 39 | * @history 2013-12-13 - Jeremy Bante - Using new 40 | * Get ( CurrentTimeUTCMilliseconds ) function in FileMaker 13. 41 | * 42 | * @see http://filemakerstandards.org/pages/viewpage.action?pageId=557138 Key values best practice 43 | * @see http://tools.ietf.org/html/rfc4122 RFC 4122 standard UUID specification 44 | ******************************************************************************/ 45 | 46 | Let ( [ 47 | // Set-up timestamp, serial number, and session key 48 | ~session = GetValue ( $$~UUID.DATA[2] ; 1 ) ; 49 | ~lastTimestamp = GetValue ( $$~UUID.DATA[2] ; 2 ) ; 50 | ~serial = GetAsNumber ( GetValue ( $$~UUID.DATA[2] ; 3 ) ) ; 51 | ~node = GetValue ( $$~UUID.DATA[2] ; 4 ) ; 52 | 53 | ~now = Mod ( Get ( CurrentTimeUTCMilliseconds ) ; 165208454460000 ) ; 54 | ~session = 55 | Case ( 56 | IsEmpty ( ~session ) ; // new session 57 | Floor ( Random * 16384 ) ; 58 | 59 | ~serial ≥ .9999 // serial number overflow 60 | and ~now = ~lastTimestamp 61 | or ~now < ~lastTimestamp ; // clock change 62 | Mod ( ~session + 1 ; 16384 ) ; 63 | 64 | /* Else */ 65 | ~session 66 | ) ; 67 | ~magnitude = 68 | Max ( 69 | Left ( Substitute ( ~serial ; "." ; "" ) ; 1 ) ; 70 | 1 71 | ) ; 72 | ~serial = 73 | Case ( 74 | ~now ≠ ~lastTimestamp or ~serial = .9999 ; 0 ; 75 | ~serial = 0 ; .1 ; 76 | ~serial < .4 ; ~serial + 10 ^ ( 0 - ~magnitude ) ; 77 | /* Else */ ~serial + 10 ^ -4 78 | ) ; 79 | ~node = 80 | If ( ~node ; 81 | ~node ; 82 | /* Else, new random node */ 83 | // set multicast bit to avoid colliding with NIC addresses 84 | ( Floor ( Random * 128 ) * 2 + 1 ) * 1099511627776 85 | + Floor ( Random * 1099511627776 ) 86 | ) ; 87 | 88 | // Store data for future reference 89 | $$~UUID.DATA[2] = List ( ~session ; ~now ; ~serial ; ~node ) 90 | ] ; 91 | // v-r-tttt tttt tttt-sss ssss-ccccc-nnnnn nnnnn nnnnn 92 | "12" //version and reserved variant code 93 | // "1-2-" 94 | & Right ( "000000000000" & ~now ; 12 ) 95 | // & "-" 96 | & Left ( Substitute ( ~serial ; "." ; "" ) & "0000000" ; 7 ) 97 | // & "-" 98 | & Right ( "00000" & ~session ; 5 ) 99 | // & "-" 100 | & Right ( "000000000000000" & ~node ; 15 ) 101 | ) -------------------------------------------------------------------------------- /Functions/#Name-Value/#.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================== 3 | * # ( name ; value ) 4 | * 5 | * RETURNS: 6 | * An name-value pair in Let notation. 7 | * 8 | * PARAMETERS: 9 | * name: The name for the returned name-value pair. name can be any value 10 | * that would be a valid Let() variable name. 11 | * value: The value for the returned name-value pair. 12 | * 13 | * EXAMPLE: 14 | * # ( "name"; $value ) & # ( "foo" ; "bar" ) 15 | * 16 | * DEPENDENCIES: none 17 | * 18 | * HISTORY: 19 | * MODIFIED on 2014-10-07 by Daniel Smith dansmith65@gmail.com to prevent 20 | * from returning an EvaluationError. 21 | * MODIFIED on 2014-06-06 by Jeremy Bante to fix an 22 | * issue where long sequences of digits could be interpreted as timestamps. 23 | * MODIFIED on 2014-05-25 by Jeremy Bante to detect 24 | * type using a different method suggested by Arnold Kegebein. 25 | * MODIFIED on 2013-12-24 by Jeremy Bante to return 26 | * an error if name contains a carriage return (Char ( 13 )), and removing 27 | * explicit encoding for line feeds. 28 | * MODIFIED on 2013-12-12 by John Jones 29 | * to explicitly encode line feed characters in text values. 30 | * MODIFIED on 2013-09-02 by Daniel Smith dansmith65@gmail.com to fix a 31 | * type misidentification bug. 32 | * MODIFIED on 2013-07-27 by Jeremy Bante to fix a 33 | * type misidentification bug. 34 | * MODIFIED on 2013-04-15 by Jeremy Bante to not 35 | * wrap numbers in GetAsNumber. 36 | * MODIFIED on 2013-01-14 by Daniel Smith dansmith65@gmail.com to include 37 | * trailing return on error result and accept a value of "?" 38 | * MODIFIED on 2012-12-12 by Daniel Smith dansmith65@gmail.com to preserve 39 | * data type of value 40 | * MODIFIED on 2012-12-07 by Jeremy Bante where an 41 | * error result could create invalid sytax for values containing a comment 42 | * close sequence. 43 | * MODIFIED on 2012-11-28 by Jeremy Bante to return 44 | * error feedback, and to prefix names with "$". 45 | * CREATED on 2012-11-10 by Jeremy Bante . 46 | * 47 | * REFERENCES: 48 | * https://github.com/filemakerstandards/fmpstandards/blob/master/Functions/%23Name-Value/%23.fmfn 49 | * ===================================== 50 | */ 51 | 52 | Let ( [ 53 | ~name = // strip leading "$$" and "$" 54 | Substitute ( 55 | "/*start*/" & name ; 56 | [ "/*start*/$$" ; "" ] ; 57 | [ "/*start*/$" ; "" ] ; 58 | [ "/*start*/" ; "" ] 59 | ) ; 60 | ~plusOneText = GetAsText ( value + 1 ) ; 61 | ~isValidDate = not EvaluationError ( GetAsDate ( value ) ) ; 62 | ~isValidTime = not EvaluationError ( GetAsTime ( value ) ) ; 63 | ~number = GetAsNumber ( value ) ; 64 | ~value = 65 | Case ( 66 | value = "" or value = "?" or ~number = "?" ; 67 | Quote ( value ) ; 68 | 69 | ~isValidDate 70 | and ~isValidTime 71 | and GetAsText ( GetAsTimestamp ( value ) + 1 ) = ~plusOneText ; 72 | "GetAsTimestamp ( " & Quote ( value ) & " )" ; 73 | 74 | ~isValidTime 75 | and GetAsText ( GetAsTime ( value ) + 1 ) = ~plusOneText ; 76 | "GetAsTime ( " & Quote ( value ) & " )" ; 77 | 78 | ~isValidDate 79 | and GetAsText ( GetAsDate ( value ) + 1 ) = ~plusOneText ; 80 | "GetAsDate ( " & Quote ( value ) & " )" ; 81 | 82 | value ≠ ~number ; 83 | Quote ( value ) ; 84 | 85 | /* Else */ 86 | ~number 87 | ) ; 88 | ~result = 89 | "$" 90 | & ~name 91 | & " = " 92 | & ~value 93 | & " ;¶" ; 94 | ~testExpression = 95 | "Let ( [ " 96 | & ~result 97 | & " ~ = \"\" ]; \"\" )" ; 98 | ~error = 99 | Case ( 100 | IsEmpty ( ~name ) or Position ( ~name ; ¶ ; 1 ; 1 ) ≠ 0 ; 101 | 11 ; // Name is not valid 102 | 103 | not IsValidExpression ( ~testExpression ) ; 104 | 1200 // Generic calculation error 105 | ) 106 | ]; 107 | If ( ~error ; // prevent bad pairs from affecting evaluation by commenting 108 | "/* Error " 109 | & ~error 110 | & " name: " 111 | & Quote ( 112 | Substitute ( // escape comment character sequences 113 | name ; 114 | [ "*/" ; "\*\/" ] ; 115 | [ "/*" ; "\/\*" ] 116 | ) 117 | ) 118 | & " value: " 119 | & Quote ( 120 | Substitute ( // escape comment character sequences 121 | value ; 122 | [ "*/" ; "\*\/" ] ; 123 | [ "/*" ; "\/\*" ] 124 | ) 125 | ) 126 | & " */" 127 | & ¶ ; 128 | /* Else */ 129 | ~result 130 | ) 131 | ) -------------------------------------------------------------------------------- /Functions/Debug.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================================== 3 | * Debug ( ) 4 | * 5 | * PARAMETERS: 6 | * none 7 | * 8 | * RETURNS: 9 | * (string) A list of informative values 10 | * about the current FMP environment. 11 | * 12 | * DEPENDENCIES: 13 | * Developer () 14 | * 15 | * NOTES: 16 | * 17 | * RELEASE: 18 | * 2015-11-04 - Added missing functions from 13/14 releases. 19 | * 2014-05-08 - Injecting LayoutID despite it not being an internal function. 20 | * 2014-03-04 - Updated recursion and added more functions. 21 | * 2013-01-25 - Updated to be recursive and use better output. Included more functions 22 | * 101009 - Initial by Matt Petrowsky 23 | * ===================================================== 24 | * 25 | */ 26 | 27 | If ( Developer; 28 | Let ( [ 29 | ~input = List ( 30 | "Environment"; 31 | " AllowAbortState"; 32 | " ApplicationArchitecture"; 33 | " ApplicationLanguage"; 34 | " ApplicationVersion"; 35 | " ConnectionState"; 36 | " CurrentDate"; 37 | " CurrentHostTimeStamp"; 38 | " CurrentTime"; 39 | " CurrentTimeStamp"; 40 | " CurrentTimeUTCMilliseconds"; 41 | " CustomMenuSetName"; 42 | " Device"; 43 | " ErrorCaptureState"; 44 | " FoundCount"; 45 | " HostApplicationVersion"; 46 | " HostIPAddress"; 47 | " HostName"; 48 | " InstalledFMPlugins"; 49 | " ModifiedFields"; 50 | " MultiUserState"; 51 | " PageNumber"; 52 | " PageNumber"; 53 | " PersistentID"; 54 | " QuickFindText"; 55 | " RecordOpenCount"; 56 | " RequestCount"; 57 | " RequestOmitState"; 58 | " ScriptAnimationState"; 59 | " SystemIPAddress"; 60 | " SystemLanguage"; 61 | " SystemNICAddress"; 62 | " SystemPlatform"; 63 | " SystemVersion"; 64 | " UseSystemFormatsState"; 65 | "Connection"; 66 | " ConnectionAttributes"; 67 | " NetworkProtocol"; 68 | " NetworkType"; 69 | "File"; 70 | " EncryptionState"; 71 | " FileName"; 72 | " FileSize"; 73 | "Errors"; 74 | " LastError"; 75 | " LastODBCError"; 76 | "User"; 77 | " AccountExtendedPrivileges"; 78 | " AccountName"; 79 | " AccountPrivilegeSetName"; 80 | " CurrentExtendedPrivileges"; 81 | " CurrentPrivilegeSetName"; 82 | " UserCount"; 83 | " UserName"; 84 | "Mobile"; 85 | " TouchKeyboardState"; 86 | "Layout"; 87 | " ActiveLayoutObjectName"; 88 | " ActivePortalRowNumber"; 89 | " AllowFormattingBarState"; 90 | " LayoutAccess"; 91 | " LayoutCount"; 92 | " LayoutName"; 93 | " LayoutNumber"; 94 | " LayoutTableName"; 95 | " LayoutTableName"; 96 | " LayoutViewState"; 97 | " MenubarState"; 98 | " StatusAreaState"; 99 | " TextRulerVisible"; 100 | "Windows"; 101 | " ScreenDepth"; 102 | " ScreenHeight"; 103 | " ScreenScaleFactor"; 104 | " ScreenWidth"; 105 | " WindowContentHeight"; 106 | " WindowContentWidth"; 107 | " WindowDesktopHeight"; 108 | " WindowDesktopWidth"; 109 | " WindowHeight"; 110 | " WindowLeft"; 111 | " WindowMode"; 112 | " WindowName"; 113 | " WindowOrientation"; 114 | " WindowStyle"; 115 | " WindowTop"; 116 | " WindowVisible"; 117 | " WindowWidth"; 118 | " WindowZoomLevel"; 119 | "Records"; 120 | " RecordAccess"; 121 | " RecordID"; 122 | " RecordModificationCount"; 123 | " RecordNumber"; 124 | " RecordOpenState"; 125 | " SortState"; 126 | " TotalRecordCount"; 127 | "Field"; 128 | " ActiveFieldContents"; 129 | " ActiveFieldName"; 130 | " ActiveFieldTableName"; 131 | " ActiveRepetitionNumber"; 132 | " ActiveSelectionSize"; 133 | " ActiveSelectionStart"; 134 | " CalculationRepetitionNumber"; 135 | "Script"; 136 | " ScriptName"; 137 | " ScriptParameter"; 138 | " ScriptResult"; 139 | "Paths"; 140 | " DesktopPath"; 141 | " DocumentsPath"; 142 | " FileMakerPath"; 143 | " FilePath"; 144 | " PreferencesPath"; 145 | " SystemDrive"; 146 | " TemporaryPath"; 147 | ); 148 | ~padding = " "; // 30 chars is longest function name 149 | ~tab = " "; 150 | ~line = GetValue ( ~input ; $~debugCounter ); 151 | ~eval = If ( Left ( ~line ; 1 ) = ~tab ; // evaluate lines prefixed with tabs 152 | Let ( [ 153 | ~function = Trim ( ~line ); 154 | ~result = Evaluate ( "Get(" & ~function & ")" ) 155 | ]; 156 | // Output 157 | ~function 158 | & Left ( ~padding ; Length ( ~padding ) - Length ( ~function ) ) // spacing out result 159 | & If ( ValueCount ( ~result ) > 1; // some functions return multiple lines 160 | Substitute ( ~result ; ¶ ; ¶ & ~tab & Left ( ~padding ; Length ( ~padding ) - 1 ) ); 161 | ~result 162 | ) 163 | ); 164 | 165 | ~line 166 | ); 167 | ~exit = $~debugCounter = ValueCount ( ~input ) 168 | ]; 169 | Let ( [ 170 | $~debugCounter = If ( ~exit ; "" ; $~debugCounter + 1 ); 171 | $~debugOutput = List ( $~debugOutput ; ~eval ) 172 | ]; 173 | If ( ~exit ; 174 | Substitute ( 175 | $~debugOutput & Let ( $~debugOutput = "" ; ""); 176 | [ "Layout¶" ; "Layout¶" 177 | & ~tab 178 | & "LayoutID" 179 | & Left ( ~padding ; Length ( ~padding ) - Length ( "LayoutID" ) - 1 ) 180 | & GetValue ( LayoutIDs ( Get ( FileName ) ) ; Get ( LayoutNumber ) ) 181 | &¶ ] 182 | ) ; 183 | Debug ) 184 | ) 185 | ) 186 | ) 187 | -------------------------------------------------------------------------------- /Functions/DeveloperWebview.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================================== 3 | * DeveloperWebview ( ) 4 | * 5 | * PURPOSE: 6 | * Use a web viewer to display developer specific information 7 | * about the current layout. 8 | * 9 | * PARAMETERS: 10 | * none 11 | * 12 | * RETURNS: 13 | * (string) Web viewer html 14 | * 15 | * DEPENDENCIES: 16 | * CustomList() 17 | * 18 | * NOTES: 19 | * You can optionally use this function directly within a web viewer 20 | * 21 | * HISTORY: 22 | * MODIFIED on 2011-9-6 by matt@filemakermagazine.com - released 23 | * ===================================================== 24 | * 25 | */ 26 | 27 | Let ( [ 28 | @title = "class=" & Quote ( "title" ); 29 | @data = "class=" & Quote ( "data" ); 30 | ~css = " 31 | * { 32 | margin: 0; 33 | } 34 | html, body { 35 | font: 11px \"DejaVu Sans Mono\", \"Anadale Mono\", \"Courier New\", Courier, mono; 36 | background-color:#eee; 37 | } 38 | ul { 39 | list-style: none; 40 | -webkit-padding-start: 15px 41 | } 42 | li:before { 43 | content: \"» \"; 44 | color: gray; 45 | } 46 | li span { 47 | float: right; 48 | color: gray; 49 | } 50 | div { 51 | } 52 | #header{ 53 | background-color: blue; 54 | color: white; 55 | padding: 6px; 56 | font-size: 22px; 57 | margin-bottom: 5px; 58 | } 59 | .title { 60 | background-color: #3f3f3f; 61 | color: white; 62 | padding: 4px; 63 | margin: 5px 0; 64 | -webkit-box-shadow: 0 3px 5px #888; 65 | box-shadow: 0 3px 5px #888; 66 | border-bottom: 1px solid #1a1a1a; 67 | } 68 | .data { 69 | margin-left: 15px; 70 | } 71 | " 72 | ]; 73 | List ( 74 | "data:text/html,"; 75 | ""; 76 | ""; 77 | "Developer Details"; 78 | ""; 79 | ""; 80 | ""; 81 | // Header 82 | "
Developer Details
"; 83 | "
"; // an anchor link to FileMaker Help @todo need to account for windows 84 | Let ( [ 85 | ~pathPrefix = Substitute ( MiddleValues ( Substitute ( Get ( FileMakerPath ) ; "/" ; ¶ ) ; 3 ; 10000 ) ; ¶ ; "/" ); 86 | ~appName = Case ( 87 | PatternCount ( Get ( ApplicationVersion ) ; "ProAdvanced" ); 88 | "FileMaker Pro Advanced.app"; 89 | 90 | "FileMaker Pro.app" 91 | ); 92 | ~pathSuffix = "/Contents/Resources/English.lproj/FileMaker Pro Help/" 93 | ]; 94 | "FileMaker Help" 95 | ); 96 | "
"; 97 | // Table 98 | "
TABLE OCCURRENCE NAME
"; 99 | "
"; 100 | Get ( LayoutTableName ); 101 | "
"; 102 | // Layout name & id 103 | "
LAYOUT NAME & ID
"; 104 | "
"; 105 | Get ( LayoutName ) & "(" & GetValue ( LayoutIDs ( Get ( FileName ) ) ; Get ( LayoutNumber ) ) & ")"; 106 | "
"; 107 | // Layout objects 108 | "
LAYOUT OBJECTS
"; 109 | "
"; 110 | Let ( [ 111 | $~html = "
    ¶" & Let ( [ 112 | $~objects = LayoutObjectNames ( Get ( FileName ) ; Get ( LayoutName ) ); 113 | $~level = 0; 114 | $~indent = "<"; 115 | $~outdent = ">"; 116 | ~function = "Let ( ~value = GetValue ( $~objects ; [n] ) ; 117 | Case ( 118 | ~value = $~indent ; 119 | Let ( $~level = $~level + 1 ; \"
      \" ); 120 | 121 | ~value = $~outdent; 122 | Let ( $~level = $~level - 1 ; \"
    \" ); 123 | 124 | \"
  • \" & ~value & \"
  • \" 125 | ) 126 | )" 127 | ]; 128 | CustomList ( 1 ; ValueCount ( $~objects ) ; ~function ) 129 | ) & "¶
"; 130 | $~objects = Substitute ( LayoutObjectNames ( Get ( FileName ) ; Get ( LayoutName ) ) ; ["<¶" ; ""] ; ["¶>" ; ""] ); 131 | $~menubar = 15; //@todo - need to account for windows vs. mac 132 | ~swap = CustomList ( 1; ValueCount ( $~objects ); // creating internal Substitution() block 133 | "Let ( ~value = GetValue ( $~objects ; [n] ); 134 | \"[\" & Quote (~value & \"<\") & \";\" & Quote ( ~value & \" (\" & GetLayoutObjectAttribute ( ~value ; \"ObjectType\" ) & \" @ \" & GetLayoutObjectAttribute ( ~value ; \"Left\" ) - Get ( WindowLeft ) & \" x \" & GetLayoutObjectAttribute ( ~value ; \"Top\" ) - Get ( WindowTop ) - ( Get ( WindowHeight ) - Get ( WindowContentHeight ) ) + $~menubar & \")<\" ) & \"]\" 135 | )" 136 | ); 137 | ~apply = "Substitute (" & Quote ( $~html ) & ";" & Substitute ( ~swap ; "¶" ; ";¶" ) & ")" 138 | ]; 139 | Let ( [ 140 | ~result = Evaluate ( ~apply ) 141 | ]; 142 | If ( ~result = "?"; // the CustomList was not evaluated properly 143 | "CustomList function is needed for this area"; 144 | ~result // super helpful list of objects with position values 145 | ) 146 | ) 147 | ); 148 | "
"; 149 | // Layout fields 150 | "
LAYOUT FIELDS
"; 151 | "
"; 152 | "

" & Substitute ( 153 | FieldNames ( Get ( FileName ) ; Get ( LayoutName ) ); 154 | [ ¶ ; "

" ] 155 | ) & "

"; 156 | "
"; 157 | // File path 158 | "
FILE PATH
"; 159 | "
"; 160 | Get ( FilePath ); 161 | "
"; 162 | ""; 163 | "" 164 | ) 165 | ) 166 | -------------------------------------------------------------------------------- /Functions/ObjectID.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================================================== 3 | * ObjectID ( object; type; file; layout ) 4 | * 5 | * PARAMETERS: 6 | * @object supply either the name or internal id of an object 7 | * (see type variable for supported objects) 8 | * @type (enum) One of the following 9 | * "Table", "Layout", "Field", "Script", or "ValueList" or (T,L,F,S,V) for shorthand 10 | * @file [optional] specify the name of the file (if using multiple files) - empty implies current file 11 | * @layout [optional] required only if @type contains "Field". 12 | * Empty implies current layout (or the field table occurrence, read the note below). 13 | * The layout should be tied to the table occurrence of the field 14 | * RETURNS: 15 | * (mixed) Internal FileMaker ID if name of object is supplied 16 | * and inverse if ID of object is supplied 17 | * DEPENDENCIES: 18 | * None 19 | * AUTHOR: 20 | * Fabrice Nordman 21 | * NOTES: 22 | * for fields (_TLFSV = "F"), if you use the full field name 23 | * (table::fieldname) AND an empty var.layout parameter, the 24 | * function will assume you are referring not to current layout but 25 | * to the table occurrence found in the _TLFSV 26 | * 27 | * ===================================================================== 28 | */ 29 | 30 | 31 | Let ( [ 32 | ~file = If ( IsEmpty ( file ); 33 | Get ( FileName ); // use current file name... otherwise 34 | ~file 35 | ); 36 | layout = Case ( 37 | IsEmpty ( layout ); 38 | Case ( 39 | PatternCount ( object; "::" ); 40 | GetValue ( Substitute ( object; "::"; ¶ ); 1 ); 41 | Get ( LayoutName ) // default 42 | ); 43 | layout // default 44 | ); 45 | layout = Case ( 46 | Int ( layout ) = layout and Length ( layout ) = 7; 47 | ObjectID ( layout; "T"; ~file; "" ); 48 | layout 49 | ); 50 | var.object = object; 51 | 52 | // Allow type to be a single character (T,L,F,S,V) 53 | var.type = Left ( type; 1 ); 54 | var.type = Choose( Position ( "TLFSV"; var.type; 1; 1 ) - 1; "Table"; "Layout"; "Field"; "Script"; "ValueList" ); 55 | var.object = Case ( // remove the repetition number 56 | var.type = "Field" and PatternCount ( var.object; "[" ); 57 | Left ( var.object; Position ( var.object; "["; 10000; -1 ) -1 ); 58 | var.object 59 | ); 60 | var.object = Case ( // for fields, do not take TO 61 | var.type = "Field" and PatternCount ( var.object; "::" ); 62 | Replace ( var.object; 1; Position ( var.object; "::"; 1; 1 ) + 1; "" ); 63 | var.object 64 | ); 65 | var.endOfString = "( \"" & file & "\"" & Case ( var.type = "field"; "; \"" & layout & "\"" ) & ")"; 66 | var.names = Evaluate ( var.type & "Names" & var.endOfString ); 67 | var.ids = Evaluate ( var.type & "IDs" & var.endOfString ) 68 | 69 | ]; 70 | 71 | Case ( 72 | var.object = GetAsNumber ( var.object ); 73 | 74 | // Convert ID -> Name 75 | Case ( 76 | not IsEmpty ( FilterValues ( var.object; var.ids )); 77 | GetValue ( var.names; Let ([ 78 | _text = var.ids; 79 | _item = var.object; 80 | _adj = ¶ & _text & ¶ 81 | ]; 82 | PatternCount ( Left ( _adj; Position ( _adj; ¶ & _item & ¶; 1; 1 ) + 1 ); ¶ ) 83 | ) 84 | ) 85 | ); 86 | // Convert Name -> ID 87 | Case ( 88 | not IsEmpty ( FilterValues ( var.object; var.names )); 89 | GetValue ( var.ids; Let ([ 90 | _text = var.names; 91 | _item = var.object; 92 | _adj = ¶ & _text & ¶ 93 | ]; 94 | PatternCount ( Left ( _adj; Position ( _adj; ¶ & _item & ¶; 1; 1 ) + 1 ); ¶ ) 95 | ) 96 | ) 97 | ) 98 | ) 99 | ) 100 | 101 | 102 | /* 103 | 104 | Original function by Fabrice Nordmann 105 | Reformatted by Matt Petrowsky 106 | 107 | Avoids hard-coding in FileMaker by using IDs instead of names 108 | 109 | v.1.6, Mar 2009 110 | Accepts field parameters such as FMvar.name_id ( 1065234::24; "F"; ""; "" ) 111 | v.1.5.2, Feb 2009 112 | Removes the repetition number if a fieldname with a repetition number is passed ( field[n]). Does not handle field names with [ anymore. 113 | v.1.5.1, Dec 2008 114 | Documentation update (thanks to Kevin Frank's comment) 115 | v.1.5, Oct 2008 116 | Documentation error fix 117 | Returns an empty result if item not found 118 | v.1.4, Aug 2008 119 | Bug fix (major) : could return erroneous result if an item ID was found in another ID. 120 | v.1.3, July 2008 121 | A field ID can be obtained by passing its full name (with table occurrence) in var.name_id and leaving var.layout empty 122 | e.g. FMvar.name_id ( "myTable::myField"; "F"; ""; "" ) 123 | v.1.2, June 2008 124 | Doesn't require Position value 125 | v.1, May 2008 126 | Requires: PositionValue ( _text; _searchValue; _start; _occurrence ) 127 | 128 | // Test code for raw data 129 | Let ( [ 130 | f = Get ( FileName ); 131 | l = Get ( LayoutName ) 132 | ]; 133 | List ( 134 | "--- TABLES ---"; 135 | TableNames ( f ); 136 | TableIDs ( f ); 137 | "--- LAYOUTS ---"; 138 | LayoutNames ( f ); 139 | LayoutIDs ( f ); 140 | "--- FIELDS ---"; 141 | FieldNames ( f; l ); 142 | FieldIDs ( f; l ); 143 | "--- SCRIPTS ---"; 144 | ScriptNames ( f ); 145 | ScriptIDs ( f ); 146 | "--- VALUE LISTS ---"; 147 | ValueListNames ( f ); 148 | ValueListIDs ( f ) 149 | ) 150 | ) 151 | 152 | */ 153 | -------------------------------------------------------------------------------- /Functions/UUID/UUIDTimeDevice.fmfn: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * UUIDTimeDevice 3 | * https://github.com/jbante/FileMaker-Techniques/blob/master/CustomFunctions/UUID/UUIDTimeDevice.fmfn 4 | * 5 | * Creates a universally unique identifier suitable for use as a primary key in 6 | * FileMaker number fields. The UUID is an encoding of the creation timestamp 7 | * (UTC), a serial number, and an identifier of the device that created the 8 | * UUID. The device ID is based on FileMaker's Get ( PersistentID ) function, 9 | * which is in turn based on Apple's IDVA (identifier for vendor). The values 10 | * returned by this and related functions have a one-to-one correspondence with 11 | * UUIDs following the RFC 4122 standard. All values in the format generated by 12 | * this function can be converted to RFC 4122, and vice versa. 13 | * 14 | * This function generates UUIDs that are meaningfully sortable. Values sort by 15 | * version, then approximate creation order. Because of the leading sort by 16 | * version, random (version 4) values (where the creation order cannot be 17 | * determined by the values) will cluster separately from timestamp-node 18 | * (version 1) values. 19 | * 20 | * The value can be converted to RFC 4122 canonical form (hexadecimal) with the 21 | * UUIDGetAsRFC4122 function. The creation timestamp can be extracted with the 22 | * UUIDGetTimestamp function. 23 | * 24 | * @return A 41-digit number of the form: 25 | * vrttttttttttttssssssscccccnnnnnnnnnnnnnnn 26 | * The sections of the UUID correspond to: 27 | * v: A UUID version (type) number 28 | * r: A variant code reserved by the RFC 4122 standard 29 | * t: The creation timestamp (seconds since 0001-01-01T00:00:00) 30 | * s: A serial number, reset for each second 31 | * c: A session key, randomly generated for each session 32 | * n: The device ID ("node") of the device that created the UUID 33 | * 34 | * @global $$~UUID.DATA 35 | * 36 | * @history 2013-09-23 - Jeremy Bante - Created 37 | * @history 2013-12-13 - Jeremy Bante - Using new 38 | * Get ( CurrentTimeUTCMilliseconds ) function in FileMaker 13. 39 | * 40 | * @see http://filemakerstandards.org/pages/viewpage.action?pageId=557138 Key values best practice 41 | * @see http://tools.ietf.org/html/rfc4122 RFC 4122 standard UUID specification 42 | * @see http://help.filemaker.com/app/answers/detail/a_id/12074 FileMaker device ID behavior documentation 43 | ******************************************************************************/ 44 | 45 | Case ( 46 | /* First call to function, check for pre-processed node data */ 47 | not $~uuid.step ; 48 | Let ( [ 49 | $~uuid.node = GetValue ( $$~UUID.DATA ; 4 ) ; 50 | $~uuid.step = 51 | If ( IsEmpty ( $~uuid.node ) ; 1 ; /* Else */ 3 ) ; 52 | $~uuid.id_Device = 53 | If ( $~uuid.step = 1 ; Left ( Get ( PersistentID ) ; 12 ) ) 54 | ] ; 55 | UUIDTimeDevice 56 | ) ; 57 | 58 | /* Parse device ID from hexadecimal to a (base 10) number */ 59 | $~uuid.step = 1 ; 60 | Let ( [ 61 | ~deviceDigit = Middle ( $~uuid.id_Device ; 12 - $~uuid.i ; 1 ) ; 62 | ~deviceDigit = // convert digit to number 63 | Position ( "0123456789abcdef" ; ~deviceDigit ; 1 ; 1 ) - 1 ; 64 | $~uuid.node = // add digit to node 65 | ~deviceDigit * ( 16 ^ $~uuid.i ) + $~uuid.node ; 66 | $~uuid.step = 67 | If ( $~uuid.i < 11 ; 68 | $~uuid.step ; 69 | /* Else */ $~uuid.step + 1 70 | ) ; 71 | $~uuid.i = $~uuid.i + 1 72 | ] ; 73 | UUIDTimeDevice 74 | ) ; 75 | 76 | /* Set multicast bit to avoid colliding with NIC addresses */ 77 | $~uuid.step = 2 ; 78 | Let ( [ 79 | ~left = Div ( $~uuid.node ; 2199023255552 ) ; // 2^41 80 | ~right = Mod ( $~uuid.node ; 1099511627776 ) ; // 2^40 81 | $~uuid.node = ( ~left * 2 + 1 ) * 1099511627776 + ~right ; 82 | $~uuid.step = $~uuid.step + 1 83 | ] ; 84 | UUIDTimeDevice 85 | ) ; 86 | 87 | /* Concatenate information */ 88 | $~uuid.step = 3 ; 89 | Let ( [ 90 | // Set-up timestamp, serial number, and session key 91 | ~session = GetValue ( $$~UUID.DATA ; 1 ) ; 92 | ~lastTimestamp = GetValue ( $$~UUID.DATA ; 2 ) ; 93 | ~serial = GetAsNumber ( GetValue ( $$~UUID.DATA ; 3 ) ) ; 94 | 95 | ~now = 96 | Mod ( Get ( CurrentTimeUTCMilliseconds ) ; 165208454460000 ) ; 97 | ~session = 98 | Case ( 99 | IsEmpty ( ~session ) ; // new session 100 | Floor ( Random * 16384 ) ; 101 | 102 | ~serial ≥ .9999 // serial number overflow 103 | and ~now = ~lastTimestamp 104 | or ~now < ~lastTimestamp ; // clock change 105 | Mod ( ~session + 1 ; 16384 ) ; 106 | 107 | /* Else */ 108 | ~session 109 | ) ; 110 | ~magnitude = Max ( Floor ( ~serial * 10 ) ; 1 ) ; 111 | ~serial = 112 | Case ( 113 | ~now ≠ ~lastTimestamp or ~serial = .9999 ; 0 ; 114 | ~serial = 0 ; .1 ; 115 | ~serial < .4 ; ~serial + 10 ^ ( 0 - ~magnitude ) ; 116 | /* Else */ ~serial + 10 ^ -4 117 | ) ; 118 | ~node = $~uuid.node ; 119 | 120 | // Store data for future reference 121 | $$~UUID.DATA = List ( ~session ; ~now ; ~serial ; ~node ) ; 122 | 123 | // purge variables 124 | $~uuid.i = "" ; 125 | $~uuid.id_Device = "" ; 126 | $~uuid.node = "" ; 127 | $~uuid.step = "" 128 | ] ; 129 | // v-r-tttt tttt tttt ttt-ssss-ccccc-nnnnn nnnnn nnnnn 130 | "12" // version and reserved variant code 131 | // "1-2-" 132 | & Right ( "000000000000000" & ~now ; 15 ) 133 | // & "-" 134 | & Left ( Substitute ( ~serial ; "." ; "" ) & "0000" ; 4 ) 135 | // & "-" 136 | & Right ( "00000" & ~session ; 5 ) 137 | // & "-" 138 | & Right ( "000000000000000" & ~node ; 15 ) 139 | ) 140 | ) --------------------------------------------------------------------------------