├── .gitignore ├── portal.filter.fmfn ├── os.isWidnows.fmfn ├── env.databaseopen.fmfn ├── php.date.fmp.fmfn ├── my.name.fmfn ├── script.result.fmfn ├── my.contents.fmfn ├── window.frontmost.fmfn ├── file.hosted.fmfn ├── list.valuewithin.fmfn ├── php.fmpized.fmfn ├── html.paragraphs.fmfn ├── vars.makeLocal.fmfn ├── html.css.fmfn ├── user.isEditing.fmfn ├── layout.view.fmfn ├── requests.reset.fmfn ├── var.toggle.fmfn ├── webviewer.data.fmfn ├── string.explode.fmfn ├── string.implode.fmfn ├── list.position.fmfn ├── var.local.fmfn ├── html.attribute.fmfn ├── exists.script.fmfn ├── date.toUnix.fmfn ├── boolean.toggle.fmfn ├── list.extract.keyedvalue.fmfn ├── var.get.fmfn ├── layout.objects.fmfn ├── requests.get.fmfn ├── tab.save.fmfn ├── html.wraptag.fmfn ├── url.isvalid.fmfn ├── file.dirname.fmfn ├── html.document.fmfn ├── error.last.fmfn ├── script.param.named.fmfn ├── calc.eval.fmfn ├── os.versionName.fmfn ├── script.parameters.fmfn ├── file.size.fmfn ├── path.dirname.fmfn ├── script.param.set.fmfn ├── date.fromUnix.fmfn ├── list.trim.fmfn ├── char.arrows.fmfn ├── var.merge.fmfn ├── script.param.let.fmfn ├── selection.delete.fmfn ├── env.get.fmfn ├── xml.tag.fmfn ├── fmstandards ├── Developer.fmfn ├── GetFieldNameVariable.fmfn ├── WindowCenter.fmfn ├── Debug.fmfn └── ErrorReference.fmfn ├── color.hex2dec.fmfn ├── window.offscreen.fmfn ├── developer.fmfn ├── file.ospath.fmfn ├── layout.variables.fmfn ├── requests.save.fmfn ├── error.calculation.fmfn ├── var.set.fmfn ├── window.activetab.fmfn ├── window.center.fmfn ├── mouse.clicks.fmfn ├── var.define.fmfn ├── portal.grouping.fmfn ├── vars.save.fmfn ├── script.param.change.fmfn ├── list.swapValue.fmfn ├── string.trim.fmfn ├── window.fronttabs.fmfn ├── vars.objects.fmfn ├── plugin.valid.fmfn ├── fields.changed.fmfn ├── list.sort.defined.fmfn ├── webviewer.records.txt ├── keyboard.modifiers.fmfn ├── window.properties.fmfn ├── number.format.fmfn ├── keyboard.chars.fmfn ├── debug.fmfn ├── var.list.fmfn ├── script.param.get.fmfn ├── html.checkbox.fmfn ├── file.setpath.fmfn ├── list.addRemove.fmfn ├── var.eval.fmfn ├── script.execute.fmfn ├── list.sort.fmfn ├── list.filter.fmfn ├── script.parameters.assign.fmfn ├── date.ranges.fmfn ├── object.id.fmfn ├── keycode.istype.fmfn ├── list.custom.fmfn ├── error.string.fmfn └── window.settings.fmfn /.gitignore: -------------------------------------------------------------------------------- 1 | .svn 2 | .DS_Store 3 | -------------------------------------------------------------------------------- /portal.filter.fmfn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/petrowsky/fmpfunctions/HEAD/portal.filter.fmfn -------------------------------------------------------------------------------- /os.isWidnows.fmfn: -------------------------------------------------------------------------------- 1 | /* 2 | os.isWindows( ) 3 | RETURNS: (bool) 4 | */ 5 | 6 | Case( Get( SystemPlatform ) = -2; 1; 0 ) -------------------------------------------------------------------------------- /env.databaseopen.fmfn: -------------------------------------------------------------------------------- 1 | /* 2 | List.valueWithin( listdata; value ) 3 | RETURNS: (bool) If supplied value is within the supplied listdata 4 | DEPS: list.valueWithin 5 | */ 6 | 7 | List.valueWithin ( DatabaseNames ; database ) -------------------------------------------------------------------------------- /php.date.fmp.fmfn: -------------------------------------------------------------------------------- 1 | /* 2 | php.date.fmp( string ) 3 | RETURNS: (date) FMP compatible date format 4 | 5 | Can be used to store a php script within a variable or calculation 6 | */ 7 | 8 | PHP_Execute ("print strtotime('" & string & "');" ) -------------------------------------------------------------------------------- /my.name.fmfn: -------------------------------------------------------------------------------- 1 | /* 2 | ===================================================== 3 | my.name() 4 | 5 | RETURNS: (string) current layout object name 6 | DEPS: none 7 | NOTES: 8 | ===================================================== 9 | */ 10 | 11 | 12 | Get ( ActiveLayoutObjectName ) -------------------------------------------------------------------------------- /script.result.fmfn: -------------------------------------------------------------------------------- 1 | /* 2 | ===================================================== 3 | script.result( ) 4 | 5 | RETURNS: (mixed) FileMaker's internal ScriptResult function 6 | DEPS: none 7 | NOTES: 8 | ===================================================== 9 | */ 10 | 11 | Get ( ScriptResult ) -------------------------------------------------------------------------------- /my.contents.fmfn: -------------------------------------------------------------------------------- 1 | /* 2 | ===================================================== 3 | my.contents( name ) 4 | 5 | RETURNS: (string) current layout object contents 6 | DEPS: none 7 | NOTES: 8 | ===================================================== 9 | */ 10 | 11 | GetLayoutObjectAttribute ( name ; "content" ) -------------------------------------------------------------------------------- /window.frontmost.fmfn: -------------------------------------------------------------------------------- 1 | /* 2 | ===================================================== 3 | window.frontmost( name ) 4 | 5 | RETURNS: (bool) If the frontmost window is the one specified. 6 | DEPS: 7 | NOTES: 8 | ===================================================== 9 | */ 10 | 11 | GetValue( WindowNames; 1 ) = name -------------------------------------------------------------------------------- /file.hosted.fmfn: -------------------------------------------------------------------------------- 1 | /* 2 | ===================================================== 3 | file.hosted() 4 | 5 | RETURNS: (bool) Whether the file is hosted or local. 6 | DEPS: None 7 | NOTES: 8 | ===================================================== 9 | */ 10 | 11 | If( Left( Get( FilePath ); 6) = "fmnet:"; True; False) -------------------------------------------------------------------------------- /list.valuewithin.fmfn: -------------------------------------------------------------------------------- 1 | /* 2 | * list.valueWithin( listdata; value ) 3 | * RETURNS: (bool) If supplied value is within the supplied listdata 4 | */ 5 | 6 | PatternCount ( ¶ & listdata & ¶ ; ¶ & value & ¶ ) 7 | 8 | /* 9 | Thanks to Koen for this suggested alternative 10 | not isempty(FilterValues(listdata ; value)) 11 | */ -------------------------------------------------------------------------------- /php.fmpized.fmfn: -------------------------------------------------------------------------------- 1 | /* 2 | php.fmpized( contents ) 3 | RETURNS: (string) FMP compatible string of a php script 4 | 5 | Can be used to store a php script within a variable or calculation 6 | */ 7 | 8 | "\"" & 9 | 10 | Substitute ( 11 | script; 12 | ["\\"; "\\\\"]; 13 | ["\""; "\\\""]; 14 | ["\¶"; "\\\¶"]; 15 | [ ¶; "\¶"] ) & 16 | 17 | "\"" -------------------------------------------------------------------------------- /html.paragraphs.fmfn: -------------------------------------------------------------------------------- 1 | /* 2 | * ===================================================== 3 | * html.paragraphs ( content ) 4 | * 5 | * RETURNS: (string) Conversion of ¶'s into

6 | * DEPS: none 7 | * NOTES: 8 | * ===================================================== 9 | */ 10 | 11 | "

" & Substitute ( content ; "¶¶" ; "

" ) & "

" -------------------------------------------------------------------------------- /vars.makeLocal.fmfn: -------------------------------------------------------------------------------- 1 | /* 2 | * ===================================================== 3 | * vars.makeLocal ( input ) 4 | * 5 | * RETURNS: (string) All global vars $$ converted to $ locals 6 | * DEPENDENCIES: none 7 | * NOTES: 8 | * ===================================================== 9 | * 10 | */ 11 | 12 | Substitute ( input ; "$$" ; "$" ) -------------------------------------------------------------------------------- /html.css.fmfn: -------------------------------------------------------------------------------- 1 | /* 2 | * ===================================================== 3 | * html.css ( content ) 4 | * 5 | * RETURNS: (string) Raw css wrapped within style tags 6 | * DEPS: none 7 | * NOTES: 8 | * ===================================================== 9 | */ 10 | 11 | "¶¶" -------------------------------------------------------------------------------- /user.isEditing.fmfn: -------------------------------------------------------------------------------- 1 | /* 2 | * ===================================================== 3 | * user.isEditing() 4 | * 5 | * RETURNS: (boolean) based on database editing state 6 | * DEPENDENCIES: none 7 | * NOTES: none 8 | * ===================================================== 9 | * 10 | */ 11 | 12 | Get ( ActiveFieldName ) ≠ null 13 | or 14 | Get ( RecordOpenCount ) > 0 -------------------------------------------------------------------------------- /layout.view.fmfn: -------------------------------------------------------------------------------- 1 | /* 2 | * ===================================================== 3 | * layout.view ( ) 4 | * 5 | * RETURNS: (string) Human readable version of Get(LayoutViewState) 6 | * DEPENDENCIES: none 7 | * NOTES: Simple helper function 8 | * ===================================================== 9 | * 10 | */ 11 | 12 | Choose ( Get(LayoutViewState) ; "Form" ; "List" ; "Table" ) -------------------------------------------------------------------------------- /requests.reset.fmfn: -------------------------------------------------------------------------------- 1 | /* 2 | * ===================================================== 3 | * requests.reset ( layout ) 4 | * 5 | * RETURNS: (bool) Success or failure of reset 6 | * DEPENDENCIES: none 7 | * NOTES: 8 | * ===================================================== 9 | * 10 | */ 11 | 12 | If ( Evaluate ( "Let ( $$" & layout & ".requests = \"\"; False )" ) = "?"; False; True ) -------------------------------------------------------------------------------- /var.toggle.fmfn: -------------------------------------------------------------------------------- 1 | /* 2 | * ===================================================== 3 | * var.toggle( value ) 4 | * 5 | * RETURNS: (bool) True or False based on contents of value 6 | * DEPENDENCIES: none 7 | * NOTES: none 8 | * ===================================================== 9 | * 10 | */ 11 | 12 | not GetAsBoolean ( value ) 13 | 14 | // Special thanks to Rob Poelking for the suggestion -------------------------------------------------------------------------------- /webviewer.data.fmfn: -------------------------------------------------------------------------------- 1 | /* 2 | * ===================================================== 3 | * webviewer.data( type; head; css; body ) 4 | * 5 | * RETURNS: (string) Webviewer data url for rendering local html content 6 | * DEPS: html.document 7 | * NOTES: 8 | * ===================================================== 9 | */ 10 | 11 | "data:text/html," & 12 | 13 | html.document( type ; head ; css ; body ) -------------------------------------------------------------------------------- /string.explode.fmfn: -------------------------------------------------------------------------------- 1 | /* 2 | ===================================================== 3 | string.explode ( string ; delimiter ) 4 | 5 | RETURNS: (list) of text values. 6 | DEPENDENCIES: none 7 | NOTES: 8 | ===================================================== 9 | 10 | A simple text parsing function designed to break a delimited 11 | string on the supplied delimiter. 12 | 13 | */ 14 | 15 | Substitute ( string ; delimiter ; ¶ ) -------------------------------------------------------------------------------- /string.implode.fmfn: -------------------------------------------------------------------------------- 1 | /* 2 | ===================================================== 3 | string.implode ( values ; delimiter ) 4 | 5 | RETURNS: (string) delimited by the delimiter. 6 | DEPENDENCIES: none 7 | NOTES: 8 | ===================================================== 9 | 10 | A simple text function designed to delimit 11 | multiple values by the supplied delimiter. 12 | 13 | */ 14 | 15 | Substitute ( values ; ¶ ; delimiter ) -------------------------------------------------------------------------------- /list.position.fmfn: -------------------------------------------------------------------------------- 1 | /* 2 | * ===================================================== 3 | * list.position ( values; search ) 4 | * 5 | * RETURNS: (number) numerical position of the supplied value 6 | * DEPS: none 7 | * NOTES: 8 | * ===================================================== 9 | */ 10 | 11 | Position ( Substitute ( Filter ( Substitute ( ¶& values &¶ ; ¶& search &¶ ; "¶•¶" ) ; "•¶" ) ; ¶ ; "|" ) ; "•" ; 1 ; 1 ) - 1 -------------------------------------------------------------------------------- /var.local.fmfn: -------------------------------------------------------------------------------- 1 | /* 2 | var.local( name ; value ) 3 | RETURNS: (bool) Whether the variable was created or not 4 | 5 | This custom function is a wrapper for the assignment(creation) of local variables. 6 | If the variable cannot be created or is empty then false is returned. Otherwise 7 | it was created. 8 | */ 9 | 10 | If ( 11 | Evaluate( "Let ( $" & name & " = " & Quote ( value ) & "; \"\" )" ) = "?" 12 | ; 0 13 | ; not IsEmpty( Evaluate( "$" & name )) 14 | ) -------------------------------------------------------------------------------- /html.attribute.fmfn: -------------------------------------------------------------------------------- 1 | /* 2 | ===================================================== 3 | html.attribute ( name ; value ; tag ) 4 | 5 | RETURNS: (string) Applies an attribute to an html tag 6 | DEPS: none 7 | NOTES: 8 | ===================================================== 9 | */ 10 | 11 | Let( [ 12 | var.start = Position( tag; ">"; 1; 1 ); 13 | var.attribute = " " & name & "=" & quote( value ) 14 | ]; 15 | 16 | Replace( tag; var.start; 0; var.attribute ) 17 | ) -------------------------------------------------------------------------------- /exists.script.fmfn: -------------------------------------------------------------------------------- 1 | /* 2 | ===================================================================== 3 | exists.script ( name ; file ) 4 | 5 | RETURNS: (bool) Whether or not the script exists in the file 6 | 7 | DEPENDENCIES: list.valueWithin() 8 | AUTHOR: Matt Petrowsky 9 | VERSION: 1.0 10 | NOTES: None 11 | 12 | ===================================================================== 13 | */ 14 | 15 | 16 | list.valueWithin ( ScriptNames ( file ) ; name ) -------------------------------------------------------------------------------- /date.toUnix.fmfn: -------------------------------------------------------------------------------- 1 | // UnixTime( current_timestamp ; time_zone ) 2 | 3 | /* 4 | Returns the local time as a unix time stamp 5 | Specify the local time zone only if you want the time in GMT 6 | */ 7 | 8 | Let( [ 9 | $time = If( current_timestamp = "" or current_timestamp = "now"; Get( CurrentTimeStamp ) ; GetAsTimestamp ( current_timestamp )); 10 | $unix = GetAsNumber( $time ) - GetAsNumber( GetAsTimestamp(Date(1;1;1970) ) ); 11 | $offset = time_zone * 3600 12 | ]; 13 | 14 | $unix - $offset 15 | 16 | ) -------------------------------------------------------------------------------- /boolean.toggle.fmfn: -------------------------------------------------------------------------------- 1 | /* 2 | * ===================================================================== 3 | * boolean.toggle ( number ) 4 | * 5 | * RETURNS: (bool) True or False based on contents of value 6 | * 7 | * DEPENDENCIES: None 8 | * AUTHOR: Matt Petrowsky 9 | * NOTES: None 10 | * 11 | * ===================================================================== 12 | */ 13 | 14 | not GetAsBoolean ( value ) 15 | 16 | // Special thanks to Rob Poelking for the suggestion -------------------------------------------------------------------------------- /list.extract.keyedvalue.fmfn: -------------------------------------------------------------------------------- 1 | /* 2 | ExtractLine ( data ; match ) 3 | Returns: (string) From specified match value until carriage return 4 | Note: Will match the first found occurrence 5 | */ 6 | 7 | Let( 8 | [ 9 | _Start = Position ( data ; match ; 1 ; 1 ) + Length( match ) ; 10 | _End = Position ( data ; ¶ ; _Start ; 1 ) ; 11 | _Length = _End - _Start 12 | ]; 13 | 14 | Middle ( data ; _Start ; _Length ) 15 | 16 | ) 17 | 18 | /* 19 | Example: 20 | list.extract.keyedvalue ( data ; "¶Subject: ") // Will extract the subject line 21 | */ -------------------------------------------------------------------------------- /var.get.fmfn: -------------------------------------------------------------------------------- 1 | /* 2 | * ===================================================== 3 | * var.get( name ) 4 | * 5 | * RETURNS: (mixed) Value of the variable specified by name 6 | * DEPENDENCIES: none 7 | * NOTES: Works only with $$global vars 8 | * ===================================================== 9 | * 10 | */ 11 | 12 | Let ( [ 13 | var.dollarsign = If ( Left ( name ; 2 ) = "$$" ; "" ; "$$" ) // check for prefixed $$ 14 | ]; 15 | Let ( var.result = Evaluate( var.dollarsign & name ) ; If ( var.result = "?" ; "" ; var.result ) 16 | ) -------------------------------------------------------------------------------- /layout.objects.fmfn: -------------------------------------------------------------------------------- 1 | /* 2 | * ===================================================== 3 | * layout.objects () 4 | * 5 | * RETURNS: (string) 6 | * Return delimited list of layout objects 7 | * from the current layout. 8 | * DEPENDENCIES: none 9 | * NOTES: none 10 | * ===================================================== 11 | */ 12 | 13 | Substitute ( 14 | LayoutObjectNames ( Get( FileName ) ; Get( LayoutName ) ) ; 15 | [ ">¶" ; "" ] ; 16 | [ "<¶" ; "" ] ; 17 | [ "¶>" ; "" ] ; 18 | [ "¶<" ; "" ] 19 | ) -------------------------------------------------------------------------------- /requests.get.fmfn: -------------------------------------------------------------------------------- 1 | /* 2 | * ===================================================== 3 | * requests.get ( layout ) 4 | * 5 | * RETURNS: (string) Return delimited list of request strings 6 | * DEPENDENCIES: none 7 | * NOTES: 8 | * ===================================================== 9 | * 10 | * Intended use is for extracting a return delimited list of 11 | * fieldname/value pairs from a global variable with the 12 | * same name as the layout. 13 | * 14 | */ 15 | 16 | If ( Evaluate ( "$$" & layout & ".requests" ) = "?"; False; Evaluate ( "$$" & layout & ".requests" ) ) -------------------------------------------------------------------------------- /tab.save.fmfn: -------------------------------------------------------------------------------- 1 | /* 2 | * ===================================================== 3 | * tab.save ( level ) 4 | * RETURNS: (bool) True or False based if tab was saved to global layout variable 5 | * DEPENDENCIES: window.activetab & list.swapValue 6 | * NOTES: none 7 | * ===================================================== 8 | * 9 | */ 10 | 11 | Let ( [ 12 | var.tabname = GetValue ( window.activetab ( 0 ; "" ) ; level ); 13 | var.variable = Get ( LayoutName ) & ".tabs"; 14 | ]; 15 | var.define ( var.variable ; list.swapValue ( Evaluate ( "$$" & var.variable ) ; var.tabname ; level ) ) 16 | ) -------------------------------------------------------------------------------- /html.wraptag.fmfn: -------------------------------------------------------------------------------- 1 | /* 2 | * ===================================================== 3 | * html.wraptag ( values; tag ) 4 | * 5 | * RETURNS: (string) HTML tags wrapped around a supplied list of items 6 | * DEPS: none 7 | * NOTES: 8 | * ===================================================== 9 | */ 10 | 11 | Let ([ 12 | 13 | var.isList = ValueCount( values ) > 1; 14 | var.opentag = "<" & tag & ">"; 15 | var.closetag = Replace( var.opentag; 1; 1; ""; 14 | type = "xhtmlt" ; ""; 15 | "" 16 | ); 17 | ""; 18 | ""; 19 | head; 20 | css; 21 | ""; 22 | ""; 23 | body; 24 | ""; 25 | "" 26 | ) -------------------------------------------------------------------------------- /error.last.fmfn: -------------------------------------------------------------------------------- 1 | /* 2 | * ===================================================== 3 | * error.last( ) 4 | * 5 | * RETURNS: (string) Parameterized result of key/values about result 6 | * DEPENDENCIES: error.string() 7 | * NOTES: Use var.eval() to turn the returned list into local variables 8 | * ===================================================== 9 | * 10 | */ 11 | 12 | Let ( [ 13 | var.error = Get ( LastError ) 14 | ]; 15 | If ( var.error ≠ 0; 16 | List ( 17 | "$error=" & True; 18 | "$errornum=" & var.error; 19 | "$errorstring=" & quote ( error.string ( var.error ) ); 20 | "$script=" & Quote ( Get ( ScriptName ) ); 21 | ); 22 | "$error=" & False // Default 23 | ) 24 | ) -------------------------------------------------------------------------------- /script.param.named.fmfn: -------------------------------------------------------------------------------- 1 | /* 2 | ===================================================== 3 | script.param.named( keys; values) 4 | 5 | RETURNS: (mixed) the value associated to the supplied key in positional order 6 | DEPS: script.param.set 7 | ===================================================== 8 | */ 9 | Let ( 10 | [ 11 | _Compliments = ValueCount ( keys ) = ValueCount ( values ); 12 | _NotEmpty = not IsEmpty( keys ) and not IsEmpty( values ) 13 | ]; 14 | 15 | If ( _Compliments and _NotEmpty ; 16 | 17 | script.param.set ( GetValue ( keys ; 1 ) ; GetValue ( values ; 1 ) ) & script.param.named ( MiddleValues ( keys ; 2 ; 1000000 ) ; MiddleValues ( values ; 2 ; 1000000 ) ); 18 | _ 19 | 20 | ) 21 | 22 | ) -------------------------------------------------------------------------------- /calc.eval.fmfn: -------------------------------------------------------------------------------- 1 | /* 2 | * ===================================================== 3 | * calc.eval( calc ) 4 | * RETURNS: (bool) True or False based on proper evaluation 5 | * DEPENDENCIES: none 6 | * NOTES: see var.eval for declaring multiple variables 7 | * ===================================================== 8 | * 9 | * This custom function is a wrapper for the 10 | * assignment(creation) of global variables. 11 | */ 12 | 13 | Let ( 14 | $$ERROR.CALC.EVAL = ""; // Reset the global error variable for evaluation 15 | If ( 16 | Evaluate( 17 | calc 18 | ) = "?"; 19 | Let ( $$ERROR.CALC.EVAL = "EVAL ERROR¶-----¶" & calc; False ); 20 | True 21 | ) 22 | ) -------------------------------------------------------------------------------- /os.versionName.fmfn: -------------------------------------------------------------------------------- 1 | /* 2 | * ===================================================== 3 | * os.versionName ( ) 4 | * 5 | * RETURNS: (string) Version name of OS being used 6 | * DEPENDENCIES: none 7 | * NOTES: none 8 | * RELEASE: 100907 9 | * ===================================================== 10 | * 11 | */ 12 | 13 | Let ([ 14 | $10.0 = "Cheetah"; 15 | $10.1 = "Puma"; 16 | $10.2 = "Jaguar"; 17 | $10.3 = "Panther"; 18 | $10.4 = "Tiger"; 19 | $10.5 = "Leopard"; 20 | $10.6 = "Snow Leopard"; 21 | $6.1 = "Windows 7"; 22 | $6.0 = "Windows Vista"; 23 | $5.2 = "Windows XP 64-Bit"; 24 | $5.1 = "Windows XP"; 25 | $5.0 = "Windows 2000" 26 | ]; 27 | Evaluate ( "$" & Truncate ( Get ( SystemVersion ) ; 1 ) ) 28 | ) -------------------------------------------------------------------------------- /script.parameters.fmfn: -------------------------------------------------------------------------------- 1 | /* 2 | ===================================================== 3 | script.parameters( style ) 4 | 5 | RETURNS: (mixed) structured format of script parameters. 6 | DEPENDENCIES: string.explode, var.eval, script.parameters.assign 7 | NOTES: 8 | ===================================================== 9 | 10 | A wrapper function for supporting multiple styles of passing 11 | script parameters into a script. 12 | */ 13 | 14 | Case( 15 | style = "single"; Get ( ScriptParameter ); 16 | style = "piped"; string.explode ( Get ( ScriptParameter ) ; "|" ); 17 | style = "let"; var.eval ( Get ( ScriptParameter ) ); 18 | style = "assign"; script.parameters.assign(); 19 | style = "xml"; "Not implemented yet"; 20 | 21 | False 22 | ) -------------------------------------------------------------------------------- /file.size.fmfn: -------------------------------------------------------------------------------- 1 | // Calculate the human readable size from bytes 2 | 3 | /* 4 | ===================================================== 5 | file.size ( bytes ) 6 | 7 | RETURNS: (string) Human readable file size in bytes 8 | DEPS: none 9 | NOTES: Using the Length() function on a container will return size in bytes 10 | ===================================================== 11 | */ 12 | 13 | Let( [ 14 | 15 | b = bytes; 16 | kb = b / 1024; 17 | mb = b / 1024^2; 18 | gb = b / 1024^3; 19 | tb = b / 1024^4 20 | 21 | ]; 22 | 23 | Case( 24 | Div( 1024; kb ) > 0; Round( kb ; 2 ) & " KB"; 25 | Div( 1024; mb ) > 0; Round( mb ; 2 ) & " MB"; 26 | Div( 1024; gb ) > 0; Round( gb ; 2 ) & " GB"; 27 | Div( 1024; tb ) > 0; Round( tb ; 2 ) & " TB"; 28 | b 29 | ) 30 | ) -------------------------------------------------------------------------------- /path.dirname.fmfn: -------------------------------------------------------------------------------- 1 | /* 2 | ===================================================== 3 | path.dirname( path ) 4 | 5 | RETURNS: (string) The parent folder of a specified file path 6 | DEPS: developer() function. 7 | NOTES: While testing non developer accounts you may need to take off if ( developer ) 8 | 9 | ===================================================== 10 | */ 11 | 12 | Let ( 13 | [ 14 | //------------------------- VARIABLES 15 | 16 | lastchar = Right ( path ; 1 ); 17 | path = If( IsEmpty ( path ) ; Get ( FilePath ) ; path ); 18 | filename = Get ( FileName ) & ".fp7"; 19 | folder = Substitute ( path ; filename ; "" ); 20 | folder = Left ( folder ; Length ( folder ) - 1 ) 21 | 22 | ]; 23 | //------------------------- RESULT 24 | 25 | If ( lastchar = "/"; 0; folder ) 26 | 27 | ) -------------------------------------------------------------------------------- /script.param.set.fmfn: -------------------------------------------------------------------------------- 1 | /* 2 | * ===================================================== 3 | * script.param.set( key; value) 4 | * 5 | * RETURNS: (string) keyed pair of values 6 | * DEPENDENCIES: none 7 | * NOTES: Compliments to Mikhail Edoshin http://mikhailedoshin.com 8 | * ===================================================== 9 | * 10 | * This function is used to encapsulate a named value from a 11 | * data structure which preserves carriage returns and 12 | * other special characters. 13 | * 14 | */ 15 | 16 | Substitute( "¶" 17 | & Substitute( key; 18 | [ "^"; ".^" ]; 19 | [ "¶"; ".^^" ] 20 | ) & "¶" 21 | & Substitute( value; 22 | [ "^"; ".^" ]; 23 | [ "¶"; ".^^" ] 24 | ) & "¶"; 25 | 26 | [ "^"; ".^" ]; 27 | [ "¶"; ".^^" ] 28 | 29 | ) & "¶" 30 | -------------------------------------------------------------------------------- /date.fromUnix.fmfn: -------------------------------------------------------------------------------- 1 | /* 2 | * ===================================================== 3 | * date.fromUnix( unix; timezone) 4 | * 5 | * RETURNS: (date) FileMaker timestamp value based on unix timestamp supplied 6 | * DEPS: none 7 | * NOTES: 8 | * ===================================================== 9 | * 10 | */ 11 | 12 | Let ( [ 13 | UnixTimeAdjusted = ( unix + (timezone * 3600 ) ); 14 | days = Truncate ( Div ( UnixTimeAdjusted; 86400 ); 0 ); 15 | days_remainder = Mod ( UnixTimeAdjusted; 86400 ); 16 | hours = Truncate ( Div ( days_remainder; 3600 ); 0 ); 17 | hours_remainder = Mod ( days_remainder; 3600 ); 18 | minutes = Truncate ( Div ( hours_remainder; 60 ); 0) ; 19 | minutes_remainder = Mod ( hours_remainder; 60 ); 20 | seconds = minutes_remainder 21 | ]; 22 | Timestamp ( Date ( 1; 1; 1970) + days; Time ( hours ; minutes ; seconds )) 23 | ) -------------------------------------------------------------------------------- /list.trim.fmfn: -------------------------------------------------------------------------------- 1 | /* 2 | ===================================================== 3 | list.trim( values ) 4 | 5 | RETURNS: (list) trimmed version of the list 6 | DEPENDENCIES: 7 | VERSION: 1.0 8 | AUTHOR: See notes 9 | NOTES: Compliments http://www.briandunning.com/cf/904 10 | 11 | ===================================================== 12 | */ 13 | 14 | Let([ 15 | var.cleaned = Substitute( values; [ " "; "" ]; [ " "; "" ]; [ " "; "" ]; [ "¶"; "" ] ); // Text minus specified chars 16 | var.char.first = Position( values; Left( var.cleaned; 1 ); 0; 1 ); // Position of firt non cleaned char 17 | var.char.last = Position( values; Right( var.cleaned; 1 ); Length( values ); -1 ) // Position of last non cleaned char 18 | ]; 19 | 20 | Case( var.char.first; Middle( values; var.char.first; var.char.last - var.char.first + 1 ) ) 21 | 22 | 23 | ) -------------------------------------------------------------------------------- /char.arrows.fmfn: -------------------------------------------------------------------------------- 1 | /* 2 | * ===================================================== 3 | * char.arrows ( direction ) 4 | * 5 | * RETURNS: (string) Unicode arrow character 6 | * PARAMS: direction = (enum) up, down, left, right 7 | * DEPENDENCIES: none 8 | * AUTHOR: Matt Petrowsky 9 | * NOTES: 10 | * ===================================================== 11 | */ 12 | 13 | Let ( [ 14 | var.direction = Left ( direction ; 1 ); 15 | var.arrow = Case ( 16 | var.direction = "u"; char ( 9650 ); //"▲"; 17 | var.direction = "d"; char ( 9660 ); //"▼"; 18 | var.direction = "l"; char ( 9664 ); //"◀"; 19 | var.direction = "r"; char ( 9654 ); //"▶"; 20 | ) 21 | ]; 22 | // Result 23 | TextFont ( var.arrow ; If ( Get( SystemPlatform ) = -2 ; "Arial" ; "Apple Symbol Regular" ) ) 24 | 25 | ) -------------------------------------------------------------------------------- /var.merge.fmfn: -------------------------------------------------------------------------------- 1 | /* 2 | * ===================================================== 3 | * var.merge( name ; default ) 4 | * 5 | * RETURNS: (mixed) An assigned variable 6 | * DEPENDENCIES: var.set() 7 | * NOTES: Used exclusively for layout merge variables (FMP11+) 8 | * ===================================================== 9 | * 10 | * This custom function is a wrapper for the 11 | * assignment(creation) of global variables 12 | * from layout based merge variables. 13 | * Use this function within conditional formatting 14 | */ 15 | 16 | Let ( [ 17 | var.isMerge = PatternCount ( name ; "<<" ) and PatternCount ( name ; ">>" ); // determine if a real merge var 18 | var.name = If ( var.isMerge ; Trim ( Substitute ( name ; ["<<";""] ; [">>";""] ) ) ; "" ) // strip off the merge wrap 19 | ]; 20 | If ( not IsEmpty ( var.name ) ; var.set ( var.name ; default ) ; False ) 21 | ) -------------------------------------------------------------------------------- /script.param.let.fmfn: -------------------------------------------------------------------------------- 1 | /* NEEDS TO ACCOUNT FOR DATATYPE CURRENTLY ONLY SUPPORTS NUMBERS 2 | ===================================================== 3 | script.param.let( keys; values) 4 | 5 | RETURNS: (mixed) the value associated to the supplied key in positional order 6 | DEPS: none 7 | ABOUT: Takes two complimented set of lists and turns them into variables 8 | ===================================================== 9 | */ 10 | Let ( 11 | [ 12 | _Compliments = ValueCount ( keys ) = ValueCount ( values ); 13 | _NotEmpty = not IsEmpty( keys ) and not IsEmpty( values ) 14 | ]; 15 | 16 | If ( _Compliments and _NotEmpty ; 17 | 18 | "$" & GetValue ( keys ; 1 ) & " = " & GetValue ( values ; 1 ) & If ( ValueCount( keys ) = 1; "¶" ; ";¶" ) 19 | & script.param.let ( MiddleValues ( keys ; 2 ; 1000000 ) ; MiddleValues ( values ; 2 ; 1000000 ) ); 20 | _ 21 | 22 | ) 23 | 24 | ) -------------------------------------------------------------------------------- /selection.delete.fmfn: -------------------------------------------------------------------------------- 1 | /* 2 | * ===================================================== 3 | * selection.delete () 4 | * 5 | * RETURNS: (string) Active field contents less selection or one backspace 6 | * DEPENDENCIES: none 7 | * NOTES: Useful when determining field contents with keystroke triggers 8 | * ===================================================== 9 | * 10 | */ 11 | 12 | 13 | Let ( [ 14 | var.content = Get ( ActiveFieldContents ); 15 | var.endPoint = If ( Get ( ActiveSelectionSize ) > 0 ; Get ( ActiveSelectionStart ) + Get ( ActiveSelectionSize ) ; False ); 16 | var.toDelete = Middle ( var.content ; Get ( ActiveSelectionStart ) ; var.endPoint - Get ( ActiveSelectionStart ) ) 17 | ]; 18 | 19 | If ( not var.endPoint; 20 | Replace ( var.content ; Get ( ActiveSelectionStart ) -1 ; 1 ; "" ); 21 | Substitute ( Get ( ActiveFieldContents ) ; var.toDelete ; "" ) 22 | ) 23 | 24 | ) -------------------------------------------------------------------------------- /env.get.fmfn: -------------------------------------------------------------------------------- 1 | /* 2 | env.get( parameter ) 3 | RETURNS: FileMaker's Get functions in a more friendly manner 4 | DEPS: os.isWindows 5 | */ 6 | 7 | Let( 8 | //--------------- VARIABLES 9 | 10 | p = parameter; 11 | 12 | //--------------- RESULT 13 | 14 | Case( 15 | 16 | p = "platform" ; If( os.isWindows ; "windows" ; "macintosh"); 17 | p = "account" ; Get ( AccountName ); 18 | p = "layout" ; Get ( LayoutName ); 19 | p = "found" ; Get ( FoundCount ); 20 | p = "button" ; Get ( LastMessageChoice ); 21 | p = "result" ; Get ( ScriptResult ); 22 | p = "mode"; Choose ( Get ( WindowMode ) ; "Browse"; "Find"; "Preview"; "Printing"); 23 | p = "filename" ; Get ( FileName ); 24 | p = "localfile" ; Get( MultiUserState ) = 1 or Get( MultiUserState ) = 0; 25 | p = "datetime" ; Get( CurrentTimeStamp ); 26 | p = "script" ; Get( ScriptName ); 27 | p = "nic" ; GetValue( Get ( SystemNICAddress ) ; 1 ); 28 | p = "version" ; Abs( Get ( ApplicationVersion ) ); 29 | 30 | 0 ) // End Case 31 | 32 | ) // End Let -------------------------------------------------------------------------------- /xml.tag.fmfn: -------------------------------------------------------------------------------- 1 | /* 2 | ===================================================== 3 | xml.tag ( tag; attributes; value ) 4 | 5 | RETURNS: (string) Valid XML tag with attributes 6 | DEPS: none 7 | NOTES: http://www.w3.org/TR/xml/ 8 | ===================================================== 9 | */ 10 | 11 | Let ( [ 12 | 13 | var.tag.cleaned = Filter ( tag ; "ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz" ); 14 | 15 | var.value.cleaned = Substitute( value; 16 | ["|";" "]; /* pipe */ 17 | [" ";" "]; /* shift-space */ 18 | ["&";"&"]; 19 | ["<";"<"]; 20 | [">";">"]; 21 | ["\"";"""]; 22 | ["'";"'"] 23 | ) 24 | ]; 25 | 26 | "<" & var.tag.cleaned & attributes & ">" & var.value.cleaned & "" 27 | 28 | ) 29 | 30 | // TODO: 31 | // Not currently validating that attributes are properly formatted - just passing them through -------------------------------------------------------------------------------- /fmstandards/Developer.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================================== 3 | * Developer ( ) 4 | * 5 | * PARAMETERS: 6 | * none 7 | * RETURNS: 8 | * (bool) True or False based on proper 9 | * evaluation 10 | * DEPENDENCIES: 11 | * none 12 | * NOTES: Because FileMaker changed Get ( PrivilegeSetName ) 13 | * Evaluate () must be used 14 | * 15 | * RELEASE: 101011 16 | * ===================================================== 17 | * 18 | */ 19 | 20 | Let ( [ 21 | 22 | var.developers = List( ""; "" ) // Add the names of the accounts which are valid developer accounts 23 | 24 | ]; 25 | 26 | PatternCount ( ¶& var.developers &¶ ; ¶& Get ( AccountName ) &¶ ) ≥ 1 27 | or 28 | If ( GetAsNumber ( Get ( ApplicationVersion ) ) < 11; 29 | Evaluate ( "Get ( PrivilegeSetName )" ) = "[Full Access]"; 30 | Evaluate ( "Get ( AccountPrivilegeSetName )" ) = "[Full Access]" 31 | ) 32 | 33 | ) -------------------------------------------------------------------------------- /fmstandards/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 | ]; 29 | Evaluate ( prefix & GetValue ( var.parts ; 2 ) ) 30 | ) -------------------------------------------------------------------------------- /color.hex2dec.fmfn: -------------------------------------------------------------------------------- 1 | /* 2 | -------------------------- 3 | Author: Christopher Gaunt 4 | v1.0, April 6, 2009 5 | solutions@cordega.com 6 | 7 | Returns an integer that represents a color obtained by converting standard 6 character hexadecimal input. Useful if you don't need to know the actual RGB values) 8 | Hex = Hexadecimal input. Handles inputs containing leading/trailing spaces and/or # symbol. Self-contained. No external or self-calls. 9 | -------------------------- 10 | */ 11 | 12 | Let ([ 13 | $HexClean = Trim ( Filter ( Upper ( Hex ) ; "0123456789ABCDEF" ) ) 14 | ;$HexSplit = 15 | "(" & ( Middle ( $HexClean ; 1 ; 1 ) & "* 16 + " & Middle ( $HexClean ; 2 ; 1 ) ) & ")* 65536 +" & 16 | "(" & ( Middle ( $HexClean ; 3 ; 1 ) & "* 16 + " & Middle ( $HexClean ; 4 ; 1 ) ) & ")* 256 +" & 17 | "(" & ( Middle ( $HexClean ; 5 ; 1 ) & "* 16 + " & Middle ( $HexClean ; 6 ; 1 ) ) & ")" 18 | ] ; 19 | Evaluate ( Substitute ( $HexSplit ; [ "A" ; 10 ] ; [ "B" ; 11 ] ; [ "C" ; 12 ] ; [ "D" ; 13 ] ; [ "E" ; 14 ] ; [ "F" ; 15 ] ) ) 20 | ) // end let -------------------------------------------------------------------------------- /window.offscreen.fmfn: -------------------------------------------------------------------------------- 1 | /* 2 | * ===================================================== 3 | * window.offscreen( property ) 4 | * 5 | * RETURNS: (int) Value of where window should appear. 6 | * DEPENDENCIES: developer() 7 | * NOTES: none 8 | * ===================================================== 9 | */ 10 | 11 | Let ( [ 12 | var.onscreen = developer and $$DEVELOPER; 13 | var.offset = 100; 14 | var.property = Left ( property ; 1 ) // Account for use of single letters or full word by using Left() 15 | ]; 16 | Case ( 17 | var.property = "t" ; If ( var.onscreen ; Get ( WindowTop ) + var.offset ; Get ( WindowDesktopHeight ) ); 18 | var.property = "l" ; If ( var.onscreen ; Get ( WindowLeft ) + var.offset ; Get ( WindowDesktopWidth ) ); 19 | 0 20 | ) 21 | ) 22 | 23 | /* 24 | Special note about $$DEVELOPER. 25 | This value is used solution wide to control 26 | when scripts operate in a developer mode 27 | This allows you to see windows and other 28 | developer specific operations or screens. 29 | This value can be set at startup. 30 | */ -------------------------------------------------------------------------------- /developer.fmfn: -------------------------------------------------------------------------------- 1 | /* 2 | * ===================================================== 3 | * developer () 4 | * 5 | * RETURNS: Boolean result based on checking for a given 6 | * account, privelege set or other critera - 7 | * you decide (see notes at bottom). 8 | * DEPENDANCIES: list.valueWithin() 9 | * ===================================================== 10 | */ 11 | 12 | 13 | Let ( [ 14 | //------------------------- VARIABLES 15 | 16 | var.developers = List( ""; "Admin" ) // Add the names of the accounts which are valid developer accounts 17 | 18 | ]; 19 | //------------------------- RESULT 20 | 21 | PatternCount ( ¶& var.developers &¶ ; ¶& Get ( AccountName ) &¶ ) 22 | or 23 | Get ( PrivilegeSetName ) = "[Full Access]" 24 | 25 | ) 26 | 27 | /* 28 | Note: if you're paranoid about security, you need to 29 | know that a super adept hacker would know how to scrape 30 | variable values out of memory, despite not being shown 31 | by FileMaker. Anything stored in global variables $$ can be 32 | captured - although you REALLY have to know a lot to do it. 33 | */ -------------------------------------------------------------------------------- /file.ospath.fmfn: -------------------------------------------------------------------------------- 1 | /* 2 | *===================================================== 3 | *file.ospath( path; escaped ) 4 | * 5 | *RETURNS: (string) an os cleaned version of a path. 6 | *PARAMS path = (string) name of the file/folder location 7 | * escaped = (bool) whether the path should escape special characters 8 | *DEPS: none 9 | *NOTES: ScriptMaster and some other plugins do their own escaping internally 10 | *This function will also work on a return delimited list of paths 11 | *===================================================== 12 | * 13 | */ 14 | 15 | Let( [ 16 | _Windows = Abs( Get( SystemPlatform ) ) - 1; 17 | _NewPath = If ( Left( path; 5) = "file:"; Substitute( Middle( path; 6; 1000000 ); "¶file:"; "¶" ); path ); 18 | _MacPath = "/Volumes" & Substitute( _NewPath; ¶; "¶/Volumes"); 19 | _WinPath = Substitute( Middle( Substitute ( _NewPath ; "/" ; "\\" ); 2; 1000000 ); "¶\\"; "¶") 20 | ]; 21 | If ( _Windows; 22 | 23 | If ( escaped; _WinPath; _WinPath ); 24 | 25 | If ( escaped; Substitute( _MacPath; [" "; "\ "]; [","; "\,"] ); _MacPath ) 26 | ) 27 | ) -------------------------------------------------------------------------------- /layout.variables.fmfn: -------------------------------------------------------------------------------- 1 | /* 2 | * ===================================================== 3 | * layout.variables ( scope ) 4 | * 5 | * RETURNS: (string) key/value pairs as $key = value in positional order 6 | * PARAMETERS: @scope = (enum) global, local 7 | * DEPENDENCIES: none 8 | * ABOUT: Takes two compliment set of lists 9 | * and turns them into FMP defined variables 10 | * NOTES: none 11 | * ===================================================== 12 | * 13 | */ 14 | 15 | Let ( [ 16 | var.fieldNames = FieldNames ( Get ( FileName ) ; Get ( LayoutName ) ); 17 | var.prefix = "Let ( var.value = GetField ( \""; 18 | var.suffix = "\" ); If ( IsEmpty ( var.value ) ; \"-\" ; var.value ) ) &\¶&"; 19 | var.calculation = var.prefix & 20 | Substitute ( 21 | var.fieldNames; 22 | [ ¶ ; ¶ & var.prefix ]; 23 | [ ¶ ; var.suffix & ¶ ] 24 | ) & var.suffix; 25 | var.calculation = Left ( var.calculation ; Length ( var.calculation ) - 3 ); // trim off last concatenation 26 | var.fieldData = Evaluate ( var.calculation ) 27 | ]; 28 | var.list ( var.fieldNames ; var.fieldData ; scope ) 29 | ) -------------------------------------------------------------------------------- /requests.save.fmfn: -------------------------------------------------------------------------------- 1 | /* 2 | * ===================================================== 3 | * requests.save ( layout ; request ) 4 | * 5 | * RETURNS: (bool) True or False based on success or failure 6 | * DEPENDENCIES: none 7 | * NOTES: 8 | * ===================================================== 9 | * 10 | * Intended use is for saving a return delimited list of 11 | * fieldname/value pairs into a global variable with the 12 | * same name as the layout. 13 | * 14 | */ 15 | 16 | Let ( [ 17 | var.requests = Evaluate ( "$$" & layout & ".requests" ); 18 | var.existing = If ( IsEmpty ( var.requests ) or var.requests = "?"; False; True ); 19 | var.saved = // The let function (as a string) to be evaluated 20 | Evaluate( 21 | "Let ( $$" 22 | & layout & ".requests" 23 | & " = " & Quote ( var.requests ) 24 | & If ( var.existing; " & \¶ & "; " & ") // add carriage return if existing content 25 | & Quote( Substitute( request; ¶; "|" ) ) & // each request on its own line 26 | "; False )" 27 | ) 28 | 29 | ]; 30 | // Return 31 | If ( var.saved = "?"; False; True ) 32 | ) -------------------------------------------------------------------------------- /error.calculation.fmfn: -------------------------------------------------------------------------------- 1 | /* 2 | * ===================================================== 3 | * error.reference ( elementName ; requiredElementNames ; requiredType ) 4 | * 5 | * PARAMETERS: @elementName = FileMaker field reference 6 | * @requiredElementNames = List() of required element names 7 | * @requiredTypes = matching list of types for each element 8 | * RETURNS: (boolean) Whether or not there is a calculation error 9 | * DEPENDENCIES: none 10 | * NOTES: none 11 | * ===================================================== 12 | * 13 | */ 14 | 15 | Let ( [ 16 | $$ERROR.REFERENCE = FilterValues ( Case ( 17 | requiredType = "Table"; 18 | TableNames ( Get ( FileName ) ); 19 | 20 | requiredType = "Field"; 21 | FieldNames ( Get ( FileName ) ; Get ( LayoutName ) ); 22 | 23 | requiredType = "Layout"; 24 | LayoutNames ( Get ( FileName ) ); 25 | 26 | "" 27 | ); requiredElementNames ) ≠ requiredElementNames &¶; 28 | 29 | // Set the error into the global if required elements were not found 30 | $$ERROR.REFERENCE = If ( $$ERROR.REFERENCE; elementName & ": Missing literal references"; "" ) 31 | ]; 32 | If ( not IsEmpty ( $$ERROR.REFERENCE ); True; False ) 33 | ) -------------------------------------------------------------------------------- /var.set.fmfn: -------------------------------------------------------------------------------- 1 | /* 2 | * ===================================================== 3 | * var.set( name ; value ) 4 | * 5 | * RETURNS: (bool) True or False based on proper evaluation 6 | * DEPENDENCIES: none 7 | * NOTES: see var.eval for declaring multiple variables 8 | * ===================================================== 9 | * 10 | * This custom function is a wrapper for the 11 | * assignment(creation) of global variables. 12 | * Variables are not tracked and errors are 13 | * revealed within $$ERROR.VAR.SET 14 | */ 15 | 16 | Let ( $function.var.make.value = value; // preserve the data type of the inbound value 17 | Let ( [ 18 | $$ERROR.VAR.SET = ""; // Reset the global error variable for evaluation 19 | var.dollarsign = If ( Left ( name ; 2 ) = "$$" or Left ( name ; 1 ) = "$"; "" ; "$$" ); // check for prefixed $$ or $ 20 | var.calc = "Let ( " & var.dollarsign & name & " = $function.var.make.value; False )" 21 | ]; 22 | If ( 23 | Evaluate( 24 | var.calc 25 | ) = "?"; 26 | Let ( $$ERROR.VAR.SET = List ("EVAL ERROR"; "-------"; var.calc ); False ); 27 | // Insert the name of the variable into the reserved $$VARIABLES global variable 28 | True 29 | ) 30 | ) 31 | ) -------------------------------------------------------------------------------- /window.activetab.fmfn: -------------------------------------------------------------------------------- 1 | /* 2 | ===================================================== 3 | window.activetab( init; theList ) 4 | 5 | RETURNS: (list) List of all active frontmost tabs. 6 | DEPS: none 7 | NOTES: Original by Koen Van Hulle (SHpartners.com) 8 | ===================================================== 9 | */ 10 | 11 | Let([ 12 | //------------------------- VARIABLES 13 | Init = Init + 1 ; 14 | 15 | ObjectList = LayoutObjectNames( Get( FileName ); Get( LayoutName ) ) ; 16 | 17 | TabToCheck = Substitute( GetValue( ObjectList; init ); ¶; "") ; 18 | 19 | ParentTab = GetLayoutObjectAttribute( TabToCheck; "enclosingObject" ) ; 20 | 21 | IsCurrentTab = Case( 22 | IsEmpty( TabToCheck );  0; 23 | GetLayoutObjectAttribute( TabToCheck ; "isFrontTabPanel" ) and 24 | IsEmpty( ParentTab ); 1; 25 | GetLayoutObjectAttribute( TabToCheck; "isFrontTabPanel" ) and 26 | PatternCount( ¶ & theList & ¶; ¶ &  ParentTab & ¶ ); 1; 27 | 0 ); 28 | 29 | theList = theList & If( IsCurrentTab; TabToCheck & ¶ ) 30 | 31 | ]; 32 | 33 | //------------------------- RESULT 34 | 35 | If( not IsEmpty( TabToCheck ); window.activetab( init; theList ); theList ) 36 | 37 | ) -------------------------------------------------------------------------------- /window.center.fmfn: -------------------------------------------------------------------------------- 1 | /* 2 | * ===================================================== 3 | * window.center ( dimension ) 4 | * 5 | * RETURNS: (int) Screen pixel value based on dimension 6 | * supplied. 7 | * REQUIRES: None 8 | * NOTES: Using a locally scoped variable within your 9 | * script will alter what this function returns 10 | * $parentWindowHeight for window height of parent 11 | * $parentWindowWidth for window width of parent 12 | * $parentWindowTop for window top of parent 13 | * $parentWindowLeft for window left of parent 14 | * ===================================================== 15 | */ 16 | 17 | Let ([ 18 | var.vertical = PatternCount ( dimension ; "vert" ); 19 | var.horizontal = PatternCount ( dimension ; "horiz" ); 20 | var.parentHeight = If ( IsEmpty ( $parentWindowHeight ) ; Get ( WindowDesktopHeight ) ; $parentWindowHeight ); 21 | var.parentWidth = If ( IsEmpty ( $parentWindowWidth ) ; Get ( WindowDesktopWidth ) ; $parentWindowWidth ) 22 | ]; 23 | 24 | Case( var.vertical; 25 | ( var.parentHeight / 2) - ( Get ( WindowHeight ) / 2 ) + $parentWindowTop; 26 | var.horizontal; 27 | ( var.parentWidth / 2) - ( Get ( WindowWidth ) / 2 ) + $parentWindowLeft 28 | ) 29 | ) -------------------------------------------------------------------------------- /mouse.clicks.fmfn: -------------------------------------------------------------------------------- 1 | /* 2 | * ===================================================== 3 | * mouse.clicks ( number ; identifier ) 4 | * RETURNS: (bool) If mouse has been clicked the number of times 5 | * as tracked by the global variable $$mouse.clicks.count 6 | * DEPENDENCIES: none 7 | * NOTES: 8 | * ===================================================== 9 | * 10 | * WARNING! You can only use a call to this custom function once within any given script. 11 | * Subsequent calls within the same script will mess up the variables. 12 | */ 13 | 14 | Let ( [ 15 | $$mouse.clicks.count = If ( IsEmpty( $$mouse.clicks.count ) ; 1 ; $$mouse.clicks.count + 1 ); 16 | $$mouse.clicks.previous = $$mouse.clicks.last; 17 | $$mouse.clicks.last = Get ( CurrentTimeStamp ); 18 | $$mouse.clicks.duration = Seconds ( $$mouse.clicks.last - $$mouse.clicks.previous ); 19 | // Criteria required to be a valid subsequent click, identifier must match 20 | $$mouse.clicks.valid = $$mouse.clicks.duration ≤ 1 and identifier = $$mouse.click.identifier; // Subsequent click within 1 second 21 | $$mouse.clicks.count = If ( not $$mouse.clicks.valid ; "" ; $$mouse.clicks.count ); 22 | $$mouse.click.identifier = identifier 23 | ]; 24 | 25 | $$mouse.clicks.count = number // Return the count as boolean 26 | 27 | ) -------------------------------------------------------------------------------- /var.define.fmfn: -------------------------------------------------------------------------------- 1 | /* 2 | * ===================================================== 3 | * var.define( name ; value ) 4 | * 5 | * RETURNS: (bool) True or False based on proper evaluation 6 | * DEPENDENCIES: none 7 | * NOTES: see var.eval for declaring multiple variables 8 | * ===================================================== 9 | * 10 | * This custom function is a wrapper for the 11 | * assignment(creation) of global variables. 12 | * Variables are tracked within $$VARIABLES 13 | */ 14 | 15 | Let ( $function.var.define.value = value; // preserve the data type of the inbound value 16 | Let ( [ 17 | $$ERROR.VAR.DEFINE = ""; // Reset the global error variable for evaluation 18 | var.dollarsign = If ( Left ( name ; 2 ) = "$$" ; "" ; "$$" ); // check for prefixed $$ 19 | var.calc = "Let ( " & var.dollarsign & name & " = $function.var.define.value; False )" 20 | ]; 21 | If ( 22 | Evaluate( 23 | var.calc 24 | ) = "?"; 25 | Let ( $$ERROR.VAR.DEFINE = List ( "EVAL ERROR"; "-------"; var.calc ); False ); 26 | // Insert the name of the variable into the reserved $$VARIABLES global variable 27 | Let ( $$VARIABLES = If ( IsEmpty ( FilterValues ( $$VARIABLES ; name ) ); List ( $$VARIABLES ; name ) ; $$VARIABLES ) ; True ) 28 | ) 29 | ) 30 | ) -------------------------------------------------------------------------------- /portal.grouping.fmfn : -------------------------------------------------------------------------------- 1 | /* 2 | * ===================================================== 3 | * portal.grouping ( row ; part ; field ) 4 | * 5 | * RETURNS: (bool) True or False based on relative portal rows 6 | * DEPENDENCIES: none 7 | * NOTES: 8 | * ===================================================== 9 | * 10 | */ 11 | 12 | Case ( 13 | part = "top"; 14 | // Determine if at the top of a group 15 | not IsEmpty ( field ) // I have a value 16 | and 17 | GetNthRecord ( field ; row ) ≠ GetNthRecord ( field ; row - 1 ) // record above me does not match 18 | and 19 | IsValid ( GetNthRecord ( field ; row + 1 ) ); // I do have a record below me 20 | 21 | part = "middle"; 22 | // Determine if in the middle of a group 23 | not IsEmpty ( field ) // I have a value 24 | and 25 | GetNthRecord ( field ; row ) = GetNthRecord ( field ; row - 1 ) // record above me has the same value 26 | and 27 | GetNthRecord ( field ; row ) = GetNthRecord ( field ; row + 1 ); // record below me has the same value 28 | 29 | part = "bottom"; 30 | // Determine if at the bottom of a group 31 | not IsEmpty ( field ) // I have a value 32 | and 33 | GetNthRecord ( field ; row ) ≠ GetNthRecord ( field ; row + 1 ) // record below me does not match 34 | and 35 | IsValid ( GetNthRecord ( field ; row + 1 ) ) // there is a record above me 36 | ) -------------------------------------------------------------------------------- /vars.save.fmfn: -------------------------------------------------------------------------------- 1 | /* 2 | * ===================================================== 3 | * vars.save( valuelist ) 4 | * RETURNS: (string) Return delimted list of valid variable declarations 5 | * DEPENDENCIES: none 6 | * NOTES: see var.define for how/where variables are saved 7 | * ===================================================== 8 | * 9 | */ 10 | 11 | Let ( [ 12 | //$$var.test = "example data"; 13 | //valuelist = "one¶two¶three"; // used for testing 14 | var.topmost_value = GetValue ( valuelist ; 1 ); 15 | var.remaining_values = If ( ValueCount ( valuelist ) = 1 ; "" ; 16 | // Stupid xValues functions return a \r 17 | Substitute( RightValues ( valuelist ; ValueCount ( valuelist ) - 1 ) & ¶ ; "¶¶" ; "" ) 18 | ); 19 | var.value = Evaluate ( "$$" & var.topmost_value ); 20 | var.value = If ( var.value ≠ "?" ; var.value ; "ERROR" ); 21 | var.string = "$$" & var.topmost_value & " = " & Quote ( var.value ); 22 | $variables.save = List ( $variables.save ; var.string ) 23 | ]; 24 | 25 | If ( isempty ( var.remaining_values ) ; $variables.save ; vars.save ( var.remaining_values ) ) 26 | 27 | ) 28 | 29 | /* 30 | // Testing code 31 | Let( [ 32 | $variables.save = ""; 33 | $$one = 1; 34 | $$two = 2; 35 | $$three = 3 36 | ]; 37 | vars.save ( List ( "one" ; "two" ; "three" ) ) 38 | ) 39 | */ -------------------------------------------------------------------------------- /script.param.change.fmfn: -------------------------------------------------------------------------------- 1 | /* 2 | * ===================================================== 3 | * script.param.change( values; changes) 4 | * 5 | * RETURNS: (mixed) new key/value list based on changes 6 | * DEPENDENCIES: none 7 | * NOTES: Compliments to Mikhail Edoshin http://mikhailedoshin.com 8 | * ===================================================== 9 | * 10 | * This function is used to replace a named value within a 11 | * data structure which preserves carriage returns and 12 | * other special characters. 13 | * 14 | */ 15 | 16 | Case( 17 | // Both values are required to swap, otherwise use whatever we have 18 | IsEmpty( values ); changes; 19 | IsEmpty( changes ); values; 20 | 21 | // Recursion on stack 22 | ValueCount( changes ) > 1; 23 | script.param.change( script.param.change( values; LeftValues( changes; 1 ) ); 24 | RightValues( changes; ValueCount( changes ) - 1 ) ); 25 | 26 | // Swap out the key/value if found 27 | Let( [ 28 | var.key = MiddleValues( Substitute( changes; [ "¶"; "" ]; [ ".^^"; "¶" ]; [ ".^"; "^" ] ); 2; 1 ); 29 | var.optionPosition = ValueCount( Left( values; Position( "¶" & values; "¶.^^" & Left( var.key; Length( var.key ) - 1 ) & ".^^"; 1; 1 ) ) ) ]; 30 | 31 | If( var.optionPosition = 0; values & changes; 32 | LeftValues( values; var.optionPosition - 1 ) 33 | & changes 34 | & RightValues( values; ValueCount( values ) - var.optionPosition ) ) 35 | ) 36 | ) -------------------------------------------------------------------------------- /list.swapValue.fmfn: -------------------------------------------------------------------------------- 1 | /* 2 | * ===================================================== 3 | * list.swapValue ( valuelist ; value; pos ) 4 | * RETURNS: (list) New list with new value in designated position 5 | * DEPENDENCIES: none 6 | * NOTES: see var.eval for declaring multiple variables 7 | * ===================================================== 8 | * 9 | */ 10 | 11 | Let ( [ 12 | // VARIABLES 13 | var.valuecount = ValueCount ( valuelist ); 14 | var.slotexists = var.valuecount > pos; 15 | var.filler = "¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶¶"; 16 | var.filleramount = If ( not var.slotexists ; Left ( var.filler ; pos - var.valuecount - 1 ) ); 17 | var.tophalf = LeftValues ( valuelist ; pos - 1 ); 18 | var.tophalf = Left ( var.tophalf ; Length ( var.tophalf ) - 1 ) & var.filleramount; // trim extra return 19 | var.bottomhalf = RightValues ( valuelist ; var.valuecount - pos ); 20 | var.bottomhalf = Left ( var.bottomhalf ; Length ( var.bottomhalf ) - 1 ) // trim extra return 21 | ]; 22 | // RESULT 23 | var.tophalf & 24 | If ( pos > 1 ; ¶ & value ; value ) & 25 | If ( not IsEmpty ( var.bottomhalf ) ; ¶ & var.bottomhalf ) 26 | ) 27 | 28 | /* 29 | // Testing values - prefix to variables within Data Viewer 30 | valuelist = List ( "tab.hah"; "tab.goog"; "tab.raw"; "tab.random"); 31 | valuelist = "tab.hah¶tab.goog¶tab.raw¶tab.random"; 32 | value = "==="; 33 | pos = 1; 34 | */ 35 | -------------------------------------------------------------------------------- /fmstandards/WindowCenter.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================================== 3 | * WindowCenter ( dimension ) 4 | * 5 | * PARAMETERS: 6 | * @dimension (enumeration) Vertical, Horizontal 7 | * RETURNS: 8 | * (int) Screen position value based on dimension 9 | * supplied 10 | * DEPENDENCIES: 11 | * none 12 | * NOTES: 13 | * Using a locally scoped variable within your 14 | * script will alter what this function returns 15 | * $parentWindowHeight for window height of parent 16 | * $parentWindowWidth for window width of parent 17 | * $parentWindowTop for window top of parent 18 | * $parentWindowLeft for window left of parent 19 | * ===================================================== 20 | * 21 | */ 22 | 23 | Let ([ 24 | 25 | var.vertical = PatternCount ( dimension ; "vert" ); 26 | var.horizontal = PatternCount ( dimension ; "horiz" ); 27 | var.parentHeight = If ( IsEmpty ( $parentWindowHeight ) ; Get ( WindowDesktopHeight ) ; $parentWindowHeight ); 28 | var.parentWidth = If ( IsEmpty ( $parentWindowWidth ) ; Get ( WindowDesktopWidth ) ; $parentWindowWidth ) 29 | 30 | ]; 31 | 32 | Case ( 33 | var.vertical; 34 | ( var.parentHeight / 2) - ( Get ( WindowHeight ) / 2 ) + $parentWindowTop; 35 | var.horizontal; 36 | ( var.parentWidth / 2) - ( Get ( WindowWidth ) / 2 ) + $parentWindowLeft 37 | ) 38 | ) -------------------------------------------------------------------------------- /string.trim.fmfn: -------------------------------------------------------------------------------- 1 | /* 2 | * ===================================================== 3 | * string.trim( text; characters ) 4 | * 5 | * RETURNS: (string) original string less supplied list of values 6 | * PARAMS: text = string; characters = List() 7 | * DEPENDENCIES: none 8 | * VERSION: 1.0 9 | * AUTHOR: See notes 10 | * NOTES: 11 | * Adapted by Matt Petrowsky from 12 | * http://www.briandunning.com/cf/904 13 | * Compliments to Debi Fuchs, Aptworks Consulting 14 | * ===================================================== 15 | */ 16 | 17 | Let([ 18 | $function.string.trim = text; 19 | var.default = List( " "; " "; " "; "\¶"); // Default list of characters to remove (space, tab, return) 20 | var.characters = If ( IsEmpty( characters ); var.default; characters ); 21 | 22 | var.function.parts = "[\"" & Substitute( var.characters; ¶; "\"; \"\"];¶[\"" ) & "\"; \"\"]"; 23 | var.function.clean = "Substitute( $function.string.trim; " & var.function.parts & " )"; // Strip out specified chars 24 | var.function.result = Evaluate( var.function.clean ); 25 | 26 | var.char.first = Position( text; Left( var.function.result; 1 ); 0; 1 ); // Position of first (non cleaned) char 27 | var.char.last = Position( text; Right( var.function.result; 1 ); Length( text ); -1 ) // Position of last (non cleaned) char 28 | ]; 29 | 30 | Case( var.char.first; Middle( text; var.char.first; var.char.last - var.char.first + 1 ) ) 31 | 32 | ) -------------------------------------------------------------------------------- /window.fronttabs.fmfn: -------------------------------------------------------------------------------- 1 | /* 2 | * ===================================================== 3 | * window.fronttabs () 4 | * 5 | * RETURNS: (list) Return delimited list of frontmost tabs. 6 | * DEPENDENCIES: list.custom(), layout.objects(), list.valueWithin() 7 | * NOTES: Adapted from Agnès Barouh 8 | * FrontTabsPanelsList() 9 | * ===================================================== 10 | */ 11 | 12 | Let ([ 13 | $function.result = "|###|"; 14 | $function.return = ¶; 15 | $function.objects = layout.objects; 16 | 17 | var.result = list.custom ( 18 | 1; // start 19 | ValueCount ( $function.objects ); // end 20 | "Let ( [ 21 | var.current = GetValue ( $function.objects; [n] ); 22 | var.isFront = GetLayoutObjectAttribute ( var.current; \"isFrontTabPanel\" ); 23 | var.enclosing = GetLayoutObjectAttribute ( var.current; \"enclosingObject\" ); 24 | var.append = $function.return & var.current; 25 | $function.result = $function.result & Case ( 26 | var.isFront and IsEmpty ( var.enclosing ) ; var.append; 27 | var.isFront and list.valueWithin ( $function.result ; var.enclosing ) ; var.append 28 | ) 29 | ]; False )" 30 | ) 31 | ]; 32 | 33 | Substitute ( $function.result ; 34 | [ "|###|¶" ; ""]; 35 | [ "|###|" ; "" ] 36 | ) 37 | ) 38 | 39 | & Let ( $function.result = "" ; "" ) 40 | -------------------------------------------------------------------------------- /vars.objects.fmfn: -------------------------------------------------------------------------------- 1 | /* 2 | * ===================================================== 3 | * vars.objects( objects ) 4 | * 5 | * RETURNS: (string) Return delimted list of valid variable declarations 6 | * DEPENDENCIES: none 7 | * NOTES: To be used with var.eval. Designed to have a list 8 | * of object names passed so it can create a list of variables 9 | * using object for the key and the object's contents for value. 10 | * Should be used within context because of GetLayoutObjectAttribute() 11 | * ===================================================== 12 | * 13 | * This is useful for an interface which uses global fields as keys 14 | * for relationships. You can call this function, pass in object names 15 | * for global fields and have a valid varible structure created for 16 | * you - ready to save. 17 | * 18 | */ 19 | 20 | Let ( [ 21 | var.current = GetValue ( objects ; 1 ); // topmost value 22 | var.remainder = RightValues ( objects ; ValueCount( objects ) - 1 ); 23 | var.return = "$" & var.current & " = " & Quote ( GetLayoutObjectAttribute ( var.current ; "content" ) ); // return string 24 | $function.vars.objects.result = List ( var.return ; $function.vars.objects.result ) // append return string to collection 25 | ]; 26 | Case ( 27 | isempty ( objects ) ; "No objects found"; 28 | var.remainder > 1 ; vars.objects( var.remainder ); 29 | $function.vars.objects.result 30 | ) 31 | ) 32 | 33 | /* 34 | Because *Values functions leave 35 | trailing return you use > 1 for 36 | moving to next item in stack 37 | */ -------------------------------------------------------------------------------- /plugin.valid.fmfn: -------------------------------------------------------------------------------- 1 | /* 2 | plugin.valid( name ; version ) 3 | 4 | RETURNS: (bool) Result of whether the tested plugin is there or not and also the version. 5 | 6 | NOTES: Uses global variables of $$PLUGIN_VERSION and $$PLUGIN_MESSAGE as return values. 7 | */ 8 | 9 | Let( [ 10 | //------------------------- VARIABLES 11 | 12 | /* 13 | Provide a list of plugins and their version functions to be 14 | used for universal checking of the plugin. This one function 15 | is where you can store all of your solution plugin checks. 16 | */ 17 | 18 | _PluginVersion = "Let( $$PLUGIN_VERSION = EXTERNAL ; 0 )"; 19 | 20 | _Found = Case( 21 | //--------------------------- ZIPPSCRIPT 22 | name = "ZippScript" ; 23 | not EvaluationError( Evaluate( Substitute( _PluginVersion ; "EXTERNAL" ; "zippScript_Version( \"\" )" ) ) ); 24 | //--------------------------- DOSCRIPT 25 | name = "DoScript" ; 26 | not EvaluationError( Evaluate( Substitute( _PluginVersion ; "EXTERNAL" ; "S4HU_VersionEventScript" ) ) ); 27 | //---------------------------------------------------------------------------------------- 28 | 0 29 | ); 30 | 31 | _Message_Missing = "The plugin " & name & " is required for this solution to function properly."; 32 | _Message_Version = "Your version of the plugin " & name & " is at " & $$PLUGIN_VERSION & " you need " & version & "."; 33 | 34 | _Version_Ok = $$PLUGIN_VERSION >= version; 35 | 36 | $$PLUGIN_MESSAGE = Case ( not _Found; _Message_Missing; not _Version_Ok; _Message_Version; "" ) 37 | 38 | ]; 39 | 40 | //------------------------- RESULT 41 | 42 | If ( _Found and _Version_Ok; 1 ; 0 ) 43 | 44 | ) -------------------------------------------------------------------------------- /fields.changed.fmfn: -------------------------------------------------------------------------------- 1 | /* 2 | * ===================================================== 3 | * fields.changed ( field ; result ; triggers ) 4 | * 5 | * RETURNS: (mixed) Either the field (typically self) or the result 6 | * PARAMETERS: @field = Always use the Self function 7 | * @result = a calculated result 8 | * @triggers = [optional] field references which should force the auto-enter to trigger 9 | * DEPENDENCIES: REQUIRES FileMaker 10 10 | * NOTES: This allows fields to become interdependant on each other 11 | * Adapted from AllowInputInAutoEnter() by Fabrice Nordmann & Tanguy Collès, BH&A 12 | * USAGE: Use when two or more fields are interdependant and any of the fields can be changed 13 | * for example: A conversion of inches -> centimeters or centimeters -> inches. 14 | * With both fields modifiable. 15 | * EXAMPLE: fields.changed ( Self ; inches / .3937007874 ; "" ) 16 | * ===================================================== 17 | * 18 | */ 19 | 20 | Case ( 21 | // If the active field is the same as the one being evaluated 22 | GetFieldName ( field ) = Get ( ActiveFieldTableName ) 23 | & "::" & Get ( ActiveFieldName ) 24 | & Case ( 25 | Get ( ActiveRepetitionNumber ) > 1 ; 26 | "[" & Get ( ActiveRepetitionNumber ) & "]" 27 | ); 28 | Get ( ActiveFieldContents ); // returned what was entered by the user 29 | result // otherwise, return the calculated result (becauase I'm not the active field) 30 | ) -------------------------------------------------------------------------------- /list.sort.defined.fmfn: -------------------------------------------------------------------------------- 1 | // Currently has a problem with different variations (e.g. Layouts vs. layouts) - needs debugging 2 | 3 | /* 4 | ===================================================== 5 | list.sort.defined( values; order ) 6 | 7 | RETURNS: (list) sorted list according to specified order 8 | DEPENDENCIES: 9 | VERSION: 1.0 10 | AUTHOR: Matt Petrowsky 11 | NOTES: 12 | 13 | ===================================================== 14 | */ 15 | 16 | Let ([ 17 | var.list = FilterValues( order; values ); // Valid order based on supplied values (no need to use an order value if not in the values) 18 | var.current = GetValue( var.list; 1 ); // Top value of the order list 19 | var.remaining = Right( var.list; Length( var.list ) - Length( var.current ) - 1 ); // Remaining order values 20 | var.found = FilterValues ( values ; var.current ); // Found value according to order list 21 | var.count = ValueCount( var.found ); // Count of found value (if found - see test below) 22 | var.values = Substitute( values; var.current; ""); // Knocks out the found value 23 | 24 | // The above could be cleaned up to include returns to catch for duplicate values 25 | // such as "script names" and "scripts" where "script" will catch both 26 | 27 | continue = ValueCount( var.list ) ≠ 0 // Exit condition for recursion 28 | ]; 29 | 30 | // Outer left and Substitute remove double returns - this could be handled better 31 | 32 | Left( 33 | Substitute( 34 | If ( continue; If ( var.count; var.found & list.sort.defined( var.values; var.remaining ) ) ) & If ( not continue; Substitute ( var.values ; "¶¶" ; ¶ ) ); 35 | "¶¶"; ¶ 36 | ) 37 | ; Length( values ) ) 38 | 39 | ) -------------------------------------------------------------------------------- /webviewer.records.txt: -------------------------------------------------------------------------------- 1 | /* 2 | * ===================================================== 3 | * webviewer.records ( format ; css ; color ) 4 | * 5 | * RETURNS: (string) Web viewer using formatted string 6 | * DEPENDENCIES: webviewer.data(), html.document(), html.css() 7 | * NOTES: Available placeholders [current], [found], [percentage] 8 | * ===================================================== 9 | * 10 | */ 11 | 12 | // example: 13 | // webviewer.records ( "Record [current] of [found] | [percentage]" ; "#bar { background-color: green }" ) 14 | 15 | Let ( [ 16 | var.record = Get(RecordNumber); 17 | var.found = Get(FoundCount); 18 | var.total = Get(TotalRecordCount); 19 | isSubset = var.total = var.found; 20 | var.percentage = Truncate ( ( var.record / var.found ) * 100 ; 0 ) & "%"; 21 | cssBar = Substitute ( "#bar { background-color: @color; height: 100%; width: @width; }"; 22 | [ "@color" ; If ( isempty ( color ) ; "transparent" ; color ) ]; 23 | [ "@width" ; var.percentage ] 24 | ) 25 | ]; 26 | webviewer.data( ""; 27 | "Record Information"; 28 | html.css( "body { text-align: center; margin: 0; border: 0; } #text{ margin:0 auto; }" & cssBar & css ); // load user css after the bar for override support 29 | List ( 30 | "
"; 31 | "
"; 32 | Substitute ( 33 | format; 34 | [ "[current]" ; var.record ]; 35 | [ "[found]" ; var.found ]; 36 | [ "[percentage]" ; var.percentage ] 37 | ); 38 | "
"; 39 | ) 40 | ) 41 | ) -------------------------------------------------------------------------------- /keyboard.modifiers.fmfn: -------------------------------------------------------------------------------- 1 | /* 2 | * ===================================================== 3 | * keyboard.modifiers( keys ; ignore ) 4 | * 5 | * RETURNS: (boolean) based on modifier keys being held down 6 | * PARAMETERS: @keys = List() of "keys" or string (e.g List("Command"; "Alt") or "command-alt") 7 | * @ignore = (bool) whether or not to ignore caps lock 8 | * DEPENDENCIES: none 9 | * NOTES: Adapted from http://www.briandunning.com/cf/473 by Peter Wagemans, SHpartners 10 | * ===================================================== 11 | * 12 | */ 13 | 14 | Let ( [ 15 | // Define 16 | var.keys = Get ( ActiveModifierKeys ); 17 | // Convert keys to their bit values (see note at bottom) 18 | var.command = Mod ( Int ( var.keys / 16 ) ; 2 ); 19 | var.alt = Mod ( Int ( var.keys / 8 ) ; 2 ); 20 | var.control = Mod ( Int ( var.keys / 4 ) ; 2 ); 21 | var.capslock = Mod ( Int ( var.keys / 2 ) ; 2 ); 22 | var.shift = Mod ( var.keys ; 2 ) 23 | ]; 24 | // Result (boolean) 25 | ( var.command xor not PatternCount ( keys ; "command" ) ) 26 | and 27 | ( var.alt xor not ( PatternCount ( keys ; "alt" ) or PatternCount ( keys ; "option" ) ) ) 28 | and 29 | ( var.control xor not ( PatternCount ( keys ; "ctrl" ) or PatternCount ( keys ; "control") ) ) 30 | and 31 | If ( not ignore ; ( var.capslock xor not PatternCount ( keys ; "capslock" ) ) ; True ) 32 | and 33 | ( var.shift xor not PatternCount ( keys ; "shift" ) ) 34 | and 35 | ( ( var.keys = 0 ) xor ( keys ≠ "" ) ) 36 | ) 37 | 38 | /* 39 | The Mod() function is used to determine 40 | if the key is within the result of 41 | Get ( ActiveModifierKeys ) 42 | 43 | 16 8 4 2 1 44 | -------------- 45 | 0 0 1 0 1 46 | | | | | | 47 | | | | | Shift 48 | | | | Caps Lock 49 | | | Ctrl 50 | | Alt/Option 51 | Apple Command Key 52 | */ -------------------------------------------------------------------------------- /window.properties.fmfn: -------------------------------------------------------------------------------- 1 | /* 2 | * ===================================================== 3 | * window.properties ( method ) 4 | * 5 | * RETURNS: (string) window properties according to specified method 6 | * PARAMETERS: method = (enum) 7 | * DEPENDENCIES: var.list() and object.id() 8 | * NOTES: Provides multiple methods for returning the 9 | * properties of the current window 10 | * ===================================================== 11 | * 12 | */ 13 | 14 | Let ( [ 15 | var.keys = List( "name"; "layout"; "id"; "top";"left";"width";"height"); 16 | var.values = List( 17 | Get ( WindowName ); 18 | Get ( LayoutName ); 19 | object.id ( Get ( LayoutName ); "Layout"; ""; "" ); 20 | Get ( WindowTop ); 21 | Get ( WindowLeft ); 22 | Get ( WindowWidth ); 23 | Get ( WindowHeight ) 24 | ) 25 | ]; 26 | // returns 27 | Case( 28 | method = "" or 29 | method = "default"; var.values; 30 | 31 | method = "piped"; Substitute ( var.values ; ¶ ; "|" ); 32 | 33 | // global variables are returned by var.list() - use Substitute ( properties ; "$$" ; "$" ) to make local 34 | method = "let"; var.list ( var.keys; 35 | List( 36 | Quote ( Get ( WindowName ) ); // because var.list needs quoted strings 37 | Quote ( Get ( LayoutName ) ); 38 | object.id ( Get ( LayoutName ); "Layout"; ""; "" ); 39 | Get ( WindowTop ); 40 | Get ( WindowLeft ); 41 | Get ( WindowWidth ); 42 | Get ( WindowHeight ) 43 | ) ; "global" ); 44 | 45 | // default 46 | var.values 47 | ) 48 | ) -------------------------------------------------------------------------------- /fmstandards/Debug.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================================== 3 | * Debug ( ) 4 | * 5 | * PARAMETERS: 6 | * none 7 | * RETURNS: 8 | * (string) A list of informative values 9 | * about the current FMP environment. 10 | * DEPENDENCIES: 11 | * Developer () 12 | * NOTES: 13 | * 14 | * RELEASE: 101009 15 | * ===================================================== 16 | * 17 | */ 18 | 19 | If ( Developer; 20 | Let ( [ 21 | // Variables 22 | $$DEBUG = List ( 23 | "App Version = " & Abs ( Get ( ApplicationVersion ) ); 24 | "App Current Time = " & GetAsText ( Get ( CurrentTimeStamp ) ); 25 | "Host Name = " & Get ( HostName ); 26 | "Host IP = " & Get ( HostIPAddress ); 27 | "File Location = " & Get ( FilePath ); 28 | "Account = " & Get ( AccountName ); 29 | "-----------------------------------"; 30 | "Script Param = " & Get ( ScriptParameter ); 31 | "Script Result = " & Get ( ScriptResult ); 32 | "-----------------------------------"; 33 | "Last Error = " & Get ( LastError ); 34 | "Last ODBC = " & Get ( LastODBCError ); 35 | "-----------------------------------"; 36 | "Layout = " & Get ( LayoutName ); 37 | "Layout Table = " & Get ( LayoutTableName ); 38 | "-----------------------------------"; 39 | "Open Records = " & Get ( RecordOpenCount ); 40 | "Record Modified = " & Get ( RecordModificationCount ); 41 | "-----------------------------------"; 42 | "Active Field = " & Get ( ActiveFieldName ); 43 | "Active Field Table = " & Get ( ActiveFieldTableName ); 44 | "Active Layout Object = " & Get ( ActiveLayoutObjectName ); 45 | "Active Layout Table = " & Get ( LayoutTableName ); 46 | "-----------------------------------"; 47 | "Local time = " & Get ( CurrentTimeStamp ); 48 | "Host time = " & Get ( CurrentHostTimeStamp ); 49 | "-----------------------------------"; 50 | ) 51 | ]; 52 | 53 | // Return value 54 | $$DEBUG 55 | 56 | ) 57 | ) -------------------------------------------------------------------------------- /number.format.fmfn: -------------------------------------------------------------------------------- 1 | /* 2 | * ===================================================== 3 | * number.format ( input ; format ) 4 | * 5 | * RETURNS: (string) Formatted input according to format supplied 6 | * DEPENDENCIES: none 7 | * NOTES: Adapted from http://www.briandunning.com/cf/769 by Toby Beedell 8 | * ===================================================== 9 | * 10 | */ 11 | 12 | Let ( [ 13 | var.input = input; 14 | var.format = format; 15 | var.valid = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; 16 | var.match = "#"; 17 | test.exit = IsEmpty ( var.input ) or IsEmpty ( var.format ) 18 | 19 | ]; 20 | If ( test.exit; 21 | // then 22 | var.input; 23 | // else 24 | Let ( [ 25 | match.count = PatternCount ( var.format; var.match ); 26 | match.none = match.count < 1; 27 | format.length = Length ( var.format ); 28 | format.end = If ( match.none and format.length > 0 ; var.format ) 29 | ]; 30 | If ( match.none; 31 | // then 32 | format.end & var.input; 33 | // else 34 | Let ( [ 35 | var.filtered = Filter ( var.input ; var.valid ); 36 | filtered.length = Length ( var.filtered ); 37 | match.position = Position ( var.format ; var.match ; 1 ; 1 ); 38 | var.prefix = If ( match.position ≠ 1 ; Left ( var.format ; match.position - 1 ) ); 39 | format.char = Middle ( var.format ; match.position ; 1 ); 40 | var.char = Left ( var.filtered ; 1 ); 41 | is.upper = IsEmpty ( Filter ( format.char ; var.match ) ); 42 | _tBit = If ( is.upper ; Upper ( var.char ) ; Lower ( var.char ) ); 43 | format.remainder = Right ( var.format ; format.length - match.position ); 44 | input.remainder = Right ( var.filtered ; filtered.length - 1 ) 45 | ]; 46 | var.prefix & _tBit & number.format ( input.remainder ; format.remainder ) 47 | ) 48 | ) 49 | ) 50 | ) 51 | ) -------------------------------------------------------------------------------- /keyboard.chars.fmfn: -------------------------------------------------------------------------------- 1 | /* 2 | * ===================================================== 3 | * keyboard.chars( character ) 4 | * 5 | * RETURNS: (string) unicode characters of keyboard symbols 6 | * PARAMETERS: @char = (string) name of the character (matches the variable name) 7 | * DEPENDENCIES: none 8 | * NOTES: 9 | * ===================================================== 10 | * 11 | */ 12 | 13 | Let ( [ 14 | wineval = "Let ( [¶ 15 | alt = TextStyleAdd ( \"alt\" ; Superscript );¶ 16 | control= TextFont ( \"ˆ\" ; \"Arial\" );¶ 17 | deleteback = TextFont ( \"Õ\" ; \"Wingdings\" );¶ 18 | deleteforward = TextFont (\"Ö\" ; \"Wingdings\" );¶ 19 | escape = TextStyleAdd ( \"esc\" ; Superscript );¶ 20 | return = TextFont (\"¿\" ; \"Symbol\" );¶ 21 | shift = TextFont ( \"ñ\" ; \"Wingdings\" )¶ 22 | ];¶" 23 | & character & 24 | ")"; 25 | 26 | maceval = "Let ( [¶ 27 | alt = \"⌥\";¶ 28 | applesymbol = \"\";¶ 29 | capslock = \"⇪\";¶ 30 | clear = \"⌧\";¶ 31 | command = \"⌘\";¶ 32 | control = \"⌃\";¶ 33 | deleteback = \"⌫\";¶ 34 | deleteforward = \"⌦\";¶ 35 | downarrow = \"⇣\"; // alternates ↓¶ 36 | eject = \"⏏\";¶ 37 | end = \"↘\"; // alternates ⇲¶ 38 | enter = \"⌤\";¶ 39 | escape = \"⎋\";¶ 40 | home = \"↖\";
// alternates ⇱ ↸¶ 41 | leftarrow = \"⇠\"; // alternates ←¶ 42 | numberlock = \"⇭\";¶ 43 | pagedown = \"⇟\";¶ 44 | pageup = \"⇞\";¶ 45 | power = \"⌽\";¶ 46 | return = \"⏎\"; // alternates ↩¶ 47 | rightarrow = \"⇢\"; // alternates →¶ 48 | shift = \"⇧\";¶ 49 | space = \"␣\";¶ 50 | tabback = \"⇤\";¶ 51 | tabforward = \"⇥\";¶ 52 | uparrow = \"⇡\" // alternates ↑¶ 53 | ];¶" 54 | & character & 55 | ")"; 56 | 57 | isWindows = Case( Get( SystemPlatform ) = -2; True ; False ); 58 | result = Case ( isWindows ; Evaluate ( wineval ) ; TextFont ( Evaluate ( maceval ) ; "Apple Symbols" ) ) 59 | ]; 60 | If ( result ≠ "?" ; result ; "No match") 61 | ) -------------------------------------------------------------------------------- /debug.fmfn: -------------------------------------------------------------------------------- 1 | /* 2 | * ===================================================== 3 | * debug() 4 | * 5 | * RETURNS: (mixed) A list of informative values 6 | * about the current FMP environment. 7 | * DEPENDANCIES: developer() function. 8 | * NOTES: While testing non developer accounts you 9 | * may need to remove off if ( developer ) 10 | * ===================================================== 11 | */ 12 | 13 | if ( developer; 14 | Let ( [ 15 | // Variables 16 | $$DEBUG = List ( 17 | "App Version = " & Abs ( Get ( ApplicationVersion ) ); 18 | "Host Name = " & Get ( HostName ); 19 | "Host IP = " & Get ( HostIPAddress ); 20 | "Location = " & Get ( FilePath ); 21 | "Account = " & Get ( AccountName ); 22 | "-----------------------------------"; 23 | "Script Param = " & Get ( ScriptParameter ); 24 | "Script Result = " & Get ( ScriptResult ); 25 | "-----------------------------------"; 26 | "Last Error = " & Get ( LastError ); 27 | "Last ODBC = " & Get ( LastODBCError ); 28 | "-----------------------------------"; 29 | "Layout = " & Get ( LayoutName ); 30 | "Layout Table = " & Get ( LayoutTableName ); 31 | "-----------------------------------"; 32 | "Open Records = " & Get ( RecordOpenCount ); 33 | "Record Modified = " & Get ( RecordModificationCount ); 34 | "-----------------------------------"; 35 | "Active Field = " & Get ( ActiveFieldName ); 36 | "Active Field Table = " & Get ( ActiveFieldTableName ); 37 | "Active Layout Object = " & Get ( ActiveLayoutObjectName ); 38 | "Active Layout Table = " & Get ( LayoutTableName ); 39 | "-----------------------------------"; 40 | "Local time = " & Get ( CurrentTimeStamp ); 41 | "Host time = " & Get ( CurrentHostTimeStamp ); 42 | "-----------------------------------"; 43 | "$counter = " & $counter; 44 | ) 45 | ]; 46 | 47 | // Return value 48 | $$DEBUG 49 | 50 | ) 51 | ) -------------------------------------------------------------------------------- /var.list.fmfn: -------------------------------------------------------------------------------- 1 | /* 2 | * ===================================================== 3 | * var.list ( keys; values; scope ) 4 | * 5 | * RETURNS: (string) key/value pairs as $key = value in positional order 6 | * PARAMETERS: @keys = List() of key names 7 | * @values = List() of values 8 | * @scope = (enum) global, local 9 | * DEPENDENCIES: none 10 | * ABOUT: Takes two compliment set of lists 11 | * and turns them into FMP defined variables 12 | * NOTES: Use FileMaker's Quote() function when passing 13 | * in string values - otherwise numeric are fine! 14 | * ===================================================== 15 | * 16 | */ 17 | 18 | Let ( [ 19 | singleValue = ValueCount ( keys ) = 1 and ValueCount ( values ) = 1; // only one key/value pair 20 | isValid = ValueCount ( keys ) = ValueCount ( values ); // both lists have the same number of values 21 | notEmpty = not IsEmpty( keys ) and not IsEmpty ( values ); // lists aren't empty 22 | value = GetValue ( values ; 1 ); 23 | valueIsNumber = Length ( GetAsNumber ( Filter ( value; "0123456789.-" ) ) ) = Length ( value ); 24 | valueIsTimestamp = GetAsTimestamp ( value ) = value; // doesn't work 25 | valueIsDate = GetAsDate ( value ) = value; // doesn't work 26 | valueIsTime = GetAsTime ( value ) = value; // doesn't work 27 | value = Case ( 28 | valueIsNumber; 29 | value; 30 | 31 | Quote ( value ) // default for all strings 32 | ); 33 | varString = If ( scope = "global" ; "$$" ; "$" ) & GetValue ( keys ; 1 ) & " = " & value & If ( ValueCount( keys ) = 1; "" ; ";¶" ) 34 | ]; 35 | Case ( 36 | singleValue; 37 | varString; 38 | 39 | isValid and notEmpty; 40 | varString & 41 | var.list ( MiddleValues ( keys ; 2 ; 1000000 ) ; MiddleValues ( values ; 2 ; 1000000 ) ; scope ); 42 | 43 | // default 44 | "ERROR » Lists don't match in length" 45 | ) 46 | ) 47 | 48 | /* 49 | // Testing code 50 | var.list ( 51 | List( 52 | "foo"; 53 | "bar" 54 | ) 55 | ; 56 | List( 57 | "single value"; 58 | "multiple¶values" 59 | ) 60 | ; "local" ) 61 | */ -------------------------------------------------------------------------------- /script.param.get.fmfn: -------------------------------------------------------------------------------- 1 | /* 2 | * ===================================================== 3 | * script.param.get( values; key) 4 | * 5 | * RETURNS: (mixed) the value associated to the supplied key 6 | * DEPENDENCIES: none 7 | * NOTES: Compliments to Mikhail Edoshin http://mikhailedoshin.com 8 | * ===================================================== 9 | * 10 | * This function is used to extract a named value from a 11 | * data structure which preserves carriage returns and 12 | * other special characters. 13 | * 14 | */ 15 | 16 | Case( 17 | IsEmpty( values ) or IsEmpty( key ); 18 | ""; 19 | 20 | Let( 21 | [ 22 | //------------------------- VARIABLES 23 | 24 | var.UnwrappedValues = Substitute( values; 25 | [ "¶"; "" ]; 26 | [ ".^^"; "¶" ]; 27 | [ ".^"; "^" ] ); 28 | var.KeyPosition = Position( "¶" & values; "¶.^^" & key & ".^^"; 1; 1 ) 29 | ]; 30 | 31 | //------------------------- RESULT 32 | 33 | If( var.KeyPosition = 0; ""; 34 | Let( 35 | [ 36 | //------------------------- VARIABLES 37 | 38 | var.ValuePosition = ValueCount( Left( values; var.KeyPosition ) ); 39 | var.OptionString = MiddleValues( values; var.ValuePosition; 1 ); 40 | var.Start = Position( var.OptionString; ".^^"; 1; 2 ) + Length( ".^^" ); 41 | var.End = Position( var.OptionString; ".^^"; 1; 3 ) 42 | ]; 43 | 44 | //------------------------- RESULT 45 | 46 | Substitute( Middle( var.OptionString; var.Start; var.End - var.Start ); 47 | [ ".^^"; "¶" ]; 48 | [ ".^"; "^" ]; 49 | [ "¶"; "" ]; 50 | [ ".^^"; "¶" ]; 51 | [ ".^"; "^" ] ) 52 | ) 53 | ) 54 | ) 55 | ) 56 | -------------------------------------------------------------------------------- /html.checkbox.fmfn: -------------------------------------------------------------------------------- 1 | /* 2 | ===================================================== 3 | html.checkbox ( string ; variable ) 4 | 5 | RETURNS: (html) A single html checkbox based on the boolean value of a variable 6 | DEPENDENCIES: none 7 | VERSION: 1.0 8 | AUTHOR: Matt Petrowsky 9 | NOTES: Advanced developers can do cool stuff with Javascript/DOM and using hidden values 10 | 11 | ===================================================== 12 | */ 13 | 14 | Let( 15 | 16 | var.checked = Evaluate( "$$" & variable ); 17 | 18 | List( 19 | "data:text/html,"; 20 | ""; 22 | ""; // Define the font here 23 | ""; 24 | "Single Checkbox"; 25 | ""; 26 | ""; // No wrap and overflow settings 27 | "
"; 28 | ""; 29 | string; 30 | ""; 31 | "
"; 32 | ""; 33 | "" 34 | ) 35 | 36 | ) 37 | 38 | 39 | 40 | /* 41 | 42 | FOR SUPER ADVANCED DEVELOPERS ONLY! 43 | 44 | If, for some reason, you want to use multiple checkboxes and you want to know the value of each 45 | you'll need to use a more advanced implementation of hiding text at the end of the input options 46 | 47 | Javascript would be used to change the value trailing the option - the following will do a toggle within the web page 48 | ""; 49 | 50 | Option would look like this. Using a trailing span, you can store the value and use a very hackish way of getting the values using Select All -> Copy 51 | "" & var.initial & ""; 52 | 53 | Other variables would be helpful 54 | var.initial = If ( var.checked ; "true" ; "false" ); 55 | var.script = If ( var.checked ; "" ; "" ) 56 | 57 | */ 58 | -------------------------------------------------------------------------------- /fmstandards/ErrorReference.fmfn: -------------------------------------------------------------------------------- 1 | /** 2 | * ===================================================== 3 | * ErrorReference ( elementName ; requiredElementNames ; requiredType ) 4 | * 5 | * PARAMETERS: 6 | * @elementName = FileMaker field reference 7 | * @requiredElementNames = List() of required element names 8 | * @requiredTypes = matching list of types for each element 9 | * RETURNS: 10 | * (boolean) Whether or not there is a calculation error 11 | * DEPENDENCIES: 12 | * none 13 | * NOTES: 14 | * References, at time of evaluation, are based on 15 | * current context. 16 | * ===================================================== 17 | * 18 | */ 19 | 20 | Let ( [ 21 | $$ERROR.REFERENCE = ""; // always start as if there is no error 22 | // Set a global variable with match values - only make function calls on first iteration 23 | $$TEMP = If ( IsEmpty ( $$TEMP ); 24 | FilterValues ( 25 | Case ( 26 | requiredType = "Table"; 27 | TableNames ( Get ( FileName ) ); 28 | 29 | requiredType = "Field"; 30 | FieldNames ( Get ( FileName ) ; Get ( LayoutName ) ); 31 | 32 | requiredType = "Layout"; 33 | LayoutNames ( Get ( FileName ) ); 34 | 35 | requiredType = "Object"; 36 | LayoutObjectNames ( Get ( FileName ) ; Get ( LayoutName ) ); 37 | 38 | requiredType = "Window"; 39 | WindowNames ( Get ( FileName ) ); 40 | 41 | "" 42 | ); requiredElementNames ); 43 | // Else 44 | $$TEMP 45 | ); 46 | 47 | var.topValue = GetValue ( requiredElementNames ; 1 ); 48 | var.valueExists = PatternCount ( $$TEMP; var.topValue ) ≥ 1 or var.topValue = ""; 49 | var.remaingValues = RightValues ( requiredElementNames ; ValueCount ( requiredElementNames ) - 1 ) 50 | // The following line is used for debugging 51 | //$$ITERATIONS = List ( $$ITERATIONS ; List ( "top:¶" & Quote ( var.topValue ) ; "exists:¶" & var.valueExists ; "remains:¶" & Quote ( var.remaingValues ) ; "match:¶" & $$TEMP ; "----------------") ) 52 | ]; 53 | If ( not var.valueExists; // return the error 54 | Let ( [ 55 | $$TEMP = ""; // reset the temp variable 56 | $$ERROR.REFERENCE = elementName & ": Missing literal references" 57 | ]; 58 | True 59 | ); 60 | 61 | // Else 62 | 63 | If ( var.remaingValues = ""; 64 | Let ( $$TEMP = "" ; False ); 65 | ErrorReference ( elementName ; var.remaingValues ; requiredType ) 66 | ) 67 | ) 68 | ) -------------------------------------------------------------------------------- /file.setpath.fmfn: -------------------------------------------------------------------------------- 1 | /* 2 | * ===================================================== 3 | * file.setpath ( location ; subfolder ) 4 | * 5 | * RETURNS: (string) path to file/folder location. 6 | * PARAMS location = (string) name of the folder location 7 | * subfolder = (string) any subfolders you wish to append 8 | * DEPS: none 9 | * NOTES: none 10 | * ===================================================== 11 | * 12 | */ 13 | 14 | Let ([ 15 | _Windows = Abs( Get( SystemPlatform ) ) - 1; 16 | 17 | // Paths are returned via an evaluate using the inbound 'location' var as a reference to the named Let() var 18 | _Paths = "Let( [ 19 | isWindows = Abs( Get( SystemPlatform ) ) - 1; 20 | isXP = isWindows and Get ( SystemVersion ) < 6; 21 | _Desktop = Get( DesktopPath ); 22 | _Documents = Get( DocumentsPath ); 23 | _Application = Get( FileMakerPath ); 24 | _File = Substitute( Get( FilePath ) ; \"file:\" ; \"\" ); 25 | _Preferences = Get( PreferencesPath ); 26 | _Temp = If ( isWindows ; Substitute ( _Documents ; \"My Documents\" ; \"Local Settings\" ) & \"Temp/\"; \"/private/tmp/\" ); 27 | _Extensions = Get( FileMakerPath ) & \"Extensions/\"; 28 | path.win = If ( isXP ; \"/Local Settings/Application Data/FileMaker/Extensions/\" ; \"/AppData/Local/FileMaker/Extensions/\" ); 29 | path.mac = \"/Library/Application Support/FileMaker/Extensions/\"; 30 | _SharedExt = Substitute ( _Desktop ; \"/Desktop/\"; If ( isWindows ; path.win ; path.mac ) ); 31 | _FMTemp = Get ( TemporaryPath ) 32 | 33 | ];¶_" & 34 | location 35 | & ")"; 36 | 37 | // Only these values for folder are possible 38 | _Match = "Desktop Documents Application File Preferences Temp Extensions SharedExt FMTemp"; 39 | 40 | // Generate the actual path 41 | _Path = If ( PatternCount( _Match ; location ); Evaluate( _Paths ) ; location ) & If ( not IsEmpty( subfolder ); subfolder ); 42 | 43 | // Remove the filename 44 | _File = Get( FileName ) & ".fp7"; 45 | _HasFile = PatternCount( _Path; _File ); 46 | _Path = If ( _HasFile; Substitute( _Path; _File; ""); _Path ) 47 | 48 | ]; 49 | 50 | _Path 51 | 52 | ) 53 | 54 | /* 55 | //Unit test 56 | List ( 57 | file.setpath ( "Desktop" ; "" ); 58 | file.setpath ( "Documents" ; "" ); 59 | file.setpath ( "Application" ; "" ); 60 | file.setpath ( "File" ; "" ); 61 | file.setpath ( "Preferences" ; "" ); 62 | file.setpath ( "Temp" ; "" ); 63 | file.setpath ( "Extensions" ; "" ); 64 | file.setpath ( "SharedExt" ; "" ); 65 | file.setpath ( "FMTemp" ; "" ); 66 | ) 67 | */ -------------------------------------------------------------------------------- /list.addRemove.fmfn: -------------------------------------------------------------------------------- 1 | /* 2 | * list.addRemove ( value; valuelist ) 3 | * RETURNS: ( string ) The supplied list with the value either removed or concatenated 4 | * 5 | */ 6 | 7 | Let ( [ //------------------------- VARIABLES 8 | 9 | var.list = Substitute ( If ( Left ( valuelist; 1 ) ≠ ¶; "^") & valuelist & "^"; ¶; "^^" ); // List minus all returns 10 | var.match = "^" & value & "^"; // Value to match 11 | var.exists = PatternCount ( var.list; var.match ) 12 | 13 | ]; //------------------------- RESULT 14 | 15 | Case ( 16 | var.exists; 17 | Let ( [ 18 | var.removed = Substitute ( var.list; var.match; "" ); // Kill matching values 19 | var.list = Substitute ( var.removed; "^^"; ¶ ); // Put returns back in 20 | var.list = Middle ( var.list; 2; 100000 ); // Trim leading ^ 21 | var.list = Left ( var.list; Length ( var.list ) - 1 ); // Trim trailing ^ 22 | var.cleaned = Substitute ( var.list; ¶; "" ); // Remove returns for trimming 23 | var.char.first = Position ( var.list; Left ( var.cleaned; 1 ); 0; 1 ); // Position of firt non cleaned char 24 | var.char.last = Position ( var.list; Right ( var.cleaned; 1 ); Length ( var.list ); -1 ) // Position of last non cleaned char 25 | ]; 26 | Middle ( var.list; var.char.first; var.char.last - var.char.first + 1 ) 27 | ); 28 | 29 | If ( valuelist ≠ ""; valuelist & ¶ ) & value // default 30 | ) 31 | 32 | ) 33 | 34 | /* 35 | * Unit tests 36 | */ 37 | 38 | /* 39 | Let ( 40 | _ = "--------------------"; 41 | 42 | List ( 43 | List ( "At beginning ( one )"; _; list.addRemove ( "one"; List ( "one"; "two"; "three" ) ); _ ); 44 | List ( "At ending ( three )"; _; list.addRemove ( "three"; List ( "one"; "two"; "three" ) ); _ ); 45 | List ( "With multiple spread out ( one )"; _; list.addRemove ( "one"; List ( "one"; "two"; "one"; "three" ) ); _ ); 46 | List ( "With multiple at beginning ( one )"; _; list.addRemove ( "one"; List ( "one"; "one"; "two"; "three" ) ); _ ); 47 | List ( "With multiple at ending ( three )"; _; list.addRemove ( "three"; List ( "one"; "two"; "three"; "three" ) ); _ ); 48 | List ( "With multiple in middle ( two )"; _; list.addRemove ( "two"; List ( "one"; "two"; "two"; "three" ) ); _ ); 49 | List ( "With even returns at start ( two )"; _; list.addRemove ( "two"; "¶¶¶¶one¶two¶three" ); _ ); 50 | List ( "With odd returns at start ( two )"; _; list.addRemove ( "two"; "¶¶¶one¶two¶three" ); _ ); 51 | List ( "With even returns at end ( two )"; _; list.addRemove ( "two"; "one¶two¶three¶¶¶¶" ); _ ); 52 | List ( "With odd returns at end ( two )"; _; list.addRemove ( "two"; "one¶two¶three¶¶¶" ); _ ); 53 | List ( "Without value ( one )"; _; list.addRemove ( "one"; List ( "two"; "three" ) ); _ ); 54 | ) 55 | ) 56 | */ -------------------------------------------------------------------------------- /var.eval.fmfn: -------------------------------------------------------------------------------- 1 | /* 2 | * ===================================================== 3 | * var.eval( contents ) 4 | * 5 | * RETURNS: (bool) True or False based on proper evaluation 6 | * DEPENDENCIES: none 7 | * NOTES: Try to always use the List() function when passing multiple values into var.eval! 8 | * $checkScriptName is a reserved local variable used when you wish to enforce 9 | * that inbound parameters match the name of the script. It is reset after the 10 | * evaluation. 11 | * RELEASE: 100909 12 | * ===================================================== 13 | * 14 | * This custom function evaluates contents passed as the 15 | * variable declaration part of a Let function. Useful for 16 | * defining global or locally scoped variables. 17 | * 18 | */ 19 | 20 | Let( [ 21 | $$ERROR.VAR.EVAL = ""; // Reset global error variable for evaluation 22 | 23 | var.empty = IsEmpty ( contents ); // test for empty contents 24 | var.semicolons_exist = ( ValueCount ( contents ) = PatternCount ( contents ; ";¶" ) ) or ( ValueCount ( contents ) - 1 = PatternCount ( contents ; ";¶" ) ); 25 | var.contents = If ( not var.semicolons_exist ; Substitute ( contents ; ¶ ; ";¶") ; contents ); // Add semicolons if needed 26 | var.trailing_semicolon = Right ( var.contents ; 1 ) = ";"; // Clean off the trailing semicolon if present 27 | var.contents = If ( var.trailing_semicolon; Left ( var.contents ; Length( var.contents ) - 1 ); var.contents ); 28 | 29 | var.script = Get ( ScriptName ); 30 | var.begin = "(" ; // beginning of parameters definition 31 | var.break = "," ; // regular parameters separator (and) 32 | var.end = ")" ; // end of parameters definition 33 | 34 | var.parameters = Middle ( var.script ; 35 | Position ( var.script ; var.begin ; 1 ; 1 ) +1; 36 | Position ( var.script ; var.end ; Length ( var.script ) ; -1 ) - Position ( var.script ; var.begin ; 1 ; 1 ) -1 37 | ); // raw parameters 38 | // ONLY ALLOWED CHARACTERS FOR SCRIPT NAME PARAMERS ARE THE FOLLOWING (minus the ¶) 39 | var.parameters = Filter ( Substitute ( var.parameters ; var.break ; ¶ ) ; "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_¶" ); 40 | var.expected = "$" & Substitute ( var.parameters ; ¶ ; "¶$" ); 41 | var.matching = FilterValues ( Substitute( var.contents; "="; ¶ ) ; var.expected ); // matching parameters found in ScriptName 42 | var.matching = If ( Right ( var.matching; 1 ) = "¶" ; Left ( var.matching ; Length ( var.matching) - 1) ); // replace extra return added by FilterValues function 43 | var.missingParams = If ( $checkScriptName = True ; var.matching ≠ var.expected ; False ) 44 | ]; 45 | 46 | If ( Evaluate( "Let ( [" & var.contents & "]; False )" ) = "?" // generates an error or 47 | or var.empty or var.missingParams; // empty contents or missing expected parameters 48 | // Return error result 49 | Let ( [ 50 | $$ERROR.VAR.EVAL = 51 | Case ( 52 | var.empty ; "EMPTY PARAMETER SET" ; 53 | var.missingParams ; "MISSING EXPECTED PARAMETERS PER SCRIPT NAME"; 54 | List ( "ERROR »"; var.contents ) 55 | ); 56 | $checkScriptName = "" // reset the $checkScriptName variable (after setting error) 57 | ]; 58 | False 59 | ); 60 | Let ( $checkScriptName = "" ; True ) 61 | ) 62 | 63 | ) -------------------------------------------------------------------------------- /script.execute.fmfn: -------------------------------------------------------------------------------- 1 | /* 2 | * ===================================================== 3 | * script.execute( method; file; script; parameter; control ) 4 | * 5 | * RETURNS: (bool) Result of the execution of a script. 6 | * DEPS: At least one Script triggering plugin. 7 | * Supported are SmartPill PHP plugin, zippScript & DoScript 8 | * NOTES: 9 | * ===================================================== 10 | */ 11 | 12 | If ( Get ( WindowMode ) = 0; 13 | Let([ 14 | //------------------------- VARIABLES 15 | 16 | _Default = "sm"; // Set your default script plugin here 17 | 18 | _File = If (IsEmpty(file); Get(FileName); file); 19 | _Control = If (IsEmpty(control); "pause"; control); 20 | method = If (IsEmpty(method); _Default; method); 21 | 22 | _Error = Case( 23 | 24 | method = "zipp"; Get ( ScriptName ) = script or Let ( [ _Result = zippScript_PerformScript( _File; script; parameter; _Control ); 25 | $$SCRIPT.TEXT = Case( 26 | _Result = "" ; "" ; 27 | _Result = "$$-1" ; "Incorrect number of parameters" ; 28 | _Result = "$$-2" ; "Unknown control parameter code" ; 29 | _Result = "$$100" ; "File is missing (named file not open)" ; 30 | _Result = "$$104" ; "Script is missing (no script of that name)") 31 | ]; "" ); 32 | 33 | method = "dosc"; Get ( ScriptName ) = script or mFMb_DoScript( script ; _File ; parameter ; _Control ) & mFMb_DS_LastErrNum; 34 | 35 | method = "sm"; Get ( ScriptName ) = script 36 | 37 | or 38 | 39 | EvaluateGroovy( 40 | Substitute ( 41 | "fmpro.performScript( ':file', ':script', \":parameter\" )"; 42 | [":file"; Get( FileName )]; 43 | [":script"; script]; 44 | [":parameter"; Substitute ( parameter; ["\""; "\\\""]; [ ¶; "\r"]; [ "$"; "\$"] )] 45 | ) 46 | ); 47 | 48 | method = "php"; 49 | 50 | Let([ 51 | //------------------------- VARIABLES 52 | _Control = If ( IsEmpty (control); 53 | 3; // 3 is the value used by fm_perform_script for pause 54 | Let( val = Left( control; 4); Int( Position ( "halt exit resu paus " ; val ; 1 ; 1 ) / 5 ) ) ); 55 | 56 | _PHPCode = "print fm_perform_script(" 57 | & Quote( _File ) & ", " 58 | & Quote( script ) & ", " 59 | & Quote( parameter ) & ", " 60 | & _Control & 61 | ");"; 62 | 63 | $$SCRIPT_RESULT = Get ( ScriptName ) = script or PHP_Execute( _PHPCode ) 64 | 65 | ]; 66 | //------------------------- RESULT 67 | 68 | 0 // I haven't put error trapping in yet. :( 69 | 70 | ); 71 | 72 | 0 73 | ) 74 | ]; 75 | 76 | If ( _Error; 77 | Let( $$SCRIPT.ERROR = _Error; 1 ) 78 | ) 79 | 80 | ) 81 | ) 82 | 83 | // Thanks Koen Van Hulle for the window mode suggestion 84 | // Thanks Peter Wagemans for the "aha" in logic for testing for the name of the currently running script and the error trapping -------------------------------------------------------------------------------- /list.sort.fmfn: -------------------------------------------------------------------------------- 1 | /* 2 | ===================================================== 3 | list.sort( values; direction; type ) 4 | 5 | RETURNS: (list) sorted version of the list 6 | DEPENDENCIES: 7 | VERSION: 1.0 8 | AUTHOR: See notes 9 | NOTES: 10 | 11 | Function uses a lot of recursion - be aware of list 12 | size limitations! 13 | 14 | Adapted and combined by Matt Petrowsky from 15 | http://www.briandunning.com/cf/593 and 16 | http://www.briandunning.com/cf/594 17 | 18 | Compliments to Shaun Flisakowski & Mogens Brun 19 | ===================================================== 20 | */ 21 | 22 | Let( divider = "||"; // Define the delimiter to divide the two halfs of the whole inbound list(should not be a possible list character) 23 | 24 | Case( 25 | 26 | PatternCount( values; divider ) = 1; // If the two lists contains the designated divider we'll run the sorting subfunction. 27 | 28 | // START SUBFUNCTION 29 | 30 | Let([ 31 | list.splitpoint = Position( values; divider; 1; 1 ); // Point where list is combined by divider 32 | values.left = Left( values; list.splitpoint - 1 ); // Real left side list 33 | values.right = Right( values; Length( values ) - ( list.splitpoint + Length( divider ) - 1 ) ); // Real right side list 34 | $left.size = ValueCount( values.left ); 35 | $right.size = ValueCount( values.right ) 36 | ]; 37 | Case( 38 | $left.size = 0; values.right; 39 | $right.size = 0; values.left; 40 | 41 | Let([ 42 | $value.left = LeftValues( values.left; 1 ); 43 | $value.right = LeftValues( values.right; 1 ); 44 | $left.compare = Case( 45 | type = "Number"; Let( $right.compare = GetAsNumber( $value.right ); GetAsNumber( $value.left ) ); 46 | type = "Time"; Let( $right.compare = GetAsTime( $value.right ); GetAsTime( $value.left ) ); 47 | type = "Date"; Let( $right.compare = GetAsDate( $value.right ); GetAsDate( $value.left ) ); 48 | // Default 49 | Let( $right.compare = $value.right; $value.left ) 50 | ) 51 | ]; 52 | // Determine sort direction based on supplied parameter. Default to Ascending as most common 53 | 54 | If( If( direction = "Dsc"; $left.compare ≥ $right.compare; $left.compare < $right.compare ); 55 | $value.left & list.sort( RightValues( values.left; $left.size - 1 ) & divider & values.right; direction; type ); 56 | $value.right & list.sort( values.left & divider & RightValues( values.right; $right.size - 1 ); direction; type ) 57 | ) 58 | ) 59 | ) 60 | ); 61 | 62 | // MAIN SORTING FUNCTION 63 | 64 | Let( 65 | $list.size = ValueCount( values ); 66 | 67 | If( $list.size ≤ 1; 68 | values; 69 | 70 | Let( [ 71 | list.splitpoint = Div( $list.size; 2 ); // Get dividing point to split whole list in half 72 | left.list = LeftValues( values; list.splitpoint ); // Left half of the list 73 | right.list = RightValues( values; $list.size - list.splitpoint ) // Right half of the list 74 | ]; 75 | // Walk through the two lists comparing values 76 | list.sort( list.sort( left.list; direction; type ) & divider & list.sort(right.list; direction; type ); direction; type ) 77 | ) 78 | ) 79 | ) 80 | ) 81 | ) -------------------------------------------------------------------------------- /list.filter.fmfn: -------------------------------------------------------------------------------- 1 | /* FilterList ( ListA ; Attribute ; ListB ; CaseSensitive ) .v2.0 2 | FilterList () requires CustomList ( Start ; End ; Function )*/ 3 | 4 | // Limited => ListA < 18700 values and ListB < 18700 values too 5 | // Attributes : Equals - NotEquals - Contains - NotContains - BeginsWith - NotBeginsWith - EndsWith - NotEndsWith 6 | // Optional parameters : CaseSensitive : Boolean 7 | 8 | // Result 9 | /* --------- CaseSensitive = empty or 0 10 | FilterList ( "One¶Two¶three¶Four¶five" ; "Equals" ; "One¶four" ; "" or 0 ) => "One¶Four" 11 | FilterList ( "One¶Two¶three¶Four¶five" ; "NotEquals" ; "One¶four" ; "" or 0 ) => "Two¶three¶five" 12 | FilterList ( "One¶Two¶three¶Four¶five" ; "Contains" ; "O¶t" ; "" or 0 ) => "One¶Two¶three¶Four" 13 | FilterList ( "One¶Two¶three¶Four¶five" ; "NotContains" ; "O¶t" ; "" or 0 ) => "five" 14 | FilterList ( "One¶Two¶three¶Four¶five" ; "BeginsWith" ; "F¶t" ; "" or 0 ) => "Two¶three¶Four¶five" 15 | FilterList ( "One¶Two¶three¶Four¶five" ; "NotBeginsWith" ; "F¶t" ; "" or 0 ) => "One" 16 | FilterList ( "One¶Two¶three¶Four¶five" ; "EndsWith" ; "o¶E" ; "" or 0 ) => "One¶Two¶three¶five" 17 | FilterList ( "One¶Two¶three¶Four¶five" ; "NotEndsWith" ; "o¶E" ; "" or 0 ) => "Four" 18 | 19 | */ 20 | /* --------- CaseSensitive = 1 21 | FilterList ( "One¶Two¶three¶Four¶five" ; "Equals" ; "One¶four" ; 1 ) => "One" 22 | FilterList ( "One¶Two¶three¶Four¶five" ; "NotEquals" ; "One¶four" ; 1 ) => "Two¶three¶Four¶five" 23 | FilterList ( "One¶Two¶three¶Four¶five" ; "Contains" ; "O¶t" ; 1 ) => "One¶three" 24 | FilterList ( "One¶Two¶three¶Four¶five" ; "NotContains" ; "O¶t" ; 1 ) => "Two¶Four¶five" 25 | FilterList ( "One¶Two¶three¶Four¶five" ; "BeginsWith" ; "F¶t" ; 1 ) => "three¶Four" 26 | FilterList ( "One¶Two¶three¶Four¶five" ; "NotBeginsWith" ; "F¶t" ; 1 ) => "One¶Two¶five" 27 | FilterList ( "One¶Two¶three¶Four¶five" ; "EndsWith" ; "o¶E" ; 1 ) => "Two" 28 | FilterList ( "One¶Two¶three¶Four¶five" ; "NotEndsWith" ; "o¶E" ; 1 ) => "One¶three¶Four¶five" 29 | */ 30 | 31 | //-------------------------------------------------------------------*/ 32 | // Agnès Barouh - Juillet 2007 - To report bugs : : barouh.agnes@wanadoo.fr 33 | //-------------------------------------------------------------------*/ 34 | 35 | Case ( 36 | ValueCount ( ListA ) > 18700 or ValueCount ( ListB ) > 18700 ; "Too many Values" ; 37 | IsEmpty ( ListA ) ; "" ; 38 | IsEmpty ( ListB ) ; ListA ; 39 | IsEmpty ( Attribute ) ; "Missing Attribute" ; 40 | IsEmpty ( FilterValues ( Attribute ; "Equals¶NotEquals¶Contains¶NotContains¶BeginsWith¶NotBeginsWith¶EndsWith¶NotEndsWith" ) ) ; "Incorrect attribute" ; 41 | not ( CaseSensitive = 1 or ( GetAsNumber ( CaseSensitive + 0 ) = 0 ) ) ; "Incorrect CaseSensitive" ; 42 | Attribute = "Equals" and CaseSensitive < 1 ; Substitute ( FilterValues ( ListA ; ListB ) & "#|#" ; ["¶#|#" ; "" ]; ["#|#" ; "" ]) ; 43 | 44 | Let ([ 45 | 46 | $TagB = Case ( IsEmpty ( FilterValues ( Attribute ; "Equals¶NotEquals¶BeginsWith¶NotBeginsWith" ) ) ; "" ; "#|#" ) ; 47 | $TagE = Case ( IsEmpty ( FilterValues ( Attribute ; "Equals¶NotEquals¶EndsWith¶NotEndsWith" ) ) ; "" ; "#|#" ) ; 48 | 49 | $MyFirstList = ListA ; 50 | $MyList = "[#|#]" & $TagB & Substitute ( Choose ( CaseSensitive ; Upper ( $MyFirstList ) ; $MyFirstList ) ; [ ¶ ; $TagE & ¶ & "[#|#]" & $TagB ] ) & $TagE ; 51 | $Values = Choose ( CaseSensitive ; Upper ( ListB ) ; ListB ) ; 52 | 53 | Trigger = CustomList ( 1 ; ValueCount ( ListB ) ; 54 | "Let ([Value = GetValue ( $Values ; [n] ) ; $MyList = case ( Not IsEmpty ( value ) ; Substitute ( $MyList ; $TagB & Value & $TagE ; \"X##X\" ) ; $MyList ) ] ; \"\" )" ) ; 55 | 56 | $MyList = Substitute ( $MyList ; [ "[#|#]"; "" ] ; [ "#|#"; "" ] ) ; 57 | $Test = Case ( Left ( Attribute ; 3 ) = "Not" ; "<1" ; ">0") 58 | ]; 59 | 60 | Case ( 61 | CaseSensitive < 1 and Left ( Attribute ; 3 ) = "Not" ; Substitute ( FilterValues ( $MyFirstList ; $MyList ) & "#|#" ; ["¶#|#" ; "" ] ; ["#|#" ; "" ]) ; 62 | 63 | CustomList ( 1 ; ValueCount ( $MyList ) ; 64 | "Let ([ Value = GetValue ( $MyList ; [n] ) ]; Case ( PatternCount ( Value ; \"X##X\")" & $Test & " ; GetValue ( $MyFirstList ; [n] )))" ) 65 | ) 66 | ) 67 | ) & Let( [ $MyFirstList = "" ; $MyList = "" ; $Values = ""] ; "" ) -------------------------------------------------------------------------------- /script.parameters.assign.fmfn: -------------------------------------------------------------------------------- 1 | /* 2 | ===================================================== 3 | script.parameters.assign ( ) 4 | RETURNS: (vars) Creates local script variables 5 | based on the name of the script 6 | DEPENDENCIES: script.param.get, script.param.set 7 | NOTES: Compliments to Alexander Zueiv 8 | ===================================================== 9 | by Alexander Zueiv 10 | October 1, 2006 11 | Adapted by Matt Petrowsky 12 | 13 | ABOUT: This function defines a local variable ($) for every 14 | parameter specified in the current script's name. To refer 15 | to any parameter within the script you only need to 16 | specify its name with a leading $. It also validates every 17 | "required" parameter for "not IsEmpty()" and returns "1" if 18 | all assignments succeeded. 19 | 20 | This function does not parse script parameters itself. It is 21 | a "wrapper" for your own custom parameter functions; it's 22 | supposed to call the custom function you currently use 23 | to get a parameter's value by its name. This should work 24 | well whatever method of sending parameters you currently 25 | use. FMP will show you where to put your function call when 26 | you click OK to save this function. 27 | 28 | Script name format: 29 | 30 | Cool Script [ paramA ; ( paramB | paramC ) {; paramD } ] 31 | 32 | Script code sample: 33 | 34 | If [ script.parameters.assign ] 35 | Set Field [ Field1 ; $paramA ] 36 | Set Field [ Field2 ; If ( IsEmpty ( $paramB ) ; $paramC ; $paramB ) ] 37 | If [ not IsEmpty ( $paramD ) ] 38 | Set Field [ Field3 ; $paramD ] 39 | End If 40 | Else 41 | Show Custom Dialog [ Get ( ScriptName ) ; "Error! Some script parameter is missing." ] 42 | End If 43 | 44 | This script will work only if ( paramA and ( paramB or paramC ) ) aren't empty, otherwise it will show 45 | the error message. 46 | 47 | To specify required validation logic for "alternative" 48 | parameters they must be enclosed in parentheses. The 49 | "optional" parameters section {} must appear at the end. 50 | 51 | */ 52 | //---------------------------------------------------------------------------------------// 53 | 54 | Let ( [ 55 | 56 | // Adjust these variables to match your scripts naming style 57 | 58 | VAR.BEGIN = "[" ; // beginning of parameters definition 59 | VAR.BREAK = ";" ; // regular parameters separator (and) 60 | VAR.ALT = "|" ; // alternative parameters separator (or) 61 | VAR.OPTION = "{" ; // beginning of optional parameters section 62 | VAR.END = "]" ; // end of parameters definition 63 | 64 | VAR.SCRIPT = Get ( ScriptName ) ; 65 | 66 | VAR.PARAMS = Middle ( VAR.SCRIPT ; 67 | Position ( VAR.SCRIPT ; VAR.BEGIN ; 1 ; 1 ) +1 ; 68 | Position ( VAR.SCRIPT ; VAR.END ; Length ( VAR.SCRIPT ) ; -1 ) - Position ( VAR.SCRIPT ; VAR.BEGIN ; 1 ; 1 ) -1 ) 69 | 70 | ] ; 71 | 72 | Case ( 73 | 74 | //---------------------------------------------------------------------------------------// 75 | 76 | $VAR.SCRIPT_PARAMETERS_COUNTER >= 1 ; 77 | 78 | Let ( [ 79 | 80 | VAR.NAME = MiddleWords ( VAR.PARAMS ; $VAR.SCRIPT_PARAMETERS_COUNTER ; 1 ) ; 81 | $VAR.SCRIPT_PARAMETERS_COUNTER = $VAR.SCRIPT_PARAMETERS_COUNTER - 1 ; 82 | 83 | // Put your function call for getting script parameters here 84 | 85 | VAR.VALUE = script.param.get ( Get( ScriptParameter ); VAR.NAME ) 86 | 87 | ] ; 88 | 89 | Evaluate ( "Let($" & VAR.NAME & "=" & Quote ( VAR.VALUE ) & ";\"\")" ) & 90 | 91 | If ( $VAR.SCRIPT_PARAMETERS_COUNTER >= 1 ; script.parameters.assign ) // recursive call to this function 92 | 93 | ) ; // End Let 94 | 95 | //---------------------------------------------------------------------------------------// 96 | 97 | WordCount ( VAR.PARAMS ) >= 1 ; 98 | 99 | Let ( [ 100 | 101 | $VAR.SCRIPT_PARAMETERS_COUNTER = WordCount ( VAR.PARAMS ) ; 102 | TEMP = script.parameters.assign ; // recursive call to this function 103 | TEMP = Substitute ( VAR.PARAMS ; [ " " ; "" ] ; [ "$" ; "" ] ) ; 104 | TEMP = If ( Position ( TEMP ; VAR.OPTION ; 1 ; 1 ) ; Left ( TEMP ; Position ( TEMP ; VAR.OPTION ; 1 ; 1 ) -1 ) ; TEMP ) ; 105 | TEMP = If ( Left ( TEMP ; 1 ) = "(" ; "(not IsEmpty($" & Right ( TEMP ; Length ( TEMP ) -1 ) ; "not IsEmpty($" & TEMP ) ; 106 | VAR.VALIDATION = Substitute ( TEMP ; 107 | [ VAR.BREAK & "(" ; ") and (not IsEmpty($" ] ; 108 | [ VAR.BREAK ; ") and not IsEmpty($" ] ; 109 | [ VAR.ALT ; ") or not IsEmpty($" ] ) & ")" ; 110 | VAR.RESULT = If ( VAR.VALIDATION = "not IsEmpty($)" ; 1 ; Evaluate ( VAR.VALIDATION ) ) 111 | 112 | ] ; 113 | 114 | VAR.RESULT 115 | 116 | ) // End Let 117 | 118 | //---------------------------------------------------------------------------------------// 119 | 120 | ) // End Case 121 | 122 | ) // End Let 123 | -------------------------------------------------------------------------------- /date.ranges.fmfn: -------------------------------------------------------------------------------- 1 | /* 2 | ===================================================== 3 | date.ranges( string; separator; withtime ) 4 | 5 | RETURNS: (string) FileMaker specific date range string. 6 | DEPS: none 7 | NOTES: The withtime value is a bool which will account for adding 24 8 | hours when used with a timestamp field type. Normal FMP dates 9 | assume 12:00 AM with just a raw date 10 | ===================================================== 11 | */ 12 | Let( 13 | [ 14 | _Now = Get( CurrentDate ); 15 | _TimeValue = If ( withtime; 1; 0) // Option for adding a day to the end date because fmp assumes 12:00 AM with raw date. 16 | ]; 17 | Case( 18 | string = "Today"; 19 | _Now & separator & _Now + _TimeValue; 20 | 21 | string = "Yesterday"; 22 | Let( _Yesterday = _Now - 1; _Yesterday & separator & _Yesterday + _TimeValue); 23 | 24 | string = "Tomorrow"; 25 | Let( _Tomorrow = _Now + 1; _Tomorrow & separator & _Tomorrow + _TimeValue); 26 | 27 | string = "ThisWeek"; 28 | _Now - DayOfWeek(_Now) + 1 & separator & _Now + ( 7 - DayOfWeek(_Now)) + _TimeValue; 29 | 30 | string = "LastWeek"; 31 | _Now - DayOfWeek(_Now) - 6 & separator & _Now - DayOfWeek(_Now) + _TimeValue; 32 | 33 | string = "NextWeek"; 34 | Let( sunday = _Now + ( 8 - DayOfWeek(_Now)); sunday & separator & sunday + 6 + _TimeValue); 35 | 36 | string = "ThisMonth"; 37 | Let( firstday = Date( Month(_Now); 1; Year(_Now)); firstday & separator & Date( Month(firstday) + 1; 0; Year(firstday)) + _TimeValue); 38 | 39 | string = "LastMonth"; 40 | Let( lastday = Date( Month(_Now); 0; Year(_Now)); Date( Month(lastday); 1; Year(lastday)) & separator & lastday + _TimeValue); 41 | 42 | string = "NextMonth"; 43 | Let( firstday = Date( Month(_Now) + 1; 0; Year(_Now)) + 1; firstday & separator & Date( Month(firstday) + 1; 0; Year(firstday)) + _TimeValue); 44 | 45 | string = "ThisYTD"; 46 | Date(1; 1; Year(_Now)) & separator & _Now + _TimeValue; 47 | 48 | string = "LastYTD"; 49 | Date(1; 1; Year(_Now)-1) & separator & Date(Month(_Now); Day(_Now); Year(_Now) - 1) + _TimeValue; 50 | 51 | string = "NextYTD"; 52 | Date(1; 1; Year(_Now) + 1) & separator & Date(Month(_Now); Day(_Now); Year(_Now) + 1) + _TimeValue; 53 | 54 | string = "ThisYear"; 55 | Date(1; 1; Year(_Now)) & separator & Date(12; 31; Year(_Now)) + _TimeValue; 56 | 57 | string = "LastYear"; 58 | Date(1; 1; Year(_Now) - 1) & separator & Date(12; 31; Year(_Now) - 1) + _TimeValue; 59 | 60 | string = "NextYear"; 61 | Date(1; 1; Year(_Now) + 1) & separator & Date(12; 31; Year(_Now) + 1) + _TimeValue; 62 | 63 | string = "ThisQuarter" or 64 | string = "LastQuarter" or 65 | string = "NextQuarter"; 66 | Let ( 67 | xMod = Case ( 68 | Mod( Month( _Now ) ; 3 ) = 0; 3; 69 | Mod( Month( _Now ) ; 3 ) ); 70 | Case( 71 | string = "ThisQuarter"; 72 | Date( Month(_Now) - xMod + 1; 1; Year(_Now) ) & separator & _Now + _TimeValue; 73 | 74 | string = "LastQuarter"; 75 | Date( Month(_Now) - xMod - 2; 1; Year(_Now) ) & separator & Date( Month(_Now) - xMod + 1; 1; Year(_Now) ) - 1 + _TimeValue; 76 | 77 | string = "NextQuarter"; 78 | Date( Month(_Now) - xMod + 4; 1; Year(_Now) ) & separator & Date( Month(_Now) - xMod + 7; 1; Year(_Now) ) - 1 + _TimeValue 79 | ) 80 | ); 81 | 82 | string = "test"; 83 | "Today = " & date.ranges("Today"; separator; withtime) & ¶ & 84 | "Yesterday = " & date.ranges("Yesterday"; separator; withtime) & ¶ & 85 | "Tomorrow = " & date.ranges("Tomorrow"; separator; withtime) & ¶ & 86 | "ThisWeek = " & date.ranges("ThisWeek"; separator; withtime) & ¶ & 87 | "LastWeek = " & date.ranges("LastWeek"; separator; withtime) & ¶ & 88 | "NextWeek = " & date.ranges("NextWeek"; separator; withtime) & ¶ & 89 | "ThisMonth = " & date.ranges("ThisMonth"; separator; withtime) & ¶ & 90 | "LastMonth = " & date.ranges("LastMonth"; separator; withtime) & ¶ & 91 | "NextMonth = " & date.ranges("NextMonth"; separator; withtime) & ¶ & 92 | "ThisYTD = " & date.ranges("ThisYTD"; separator; withtime) & ¶ & 93 | "LastYTD = " & date.ranges("LastYTD"; separator; withtime) & ¶ & 94 | "NextYTD = " & date.ranges("NextYTD"; separator; withtime) & ¶ & 95 | "ThisYear = " & date.ranges("ThisYear"; separator; withtime) & ¶ & 96 | "LastYear = " & date.ranges("LastYear"; separator; withtime) & ¶ & 97 | "NextYear = " & date.ranges("NextYear"; separator; withtime) & ¶ & 98 | "ThisQuarter = " & date.ranges("ThisQuarter"; separator; withtime) & ¶ & 99 | "LastQuarter = " & date.ranges("LastQuarter"; separator; withtime) & ¶ & 100 | "NextQuarter = " & date.ranges("NextQuarter"; separator; withtime) 101 | ) 102 | ) 103 | /* 104 | Original by 105 | Andy Frazier, Mx4Px 106 | http://www.andyfrazier.com 107 | via Briandunning.com 108 | Compliments to Excelisys 109 | Modified by Matt Petrowsky 110 | */ -------------------------------------------------------------------------------- /object.id.fmfn: -------------------------------------------------------------------------------- 1 | /* 2 | * ===================================================================== 3 | * object.id ( object; type; file; layout ) 4 | * 5 | * @object = supply either the name or internal id of an object 6 | * (see types variable for supported objects) 7 | * @type = (enum) One of the following 8 | * "Table", "Layout", "Field", "Script", or "ValueList" or (T,L,F,S,V) for shorthand 9 | * @file = [optional] specify the name of the file (if using multiple files) - empty implies current file 10 | * @layout = [optional] required only if @type contains "Field". 11 | * Empty implies current layout (or the field table occurrence, read the note below). 12 | * The layout should be tied to the table occurrence of the field 13 | * 14 | * RETURNS: (mixed) Internal FileMaker ID if name of object is supplied 15 | * and inverse if ID of object is supplied 16 | * 17 | * DEPENDENCIES: None 18 | * AUTHOR: Fabrice Nordman 19 | * NOTES: for fields (_TLFSV = "F"), if you use the full field name 20 | * (table::fieldname) AND an empty var.layout parameter, the 21 | * function will assume you are referring not to current layout but 22 | * to the table occurrence found in the _TLFSV 23 | * 24 | * ===================================================================== 25 | */ 26 | 27 | 28 | Let ([ 29 | file = If ( 30 | IsEmpty ( file ); 31 | Get ( FileName ); // use current file name... otherwise 32 | file 33 | ); 34 | layout = Case ( 35 | IsEmpty ( layout ); 36 | Case ( 37 | PatternCount ( object; "::" ); 38 | GetValue ( Substitute ( object; "::"; ¶ ); 1 ); 39 | Get ( LayoutName ) // default 40 | ); 41 | layout // default 42 | ); 43 | layout = Case ( 44 | Int ( layout ) = layout and Length ( layout ) = 7; 45 | object.id ( layout; "T"; file; "" ); 46 | layout 47 | ); 48 | var.object = object; 49 | 50 | // Allow type to be a single character (T,L,F,S,V) 51 | var.type = Left ( type; 1 ); 52 | var.type = Choose( Position ( "TLFSV"; var.type; 1; 1 ) - 1; "Table"; "Layout"; "Field"; "Script"; "ValueList" ); 53 | var.object = Case ( // remove the repetition number 54 | var.type = "Field" and PatternCount ( var.object; "[" ); 55 | Left ( var.object; Position ( var.object; "["; 10000; -1 ) -1 ); 56 | var.object 57 | ); 58 | var.object = Case ( // for fields, do not take TO 59 | var.type = "Field" and PatternCount ( var.object; "::" ); 60 | Replace ( var.object; 1; Position ( var.object; "::"; 1; 1 ) + 1; "" ); 61 | var.object 62 | ); 63 | var.endOfString = "( \"" & file & "\"" & Case ( var.type = "field"; "; \"" & layout & "\"" ) & ")"; 64 | var.names = Evaluate ( var.type & "Names" & var.endOfString ); 65 | var.ids = Evaluate ( var.type & "IDs" & var.endOfString ) 66 | ]; 67 | 68 | Case ( 69 | var.object = GetAsNumber ( var.object ); 70 | 71 | // Convert ID -> Name 72 | Case ( 73 | not IsEmpty ( FilterValues ( var.object; var.ids )); 74 | GetValue ( var.names; Let ([ 75 | _text = var.ids; 76 | _item = var.object; 77 | _adj = ¶ & _text & ¶ 78 | ]; 79 | PatternCount ( Left ( _adj; Position ( _adj; ¶ & _item & ¶; 1; 1 ) + 1 ); ¶ ) 80 | ) 81 | ) 82 | ); 83 | // Convert Name -> ID 84 | Case ( 85 | not IsEmpty ( FilterValues ( var.object; var.names )); 86 | GetValue ( var.ids; Let ([ 87 | _text = var.names; 88 | _item = var.object; 89 | _adj = ¶ & _text & ¶ 90 | ]; 91 | PatternCount ( Left ( _adj; Position ( _adj; ¶ & _item & ¶; 1; 1 ) + 1 ); ¶ ) 92 | ) 93 | ) 94 | ) 95 | ) 96 | ) 97 | 98 | 99 | /* 100 | 101 | Original function by Fabrice Nordmann 102 | Reformatted by Matt Petrowsky 103 | 104 | Avoids hard-coding in FileMaker by using IDs instead of names 105 | 106 | v.1.6, Mar 2009 107 | Accepts field parameters such as FMvar.name_id ( 1065234::24; "F"; ""; "" ) 108 | v.1.5.2, Feb 2009 109 | Removes the repetition number if a fieldname with a repetition number is passed ( field[n]). Does not handle field names with [ anymore. 110 | v.1.5.1, Dec 2008 111 | Documentation update (thanks to Kevin Frank's comment) 112 | v.1.5, Oct 2008 113 | Documentation error fix 114 | Returns an empty result if item not found 115 | v.1.4, Aug 2008 116 | Bug fix (major) : could return erroneous result if an item ID was found in another ID. 117 | v.1.3, July 2008 118 | A field ID can be obtained by passing its full name (with table occurrence) in var.name_id and leaving var.layout empty 119 | e.g. FMvar.name_id ( "myTable::myField"; "F"; ""; "" ) 120 | v.1.2, June 2008 121 | Doesn't require Position value 122 | v.1, May 2008 123 | Requires: PositionValue ( _text; _searchValue; _start; _occurrence ) 124 | 125 | // Test code for raw data 126 | Let ( [ 127 | f = Get ( FileName ); 128 | l = Get ( LayoutName ) 129 | ]; 130 | List ( 131 | "--- TABLES ---"; 132 | TableNames ( f ); 133 | TableIDs ( f ); 134 | "--- LAYOUTS ---"; 135 | LayoutNames ( f ); 136 | LayoutIDs ( f ); 137 | "--- FIELDS ---"; 138 | FieldNames ( f; l ); 139 | FieldIDs ( f; l ); 140 | "--- SCRIPTS ---"; 141 | ScriptNames ( f ); 142 | ScriptIDs ( f ); 143 | "--- VALUE LISTS ---"; 144 | ValueListNames ( f ); 145 | ValueListIDs ( f ) 146 | ) 147 | ) 148 | 149 | */ -------------------------------------------------------------------------------- /keycode.istype.fmfn: -------------------------------------------------------------------------------- 1 | /* 2 | * ===================================================== 3 | * keycode.istype( type ) 4 | * 5 | * RETURNS: (boolean) Whether the keystoke used is within the specified type 6 | * DEPS: none 7 | * NOTES: 8 | * ===================================================== 9 | */ 10 | 11 | Let ( 12 | [ 13 | // Capture the keystroke for use 14 | 15 | var.keystroke = Code ( Get ( TriggerKeystroke ) ); 16 | 17 | // Possible keystokes & combinations 18 | 19 | var.arrows = var.keystroke ≥ 28 and var.keystroke ≤ 31; 20 | var.uppercase = var.keystroke ≥ 65 and var.keystroke ≤ 90; 21 | var.lowercase = var.keystroke ≥ 97 and var.keystroke ≤ 122; 22 | var.letter = var.uppercase or var.lowercase; 23 | var.number = var.keystroke ≥ 48 and var.keystroke ≤ 57; 24 | 25 | // Common editing keystrokes 26 | 27 | var.home = var.keystroke = 1; 28 | var.pageup = var.keystroke = 2; 29 | var.pagedown = var.keystroke = 3; 30 | var.end = var.keystroke = 4; 31 | var.backspace = var.keystroke = 8; 32 | var.tab = var.keystroke = 9; 33 | var.enter = var.keystroke = 10; 34 | var.return = var.keystroke = 13; 35 | var.escape = var.keystroke = 27; 36 | var.leftarrow = var.keystroke = 28; 37 | var.uparrow = var.keystroke = 29; 38 | var.rightarrow = var.keystroke = 30; 39 | var.downarrow = var.keystroke = 31; 40 | var.space = var.keystroke = 32; 41 | var.delete = var.keystroke = 127; 42 | 43 | // Punctiation & Math 44 | 45 | var.exclamation = var.keystroke = 33; 46 | var.doublequote = var.keystroke = 34; 47 | var.numbersign = var.keystroke = 35; 48 | var.dollar = var.keystroke = 36; 49 | var.percent = var.keystroke = 37; 50 | var.ampersand = var.keystroke = 38; 51 | var.singlequote = var.keystroke = 39; 52 | var.openparen = var.keystroke = 40; 53 | var.closeparen = var.keystroke = 41; 54 | var.asterisk = var.keystroke = 42; 55 | var.plus = var.keystroke = 43; 56 | var.comma = var.keystroke = 44; 57 | var.minus = var.keystroke = 45; 58 | var.period = var.keystroke = 46; 59 | var.slash = var.keystroke = 47; 60 | var.colon = var.keystroke = 58; 61 | var.semicolon = var.keystroke = 59; 62 | var.lessthan = var.keystroke = 60; 63 | var.equals = var.keystroke = 61; 64 | var.greaterthan = var.keystroke = 62; 65 | var.questionmark = var.keystroke = 63; 66 | var.openbracket = var.keystroke = 91; 67 | var.backslash = var.keystroke = 92; 68 | var.closebracket = var.keystroke = 93; 69 | var.caret = var.keystroke = 94; 70 | var.underscore = var.keystroke = 95; 71 | var.openbrace = var.keystroke = 123; 72 | var.pipe = var.keystroke = 124; 73 | var.closebrace = var.keystroke = 125; 74 | var.tilde = var.keystroke = 126; 75 | 76 | // Special combinations and groupings 77 | 78 | var.edit = var.home 79 | or var.pageup 80 | or var.pagedown 81 | or var.end 82 | or var.backspace 83 | or var.tab 84 | or var.enter 85 | or var.return 86 | or var.escape 87 | or var.return 88 | or var.space 89 | or var.delete; 90 | 91 | var.punctuation = var.period 92 | or var.exclamation 93 | or var.questionmark 94 | or var.comma 95 | or var.semicolon 96 | or var.colon 97 | or var.space 98 | or var.singlequote 99 | or var.doublequote 100 | or var.openparen 101 | or var.closeparen 102 | or var.openbrace 103 | or var.closebrace 104 | or var.openbracket 105 | or var.closebracket 106 | 107 | ]; 108 | // English named versions of keystrokes 109 | 110 | If ( 111 | type = "arrows" and var.arrows or 112 | type = "uppercase" and var.uppercase or 113 | type = "lowercase" and var.lowercase or 114 | type = "letter" and var.letter or 115 | type = "number" and var.number or 116 | type = "home" and var.home or 117 | type = "pageup" and var.pageup or 118 | type = "pagedown" and var.pagedown or 119 | type = "end" and var.end or 120 | type = "backspace" and var.backspace or 121 | type = "tab" and var.tab or 122 | type = "enter" and var.enter or 123 | type = "return" and var.return or 124 | type = "escape" and var.escape or 125 | (type = "leftarrow" or type = "left arrow") and var.leftarrow or 126 | type = "uparrow" and var.uparrow or 127 | type = "rightarrow" and var.rightarrow or 128 | type = "downarrow" and var.downarrow or 129 | type = "space" and var.space or 130 | type = "delete" and var.delete or 131 | type = "exclamation" and var.exclamation or 132 | type = "doublequote" and var.doublequote or 133 | type = "number sign" and var.numbersign or 134 | type = "dollar" and var.dollar or 135 | type = "percent" and var.percent or 136 | type = "ampersand" and var.ampersand or 137 | type = "singlequote" and var.singlequote or 138 | type = "openparen" and var.openparen or 139 | type = "closeparen" and var.closeparen or 140 | type = "asterisk" and var.asterisk or 141 | type = "plus" and var.plus or 142 | type = "comma" and var.comma or 143 | type = "minus" and var.minus or 144 | type = "period" and var.period or 145 | type = "slash" and var.slash or 146 | type = "colon" and var.colon or 147 | type = "semicolon" and var.semicolon or 148 | type = "lessthan" and var.lessthan or 149 | type = "equals" and var.equals or 150 | type = "greaterthan" and var.greaterthan or 151 | type = "questionmark" and var.questionmark or 152 | type = "openbracket" and var.openbracket or 153 | type = "backslash" and var.backslash or 154 | type = "closebracket" and var.closebracket or 155 | type = "caret" and var.caret or 156 | type = "underscore" and var.underscore or 157 | type = "openbrace" and var.openbrace or 158 | type = "pipe" and var.pipe or 159 | type = "closebrace" and var.closebrace or 160 | type = "tilde" and var.tilde or 161 | type = "editing" and var.edit or 162 | type = "punctuation" and var.punctuation; True; 163 | False 164 | ) 165 | ) -------------------------------------------------------------------------------- /list.custom.fmfn: -------------------------------------------------------------------------------- 1 | /* Special Thanks to Ugo Di Luca - Grazie Mille, pour 2 | l'aiguillage qu'il m'a fait prendre, merci à lui pour son 3 | implication afin de vous rendre l'utilisation de 4 | CustomList() plus limpide Merci pour tous ses commentaires 5 | et sa notice ® Ugo Di Luca 6 | =========================================================== 7 | 8 | // Author: Agnès Barouh - barouh.agnes@wanadoo.fr 9 | 10 | // CustomList ( Start ; End ; Function ) v_4.5 11 | // [please, do not used "CLNum" in your calculation with Let() ] 12 | // Objectives and examples : 13 | 14 | - Build any list based on all Native or Custom Functions involving a 'Number' value as a parameter, such as : 15 | Left(), Middle(), GetValue(), GetRepetitionNumber (), GetNthRecord(), GetLayoutObjectAttribute () ... 16 | 17 | ex : - CustomList ( 1 ; Get ( FoundCount ) ; "GetNthRecord ( FirstName ; [n] )" ) 18 | 19 | will return James¶Henry¶Susan if your foundset has 3 records. 20 | 21 | - Build any range based on Dates, Times, TimeStamps, and obviously Numbers 22 | 23 | ex : CustomList ( 1 ; 5 ; "GetAsDate ( StartingDate ) + [n]" ) 24 | 25 | will return a range of 5 dates starting from the specified StartingDate 26 | 27 | The 'Function' Parameter is nothing else than a literal calculation expression. 28 | Therefore, CustomList allows for any filtering or parsing 29 | process based on any condition you may need. 30 | 31 | ex : CustomList ( 10 ; 100 ; "Let ( [ Value = GetValue ( MyList ; [n] ) ] ; Case ( PatternCount ( Value ; "X" ) ; Value ))" ) 32 | 33 | will parse any value containing a "X" in the 'MyList' chain, 34 | in between the 10th and the 100th values. 35 | 36 | See the 'Under the Hood' part at the end of the function to fully understand the process of this function 37 | 38 | -------------------------------- 39 | /* MAJOR UPDATE */ Updated July'08 40 | -------------------------------- 41 | 42 | CustomList is based on a totally new algorithm, and is now 43 | voluntarily bridled to a maximum range of 500,000 values, 44 | where the first version was technically limited to a max of 45 | 18,700 values. 46 | 47 | Previous version still available here : http://www.briandunning.com/cf/747 48 | 49 | The new CustomList() is faster and still is NOT recursive. 50 | The arguments are unchanged which makes it compatible with 51 | all your previous developments involving CustomList(). 52 | 53 | For Developer ease, the new CustomList() includes a 54 | debugging mode. find the "*****DEBUGGING MODE*****" tag in 55 | the formula below to switch mode. When debug is set to 1, 56 | any error will be returned with its appropriate explanatory 57 | code, else the result will be set to "?" 58 | -------------------------------- */ 59 | 60 | // ----------- FORMULA STARTS HERE ----------- 61 | 62 | Case ( 63 | /*This function will not evaluate if Invalid parameters were passed for Start and End.*/ 64 | 65 | IsEmpty ( Start ) or IsEmpty ( End ) or End < 1 or Start < 1; ""; 66 | 67 | Let ( [ 68 | Start = GetAsNumber ( Start ); 69 | End = GetAsNumber ( End ); 70 | Diff = End - Start + 1; 71 | 72 | /*Check for a range higher than 500,000 values. CustomList() is voluntarily restrained to this limit.*/ 73 | 74 | End = Case ( Diff > 500000 or End < Start or IsEmpty ( Start ) or IsEmpty ( End ); "Error"; End ); 75 | $null = "\"\""; 76 | 77 | /*CustomList has its own recursion model. As CustomList may be involved into the "function" argument, 78 | each CustomList expression used is passed to a repeating variable for evaluation*/ 79 | 80 | iter = Let ( $CLExeCount = $CLExeCount + 1 ; $CLExeCount & PatternCount ( Function ; "CustomList" ) + 1 ) ; 81 | $CLn[ iter ] = Start - 1; 82 | Calc = Case ( Diff ≥ 1600; 169; Floor ( Diff / 10 ) + 1 ); 83 | 84 | /*Here starts the "magic" of the Substitutions and the whole mechanism. 85 | CustomList() is set to evaluate stacks of 1,700 values at a time, which is the 86 | current limit of FileMaker internal Evaluate function */ 87 | 88 | First = Substitute ( ( 10 ^ Calc ) - 1; 9; "__________" ) & "_________"; 89 | X = Floor ( Diff / 1700 ); 90 | $CLRemainder[ iter ] = Diff - ( X * 1700 ); 91 | 92 | /*When the "Function" argument is left empty, CustomList() will return a numeric list based on the range defined */ 93 | 94 | FunctionR = Case ( IsEmpty ( Function ); "CLNum"; Substitute ( Function; ["[n]"; "CLNum"] ; [¶ ; ""] ) ); 95 | 96 | /*Each repeating variable content is parsed in order to get our String ready for the last evaluation - Special care is made for 97 | French users here, please substitute the "definir" below with your local translation of the "Let" function if you're not using an english 98 | version. The use of "Let ([" is recommanded anyway */ 99 | 100 | $CLExecute[ iter ] = Case ( Left ( Substitute ( Lower ( Function ); ["definir"; "Let" ]; [" "; ""]; ["¶"; ""]); 5 ) = "Let(["; 101 | Substitute ( "Let([$CLn[" & iter & "] = $CLn[" & iter & "] + 1 ; CLNum = $CLn[" & iter & "]" & First & "|"; 102 | [ "_"; "|¶Let([$CLn[" & iter & "] = $CLn[" & iter & "] + 1 ; CLNum = $CLn[" & iter & "] "]; [ "|";";" & 103 | Replace ( FunctionR; 1; Position ( FunctionR; "["; 1; 1 ); "" ) & "&\"#^#|#^#\"&"] ); 104 | Substitute ( "Let([$CLn[" & iter & "] = $CLn[" & iter & "] + 1 ; CLNum = $CLn[" & iter & "]" & First & "|"; 105 | [ "_"; "|¶Let([$CLn[" & iter & "] = $CLn[" & iter & "] + 1 ; CLNum = $CLn[" & iter & "] "]; [ "|";"];" & FunctionR & ")&\"#^#|#^#\"&"] ) ); 106 | 107 | /*Final compilation starts here. The reminder part above each 1,700 values is treated now. */ 108 | 109 | Final = Case ( X > 0; Substitute ( ( 10 ^ X ) - 1; 9; "Evaluate ( $CLExecute[" & iter & "] & $null ) & " ) ) & 110 | "Evaluate( LeftValues ( $CLExecute[" & iter & "] ; $CLRemainder[" & iter & "] ) & $null ) & " & $null; 111 | 112 | /*The Final variable can now be evaluated to get our List*/ 113 | 114 | Result = Case ( End <> "Error"; Substitute ( "#^#" & Evaluate ( Final ) & "#^#"; 115 | [ "#^#|#^#"; "¶" ]; [ "¶"; "¶#^#" ]; [ "#^#¶"; "" ]; [ "¶#^#"; "¶" ]; [ "¶#^#"; "" ]; [ "#^#"; "" ] ) ) ; 116 | $CLExecute[ iter ] = "" 117 | 118 | // ----------- FUNCTION RESULT BELOW ----------- 119 | ]; 120 | /*CustomList returns either the valid result, or an error formatted according to the debugging mode chosen above*/ 121 | 122 | Case ( 123 | ( Length ( Result ) and ( Result = Filter ( Result; "?" ))) or End = "Error"; 124 | Let ([ 125 | /*****DEBUGGING MODE*****/ // Case Debug = 1, returned error "[error_CL], Number, Name and Calculation error" ,if Debug <> 1, returned error is "?" 126 | Debug = "1"; 127 | Write = Substitute ( Function; "[n]"; 1 ); NumError = EvaluationError ( Evaluate ( Write ) ); 128 | Error = "[" & NumError & "] " & "Unlisted error | Unknown error, check calculation or check \"Start\" and \"End\" ¶102 | Field is missing¶103 | Relationship is missing¶106 | Table is missing¶113 | Function is missing¶1204 | Number, text constant, field name or \"(\" expected¶1205 | Comment is not terminated with \"*/\"¶1206 | Text constant must end with a quotation mark¶1207 | Unbalanced parenthesis¶1208 | Operator or function missing or \"(\" not expected¶1211 | List usage is not allowed in this function¶1212 | An operator (for example, +, -, *,;) is expected here¶1215 | This parameter is an invalid Get function parameter"; 129 | Pos = ValueCount ( Left ( Error; Position ( Error; NumError & " "; 1; 1 ) ) ) 130 | ]; 131 | Case ( Debug = 1; "[Error_CL] | Return error : " & GetValue ( Error; Case ( Pos = 0; 1; Pos ) ) & ¶ & TextStyleAdd ( "Calculation ( for [n] = 1 ) : "; Bold ) & Write; "?" )); 132 | Result )) 133 | ) 134 | 135 | // ----------- UNDER THE HOOD ----------- 136 | 137 | /* Not very much afterwards... 138 | Basically, CustomList() does two things : 139 | 1/ Transform your formula in a litteral chain : 140 | 141 | CustomList ( 1; 4; "GetNthRecord ( Field ; [n])") 142 | therefore becomes 143 | "Let ([ CLNum = 1 ] ; GetNthRecord ( Field ; CLNum )) & ¶ & 144 | Let ([ CLNum = 2 ] ; GetNthRecord ( Field ; CLNum )) & ¶ & 145 | Let ([ CLNum = 3 ] ; GetNthRecord ( Field ; CLNum )) & ¶ & 146 | Let ([ CLNum = 4 ] ; GetNthRecord ( Field ; CLNum ))" 147 | 148 | 2/ Evaluates this chain. 149 | 150 | Interrested in the mechanism ? 151 | My advice then : dissect this function by escaping the 152 | 'Result' and placing one of the numerous intermediary 153 | variables available. 154 | 155 | Special attention should be paid to the 'First' Variable, 156 | everything starts from there ! 157 | */ -------------------------------------------------------------------------------- /error.string.fmfn: -------------------------------------------------------------------------------- 1 | /* 2 | * ===================================================== 3 | * error.string( num ) 4 | * 5 | * RETURNS: (string) English version of current error 6 | * DEPENDENCIES: none 7 | * NOTES: FileMaker 10 error codes at http://www.filemaker.com/help/html/error_codes.html#1027502 8 | * ===================================================== 9 | * 10 | */ 11 | 12 | Case ( 13 | num = -1; "Unknown error"; 14 | num = 0; "No error"; 15 | num = 1; "User canceled action"; 16 | num = 2; "Memory error"; 17 | num = 3; "Command is unavailable (for example, wrong operating system, wrong mode, etc.)"; 18 | num = 4; "Command is unknown"; 19 | num = 5; "Command is invalid (for example, a Set Field script step does not have a calculation specified)"; 20 | num = 6; "File is read-only"; 21 | num = 7; "Running out of memory"; 22 | num = 8; "Empty result"; 23 | num = 9; "Insufficient privileges"; 24 | num = 10; "Requested data is missing"; 25 | num = 11; "Name is not valid"; 26 | num = 12; "Name already exists"; 27 | num = 13; "File or object is in use"; 28 | num = 14; "Out of range"; 29 | num = 15; "Can't divide by zero"; 30 | num = 16; "Operation failed, request retry (for example, a user query)"; 31 | num = 17; "Attempt to convert foreign character set to UTF-16 failed"; 32 | num = 18; "Client must provide account information to proceed"; 33 | num = 19; "String contains characters other than A-Z, a-z, 0-9 (ASCII)"; 34 | num = 20; "Command/operation canceled by triggered script"; 35 | num = 100; "File is missing"; 36 | num = 101; "Record is missing"; 37 | num = 102; "Field is missing"; 38 | num = 103; "Relationship is missing"; 39 | num = 104; "Script is missing"; 40 | num = 105; "Layout is missing"; 41 | num = 106; "Table is missing"; 42 | num = 107; "Index is missing"; 43 | num = 108; "Value list is missing"; 44 | num = 109; "Privilege set is missing"; 45 | num = 110; "Related tables are missing"; 46 | num = 111; "Field repetition is invalid"; 47 | num = 112; "Window is missing"; 48 | num = 113; "Function is missing"; 49 | num = 114; "File reference is missing"; 50 | num = 115; "Specified menu set is not present"; 51 | num = 116; "Specified layout object is not present"; 52 | num = 117; "Specified data source is not present"; 53 | num = 130; "Files are damaged or missing and must be reinstalled"; 54 | num = 131; "Language pack files are missing (such as template files)"; 55 | num = 200; "Record access is denied"; 56 | num = 201; "Field cannot be modified"; 57 | num = 202; "Field access is denied"; 58 | num = 203; "No records in file to print, or password doesn't allow print access"; 59 | num = 204; "No access to field(s) in sort order"; 60 | num = 205; "User does not have access privileges to create new records; import will overwrite existing data"; 61 | num = 206; "User does not have password change privileges, or file is not modifiable"; 62 | num = 207; "User does not have sufficient privileges to change database schema, or file is not modifiable"; 63 | num = 208; "Password does not contain enough characters"; 64 | num = 209; "New password must be different from existing one"; 65 | num = 210; "User account is inactive"; 66 | num = 211; "Password has expired"; 67 | num = 212; "Invalid user account and/or password; please try again"; 68 | num = 213; "User account and/or password does not exist"; 69 | num = 214; "Too many login attempts"; 70 | num = 215; "Administrator privileges cannot be duplicated"; 71 | num = 216; "Guest account cannot be duplicated"; 72 | num = 217; "User does not have sufficient privileges to modify administrator account"; 73 | num = 300; "File is locked or in use"; 74 | num = 301; "Record is in use by another user"; 75 | num = 302; "Table is in use by another user"; 76 | num = 303; "Database schema is in use by another user"; 77 | num = 304; "Layout is in use by another user"; 78 | num = 306; "Record modification ID does not match"; 79 | num = 400; "Find criteria are empty"; 80 | num = 401; "No records match the request"; 81 | num = 402; "Selected field is not a match field for a lookup"; 82 | num = 403; "Exceeding maximum record limit for trial version of FileMaker Pro"; 83 | num = 404; "Sort order is invalid"; 84 | num = 405; "Number of records specified exceeds number of records that can be omitted"; 85 | num = 406; "Replace/Reserialize criteria are invalid"; 86 | num = 407; "One or both match fields are missing (invalid relationship)"; 87 | num = 408; "Specified field has inappropriate data type for this operation"; 88 | num = 409; "Import order is invalid"; 89 | num = 410; "Export order is invalid"; 90 | num = 412; "Wrong version of FileMaker Pro used to recover file"; 91 | num = 413; "Specified field has inappropriate field type"; 92 | num = 414; "Layout cannot display the result"; 93 | num = 415; "One or more required related records are not available"; 94 | num = 416; "Primary key required from data source table"; 95 | num = 417; "Database is not supported for ODBC operations"; 96 | num = 500; "Date value does not meet validation entry options"; 97 | num = 501; "Time value does not meet validation entry options"; 98 | num = 502; "Number value does not meet validation entry options"; 99 | num = 503; "Value in field is not within the range specified in validation entry options"; 100 | num = 504; "Value in field is not unique as required in validation entry options"; 101 | num = 505; "Value in field is not an existing value in the database file as required in validation entry options"; 102 | num = 506; "Value in field is not listed on the value list specified in validation entry option"; 103 | num = 507; "Value in field failed calculation test of validation entry option"; 104 | num = 508; "Invalid value entered in Find mode"; 105 | num = 509; "Field requires a valid value"; 106 | num = 510; "Related value is empty or unavailable"; 107 | num = 511; "Value in field exceeds maximum number of allowed characters"; 108 | num = 512; "Record was already modified by another user"; 109 | num = 513; "Record must have a value in some field to be created"; 110 | num = 600; "Print error has occurred"; 111 | num = 601; "Combined header and footer exceed one page"; 112 | num = 602; "Body doesn't fit on a page for current column setup"; 113 | num = 603; "Print connection lost"; 114 | num = 700; "File is of the wrong file type for import"; 115 | num = 706; "EPSF file has no preview image"; 116 | num = 707; "Graphic translator cannot be found"; 117 | num = 708; "Can't import the file or need color monitor support to import file"; 118 | num = 709; "QuickTime movie import failed"; 119 | num = 710; "Unable to update QuickTime reference because the database file is read-only"; 120 | num = 711; "Import translator cannot be found"; 121 | num = 714; "Password privileges do not allow the operation"; 122 | num = 715; "Specified Excel worksheet or named range is missing"; 123 | num = 716; "A SQL query using DELETE, INSERT, or UPDATE is not allowed for ODBC import"; 124 | num = 717; "There is not enough XML/XSL information to proceed with the import or export"; 125 | num = 718; "Error in parsing XML file (from Xerces)"; 126 | num = 719; "Error in transforming XML using XSL (from Xalan)"; 127 | num = 720; "Error when exporting; intended format does not support repeating fields"; 128 | num = 721; "Unknown error occurred in the parser or the transformer"; 129 | num = 722; "Cannot import data into a file that has no fields"; 130 | num = 723; "You do not have permission to add records to or modify records in the target table"; 131 | num = 724; "You do not have permission to add records to the target table"; 132 | num = 725; "You do not have permission to modify records in the target table"; 133 | num = 726; "There are more records in the import file than in the target table; not all records were imported"; 134 | num = 727; "There are more records in the target table than in the import file; not all records were updated"; 135 | num = 729; "Errors occurred during import; records could not be imported"; 136 | num = 730; "Unsupported Excel version (convert file to Excel 7.0 (Excel 95), 97, 2000, XP, or 2007 format and try again)"; 137 | num = 731; "The file you are importing from contains no data"; 138 | num = 732; "This file cannot be inserted because it contains other files"; 139 | num = 733; "A table cannot be imported into itself"; 140 | num = 734; "This file type cannot be displayed as a picture"; 141 | num = 735; "This file type cannot be displayed as a picture; it will be inserted and displayed as a file"; 142 | num = 736; "Too much data to export to this format; it will be truncated"; 143 | num = 737; "Bento collection or library is missing; data cannot be imported"; 144 | num = 800; "Unable to create file on disk"; 145 | num = 801; "Unable to create temporary file on System disk"; 146 | num = 802; "Unable to open file"; 147 | num = 803; "File is single user or host cannot be found"; 148 | num = 804; "File cannot be opened as read-only in its current state"; 149 | num = 805; "File is damaged; use Recover command"; 150 | num = 806; "File cannot be opened with this version of FileMaker Pro"; 151 | num = 807; "File is not a FileMaker Pro file or is severely damaged"; 152 | num = 808; "Cannot open file because access privileges are damaged"; 153 | num = 809; "Disk/volume is full"; 154 | num = 810; "Disk/volume is locked"; 155 | num = 811; "Temporary file cannot be opened as FileMaker Pro file"; 156 | num = 813; "Record Synchronization error on network"; 157 | num = 814; "File(s) cannot be opened because maximum number is open"; 158 | num = 815; "Couldn't open lookup file"; 159 | num = 816; "Unable to convert file"; 160 | num = 817; "Unable to open file because it does not belong to this solution"; 161 | num = 819; "Cannot save a local copy of a remote file"; 162 | num = 820; "File is in the process of being closed"; 163 | num = 821; "Host forced a disconnect"; 164 | num = 822; "FMI files not found; reinstall missing files"; 165 | num = 823; "Cannot set file to single-user, guests are connected"; 166 | num = 824; "File is damaged or not a FileMaker file"; 167 | num = 900; "General spelling engine error"; 168 | num = 901; "Main spelling dictionary not installed"; 169 | num = 902; "Could not launch the Help system"; 170 | num = 903; "Command cannot be used in a shared file"; 171 | num = 905; "No active field selected; command can only be used if there is an active field"; 172 | num = 906; "Current file must be shared in order to use this command"; 173 | num = 920; "Can’t initialize the spelling engine"; 174 | num = 921; "User dictionary cannot be loaded for editing"; 175 | num = 922; "User dictionary cannot be found"; 176 | num = 923; "User dictionary is read-only"; 177 | num = 951; "An unexpected error occurred (*)"; 178 | num = 954; "Unsupported XML grammar (*)"; 179 | num = 955; "No database name (*)"; 180 | num = 956; "Maximum number of database sessions exceeded (*)"; 181 | num = 957; "Conflicting commands (*)"; 182 | num = 958; "Parameter missing (*)"; 183 | num = 1200; "Generic calculation error"; 184 | num = 1201; "Too few parameters in the function"; 185 | num = 1202; "Too many parameters in the function"; 186 | num = 1203; "Unexpected end of calculation"; 187 | num = 1204; "Number, text constant, field name or \"(\" expected"; 188 | num = 1205; "Comment is not terminated with \"*/\""; 189 | num = 1206; "Text constant must end with a quotation mark"; 190 | num = 1207; "Unbalanced parenthesis"; 191 | num = 1208; "Operator missing, function not found or \"(\" not expected"; 192 | num = 1209; "Name (such as field name or layout name) is missing"; 193 | num = 1210; "Plug-in function has already been registered"; 194 | num = 1211; "List usage is not allowed in this function"; 195 | num = 1212; "An operator (for example, +, -, *) is expected here"; 196 | num = 1213; "This variable has already been defined in the Let function"; 197 | num = 1214; "AVERAGE, COUNT, EXTEND, GETREPETITION, MAX, MIN, NPV, STDEV, SUM and GETSUMMARY: expression found where a field alone is needed"; 198 | num = 1215; "This parameter is an invalid Get function parameter"; 199 | num = 1216; "Only Summary fields allowed as first argument in GETSUMMARY"; 200 | num = 1217; "Break field is invalid"; 201 | num = 1218; "Cannot evaluate the number"; 202 | num = 1219; "A field cannot be used in its own formula"; 203 | num = 1220; "Field type must be normal or calculated"; 204 | num = 1221; "Data type must be number, date, time, or timestamp"; 205 | num = 1222; "Calculation cannot be stored"; 206 | num = 1223; "The function is not implemented"; 207 | num = 1224; "The function is not defined"; 208 | num = 1225; "The function is not supported in this context"; 209 | num = 1300; "The specified name can’t be used"; 210 | num = 1400; "ODBC driver initialization failed; make sure the ODBC drivers are properly installed"; 211 | num = 1401; "Failed to allocate environment (ODBC)"; 212 | num = 1402; "Failed to free environment (ODBC)"; 213 | num = 1403; "Failed to disconnect (ODBC)"; 214 | num = 1404; "Failed to allocate connection (ODBC)"; 215 | num = 1405; "Failed to free connection (ODBC)"; 216 | num = 1406; "Failed check for SQL API (ODBC)"; 217 | num = 1407; "Failed to allocate statement (ODBC)"; 218 | num = 1408; "Extended error (ODBC)"; 219 | num = 1409; "Error (ODBC)"; 220 | num = 1413; "Failed communication link (ODBC)"; 221 | num = 1450; "Action requires PHP privilege extension (*)"; 222 | num = 1451; "Action requires that current file be remote"; 223 | num = 1501; "SMTP authentication failed"; 224 | num = 1502; "Connection refused by SMTP server"; 225 | num = 1503; "Error with SSL"; 226 | num = 1504; "SMTP server requires the connection to be encrypted"; 227 | num = 1505; "Specified authentication is not supported by SMTP server"; 228 | num = 1506; "Email(s) could not be sent successfully"; 229 | num = 1507; "Unable to log in to the SMTP server" 230 | ) -------------------------------------------------------------------------------- /window.settings.fmfn: -------------------------------------------------------------------------------- 1 | /* 2 | * This is taken from the Window Controller field in Mikhail Edoshin's 3 | * sample file. http://mikhailedoshin.com/cookbook/window_controller/ 4 | * The intention is to convert this into a custom function which can 5 | * be simply called without all of structure and setup. Will need to 6 | * be refactored. 7 | */ 8 | 9 | 10 | // Uses Set Contains() 11 | 12 | Case( True or globals_wc_alignment; /* Trigger */ 13 | 14 | Let( [ 15 | 16 | /* Read environment settings. */ 17 | 18 | on Mac = Get( SystemPlatform ) = 1 19 | or Get( SystemPlatform ) = -1; 20 | 21 | on Windows = Get( SystemPlatform ) = 2 22 | or Get( SystemPlatform ) = -2; 23 | 24 | FileMaker version = GetAsNumber( Get( ApplicationVersion ) ); 25 | 26 | /* Adjustments and preferences 27 | 28 | Under Windows FileMaker v9 or earlier may or may not 29 | display the status bar (18 px) at the bottom of 30 | FileMaker application window, but there's no function to 31 | know its state and Get( WindowDesktopHeight ) and Get( 32 | WindowDesktopWidth ) functions do not see that status 33 | bar either. Since it's on by default, I assume it stays 34 | on. 35 | 36 | FileMaker v10 doesn't have that status bar anymore. */ 37 | 38 | status bar adjustment = Case( on Windows and FileMaker version < 10; -18 ); 39 | 40 | /* FileMaker v9 or earlier have toolbars, but Get( 41 | WindowDesktopHeight ) and Get( WindowDesktopWidth ) 42 | functions do not see them. Since users can move and 43 | rearrange toolbars, there's no fixed size to account for 44 | and what's below are the most likely settings: toolbars 45 | are on and take a single row at the top (27 or 28px 46 | depending on platform). 47 | 48 | In FileMaker v10 toolbars are no longer used. */ 49 | 50 | toolbar adjustment = Case( FileMaker version < 10; Case( on Mac; -27; on Windows; -28 ) ); 51 | 52 | /* To prevent windows to open off screen this 53 | calculation checks their coordinates and, if necessary, 54 | alters them to make sure there's some minimal distance 55 | (the safety margin) beween the window and screen edges. 56 | A negative margin would allow a window to open slightly 57 | off screen, but not more than the absolute value of the 58 | margin.*/ 59 | 60 | safety margin = 0; 61 | 62 | /* A window may be set to zoom down to fit on screen. In 63 | this case controller starts with some initial zoom level 64 | and then reduces it until the window fits the available 65 | space. It may be desirable to scale the window to fit 66 | not the whole space, but a somewhat smaller area, to 67 | make the window more convenient to manipulate. To 68 | support this the controller uses a scale margin, a 69 | distance that is automatically subtracted from the 70 | available space when calculating the scaled size. E.g. 71 | if scale margin is 15, the controller will reduce the 72 | available space by 30px from either dimension. 73 | 74 | Unlike safety margin, scale margin should not be 75 | negative. Scale margin applies to scaling only; windows 76 | with fixed size are not affected. */ 77 | 78 | scale margin = 0; 79 | 80 | /* End of adjustments and preferences; all the rest is 81 | automatic. 82 | 83 | Depending on which parameters are passed there could be 84 | saved or default settings. */ 85 | 86 | has saved settings = 87 | IsValid( Window Settings::Window ); 88 | 89 | has default size = 90 | IsValid( Window Size::Name ); 91 | 92 | has alignment = 93 | IsValid( Window Alignment::Name ); 94 | 95 | /* The default size may be specified not in pixels. */ 96 | 97 | multiplier to px = 98 | Case( has default size; 99 | Let( unit = Window Size::Unit; 100 | Case( 101 | unit = "px"; 1; 102 | unit = "in"; 72; 103 | unit = "cm"; 72 / 2.54 ) ) ); 104 | 105 | /* Read content width and height. */ 106 | 107 | content width = 108 | Case( 109 | not IsEmpty( script.parameter( "width" ) ); 110 | GetAsNumber( script.parameter( "width" ) ); 111 | has saved settings; 112 | Window Settings::Width; 113 | has default size; 114 | Ceiling( Window Size::Width * multiplier to px ); 115 | // otherwise use current window's 116 | Get( WindowContentWidth ) ); 117 | 118 | content height = 119 | Case( 120 | not IsEmpty( script.parameter( "height" ) ); 121 | GetAsNumber( script.parameter( "height" ) ); 122 | has saved settings; 123 | Window Settings::Height; 124 | has default size; 125 | Ceiling( Window Size::Height * multiplier to px ); 126 | // otherwise use current window's 127 | Get( WindowContentHeight ) ); 128 | 129 | /* Read desired settings for status area, zoom and view. */ 130 | 131 | $status area = 132 | Case( 133 | not IsEmpty( script.parameter( "status area" ) ); 134 | script.parameter( "status area" ); 135 | has saved settings; 136 | Window Settings::Status Area; 137 | has default size; 138 | Let( user choice = 139 | Window Size::Status Area; 140 | Case( 141 | user choice = "Same"; Get( StatusAreaState ); 142 | user choice = "Off"; 0; 143 | user choice = "On"; 1; 144 | user choice = "On, locked"; 2; 145 | user choice = "Off, locked"; 3 ) ); 146 | // otherwise use current window's 147 | Get( StatusAreaState ) ); 148 | 149 | /* View is always passed explicitly or read from default 150 | size. It is never saved nor it copied from the current 151 | window; if neither the parameter nor default settings 152 | are present, the controller assumes form view. */ 153 | 154 | view = 155 | Case( 156 | not IsEmpty( script.parameter( "view" ) ); 157 | script.parameter( "view" ); 158 | has default size; 159 | Let( user choice = 160 | Window Size::View; 161 | Case( 162 | user choice = "Form"; 0; 163 | user choice = "List"; 1; 164 | user choice = "Table"; 2 ) ); 165 | // otherwise default to form 166 | 0 /* Form */ ); 167 | 168 | /* Measure the size of controls using the current window. */ 169 | 170 | full controls width = 171 | Get( WindowWidth ) - Get( WindowContentWidth ); 172 | full controls height = 173 | Get( WindowHeight ) - Get( WindowContentHeight ); 174 | 175 | /* The size of controls includes operating system 176 | controls (window title, border, scrollbars) and 177 | FileMaker controls (status area/toolbar and the vertical 178 | bar on the left side that appears in list and table 179 | views). 180 | 181 | System controls are always present; under Mac OS X 182 | their size is fixed, under Windows it may vary, but 183 | since the controller measures them each time, it always 184 | gets up-to-date values. 185 | 186 | FileMaker controls can be on or off. If the current 187 | window and the target have different settings, the 188 | controller needs to make some adjustments to correctly 189 | calculate window size and position. There are two 190 | possible differences: status area and view. 191 | 192 | FileMaker v7-9 has both status area and view are on the 193 | left side. The status area is 69 px. The view area is 3 194 | px in list or table views, but if status area is on, the 195 | view area merges with the status area so the total width 196 | remains 69 px. Toggling the status area on or off 197 | doesn't change the total window width, but adds space 198 | for content. 199 | 200 | FileMaker v10 has status toolbar (formerly area) on top 201 | and view area on left. The view area remains 3 px in 202 | list of table views and is no longer affected by status 203 | toolbar. The status toolbar consists of a large bar 204 | above a smaller one. Under Windows their total size is 205 | 80 px. Under Mac the size of the large bar may vary and 206 | the size of the small bar is 25 px. Under Windows 207 | toggling the bar on or off doesn't change the total 208 | window height, but adds more space for content. Under 209 | Mac toggling the bar adjusts the window height by the 210 | size of the large bar, but doesn't include the small 211 | bar. 212 | 213 | This means that under Mac the window controller uses 214 | one (final) height value to position the window 215 | vertically and to calculate the proper zoom level, but 216 | may adjust this height by the (variable) height of the 217 | status toolbar to open the window, because the height 218 | will automatically change when the toolbar is toggled. 219 | 220 | To calculate the variable height of the toolbar the 221 | controller uses the fact that system controls under Mac 222 | have fixed height (of 37 px). */ 223 | 224 | status area is on = 225 | Get( StatusAreaState ) = 1 or Get( StatusAreaState ) = 2; 226 | 227 | status area should be on = 228 | $status area = 1 or $status area = 2; 229 | 230 | status area width adjustment = 231 | Case( 232 | 10 ≤ FileMaker version; 233 | 0; 234 | status area is on and not status area should be on; 235 | -69; 236 | not status area is on and status area should be on; 237 | +69; 238 | /* otherwise */ 239 | 0 ); 240 | 241 | status area height adjustment = 242 | Case( 243 | FileMaker version < 10; 244 | 0; 245 | // otherwise 246 | Let( adjustment = 247 | Case( 248 | on Mac; 249 | full controls height - 37 /* system controls */; 250 | on WIndows; 251 | 80 ); 252 | Case( 253 | status area is on and not status area should be on; 254 | - adjustment; 255 | not status area is on and status area should be on; 256 | + adjustment; 257 | /* otherwise */ 258 | 0 ) ) ); 259 | 260 | Mac open adjustment = 261 | Case( 10 ≤ FileMaker version and on Mac; 262 | Let( adjustment = 263 | full controls height - 37 /* system controls */ - 25 /* small bar */; 264 | Case( 265 | status area is on and not status area should be on; 266 | + adjustment; 267 | not status area is on and status area should be on; 268 | - adjustment; 269 | /* otherwise */ 270 | 0 ) ) ); 271 | 272 | /* Adjust for view state, if necessary. */ 273 | 274 | view is form = 275 | Get( LayoutViewState ) = 0; 276 | view should be form = 277 | view = 0; 278 | 279 | /* Starting with v10 view adjustment no longer depends on status area state. */ 280 | 281 | view width adjustment = 282 | Case( 283 | view is form 284 | and not view should be form 285 | and ( 10 ≤ FileMaker version 286 | or not status area should be on ); 287 | +3; 288 | not view is form 289 | and view should be form 290 | and ( 10 ≤ FileMaker version 291 | or not status area is on ); 292 | -3; 293 | /* otherwise */ 294 | 0 ); 295 | 296 | /* Calculate sizes of controls for the target window. */ 297 | 298 | controls width = 299 | full controls width + status area width adjustment + view width adjustment; 300 | controls height = 301 | full controls height + status area height adjustment; 302 | 303 | /* Get fresh dimensions of desktop. The controller uses 304 | them to calculate content scale, if necessary, to align 305 | against desktop, if this is the reference and finally to 306 | make sure the window stays on the desktop.*/ 307 | 308 | // Under Windows FileMaker reports 4 pixels less in either dimension 309 | 310 | platform adjustment = 311 | Case( on Windows; -4 ); 312 | 313 | desktop width = 314 | Get( WindowDesktopWidth ) 315 | + platform adjustment; 316 | 317 | desktop height = 318 | Get ( WindowDesktopHeight ) 319 | + platform adjustment 320 | + status bar adjustment 321 | + toolbar adjustment; 322 | 323 | /* There are two zoom levels: initial zoom and target 324 | zoom. The initial zoom level is read from saved settings 325 | or from the current window; the target zoom is what 326 | developer specifies via a parameter or in window size. 327 | */ 328 | 329 | source zoom = 330 | Case( 331 | has saved settings; 332 | Window Settings::Zoom; 333 | // otherwise use current window's 334 | GetAsNumber( Get( WindowZoomLevel ) ) ); 335 | 336 | target zoom = 337 | Case( 338 | not IsEmpty( script.parameter( "zoom" ) ); 339 | GetAsNumber( script.parameter( "zoom" ) ); 340 | has saved settings; 341 | Window Settings::Zoom; 342 | has default size; 343 | Let( user choice = Window Size::Zoom; 344 | Case( 345 | user choice = "Same"; 346 | GetAsNumber( Get( WindowZoomLevel ) ); 347 | /* otherwise */ 348 | GetAsNumber( user choice ) ) ); 349 | // otherwise use current window's 350 | GetAsNumber( Get( WindowZoomLevel ) ) ); 351 | 352 | initial scale = 353 | target zoom / source zoom; 354 | 355 | scale = 356 | Case( 357 | not IsEmpty( script.parameter( "zoom" ) ) 358 | or has saved settings; 359 | initial scale; 360 | has default size 361 | and Set Contains( Window Size::Options; "Scale down to fit" ); 362 | Let( [ 363 | desktop width = 364 | desktop width - 2 * scale margin; 365 | desktop height = 366 | desktop height - 2 * scale margin; 367 | max horizontal scale = 368 | ( desktop width - controls width ) / content width; 369 | max vertical scale = 370 | ( desktop height - controls height ) / content height; 371 | min max scale = 372 | Min( initial scale; max vertical scale; max horizontal scale ) ]; 373 | 374 | Case( 375 | 4 ≤ min max scale; 4; 376 | 3 ≤ min max scale; 3; 377 | 2 ≤ min max scale; 2; 378 | 1.5 ≤ min max scale; 1.5; 379 | 1 ≤ min max scale; 1; 380 | .75 ≤ min max scale; .75; 381 | .5 ≤ min max scale; .5; 382 | /* otherwise use smallest */ .25 ) ); 383 | // otherwise 384 | initial scale ); 385 | 386 | /* Calculate the final zoom and almost final width and 387 | height; on Mac the height can still be changed to 388 | account for status toolbar behavior. */ 389 | 390 | globals_wc_width = 391 | Ceiling( content width * scale ) + controls width; 392 | globals_wc_height = 393 | Ceiling( content height * scale ) + controls height; 394 | $zoom = 395 | Case( 396 | not IsEmpty( script.parameter( "zoom" ) ) 397 | or has saved settings; 398 | target zoom; 399 | has default size 400 | and Set Contains( Window Size::Options; "Scale down to fit" ); 401 | scale * 100; 402 | // otherwise 403 | target zoom ); 404 | 405 | /* If the user doesn't specify the “alignment” 406 | parameter, the Alignment field may fetch the default 407 | alignment and if the user specifies the parameter, the 408 | script copies it in the Alignment field. So things looks 409 | identical, but the situation is different; if the user 410 | specifies alignment explicitly, this overrides saved top 411 | and left settings, but if alignment is default, saved 412 | settings take precedence. The controller tells the 413 | difference by checking the parameter. */ 414 | 415 | explicit alignment = 416 | not IsEmpty( script.parameter( "alignment" ) ); 417 | 418 | /* The top and left may be specified explicitly from 419 | parameters, saved settings and current window (as a 420 | fallback) or be calculated dynamically from alignment. 421 | */ 422 | 423 | explicit left = 424 | Case( 425 | not IsEmpty( script.parameter( "left" ) ); 426 | GetAsNumber( script.parameter( "left" ) ); 427 | not explicit alignment and has saved settings; 428 | Window Settings::Left; 429 | not has default size; 430 | Get( WindowLeft ) ); 431 | 432 | explicit top = 433 | Case( 434 | not IsEmpty( script.parameter( "top" ) ); 435 | GetAsNumber( script.parameter( "top" ) ); 436 | not explicit alignment and has saved settings; 437 | Window Settings::Top; 438 | not has default size; 439 | Get( WindowTop ) ); 440 | 441 | // The controller calculates position from alignment only if it didn't get explicit position so far. 442 | 443 | bounds = 444 | Case( IsEmpty( explicit top ) or IsEmpty( explicit left ); 445 | Let( reference = Window Alignment::Reference; 446 | Case( 447 | reference = "desktop"; 448 | "0 0 " 449 | & desktop width 450 | & " " & desktop height; 451 | 452 | reference = "current window"; 453 | Get( WindowLeft ) 454 | & " " & Get( WindowTop ) 455 | & " " & ( Get( WindowLeft ) + Get( WindowWidth ) ) 456 | & " " & ( Get( WindowTop ) + Get( WindowHeight ) ); 457 | 458 | reference = "object"; 459 | GetLayoutObjectAttribute( Window Alignment::Object Name; 460 | "bounds" ) ) ) ); 461 | 462 | initial left = 463 | If( not IsEmpty( explicit left ); 464 | explicit left; 465 | // else 466 | Let( [ 467 | globals_wc_left = 468 | MiddleWords( bounds; 1; 1 ); 469 | right = 470 | MiddleWords( bounds; 3; 1 ); 471 | point = 472 | Window Alignment::Horizontal Point; 473 | reference = 474 | Case( 475 | point = "Left"; 476 | globals_wc_left; 477 | point = "Right"; 478 | right; 479 | point = "Center"; 480 | globals_wc_left + Floor( ( right - globals_wc_left ) / 2 ) ); 481 | proxy = 482 | Window Alignment::Horizontal Proxy ]; 483 | 484 | Window Alignment::Horizontal Offset 485 | + Case( 486 | proxy = "Left edge"; 487 | reference; 488 | proxy = "Right edge"; 489 | reference - globals_wc_width; 490 | proxy = "Center"; 491 | reference - Floor( globals_wc_width / 2 ) ) ) ); 492 | 493 | initial top = 494 | If( not IsEmpty( explicit top ); 495 | explicit top; 496 | // else 497 | Let( [ 498 | globals_wc_top = 499 | MiddleWords( bounds; 2; 1 ); 500 | bottom = 501 | MiddleWords( bounds; 4; 1 ); 502 | point = 503 | Window Alignment::Vertical Point; 504 | reference = 505 | Case( 506 | point = "Top"; 507 | globals_wc_top; 508 | point = "Bottom"; 509 | bottom; 510 | point = "Middle"; 511 | globals_wc_top + Floor( ( bottom - globals_wc_top ) / 2 ) ); 512 | proxy = 513 | Window Alignment::Vertical Proxy ]; 514 | 515 | Window Alignment::Vertical Offset 516 | + Case( 517 | proxy = "Top edge"; 518 | reference; 519 | proxy = "Bottom edge"; 520 | reference - globals_wc_height; 521 | proxy = "Middle"; 522 | reference - Floor( globals_wc_height / 2 ) ) ) ); 523 | 524 | // Finally make sure the window stays on screen 525 | 526 | $left = 527 | Let( [ 528 | minimum left = 529 | safety margin; 530 | maximum left = 531 | desktop width - globals_wc_width - 2 * safety margin ]; 532 | 533 | Max( minimum left; Min( maximum left; initial left ) ) ); 534 | 535 | $top = 536 | Let( [ 537 | minimum top = 538 | safety margin; 539 | maximum top = 540 | desktop height - globals_wc_height - 2* safety margin ]; 541 | 542 | Max( minimum top; Min( maximum top; initial top ) ) ); 543 | 544 | /* And finally adjust the height, if necessary. */ 545 | $width = 546 | globals_wc_width; 547 | $height = 548 | globals_wc_height + Mac open adjustment ]; 549 | 550 | "" /* Nothing to output */ ) ) --------------------------------------------------------------------------------