<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://wiki.flightgear.org/w/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Clm76</id>
	<title>FlightGear wiki - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.flightgear.org/w/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Clm76"/>
	<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/Special:Contributions/Clm76"/>
	<updated>2026-04-05T08:38:08Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.39.6</generator>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Nasal_library&amp;diff=138674</id>
		<title>Nasal library</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Nasal_library&amp;diff=138674"/>
		<updated>2023-11-12T10:31:53Z</updated>

		<summary type="html">&lt;p&gt;Clm76: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Nasal Navigation|nocat=1}}&lt;br /&gt;
This page documents the global '''library functions and variables''' of FlightGear's built-in scripting language, [[Nasal]]. This includes ''[[#Core library functions|core library functions]]'', which were included in Nasal before its integration into FlightGear, the ''[[#Extension functions|extension functions]]'', which have been subsequently added, and are specifically designed for FlightGear, and the ''[[#variables|global variables]]'', which are conversion variables, added with extension functions, for converting between units. The relevant folders in [[Git]] are:&lt;br /&gt;
* {{flightgear file|src/Scripting}}&lt;br /&gt;
* {{simgear file|simgear/nasal}}&lt;br /&gt;
&lt;br /&gt;
All these functions and variables are in the global namespace, that is, they are directly accessible (e.g., one can call &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; inline&amp;gt;magvar()&amp;lt;/syntaxhighlight&amp;gt; instead of &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; inline&amp;gt;namespace.magvar()&amp;lt;/syntaxhighlight&amp;gt;). However, if a namespace must be used, &amp;lt;code&amp;gt;globals&amp;lt;/code&amp;gt; is the correct namespace, but using it is not recommended. For a more complete explanation, see [[Nasal Namespaces in-depth]].&lt;br /&gt;
&lt;br /&gt;
{{tip|Copy &amp;amp; paste the examples into your [[Nasal Console]] and execute them to see what they do.|width=70%}}&lt;br /&gt;
&lt;br /&gt;
== Core library functions ==&lt;br /&gt;
This is the list of the basic '''core library functions.''' Most of these functions were part of the original Nasal library (before its integration in to FlightGear), while some have been added or changed over time.  See also:&lt;br /&gt;
* http://plausible.org/nasal/lib.html ([http://web.archive.org/web/20101010094553/http://plausible.org/nasal/lib.html archive])&lt;br /&gt;
* {{simgear file|simgear/nasal/lib.c}} ([http://sourceforge.net/p/flightgear/simgear/ci/next/log/?path=/simgear/nasal/lib.c history])&lt;br /&gt;
&lt;br /&gt;
=== append() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = append(vector, element[, element[, ...]]);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=42|t=Source}}&lt;br /&gt;
|text = This function appends, or adds, the given element(s) to the end of the vector given in the first argument.  Returns the vector operated on.&lt;br /&gt;
|param1 = vector&lt;br /&gt;
|param1text = The vector to which the arguments will be appended.&lt;br /&gt;
|param2 = element&lt;br /&gt;
|param2text = An element to be added to the vector.&lt;br /&gt;
|example1 = &lt;br /&gt;
var vector = [1, 2, 3]; # Initialize the vector&lt;br /&gt;
append(vector, 4); # Append the number 4 to the end of the vector&lt;br /&gt;
debug.dump(vector); # Print the contents of the vector&lt;br /&gt;
|example2 = &lt;br /&gt;
var vector = [1, 2, 3]; # Initialize the vector&lt;br /&gt;
append(vector, 4, 5, 6); # Append the numbers 4, 5, and 6 to the end of the vector&lt;br /&gt;
debug.dump(vector); # Print the contents of the vector&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== bind() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = bind(function, locals[, outer_scope]);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=502|t=Source}}&lt;br /&gt;
|text = This creates a new function object. A function in Nasal is three things: the actual code, a hash/namespace of local variables available to the function namespace, and the closure object of that namespace. These correspond to the three arguments respectively.&lt;br /&gt;
|param1 = function&lt;br /&gt;
|param1text = Function to evaluate.&lt;br /&gt;
|param2 = locals&lt;br /&gt;
|param2text = Hash containing values that will become the namespace (first closure) for the function.&lt;br /&gt;
|param3 = outer_scope&lt;br /&gt;
|param3text = Optional function which is bound to the next closure. This can be bound to yet another, making a linked list.&lt;br /&gt;
|example1 = # This is a namespace/hash with a single member, named &amp;quot;key,&amp;quot; which is initialized to 12 &lt;br /&gt;
var Namespace = {&lt;br /&gt;
    key: 12&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
# This is different namespace/hash containing a function&lt;br /&gt;
# dividing a variable &amp;quot;key&amp;quot; (which is unavailable/nil in this namespace) by 2&lt;br /&gt;
var AnotherNamespace = {&lt;br /&gt;
    ret: func {&lt;br /&gt;
        key /= 2;&lt;br /&gt;
    }&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
# To see that key is not available, try to call AnotherNamespace.ret() first&lt;br /&gt;
call(AnotherNamespace.ret, [], nil, nil, var errors = []);&lt;br /&gt;
if(size(errors)){&lt;br /&gt;
    print(&amp;quot;Key could not be divided/resolved!&amp;quot;);&lt;br /&gt;
    debug.printerror(errors);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# Associate the AnotherNamespace.ret() function with the first namespace&lt;br /&gt;
# so that &amp;quot;key&amp;quot; is now available&lt;br /&gt;
var function = bind(AnotherNamespace.ret, Namespace);&lt;br /&gt;
&lt;br /&gt;
# Invoke the new function&lt;br /&gt;
function();&lt;br /&gt;
&lt;br /&gt;
# Print out the value of Namespace.key&lt;br /&gt;
# It was changed to 12 from 6 by AnotherNamespace.ret()&lt;br /&gt;
print(Namespace.key);&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== call() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = call(func[, args[, me[, locals[, error]]]);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=247|t=Source}}&lt;br /&gt;
|text = Calls the given function with the given arguments and returns the result.  This function is very useful as it allows much more control over function calls and catches any errors or {{func link|die()}} calls that would normally trigger run-time errors cancelling execution of the script otherwise. &lt;br /&gt;
|param1 = func&lt;br /&gt;
|param1text = Function to execute.&lt;br /&gt;
|param2 = args&lt;br /&gt;
|param2text = Vector containing arguments to give to the called function.&lt;br /&gt;
|param3 = me&lt;br /&gt;
|param3text = &amp;lt;code&amp;gt;'''me'''&amp;lt;/code&amp;gt; reference for the function call (i.e., for method calls). If given, this will override any &amp;lt;code&amp;gt;'''me'''&amp;lt;/code&amp;gt; value existing in the namespace (locals argument).&lt;br /&gt;
|param4 = locals&lt;br /&gt;
|param4text = A hash with key/value pairs that will be available to the called function, typically used as the namespace for the function to be called.&lt;br /&gt;
|param5 = error&lt;br /&gt;
|param5text = A vector to append errors to.  If the called function generates an error, the error, place, and line will be written to this.  These errors can be printed using {{func link|printerror()|debug}}.&lt;br /&gt;
|example1 =&lt;br /&gt;
# prints &amp;quot;Called from call()&amp;quot;&lt;br /&gt;
call(func {&lt;br /&gt;
    print(&amp;quot;Called from call()&amp;quot;);&lt;br /&gt;
});&lt;br /&gt;
|example2 =&lt;br /&gt;
# prints &amp;quot;a = 1 : b = 2&lt;br /&gt;
call(func(a, b){&lt;br /&gt;
        print(&amp;quot;a = &amp;quot;, a, &amp;quot; : b = &amp;quot;, b);&lt;br /&gt;
    },&lt;br /&gt;
    [1, 2]&lt;br /&gt;
);&lt;br /&gt;
|example3 =&lt;br /&gt;
var Hash = {&lt;br /&gt;
    new: func {&lt;br /&gt;
        var m = { parents: [Hash] };&lt;br /&gt;
&lt;br /&gt;
        m.el1 = &amp;quot;string1&amp;quot;;&lt;br /&gt;
        m.el2 = &amp;quot;string2&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
        return m;&lt;br /&gt;
    }&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
# prints &amp;quot;me.el1 = string1&amp;quot;, then &amp;quot;me.el2 = string2&amp;quot; on the next line&lt;br /&gt;
call(func(a, b){        &lt;br /&gt;
        print(&amp;quot;me.el&amp;quot;, a, &amp;quot; = &amp;quot;, me[&amp;quot;el&amp;quot; ~ a]);      &lt;br /&gt;
        print(&amp;quot;me.el&amp;quot;, b, &amp;quot; = &amp;quot;, me[&amp;quot;el&amp;quot; ~ b]);&lt;br /&gt;
    },&lt;br /&gt;
    [1, 2],&lt;br /&gt;
    Hash.new()&lt;br /&gt;
);&lt;br /&gt;
|example4 =&lt;br /&gt;
# prints the value of math.pi&lt;br /&gt;
call(func {&lt;br /&gt;
        print(pi);&lt;br /&gt;
    }, nil, nil, &lt;br /&gt;
    math&lt;br /&gt;
);&lt;br /&gt;
|example5 =&lt;br /&gt;
call(func {&lt;br /&gt;
        print(math.ip); # math.ip doesn't exist&lt;br /&gt;
    }, nil, nil, nil,&lt;br /&gt;
    var errs = []&lt;br /&gt;
);&lt;br /&gt;
debug.printerror(errs); # The error is caught and printed using debug.printerror()&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== caller() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = caller([level]);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=404|t=Source}}&lt;br /&gt;
|text = Returns a vector containing a record from the current call stack.  The level numbering starts from the currently executing function (level 0).  Level 1 (the default) is the caller of the current function, and so on.&lt;br /&gt;
&lt;br /&gt;
The result is a four-element vector containing '''[0]''' a hash of local variables, '''[1]''' the function object, '''[2]''' the full source file name (incl. path) and '''[3]''' the line number. &lt;br /&gt;
|param1 = level&lt;br /&gt;
|param1text = Optional integer specifying the stack level to return a result from.  Defaults to 1 (i.e. the caller of the currently executing function).&lt;br /&gt;
|example1 =&lt;br /&gt;
var myFunction = func(a, b){&lt;br /&gt;
    debug.dump(caller(0)[0]); # prints a hash of local variables, including arguments a and b&lt;br /&gt;
    return 2 * 2;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;2 x 2 = &amp;quot;, myFunction(2, 2));&lt;br /&gt;
|example2 =&lt;br /&gt;
var get_arg_value = func(){&lt;br /&gt;
    print(&amp;quot;Argument to myFunc = &amp;quot;, caller(1)[0]['a']); # print the value of myFunc's single argument, using caller()&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
var myFunc = func(a){&lt;br /&gt;
    get_arg_value();&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
myFunc(3);&lt;br /&gt;
|example3text = This is a real example taken from {{fgdata file|Nasal/canvas/MapStructure.nas}}.  Function &amp;lt;code&amp;gt;r()&amp;lt;/code&amp;gt; (above the TODOs) returns a hash with the key/value pairs as per its arguments. For example, something like this is returned: &amp;lt;code&amp;gt;{ name: &amp;quot;&amp;lt;name&amp;gt;&amp;quot;, vis: 1, zindex: nil }&amp;lt;/code&amp;gt;.&lt;br /&gt;
|example3 =&lt;br /&gt;
var MapStructure_selfTest = func() {&lt;br /&gt;
	var temp = {};&lt;br /&gt;
	temp.dlg = canvas.Window.new([600,400],&amp;quot;dialog&amp;quot;);&lt;br /&gt;
	temp.canvas = temp.dlg.createCanvas().setColorBackground(1,1,1,0.5);&lt;br /&gt;
	temp.root = temp.canvas.createGroup();&lt;br /&gt;
	var TestMap = temp.root.createChild(&amp;quot;map&amp;quot;);&lt;br /&gt;
	TestMap.setController(&amp;quot;Aircraft position&amp;quot;);&lt;br /&gt;
	TestMap.setRange(25); # TODO: implement zooming/panning via mouse/wheel here, for lack of buttons :-/&lt;br /&gt;
	TestMap.setTranslation(&lt;br /&gt;
		temp.canvas.get(&amp;quot;view[0]&amp;quot;)/2,&lt;br /&gt;
		temp.canvas.get(&amp;quot;view[1]&amp;quot;)/2&lt;br /&gt;
	);&lt;br /&gt;
	var r = func(name,vis=1,zindex=nil) return caller(0)[0];&lt;br /&gt;
	# TODO: we'll need some z-indexing here, right now it's just random&lt;br /&gt;
	# TODO: use foreach/keys to show all layers in this case by traversing SymbolLayer.registry direclty ?&lt;br /&gt;
	# maybe encode implicit z-indexing for each lcontroller ctor call ? - i.e. preferred above/below order ?&lt;br /&gt;
	foreach(var type; [r('TFC',0),r('APT'),r('DME'),r('VOR'),r('NDB'),r('FIX',0),r('RTE'),r('WPT'),r('FLT'),r('WXR'),r('APS'), ] ) &lt;br /&gt;
		TestMap.addLayer(factory: canvas.SymbolLayer, type_arg: type.name,&lt;br /&gt;
					visible: type.vis, priority: type.zindex,&lt;br /&gt;
		);&lt;br /&gt;
}; # MapStructure_selfTest&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== chr() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = chr(code);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=175|t=Source}}&lt;br /&gt;
|text = Returns a character as per the single argument. Extended ASCII is supported (see http://www.asciitable.com/ for a list of supported characters), although this may vary between different systems.  For a list of the most commonly used characters, see the {{wikipedia|ASCII#ASCII printable code chart|ASCII printable code chart}} ('''Dec''' column). The following table lists supported control characters, along with their equivalent control characters in Nasal strings.  {{Note|In Nasal, only strings enclosed with double-quotes (&amp;lt;code&amp;gt;&amp;quot;string&amp;quot;&amp;lt;/code&amp;gt;) supports control chracters.  Strings in single quotes (&amp;lt;code&amp;gt;'string'&amp;lt;/code&amp;gt;) do not.}}&lt;br /&gt;
{{{!}} class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Code !! Name !! Equivalent to&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} 10 {{!!}} {{Wikipedia|Newline}} {{!!}} &amp;lt;code&amp;gt;\n&amp;lt;/code&amp;gt;&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} 9 {{!!}} {{Wikipedia|Tab key#Tab characters|Horizontal tab}} {{!!}} &amp;lt;code&amp;gt;\t&amp;lt;/code&amp;gt;&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} 13 {{!!}} {{Wikipedia|Carriage return}} {{!!}} &amp;lt;code&amp;gt;\r&amp;lt;/code&amp;gt;&lt;br /&gt;
{{!}}}&lt;br /&gt;
|param1 = code&lt;br /&gt;
|param1text = Integer character code for the desired glyph.&lt;br /&gt;
|example1 = print(&amp;quot;Code 65 = &amp;quot;, chr(65)); # prints &amp;quot;Code 65 = A&amp;quot;&lt;br /&gt;
|example2text = This example displays all of the characters in a list, in the format &amp;lt;code&amp;gt;Code '''n''' = &amp;gt;'''char'''&amp;lt;&amp;lt;/code&amp;gt;, '''n''' being the index, and '''char''' being the character.&lt;br /&gt;
|example2 =&lt;br /&gt;
for(var i = 0; i &amp;lt;= 255; i += 1){&lt;br /&gt;
    print(&amp;quot;Code &amp;quot;, i, &amp;quot; = &amp;gt;&amp;quot;, chr(i), &amp;quot;&amp;lt;&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== closure() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = closure(func[, level]);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=421|t=Source}}&lt;br /&gt;
|text = Returns the hash table containing the lexical namespace of the given function. The level numbering start with level 0 being the namespace of '''func'''. &lt;br /&gt;
|param1 = func&lt;br /&gt;
|param1text = Function to evaluate.&lt;br /&gt;
|param2 = level&lt;br /&gt;
|param2text = Optional integer specifying the scope level.  Defaults to 0 (the namespace of '''func''').&lt;br /&gt;
|example1 =&lt;br /&gt;
var get_math_e = func {&lt;br /&gt;
    return e; # return the value of math.e&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var myFunction = bind(get_math_e, math); # bind get_math_e to the math namespace, so that math.e is immediately available to get_math_e&lt;br /&gt;
debug.dump(closure(myFunction)); # print the namespace of get_math_e&lt;br /&gt;
&lt;br /&gt;
print(myFunction());&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== cmp() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = cmp(a, b);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=112|t=Source}}&lt;br /&gt;
|text = Compares two strings, returning -1 if '''a''' is less than '''b''', 0 if they are identical and 1 if '''a''' is greater than '''b'''. &lt;br /&gt;
|param1 = a&lt;br /&gt;
|param1text = First string argument for comparison.&lt;br /&gt;
|param2 = b&lt;br /&gt;
|param2text = Second string argument for comparison.&lt;br /&gt;
|example1 = print(cmp(&amp;quot;1&amp;quot;, &amp;quot;two&amp;quot;)); # prints -1&lt;br /&gt;
|example2 = print(cmp(&amp;quot;string&amp;quot;, &amp;quot;string&amp;quot;)); # prints 0&lt;br /&gt;
|example3 = print(cmp(&amp;quot;one&amp;quot;, &amp;quot;2&amp;quot;)); # prints 1&lt;br /&gt;
|example4 = print(cmp(&amp;quot;string1&amp;quot;, &amp;quot;string2&amp;quot;)); # prints -1&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== compile() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = compile(code[, filename]);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=220|t=Source}}&lt;br /&gt;
|text = Compiles the specified code string and returns a function object bound to the current lexical context.  If there is an error, the function dies, with the argument to {{func link|die()}} being '''filename'''.&lt;br /&gt;
|param1 = code&lt;br /&gt;
|param1text = String containing Nasal code to be compiled.&lt;br /&gt;
|param2 = filename&lt;br /&gt;
|param2text = Optional string used for error messages/logging. Defaults to &amp;lt;code&amp;gt;&amp;lt;compile&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
|example1 = &lt;br /&gt;
var myCode = 'print(&amp;quot;hello&amp;quot;);';&lt;br /&gt;
var helloFunc = compile(myCode, &amp;quot;myCode&amp;quot;);&lt;br /&gt;
helloFunc();&lt;br /&gt;
|example2text = &amp;lt;code&amp;gt;compile&amp;lt;/code&amp;gt; is very convenient to support Nasal loaded from other files.  For instance, [[PropertyList XML files]] (such as GUI dialogs) may contain embedded Nasal sections that need to be parsed, processed and compiled.  For an example of how to do this, save the below XML code as &amp;lt;tt&amp;gt;''[[$FG_ROOT]]/gui/dialogs/test.xml''&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?xml version=&amp;quot;1.0&amp;quot;?&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;PropertyList&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nasal&amp;gt;&amp;lt;![CDATA[&lt;br /&gt;
print(&amp;quot;You have FlightGear v&amp;quot;, getprop(&amp;quot;/sim/version/flightgear&amp;quot;));&lt;br /&gt;
]]&amp;gt;&amp;lt;/nasal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/PropertyList&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Now, start FlightGear and execute this code in the [[Nasal Console]].&lt;br /&gt;
|example2 =&lt;br /&gt;
# Build the path&lt;br /&gt;
var FGRoot = getprop(&amp;quot;/sim/fg-root&amp;quot;);&lt;br /&gt;
var filename = &amp;quot;/gui/dialogs/test.xml&amp;quot;;&lt;br /&gt;
var path = FGRoot ~ filename;&lt;br /&gt;
&lt;br /&gt;
var blob = io.read_properties(path);&lt;br /&gt;
var script = blob.getValues().nasal; # Get the nasal string&lt;br /&gt;
&lt;br /&gt;
# Compile the script.  We're passing the filename here for better runtime diagnostics &lt;br /&gt;
var code = call(func {&lt;br /&gt;
    compile(script, filename);&lt;br /&gt;
}, nil, nil, var compilation_errors = []);&lt;br /&gt;
&lt;br /&gt;
if(size(compilation_errors)){&lt;br /&gt;
    die(&amp;quot;Error compiling code in: &amp;quot; ~ filename);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# Invoke the compiled script, equivalent to code(); &lt;br /&gt;
# We're using call() here to detect errors:&lt;br /&gt;
call(code, [], nil, nil, var runtime_errors = []);&lt;br /&gt;
&lt;br /&gt;
if(size(runtime_errors)){&lt;br /&gt;
    die(&amp;quot;Error calling code compiled loaded from: &amp;quot; ~ filename);&lt;br /&gt;
}&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== contains() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = contains(hash, key);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=184|t=Source}}&lt;br /&gt;
|text = Returns 1 (True) if the hash contains the specified key, or 0 (False) if not.&lt;br /&gt;
|param1 = hash&lt;br /&gt;
|param1text = The hash to search in.&lt;br /&gt;
|param2 = key&lt;br /&gt;
|param2text = The scalar to be searched for, contained as a key in the hash.&lt;br /&gt;
|example1 =&lt;br /&gt;
# Initialize a hash&lt;br /&gt;
var hash = {&lt;br /&gt;
    element: &amp;quot;value&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
print(contains(hash, &amp;quot;element&amp;quot;) ? &amp;quot;Yes&amp;quot; : &amp;quot;No&amp;quot;); # This will print &amp;quot;Yes&amp;quot;&lt;br /&gt;
|example2 =&lt;br /&gt;
# Initialize a hash&lt;br /&gt;
var hash = {&lt;br /&gt;
    element: &amp;quot;value&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
print(contains(hash, &amp;quot;element2&amp;quot;) ? &amp;quot;Yes&amp;quot; : &amp;quot;No&amp;quot;); # This will print &amp;quot;No&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===contains()===&lt;br /&gt;
{{Requires commit|desc=Vector support|details=see {{Merge-request|project=fgdata|id=305}}|commit=ee39abbd3b70c9b6d5e3a1c4ccedddaac1a92b11|repo=Simgear}}&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = contains(vector, item);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=184|t=Source}}&lt;br /&gt;
|text = Returns 1 (True) if the vector contains the specified item, or 0 (False) if not.&lt;br /&gt;
|param1 = vector&lt;br /&gt;
|param1text = The vector to search in.&lt;br /&gt;
|param2 = item&lt;br /&gt;
|param2text = The object to be searched for in the vector.&lt;br /&gt;
|example1 =&lt;br /&gt;
# Initialize a hash&lt;br /&gt;
var vec = [&amp;quot;element&amp;quot;, &amp;quot;foo&amp;quot;];&lt;br /&gt;
print(contains(hash, &amp;quot;element&amp;quot;) ? &amp;quot;Yes&amp;quot; : &amp;quot;No&amp;quot;); # This will print &amp;quot;Yes&amp;quot;&lt;br /&gt;
|example2 =&lt;br /&gt;
# Initialize a hash&lt;br /&gt;
var vec = [&amp;quot;element&amp;quot;, &amp;quot;foo&amp;quot;];&lt;br /&gt;
print(contains(hash, &amp;quot;element2&amp;quot;) ? &amp;quot;Yes&amp;quot; : &amp;quot;No&amp;quot;); # This will print &amp;quot;No&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===delete() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = delete(hash, key);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=83|t=Source}}&lt;br /&gt;
|text = Deletes the key from the hash if it exists. Operationally, this is NOT identical to setting the hash value specified by the key to &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; as the key will stay in the hash (at least for a while). This variant potentially frees storage by deleting the reference to the key and by shrinking the hash.  Returns the hash that has been operated on.&lt;br /&gt;
|param1 = hash&lt;br /&gt;
|param1text = The hash from which to delete the key.&lt;br /&gt;
|param2 = key&lt;br /&gt;
|param2text = The scalar to be deleted, contained as a key in the hash.&lt;br /&gt;
|example1 =&lt;br /&gt;
# Initialize the hash&lt;br /&gt;
var hash = {&lt;br /&gt;
    element1: &amp;quot;value1&amp;quot;,&lt;br /&gt;
    element2: &amp;quot;value2&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
delete(hash, &amp;quot;element1&amp;quot;); # Delete element1&lt;br /&gt;
debug.dump(hash); # prints the hash, which is now minus element1&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===die()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = die(error);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=288|t=Source}}&lt;br /&gt;
|text = Terminates execution and unwinds the stack.  The place and the line will be added to the '''error'''.  This invokes the same internal exception handler used for internal runtime errors. Use this to signal fatal errors, or to implement exception handling. The error thrown (including internal runtime errors) can be caught with {{func link|call()}}.&lt;br /&gt;
|param1 = error&lt;br /&gt;
|param1text = String describing the error.&lt;br /&gt;
:{{inote|This parameter is technically optional, but it is highly recommended to use it.}}&lt;br /&gt;
|example1 = &lt;br /&gt;
print(&amp;quot;Will print&amp;quot;);&lt;br /&gt;
die(&amp;quot;Don't go any further!&amp;quot;); &lt;br /&gt;
print(&amp;quot;Won't print&amp;quot;); # Will not be printed because die() stops the process&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== find()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = find(needle, haystack);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=450|t=Source}}&lt;br /&gt;
|text = Finds and returns the index of the first occurrence of the string '''needle''' in the string '''haystack''', or -1 if no such occurrence was found.&lt;br /&gt;
|param1 = needle&lt;br /&gt;
|param1text = String to search for.&lt;br /&gt;
|param2 = haystack&lt;br /&gt;
|param2text = String to search in.&lt;br /&gt;
|example1 = print(find(&amp;quot;c&amp;quot;, &amp;quot;abcdef&amp;quot;)); # prints 2&lt;br /&gt;
|example2 = print(find(&amp;quot;x&amp;quot;, &amp;quot;abcdef&amp;quot;)); # prints -1&lt;br /&gt;
|example3 = print(find(&amp;quot;cd&amp;quot;, &amp;quot;abcdef&amp;quot;)); # prints 2&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===ghosttype()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = ghosttype(ghost);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=207|t=Source}}&lt;br /&gt;
|text = Returns a string containing either a descriptive name of a ghost (a raw C/C++ object), or a unique id (the pointer to the C/C++ &amp;lt;code&amp;gt;naGhostType&amp;lt;/code&amp;gt; instance) if no name has been set.  Ghost is an acronym that stands for '''G'''arbage-collected '''H'''andle to '''O'''ut'''S'''ide '''T'''hingy.&lt;br /&gt;
|param1 = ghost&lt;br /&gt;
|param1text = Ghost to return a description for.&lt;br /&gt;
|example1 = print(ghosttype(airportinfo())); # prints &amp;quot;airport&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===id()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = id(object);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=570|t=Source}}&lt;br /&gt;
|text = Returns a string containing information on the type and ID of the object provided in the single argument.  The information is returned in the form of &amp;lt;code&amp;gt;'''&amp;lt;type&amp;gt;''':'''&amp;lt;id&amp;gt;'''&amp;lt;/code&amp;gt;, where '''&amp;lt;type&amp;gt;''' is the type of object, and '''&amp;lt;id&amp;gt;''' is the ID.&lt;br /&gt;
|param1 = object&lt;br /&gt;
|param1text = Can be either of a string, a vector, a hash, a code, a function, or a ghost.&lt;br /&gt;
|example1 = print(id(&amp;quot;A&amp;quot;)); # prints &amp;quot;str:000000001624A590&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===int() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = int(number);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=90|t=Source}}&lt;br /&gt;
|text = Returns the integer part of the numeric value of the single argument, or &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; if none exists.&lt;br /&gt;
|param1 = number&lt;br /&gt;
|param1text = Number or string with just a number in it to return an integer from.&lt;br /&gt;
|example1 = print(int(23)); # prints &amp;quot;23&amp;quot;&lt;br /&gt;
|example2 = print(int(23.123)); # prints &amp;quot;23&amp;quot;&lt;br /&gt;
|example3 = debug.dump(int(&amp;quot;string&amp;quot;)); # prints &amp;quot;nil&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===keys()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = keys(hash);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=33|t=Source}}&lt;br /&gt;
|text = Returns a vector containing the list of keys found in the single hash argument. &lt;br /&gt;
|param1 = hash&lt;br /&gt;
|param1text = The hash to return the keys from.&lt;br /&gt;
|example1 = &lt;br /&gt;
# Initialize a hash&lt;br /&gt;
var hash = {&lt;br /&gt;
    element1: &amp;quot;value&amp;quot;,&lt;br /&gt;
    element2: &amp;quot;value&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
debug.dump(keys(hash)); # print the vector&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===left()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = left(string, length);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=149|t=Source}}&lt;br /&gt;
|version = 2.12&lt;br /&gt;
|commit = {{simgear commit|bd7163|t=commit}}&lt;br /&gt;
|text = Returns a substring of '''string''', starting from the left.&lt;br /&gt;
|param1 = string&lt;br /&gt;
|param1text = String to return part of.&lt;br /&gt;
|param2 = length&lt;br /&gt;
|param2text = Integer specifying the length of the substring to return.&lt;br /&gt;
|example1 = print(left(&amp;quot;string&amp;quot;, 2)); # prints &amp;quot;st&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== num()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = num(number);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=102|t=Source}}&lt;br /&gt;
|text = Returns the numerical value of the single string argument, or &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; if none exists. &lt;br /&gt;
|param1 = number&lt;br /&gt;
|param1text = String with just a number in it to return a number from.&lt;br /&gt;
|example1 = print(num(&amp;quot;23&amp;quot;)); # prints &amp;quot;23&amp;quot;&lt;br /&gt;
|example2 = print(num(&amp;quot;23.123&amp;quot;)); # prints &amp;quot;23.123&amp;quot;&lt;br /&gt;
|example3 = debug.dump(num(&amp;quot;string&amp;quot;)); # prints &amp;quot;nil&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===pop()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = pop(vector);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=50|t=Source}}&lt;br /&gt;
|text = Removes and returns the last element of the single vector argument, or &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; if the vector is empty. &lt;br /&gt;
|param1 = vector&lt;br /&gt;
|param1text = Vector to remove an element from.&lt;br /&gt;
|example1 = &lt;br /&gt;
var vector = [1, 2, 3];&lt;br /&gt;
pop(vector);&lt;br /&gt;
debug.dump(vector); # prints &amp;quot;[1, 2]&amp;quot;&lt;br /&gt;
|example2 = &lt;br /&gt;
var vector = [1, 2, 3];&lt;br /&gt;
debug.dump(pop(vector)); # prints &amp;quot;3&amp;quot;&lt;br /&gt;
|example3 = &lt;br /&gt;
var vector = [];&lt;br /&gt;
debug.dump(pop(vector)); # prints &amp;quot;nil&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===right()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = right(string, length);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=161|t=Source}}&lt;br /&gt;
|version = 2.12&lt;br /&gt;
|commit = {{simgear commit|bd7163|t=commit}}&lt;br /&gt;
|text = Returns a substring of '''string''', starting from the right.&lt;br /&gt;
|param1 = string&lt;br /&gt;
|param1text = String to return part of.&lt;br /&gt;
|param2 = length&lt;br /&gt;
|param2text = Integer specifying the length of the substring to return.&lt;br /&gt;
|example1 = print(right(&amp;quot;string&amp;quot;, 2)); # prints &amp;quot;ng&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== setsize()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = setsize(vector, size);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=56|t=Source}}&lt;br /&gt;
|text = Sets the size of a vector. The first argument specifies a vector, the second a number representing the desired size of that vector. If the vector is currently larger than the specified size, it is truncated. If it is smaller, it is padded with &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; entries. Returns the vector operated upon. &lt;br /&gt;
|param1 = vector&lt;br /&gt;
|param1text = The vector to be operated on.&lt;br /&gt;
|param2 = size&lt;br /&gt;
|param2text = The desired size of the vector in number of entries.&lt;br /&gt;
|example1 = &lt;br /&gt;
var vector = [1, 2, 3]; # Initialize a vector&lt;br /&gt;
setsize(vector, 4);&lt;br /&gt;
debug.dump(vector); # print the vector&lt;br /&gt;
|example2 = &lt;br /&gt;
var vector = [1, 2, 3]; # Initialize a vector&lt;br /&gt;
setsize(vector, 2);&lt;br /&gt;
debug.dump(vector); # print the vector&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===size()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = size(object);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=23|t=Source}}&lt;br /&gt;
|text = Returns the size of the single argument. For strings, this is the length in bytes. For vectors, this is the number of elements. For hashes, it is the number of key/value pairs. If the argument is &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; or a number, this error will be thrown: &amp;lt;code&amp;gt;object has no size()&amp;lt;/code&amp;gt;.&lt;br /&gt;
|param1 = object&lt;br /&gt;
|param1text = Object to find the size of.  Must be a string, a vector or a hash.&lt;br /&gt;
|example1 = &lt;br /&gt;
var string = &amp;quot;string&amp;quot;;&lt;br /&gt;
print(size(string)); # prints &amp;quot;6&amp;quot;&lt;br /&gt;
|example2 =&lt;br /&gt;
var vector = [1, 2, 3];&lt;br /&gt;
print(size(vector)); # prints &amp;quot;3&amp;quot;&lt;br /&gt;
|example3 =&lt;br /&gt;
var hash = {&lt;br /&gt;
    element1: &amp;quot;value1&amp;quot;,&lt;br /&gt;
    element2: &amp;quot;value2&amp;quot;,&lt;br /&gt;
    element3: &amp;quot;value3&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
print(size(hash)); # prints &amp;quot;3&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== sort()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = sort(vector, function);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=542|t=Source}}&lt;br /&gt;
|text = Returns a vector containing the elements in the input '''vector''' sorted in according to the rule given by '''function'''. Implemented with the ANSI C {{func link|qsort()|link=http://www.cplusplus.com/reference/cstdlib/qsort/}}, &amp;lt;code&amp;gt;sort()&amp;lt;/code&amp;gt; is stable.  This means that if the rules in the first example are used, equal elements in the output vector will appear in the same relative order as they do in the input.  It is run in a loop, so '''function''' is run several times.&lt;br /&gt;
|param1 = vector&lt;br /&gt;
|param1text = Input vector to sort.&lt;br /&gt;
|param2 = function&lt;br /&gt;
|param2text = Function according to which the elements will be sorted by.  It should take two arguments and should return one of 1, 0, or -1.&lt;br /&gt;
{{{!}} class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Return value !! Meaning&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} less than 0 {{!!}} first argument should go before second argument&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} 0 {{!!}} first argument equals second argument&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} greater than 0 {{!!}} first argument should go after second argument&lt;br /&gt;
{{!}}}&lt;br /&gt;
&lt;br /&gt;
|example1text = This example sorts elements from smallest to greatest.&lt;br /&gt;
|example1 = &lt;br /&gt;
var sort_rules = func(a, b){&lt;br /&gt;
    if(a &amp;lt; b){&lt;br /&gt;
        return -1; # A should before b in the returned vector&lt;br /&gt;
    }elsif(a == b){&lt;br /&gt;
        return 0; # A is equivalent to b &lt;br /&gt;
    }else{&lt;br /&gt;
        return 1; # A should after b in the returned vector&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
debug.dump(sort([3, 2, 5, 6, 4, 1], sort_rules)); # prints &amp;quot;[1, 2, 3, 4, 5, 6]&amp;quot;&lt;br /&gt;
|example2text = This example sorts elements from greatest to smallest.&lt;br /&gt;
|example2 = &lt;br /&gt;
# Outputs the elements in reverse order (greatest to smallest)&lt;br /&gt;
var sort_rules = func(a, b){&lt;br /&gt;
    if(a &amp;lt; b){&lt;br /&gt;
        return 1; # -1 in the above example&lt;br /&gt;
    }elsif(a == b){&lt;br /&gt;
        return 0;&lt;br /&gt;
    }else{&lt;br /&gt;
        return -1; # 1 in the above example&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
debug.dump(sort([3, 2, 5, 6, 4, 1], sort_rules)); # prints &amp;quot;[6, 5, 4, 3, 2, 1]&amp;quot;&lt;br /&gt;
|example3text = This example sorts a vector of strings (runways for example) from smallest to greatest.&lt;br /&gt;
|example3 = &lt;br /&gt;
var runways = [&amp;quot;09R&amp;quot;,&amp;quot;27R&amp;quot;,&amp;quot;26L&amp;quot;,&amp;quot;09L&amp;quot;,&amp;quot;15&amp;quot;];&lt;br /&gt;
var rwy = sort(runways,func(a,b) cmp(a,b));&lt;br /&gt;
debug.dump(rwy); # prints ['09L','09R','15','26L','27R']&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== split()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = split(delimiter, string);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=460|t=Source}}&lt;br /&gt;
|text = Splits the input string into a vector of substrings bounded by occurrences of the delimiter substring.&lt;br /&gt;
|param1 = delimiter&lt;br /&gt;
|param1text = String that will split the substrings in the returned vector.&lt;br /&gt;
|param2 = string&lt;br /&gt;
|param2text = String to split up.&lt;br /&gt;
|example1 = debug.dump(split(&amp;quot;cd&amp;quot;, &amp;quot;abcdef&amp;quot;)); # prints &amp;quot;['ab', 'ef']&amp;quot;&lt;br /&gt;
|example2 = debug.dump(split(&amp;quot;.&amp;quot;, &amp;quot;3.2.0&amp;quot;)); # prints &amp;quot;[3, 2, 0]&amp;quot;&lt;br /&gt;
|example3 = debug.dump(split(&amp;quot;/&amp;quot;, &amp;quot;path/to/file&amp;quot;)); # prints &amp;quot;['path', 'to', 'file']&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===sprintf()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = &amp;lt;nowiki&amp;gt;sprintf(format[, arg[, arg, [...]]]);&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=355|t=Source}}&lt;br /&gt;
|text = Creates and returns a string formatted using ANSI C {{func link|vsnprintf()|link=http://en.cppreference.com/w/c/io/vfprintf}} &amp;lt;ref&amp;gt;&lt;br /&gt;
{{Cite web&lt;br /&gt;
|url = http://sourceforge.net/p/flightgear/simgear/ci/next/tree/simgear/nasal/lib.c#l308&lt;br /&gt;
|title = fgdata/simgear/simgear/nasal/lib.c, line 308&lt;br /&gt;
|accessdate = October 2015&lt;br /&gt;
}}&lt;br /&gt;
&amp;lt;/ref&amp;gt;.  Below is a table of supported format specifiers.&lt;br /&gt;
{{{!}} class=&amp;quot;wikitable&amp;quot; width=&amp;quot;75%&amp;quot;&lt;br /&gt;
{{!}}+ %[flags][width][.precision]specifier&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; {{!}} Flags&lt;br /&gt;
{{!-}}&lt;br /&gt;
! Flag !! Output&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;+&amp;lt;/code&amp;gt; {{!!}} Forces to precede the result with a plus or minus sign ('''+''' or '''-''') even for positive numbers. By default, only negative numbers are preceded with a '''-''' sign.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} ''space'' {{!!}} Prefixes non-signed numbers with a space.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;-&amp;lt;/code&amp;gt; {{!!}} Left-align the output of this placeholder (the default is to right-align the output) when the width option is specified.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt; {{!!}} Use 0 instead of spaces to pad a field when the width option is specified.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;#&amp;lt;/code&amp;gt; {{!!}} Used with &amp;lt;code&amp;gt;o&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;X&amp;lt;/code&amp;gt; specifiers the value is preceded with &amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;0x&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;0X&amp;lt;/tt&amp;gt; respectively for values different than zero. Used with &amp;lt;code&amp;gt;e&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;E&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;f&amp;lt;/code&amp;gt;, it forces the written output to contain a decimal point even if no digits would follow. By default, if no digits follow, no decimal point is written. Used with &amp;lt;code&amp;gt;g&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;G&amp;lt;/code&amp;gt; the result is the same as with &amp;lt;code&amp;gt;e&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;E&amp;lt;/code&amp;gt; but trailing zeros are not removed.&lt;br /&gt;
{{!-}}&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; {{!}} Width&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} colspan=&amp;quot;2&amp;quot; {{!}} Integer specifying the minimum number of characters to be returned. This includes the decimal point and decimal fraction as well as + or - signs.&lt;br /&gt;
{{!-}}&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; {{!}} Precision&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} colspan=&amp;quot;2&amp;quot; {{!}} Integer preceded by a dot specifying the number of decimal places to be written.&lt;br /&gt;
{{!-}}&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; {{!}} Specifiers&lt;br /&gt;
{{!-}}&lt;br /&gt;
! Specifier !! Output&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;d&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;i&amp;lt;/code&amp;gt; {{!!}} Signed decimal number.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;s&amp;lt;/code&amp;gt; {{!!}} A string&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;%&amp;lt;/code&amp;gt; {{!!}} Percent (%) character.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;c&amp;lt;/code&amp;gt; {{!!}} A single character assigned to a character code, the code given in an integer argument.  See http://www.asciitable.com/ for a list of supported characters and their codes.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;o&amp;lt;/code&amp;gt; {{!!}} Unsigned integer as an octal number.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;u&amp;lt;/code&amp;gt; {{!!}} Unsigned decimal integer.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;X&amp;lt;/code&amp;gt; {{!!}} Unsigned integer as a hexadecimal number.  If &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt; is used, any letters in the number are lowercase, while &amp;lt;code&amp;gt;X&amp;lt;/code&amp;gt; gives uppercase.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;e&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;E&amp;lt;/code&amp;gt; {{!!}} Double value in scientific notation (i.e., ''[-]ddd.ddd'''e'''[+/-]ddd''), with an exponent being denoted by &amp;lt;tt&amp;gt;e&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;E&amp;lt;/tt&amp;gt; depending on whether an upper or lowercase is used respectively.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;f&amp;lt;/code&amp;gt; {{!!}} Floating-point number, in fixed decimal notation, by default with 6 decimal places.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;F&amp;lt;/code&amp;gt; {{!!}} Appears to be available&amp;lt;ref&amp;gt;&lt;br /&gt;
{{Cite web&lt;br /&gt;
|url = http://sourceforge.net/p/flightgear/simgear/ci/next/tree/simgear/nasal/lib.c#l389&lt;br /&gt;
|title = fgdata/simgear/simgear/nasal/lib.c, line 389&lt;br /&gt;
|accessdate = October 2015&lt;br /&gt;
}}&lt;br /&gt;
&amp;lt;/ref&amp;gt;, but doesn't work.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;g&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;G&amp;lt;/code&amp;gt; {{!!}} Double in either normal or exponential notation, whichever is more appropriate for its magnitude. &amp;lt;code&amp;gt;g&amp;lt;/code&amp;gt; uses lower-case letters, &amp;lt;code&amp;gt;G&amp;lt;/code&amp;gt; uses upper-case letters. This type differs slightly from fixed-point notation in that insignificant zeroes to the right of the decimal point are not included.  Also, the decimal point is not included on whole numbers.&lt;br /&gt;
{{!}}}&lt;br /&gt;
&lt;br /&gt;
|param1 = format&lt;br /&gt;
|param1text = String specifying the format.  Can be used with or without a format specifiers.  See below for examples.&lt;br /&gt;
|param2 = arg&lt;br /&gt;
|param2text = Argument specifying a value to replace a format placeholder (such as &amp;lt;code&amp;gt;%d&amp;lt;/code&amp;gt;) in the format string.  Not required if there are no format specifiers.&lt;br /&gt;
&lt;br /&gt;
|example1 = print(sprintf(&amp;quot;%i&amp;quot;, 54)); # prints &amp;quot;54&amp;quot;&lt;br /&gt;
|example2 = print(sprintf(&amp;quot;Pi = %+.10f&amp;quot;, math.pi)); # prints &amp;quot;Pi = +3.1415926536&amp;quot;&lt;br /&gt;
|example3 = &lt;br /&gt;
print(sprintf(&amp;quot;%6d&amp;quot;, 23)); # prints &amp;quot;    23&amp;quot;&lt;br /&gt;
print(sprintf(&amp;quot;%06d&amp;quot;, 23)); # prints &amp;quot;000023&amp;quot;&lt;br /&gt;
|example4 =&lt;br /&gt;
var FGVer = getprop(&amp;quot;/sim/version/flightgear&amp;quot;);&lt;br /&gt;
print(sprintf(&amp;quot;You have FlightGear v%s&amp;quot;, FGVer)); # prints &amp;quot;You have FlightGear v&amp;lt;your version&amp;gt;&amp;quot;&lt;br /&gt;
|example5 = &lt;br /&gt;
print(sprintf(&amp;quot;Hexadecimal 100000 = %X&amp;quot;, 100000)); # prints &amp;quot;Hexadecimal 100000 = 186A0&amp;quot;&lt;br /&gt;
print(sprintf(&amp;quot;Hexadecimal 100000 = %x&amp;quot;, 100000)); # prints &amp;quot;Hexadecimal 100000 = 186a0&amp;quot;&lt;br /&gt;
|example6 = print(sprintf(&amp;quot;Code 65 is %c&amp;quot;, 65)); # prints &amp;quot;Code 65 is A&amp;quot;&lt;br /&gt;
|example7 = &lt;br /&gt;
print(sprintf(&amp;quot;%e&amp;quot;, 54)); # prints &amp;quot;5.400000e+001&amp;quot;&lt;br /&gt;
print(sprintf(&amp;quot;%E&amp;quot;, 54)); # prints &amp;quot;5.400000E+001&amp;quot;&lt;br /&gt;
|example8 = print(sprintf(&amp;quot;%o&amp;quot;, 54)); # prints &amp;quot;66&amp;quot;&lt;br /&gt;
|example9 = print(sprintf(&amp;quot;50%% of 100 is %i&amp;quot;, 100 / 2)); # prints &amp;quot;50% of 100 is 50&amp;quot;&lt;br /&gt;
|example10 =&lt;br /&gt;
print(sprintf(&amp;quot;%.2f&amp;quot;, 1.4));   #prints &amp;quot;1.40&amp;quot;&lt;br /&gt;
print(sprintf(&amp;quot;%.1f&amp;quot;, 1.4));   #prints &amp;quot;1.4&amp;quot;&lt;br /&gt;
print(sprintf(&amp;quot;% 4.1f&amp;quot;, 1.4)); #prints &amp;quot; 1.4&amp;quot;&lt;br /&gt;
print(sprintf(&amp;quot;%04.1f&amp;quot;, 1.4)); #prints &amp;quot;01.4&amp;quot;&lt;br /&gt;
print(sprintf(&amp;quot;% 6.1f&amp;quot;, 1.4)); #prints &amp;quot;   1.4&amp;quot;&lt;br /&gt;
print(sprintf(&amp;quot;%06.1f&amp;quot;, 1.4)); #prints &amp;quot;0001.4&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===streq()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = streq(a, b);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=129|t=Source}}&lt;br /&gt;
|text = Tests the string values of the two arguments for equality. This function is needed because the &amp;lt;code&amp;gt;'''=='''&amp;lt;/code&amp;gt; operator (see [[Nasal_Operators#Equality|Nasal Operators]]) tests for numeric equality first.  If either or both the arguments are not strings, 0 (False) will be returned.  Returns either 0 (False) or 1 (True).  {{Note|This function is rarely required in typical code.}}&lt;br /&gt;
|param1 = a&lt;br /&gt;
|param1text = First argument for testing equality.&lt;br /&gt;
|param2 = b&lt;br /&gt;
|param2text = Second argument for testing equality.&lt;br /&gt;
|example1 = print(streq(&amp;quot;0&amp;quot;, &amp;quot;0&amp;quot;)); # prints &amp;quot;1&amp;quot; (True)&lt;br /&gt;
|example2 = &lt;br /&gt;
print(0 == 0.0); # prints &amp;quot;1&amp;quot; (True)&lt;br /&gt;
print(streq(&amp;quot;0&amp;quot;, &amp;quot;0.0&amp;quot;)); # prints &amp;quot;0&amp;quot; (False)&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===substr()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = substr(string, start [, length]);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=129|t=Source}}&lt;br /&gt;
|text = Similar the {{func link|subvec()}}, but operates on strings. Computes and returns a substring. The first argument specifies a string, the second is the index of the start of a substring, the optional third argument specifies a length (the default is to return the rest of the string from the start).&lt;br /&gt;
|param1 = string&lt;br /&gt;
|param1text = String to return a substring from.&lt;br /&gt;
|param2 = start&lt;br /&gt;
|param2text = Integer specifying the start of a substring. Negative values specify a position from the end of the string.&lt;br /&gt;
|param3 = length&lt;br /&gt;
|param3text = Optional argument specifying the length of the substring. Defaults to the end of the string.&lt;br /&gt;
|example1 = print(substr(&amp;quot;abcde&amp;quot;, 1, 3)); # prints &amp;quot;bcd&amp;quot;&lt;br /&gt;
|example2 = print(substr(&amp;quot;abcde&amp;quot;, 1)); # prints &amp;quot;bcde&amp;quot;&lt;br /&gt;
|example3 = print(substr(&amp;quot;abcde&amp;quot;, 2, 1)); # prints &amp;quot;c&amp;quot;&lt;br /&gt;
|example4 = print(substr(&amp;quot;abcde&amp;quot;, -2)); # prints &amp;quot;de&amp;quot;&lt;br /&gt;
|example5 = print(substr(&amp;quot;abcde&amp;quot;, -3, 2)); # prints &amp;quot;cd&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===subvec()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = subvec(vector, start[, length]);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=63|t=Source}}&lt;br /&gt;
|text = Returns a sub-range of a vector. The first argument specifies a vector, the second a starting index, and the optional third argument indicates a length (the default is to the end of the vector). &lt;br /&gt;
|param1 = vector&lt;br /&gt;
|param1text = The vector to take the sub-vector from.&lt;br /&gt;
|param2 = start&lt;br /&gt;
|param2text = The starting point of the sub-vector within the given vector.&lt;br /&gt;
|param3 = length&lt;br /&gt;
|param3text = Optional argument specifying the length of the sub-vector, from the starting point.&lt;br /&gt;
'''Notes:'''&lt;br /&gt;
* Omitting the ''vector'' and ''start'' arguments is not an error (possibly it should be) but the return value is ''nil''.&lt;br /&gt;
* A negative ''start'' argument ''is'' an error. This seems wrong. Perhaps the language designer could comment.&lt;br /&gt;
* A value of ''start'' greater than ''size(vector)'' causes an error. A value equal to ''size(vector)'' returns an empty vector.&lt;br /&gt;
* If the value of ''length'' is greater than ''size(vector) - start'' then it is ignored. That is, all elements from ''start'' to the end of ''vector'' are returned. If ''length'' is zero then an empty vector is returned. A negative value of ''length'' causes an error.&lt;br /&gt;
|example1 = &lt;br /&gt;
var vector = [1, 2, 3];&lt;br /&gt;
debug.dump(subvec(vector, 0)); # prints &amp;quot;[1, 2, 3]&amp;quot;&lt;br /&gt;
|example2 = &lt;br /&gt;
var vector = [1, 2, 3];&lt;br /&gt;
debug.dump(subvec(vector, 1)); # prints &amp;quot;[2, 3]&amp;quot;&lt;br /&gt;
|example3 = &lt;br /&gt;
var vector = [1, 2, 3];&lt;br /&gt;
debug.dump(subvec(vector, 1, 1)); # prints &amp;quot;[2]&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== typeof()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = typeof(object);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=193|t=Source}}&lt;br /&gt;
|text = Returns a string indicating the whether the object is &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt;, a scalar (number or string), a vector, a hash, a function, or a ghost.&lt;br /&gt;
|param1 = object&lt;br /&gt;
|param1text = Object to return the type of.&lt;br /&gt;
|example1 = &lt;br /&gt;
var object = nil;&lt;br /&gt;
print(typeof(object)); # prints &amp;quot;nil&amp;quot;&lt;br /&gt;
|example2 = &lt;br /&gt;
var object = &amp;quot;Hello world!&amp;quot;;&lt;br /&gt;
print(typeof(object)); # prints &amp;quot;scalar&amp;quot;&lt;br /&gt;
|example3 = &lt;br /&gt;
var object = math.pi;&lt;br /&gt;
print(typeof(object)); # prints &amp;quot;scalar&amp;quot;&lt;br /&gt;
|example4 = &lt;br /&gt;
var object = [1, 2, 3];&lt;br /&gt;
print(typeof(object)); # prints &amp;quot;vector&amp;quot;&lt;br /&gt;
|example5 = &lt;br /&gt;
var object = {};&lt;br /&gt;
print(typeof(object)); # prints &amp;quot;hash&amp;quot;&lt;br /&gt;
|example6 = &lt;br /&gt;
var object = func {};&lt;br /&gt;
print(typeof(object)); # prints &amp;quot;func&amp;quot;&lt;br /&gt;
|example7 =&lt;br /&gt;
var object = airportinfo();&lt;br /&gt;
print(typeof(object)); # prints &amp;quot;ghost&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- == Extension modules ==&lt;br /&gt;
=== thread ===&lt;br /&gt;
{{WIP}}&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = thread.newthread(func);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/threadlib.c|l=101|t=Source}}&lt;br /&gt;
|text = &lt;br /&gt;
|example1text = start a new worker thread&lt;br /&gt;
|example1 = thread.newthread( func() {} );&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = thread.newlock();&lt;br /&gt;
|source = {{simgear file|simgear/nasal/threadlib.c|l=101|t=Source}}&lt;br /&gt;
|text = &lt;br /&gt;
|example1text = create a new lock&lt;br /&gt;
|example1 = var lock = thread.newlock()&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = thread.lock();&lt;br /&gt;
|source = {{simgear file|simgear/nasal/threadlib.c|l=101|t=Source}}&lt;br /&gt;
|text = &lt;br /&gt;
|example1text = lock a lock&lt;br /&gt;
|example1 = var lock = thread.newlock()&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = thread.unlock();&lt;br /&gt;
|source = {{simgear file|simgear/nasal/threadlib.c|l=101|t=Source}}&lt;br /&gt;
|text = &lt;br /&gt;
|example1text = unlock a lock&lt;br /&gt;
|example1 = var lock = thread.unlock()&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = thread.newsem();&lt;br /&gt;
|source = {{simgear file|simgear/nasal/threadlib.c|l=101|t=Source}}&lt;br /&gt;
|text = &lt;br /&gt;
|example1text = create a new {{Wikipedia|semaphore}}&lt;br /&gt;
|example1 = var semaphore = thread.newsem()&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = thread.semdown();&lt;br /&gt;
|source = {{simgear file|simgear/nasal/threadlib.c|l=101|t=Source}}&lt;br /&gt;
|text = &lt;br /&gt;
|example1text = semaphore down&lt;br /&gt;
|example1 = thread.semdown(semaphore)&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = thread.semup();&lt;br /&gt;
|source = {{simgear file|simgear/nasal/threadlib.c|l=101|t=Source}}&lt;br /&gt;
|text = &lt;br /&gt;
|example1text = semaphore up&lt;br /&gt;
|example1 = thread.semup(semaphore)&lt;br /&gt;
}} --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Extension functions==&lt;br /&gt;
The '''extension functions''' are global functions that have been added to Nasal since its integration into FlightGear. Unlike the core library functions, they are generally specifically designed to interact directly with FlightGear. Extension functions come from three source files:&lt;br /&gt;
*{{flightgear file|src/Scripting/NasalPositioned.cxx}}&lt;br /&gt;
*{{flightgear file|src/Scripting/NasalSys.cxx}}&lt;br /&gt;
*{{fgdata file|Nasal/globals.nas}}&lt;br /&gt;
&lt;br /&gt;
===abort()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = abort();&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=565|t=Source}}&lt;br /&gt;
|text = This function is a wrapper for the C++ {{func link|abort()|link=http://www.cplusplus.com/reference/cstdlib/abort/}} function. It simply aborts FlightGear with an error, which varies depending on the operating system. This function should not really be used; instead, please use the &amp;quot;exit&amp;quot; [[Fgcommands|fgcommand]], which will exit FlightGear more gracefully (see example below).&lt;br /&gt;
|example1text = This example will immediately stop FlightGear with an error, such as &amp;quot;FlightGear has stopped working.&amp;quot;&lt;br /&gt;
|example1 = abort();&lt;br /&gt;
|example2text = For exiting FlightGear in a better way, please use the following code:&lt;br /&gt;
|example2 = fgcommand(&amp;quot;exit&amp;quot;);&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== abs() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = abs(number);&lt;br /&gt;
|source = {{fgdata file|Nasal/globals.nas|t=Source}}&lt;br /&gt;
|text = This simple function returns the {{wikipedia|absolute value|noicon=1}} of the provided number.&lt;br /&gt;
|param1 = number&lt;br /&gt;
|param1text = This argument is required and should be a number.&lt;br /&gt;
|example1 = print(abs(1)); # prints &amp;quot;1&amp;quot;&lt;br /&gt;
|example2 = print(abs(-1)); # prints &amp;quot;1&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===aircraftToCart() ===&lt;br /&gt;
This new function in FG 2017.2.1 takes coordinates in aircraft structural coordinate system, and translate them into geocentric coordinates.&lt;br /&gt;
Example for (5,6,7):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
var pos = aircraftToCart({x: -5, y: 6, z: -7});&lt;br /&gt;
var coord = geo.Coord.new();&lt;br /&gt;
coord.set_xyz(pos.x, pos.y, pos.z);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Notice: x and z is inverted sign on purpose.&lt;br /&gt;
if you want lat. lon, alt from that, just call: (degrees and meters)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
coord.lat()&lt;br /&gt;
coord.lon()&lt;br /&gt;
coord.alt()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===addcommand() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = addcommand(name, code);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=659|t=Source}}&lt;br /&gt;
|version = 2.12&lt;br /&gt;
|commit = {{flightgear commit|7b663c|t=commit}}&lt;br /&gt;
|text = {{see also|Howto:Add new fgcommands to FlightGear}}&lt;br /&gt;
&lt;br /&gt;
This function enables the addition of a new custom [[fgcommands|fgcommand]] to FlightGear from within Nasal. An fgcommand created using this method can be used in exactly the same way as the built-in fgcommands. Also, an fgcommand created via this method will always return True or 1, like all other fgcommands.&lt;br /&gt;
|param1 = name&lt;br /&gt;
|param1text = This will become the name of the new fgcommand. Must be a string.&lt;br /&gt;
|param2 = code&lt;br /&gt;
|param2text = The code that will be executed when the fgcommand is run. Must be a function.&lt;br /&gt;
|example1text = This example adds a new fgcommand and then runs it. Although it executes a simple {{func link|print()}} statement, any valid Nasal code can be used.&lt;br /&gt;
|example1 = addcommand(&amp;quot;myFGCmd&amp;quot;, func(node) {&lt;br /&gt;
    print(&amp;quot;fgcommand 'myFGCmd' has been run.&amp;quot;);&lt;br /&gt;
    props.dump( node );&lt;br /&gt;
});&lt;br /&gt;
fgcommand(&amp;quot;myFGCmd&amp;quot;, props.Node.new({foo:1, bar:2}) );&lt;br /&gt;
|example2text = This example demonstrates how parameters are defined in a new fgcommand.&lt;br /&gt;
|example2 = addcommand(&amp;quot;myFGCmd&amp;quot;, func(node){&lt;br /&gt;
    print(node.getNode(&amp;quot;number&amp;quot;).getValue()); # prints the value of &amp;quot;number,&amp;quot; which is 12&lt;br /&gt;
});&lt;br /&gt;
fgcommand(&amp;quot;myFGCmd&amp;quot;, props.Node.new({&amp;quot;number&amp;quot;: 12}));&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===airportinfo()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = airportinfo();&lt;br /&gt;
airportinfo(type);&lt;br /&gt;
airportinfo(id);&lt;br /&gt;
airportinfo(lat, lon[, type]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1024|t=Source}}&lt;br /&gt;
|text = Function for retrieval of airport, heliport, or seaplane base information. It returns a Nasal ghost; however, its structure is like that of a Nasal hash. The following information is returned:&lt;br /&gt;
* '''parents''': A vector containing a hash of various functions to access information about the runway. See {{flightgear file|src/Scripting/NasalPositioned.cxx|l=2659}} for full list.&lt;br /&gt;
* '''lon''': Longitude of the location.&lt;br /&gt;
* '''lat''': Latitude of the location.&lt;br /&gt;
* '''has_metar''': True or false depending whether the airport has a [[METAR]] code defined for it.&lt;br /&gt;
* '''elevation''': Elevation of the location in metres.&lt;br /&gt;
* '''id''': ICAO code of the airport (or ID of the seaplane base/heliport).&lt;br /&gt;
* '''name''': Name of the airport/heliport/seaplane base.&lt;br /&gt;
* '''runways'''&lt;br /&gt;
** '''&amp;lt;runway name&amp;gt;'''&lt;br /&gt;
*** '''id''': Name of runway.&lt;br /&gt;
*** '''lat''': Latitude of the runway.&lt;br /&gt;
*** '''lon''': Longitude of the runway.&lt;br /&gt;
*** '''heading''': Heading of the runway.&lt;br /&gt;
*** '''length''': Length of the runway in metres.&lt;br /&gt;
*** '''width''': Width of the runway in metres.&lt;br /&gt;
*** '''surface''': Runway surface type.&lt;br /&gt;
*** '''threshold''': Length of the runway's {{wikipedia|displaced threshold}} in metres. Will return 0 if there is none.&lt;br /&gt;
*** '''stopway''': Length of the runway's stopway (the area before the threshold) in metres. Will return 0 if there is none.&lt;br /&gt;
*** '''reciprocal''': &amp;lt;code&amp;gt;runway&amp;lt;/code&amp;gt; ghost of the reciprocal runway.&lt;br /&gt;
*** '''ils_frequency_mhz''': ILS frequency in megahertz.&lt;br /&gt;
*** '''ils''': &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt; ghost of the ILS transmitter.&lt;br /&gt;
* '''helipads'''&lt;br /&gt;
** '''&amp;lt;helipad name&amp;gt;'''&lt;br /&gt;
*** '''id''': Name of helipad.&lt;br /&gt;
*** '''lat''': Latitude of the helipad.&lt;br /&gt;
*** '''lon''': Longitude of the helipad.&lt;br /&gt;
*** '''heading''': Heading of the helipad.&lt;br /&gt;
*** '''length''': Length of the helipad in metres.&lt;br /&gt;
*** '''width''': Width of the helipad in metres.&lt;br /&gt;
*** '''surface''': Helipad surface type.&lt;br /&gt;
* '''taxiways'''&lt;br /&gt;
** '''&amp;lt;taxiway name&amp;gt;'''&lt;br /&gt;
*** '''id''': Name of taxiway.&lt;br /&gt;
*** '''lat''': Latitude of the taxiway.&lt;br /&gt;
*** '''lon''': Longitude of the taxiway.&lt;br /&gt;
*** '''heading''': Heading of the taxiway.&lt;br /&gt;
*** '''length''': Length of the taxiway in metres.&lt;br /&gt;
*** '''width''': Width of the taxiway in metres.&lt;br /&gt;
*** '''surface''': Taxiway surface type.&lt;br /&gt;
&lt;br /&gt;
Information is extracted in the same way as accessing members of a Nasal hash. For example:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
# prints to lengths of the runways of the nearest airport in feet and metres&lt;br /&gt;
var info = airportinfo();&lt;br /&gt;
print(&amp;quot;-- Lengths of the runways at &amp;quot;, info.name, &amp;quot; (&amp;quot;, info.id, &amp;quot;) --&amp;quot;);&lt;br /&gt;
foreach(var rwy; keys(info.runways)){&lt;br /&gt;
    print(rwy, &amp;quot;: &amp;quot;, math.round(info.runways[rwy].length * M2FT), &amp;quot; ft (&amp;quot;, info.runways[rwy].length, &amp;quot; m)&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that searches for locations that are a long way away (e.g., the nearest seaplane base to the middle of the Sahara) may cause FlightGear to pause for an amount of time.&lt;br /&gt;
|param1 = id&lt;br /&gt;
|param1text = The {{wikipedia|International Civil Aviation Organization airport code|ICAO code|noicon=1}} of an airport to retrieve information about.&lt;br /&gt;
|param2 = type&lt;br /&gt;
|param2text = When this argument is used, the function will return the closest airport of a certain type. Can be one of &amp;quot;heliport,&amp;quot; &amp;quot;seaport,&amp;quot; or &amp;quot;airport&amp;quot; (default).&lt;br /&gt;
: {{inote|Running this function without any parameters is equivalent to this:&lt;br /&gt;
: &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
airportinfo(&amp;quot;airport&amp;quot;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
|param3 = lat ''and'' lon&lt;br /&gt;
|param3text = When these parameters are used, the function will return information on the nearest airport, heliport or seaplane base (depending on the '''type''' parameter) to those coordinates.&lt;br /&gt;
|example1 = var info = airportinfo();&lt;br /&gt;
print(&amp;quot;Nearest airport: &amp;quot;, info.name, &amp;quot; (&amp;quot;, info.id, &amp;quot;)&amp;quot;); # prints the name and ICAO code of the nearest airport&lt;br /&gt;
|example2 = var info = airportinfo(&amp;quot;heliport&amp;quot;);&lt;br /&gt;
print(&amp;quot;Elevation of the nearest heliport: &amp;quot;, math.round(info.elevation * M2FT), &amp;quot; ft&amp;quot;); # prints the elevation and name of the nearest heliport&lt;br /&gt;
|example3 = var info = airportinfo(&amp;quot;KSQL&amp;quot;);&lt;br /&gt;
print(&amp;quot;-- Runways of &amp;quot;, info.name, &amp;quot; (&amp;quot;, info.id, &amp;quot;): --&amp;quot;);&lt;br /&gt;
foreach(var rwy; keys(info.runways)) {&lt;br /&gt;
    print(rwy); # prints the runways of KSQL&lt;br /&gt;
}&lt;br /&gt;
|example4 = var info = airportinfo(37.81909385, -122.4722484);&lt;br /&gt;
print(&amp;quot;Coordinates of the nearest airport: &amp;quot;, info.lat, &amp;quot;, &amp;quot;, info.lon); # print the name and ICAO of the nearest airport to the Golden Gate Bridge&lt;br /&gt;
|example5 = var info = airportinfo(37.81909385, -122.4722484, &amp;quot;seaport&amp;quot;);&lt;br /&gt;
print(&amp;quot;Nearest seaplane base: &amp;quot;, info.name, &amp;quot; (&amp;quot;, info.id, &amp;quot;)&amp;quot;); # print the name and ID of the nearest seaplane base to the Golden Gate Bridge&lt;br /&gt;
|example6text = This example prints the all information from an &amp;lt;code&amp;gt;airportinfo()&amp;lt;/code&amp;gt; call.&lt;br /&gt;
|example6 = var info = airportinfo(&amp;quot;KSFO&amp;quot;);&lt;br /&gt;
print(info.name);&lt;br /&gt;
print(info.id);&lt;br /&gt;
print(info.lat);&lt;br /&gt;
print(info.lon);&lt;br /&gt;
print(info.has_metar);&lt;br /&gt;
print(info.elevation);&lt;br /&gt;
foreach(var rwy; keys(info.runways)){&lt;br /&gt;
    print(&amp;quot;-- &amp;quot;, rwy, &amp;quot; --&amp;quot;);&lt;br /&gt;
    print(info.runways[rwy].lat);&lt;br /&gt;
    print(info.runways[rwy].lon);&lt;br /&gt;
    print(info.runways[rwy].length);&lt;br /&gt;
    print(info.runways[rwy].width);&lt;br /&gt;
    print(info.runways[rwy].heading);&lt;br /&gt;
    print(info.runways[rwy].stopway);&lt;br /&gt;
    print(info.runways[rwy].threshold);&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===airwaysRoute() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = airwaysRoute(start, end[, type]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1933|t=Source}}&lt;br /&gt;
|text = {{see also|Nasal Flightplan}}&lt;br /&gt;
This function returns a vector containing waypoints between two given waypoints. The returned waypoints are ghosts, but can be accessed in the same way as a Nasal hash. See [[Nasal Flightplan]] for more information.&lt;br /&gt;
|param1 = start&lt;br /&gt;
|param1text = Start waypoint, in the form of a waypoint ghost, such as that provided by {{func link|flightplan()}}.&lt;br /&gt;
|param2 = end&lt;br /&gt;
|param2text = Same as above.&lt;br /&gt;
|param3 = type&lt;br /&gt;
|param3text = Instructs the function to compute a high level route (when set to &amp;quot;highlevel&amp;quot;), or a low level route (when set to &amp;quot;lowlevel&amp;quot;). Defaults to &amp;quot;highlevel.&amp;quot;&lt;br /&gt;
|example1text = In the [[route manager]] dialog, add two waypoints to the flightplan, ideally ones that are far apart (tip: use the [[Map]] for this). Then run this code in your Nasal Console.&lt;br /&gt;
|example1 = var fp = flightplan();&lt;br /&gt;
var start = fp.getWP(0);&lt;br /&gt;
var end = fp.getWP(fp.getPlanSize() - 1);&lt;br /&gt;
var rt = airwaysRoute(start, end);&lt;br /&gt;
foreach(var wp; rt){&lt;br /&gt;
    print(wp.wp_name); # print the waypoints in the computed route&lt;br /&gt;
}&lt;br /&gt;
|example2text = Exactly the same as above, but computes a low level path.&lt;br /&gt;
|example2 = var fp = flightplan();&lt;br /&gt;
var start = fp.getWP(0);&lt;br /&gt;
var end = fp.getWP(fp.getPlanSize() - 1);&lt;br /&gt;
var rt = airwaysRoute(start, end, &amp;quot;lowlevel&amp;quot;);&lt;br /&gt;
foreach(var wp; rt){&lt;br /&gt;
    print(wp.wp_name); # print the waypoints in the computed route&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===airway()===&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = airway(ident [, pos]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=2644|t=Source}}&lt;br /&gt;
|text = {{see also|Nasal Flightplan}}&lt;br /&gt;
This function returns a ghost containing an airway of a specified id.&lt;br /&gt;
|param1 = ident&lt;br /&gt;
|param1text = a Positioned ghost (leg, navaid, airport) and so on, passed to the search function.&lt;br /&gt;
|param2 = pos&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===assert()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = assert(condition[, message]);&lt;br /&gt;
|source = {{fgdata file|Nasal/globals.nas|t=Source}}&lt;br /&gt;
|version = 3.2&lt;br /&gt;
|commit = {{fgdata commit|8b16a7|t=commit}}&lt;br /&gt;
|text = Returns either true if the condition evaluates as true, or aborts with a {{func link|die()}} call, which can be customised.&lt;br /&gt;
|param1 = condition&lt;br /&gt;
|param1text = Condition to evaluate.&lt;br /&gt;
|param2 = message&lt;br /&gt;
|param2text = Optional message that will be used in any {{func link|die()}} call. Defaults to &amp;quot;assertion failed!&amp;quot;&lt;br /&gt;
|example1 = var a = 1;&lt;br /&gt;
var b = 2;&lt;br /&gt;
print(assert(a &amp;lt; b)); # prints &amp;quot;1&amp;quot; (true)&lt;br /&gt;
|example2 = var a = 1;&lt;br /&gt;
var b = 2;&lt;br /&gt;
assert(a &amp;gt; b, 'a is not greater than b'); # aborts with a custom error message&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===carttogeod()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = carttogeod(x, y, z);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=945|t=Source}}&lt;br /&gt;
|text = Converts {{wikipedia|ECEF|Earth-centered, Earth-fixed}} coordinates (x, y and z) to {{wikipedia|geodetic coordinates}} (latitude, longitude, and altitude). A vector is returned containing latitude and longitude, both in degrees, and altitude, which is returned in metres above the equatorial radius of Earth as defined by the {{wikipedia|WGS 84}} (6,378,137 metres).&amp;lt;ref&amp;gt;{{simgear file|simgear/math/sg_geodesy.hxx|l=43}}&amp;lt;/ref&amp;gt;&lt;br /&gt;
|param1 = x&lt;br /&gt;
|param1text = Mandatory x-axis value, in metres.&lt;br /&gt;
|param2 = y&lt;br /&gt;
|param2text = Mandatory y-axis value, in metres.&lt;br /&gt;
|param3 = z&lt;br /&gt;
|param3text = Mandatory z-axis value, in metres.&lt;br /&gt;
|example1 = var (lat, lon, alt) = carttogeod(6378137, 0, 0); # point is the intersection of the prime meridian and equator.&lt;br /&gt;
print(&amp;quot;Latitude: &amp;quot;, lat); # prints lat, lon and alt, which are all zero, see above&lt;br /&gt;
print(&amp;quot;Longitude: &amp;quot;, lon);&lt;br /&gt;
print(&amp;quot;Altitude: &amp;quot;, alt);&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===cmdarg()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|private = _cmdarg()&lt;br /&gt;
|syntax = cmdarg();&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=513|t=Part 1}} {{!}} {{fgdata file|Nasal/globals.nas|t=Part 2}}&lt;br /&gt;
|text = &amp;lt;code&amp;gt;cmdarg()&amp;lt;/code&amp;gt; returns the property root of certain types of XML files. These could be nodes in the [[Property Tree]], or temporary and/or non-public nodes outside the Property tree. &lt;br /&gt;
It is used by Nasal scripts embedded in XML files. It returns a &amp;lt;code&amp;gt;props.Node&amp;lt;/code&amp;gt; object (see {{fgdata file|Nasal/props.nas}}), and you can use all of its methods on the returned value. &amp;lt;code&amp;gt;cmdarg()&amp;lt;/code&amp;gt; should only be used in four types/places of XML files:&lt;br /&gt;
* Bindings: This is needed so that the value of a joystick's axis can be accessed internally.&lt;br /&gt;
* Dialogs: This will return the root of the dialog in the Property Tree. This is useful for dialogs that are created/modified procedurally (e.g. for populating/changing widgets while loading the dialog). &lt;br /&gt;
* Embedded Canvases: The Nasal code behind [[Canvas]] windows [[Howto:Adding a canvas to a GUI dialog|embedded in PUI dialogs]] can use it to accessing the root directory of their Canvas.&lt;br /&gt;
* Animation XML files: If the animation XML file is used in an AI/MP model, &amp;lt;code&amp;gt;cmdarg()&amp;lt;/code&amp;gt; will return the root of the AI model in the &amp;lt;code&amp;gt;/ai/models/&amp;lt;/code&amp;gt; directory. Examples: &amp;lt;code&amp;gt;/ai/models/aircraft[3]/&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/ai/models/multiplayer[1]/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You should not use &amp;lt;code&amp;gt;cmdarg()&amp;lt;/code&amp;gt; in places other than those stated above. Although it won't cause an error, it will return the value of the last legitimate &amp;lt;code&amp;gt;cmdarg()&amp;lt;/code&amp;gt; call. &lt;br /&gt;
&lt;br /&gt;
Also, you should not delay &amp;lt;code&amp;gt;cmdarg()&amp;lt;/code&amp;gt; using {{func link|maketimer()}}, {{func link|settimer()}} or {{func link|setlistener()}}, because it will return an unrelated property.&lt;br /&gt;
|example1 = fgcommand(&amp;quot;dialog-show&amp;quot;, {&amp;quot;dialog-name&amp;quot;: &amp;quot;cmdarg-demo&amp;quot;});&lt;br /&gt;
|example1text = &amp;lt;br&amp;gt;This example demonstrates the usage of &amp;lt;code&amp;gt;cmdarg()&amp;lt;/code&amp;gt; in a binding.  Save the below XML snippet as &amp;lt;tt&amp;gt;[[$FG_ROOT]]/gui/dialogs/cmdarg-demo.xml&amp;lt;/tt&amp;gt;. Then run the Nasal snippet below in your [[Nasal Console]]. Upon clicking {{button|Close}}, a message will be printed sowing the root of the binding in the Property Tree.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;PropertyList&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;name&amp;gt;cmdarg-demo&amp;lt;/name&amp;gt;&lt;br /&gt;
&amp;lt;layout&amp;gt;vbox&amp;lt;/layout&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;text&amp;gt;&lt;br /&gt;
  &amp;lt;label&amp;gt;Click &amp;quot;Close&amp;quot; to activate the demonstration (a message in the console).&amp;lt;/label&amp;gt;&lt;br /&gt;
&amp;lt;/text&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;button&amp;gt;&lt;br /&gt;
  &amp;lt;legend&amp;gt;Close&amp;lt;/legend&amp;gt;&lt;br /&gt;
  &amp;lt;binding&amp;gt;&lt;br /&gt;
    &amp;lt;command&amp;gt;nasal&amp;lt;/command&amp;gt;&lt;br /&gt;
    &amp;lt;script&amp;gt;print(&amp;quot;Button binding root: '&amp;quot; ~ cmdarg().getPath() ~ &amp;quot;'&amp;quot;);&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;/binding&amp;gt;&lt;br /&gt;
  &amp;lt;binding&amp;gt;&lt;br /&gt;
    &amp;lt;command&amp;gt;dialog-close&amp;lt;/command&amp;gt;&lt;br /&gt;
  &amp;lt;/binding&amp;gt;&lt;br /&gt;
&amp;lt;/button&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/PropertyList&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|example2text = This example demonstrates the usage of &amp;lt;code&amp;gt;cmdarg()&amp;lt;/code&amp;gt; in Nasal code within dialogs.  Open &amp;lt;tt&amp;gt;[[$FG_ROOT]]/gui/dialogs/cmdarg-demo.xml&amp;lt;/tt&amp;gt; from the previous example, copy &amp;amp; paste the code below, and save it. Then run the same Nasal snippet as the previous example in your Nasal Console. If you click {{button|Click me!}}, the button's label will change to &amp;quot;I've been changed!&amp;quot;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;PropertyList&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;name&amp;gt;cmdarg-demo&amp;lt;/name&amp;gt;&lt;br /&gt;
&amp;lt;layout&amp;gt;vbox&amp;lt;/layout&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;text&amp;gt;&lt;br /&gt;
  &amp;lt;label&amp;gt;Click &amp;quot;Click me!&amp;quot; to activate the demonstration (the button's label will change).&amp;lt;/label&amp;gt;&lt;br /&gt;
&amp;lt;/text&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;button&amp;gt;&lt;br /&gt;
  &amp;lt;legend&amp;gt;Click me!&amp;lt;/legend&amp;gt;&lt;br /&gt;
  &amp;lt;binding&amp;gt;&lt;br /&gt;
    &amp;lt;command&amp;gt;nasal&amp;lt;/command&amp;gt;&lt;br /&gt;
    &amp;lt;script&amp;gt;change_label();&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;/binding&amp;gt;&lt;br /&gt;
&amp;lt;/button&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;button&amp;gt;&lt;br /&gt;
  &amp;lt;legend&amp;gt;Close&amp;lt;/legend&amp;gt;&lt;br /&gt;
  &amp;lt;binding&amp;gt;&lt;br /&gt;
    &amp;lt;command&amp;gt;dialog-close&amp;lt;/command&amp;gt;&lt;br /&gt;
  &amp;lt;/binding&amp;gt;&lt;br /&gt;
&amp;lt;/button&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nasal&amp;gt;&lt;br /&gt;
  &amp;lt;open&amp;gt;&amp;lt;![CDATA[&lt;br /&gt;
    var dlg_root = cmdarg();&lt;br /&gt;
    var dlg_name = {&amp;quot;dialog-name&amp;quot;: &amp;quot;cmdarg-demo&amp;quot;};&lt;br /&gt;
    var change_label = func {&lt;br /&gt;
        dlg_root.getNode(&amp;quot;button[0]/legend&amp;quot;).setValue(&amp;quot;I've been changed!&amp;quot;);&lt;br /&gt;
        fgcommand(&amp;quot;dialog-close&amp;quot;, dlg_name);&lt;br /&gt;
        fgcommand(&amp;quot;dialog-show&amp;quot;, dlg_name);&lt;br /&gt;
    }&lt;br /&gt;
  ]]&amp;gt;&amp;lt;/open&amp;gt;&lt;br /&gt;
&amp;lt;/nasal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/PropertyList&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|example3text = For an example of &amp;lt;code&amp;gt;cmdarg()&amp;lt;/code&amp;gt; used with Canvas, please see [[Howto:Adding a canvas to a GUI dialog#FGPlot|Howto:Adding a canvas to a GUI dialog]].&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===courseAndDistance()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = courseAndDistance(to);&lt;br /&gt;
courseAndDistance(from, to);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1668|t=Source}}&lt;br /&gt;
|text = Returns a vector containing the course from one point to another and the distance between them in nautical miles. The course is the initial bearing (see [http://www.movable-type.co.uk/scripts/latlong.html#bearing here]), and is in the range 0–360. Both arguments can be one of:&lt;br /&gt;
:* An &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;runway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;taxiway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fix&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;waypoint&amp;lt;/code&amp;gt; ghost type&lt;br /&gt;
:* A hash with ''lat'' and ''lon'' members&lt;br /&gt;
:* A geo.Coord object with geodetic coordinates (cartesian coordinates will not be accepted)&lt;br /&gt;
|param1 = from&lt;br /&gt;
|param1text = Optional parameter defining the from where the function should calculate its results. If the function is given one argument ('''to'''), the aircraft's current position will be used. As well as the argument types as defined above, this argument can be two numbers separated with a comma, as if the function is taking three arguments. See example 5 below.&lt;br /&gt;
|param2 = to&lt;br /&gt;
|param2text = Like the first parameter, but defines the second point.&lt;br /&gt;
|example1text = This example demonstrates the usage of the function with the &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt; ghost type.&lt;br /&gt;
|example1 = var from = airportinfo(&amp;quot;KSFO&amp;quot;);&lt;br /&gt;
var to = airportinfo(&amp;quot;KSQL&amp;quot;);&lt;br /&gt;
var (course, dist) = courseAndDistance(from, to);&lt;br /&gt;
print(course); # prints course from KSFO to KSQL&lt;br /&gt;
print(dist); # prints distance in nm from KSFO to KSQL&lt;br /&gt;
|example2text = This example demonstrates the usage of the function with hashes containing ''lat'' and ''lon''.&lt;br /&gt;
|example2 = var from = {lat: 0, lon: 0};&lt;br /&gt;
var to = {lat: 1, lon: 1};&lt;br /&gt;
var (course, dist) = courseAndDistance(from, to);&lt;br /&gt;
print(course);&lt;br /&gt;
print(dist);&lt;br /&gt;
|example3text = This example demonstrates usage of a geo.Coord object.&lt;br /&gt;
|example3 = var from = geo.Coord.new().set_latlon(0, 0);&lt;br /&gt;
var to = geo.Coord.new().set_latlon(1, 1);&lt;br /&gt;
var (course, dist) = courseAndDistance(from, to);&lt;br /&gt;
print(course);&lt;br /&gt;
print(dist);&lt;br /&gt;
|example4text = This example demonstrates usage of differing parameter types.&lt;br /&gt;
|example4 = var from = airportinfo(&amp;quot;KSFO&amp;quot;);&lt;br /&gt;
var to = geo.Coord.new().set_latlon(0, 0);&lt;br /&gt;
var (course, dist) = courseAndDistance(from, to);&lt;br /&gt;
print(course);&lt;br /&gt;
print(dist);&lt;br /&gt;
|example5text = The same as above, but the other way round.&lt;br /&gt;
|example5 = var to = {lat: 1, lon: 1};&lt;br /&gt;
var (course, dist) = courseAndDistance(0, 0, to);&lt;br /&gt;
print(course);&lt;br /&gt;
print(dist);&lt;br /&gt;
|example6text = Usage of just one parameter.&lt;br /&gt;
|example6 = var dest = airportinfo(&amp;quot;KSQL&amp;quot;);&lt;br /&gt;
var (course, dist) = courseAndDistance(dest);&lt;br /&gt;
print(&amp;quot;Turn to heading &amp;quot;, math.round(course), &amp;quot;. You have &amp;quot;, sprintf(&amp;quot;%.2f&amp;quot;, dist), &amp;quot; nm to go&amp;quot;);&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===createFlightplan()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = createFlightplan(path);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=2331|t=Source}}&lt;br /&gt;
|text = Creates an empty flightplan object. It accepts one argument, ''path'' passed an absolute path to a .fgfp / .gpx file, it will populate the flightplan with waypoints from the file.&lt;br /&gt;
|param1 = path&lt;br /&gt;
|param1text = Optional parameter defining the file from which a flightplan will be populated.&lt;br /&gt;
|example1 = &lt;br /&gt;
var path = getprop(&amp;quot;/sim/fg-home&amp;quot;) ~ &amp;quot;/Export/test.fgfp&amp;quot;;&lt;br /&gt;
var flightplan = createFlightplan(path);&lt;br /&gt;
debug.dump(flightplan);&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===createDiscontinuity()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = createDiscontinuity();&lt;br /&gt;
|text = Returns a &amp;lt;code&amp;gt;waypoint&amp;lt;/code&amp;gt; ghost object. A route discontinuity is inserted by an {{abbr|FMS|Flight Management System}} when it is unsure how to connect two waypoints.&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=2045|t=Source}}&lt;br /&gt;
|version = 2016.1&lt;br /&gt;
|commit = {{flightgear commit|caead6|t=commit}}&lt;br /&gt;
}}&lt;br /&gt;
===createViaTo()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = createViaTo(airway, waypoint);&lt;br /&gt;
|text = Returns a &amp;lt;code&amp;gt;waypoint&amp;lt;/code&amp;gt; ghost object. It represents a route &amp;quot;via '''airway''' to '''waypoint'''&amp;quot;.&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=2009|t=Source}}&lt;br /&gt;
|version = 2016.1&lt;br /&gt;
|commit = {{flightgear commit|caead6|t=commit}}&lt;br /&gt;
|param1 = airway&lt;br /&gt;
|param1text = The name of an airway.&lt;br /&gt;
|param2 = waypoint&lt;br /&gt;
|param2text = Must be in the airway and one of:&lt;br /&gt;
* The name of a waypoint.&lt;br /&gt;
* An &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;runway&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;fix&amp;lt;/code&amp;gt; ghost object.&lt;br /&gt;
}}&lt;br /&gt;
===createWP()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = createWP(pos, name[, flag]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1964|t=Source}}&lt;br /&gt;
|text = Creates a new waypoint ghost object.&lt;br /&gt;
|param1 = pos&lt;br /&gt;
|param1text = Dictates the position of the new waypoint. It can be one of the following:&lt;br /&gt;
:* An &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;runway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;taxiway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fix&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;waypoint&amp;lt;/code&amp;gt; ghost type&lt;br /&gt;
:* A hash with ''lat'' and ''lon'' members&lt;br /&gt;
:* A geo.Coord object&lt;br /&gt;
:* Two numbers separated by a comma, as if the function is taking three arguments. See example 4 below.&lt;br /&gt;
|param2 = name&lt;br /&gt;
|param2text = String that will become the name of the new waypoint.&lt;br /&gt;
|param3 = flag&lt;br /&gt;
|param3text = Optional string that will tell FlightGear what type of waypoint it is. Must be one of &amp;quot;sid,&amp;quot; &amp;quot;star,&amp;quot; &amp;quot;approach,&amp;quot; &amp;quot;missed,&amp;quot; or &amp;quot;pseudo.&amp;quot;&lt;br /&gt;
|example1text = Creates a waypoint directly in front and 1 km away and appends it to the flight plan.&lt;br /&gt;
|example1 = var pos = geo.aircraft_position().apply_course_distance(getprop(&amp;quot;/orientation/heading-deg&amp;quot;), 1000);&lt;br /&gt;
var wp = createWP(pos, &amp;quot;NEWWP&amp;quot;);&lt;br /&gt;
var fp = flightplan();&lt;br /&gt;
fp.appendWP(wp);&lt;br /&gt;
|example2 = var pos = geo.aircraft_position().apply_course_distance(getprop(&amp;quot;/orientation/heading-deg&amp;quot;), 1000);&lt;br /&gt;
var wp = createWP({lat: pos.lat(), lon: pos.lon()}, &amp;quot;NEWWP&amp;quot;);&lt;br /&gt;
var fp = flightplan();&lt;br /&gt;
fp.appendWP(wp);&lt;br /&gt;
|example3 = var apt = airportinfo();&lt;br /&gt;
var wp = createWP(apt, &amp;quot;NEWWP&amp;quot;);&lt;br /&gt;
var fp = flightplan();&lt;br /&gt;
fp.appendWP(wp);&lt;br /&gt;
|example4 = var pos = geo.aircraft_position().apply_course_distance(getprop(&amp;quot;/orientation/heading-deg&amp;quot;), 1000);&lt;br /&gt;
var wp = createWP(pos.lat(), pos.lon(), &amp;quot;NEWWP&amp;quot;);&lt;br /&gt;
var fp = flightplan();&lt;br /&gt;
fp.appendWP(wp);&lt;br /&gt;
|example5text = Creates a new waypoint and adds it to the flight plan. Waypoints of the type &amp;quot;pseudo&amp;quot; are then removed from the flight plan, including the new waypoint. The {{func link|print()}} statements show this.&lt;br /&gt;
|example5 = var pos = geo.aircraft_position().apply_course_distance(getprop(&amp;quot;/orientation/heading-deg&amp;quot;), 1000);&lt;br /&gt;
var wp = createWP(pos, &amp;quot;NEWWP&amp;quot;, &amp;quot;pseudo&amp;quot;);&lt;br /&gt;
var fp = flightplan();&lt;br /&gt;
fp.appendWP(wp);&lt;br /&gt;
print(fp.getPlanSize());&lt;br /&gt;
fp.clearWPType(&amp;quot;pseudo&amp;quot;);&lt;br /&gt;
print(fp.getPlanSize());&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===createWPFrom()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = createWPFrom(object[, flag]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1989|t=Source}}&lt;br /&gt;
|text = Creates a new waypoint object from another object.&lt;br /&gt;
|param1 = object&lt;br /&gt;
|param1text = A ghost object. Must be a ghost type that is one of &amp;quot;airport,&amp;quot; &amp;quot;navaid,&amp;quot; &amp;quot;runway,&amp;quot; or &amp;quot;fix.&amp;quot;&lt;br /&gt;
|param2 = flag&lt;br /&gt;
|param2text = Optional string that will tell FlightGear what type of waypoint it is. Must be one of &amp;quot;sid,&amp;quot; &amp;quot;star,&amp;quot; &amp;quot;approach,&amp;quot; &amp;quot;missed,&amp;quot; or &amp;quot;pseudo.&amp;quot;&lt;br /&gt;
|example1text = Creates a new waypoint and appends it to the flight plan.&lt;br /&gt;
|example1 = var apt = airportinfo(&amp;quot;KSFO&amp;quot;);&lt;br /&gt;
var wp = createWPFrom(apt);&lt;br /&gt;
var fp = flightplan();&lt;br /&gt;
fp.appendWP(wp);&lt;br /&gt;
|example2text = Creates a new waypoint and appends it to the flight plan. This way point is then removed; the {{func link|print()}} statements prove this.&lt;br /&gt;
|example2 = var apt = airportinfo(&amp;quot;KSFO&amp;quot;);&lt;br /&gt;
var wp = createWPFrom(apt, &amp;quot;pseudo&amp;quot;);&lt;br /&gt;
print(wp.wp_name);&lt;br /&gt;
var fp = flightplan();&lt;br /&gt;
fp.appendWP(wp);&lt;br /&gt;
print(fp.getPlanSize());&lt;br /&gt;
fp.clearWPType(&amp;quot;pseudo&amp;quot;);&lt;br /&gt;
print(fp.getPlanSize());&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===defined()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = defined(symbol);&lt;br /&gt;
|source = {{fgdata file|Nasal/globals.nas|t=Source}}&lt;br /&gt;
|text = Returns 1 (true) or 0 (false) depending on whether a variable exists.&lt;br /&gt;
|param1 = symbol&lt;br /&gt;
|param1text = A string that will be what the function searches for.&lt;br /&gt;
|example1 = var number = 12;&lt;br /&gt;
var check_exist = func {&lt;br /&gt;
    print(&amp;quot;Variable 'number' &amp;quot;, defined(&amp;quot;number&amp;quot;) == 1 ? &amp;quot;exists&amp;quot; : &amp;quot;does not exist&amp;quot;); # 'number' does exist&lt;br /&gt;
    print(&amp;quot;Variable 'number2' &amp;quot;, defined(&amp;quot;number2&amp;quot;) == 1 ? &amp;quot;exists&amp;quot; : &amp;quot;does not exist&amp;quot;); # 'number2' does not exist&lt;br /&gt;
}&lt;br /&gt;
check_exist();&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===directory()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = directory(path);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=572|t=Source}}&lt;br /&gt;
|text = Returns a vector containing a list of the folders and files in a given file path or &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; if the path doesn't exist. Hidden folders and files are not added to the vector.&lt;br /&gt;
{{inote|The first two elements of the vector will be &amp;lt;code&amp;gt;'.'&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;'..'&amp;lt;/code&amp;gt;. These are for navigating back up the file tree, but have no use in Nasal. They can be safely removed from the vector.}}&lt;br /&gt;
|param1 = path&lt;br /&gt;
|param1text = Absolute file path.&lt;br /&gt;
|example1text = Gets the folders and files in [[$FG_ROOT]], and then removes the extra first two elements (see note above). &lt;br /&gt;
|example1 = var dir = directory(getprop(&amp;quot;/sim/fg-root&amp;quot;)); # get directory&lt;br /&gt;
dir = subvec(dir, 2); # strips off the first two elements&lt;br /&gt;
debug.dump(dir); # dump the vector&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===fgcommand()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = fgcommand(cmd[, args]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=456|t=Part 1}} {{!}} {{fgdata file|Nasal/globals.nas|t=Part 2}}&lt;br /&gt;
|text = Runs an fgcommand. See also {{readme file|commands}} and [[Bindings]] for more information. See {{flightgear file|src/Main/fg_commands.cxx|l=1425}} for the full list of fgcommands. Note that fgcommands generated by {{func link|addcommand()}} can also be run using this function. Also, the full list of fgcommands depends on the version of FlightGear you have. Returns 1 (true) if the fgcommand succeeded or 0 (false) if it failed.&lt;br /&gt;
|param1 = cmd&lt;br /&gt;
|param1text = String that is the name of the command that is to be run.&lt;br /&gt;
|param2 = args&lt;br /&gt;
|param2text = If the fgcommand takes arguments, they are inputted using this argument. Can either be a &amp;lt;code&amp;gt;props.Node&amp;lt;/code&amp;gt; object, or a hash (see examples below).&lt;br /&gt;
|example1 = fgcommand(&amp;quot;null&amp;quot;); # does nothing&lt;br /&gt;
|example2 = var args = props.Node.new({'script': 'print(&amp;quot;Running fgcommand&amp;quot;);'});&lt;br /&gt;
if (fgcommand(&amp;quot;nasal&amp;quot;, args)) { # prints &amp;quot;Running fgcommand&amp;quot; and then one of these print statements&lt;br /&gt;
    print(&amp;quot;Fgcommand succeeded&amp;quot;);&lt;br /&gt;
} else {&lt;br /&gt;
    print(&amp;quot;Fgcommand encountered a problem&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
|example3 = var args = { 'dialog-name': 'about' };&lt;br /&gt;
fgcommand(&amp;quot;dialog-show&amp;quot;, args); # shows the 'about' dialog&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===findAirportsByICAO() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = findAirportsByICAO(search[, type]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1096|t=Source}}&lt;br /&gt;
|text = Returns a vector containing &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt; ghost objects which are (by default) airports whose ICAO code matches the search string. The results are sorted by range from closest to furthest.&lt;br /&gt;
|param1 = search&lt;br /&gt;
|param1text = Search string for the function. Can either be a partial or a full ICAO code.&lt;br /&gt;
:{{icaution|The more matches there are for the given code, the longer the function will take. Passing just one character (e.g., &amp;quot;K&amp;quot;), might make FlightGear hang for a certain amount of time.}}&lt;br /&gt;
|param2 = type&lt;br /&gt;
|param2text = This will narrow the search to airports of a certain type. By default, only airports are searched for. May be one of &amp;quot;airport,&amp;quot; &amp;quot;heliport,&amp;quot; or &amp;quot;seaport.&amp;quot;&lt;br /&gt;
|example1 = var apts = findAirportsByICAO(&amp;quot;KSF&amp;quot;); # finds all airports matching &amp;quot;KSF&amp;quot;&lt;br /&gt;
foreach(var apt; apts){&lt;br /&gt;
    print(apt.name, &amp;quot; (&amp;quot;, apt.id, &amp;quot;)&amp;quot;); # prints them&lt;br /&gt;
}&lt;br /&gt;
|example2 = var apts = findAirportsByICAO(&amp;quot;SP0&amp;quot;, &amp;quot;seaport&amp;quot;); # finds all seaplane bases matching &amp;quot;SP0&amp;quot;&lt;br /&gt;
foreach(var apt; apts){&lt;br /&gt;
    print(apt.name, &amp;quot; (&amp;quot;, apt.id, &amp;quot;)&amp;quot;); # prints them&lt;br /&gt;
}&lt;br /&gt;
|example3 = var apt = findAirportsByICAO(&amp;quot;XBET&amp;quot;); # one way to check if an airport does exist&amp;quot;&lt;br /&gt;
if (size(apt) == 0) {&lt;br /&gt;
    print(&amp;quot;Airport does not exist&amp;quot;); # this one will be printed&lt;br /&gt;
} else {&lt;br /&gt;
    print(&amp;quot;Airport does exist&amp;quot;);&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===findAirportsWithinRange()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = findAirportsWithinRange([pos, ]range[, type]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1066|t=Source}}&lt;br /&gt;
|text = Returns a vector of &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt; ghost object which are (by default) airports that are within a given range of a given position, or the aircraft's current position. The results are sorted by range from closest to furthest.&lt;br /&gt;
|param1 = pos&lt;br /&gt;
|param1text = Optional position to search around. If not given, the aircraft's current position will be used. Can be one of:&lt;br /&gt;
:* An &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;runway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;taxiway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fix&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;waypoint&amp;lt;/code&amp;gt; ghost type&lt;br /&gt;
:* A hash with ''lat'' and ''lon'' members&lt;br /&gt;
:* A geo.Coord object&lt;br /&gt;
:* Two numbers separated by a comma, as if the function is taking three arguments. Example: &amp;lt;code&amp;gt;findAirportsWithinRange(lat, lon, range, type);&amp;lt;/code&amp;gt;.&lt;br /&gt;
|param2 = range&lt;br /&gt;
|param2text = Mandatory number giving the range in nautical miles within which to search for airports/heliports/seaplane bases.only airports are searched for.&lt;br /&gt;
|param3 = type&lt;br /&gt;
|param3text = This will narrow the search to airports of a certain type. By default, only airports are searched for. May be one of &amp;quot;airport,&amp;quot; &amp;quot;heliport,&amp;quot; or &amp;quot;seaport.&amp;quot;&lt;br /&gt;
|example1text = Searches for airports within 10 nm of [[KSFO]].&lt;br /&gt;
|example1 = var pos = airportinfo(&amp;quot;KSFO&amp;quot;);&lt;br /&gt;
var apts = findAirportsWithinRange(pos, 10);&lt;br /&gt;
foreach(var apt; apts){&lt;br /&gt;
    print(apt.name, &amp;quot; (&amp;quot;, apt.id, &amp;quot;)&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
|example2text = Searches for seaplane bases within 15 nm of [[KSFO]].&lt;br /&gt;
|example2 = var pos = airportinfo(&amp;quot;KSFO&amp;quot;);&lt;br /&gt;
var apts = findAirportsWithinRange(pos, 15, &amp;quot;seaport&amp;quot;);&lt;br /&gt;
foreach(var apt; apts){&lt;br /&gt;
    print(apt.name, &amp;quot; (&amp;quot;, apt.id, &amp;quot;)&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
|example3text = Searches for airports within 10 nm of your current position.&lt;br /&gt;
|example3 = var apts = findAirportsWithinRange(10);&lt;br /&gt;
foreach(var apt; apts){&lt;br /&gt;
    print(apt.name, &amp;quot; (&amp;quot;, apt.id, &amp;quot;)&amp;quot;);&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===findCommByFrequencyMHz()===&lt;br /&gt;
 findCommByFrequencyMHz([pos, ]freq[, type]);&lt;br /&gt;
[[sourceforge:p/flightgear/flightgear/ci/next/tree/src/Scripting/NasalPositioned.cxx#l1547|source]]&lt;br /&gt;
&lt;br /&gt;
Returns a &amp;lt;code&amp;gt;comm&amp;lt;/code&amp;gt; ghost object for a station matching a given frequency. If there is more than one station with that frequency, the nearest station is returned.&lt;br /&gt;
&lt;br /&gt;
'''pos'''&lt;br /&gt;
&lt;br /&gt;
: Optional position to search around. If not given, the aircraft's current position will be used. Can be one of:&lt;br /&gt;
:* An &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;runway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;taxiway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fix&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;waypoint&amp;lt;/code&amp;gt; ghost type&lt;br /&gt;
:* A hash with ''lat'' and ''lon'' members&lt;br /&gt;
:* A geo.Coord object&lt;br /&gt;
:* Two numbers separated by a comma, as if the function is taking three arguments. Example: &amp;lt;code&amp;gt;findCommByFrequencyMHz(lat, lon, freq, type);&amp;lt;/code&amp;gt;.&lt;br /&gt;
; freq&lt;br /&gt;
: Frequency, in megahertz, of the station to search for.&lt;br /&gt;
; type&lt;br /&gt;
: This will narrow the search to station of a certain type. Defaults to &amp;quot;all.&amp;quot; For the full list of accepted type arguments, see flightgear/src/Navaids/positioned.cxx (line 160)&lt;br /&gt;
'''Example'''&lt;br /&gt;
 var com = findCommByFrequencyMHz(123.6);&lt;br /&gt;
 print(&amp;quot;ID: &amp;quot;, com.id); # prints info about the comm station&lt;br /&gt;
 print(&amp;quot;Name: &amp;quot;, com.name);&lt;br /&gt;
 print(&amp;quot;Latitude: &amp;quot;, com.lat);&lt;br /&gt;
 print(&amp;quot;Longitude: &amp;quot;, com.lon);&lt;br /&gt;
 print(&amp;quot;Type: &amp;quot;, com.type);&lt;br /&gt;
 print(&amp;quot;Frequency: &amp;quot;, sprintf(&amp;quot;%.3f&amp;quot;, com.frequency), &amp;quot; Mhz&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
=== findFixesByID() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = findFixesByID([pos, ]id);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1627|t=Source}}&lt;br /&gt;
|text = Returns a vector containing &amp;lt;code&amp;gt;fix&amp;lt;/code&amp;gt; ghost objects matching a given ID, sorted by range from a certain position.&lt;br /&gt;
{{inote|Fixes are (usually) also known as waypoints.}}&lt;br /&gt;
|param1 = pos&lt;br /&gt;
|param1text = Optional position to search around. If not given, the aircraft's current position will be used. Can be one of:&lt;br /&gt;
:* An &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;runway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;taxiway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fix&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;waypoint&amp;lt;/code&amp;gt; ghost type&lt;br /&gt;
:* A hash with ''lat'' and ''lon'' members&lt;br /&gt;
:* A geo.Coord object&lt;br /&gt;
:* Two numbers separated by a comma, as if the function is taking three arguments. Example: &amp;lt;code&amp;gt;findFixesByID(lat, lon, id);&amp;lt;/code&amp;gt;.&lt;br /&gt;
|param2 = id&lt;br /&gt;
|param2text = Full or partial ID of the fix to search for.&lt;br /&gt;
:{{inote|1=Inputting a partial ID does not work correctly (see [http://forum.flightgear.org/viewtopic.php?f=30&amp;amp;t=28129 here]). It is best to just input a full ID.}}&lt;br /&gt;
|example1 = var fixes = findFixesByID(&amp;quot;POGIC&amp;quot;);&lt;br /&gt;
foreach(var fix; fixes){&lt;br /&gt;
    print(fix.id, &amp;quot; - lat: &amp;quot;, fix.lat, &amp;quot; {{!}} lon: &amp;quot;, fix.lon); # prints information about POGIC&lt;br /&gt;
}&lt;br /&gt;
|example2 = var fix = findFixesByID(&amp;quot;ZUNAP&amp;quot;);&lt;br /&gt;
fix = fix[0];&lt;br /&gt;
var (course, dist) = courseAndDistance(fix);&lt;br /&gt;
print(&amp;quot;Turn to heading &amp;quot;, math.round(course), &amp;quot;. You have &amp;quot;, sprintf(&amp;quot;%.2f&amp;quot;, dist), &amp;quot; nm to go to reach &amp;quot;, fixes[0].id);&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===findNavaidByFrequencyMHz()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = findNavaidByFrequencyMHz([pos, ]freq[, type]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1547|t=Source}}&lt;br /&gt;
|text = Returns a &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt; ghost object for a navaid matching a given frequency. If there is more than one navaid with that frequency, the nearest station is returned.&lt;br /&gt;
|param1 = pos&lt;br /&gt;
|param1text = Optional position to search around. If not given, the aircraft's current position will be used. Can be one of:&lt;br /&gt;
:* An &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;runway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;taxiway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fix&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;waypoint&amp;lt;/code&amp;gt; ghost type&lt;br /&gt;
:* A hash with ''lat'' and ''lon'' members&lt;br /&gt;
:* A geo.Coord object&lt;br /&gt;
:* Two numbers separated by a comma, as if the function is taking three arguments. Example: &amp;lt;code&amp;gt;findNavaidByFrequencyMHz(lat, lon, freq, type);&amp;lt;/code&amp;gt;.&lt;br /&gt;
|param2 = freq&lt;br /&gt;
|param2text = Frequency, in megahertz, of the navaid to search for.&lt;br /&gt;
|param3 = type&lt;br /&gt;
|param3text = This will narrow the search to navaids of a certain type. Defaults to &amp;quot;all.&amp;quot; For the full list of accepted type arguments, see {{flightgear file|src/Navaids/positioned.cxx|l=127}}.&lt;br /&gt;
|example1 = var navaid = findNavaidByFrequencyMHz(109.55);&lt;br /&gt;
print(&amp;quot;ID: &amp;quot;, navaid.id); # prints info about the navaid&lt;br /&gt;
print(&amp;quot;Name: &amp;quot;, navaid.name);&lt;br /&gt;
print(&amp;quot;Latitude: &amp;quot;, navaid.lat);&lt;br /&gt;
print(&amp;quot;Longitude: &amp;quot;, navaid.lon);&lt;br /&gt;
print(&amp;quot;Elevation (AMSL): &amp;quot;, navaid.elevation, &amp;quot; m&amp;quot;);&lt;br /&gt;
print(&amp;quot;Type: &amp;quot;, navaid.type);&lt;br /&gt;
print(&amp;quot;Frequency: &amp;quot;, sprintf(&amp;quot;%.3f&amp;quot;, navaid.frequency / 100), &amp;quot; Mhz&amp;quot;);&lt;br /&gt;
print(&amp;quot;Range: &amp;quot;, navaid.range_nm, &amp;quot; nm&amp;quot;);&lt;br /&gt;
if(navaid.course) print(&amp;quot;Course: &amp;quot;, navaid.course);&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== findNavaidsByFrequencyMHz()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = findNavaidsByFrequencyMHz([pos, ]freq[, type]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1572|t=Source}}&lt;br /&gt;
|text = Returns a vector conatining &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt; ghost objects for navaids that match a given frequency, sorted from nearest to furthest.&lt;br /&gt;
|param1 = pos&lt;br /&gt;
|param1text = Optional position to search around. If not given, the aircraft's current position will be used. Can be one of:&lt;br /&gt;
:* An &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;runway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;taxiway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fix&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;waypoint&amp;lt;/code&amp;gt; ghost type&lt;br /&gt;
:* A hash with ''lat'' and ''lon'' members&lt;br /&gt;
:* A geo.Coord object&lt;br /&gt;
:* Two numbers separated by a comma, as if the function is taking three arguments. Example: &amp;lt;code&amp;gt;findNavaidsByFrequencyMHz(lat, lon, freq, type);&amp;lt;/code&amp;gt;.&lt;br /&gt;
|param2 = freq&lt;br /&gt;
|param2text = Frequency, in megahertz, of the navaid to search for.&lt;br /&gt;
|param3 = type&lt;br /&gt;
|param3text = This will narrow the search to navaids of a certain type. Defaults to &amp;quot;all.&amp;quot; For the full list of accepted type arguments, see {{flightgear file|src/Navaids/positioned.cxx|l=127}}.&lt;br /&gt;
|example1 = var navaids = findNavaidsByFrequencyMHz(109.55);&lt;br /&gt;
foreach(var navaid; navaids){&lt;br /&gt;
    print(&amp;quot;--&amp;quot;);&lt;br /&gt;
    print(&amp;quot;ID: &amp;quot;, navaid.id); # prints info about the navaid&lt;br /&gt;
    print(&amp;quot;Name: &amp;quot;, navaid.name);&lt;br /&gt;
    print(&amp;quot;Latitude: &amp;quot;, navaid.lat);&lt;br /&gt;
    print(&amp;quot;Longitude: &amp;quot;, navaid.lon);&lt;br /&gt;
    print(&amp;quot;Elevation (AMSL): &amp;quot;, navaid.elevation, &amp;quot; m&amp;quot;);&lt;br /&gt;
    print(&amp;quot;Type: &amp;quot;, navaid.type);&lt;br /&gt;
    print(&amp;quot;Frequency: &amp;quot;, sprintf(&amp;quot;%.3f&amp;quot;, navaid.frequency / 100), &amp;quot; Mhz&amp;quot;);&lt;br /&gt;
    print(&amp;quot;Range: &amp;quot;, navaid.range_nm, &amp;quot; nm&amp;quot;);&lt;br /&gt;
    if(navaid.course) print(&amp;quot;Course: &amp;quot;, navaid.course);&lt;br /&gt;
    print(&amp;quot;--&amp;quot;);&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===findNavaidsByID()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = findNavaidsByID([pos, ]id[, type]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1600|t=Source}}&lt;br /&gt;
|text = Returns a vector containing &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt; ghost objects matching a given ID, sorted by range from a certain position.&lt;br /&gt;
|param1 = pos&lt;br /&gt;
|param1text = Optional position to search around. If not given, the aircraft's current position will be used. Can be one of:&lt;br /&gt;
:* An &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;runway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;taxiway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fix&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;waypoint&amp;lt;/code&amp;gt; ghost type&lt;br /&gt;
:* A hash with ''lat'' and ''lon'' members&lt;br /&gt;
:* A geo.Coord object&lt;br /&gt;
:* Two numbers separated by a comma, as if the function is taking three arguments. Example: &amp;lt;code&amp;gt;findNavaidsByID(lat, lon, id, type);&amp;lt;/code&amp;gt;.&lt;br /&gt;
|param2 = id&lt;br /&gt;
|param2text = Full or partial ID of the fix to search for.&lt;br /&gt;
:{{inote|1=Inputting a partial ID does not work correctly (see [http://forum.flightgear.org/viewtopic.php?f=30&amp;amp;t=28129 here]). It is best to just input a full ID.}}&lt;br /&gt;
|param3 = type&lt;br /&gt;
|param3text = This will narrow the search to navaids of a certain type. Defaults to &amp;quot;all.&amp;quot; For the full list of accepted type arguments, see {{flightgear file|src/Navaids/positioned.cxx|l=127}}.&lt;br /&gt;
|example1 = var navaid = findNavaidsByID(&amp;quot;MXW&amp;quot;);&lt;br /&gt;
navaid = navaid[0];&lt;br /&gt;
print(&amp;quot;ID: &amp;quot;, navaid.id); # prints info about 'MXW' (a VOR station)&lt;br /&gt;
print(&amp;quot;Name: &amp;quot;, navaid.name);&lt;br /&gt;
print(&amp;quot;Latitude: &amp;quot;, navaid.lat);&lt;br /&gt;
print(&amp;quot;Longitude: &amp;quot;, navaid.lon);&lt;br /&gt;
print(&amp;quot;Elevation (AMSL): &amp;quot;, navaid.elevation, &amp;quot; m&amp;quot;);&lt;br /&gt;
print(&amp;quot;Type: &amp;quot;, navaid.type);&lt;br /&gt;
print(&amp;quot;Frequency: &amp;quot;, sprintf(&amp;quot;%.3f&amp;quot;, navaid.frequency / 1000), &amp;quot; Mhz&amp;quot;);&lt;br /&gt;
print(&amp;quot;Range: &amp;quot;, navaid.range_nm, &amp;quot; nm&amp;quot;);&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===findNavaidsWithinRange()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = findNavaidsWithinRange([pos, ]range[, type]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1518|t=Source}}&lt;br /&gt;
|text = Returns a vector of &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt; ghost objects which are within a given range of a given position (by default the aircraft's current position). The results are sorted from closest to furthest.&lt;br /&gt;
|param1 = pos&lt;br /&gt;
|param1text = Optional position to search around. If not given, the aircraft's current position will be used. Can be one of:&lt;br /&gt;
:* An &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;runway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;taxiway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fix&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;waypoint&amp;lt;/code&amp;gt; ghost type&lt;br /&gt;
:* A hash with ''lat'' and ''lon'' members&lt;br /&gt;
:* A geo.Coord object&lt;br /&gt;
:* Two numbers separated by a comma, as if the function is taking three arguments. Example: &amp;lt;code&amp;gt;findNavaidsWithinRange(lat, lon, range, type);&amp;lt;/code&amp;gt;.&lt;br /&gt;
|param2 = range&lt;br /&gt;
|param2text = Mandatory number giving the range in nautical miles within which to search for navaids. Defaults to &amp;quot;all.&amp;quot; For the full list of accepted type arguments, see {{flightgear file|src/Navaids/positioned.cxx|l=127}}.&lt;br /&gt;
|param3 = type&lt;br /&gt;
|param3text = This will narrow the search to navaids of a certain type.&lt;br /&gt;
|example1text = Searches for navaids within 10 nm of [[KSFO]].&lt;br /&gt;
|example1 = var pos = airportinfo(&amp;quot;KSFO&amp;quot;);&lt;br /&gt;
var navs = findNavaidsWithinRange(pos, 10);&lt;br /&gt;
foreach(var nav; navs){&lt;br /&gt;
    print(nav.name, &amp;quot; (ID: &amp;quot;, nav.id, &amp;quot;)&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
|example2text = Searches for navaids within 10 nm of your current position.&lt;br /&gt;
|example2 = var navs = findNavaidsWithinRange(10);&lt;br /&gt;
foreach(var nav; navs){&lt;br /&gt;
    print(nav.name, &amp;quot; (ID: &amp;quot;, nav.id, &amp;quot;)&amp;quot;);&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===finddata()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = finddata(path);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=603|t=Source}}&lt;br /&gt;
|text = Takes a relative path and tries to return an absolute one. It works by appending the relative path to some paths and testing to see if they exist. As of FlightGear v3.7, these paths are the TerraSync directory (tested first) and [[$FG_ROOT]]. &lt;br /&gt;
|param1 = path&lt;br /&gt;
|param1text = A relative path as a string.&lt;br /&gt;
|example1 = var path = finddata(&amp;quot;Aircraft/Generic&amp;quot;);&lt;br /&gt;
print(path); # prints the absolute path to $FG_ROOT/Aircraft/Generic&lt;br /&gt;
|example2 = var path = finddata(&amp;quot;Airports&amp;quot;);&lt;br /&gt;
print(path); # prints the absolute path to &amp;lt;TerraSync dir&amp;gt;/Airports&lt;br /&gt;
|example3 = var path = finddata(&amp;quot;preferences.xml&amp;quot;);&lt;br /&gt;
print(path); # prints the absolute path to $FG_ROOT/preferences.xml&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===flightplan()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = flightplan([path]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1738|t=Source}}&lt;br /&gt;
|text = {{see also|Nasal Flightplan}}&lt;br /&gt;
Returns a flight plan object, either one for the current flight plan, or one loaded from a given path.&lt;br /&gt;
|param1 = path&lt;br /&gt;
|param1text = Optional path to flight plan XML file.&lt;br /&gt;
|example1text = Gets the active flight plan and gets the ID of the current waypoint. Note that this example requires a flight plan to be set in the [[Route Manager]] first.&lt;br /&gt;
|example1 = var fp = flightplan();&lt;br /&gt;
print(fp.getWP(fp.current).id);&lt;br /&gt;
|example2text = Creates a new flight plan from an XML file and prints the IDs of the waypoints. Note that this example requires a flight plan to have been created and saved as &amp;lt;tt&amp;gt;''[[$FG_HOME]]/fp-demo.xml''&amp;lt;/tt&amp;gt;.&lt;br /&gt;
|example2 = var path = getprop('/sim/fg-home') ~ '/fp-demo.xml';&lt;br /&gt;
var fp = flightplan(path);&lt;br /&gt;
for(var i = 0; i &amp;lt; fp.getPlanSize(); i += 1){&lt;br /&gt;
    print(fp.getWP(i).id);&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===geodinfo()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = geodinfo(lat, lon[, max_alt]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=981|t=Source}}&lt;br /&gt;
|text = Returns a vector containing two entries or &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; if no information could be obtained because the terrain tile wasn't loaded. The first entry in the vector is the elevation (in meters) for the given point, and the second is a hash with information about the assigned material (as defined in &amp;lt;tt&amp;gt;''[[$FG_ROOT]]/Materials''&amp;lt;/tt&amp;gt;), or &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; if there was no material information available (for example, because there is an untextured building at that location). The structure of the hash is as follows (see also {{readme file|materials}}):&lt;br /&gt;
* '''light_coverage:''' The coverage of a single point of light in m&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;.&lt;br /&gt;
* '''bumpiness:''' Normalized bumpiness factor for the material.&lt;br /&gt;
* '''load_resistance:''' The amount of pressure in N/m&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt; the material can withstand without deformation.&lt;br /&gt;
* '''solid:''' 1 (true) or false (0) depending on whether the material is solid or not.&lt;br /&gt;
* '''names:''' Vector of scenery types (usually generated by [[TerraGear]]) that will use this material. &lt;br /&gt;
* '''friction_factor:''' Normalized friction factor of the material.&lt;br /&gt;
* '''rolling_friction:''' The rolling friction coefficient of the material.&lt;br /&gt;
&lt;br /&gt;
Note that this function is a ''very'' CPU-intensive operation, particularly in FlightGear v2.4 and earlier. It is advised to use this function as little as possible.&lt;br /&gt;
|param1 = lat&lt;br /&gt;
|param1text = Latitude, inputted as a number.&lt;br /&gt;
|param2 = lon&lt;br /&gt;
|param2text = Longitude, inputted as a number.&lt;br /&gt;
|param3 = max_alt&lt;br /&gt;
|param3text = The altitude, in metres, from which the function will begin searching for the height of the terrain. Defaults to 10,000 metres. If the terrain is higher than this argument specifies, &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; will be returned.&lt;br /&gt;
|example1text = Dumps information about ground underneath the aircraft.&lt;br /&gt;
|example1 = var pos = geo.aircraft_position();&lt;br /&gt;
var info = geodinfo(pos.lat(), pos.lon());&lt;br /&gt;
debug.dump(info);&lt;br /&gt;
|example2text = Prints whether the ground underneath the aircraft is solid or is water.&lt;br /&gt;
|example2 = var pos = geo.aircraft_position();&lt;br /&gt;
var info = geodinfo(pos.lat(), pos.lon());&lt;br /&gt;
if (info != nil and info[1] != nil) {&lt;br /&gt;
    print(&amp;quot;The ground underneath the aircraft is &amp;quot;, info[1].solid == 1 ? &amp;quot;solid.&amp;quot; : &amp;quot;water.&amp;quot;);&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== geodtocart() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = geodtocart(lat, lon, alt);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=962|t=Source}}&lt;br /&gt;
|text = Converts {{wikipedia|geodetic coordinates}} (latitude, longitude, and altitude) to {{wikipedia|ECEF|Earth-centered, Earth-fixed}} coordinates (x, y and z). A vector is returned containing x, y, and z in metres. The equatorial radius of earth used is that defined by the {{wikipedia|WGS 84}} (6,378,137 metres). All argument are mandatory.&lt;br /&gt;
|param1 = lat&lt;br /&gt;
|param1text = Latitude, in degrees.&lt;br /&gt;
|param2 = lon&lt;br /&gt;
|param2text = Longitude, in degrees.&lt;br /&gt;
|param3 = alt&lt;br /&gt;
|param3text = Altitude, in metres.&lt;br /&gt;
|example1 = var (x, y, z) = geodtocart(0, 0, 0); # point is the intersection of the prime meridian and equator.&lt;br /&gt;
print(&amp;quot;x: &amp;quot;, x); # prints &amp;quot;x: 6378137&amp;quot;&lt;br /&gt;
print(&amp;quot;y: &amp;quot;, y); # prints &amp;quot;y: 0&amp;quot;&lt;br /&gt;
print(&amp;quot;z: &amp;quot;, z); # prints &amp;quot;y: 0&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== get_cart_ground_intersection()===&lt;br /&gt;
Introduced in 2017.2.1, see [[Terrain Detection]].&lt;br /&gt;
&lt;br /&gt;
===getprop()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = &amp;lt;nowiki&amp;gt;getprop(path[, path[, ...]]);&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=345|t=Source}}&lt;br /&gt;
|text = Returns the value of a node in the [[Property Tree]] or &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; if the node does not exist or the value is not a number (NaN).&lt;br /&gt;
|param1 = path&lt;br /&gt;
|param1text = There needs to be at least one argument, but there is no limit to the maximum amount of arguments that can be given. The arguments will be concatenated together to form a property tree path. The arguments must be strings, but in FlightGear v3.2 onwards, there is also support (added by {{flightgear commit|34ed79}}) for numeric arguments as indices. See example 2 below.&lt;br /&gt;
|example1 = print(&amp;quot;You have FlightGear v&amp;quot;, getprop(&amp;quot;/sim/version/flightgear&amp;quot;)); # prints FlightGear version&lt;br /&gt;
|example2text = Note that the example below will only work in FlightGear 3.2 and above.&lt;br /&gt;
|example2 = for(var i = 0; i &amp;lt; 8; i += 1){&lt;br /&gt;
    print(&amp;quot;View #&amp;quot;, i + 1, &amp;quot; is named &amp;quot;, getprop(&amp;quot;/sim/view&amp;quot;, i, &amp;quot;name&amp;quot;));&lt;br /&gt;
}&lt;br /&gt;
|example3text = Same as above, but is supported by all versions of FlightGear.&lt;br /&gt;
|example3 = for(var i = 0; i &amp;lt; 8; i += 1){&lt;br /&gt;
    print(&amp;quot;View #&amp;quot;, i + 1, &amp;quot; is named &amp;quot;, getprop(&amp;quot;/sim/view[&amp;quot; ~ i ~ &amp;quot;]/name&amp;quot;));&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
==== See also====&lt;br /&gt;
{{note| If you have to read/write the same property multiple times (e.g. in an update loop), it is more efficient to use a node object: &lt;br /&gt;
To get a Node rather than its value, use &amp;lt;code&amp;gt;props.globals.getNode()&amp;lt;/code&amp;gt; - see [[Nasal_library/props]]. }}&lt;br /&gt;
&lt;br /&gt;
===greatCircleMove()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = greatCircleMove([pos, ]course, dist);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1681|t=Source}}&lt;br /&gt;
|text = Calculates a new set of geodetic coordinates using inputs of course and distance, either from the aircraft's current position (by default) or from another set of coordinates. Returns a hash containing two members, ''lat'' and ''lon'' (latitude and longitude respectively).&lt;br /&gt;
|param1 = pos&lt;br /&gt;
|param1text = Optional position to calculate from. If not given, the aircraft's current position will be used. Can be one of:&lt;br /&gt;
:* An &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;runway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;taxiway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fix&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;waypoint&amp;lt;/code&amp;gt; ghost object.&lt;br /&gt;
:* A hash with ''lat'' and ''lon'' members&lt;br /&gt;
:* A &amp;lt;code&amp;gt;geo.Coord&amp;lt;/code&amp;gt; object&lt;br /&gt;
:* A lat/lon pair, that is, a pair of numbers (latitude followed by longitude) separated by a comma: &amp;lt;code&amp;gt;greatCircleMove(lat,lon, ...)&amp;lt;/code&amp;gt;.&lt;br /&gt;
|param2 = course&lt;br /&gt;
|param2text = Course to new set of coordinates, in degrees (in the range 0–360).&lt;br /&gt;
|param3 = dist&lt;br /&gt;
|param3text = Distance in nautical miles to the new set of coordinates.&lt;br /&gt;
|example1 = var pos = greatCircleMove(0,0, 0, 1);&lt;br /&gt;
debug.dump(pos); # print hash with coordinates&lt;br /&gt;
|example2 = var fix = findFixesByID(&amp;quot;POGIC&amp;quot;);&lt;br /&gt;
fix = fix[0];&lt;br /&gt;
var pos = greatCircleMove(fix, 45, 10);&lt;br /&gt;
debug.dump(pos); # print hash with coordinates&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===interpolate()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|private = _interpolate()&lt;br /&gt;
|syntax = &amp;lt;nowiki&amp;gt;interpolate(prop, value1, time1[, value2, time2[, ...]]);&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=522|t=Part 1}} {{!}} {{fgdata file|Nasal/globals.nas|t=Part 2}}&lt;br /&gt;
|text = Linearly interpolates a node in the property tree to a given value in a specified time. The value/time pairs will be run one after the other in the order that they are passed to the function. Note that the interpolation will continue even when the simulation is paused.&lt;br /&gt;
|param1 = prop&lt;br /&gt;
|param1text = String or &amp;lt;code&amp;gt;props.Node&amp;lt;/code&amp;gt; object that indicates a node in the property tree to be used.&lt;br /&gt;
|param2 = value''n''&lt;br /&gt;
|param2text = Target value to change the property to in the set amount of time. This should be a number.&lt;br /&gt;
|param3 = time''n''&lt;br /&gt;
|param3text = Time in seconds, that will be taken for the interpolation.&lt;br /&gt;
|example1text = Paste the code below into the Nasal Console and execute. Then, open the Property Browser and look for the property. Finally, run the code again, and watch the value of the property change.&lt;br /&gt;
|example1 = setprop(&amp;quot;/test&amp;quot;, 0); # (re-)set property&lt;br /&gt;
interpolate(&amp;quot;/test&amp;quot;,&lt;br /&gt;
    50, 5, # interpolate to 50 in 5 seconds&lt;br /&gt;
    10, 2, # interpolate to 10 in 2 seconds&lt;br /&gt;
    0, 5); # interpolate to 0 in 5 seconds&lt;br /&gt;
|example2 = # Apply the left brake at 20% per second&lt;br /&gt;
var prop = &amp;quot;controls/gear/brake-left&amp;quot;;&lt;br /&gt;
var dist = 1 - getprop(prop);&lt;br /&gt;
if (dist == 1) {&lt;br /&gt;
    interpolate(prop, 1, dist / 0.2);&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===isa()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = isa(object, class);&lt;br /&gt;
|source = {{fgdata file|Nasal/globals.nas|t=Source}}&lt;br /&gt;
|text = Checks if an object is an instance of, or inherits from, a second object (or class), returning 1 (true) if it is and 0 (false) if otherwise.&lt;br /&gt;
|param1 = object&lt;br /&gt;
|param1text = Object to check.&lt;br /&gt;
|param2 = class&lt;br /&gt;
|param2text = Class/object to check that '''object''' inherits from or is an instance of.&lt;br /&gt;
|example1 = var coord = geo.Coord.new();&lt;br /&gt;
if(isa(coord, geo.Coord)){&lt;br /&gt;
    print(&amp;quot;Variable 'coord' is an instance of class 'geo.Coord'&amp;quot;); # this one will be printed&lt;br /&gt;
} else {&lt;br /&gt;
    print(&amp;quot;Variable 'coord' is not an instance of class 'geo.Coord'&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
|example2 = var coord = geo.Coord.new();&lt;br /&gt;
if(isa(coord, props.Node)){&lt;br /&gt;
    print(&amp;quot;Variable 'coord' is an instance of class 'props.Node'&amp;quot;);&lt;br /&gt;
} else {&lt;br /&gt;
    print(&amp;quot;Variable 'coord' is not an instance of class 'props.Node'&amp;quot;); # this one will be printed&lt;br /&gt;
}&lt;br /&gt;
|example3text = The example below demonstrates checking of inheritance.&lt;br /&gt;
|example3 = var Const = {&lt;br /&gt;
    constant: 2,&lt;br /&gt;
    getConst: func {&lt;br /&gt;
        return me.constant;&lt;br /&gt;
    }&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
var Add = {&lt;br /&gt;
    new: func {&lt;br /&gt;
        return { parents: [Add, Const] };&lt;br /&gt;
    },&lt;br /&gt;
&lt;br /&gt;
    addToConst: func(a){&lt;br /&gt;
        return a * me.getConst();&lt;br /&gt;
    }&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
var m = Add.new();&lt;br /&gt;
print(m.addToConst(4));&lt;br /&gt;
&lt;br /&gt;
if(isa(m, Add)) print(&amp;quot;Variable 'm' is an instance of class 'Add'&amp;quot;); # will be printed&lt;br /&gt;
if(isa(m, Const)) print(&amp;quot;Variable 'm' is an instance of class 'Const'&amp;quot;); # will also be printed&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===logprint()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = &amp;lt;nowiki&amp;gt;logprint(priority[, msg[, msg[, ...]]]);&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=431|t=Source}}&lt;br /&gt;
|text = Concatenates a message and logs it with a given priority level. Unlike {{func link|print()}} and {{func link|printlog()}}, message outputted by this function will be logged in your &amp;lt;code&amp;gt;[[Commonly used debugging tools#fgfs.log|fgfs.log]]&amp;lt;/code&amp;gt; file as coming from the Nasal file itself rather than from {{flightgear file|src/Scripting/NasalSys.cxx}}.&lt;br /&gt;
|param1 = priority&lt;br /&gt;
|param1text = Number specifying the priority level of the outputted message:&lt;br /&gt;
:{{{!}} class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Number !! Debug type&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} 1 {{!!}} Bulk&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} 2 {{!!}} Debug&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} 3 {{!!}} Info&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} 4 {{!!}} Warn&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} 5 {{!!}} Alert&lt;br /&gt;
{{!}}}&lt;br /&gt;
|param2 = msg&lt;br /&gt;
|param2text = The message. There is no limit to the arguments you give give. They will be concatenated together before logging.&lt;br /&gt;
|example1 = # logs the value of pi to three decimal places with log level 3&lt;br /&gt;
logprint(3, &amp;quot;pi = &amp;quot;, sprintf(&amp;quot;%.3f&amp;quot;, math.pi));&lt;br /&gt;
|example2 = logprint(5, &amp;quot;Alert! This is an important message!&amp;quot;);&lt;br /&gt;
}}&lt;br /&gt;
{{note| &lt;br /&gt;
The following constants have been added to the development branch of FlightGear (&amp;quot;next&amp;quot;) and will be releases with FG 2020.1 so you won't have to remember the numbers anymore:&lt;br /&gt;
&lt;br /&gt;
LOG_BULK, LOG_WARN, LOG_DEBUG, LOG_INFO, LOG_ALERT, DEV_WARN, DEV_ALERT }}&lt;br /&gt;
&lt;br /&gt;
===magvar() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = magvar([pos]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1642|t=Source}}&lt;br /&gt;
|text = Returns the {{wikipedia|magnetic variation}} at a given set of coordinates. The table below gives the magnetic model used depending on the version of FlightGear.&lt;br /&gt;
{{{!}} class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! FlightGear versions !! Model !! Reference date&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} 3.6 and above {{!!}} {{wikipedia|World Magnetic Model}} (WMM) 2015 {{!!}} 1 January 2015&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} 0.9.11-pre1 to 3.4 {{!!}} WMM 2005 {{!!}} 1 January 2005&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} 0.7.3 to 0.9.10 {{!!}} WMM 2000 {{!!}} 1 January 2000&lt;br /&gt;
{{!}}}&lt;br /&gt;
|param1 = pos&lt;br /&gt;
|param1text = Optional position to calculate from. If not given, the aircraft's current position will be used. Can be one of:&lt;br /&gt;
:* An &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;runway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;taxiway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fix&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;waypoint&amp;lt;/code&amp;gt; ghost object.&lt;br /&gt;
:* A hash with ''lat'' and ''lon'' members&lt;br /&gt;
:* A &amp;lt;code&amp;gt;geo.Coord&amp;lt;/code&amp;gt; object&lt;br /&gt;
:* A lat/lon pair, that is, a pair of numbers (latitude followed by longitude) separated by a comma: &amp;lt;code&amp;gt;magvar(lat,lon)&amp;lt;/code&amp;gt;.&lt;br /&gt;
|example1 = print(magvar(0, 0)); # prints the magnetic variation at 0, 0&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===maketimer() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = maketimer(interval[, self], function);&lt;br /&gt;
|source = ''Implemented using the {{API Link|flightgear|class|TimerObj}} class.''&amp;lt;br&amp;gt;{{flightgear file|src/Scripting/NasalSys.cxx|l=90|t=Part 1}} {{!}} {{flightgear file|src/Scripting/NasalSys.cxx|l=533|t=Part 2}}&lt;br /&gt;
|version = 2.12&lt;br /&gt;
|commit = {{flightgear commit|ab939f|t=commit}}&lt;br /&gt;
|text = Returns a timer object containing the following methods and members:&lt;br /&gt;
* '''start()''': Starts the timer.&lt;br /&gt;
* '''stop()''': Stops the timer.&lt;br /&gt;
* '''restart(interval)''': Restarts the timer with the given interval.&lt;br /&gt;
* '''singleShot''': Bool showing whether the timer is only to be run once, or continuously until told to stop. Can be both set and read from (see examples).&lt;br /&gt;
* '''isRunning''': Read-only bool telling whether the timer is currently running.&lt;br /&gt;
* '''simulatedTime''': (FG 2017.1+; {{flightgear commit|0af316|t=commit}}) Bool telling whether the timer is using simulated time (which accounts for pause, etc.). Defaults to false (use real time). Can be both read and set. This cannot be changed while the timer is running.&lt;br /&gt;
Unlike {{func link|settimer()}}, which it replaces, &amp;lt;code&amp;gt;maketimer()&amp;lt;/code&amp;gt; provides more control over the timer. In addition, it can help reduce memory usage.&lt;br /&gt;
|param1 = interval&lt;br /&gt;
|param1text = Interval in seconds for the timer.&lt;br /&gt;
|param2 = self&lt;br /&gt;
|param2text = Optional parameter specifying what any &amp;lt;code&amp;gt;'''me'''&amp;lt;/code&amp;gt; references in the function being called will refer to.&lt;br /&gt;
|param3 = function&lt;br /&gt;
|param3text = Function to be called at the given interval.&lt;br /&gt;
|example1 = var timer = maketimer(1, func(){&lt;br /&gt;
    print(&amp;quot;Hello, World!&amp;quot;); # print &amp;quot;Hello, World!&amp;quot; once every second (call timer.stop() to stop it)&lt;br /&gt;
});&lt;br /&gt;
timer.start();&lt;br /&gt;
|example2 = var timer = maketimer(1, math, func(){&lt;br /&gt;
    print(me.math); # 'me' reference is the 'math' namespace&lt;br /&gt;
});&lt;br /&gt;
timer.singleShot = 1; # timer will only be run once&lt;br /&gt;
timer.start();&lt;br /&gt;
|example3 = var timer = maketimer(1, func(){&lt;br /&gt;
    print(&amp;quot;Hello, World!&amp;quot;); # print &amp;quot;Hello, World!&amp;quot; once every second (call timer.stop() to stop it)&lt;br /&gt;
});&lt;br /&gt;
timer.start();&lt;br /&gt;
print(timer.isRunning); # prints 1&lt;br /&gt;
|example4text = In the example below, &amp;quot;Hello, World!&amp;quot; will be printed after one second the first time, and after two seconds thereafter.&lt;br /&gt;
|example4 = var update = func(){&lt;br /&gt;
    print(&amp;quot;Hello, World!&amp;quot;);&lt;br /&gt;
    timer.restart(2); # restarts the timer with a two second interval&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var timer = maketimer(1, update);&lt;br /&gt;
timer.singleShot = 1;&lt;br /&gt;
timer.start();&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===maketimestamp()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = maketimestamp()&lt;br /&gt;
|source = ''Implemented using the {{API Link|flightgear|class|TimeStampObj}} class.''&amp;lt;br&amp;gt;{{flightgear file|src/Scripting/NasalSys.cxx|l=214|t=Part 1}} {{!}} {{flightgear file|src/Scripting/NasalSys.cxx|l=589|t=Part 2}}&lt;br /&gt;
|version = 2019.2&lt;br /&gt;
|commit = {{flightgear commit|7db06300|t=commit}}&lt;br /&gt;
|text = Returns a time stamp object to allow high resolution timing of Nasal operations. When created the timer will automatically be stamped. The object has the following methods:&lt;br /&gt;
* '''stamp()''': Resets the timing operation. Call this first.&lt;br /&gt;
* '''elapsedMSec()''': returns number of milliseconds elapsed since stamp() called. Resolution may vary depending on platform but is usually at least millisecond accuracy.&lt;br /&gt;
* '''elapsedUSec()''': returns number of microseconds elapsed since stamp() called. Resolution may vary depending on platform but is usually at least millisecond accuracy.&lt;br /&gt;
|&lt;br /&gt;
|example1text = In the example below the number of milliseconds elapsed will be printed.&lt;br /&gt;
|example1 = var timestamp = maketimestamp();&lt;br /&gt;
timestamp.stamp();&lt;br /&gt;
print(timestamp.elapsedMSec(), &amp;quot;ms elapsed&amp;quot;);&lt;br /&gt;
print(timestamp.elapsedMSec(), &amp;quot;ms elapsed&amp;quot;);&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===md5()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = md5(string);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=758|t=Source}}&lt;br /&gt;
|version = 3.2&lt;br /&gt;
|commit = {{flightgear commit|cfbf9e|t=commit}}&lt;br /&gt;
|text = Returns a the {{wikipedia|MD5}} hash (as a string) of the inputted string.&lt;br /&gt;
|param1 = string&lt;br /&gt;
|param1text = String the generate the hash of. Mandatory.&lt;br /&gt;
|example1text = The below code should output &amp;lt;code&amp;gt;65a8e27d8879283831b664bd8b7f0ad4&amp;lt;/code&amp;gt;.&lt;br /&gt;
|example1 = print(md5(&amp;quot;Hello, World!&amp;quot;));&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===navinfo()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = navinfo(lat, lon, type, id);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1453|t=Source}}&lt;br /&gt;
|text = Returns vector &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt; ghost objects matching the given '''type''' and '''id''' or &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; on error.&lt;br /&gt;
|param1 = lat ''and'' lon&lt;br /&gt;
|param1text = If given, the returned navaids will be put into order of ascending distance from the location.&lt;br /&gt;
|param2 = type&lt;br /&gt;
|param2text = Narrows the search to the given type. Must be one of &amp;quot;any,&amp;quot; &amp;quot;fix,&amp;quot; &amp;quot;vor,&amp;quot; &amp;quot;ndb,&amp;quot; &amp;quot;ils,&amp;quot; &amp;quot;dme,&amp;quot; or &amp;quot;tacan.&amp;quot; Defaults to the equivalent of &amp;quot;any.&amp;quot;&lt;br /&gt;
|param3 = id&lt;br /&gt;
|param3text = ID to search for. Note that, although all the parameters are technically optional, this parameter must be given, otherwise an empty vector will be returned.&lt;br /&gt;
|example1 = navinfo(&amp;quot;vor&amp;quot;); # returns all VORs&lt;br /&gt;
|example2 = navinfo(&amp;quot;HAM&amp;quot;); # return all navaids whose names start with &amp;quot;HAM&amp;quot;&lt;br /&gt;
|example3 = navinfo(&amp;quot;vor&amp;quot;, &amp;quot;HAM&amp;quot;); # return all VORs whose names start with &amp;quot;HAM&amp;quot;&lt;br /&gt;
|example4 = navinfo(34,48,&amp;quot;vor&amp;quot;,&amp;quot;HAM&amp;quot;); # return all VORs whose names start with &amp;quot;HAM&amp;quot; and sorted by distance relative to 34°, 48°&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===parse_markdown()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = parse_markdown(markdown);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=745|t=Part 1}} {{!}} {{simgear file|simgear/misc/SimpleMarkdown.cxx|t=Part 2}} &lt;br /&gt;
|version = 3.2&lt;br /&gt;
|text = Parses a string containing {{wikipedia|Markdown}} and returns the result as a string. As of FlightGear 2016.1, it is just a simple parser, and does the following:&lt;br /&gt;
* It strips whitespace from the beginning of the string.&lt;br /&gt;
* It supports [http://daringfireball.net/projects/markdown/syntax#p paragraphs and line breaks].&lt;br /&gt;
* It collapses whitespace.&lt;br /&gt;
* It converts unordered [http://daringfireball.net/projects/markdown/syntax#list lists] to use a bullet character (&amp;amp;bull;). Note that the bullet character is implemented in hexadecimal UTF-8 character bytes (&amp;lt;code&amp;gt;E2 80 A2&amp;lt;/code&amp;gt;), as so may not work properly when the being displayed in an encoding other than UTF-8.&lt;br /&gt;
|param1 = markdown&lt;br /&gt;
|param1text = String containing Markdown to be parsed.&lt;br /&gt;
|example1text = &lt;br /&gt;
Save the below code as &amp;lt;tt&amp;gt;''[[$FG_ROOT]]/gui/dialogs/test.xml''&amp;lt;/tt&amp;gt;, then run the Nasal code below it to open the dialog. To change the markdown to be parsed, simply change the code in the highlighted section, save it, and reload the GUI (&amp;lt;tt&amp;gt;Debug &amp;gt; Reload GUI&amp;lt;/tt&amp;gt;).&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot; highlight=&amp;quot;41-50&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;PropertyList&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;name&amp;gt;test&amp;lt;/name&amp;gt;&lt;br /&gt;
&amp;lt;layout&amp;gt;vbox&amp;lt;/layout&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;group&amp;gt;&lt;br /&gt;
    &amp;lt;layout&amp;gt;hbox&amp;lt;/layout&amp;gt;&lt;br /&gt;
    &lt;br /&gt;
    &amp;lt;empty&amp;gt;&lt;br /&gt;
        &amp;lt;stretch&amp;gt;true&amp;lt;/stretch&amp;gt;&lt;br /&gt;
    &amp;lt;/empty&amp;gt;&lt;br /&gt;
    &lt;br /&gt;
    &amp;lt;text&amp;gt;&lt;br /&gt;
        &amp;lt;label&amp;gt;parse_markdown() test dialog&amp;lt;/label&amp;gt;&lt;br /&gt;
    &amp;lt;/text&amp;gt;&lt;br /&gt;
    &lt;br /&gt;
    &amp;lt;empty&amp;gt;&lt;br /&gt;
        &amp;lt;stretch&amp;gt;true&amp;lt;/stretch&amp;gt;&lt;br /&gt;
    &amp;lt;/empty&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    &amp;lt;button&amp;gt;&lt;br /&gt;
        &amp;lt;legend&amp;gt;&amp;lt;/legend&amp;gt;&lt;br /&gt;
        &amp;lt;pref-width&amp;gt;16&amp;lt;/pref-width&amp;gt;&lt;br /&gt;
        &amp;lt;pref-height&amp;gt;16&amp;lt;/pref-height&amp;gt;&lt;br /&gt;
        &amp;lt;binding&amp;gt;&lt;br /&gt;
            &amp;lt;command&amp;gt;dialog-close&amp;lt;/command&amp;gt;&lt;br /&gt;
        &amp;lt;/binding&amp;gt;&lt;br /&gt;
    &amp;lt;/button&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/group&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;canvas&amp;gt;&lt;br /&gt;
    &amp;lt;name&amp;gt;Canvas plot&amp;lt;/name&amp;gt;&lt;br /&gt;
    &amp;lt;stretch&amp;gt;true&amp;lt;/stretch&amp;gt;&lt;br /&gt;
    &amp;lt;pref-width&amp;gt;400&amp;lt;/pref-width&amp;gt;&lt;br /&gt;
    &amp;lt;pref-height&amp;gt;300&amp;lt;/pref-height&amp;gt;&lt;br /&gt;
    &amp;lt;nasal&amp;gt;&lt;br /&gt;
        &amp;lt;load&amp;gt;&amp;lt;![CDATA[&lt;br /&gt;
var text = 'Items:&lt;br /&gt;
* apples&lt;br /&gt;
* oranges&lt;br /&gt;
* pears&lt;br /&gt;
&lt;br /&gt;
Some text.&lt;br /&gt;
Some more items:&lt;br /&gt;
* apples&lt;br /&gt;
* oranges&lt;br /&gt;
* pears';&lt;br /&gt;
&lt;br /&gt;
var parsed = parse_markdown(text);&lt;br /&gt;
&lt;br /&gt;
var root_canvas = canvas.get(cmdarg());&lt;br /&gt;
root_canvas.setColorBackground(255, 255, 255);&lt;br /&gt;
var root = root_canvas.createGroup();&lt;br /&gt;
&lt;br /&gt;
var text_dis = root.createChild(&amp;quot;text&amp;quot;)&lt;br /&gt;
    .setText(parsed)&lt;br /&gt;
    .setTranslation(5, 5)&lt;br /&gt;
    .setFont(&amp;quot;LiberationFonts\LiberationSans-Regular.ttf&amp;quot;)&lt;br /&gt;
    .setFontSize(15)&lt;br /&gt;
    .setColor(0, 0, 0)&lt;br /&gt;
    .setDrawMode(canvas.Text.TEXT)&lt;br /&gt;
    .setAlignment(&amp;quot;left-top&amp;quot;);&lt;br /&gt;
        ]]&amp;gt;&amp;lt;/load&amp;gt;&lt;br /&gt;
    &amp;lt;/nasal&amp;gt;&lt;br /&gt;
&amp;lt;/canvas&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/PropertyList&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|example1 = fgcommand(&amp;quot;dialog-show&amp;quot;, {&amp;quot;dialog-name&amp;quot;: &amp;quot;test&amp;quot;});&lt;br /&gt;
|example2text = The example below parses Markdown and outputs it in a HTML document. The parsed text is placed in &amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot; inline&amp;gt;&amp;lt;pre&amp;gt;&amp;lt;/pre&amp;gt;&amp;lt;/syntaxhighlight&amp;gt; tags. To change the Markdown to be parsed, simply edit the variable &amp;lt;tt&amp;gt;markdown&amp;lt;/tt&amp;gt; at the top of the code.&lt;br /&gt;
|example2 = &amp;lt;nowiki&amp;gt;var markdown = 'Items:&lt;br /&gt;
* apples&lt;br /&gt;
* oranges&lt;br /&gt;
* pears&lt;br /&gt;
&lt;br /&gt;
Some text.&lt;br /&gt;
Some more items:&lt;br /&gt;
* apples&lt;br /&gt;
* oranges&lt;br /&gt;
* pears';&lt;br /&gt;
&lt;br /&gt;
var parsed = parse_markdown(markdown);&lt;br /&gt;
&lt;br /&gt;
debug.dump(parsed);&lt;br /&gt;
&lt;br /&gt;
var path = string.normpath(getprop(&amp;quot;/sim/fg-home&amp;quot;) ~ '/Export/parse_markdown()-test.html');&lt;br /&gt;
&lt;br /&gt;
var file = io.open(path, &amp;quot;w&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
var html = &amp;quot;&amp;lt;!DOCTYPE html&amp;gt;\n\n&amp;lt;html&amp;gt;\n\n&amp;lt;head&amp;gt;\n\t&amp;lt;meta charset=\&amp;quot;UTF-8\&amp;quot;&amp;gt;\n\t&amp;lt;title&amp;gt;parse_markdown() test generated by Nasal&amp;lt;/title&amp;gt;\n&amp;lt;/head&amp;gt;\n\n&amp;lt;body&amp;gt;\n\t&amp;lt;pre&amp;gt;&amp;quot; ~ parsed ~ &amp;quot;&amp;lt;/pre&amp;gt;\n&amp;lt;/body&amp;gt;\n\n&amp;lt;/html&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
io.write(file, html);&lt;br /&gt;
io.close(file);&lt;br /&gt;
print(&amp;quot;Done, file ready for viewing (&amp;quot; ~ path ~ &amp;quot;)&amp;quot;);&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===parsexml()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = &amp;lt;nowiki&amp;gt;parsexml(path[, start[, end[, data[, pro_ins]]]]);&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=710|t=Source}}&lt;br /&gt;
|text = This function is an interface into the built-in [http://expat.sourceforge.net/ Expat XML parser]. The absolute path to the file is returned as string, or &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; is returned on error.&lt;br /&gt;
|param1 = path&lt;br /&gt;
|param1text = Mandatory absolute path to the XML file to be parsed.&lt;br /&gt;
|param2 = start&lt;br /&gt;
|param2text = Optional callback function that will be called for every starting tag. The function should take two argument: the tag name and a hash containing attributes.&lt;br /&gt;
|param3 = end&lt;br /&gt;
|param3text = Optional callback function that will be called for every ending tag. The function should take one argument: the tag name.&lt;br /&gt;
|param4 = data&lt;br /&gt;
|param4text = Optional callback function that will be called for every piece of data within a set of tags. The function should take one argument: the data as a string.&lt;br /&gt;
|param5 = pro_ins&lt;br /&gt;
|param5text = Optional callback function that will be called for every {{wikipedia|Processing Instruction|processing instruction}}. The function should take two argument: the target and the data string.&lt;br /&gt;
|example1text = Save the below XML code in &amp;lt;tt&amp;gt;''[[$FG_HOME]]/Export/demo.xml''&amp;lt;/tt&amp;gt;. Then, execute the Nasal code below in the Nasal Console. The XML will be parsed and each bit of the code will be printed.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;?xml-stylesheet type=&amp;quot;text/xsl&amp;quot; href=&amp;quot;style.xsl&amp;quot;?&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;foo&amp;gt;&lt;br /&gt;
  &amp;lt;blah type=&amp;quot;string&amp;quot;&amp;gt;&amp;lt;![CDATA[ &amp;lt;sender&amp;gt;John Smith&amp;lt;/sender&amp;gt; ]]&amp;gt;&amp;lt;/blah&amp;gt;&lt;br /&gt;
  &amp;lt;blah2 type=&amp;quot;string&amp;quot;&amp;gt;Orange &amp;amp;amp; lemons&amp;lt;/blah2&amp;gt;&lt;br /&gt;
&amp;lt;/foo&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|example1 = var start = func(name, attr){&lt;br /&gt;
    print(&amp;quot;Starting tag: '&amp;quot;, name, &amp;quot;'&amp;quot;);&lt;br /&gt;
    foreach(var a; keys(attr)){&lt;br /&gt;
        print(&amp;quot;\twith attribute &amp;quot;, a, '=&amp;quot;', attr[a], '&amp;quot;');&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var end = func(name){&lt;br /&gt;
    print(&amp;quot;Ending tag: '&amp;quot;, name, &amp;quot;'&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var data = func(data){&lt;br /&gt;
    print(&amp;quot;Data = '&amp;quot;, data, &amp;quot;'&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var pro_instr = func(target, data){&lt;br /&gt;
    print(&amp;quot;Processing instruction: target = '&amp;quot;, target, &amp;quot;', data = '&amp;quot;, data, &amp;quot;'&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
parsexml(getprop(&amp;quot;/sim/fg-home&amp;quot;) ~ '/Export/demo.xml', start, end, data, pro_instr);&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===print()===&lt;br /&gt;
{{Note|As of 07/2020, we are in the process of slowly deprecating and removing Nasal print() (in fgdata) where possible in favor of log [[#logprint()]] which takes a priority level.&lt;br /&gt;
&lt;br /&gt;
This is part of the goal that we achieve zero output at log-level at alert, and everything shown at ‘warn; is really a warning, not just information.&amp;lt;ref&amp;gt;https://sourceforge.net/p/flightgear/mailman/message/37042224/&amp;lt;/ref&amp;gt;}}&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = &amp;lt;nowiki&amp;gt;print(data[, data[, ...]]);&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=415|t=Source}}&lt;br /&gt;
|text = Concatenates its arguments and then prints it to the terminal and the [[Commonly used debugging tools#fgfs.log|log]]. Note that a newline is automatically added.&lt;br /&gt;
|param1 = data&lt;br /&gt;
|param1text = Data to print. Only strings and numbers can be printed; other data types will not be. There many be any number of arguments; they will just be concatenated together.&lt;br /&gt;
|example1 = print(&amp;quot;Just&amp;quot;, &amp;quot; a &amp;quot;, &amp;quot;test&amp;quot;); # prints &amp;quot;Just a test&amp;quot;&lt;br /&gt;
|example2 = print(&amp;quot;pi = &amp;quot;, math.pi); # prints &amp;quot;pi = 3.141592...&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== printf() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = &amp;lt;nowiki&amp;gt;printf(format[, arg[, arg, [...]]]);&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|source = {{fgdata file|Nasal/globals.nas|t=Source}}&lt;br /&gt;
|text = Creates and prints a formatted string. For a description of its arguments, see {{func link|sprintf()}} (it is, in fact, implemented using &amp;lt;code&amp;gt;sprintf()&amp;lt;/code&amp;gt;).&lt;br /&gt;
|example1 = printf(&amp;quot;In hexadecimal, 100000 = %X&amp;quot;, 186A0); # prints &amp;quot;In hexadecimal, 100000 = 186A0&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===printlog()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = &amp;lt;nowiki&amp;gt;printlog(level, data[, data[, ...]]);&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|source = {{fgdata file|Nasal/globals.nas|t=Source}}&lt;br /&gt;
|text = Prints the given message with the given log level. If the log level is higher or equal to &amp;lt;code&amp;gt;/sim/logging/priority&amp;lt;/code&amp;gt;, it is printed.&lt;br /&gt;
|param1 = level&lt;br /&gt;
|param1text = Mandatory log level as a string. Must be one of &amp;quot;none,&amp;quot; &amp;quot;bulk,&amp;quot; &amp;quot;debug,&amp;quot; &amp;quot;info,&amp;quot; &amp;quot;warn,&amp;quot; or &amp;quot;alert.&amp;quot; Note that &amp;quot;none&amp;quot; will mean that the message will never be printed.&lt;br /&gt;
|param2 = data&lt;br /&gt;
|param2text = Data to be printed. Only strings and numbers will be printed; others will not be. There may be any number of arguments; they will just be concatenated together.&lt;br /&gt;
|example1 = printlog(&amp;quot;alert&amp;quot;, &amp;quot;This is an alert&amp;quot;); # message will be printed&lt;br /&gt;
|example2 = printlog(&amp;quot;info&amp;quot;, &amp;quot;Just informing you about something&amp;quot;); # message will be printed only if log level is set to &amp;quot;info&amp;quot; or less&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===rand()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = rand();&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=554|t=Source}}&lt;br /&gt;
|text = Returns a random floating point number between 0 (inclusive) and 1 (exclusive). It takes no arguments.&lt;br /&gt;
|example1 = print(rand()); # prints random number&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===registerFlightPlanDelegate()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = registerFlightPlanDelegate(init_func);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1879|t=Source}}&lt;br /&gt;
|text = Registers a flight plan delegate. See &amp;lt;tt&amp;gt;''{{fgdata file|Nasal/route_manager.nas|t=$FG_ROOT/Nasal/route_manager.nas}}''&amp;lt;/tt&amp;gt; for examples.&lt;br /&gt;
|param1 = init_func&lt;br /&gt;
|param1text = Initialization function which will be called during FlightGear's startup.&lt;br /&gt;
}}&lt;br /&gt;
===removecommand()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = removecommand(cmd);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=668|t=Source}}&lt;br /&gt;
|text = Removes the given fgcommand. Returns &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt;.&lt;br /&gt;
{{caution|This will remove '''any''' [[fgcommands|fgcommand]], even those implemented in C++, so use with caution!}}&lt;br /&gt;
|param1 = cmd&lt;br /&gt;
|param1text = String specifying the name of the command to remove.&lt;br /&gt;
|example1 = addcommand(&amp;quot;hello&amp;quot;, func(){&lt;br /&gt;
    print(&amp;quot;Hello&amp;quot;);&lt;br /&gt;
});&lt;br /&gt;
fgcommand(&amp;quot;hello&amp;quot;); # &amp;quot;Hello&amp;quot; will be printed&lt;br /&gt;
removecommand(&amp;quot;hello&amp;quot;); # removes it&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===removelistener()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = removelistener(id);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=1384|t=Part 1}} {{!}} {{flightgear file|src/Scripting/NasalSys.cxx|l=506|t=Part 2}}&lt;br /&gt;
|text = Removes and deactivates the given listener and returns the number of listeners left or &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; on error.&lt;br /&gt;
{{note|It is good practice to remove listeners when they are not required anymore. This prevents the listeners reducing FlightGear's run performance.}}&lt;br /&gt;
|param1 = id&lt;br /&gt;
|param1text = ID of listener as returned by {{func link|setlistener()}}.&lt;br /&gt;
|example1 = var ls = setlistener(&amp;quot;/sim/test&amp;quot;, func(){&lt;br /&gt;
    print(&amp;quot;Property '/sim/test' has been changed&amp;quot;);&lt;br /&gt;
});&lt;br /&gt;
setprop(&amp;quot;/sim/test&amp;quot;, &amp;quot;blah&amp;quot;); # trigger listener&lt;br /&gt;
var rem = removelistener(ls); # remove listener&lt;br /&gt;
print(&amp;quot;There are &amp;quot;, rem, &amp;quot; listeners remaining&amp;quot;);&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===resolvepath()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = resolvepath(path);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=604|t=Source}}&lt;br /&gt;
|text = Takes a relative path as a string and uses [[SimGear]]'s path-resolving framework to return an absolute path as a string. If the path could not be resolved, an empty string is returned. See [[Resolving Paths]] for a detailed description of the algorithm. This function can also be used to check if a file exists.&lt;br /&gt;
|param1 = path&lt;br /&gt;
|param1text = Relative path to be completed.&lt;br /&gt;
|example1 = print(resolvepath(&amp;quot;Nasal/globals.nas&amp;quot;)); # prints the equivalent of $FG_ROOT/Nasal/globals.nas&lt;br /&gt;
|example2 = print(resolvepath(&amp;quot;blah&amp;quot;)); # prints nothing; could not be resolved&lt;br /&gt;
|example3 = var file_path = resolvepath(&amp;quot;Aircraft/SenecaII/some-file&amp;quot;);&lt;br /&gt;
if (file_path != &amp;quot;&amp;quot;){&lt;br /&gt;
    gui.popupTip(&amp;quot;some-file found&amp;quot;, 2);&lt;br /&gt;
} else {&lt;br /&gt;
    gui.popupTip(&amp;quot;some-file not found&amp;quot;, 2);&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===setlistener()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = &amp;lt;nowiki&amp;gt;setlistener(node, code[, init[, type]]);&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|private = _setlistener()&lt;br /&gt;
|source = {{flightgear file|src/Scripting/Nasal|l=499|t=Part 1}} {{!}} {{flightgear file|src/Scripting/Nasal|l=1350|t=Part 2}}&amp;lt;br&amp;gt;''Listener implemented using the {{API Link|flightgear|class|FGNasalListener}} class.''&lt;br /&gt;
|text = Creates a listener which will be triggered when the given property is changed (depending on the '''type'''). A unique integer ID is returned; this can later be used as the argument to {{func link|removelistener()}}.&lt;br /&gt;
{{note|Listeners are known to be a source of resource leaks. To avoid this, please take measures such as:&lt;br /&gt;
* Using {{func link|removelistener()}} when they are not needed any more.&lt;br /&gt;
* Using a single initialization listener.&lt;br /&gt;
* Avoiding tying listeners to properties that are rapidly updated (e.g., many times per frame).&lt;br /&gt;
}}&lt;br /&gt;
|param1 = node&lt;br /&gt;
|param1text = Mandatory string or &amp;lt;code&amp;gt;props.Node&amp;lt;/code&amp;gt; object pointing to a property in the [[Property Tree]].&lt;br /&gt;
|param2 = code&lt;br /&gt;
|param2text = Mandatory callback function to execute when the listener is triggered. The function can take up to four arguments in the following order:&lt;br /&gt;
* '''changed''': a &amp;lt;code&amp;gt;props.Node&amp;lt;/code&amp;gt; object pointing to the changed node.&lt;br /&gt;
* '''listen''': a &amp;lt;code&amp;gt;props.Node&amp;lt;/code&amp;gt; object pointing to the listened-to node. Note that this argument maybe different depending on the '''type'''.&lt;br /&gt;
* '''mode''': an integer telling how the listener was triggered. 0 means that the value was changed. 1 means that a child property was added. -1 means that a child property was removed.&lt;br /&gt;
* '''is_child''': boolean telling whether '''changed''' is a child property of the listened-to '''node''' or not. 1 (true) if it is, 0 (false) otherwise.&lt;br /&gt;
|param3 = init&lt;br /&gt;
|param3text = If set to 1 (true), the listener will additionally be triggered when it is created. This argument is optional and defaults to 0 (false).&lt;br /&gt;
|param4 = type&lt;br /&gt;
|param4text = Integer specifying the listener's behavior. 0 means that the listener will only trigger when the property is changed. 1 means that the trigger will always be triggered when the property is written to. 2 will mean that the listener will be triggered even if child properties are modified. This argument is optional and defaults to 1.&lt;br /&gt;
|example1 = var prop = props.globals.initNode(&amp;quot;/sim/test&amp;quot;, &amp;quot;&amp;quot;, &amp;quot;STRING&amp;quot;); # create property&lt;br /&gt;
var id = setlistener(&amp;quot;/sim/test&amp;quot;, func(n){ # create listener&lt;br /&gt;
    print(&amp;quot;Value: &amp;quot;, n.getValue());&lt;br /&gt;
});&lt;br /&gt;
setprop(&amp;quot;/sim/test&amp;quot;, &amp;quot;blah&amp;quot;); # trigger listener&lt;br /&gt;
removelistener(id); # remove listener&lt;br /&gt;
prop.remove(); # remove property&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===setprop()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = &amp;lt;nowiki&amp;gt;setprop(path[, path[, ...]], value);&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=385|t=Source}}&lt;br /&gt;
|text = Sets the value of a property in the [[Property Tree]]. If the property does not exist, it will be created. Returns 1 (true) on success or 0 (false) if there was an error.&lt;br /&gt;
{{note|If you want to remove a property, you will have to use one of the &amp;lt;code&amp;gt;props&amp;lt;/code&amp;gt; helpers:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
props.globals.getNode(&amp;quot;foo/bar&amp;quot;).remove(); # take out the complete node&lt;br /&gt;
props.globals.getNode(&amp;quot;foo&amp;quot;).removeChild(&amp;quot;bar&amp;quot;); # take out a certain child node&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
|param1 = path&lt;br /&gt;
|param1text = There needs to be at least one '''path''' argument, but there is no limit to the maximum amount that can be given. They will be concatenated together to form a property tree path. The arguments must be strings, but in FlightGear v3.2 onwards, there also is support (added by {{flightgear commit|34ed79}}) for numeric arguments as indices. See example 2 below.&lt;br /&gt;
|param2 = value&lt;br /&gt;
|param2text = Value to write to the given property. Must be either a string or a number.&lt;br /&gt;
|example1 = setprop(&amp;quot;/sim/demo&amp;quot;, &amp;quot;This is a demo&amp;quot;);&lt;br /&gt;
|example2text = Note that the example below will only work in FlightGear 3.2 and above.&lt;br /&gt;
|example2 = for(var i = 0; i &amp;lt; 3; i += 1){&lt;br /&gt;
    setprop(&amp;quot;/sim/demo&amp;quot;, i, &amp;quot;Demo #&amp;quot; ~ i));&lt;br /&gt;
}&lt;br /&gt;
|example3text = Same as above, but is supported by all versions of FlightGear.&lt;br /&gt;
|example3 = for(var i = 0; i &amp;lt; 3; i += 1){&lt;br /&gt;
    setprop(&amp;quot;/sim/demo[&amp;quot; ~ i ~ &amp;quot;]&amp;quot;, &amp;quot;Demo #&amp;quot; ~ i));&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
====See also====&lt;br /&gt;
{{note| If you have to read/write the same property multiple times (e.g. in an update loop), it is more efficient to use a node object: &lt;br /&gt;
To get a Node rather than its value, use &amp;lt;code&amp;gt;props.globals.getNode()&amp;lt;/code&amp;gt; - see [[Nasal_library/props]]. }}&lt;br /&gt;
&lt;br /&gt;
===settimer() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = settimer(function, delta[, realtime]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=499|t=Part 1}} {{!}} {{flightgear file|src/Scripting/NasalSys.cxx|l=1286|t=Part 2}}&lt;br /&gt;
|text = Runs the given function a specified amount of seconds after the current time. Returns &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt;.&lt;br /&gt;
{{note|Improper use of &amp;lt;code&amp;gt;settimer()&amp;lt;/code&amp;gt; may cause resource leaks. It is also highly recommended that the newer {{func link|maketimer()}} should be used instead of this function.}} &lt;br /&gt;
|param1 = function&lt;br /&gt;
|param1text = Mandatory function that will be called. It may be necessary to enclose code in an anonymous function (see example).&lt;br /&gt;
|param2 = delta&lt;br /&gt;
|param2text = Mandatory amount of time in seconds after which the function will be called.&lt;br /&gt;
|param3 = realtime&lt;br /&gt;
|param3text = If 1 (true), &amp;quot;real time&amp;quot; will be used instead of &amp;quot;simulation time.&amp;quot; Defaults to 0 (false). Note that if &amp;quot;simulation time&amp;quot; is used, the timer will not run while FlightGear is paused.&lt;br /&gt;
|example1 = var myFunc = func(){&lt;br /&gt;
    print(&amp;quot;Hello&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
settimer(myFunc, 2); # runs myFunc after 2 seconds&lt;br /&gt;
|example2 = var sqr = func(a){&lt;br /&gt;
    return a * a;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
settimer(func(){&lt;br /&gt;
    print(sqr(2)); # will print 4 after 2 seconds&lt;br /&gt;
}, 2);&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===srand() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = srand();&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=559|t=Source}}&lt;br /&gt;
|text = Makes the pseudorandom number generator (see {{func link|rand()}}) generate a new {{wikipedia|random seed|noicon=1}} based on time. Returns 0.&lt;br /&gt;
}}&lt;br /&gt;
===systime()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = systime();&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=770|t=Source}}&lt;br /&gt;
|text = Returns the {{wikipedia|Unix time}} (seconds since since 00:00:00 UTC, 1/1/1970) as a floating point number with high resolution. This function is useful for benchmarking purposes (see example 2).&lt;br /&gt;
{{note|1=High resolution timers under Windows can produce inaccurate or fixed sub-millisecond results.&amp;lt;ref&amp;gt;{{cite web|url=http://forum.flightgear.org/viewtopic.php?f=30&amp;amp;t=29259|title=Nasal: systime() ??!?|author=Necolatis|date=Apr 2nd, 2016}}&amp;lt;/ref&amp;gt; This is due to the underlying {{func link|GetSystemTimeAsFileTime()|link=https://msdn.microsoft.com/en-us/library/windows/desktop/ms724397(v=vs.85).aspx}} API call, which depends on hardware availability of suitable high resolution timers. See also [https://msdn.microsoft.com/en-us/library/windows/desktop/dn553408(v=vs.85).aspx Acquiring high-resolution time stamps]}}&lt;br /&gt;
|example1 = print(&amp;quot;Unix time: &amp;quot;, systime()); # prints Unix time&lt;br /&gt;
|example2 = var myFunc = func(){&lt;br /&gt;
    for(var i = 0; i &amp;lt;= 10; i += 1){&lt;br /&gt;
        print(&amp;quot;Interation #&amp;quot;, i);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
var t = systime(); # record time&lt;br /&gt;
myFunc(); # run function&lt;br /&gt;
var t2 = systime(); # record new time&lt;br /&gt;
print(&amp;quot;myFunc() took &amp;quot;, t2 - t, &amp;quot; seconds&amp;quot;); # print result&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===thisfunc()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = thisfunc();&lt;br /&gt;
|source = {{fgdata file|Nasal/globals.nas|t=Source}}&lt;br /&gt;
|text = Returns the function from which this function is called. This allows a function to reliably and safely call itself from within a closure.&lt;br /&gt;
|example1 = var stringify_vec = func(input){&lt;br /&gt;
    if (typeof(input) == &amp;quot;scalar&amp;quot;){&lt;br /&gt;
        return sprintf(&amp;quot;%s&amp;quot;, input);&lt;br /&gt;
    } elsif (typeof(input) == &amp;quot;vector&amp;quot;) {&lt;br /&gt;
        if (size(input) == 0) return &amp;quot;[]&amp;quot;;&lt;br /&gt;
        var this = thisfunc();&lt;br /&gt;
        var buffer = &amp;quot;[&amp;quot;;&lt;br /&gt;
        for(var i = 0; i &amp;lt; size(input); i += 1){&lt;br /&gt;
            buffer ~= this(input[i]);&lt;br /&gt;
            if (i == size(input) - 1) {&lt;br /&gt;
                buffer ~= &amp;quot;]&amp;quot;;&lt;br /&gt;
            } else {&lt;br /&gt;
                buffer ~= &amp;quot;, &amp;quot;;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        return buffer;&lt;br /&gt;
    } else {&lt;br /&gt;
        die(&amp;quot;stringify_vec(): Error! Invalid input. Must be a vector or scalar&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var test_vec = [&amp;quot;a&amp;quot;, &amp;quot;b&amp;quot;, &amp;quot;c&amp;quot;, 1, 2, 3];&lt;br /&gt;
debug.dump(stringify_vec(test_vec)); # prints &amp;quot;[a, b, c, 1, 2, 3]&amp;quot;&lt;br /&gt;
test_vec = [];&lt;br /&gt;
debug.dump(stringify_vec(test_vec)); # prints &amp;quot;[]&amp;quot;&lt;br /&gt;
test_vec = {};&lt;br /&gt;
debug.dump(stringify_vec(test_vec)); # will throw an error&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===tileIndex() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = tileIndex();&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1720|t=Source}}&lt;br /&gt;
|text = Returns the index of the tile at the aircraft's current position as a string. This corresponds to the name of the STG file of the tile. For example, at [[KSFO]], this would be &amp;lt;code&amp;gt;942050&amp;lt;/code&amp;gt;, corresponding to &amp;lt;tt&amp;gt;''[[$FG_SCENERY]]/Terrain/w130n30/w123n3/942050.stg''&amp;lt;/tt&amp;gt;.&lt;br /&gt;
|example1 = print(tileIndex()); # print index&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== tilePath()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = tilePath();&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1712|t=Source}}&lt;br /&gt;
|text = Returns the base path of the tile at the aircraft's current position as a string. For example, at KSFO, this would be &amp;lt;code&amp;gt;w130n30/w123n3&amp;lt;/code&amp;gt;, corresponding to &amp;lt;tt&amp;gt;''[[$FG_SCENERY]]/Terrain/w130n30/w123n3''&amp;lt;/tt&amp;gt;.&lt;br /&gt;
|example1 = print(tilePath()); # print path&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== values()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = values(hash);&lt;br /&gt;
|source = {{fgdata file|Nasal/globals.nas|t=Source}}&lt;br /&gt;
|text = Returns a vector containing the values of the given hash.&lt;br /&gt;
|param1 = hash&lt;br /&gt;
|param1text = Mandatory hash to get the values of.&lt;br /&gt;
|example1 = var hash = {&lt;br /&gt;
    &amp;quot;a&amp;quot;: 1,&lt;br /&gt;
    &amp;quot;b&amp;quot;: 2,&lt;br /&gt;
    &amp;quot;c&amp;quot;: 3&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
foreach(var val; values(hash)){&lt;br /&gt;
    print(val);&lt;br /&gt;
}&lt;br /&gt;
|example2text = The below example does exactly the same thing as the above example, but does not use &amp;lt;code&amp;gt;values()&amp;lt;/code&amp;gt;:&lt;br /&gt;
|example2 = var hash = {&lt;br /&gt;
    &amp;quot;a&amp;quot;: 1,&lt;br /&gt;
    &amp;quot;b&amp;quot;: 2,&lt;br /&gt;
    &amp;quot;c&amp;quot;: 3&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
foreach(var key; keys(hash)){&lt;br /&gt;
    print(hash[key]);&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
==Extension functions new in FG 2020.1 ==&lt;br /&gt;
The following functions have been added to the nasal core library and will be released with FlightGear version 2020.1. &lt;br /&gt;
Before the release they are available in the development branch &amp;quot;next&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
===isfunc()===&lt;br /&gt;
Returns 1 if type or argument is a function, otherwise 0.&lt;br /&gt;
&lt;br /&gt;
===isghost()===&lt;br /&gt;
Returns 1 if type or argument is a ghost, otherwise 0.&lt;br /&gt;
&lt;br /&gt;
=== ishash()===&lt;br /&gt;
Returns 1 if type or argument is a hash, otherwise 0.&lt;br /&gt;
&lt;br /&gt;
===isint()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = isint(x);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|t=Source}}&lt;br /&gt;
|text = Returns 1 if argument has a numeric value and x == floor(x), e.g. integer.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===isnum()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = isnum(x);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|t=Source}}&lt;br /&gt;
|text = Returns 1 if typeof(x) is &amp;quot;scalar&amp;quot; and x has a numeric value otherwise 0. &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===isscalar()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = isscalar(x);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|t=Source}}&lt;br /&gt;
|text = Returns 1 if type or argument is a scalar (string or numeric), otherwise (vector, hash, func, ...) it returns 0. This is useful to check if a variable can be converted to string e.g. when useing the string concat operator &amp;quot;~&amp;quot;. &lt;br /&gt;
|example1 = var a = &amp;quot;foo&amp;quot;; &lt;br /&gt;
var b=42;&lt;br /&gt;
if (isscalar(a) and isscalar(b)) print(a~b);&lt;br /&gt;
if (isstr(a)) print(&amp;quot;a is a string&amp;quot;);&lt;br /&gt;
if (isint(b)) print(&amp;quot;b is an integer&amp;quot;);&lt;br /&gt;
# if (isscalar(a))... is equivalent to if (typeof(a) == &amp;quot;scalar&amp;quot;)...&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===isstr()===&lt;br /&gt;
Returns 1 if type or argument is a string, otherwise 0. &lt;br /&gt;
&lt;br /&gt;
===isvec()===&lt;br /&gt;
Returns 1 if type or argument is a vector, otherwise 0.&lt;br /&gt;
&lt;br /&gt;
===vecindex()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = vecindex(vector, value);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|t=Source}}&lt;br /&gt;
|text = Returns the index of value or nil, if value is not found in vector.&lt;br /&gt;
|example1=&lt;br /&gt;
var myvector = [&amp;quot;apple&amp;quot;, &amp;quot;bananna&amp;quot;, &amp;quot;coconut&amp;quot;];&lt;br /&gt;
# to check if a vector contains a certain value compare vecindex to nil&lt;br /&gt;
if (vecindex(myvector, &amp;quot;apple&amp;quot;) != nil) {&lt;br /&gt;
    print(&amp;quot;found apple&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
# WARNING: this will not work as desired because index is 0&lt;br /&gt;
if (vecindex(myvector, &amp;quot;apple&amp;quot;)) {&lt;br /&gt;
    print(&amp;quot;found apple&amp;quot;);&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
==Variables==&lt;br /&gt;
Various global constants (technically variables) are provided for use in converting between different units. They are all found in {{fgdata file|Nasal/globals.nas|text=$FG_ROOT/Nasal/globals.nas}}.&lt;br /&gt;
&lt;br /&gt;
===D2R===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var radians = degrees * D2R;&lt;br /&gt;
|text = Converts an angle from degrees to radians when multiplied by the angle in degrees. Equal to &amp;lt;code&amp;gt;π / 180&amp;lt;/code&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
===FPS2KT===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var knots = feet_per_second * FPS2KT;&lt;br /&gt;
|text = Converts a velocity from feet per second to knots when multiplied by the velocity in feet per second. Approximately equal to 0.5925.&lt;br /&gt;
}}&lt;br /&gt;
===FT2M===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var metres = feet * FT2M;&lt;br /&gt;
|text = Converts a length from feet to metres when multiplied by the length in feet. Equal to 0.3048.&lt;br /&gt;
}}&lt;br /&gt;
===GAL2L ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var litres = gallons * GAL2L;&lt;br /&gt;
|text = Converts a volume from US liquid gallons to litres when multiplied by the volume in gallons. Approximately equal to 3.7854.&lt;br /&gt;
}}&lt;br /&gt;
===IN2M===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var metres = inches * IN2M;&lt;br /&gt;
|text = Converts a length from inches to metres when multiplied by the length in inches. Equal to 0.0254.&lt;br /&gt;
}}&lt;br /&gt;
===KG2LB===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var pounds = kilograms * KG2LB;&lt;br /&gt;
|text = Converts a mass from kilograms to pounds when multiplied by the mass in kilograms. Approximately equal to 2.2046.&lt;br /&gt;
}}&lt;br /&gt;
===KT2FPS===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var feet_per_second = knots * KT2FPS;&lt;br /&gt;
|text = Converts a velocity from knots to feet per second when multiplied by the velocity in knots. Approximately equal to 1.6878.&lt;br /&gt;
}}&lt;br /&gt;
===KT2MPS===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var metres_per_second = knots * KT2MPS;&lt;br /&gt;
|text = Converts a velocity from knots to metres per second when multiplied by the velocity in knots. Approximately equal to 0.5144.&lt;br /&gt;
}}&lt;br /&gt;
===L2GAL===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var gallons = litres * L2GAL;&lt;br /&gt;
|text = Converts a volume from litres to US liquid gallons when multiplied by the volume in litres. Approximately equal to 0.2642.&lt;br /&gt;
}}&lt;br /&gt;
===LB2KG===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var kilograms = pounds * LB2KG;&lt;br /&gt;
|text = Converts a mass from pounds to kilograms when multiplied by the mass in pounds. Approximately equal to 0.4536.&lt;br /&gt;
}}&lt;br /&gt;
===M2FT ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var feet = metres * M2FT;&lt;br /&gt;
|text = Converts a length from metres to feet when multiplied by the length in metres. Approximately equal to 3.2808.&lt;br /&gt;
}}&lt;br /&gt;
===M2IN===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var inches = metres * M2IN;&lt;br /&gt;
|text = Converts a length from metres to inches when multiplied by the length in metres. Approximately equal to 39.3701.&lt;br /&gt;
}}&lt;br /&gt;
===M2NM ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var nautical_miles = metres * M2NM;&lt;br /&gt;
|text = Converts a length from metres to nautical miles when multiplied by the length in metres. Approximately equal to 0.00054.&lt;br /&gt;
}}&lt;br /&gt;
===MPS2KT ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var knots = metres_per_second * MPS2KT;&lt;br /&gt;
|text = Converts a velocity from metres per second to knots when multiplied by the velocity in metres per second. Approximately equal to 1.9438.&lt;br /&gt;
}}&lt;br /&gt;
===NM2M ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var metres = nautical_miles * NM2M;&lt;br /&gt;
|text = Converts a length from nautical miles to metres when multiplied by the length in nautical miles. Equal to 1,852.&lt;br /&gt;
}}&lt;br /&gt;
===R2D===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var degrees = radians * R2D;&lt;br /&gt;
|text = Converts an angle from radians to degrees when multiplied by the angle in radians. Equal to &amp;lt;code&amp;gt;180 / π&amp;lt;/code&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Appendix}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Nasal namespaces}}&lt;/div&gt;</summary>
		<author><name>Clm76</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Nasal_library&amp;diff=138673</id>
		<title>Nasal library</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Nasal_library&amp;diff=138673"/>
		<updated>2023-11-12T10:29:01Z</updated>

		<summary type="html">&lt;p&gt;Clm76: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Nasal Navigation|nocat=1}}&lt;br /&gt;
This page documents the global '''library functions and variables''' of FlightGear's built-in scripting language, [[Nasal]]. This includes ''[[#Core library functions|core library functions]]'', which were included in Nasal before its integration into FlightGear, the ''[[#Extension functions|extension functions]]'', which have been subsequently added, and are specifically designed for FlightGear, and the ''[[#variables|global variables]]'', which are conversion variables, added with extension functions, for converting between units. The relevant folders in [[Git]] are:&lt;br /&gt;
* {{flightgear file|src/Scripting}}&lt;br /&gt;
* {{simgear file|simgear/nasal}}&lt;br /&gt;
&lt;br /&gt;
All these functions and variables are in the global namespace, that is, they are directly accessible (e.g., one can call &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; inline&amp;gt;magvar()&amp;lt;/syntaxhighlight&amp;gt; instead of &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; inline&amp;gt;namespace.magvar()&amp;lt;/syntaxhighlight&amp;gt;). However, if a namespace must be used, &amp;lt;code&amp;gt;globals&amp;lt;/code&amp;gt; is the correct namespace, but using it is not recommended. For a more complete explanation, see [[Nasal Namespaces in-depth]].&lt;br /&gt;
&lt;br /&gt;
{{tip|Copy &amp;amp; paste the examples into your [[Nasal Console]] and execute them to see what they do.|width=70%}}&lt;br /&gt;
&lt;br /&gt;
== Core library functions ==&lt;br /&gt;
This is the list of the basic '''core library functions.''' Most of these functions were part of the original Nasal library (before its integration in to FlightGear), while some have been added or changed over time.  See also:&lt;br /&gt;
* http://plausible.org/nasal/lib.html ([http://web.archive.org/web/20101010094553/http://plausible.org/nasal/lib.html archive])&lt;br /&gt;
* {{simgear file|simgear/nasal/lib.c}} ([http://sourceforge.net/p/flightgear/simgear/ci/next/log/?path=/simgear/nasal/lib.c history])&lt;br /&gt;
&lt;br /&gt;
=== append() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = append(vector, element[, element[, ...]]);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=42|t=Source}}&lt;br /&gt;
|text = This function appends, or adds, the given element(s) to the end of the vector given in the first argument.  Returns the vector operated on.&lt;br /&gt;
|param1 = vector&lt;br /&gt;
|param1text = The vector to which the arguments will be appended.&lt;br /&gt;
|param2 = element&lt;br /&gt;
|param2text = An element to be added to the vector.&lt;br /&gt;
|example1 = &lt;br /&gt;
var vector = [1, 2, 3]; # Initialize the vector&lt;br /&gt;
append(vector, 4); # Append the number 4 to the end of the vector&lt;br /&gt;
debug.dump(vector); # Print the contents of the vector&lt;br /&gt;
|example2 = &lt;br /&gt;
var vector = [1, 2, 3]; # Initialize the vector&lt;br /&gt;
append(vector, 4, 5, 6); # Append the numbers 4, 5, and 6 to the end of the vector&lt;br /&gt;
debug.dump(vector); # Print the contents of the vector&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== bind() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = bind(function, locals[, outer_scope]);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=502|t=Source}}&lt;br /&gt;
|text = This creates a new function object. A function in Nasal is three things: the actual code, a hash/namespace of local variables available to the function namespace, and the closure object of that namespace. These correspond to the three arguments respectively.&lt;br /&gt;
|param1 = function&lt;br /&gt;
|param1text = Function to evaluate.&lt;br /&gt;
|param2 = locals&lt;br /&gt;
|param2text = Hash containing values that will become the namespace (first closure) for the function.&lt;br /&gt;
|param3 = outer_scope&lt;br /&gt;
|param3text = Optional function which is bound to the next closure. This can be bound to yet another, making a linked list.&lt;br /&gt;
|example1 = # This is a namespace/hash with a single member, named &amp;quot;key,&amp;quot; which is initialized to 12 &lt;br /&gt;
var Namespace = {&lt;br /&gt;
    key: 12&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
# This is different namespace/hash containing a function&lt;br /&gt;
# dividing a variable &amp;quot;key&amp;quot; (which is unavailable/nil in this namespace) by 2&lt;br /&gt;
var AnotherNamespace = {&lt;br /&gt;
    ret: func {&lt;br /&gt;
        key /= 2;&lt;br /&gt;
    }&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
# To see that key is not available, try to call AnotherNamespace.ret() first&lt;br /&gt;
call(AnotherNamespace.ret, [], nil, nil, var errors = []);&lt;br /&gt;
if(size(errors)){&lt;br /&gt;
    print(&amp;quot;Key could not be divided/resolved!&amp;quot;);&lt;br /&gt;
    debug.printerror(errors);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# Associate the AnotherNamespace.ret() function with the first namespace&lt;br /&gt;
# so that &amp;quot;key&amp;quot; is now available&lt;br /&gt;
var function = bind(AnotherNamespace.ret, Namespace);&lt;br /&gt;
&lt;br /&gt;
# Invoke the new function&lt;br /&gt;
function();&lt;br /&gt;
&lt;br /&gt;
# Print out the value of Namespace.key&lt;br /&gt;
# It was changed to 12 from 6 by AnotherNamespace.ret()&lt;br /&gt;
print(Namespace.key);&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== call() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = call(func[, args[, me[, locals[, error]]]);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=247|t=Source}}&lt;br /&gt;
|text = Calls the given function with the given arguments and returns the result.  This function is very useful as it allows much more control over function calls and catches any errors or {{func link|die()}} calls that would normally trigger run-time errors cancelling execution of the script otherwise. &lt;br /&gt;
|param1 = func&lt;br /&gt;
|param1text = Function to execute.&lt;br /&gt;
|param2 = args&lt;br /&gt;
|param2text = Vector containing arguments to give to the called function.&lt;br /&gt;
|param3 = me&lt;br /&gt;
|param3text = &amp;lt;code&amp;gt;'''me'''&amp;lt;/code&amp;gt; reference for the function call (i.e., for method calls). If given, this will override any &amp;lt;code&amp;gt;'''me'''&amp;lt;/code&amp;gt; value existing in the namespace (locals argument).&lt;br /&gt;
|param4 = locals&lt;br /&gt;
|param4text = A hash with key/value pairs that will be available to the called function, typically used as the namespace for the function to be called.&lt;br /&gt;
|param5 = error&lt;br /&gt;
|param5text = A vector to append errors to.  If the called function generates an error, the error, place, and line will be written to this.  These errors can be printed using {{func link|printerror()|debug}}.&lt;br /&gt;
|example1 =&lt;br /&gt;
# prints &amp;quot;Called from call()&amp;quot;&lt;br /&gt;
call(func {&lt;br /&gt;
    print(&amp;quot;Called from call()&amp;quot;);&lt;br /&gt;
});&lt;br /&gt;
|example2 =&lt;br /&gt;
# prints &amp;quot;a = 1 : b = 2&lt;br /&gt;
call(func(a, b){&lt;br /&gt;
        print(&amp;quot;a = &amp;quot;, a, &amp;quot; : b = &amp;quot;, b);&lt;br /&gt;
    },&lt;br /&gt;
    [1, 2]&lt;br /&gt;
);&lt;br /&gt;
|example3 =&lt;br /&gt;
var Hash = {&lt;br /&gt;
    new: func {&lt;br /&gt;
        var m = { parents: [Hash] };&lt;br /&gt;
&lt;br /&gt;
        m.el1 = &amp;quot;string1&amp;quot;;&lt;br /&gt;
        m.el2 = &amp;quot;string2&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
        return m;&lt;br /&gt;
    }&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
# prints &amp;quot;me.el1 = string1&amp;quot;, then &amp;quot;me.el2 = string2&amp;quot; on the next line&lt;br /&gt;
call(func(a, b){        &lt;br /&gt;
        print(&amp;quot;me.el&amp;quot;, a, &amp;quot; = &amp;quot;, me[&amp;quot;el&amp;quot; ~ a]);      &lt;br /&gt;
        print(&amp;quot;me.el&amp;quot;, b, &amp;quot; = &amp;quot;, me[&amp;quot;el&amp;quot; ~ b]);&lt;br /&gt;
    },&lt;br /&gt;
    [1, 2],&lt;br /&gt;
    Hash.new()&lt;br /&gt;
);&lt;br /&gt;
|example4 =&lt;br /&gt;
# prints the value of math.pi&lt;br /&gt;
call(func {&lt;br /&gt;
        print(pi);&lt;br /&gt;
    }, nil, nil, &lt;br /&gt;
    math&lt;br /&gt;
);&lt;br /&gt;
|example5 =&lt;br /&gt;
call(func {&lt;br /&gt;
        print(math.ip); # math.ip doesn't exist&lt;br /&gt;
    }, nil, nil, nil,&lt;br /&gt;
    var errs = []&lt;br /&gt;
);&lt;br /&gt;
debug.printerror(errs); # The error is caught and printed using debug.printerror()&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== caller() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = caller([level]);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=404|t=Source}}&lt;br /&gt;
|text = Returns a vector containing a record from the current call stack.  The level numbering starts from the currently executing function (level 0).  Level 1 (the default) is the caller of the current function, and so on.&lt;br /&gt;
&lt;br /&gt;
The result is a four-element vector containing '''[0]''' a hash of local variables, '''[1]''' the function object, '''[2]''' the full source file name (incl. path) and '''[3]''' the line number. &lt;br /&gt;
|param1 = level&lt;br /&gt;
|param1text = Optional integer specifying the stack level to return a result from.  Defaults to 1 (i.e. the caller of the currently executing function).&lt;br /&gt;
|example1 =&lt;br /&gt;
var myFunction = func(a, b){&lt;br /&gt;
    debug.dump(caller(0)[0]); # prints a hash of local variables, including arguments a and b&lt;br /&gt;
    return 2 * 2;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;2 x 2 = &amp;quot;, myFunction(2, 2));&lt;br /&gt;
|example2 =&lt;br /&gt;
var get_arg_value = func(){&lt;br /&gt;
    print(&amp;quot;Argument to myFunc = &amp;quot;, caller(1)[0]['a']); # print the value of myFunc's single argument, using caller()&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
var myFunc = func(a){&lt;br /&gt;
    get_arg_value();&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
myFunc(3);&lt;br /&gt;
|example3text = This is a real example taken from {{fgdata file|Nasal/canvas/MapStructure.nas}}.  Function &amp;lt;code&amp;gt;r()&amp;lt;/code&amp;gt; (above the TODOs) returns a hash with the key/value pairs as per its arguments. For example, something like this is returned: &amp;lt;code&amp;gt;{ name: &amp;quot;&amp;lt;name&amp;gt;&amp;quot;, vis: 1, zindex: nil }&amp;lt;/code&amp;gt;.&lt;br /&gt;
|example3 =&lt;br /&gt;
var MapStructure_selfTest = func() {&lt;br /&gt;
	var temp = {};&lt;br /&gt;
	temp.dlg = canvas.Window.new([600,400],&amp;quot;dialog&amp;quot;);&lt;br /&gt;
	temp.canvas = temp.dlg.createCanvas().setColorBackground(1,1,1,0.5);&lt;br /&gt;
	temp.root = temp.canvas.createGroup();&lt;br /&gt;
	var TestMap = temp.root.createChild(&amp;quot;map&amp;quot;);&lt;br /&gt;
	TestMap.setController(&amp;quot;Aircraft position&amp;quot;);&lt;br /&gt;
	TestMap.setRange(25); # TODO: implement zooming/panning via mouse/wheel here, for lack of buttons :-/&lt;br /&gt;
	TestMap.setTranslation(&lt;br /&gt;
		temp.canvas.get(&amp;quot;view[0]&amp;quot;)/2,&lt;br /&gt;
		temp.canvas.get(&amp;quot;view[1]&amp;quot;)/2&lt;br /&gt;
	);&lt;br /&gt;
	var r = func(name,vis=1,zindex=nil) return caller(0)[0];&lt;br /&gt;
	# TODO: we'll need some z-indexing here, right now it's just random&lt;br /&gt;
	# TODO: use foreach/keys to show all layers in this case by traversing SymbolLayer.registry direclty ?&lt;br /&gt;
	# maybe encode implicit z-indexing for each lcontroller ctor call ? - i.e. preferred above/below order ?&lt;br /&gt;
	foreach(var type; [r('TFC',0),r('APT'),r('DME'),r('VOR'),r('NDB'),r('FIX',0),r('RTE'),r('WPT'),r('FLT'),r('WXR'),r('APS'), ] ) &lt;br /&gt;
		TestMap.addLayer(factory: canvas.SymbolLayer, type_arg: type.name,&lt;br /&gt;
					visible: type.vis, priority: type.zindex,&lt;br /&gt;
		);&lt;br /&gt;
}; # MapStructure_selfTest&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== chr() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = chr(code);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=175|t=Source}}&lt;br /&gt;
|text = Returns a character as per the single argument. Extended ASCII is supported (see http://www.asciitable.com/ for a list of supported characters), although this may vary between different systems.  For a list of the most commonly used characters, see the {{wikipedia|ASCII#ASCII printable code chart|ASCII printable code chart}} ('''Dec''' column). The following table lists supported control characters, along with their equivalent control characters in Nasal strings.  {{Note|In Nasal, only strings enclosed with double-quotes (&amp;lt;code&amp;gt;&amp;quot;string&amp;quot;&amp;lt;/code&amp;gt;) supports control chracters.  Strings in single quotes (&amp;lt;code&amp;gt;'string'&amp;lt;/code&amp;gt;) do not.}}&lt;br /&gt;
{{{!}} class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Code !! Name !! Equivalent to&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} 10 {{!!}} {{Wikipedia|Newline}} {{!!}} &amp;lt;code&amp;gt;\n&amp;lt;/code&amp;gt;&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} 9 {{!!}} {{Wikipedia|Tab key#Tab characters|Horizontal tab}} {{!!}} &amp;lt;code&amp;gt;\t&amp;lt;/code&amp;gt;&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} 13 {{!!}} {{Wikipedia|Carriage return}} {{!!}} &amp;lt;code&amp;gt;\r&amp;lt;/code&amp;gt;&lt;br /&gt;
{{!}}}&lt;br /&gt;
|param1 = code&lt;br /&gt;
|param1text = Integer character code for the desired glyph.&lt;br /&gt;
|example1 = print(&amp;quot;Code 65 = &amp;quot;, chr(65)); # prints &amp;quot;Code 65 = A&amp;quot;&lt;br /&gt;
|example2text = This example displays all of the characters in a list, in the format &amp;lt;code&amp;gt;Code '''n''' = &amp;gt;'''char'''&amp;lt;&amp;lt;/code&amp;gt;, '''n''' being the index, and '''char''' being the character.&lt;br /&gt;
|example2 =&lt;br /&gt;
for(var i = 0; i &amp;lt;= 255; i += 1){&lt;br /&gt;
    print(&amp;quot;Code &amp;quot;, i, &amp;quot; = &amp;gt;&amp;quot;, chr(i), &amp;quot;&amp;lt;&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== closure() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = closure(func[, level]);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=421|t=Source}}&lt;br /&gt;
|text = Returns the hash table containing the lexical namespace of the given function. The level numbering start with level 0 being the namespace of '''func'''. &lt;br /&gt;
|param1 = func&lt;br /&gt;
|param1text = Function to evaluate.&lt;br /&gt;
|param2 = level&lt;br /&gt;
|param2text = Optional integer specifying the scope level.  Defaults to 0 (the namespace of '''func''').&lt;br /&gt;
|example1 =&lt;br /&gt;
var get_math_e = func {&lt;br /&gt;
    return e; # return the value of math.e&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var myFunction = bind(get_math_e, math); # bind get_math_e to the math namespace, so that math.e is immediately available to get_math_e&lt;br /&gt;
debug.dump(closure(myFunction)); # print the namespace of get_math_e&lt;br /&gt;
&lt;br /&gt;
print(myFunction());&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== cmp() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = cmp(a, b);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=112|t=Source}}&lt;br /&gt;
|text = Compares two strings, returning -1 if '''a''' is less than '''b''', 0 if they are identical and 1 if '''a''' is greater than '''b'''. &lt;br /&gt;
|param1 = a&lt;br /&gt;
|param1text = First string argument for comparison.&lt;br /&gt;
|param2 = b&lt;br /&gt;
|param2text = Second string argument for comparison.&lt;br /&gt;
|example1 = print(cmp(&amp;quot;1&amp;quot;, &amp;quot;two&amp;quot;)); # prints -1&lt;br /&gt;
|example2 = print(cmp(&amp;quot;string&amp;quot;, &amp;quot;string&amp;quot;)); # prints 0&lt;br /&gt;
|example3 = print(cmp(&amp;quot;one&amp;quot;, &amp;quot;2&amp;quot;)); # prints 1&lt;br /&gt;
|example4 = print(cmp(&amp;quot;string1&amp;quot;, &amp;quot;string2&amp;quot;)); # prints -1&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== compile() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = compile(code[, filename]);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=220|t=Source}}&lt;br /&gt;
|text = Compiles the specified code string and returns a function object bound to the current lexical context.  If there is an error, the function dies, with the argument to {{func link|die()}} being '''filename'''.&lt;br /&gt;
|param1 = code&lt;br /&gt;
|param1text = String containing Nasal code to be compiled.&lt;br /&gt;
|param2 = filename&lt;br /&gt;
|param2text = Optional string used for error messages/logging. Defaults to &amp;lt;code&amp;gt;&amp;lt;compile&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
|example1 = &lt;br /&gt;
var myCode = 'print(&amp;quot;hello&amp;quot;);';&lt;br /&gt;
var helloFunc = compile(myCode, &amp;quot;myCode&amp;quot;);&lt;br /&gt;
helloFunc();&lt;br /&gt;
|example2text = &amp;lt;code&amp;gt;compile&amp;lt;/code&amp;gt; is very convenient to support Nasal loaded from other files.  For instance, [[PropertyList XML files]] (such as GUI dialogs) may contain embedded Nasal sections that need to be parsed, processed and compiled.  For an example of how to do this, save the below XML code as &amp;lt;tt&amp;gt;''[[$FG_ROOT]]/gui/dialogs/test.xml''&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?xml version=&amp;quot;1.0&amp;quot;?&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;PropertyList&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nasal&amp;gt;&amp;lt;![CDATA[&lt;br /&gt;
print(&amp;quot;You have FlightGear v&amp;quot;, getprop(&amp;quot;/sim/version/flightgear&amp;quot;));&lt;br /&gt;
]]&amp;gt;&amp;lt;/nasal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/PropertyList&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Now, start FlightGear and execute this code in the [[Nasal Console]].&lt;br /&gt;
|example2 =&lt;br /&gt;
# Build the path&lt;br /&gt;
var FGRoot = getprop(&amp;quot;/sim/fg-root&amp;quot;);&lt;br /&gt;
var filename = &amp;quot;/gui/dialogs/test.xml&amp;quot;;&lt;br /&gt;
var path = FGRoot ~ filename;&lt;br /&gt;
&lt;br /&gt;
var blob = io.read_properties(path);&lt;br /&gt;
var script = blob.getValues().nasal; # Get the nasal string&lt;br /&gt;
&lt;br /&gt;
# Compile the script.  We're passing the filename here for better runtime diagnostics &lt;br /&gt;
var code = call(func {&lt;br /&gt;
    compile(script, filename);&lt;br /&gt;
}, nil, nil, var compilation_errors = []);&lt;br /&gt;
&lt;br /&gt;
if(size(compilation_errors)){&lt;br /&gt;
    die(&amp;quot;Error compiling code in: &amp;quot; ~ filename);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# Invoke the compiled script, equivalent to code(); &lt;br /&gt;
# We're using call() here to detect errors:&lt;br /&gt;
call(code, [], nil, nil, var runtime_errors = []);&lt;br /&gt;
&lt;br /&gt;
if(size(runtime_errors)){&lt;br /&gt;
    die(&amp;quot;Error calling code compiled loaded from: &amp;quot; ~ filename);&lt;br /&gt;
}&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== contains() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = contains(hash, key);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=184|t=Source}}&lt;br /&gt;
|text = Returns 1 (True) if the hash contains the specified key, or 0 (False) if not.&lt;br /&gt;
|param1 = hash&lt;br /&gt;
|param1text = The hash to search in.&lt;br /&gt;
|param2 = key&lt;br /&gt;
|param2text = The scalar to be searched for, contained as a key in the hash.&lt;br /&gt;
|example1 =&lt;br /&gt;
# Initialize a hash&lt;br /&gt;
var hash = {&lt;br /&gt;
    element: &amp;quot;value&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
print(contains(hash, &amp;quot;element&amp;quot;) ? &amp;quot;Yes&amp;quot; : &amp;quot;No&amp;quot;); # This will print &amp;quot;Yes&amp;quot;&lt;br /&gt;
|example2 =&lt;br /&gt;
# Initialize a hash&lt;br /&gt;
var hash = {&lt;br /&gt;
    element: &amp;quot;value&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
print(contains(hash, &amp;quot;element2&amp;quot;) ? &amp;quot;Yes&amp;quot; : &amp;quot;No&amp;quot;); # This will print &amp;quot;No&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===contains()===&lt;br /&gt;
{{Requires commit|desc=Vector support|details=see {{Merge-request|project=fgdata|id=305}}|commit=ee39abbd3b70c9b6d5e3a1c4ccedddaac1a92b11|repo=Simgear}}&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = contains(vector, item);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=184|t=Source}}&lt;br /&gt;
|text = Returns 1 (True) if the vector contains the specified item, or 0 (False) if not.&lt;br /&gt;
|param1 = vector&lt;br /&gt;
|param1text = The vector to search in.&lt;br /&gt;
|param2 = item&lt;br /&gt;
|param2text = The object to be searched for in the vector.&lt;br /&gt;
|example1 =&lt;br /&gt;
# Initialize a hash&lt;br /&gt;
var vec = [&amp;quot;element&amp;quot;, &amp;quot;foo&amp;quot;];&lt;br /&gt;
print(contains(hash, &amp;quot;element&amp;quot;) ? &amp;quot;Yes&amp;quot; : &amp;quot;No&amp;quot;); # This will print &amp;quot;Yes&amp;quot;&lt;br /&gt;
|example2 =&lt;br /&gt;
# Initialize a hash&lt;br /&gt;
var vec = [&amp;quot;element&amp;quot;, &amp;quot;foo&amp;quot;];&lt;br /&gt;
print(contains(hash, &amp;quot;element2&amp;quot;) ? &amp;quot;Yes&amp;quot; : &amp;quot;No&amp;quot;); # This will print &amp;quot;No&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===delete() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = delete(hash, key);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=83|t=Source}}&lt;br /&gt;
|text = Deletes the key from the hash if it exists. Operationally, this is NOT identical to setting the hash value specified by the key to &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; as the key will stay in the hash (at least for a while). This variant potentially frees storage by deleting the reference to the key and by shrinking the hash.  Returns the hash that has been operated on.&lt;br /&gt;
|param1 = hash&lt;br /&gt;
|param1text = The hash from which to delete the key.&lt;br /&gt;
|param2 = key&lt;br /&gt;
|param2text = The scalar to be deleted, contained as a key in the hash.&lt;br /&gt;
|example1 =&lt;br /&gt;
# Initialize the hash&lt;br /&gt;
var hash = {&lt;br /&gt;
    element1: &amp;quot;value1&amp;quot;,&lt;br /&gt;
    element2: &amp;quot;value2&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
delete(hash, &amp;quot;element1&amp;quot;); # Delete element1&lt;br /&gt;
debug.dump(hash); # prints the hash, which is now minus element1&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===die()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = die(error);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=288|t=Source}}&lt;br /&gt;
|text = Terminates execution and unwinds the stack.  The place and the line will be added to the '''error'''.  This invokes the same internal exception handler used for internal runtime errors. Use this to signal fatal errors, or to implement exception handling. The error thrown (including internal runtime errors) can be caught with {{func link|call()}}.&lt;br /&gt;
|param1 = error&lt;br /&gt;
|param1text = String describing the error.&lt;br /&gt;
:{{inote|This parameter is technically optional, but it is highly recommended to use it.}}&lt;br /&gt;
|example1 = &lt;br /&gt;
print(&amp;quot;Will print&amp;quot;);&lt;br /&gt;
die(&amp;quot;Don't go any further!&amp;quot;); &lt;br /&gt;
print(&amp;quot;Won't print&amp;quot;); # Will not be printed because die() stops the process&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== find()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = find(needle, haystack);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=450|t=Source}}&lt;br /&gt;
|text = Finds and returns the index of the first occurrence of the string '''needle''' in the string '''haystack''', or -1 if no such occurrence was found.&lt;br /&gt;
|param1 = needle&lt;br /&gt;
|param1text = String to search for.&lt;br /&gt;
|param2 = haystack&lt;br /&gt;
|param2text = String to search in.&lt;br /&gt;
|example1 = print(find(&amp;quot;c&amp;quot;, &amp;quot;abcdef&amp;quot;)); # prints 2&lt;br /&gt;
|example2 = print(find(&amp;quot;x&amp;quot;, &amp;quot;abcdef&amp;quot;)); # prints -1&lt;br /&gt;
|example3 = print(find(&amp;quot;cd&amp;quot;, &amp;quot;abcdef&amp;quot;)); # prints 2&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===ghosttype()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = ghosttype(ghost);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=207|t=Source}}&lt;br /&gt;
|text = Returns a string containing either a descriptive name of a ghost (a raw C/C++ object), or a unique id (the pointer to the C/C++ &amp;lt;code&amp;gt;naGhostType&amp;lt;/code&amp;gt; instance) if no name has been set.  Ghost is an acronym that stands for '''G'''arbage-collected '''H'''andle to '''O'''ut'''S'''ide '''T'''hingy.&lt;br /&gt;
|param1 = ghost&lt;br /&gt;
|param1text = Ghost to return a description for.&lt;br /&gt;
|example1 = print(ghosttype(airportinfo())); # prints &amp;quot;airport&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===id()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = id(object);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=570|t=Source}}&lt;br /&gt;
|text = Returns a string containing information on the type and ID of the object provided in the single argument.  The information is returned in the form of &amp;lt;code&amp;gt;'''&amp;lt;type&amp;gt;''':'''&amp;lt;id&amp;gt;'''&amp;lt;/code&amp;gt;, where '''&amp;lt;type&amp;gt;''' is the type of object, and '''&amp;lt;id&amp;gt;''' is the ID.&lt;br /&gt;
|param1 = object&lt;br /&gt;
|param1text = Can be either of a string, a vector, a hash, a code, a function, or a ghost.&lt;br /&gt;
|example1 = print(id(&amp;quot;A&amp;quot;)); # prints &amp;quot;str:000000001624A590&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===int() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = int(number);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=90|t=Source}}&lt;br /&gt;
|text = Returns the integer part of the numeric value of the single argument, or &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; if none exists.&lt;br /&gt;
|param1 = number&lt;br /&gt;
|param1text = Number or string with just a number in it to return an integer from.&lt;br /&gt;
|example1 = print(int(23)); # prints &amp;quot;23&amp;quot;&lt;br /&gt;
|example2 = print(int(23.123)); # prints &amp;quot;23&amp;quot;&lt;br /&gt;
|example3 = debug.dump(int(&amp;quot;string&amp;quot;)); # prints &amp;quot;nil&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===keys()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = keys(hash);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=33|t=Source}}&lt;br /&gt;
|text = Returns a vector containing the list of keys found in the single hash argument. &lt;br /&gt;
|param1 = hash&lt;br /&gt;
|param1text = The hash to return the keys from.&lt;br /&gt;
|example1 = &lt;br /&gt;
# Initialize a hash&lt;br /&gt;
var hash = {&lt;br /&gt;
    element1: &amp;quot;value&amp;quot;,&lt;br /&gt;
    element2: &amp;quot;value&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
debug.dump(keys(hash)); # print the vector&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===left()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = left(string, length);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=149|t=Source}}&lt;br /&gt;
|version = 2.12&lt;br /&gt;
|commit = {{simgear commit|bd7163|t=commit}}&lt;br /&gt;
|text = Returns a substring of '''string''', starting from the left.&lt;br /&gt;
|param1 = string&lt;br /&gt;
|param1text = String to return part of.&lt;br /&gt;
|param2 = length&lt;br /&gt;
|param2text = Integer specifying the length of the substring to return.&lt;br /&gt;
|example1 = print(left(&amp;quot;string&amp;quot;, 2)); # prints &amp;quot;st&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== num()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = num(number);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=102|t=Source}}&lt;br /&gt;
|text = Returns the numerical value of the single string argument, or &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; if none exists. &lt;br /&gt;
|param1 = number&lt;br /&gt;
|param1text = String with just a number in it to return a number from.&lt;br /&gt;
|example1 = print(num(&amp;quot;23&amp;quot;)); # prints &amp;quot;23&amp;quot;&lt;br /&gt;
|example2 = print(num(&amp;quot;23.123&amp;quot;)); # prints &amp;quot;23.123&amp;quot;&lt;br /&gt;
|example3 = debug.dump(num(&amp;quot;string&amp;quot;)); # prints &amp;quot;nil&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===pop()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = pop(vector);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=50|t=Source}}&lt;br /&gt;
|text = Removes and returns the last element of the single vector argument, or &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; if the vector is empty. &lt;br /&gt;
|param1 = vector&lt;br /&gt;
|param1text = Vector to remove an element from.&lt;br /&gt;
|example1 = &lt;br /&gt;
var vector = [1, 2, 3];&lt;br /&gt;
pop(vector);&lt;br /&gt;
debug.dump(vector); # prints &amp;quot;[1, 2]&amp;quot;&lt;br /&gt;
|example2 = &lt;br /&gt;
var vector = [1, 2, 3];&lt;br /&gt;
debug.dump(pop(vector)); # prints &amp;quot;3&amp;quot;&lt;br /&gt;
|example3 = &lt;br /&gt;
var vector = [];&lt;br /&gt;
debug.dump(pop(vector)); # prints &amp;quot;nil&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===right()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = right(string, length);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=161|t=Source}}&lt;br /&gt;
|version = 2.12&lt;br /&gt;
|commit = {{simgear commit|bd7163|t=commit}}&lt;br /&gt;
|text = Returns a substring of '''string''', starting from the right.&lt;br /&gt;
|param1 = string&lt;br /&gt;
|param1text = String to return part of.&lt;br /&gt;
|param2 = length&lt;br /&gt;
|param2text = Integer specifying the length of the substring to return.&lt;br /&gt;
|example1 = print(right(&amp;quot;string&amp;quot;, 2)); # prints &amp;quot;ng&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== setsize()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = setsize(vector, size);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=56|t=Source}}&lt;br /&gt;
|text = Sets the size of a vector. The first argument specifies a vector, the second a number representing the desired size of that vector. If the vector is currently larger than the specified size, it is truncated. If it is smaller, it is padded with &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; entries. Returns the vector operated upon. &lt;br /&gt;
|param1 = vector&lt;br /&gt;
|param1text = The vector to be operated on.&lt;br /&gt;
|param2 = size&lt;br /&gt;
|param2text = The desired size of the vector in number of entries.&lt;br /&gt;
|example1 = &lt;br /&gt;
var vector = [1, 2, 3]; # Initialize a vector&lt;br /&gt;
setsize(vector, 4);&lt;br /&gt;
debug.dump(vector); # print the vector&lt;br /&gt;
|example2 = &lt;br /&gt;
var vector = [1, 2, 3]; # Initialize a vector&lt;br /&gt;
setsize(vector, 2);&lt;br /&gt;
debug.dump(vector); # print the vector&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===size()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = size(object);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=23|t=Source}}&lt;br /&gt;
|text = Returns the size of the single argument. For strings, this is the length in bytes. For vectors, this is the number of elements. For hashes, it is the number of key/value pairs. If the argument is &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; or a number, this error will be thrown: &amp;lt;code&amp;gt;object has no size()&amp;lt;/code&amp;gt;.&lt;br /&gt;
|param1 = object&lt;br /&gt;
|param1text = Object to find the size of.  Must be a string, a vector or a hash.&lt;br /&gt;
|example1 = &lt;br /&gt;
var string = &amp;quot;string&amp;quot;;&lt;br /&gt;
print(size(string)); # prints &amp;quot;6&amp;quot;&lt;br /&gt;
|example2 =&lt;br /&gt;
var vector = [1, 2, 3];&lt;br /&gt;
print(size(vector)); # prints &amp;quot;3&amp;quot;&lt;br /&gt;
|example3 =&lt;br /&gt;
var hash = {&lt;br /&gt;
    element1: &amp;quot;value1&amp;quot;,&lt;br /&gt;
    element2: &amp;quot;value2&amp;quot;,&lt;br /&gt;
    element3: &amp;quot;value3&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
print(size(hash)); # prints &amp;quot;3&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== sort()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = sort(vector, function);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=542|t=Source}}&lt;br /&gt;
|text = Returns a vector containing the elements in the input '''vector''' sorted in according to the rule given by '''function'''. Implemented with the ANSI C {{func link|qsort()|link=http://www.cplusplus.com/reference/cstdlib/qsort/}}, &amp;lt;code&amp;gt;sort()&amp;lt;/code&amp;gt; is stable.  This means that if the rules in the first example are used, equal elements in the output vector will appear in the same relative order as they do in the input.  It is run in a loop, so '''function''' is run several times.&lt;br /&gt;
|param1 = vector&lt;br /&gt;
|param1text = Input vector to sort.&lt;br /&gt;
|param2 = function&lt;br /&gt;
|param2text = Function according to which the elements will be sorted by.  It should take two arguments and should return one of 1, 0, or -1.&lt;br /&gt;
{{{!}} class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Return value !! Meaning&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} less than 0 {{!!}} first argument should go before second argument&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} 0 {{!!}} first argument equals second argument&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} greater than 0 {{!!}} first argument should go after second argument&lt;br /&gt;
{{!}}}&lt;br /&gt;
&lt;br /&gt;
|example1text = This example sorts elements from smallest to greatest.&lt;br /&gt;
|example1 = &lt;br /&gt;
var sort_rules = func(a, b){&lt;br /&gt;
    if(a &amp;lt; b){&lt;br /&gt;
        return -1; # A should before b in the returned vector&lt;br /&gt;
    }elsif(a == b){&lt;br /&gt;
        return 0; # A is equivalent to b &lt;br /&gt;
    }else{&lt;br /&gt;
        return 1; # A should after b in the returned vector&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
debug.dump(sort([3, 2, 5, 6, 4, 1], sort_rules)); # prints &amp;quot;[1, 2, 3, 4, 5, 6]&amp;quot;&lt;br /&gt;
|example2text = This example sorts elements from greatest to smallest.&lt;br /&gt;
|example2 = &lt;br /&gt;
# Outputs the elements in reverse order (greatest to smallest)&lt;br /&gt;
var sort_rules = func(a, b){&lt;br /&gt;
    if(a &amp;lt; b){&lt;br /&gt;
        return 1; # -1 in the above example&lt;br /&gt;
    }elsif(a == b){&lt;br /&gt;
        return 0;&lt;br /&gt;
    }else{&lt;br /&gt;
        return -1; # 1 in the above example&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
debug.dump(sort([3, 2, 5, 6, 4, 1], sort_rules)); # prints &amp;quot;[6, 5, 4, 3, 2, 1]&amp;quot;&lt;br /&gt;
|example3text = This example sorts a vector of strings (runways for example) from smallest to greatest.&lt;br /&gt;
|example3 = &lt;br /&gt;
var runways = [&amp;quot;09R&amp;quot;,&amp;quot;27R&amp;quot;,&amp;quot;26L&amp;quot;,&amp;quot;09L&amp;quot;,&amp;quot;15&amp;quot;];&lt;br /&gt;
var rwy = sort(runways,func(a,b) cmp(a,b));&lt;br /&gt;
debug.dump(rwy); # prints ['09L','09R','15','26L','27R']&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== split()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = split(delimiter, string);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=460|t=Source}}&lt;br /&gt;
|text = Splits the input string into a vector of substrings bounded by occurrences of the delimiter substring.&lt;br /&gt;
|param1 = delimiter&lt;br /&gt;
|param1text = String that will split the substrings in the returned vector.&lt;br /&gt;
|param2 = string&lt;br /&gt;
|param2text = String to split up.&lt;br /&gt;
|example1 = debug.dump(split(&amp;quot;cd&amp;quot;, &amp;quot;abcdef&amp;quot;)); # prints &amp;quot;['ab', 'ef']&amp;quot;&lt;br /&gt;
|example2 = debug.dump(split(&amp;quot;.&amp;quot;, &amp;quot;3.2.0&amp;quot;)); # prints &amp;quot;[3, 2, 0]&amp;quot;&lt;br /&gt;
|example3 = debug.dump(split(&amp;quot;/&amp;quot;, &amp;quot;path/to/file&amp;quot;)); # prints &amp;quot;['path', 'to', 'file']&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===sprintf()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = &amp;lt;nowiki&amp;gt;sprintf(format[, arg[, arg, [...]]]);&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=355|t=Source}}&lt;br /&gt;
|text = Creates and returns a string formatted using ANSI C {{func link|vsnprintf()|link=http://en.cppreference.com/w/c/io/vfprintf}} &amp;lt;ref&amp;gt;&lt;br /&gt;
{{Cite web&lt;br /&gt;
|url = http://sourceforge.net/p/flightgear/simgear/ci/next/tree/simgear/nasal/lib.c#l308&lt;br /&gt;
|title = fgdata/simgear/simgear/nasal/lib.c, line 308&lt;br /&gt;
|accessdate = October 2015&lt;br /&gt;
}}&lt;br /&gt;
&amp;lt;/ref&amp;gt;.  Below is a table of supported format specifiers.&lt;br /&gt;
{{{!}} class=&amp;quot;wikitable&amp;quot; width=&amp;quot;75%&amp;quot;&lt;br /&gt;
{{!}}+ %[flags][width][.precision]specifier&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; {{!}} Flags&lt;br /&gt;
{{!-}}&lt;br /&gt;
! Flag !! Output&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;+&amp;lt;/code&amp;gt; {{!!}} Forces to precede the result with a plus or minus sign ('''+''' or '''-''') even for positive numbers. By default, only negative numbers are preceded with a '''-''' sign.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} ''space'' {{!!}} Prefixes non-signed numbers with a space.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;-&amp;lt;/code&amp;gt; {{!!}} Left-align the output of this placeholder (the default is to right-align the output) when the width option is specified.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt; {{!!}} Use 0 instead of spaces to pad a field when the width option is specified.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;#&amp;lt;/code&amp;gt; {{!!}} Used with &amp;lt;code&amp;gt;o&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;X&amp;lt;/code&amp;gt; specifiers the value is preceded with &amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;0x&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;0X&amp;lt;/tt&amp;gt; respectively for values different than zero. Used with &amp;lt;code&amp;gt;e&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;E&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;f&amp;lt;/code&amp;gt;, it forces the written output to contain a decimal point even if no digits would follow. By default, if no digits follow, no decimal point is written. Used with &amp;lt;code&amp;gt;g&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;G&amp;lt;/code&amp;gt; the result is the same as with &amp;lt;code&amp;gt;e&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;E&amp;lt;/code&amp;gt; but trailing zeros are not removed.&lt;br /&gt;
{{!-}}&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; {{!}} Width&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} colspan=&amp;quot;2&amp;quot; {{!}} Integer specifying the minimum number of characters to be returned. This includes the decimal point and decimal fraction as well as + or - signs.&lt;br /&gt;
{{!-}}&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; {{!}} Precision&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} colspan=&amp;quot;2&amp;quot; {{!}} Integer preceded by a dot specifying the number of decimal places to be written.&lt;br /&gt;
{{!-}}&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; {{!}} Specifiers&lt;br /&gt;
{{!-}}&lt;br /&gt;
! Specifier !! Output&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;d&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;i&amp;lt;/code&amp;gt; {{!!}} Signed decimal number.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;s&amp;lt;/code&amp;gt; {{!!}} A string&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;%&amp;lt;/code&amp;gt; {{!!}} Percent (%) character.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;c&amp;lt;/code&amp;gt; {{!!}} A single character assigned to a character code, the code given in an integer argument.  See http://www.asciitable.com/ for a list of supported characters and their codes.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;o&amp;lt;/code&amp;gt; {{!!}} Unsigned integer as an octal number.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;u&amp;lt;/code&amp;gt; {{!!}} Unsigned decimal integer.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;X&amp;lt;/code&amp;gt; {{!!}} Unsigned integer as a hexadecimal number.  If &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt; is used, any letters in the number are lowercase, while &amp;lt;code&amp;gt;X&amp;lt;/code&amp;gt; gives uppercase.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;e&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;E&amp;lt;/code&amp;gt; {{!!}} Double value in scientific notation (i.e., ''[-]ddd.ddd'''e'''[+/-]ddd''), with an exponent being denoted by &amp;lt;tt&amp;gt;e&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;E&amp;lt;/tt&amp;gt; depending on whether an upper or lowercase is used respectively.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;f&amp;lt;/code&amp;gt; {{!!}} Floating-point number, in fixed decimal notation, by default with 6 decimal places.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;F&amp;lt;/code&amp;gt; {{!!}} Appears to be available&amp;lt;ref&amp;gt;&lt;br /&gt;
{{Cite web&lt;br /&gt;
|url = http://sourceforge.net/p/flightgear/simgear/ci/next/tree/simgear/nasal/lib.c#l389&lt;br /&gt;
|title = fgdata/simgear/simgear/nasal/lib.c, line 389&lt;br /&gt;
|accessdate = October 2015&lt;br /&gt;
}}&lt;br /&gt;
&amp;lt;/ref&amp;gt;, but doesn't work.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;g&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;G&amp;lt;/code&amp;gt; {{!!}} Double in either normal or exponential notation, whichever is more appropriate for its magnitude. &amp;lt;code&amp;gt;g&amp;lt;/code&amp;gt; uses lower-case letters, &amp;lt;code&amp;gt;G&amp;lt;/code&amp;gt; uses upper-case letters. This type differs slightly from fixed-point notation in that insignificant zeroes to the right of the decimal point are not included.  Also, the decimal point is not included on whole numbers.&lt;br /&gt;
{{!}}}&lt;br /&gt;
&lt;br /&gt;
|param1 = format&lt;br /&gt;
|param1text = String specifying the format.  Can be used with or without a format specifiers.  See below for examples.&lt;br /&gt;
|param2 = arg&lt;br /&gt;
|param2text = Argument specifying a value to replace a format placeholder (such as &amp;lt;code&amp;gt;%d&amp;lt;/code&amp;gt;) in the format string.  Not required if there are no format specifiers.&lt;br /&gt;
&lt;br /&gt;
|example1 = print(sprintf(&amp;quot;%i&amp;quot;, 54)); # prints &amp;quot;54&amp;quot;&lt;br /&gt;
|example2 = print(sprintf(&amp;quot;Pi = %+.10f&amp;quot;, math.pi)); # prints &amp;quot;Pi = +3.1415926536&amp;quot;&lt;br /&gt;
|example3 = &lt;br /&gt;
print(sprintf(&amp;quot;%6d&amp;quot;, 23)); # prints &amp;quot;    23&amp;quot;&lt;br /&gt;
print(sprintf(&amp;quot;%06d&amp;quot;, 23)); # prints &amp;quot;000023&amp;quot;&lt;br /&gt;
|example4 =&lt;br /&gt;
var FGVer = getprop(&amp;quot;/sim/version/flightgear&amp;quot;);&lt;br /&gt;
print(sprintf(&amp;quot;You have FlightGear v%s&amp;quot;, FGVer)); # prints &amp;quot;You have FlightGear v&amp;lt;your version&amp;gt;&amp;quot;&lt;br /&gt;
|example5 = &lt;br /&gt;
print(sprintf(&amp;quot;Hexadecimal 100000 = %X&amp;quot;, 100000)); # prints &amp;quot;Hexadecimal 100000 = 186A0&amp;quot;&lt;br /&gt;
print(sprintf(&amp;quot;Hexadecimal 100000 = %x&amp;quot;, 100000)); # prints &amp;quot;Hexadecimal 100000 = 186a0&amp;quot;&lt;br /&gt;
|example6 = print(sprintf(&amp;quot;Code 65 is %c&amp;quot;, 65)); # prints &amp;quot;Code 65 is A&amp;quot;&lt;br /&gt;
|example7 = &lt;br /&gt;
print(sprintf(&amp;quot;%e&amp;quot;, 54)); # prints &amp;quot;5.400000e+001&amp;quot;&lt;br /&gt;
print(sprintf(&amp;quot;%E&amp;quot;, 54)); # prints &amp;quot;5.400000E+001&amp;quot;&lt;br /&gt;
|example8 = print(sprintf(&amp;quot;%o&amp;quot;, 54)); # prints &amp;quot;66&amp;quot;&lt;br /&gt;
|example9 = print(sprintf(&amp;quot;50%% of 100 is %i&amp;quot;, 100 / 2)); # prints &amp;quot;50% of 100 is 50&amp;quot;&lt;br /&gt;
|example10 =&lt;br /&gt;
print(sprintf(&amp;quot;%.2f&amp;quot;, 1.4));   #prints &amp;quot;1.40&amp;quot;&lt;br /&gt;
print(sprintf(&amp;quot;%.1f&amp;quot;, 1.4));   #prints &amp;quot;1.4&amp;quot;&lt;br /&gt;
print(sprintf(&amp;quot;% 4.1f&amp;quot;, 1.4)); #prints &amp;quot; 1.4&amp;quot;&lt;br /&gt;
print(sprintf(&amp;quot;%04.1f&amp;quot;, 1.4)); #prints &amp;quot;01.4&amp;quot;&lt;br /&gt;
print(sprintf(&amp;quot;% 6.1f&amp;quot;, 1.4)); #prints &amp;quot;   1.4&amp;quot;&lt;br /&gt;
print(sprintf(&amp;quot;%06.1f&amp;quot;, 1.4)); #prints &amp;quot;0001.4&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===streq()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = streq(a, b);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=129|t=Source}}&lt;br /&gt;
|text = Tests the string values of the two arguments for equality. This function is needed because the &amp;lt;code&amp;gt;'''=='''&amp;lt;/code&amp;gt; operator (see [[Nasal_Operators#Equality|Nasal Operators]]) tests for numeric equality first.  If either or both the arguments are not strings, 0 (False) will be returned.  Returns either 0 (False) or 1 (True).  {{Note|This function is rarely required in typical code.}}&lt;br /&gt;
|param1 = a&lt;br /&gt;
|param1text = First argument for testing equality.&lt;br /&gt;
|param2 = b&lt;br /&gt;
|param2text = Second argument for testing equality.&lt;br /&gt;
|example1 = print(streq(&amp;quot;0&amp;quot;, &amp;quot;0&amp;quot;)); # prints &amp;quot;1&amp;quot; (True)&lt;br /&gt;
|example2 = &lt;br /&gt;
print(0 == 0.0); # prints &amp;quot;1&amp;quot; (True)&lt;br /&gt;
print(streq(&amp;quot;0&amp;quot;, &amp;quot;0.0&amp;quot;)); # prints &amp;quot;0&amp;quot; (False)&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===substr()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = substr(string, start [, length]);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=129|t=Source}}&lt;br /&gt;
|text = Similar the {{func link|subvec()}}, but operates on strings. Computes and returns a substring. The first argument specifies a string, the second is the index of the start of a substring, the optional third argument specifies a length (the default is to return the rest of the string from the start).&lt;br /&gt;
|param1 = string&lt;br /&gt;
|param1text = String to return a substring from.&lt;br /&gt;
|param2 = start&lt;br /&gt;
|param2text = Integer specifying the start of a substring. Negative values specify a position from the end of the string.&lt;br /&gt;
|param3 = length&lt;br /&gt;
|param3text = Optional argument specifying the length of the substring. Defaults to the end of the string.&lt;br /&gt;
|example1 = print(substr(&amp;quot;abcde&amp;quot;, 1, 3)); # prints &amp;quot;bcd&amp;quot;&lt;br /&gt;
|example2 = print(substr(&amp;quot;abcde&amp;quot;, 1)); # prints &amp;quot;bcde&amp;quot;&lt;br /&gt;
|example3 = print(substr(&amp;quot;abcde&amp;quot;, 2, 1)); # prints &amp;quot;c&amp;quot;&lt;br /&gt;
|example4 = print(substr(&amp;quot;abcde&amp;quot;, -2)); # prints &amp;quot;de&amp;quot;&lt;br /&gt;
|example5 = print(substr(&amp;quot;abcde&amp;quot;, -3, 2)); # prints &amp;quot;cd&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===subvec()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = subvec(vector, start[, length]);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=63|t=Source}}&lt;br /&gt;
|text = Returns a sub-range of a vector. The first argument specifies a vector, the second a starting index, and the optional third argument indicates a length (the default is to the end of the vector). &lt;br /&gt;
|param1 = vector&lt;br /&gt;
|param1text = The vector to take the sub-vector from.&lt;br /&gt;
|param2 = start&lt;br /&gt;
|param2text = The starting point of the sub-vector within the given vector.&lt;br /&gt;
|param3 = length&lt;br /&gt;
|param3text = Optional argument specifying the length of the sub-vector, from the starting point.&lt;br /&gt;
'''Notes:'''&lt;br /&gt;
* Omitting the ''vector'' and ''start'' arguments is not an error (possibly it should be) but the return value is ''nil''.&lt;br /&gt;
* A negative ''start'' argument ''is'' an error. This seems wrong. Perhaps the language designer could comment.&lt;br /&gt;
* A value of ''start'' greater than ''size(vector)'' causes an error. A value equal to ''size(vector)'' returns an empty vector.&lt;br /&gt;
* If the value of ''length'' is greater than ''size(vector) - start'' then it is ignored. That is, all elements from ''start'' to the end of ''vector'' are returned. If ''length'' is zero then an empty vector is returned. A negative value of ''length'' causes an error.&lt;br /&gt;
|example1 = &lt;br /&gt;
var vector = [1, 2, 3];&lt;br /&gt;
debug.dump(subvec(vector, 0)); # prints &amp;quot;[1, 2, 3]&amp;quot;&lt;br /&gt;
|example2 = &lt;br /&gt;
var vector = [1, 2, 3];&lt;br /&gt;
debug.dump(subvec(vector, 1)); # prints &amp;quot;[2, 3]&amp;quot;&lt;br /&gt;
|example3 = &lt;br /&gt;
var vector = [1, 2, 3];&lt;br /&gt;
debug.dump(subvec(vector, 1, 1)); # prints &amp;quot;[2]&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== typeof()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = typeof(object);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=193|t=Source}}&lt;br /&gt;
|text = Returns a string indicating the whether the object is &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt;, a scalar (number or string), a vector, a hash, a function, or a ghost.&lt;br /&gt;
|param1 = object&lt;br /&gt;
|param1text = Object to return the type of.&lt;br /&gt;
|example1 = &lt;br /&gt;
var object = nil;&lt;br /&gt;
print(typeof(object)); # prints &amp;quot;nil&amp;quot;&lt;br /&gt;
|example2 = &lt;br /&gt;
var object = &amp;quot;Hello world!&amp;quot;;&lt;br /&gt;
print(typeof(object)); # prints &amp;quot;scalar&amp;quot;&lt;br /&gt;
|example3 = &lt;br /&gt;
var object = math.pi;&lt;br /&gt;
print(typeof(object)); # prints &amp;quot;scalar&amp;quot;&lt;br /&gt;
|example4 = &lt;br /&gt;
var object = [1, 2, 3];&lt;br /&gt;
print(typeof(object)); # prints &amp;quot;vector&amp;quot;&lt;br /&gt;
|example5 = &lt;br /&gt;
var object = {};&lt;br /&gt;
print(typeof(object)); # prints &amp;quot;hash&amp;quot;&lt;br /&gt;
|example6 = &lt;br /&gt;
var object = func {};&lt;br /&gt;
print(typeof(object)); # prints &amp;quot;func&amp;quot;&lt;br /&gt;
|example7 =&lt;br /&gt;
var object = airportinfo();&lt;br /&gt;
print(typeof(object)); # prints &amp;quot;ghost&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- == Extension modules ==&lt;br /&gt;
=== thread ===&lt;br /&gt;
{{WIP}}&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = thread.newthread(func);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/threadlib.c|l=101|t=Source}}&lt;br /&gt;
|text = &lt;br /&gt;
|example1text = start a new worker thread&lt;br /&gt;
|example1 = thread.newthread( func() {} );&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = thread.newlock();&lt;br /&gt;
|source = {{simgear file|simgear/nasal/threadlib.c|l=101|t=Source}}&lt;br /&gt;
|text = &lt;br /&gt;
|example1text = create a new lock&lt;br /&gt;
|example1 = var lock = thread.newlock()&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = thread.lock();&lt;br /&gt;
|source = {{simgear file|simgear/nasal/threadlib.c|l=101|t=Source}}&lt;br /&gt;
|text = &lt;br /&gt;
|example1text = lock a lock&lt;br /&gt;
|example1 = var lock = thread.newlock()&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = thread.unlock();&lt;br /&gt;
|source = {{simgear file|simgear/nasal/threadlib.c|l=101|t=Source}}&lt;br /&gt;
|text = &lt;br /&gt;
|example1text = unlock a lock&lt;br /&gt;
|example1 = var lock = thread.unlock()&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = thread.newsem();&lt;br /&gt;
|source = {{simgear file|simgear/nasal/threadlib.c|l=101|t=Source}}&lt;br /&gt;
|text = &lt;br /&gt;
|example1text = create a new {{Wikipedia|semaphore}}&lt;br /&gt;
|example1 = var semaphore = thread.newsem()&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = thread.semdown();&lt;br /&gt;
|source = {{simgear file|simgear/nasal/threadlib.c|l=101|t=Source}}&lt;br /&gt;
|text = &lt;br /&gt;
|example1text = semaphore down&lt;br /&gt;
|example1 = thread.semdown(semaphore)&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = thread.semup();&lt;br /&gt;
|source = {{simgear file|simgear/nasal/threadlib.c|l=101|t=Source}}&lt;br /&gt;
|text = &lt;br /&gt;
|example1text = semaphore up&lt;br /&gt;
|example1 = thread.semup(semaphore)&lt;br /&gt;
}} --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Extension functions==&lt;br /&gt;
The '''extension functions''' are global functions that have been added to Nasal since its integration into FlightGear. Unlike the core library functions, they are generally specifically designed to interact directly with FlightGear. Extension functions come from three source files:&lt;br /&gt;
*{{flightgear file|src/Scripting/NasalPositioned.cxx}}&lt;br /&gt;
*{{flightgear file|src/Scripting/NasalSys.cxx}}&lt;br /&gt;
*{{fgdata file|Nasal/globals.nas}}&lt;br /&gt;
&lt;br /&gt;
===abort()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = abort();&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=565|t=Source}}&lt;br /&gt;
|text = This function is a wrapper for the C++ {{func link|abort()|link=http://www.cplusplus.com/reference/cstdlib/abort/}} function. It simply aborts FlightGear with an error, which varies depending on the operating system. This function should not really be used; instead, please use the &amp;quot;exit&amp;quot; [[Fgcommands|fgcommand]], which will exit FlightGear more gracefully (see example below).&lt;br /&gt;
|example1text = This example will immediately stop FlightGear with an error, such as &amp;quot;FlightGear has stopped working.&amp;quot;&lt;br /&gt;
|example1 = abort();&lt;br /&gt;
|example2text = For exiting FlightGear in a better way, please use the following code:&lt;br /&gt;
|example2 = fgcommand(&amp;quot;exit&amp;quot;);&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== abs() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = abs(number);&lt;br /&gt;
|source = {{fgdata file|Nasal/globals.nas|t=Source}}&lt;br /&gt;
|text = This simple function returns the {{wikipedia|absolute value|noicon=1}} of the provided number.&lt;br /&gt;
|param1 = number&lt;br /&gt;
|param1text = This argument is required and should be a number.&lt;br /&gt;
|example1 = print(abs(1)); # prints &amp;quot;1&amp;quot;&lt;br /&gt;
|example2 = print(abs(-1)); # prints &amp;quot;1&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===aircraftToCart() ===&lt;br /&gt;
This new function in FG 2017.2.1 takes coordinates in aircraft structural coordinate system, and translate them into geocentric coordinates.&lt;br /&gt;
Example for (5,6,7):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
var pos = aircraftToCart({x: -5, y: 6, z: -7});&lt;br /&gt;
var coord = geo.Coord.new();&lt;br /&gt;
coord.set_xyz(pos.x, pos.y, pos.z);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Notice: x and z is inverted sign on purpose.&lt;br /&gt;
if you want lat. lon, alt from that, just call: (degrees and meters)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
coord.lat()&lt;br /&gt;
coord.lon()&lt;br /&gt;
coord.alt()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===addcommand() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = addcommand(name, code);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=659|t=Source}}&lt;br /&gt;
|version = 2.12&lt;br /&gt;
|commit = {{flightgear commit|7b663c|t=commit}}&lt;br /&gt;
|text = {{see also|Howto:Add new fgcommands to FlightGear}}&lt;br /&gt;
&lt;br /&gt;
This function enables the addition of a new custom [[fgcommands|fgcommand]] to FlightGear from within Nasal. An fgcommand created using this method can be used in exactly the same way as the built-in fgcommands. Also, an fgcommand created via this method will always return True or 1, like all other fgcommands.&lt;br /&gt;
|param1 = name&lt;br /&gt;
|param1text = This will become the name of the new fgcommand. Must be a string.&lt;br /&gt;
|param2 = code&lt;br /&gt;
|param2text = The code that will be executed when the fgcommand is run. Must be a function.&lt;br /&gt;
|example1text = This example adds a new fgcommand and then runs it. Although it executes a simple {{func link|print()}} statement, any valid Nasal code can be used.&lt;br /&gt;
|example1 = addcommand(&amp;quot;myFGCmd&amp;quot;, func(node) {&lt;br /&gt;
    print(&amp;quot;fgcommand 'myFGCmd' has been run.&amp;quot;);&lt;br /&gt;
    props.dump( node );&lt;br /&gt;
});&lt;br /&gt;
fgcommand(&amp;quot;myFGCmd&amp;quot;, props.Node.new({foo:1, bar:2}) );&lt;br /&gt;
|example2text = This example demonstrates how parameters are defined in a new fgcommand.&lt;br /&gt;
|example2 = addcommand(&amp;quot;myFGCmd&amp;quot;, func(node){&lt;br /&gt;
    print(node.getNode(&amp;quot;number&amp;quot;).getValue()); # prints the value of &amp;quot;number,&amp;quot; which is 12&lt;br /&gt;
});&lt;br /&gt;
fgcommand(&amp;quot;myFGCmd&amp;quot;, props.Node.new({&amp;quot;number&amp;quot;: 12}));&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===airportinfo()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = airportinfo();&lt;br /&gt;
airportinfo(type);&lt;br /&gt;
airportinfo(id);&lt;br /&gt;
airportinfo(lat, lon[, type]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1024|t=Source}}&lt;br /&gt;
|text = Function for retrieval of airport, heliport, or seaplane base information. It returns a Nasal ghost; however, its structure is like that of a Nasal hash. The following information is returned:&lt;br /&gt;
* '''parents''': A vector containing a hash of various functions to access information about the runway. See {{flightgear file|src/Scripting/NasalPositioned.cxx|l=2659}} for full list.&lt;br /&gt;
* '''lon''': Longitude of the location.&lt;br /&gt;
* '''lat''': Latitude of the location.&lt;br /&gt;
* '''has_metar''': True or false depending whether the airport has a [[METAR]] code defined for it.&lt;br /&gt;
* '''elevation''': Elevation of the location in metres.&lt;br /&gt;
* '''id''': ICAO code of the airport (or ID of the seaplane base/heliport).&lt;br /&gt;
* '''name''': Name of the airport/heliport/seaplane base.&lt;br /&gt;
* '''runways'''&lt;br /&gt;
** '''&amp;lt;runway name&amp;gt;'''&lt;br /&gt;
*** '''id''': Name of runway.&lt;br /&gt;
*** '''lat''': Latitude of the runway.&lt;br /&gt;
*** '''lon''': Longitude of the runway.&lt;br /&gt;
*** '''heading''': Heading of the runway.&lt;br /&gt;
*** '''length''': Length of the runway in metres.&lt;br /&gt;
*** '''width''': Width of the runway in metres.&lt;br /&gt;
*** '''surface''': Runway surface type.&lt;br /&gt;
*** '''threshold''': Length of the runway's {{wikipedia|displaced threshold}} in metres. Will return 0 if there is none.&lt;br /&gt;
*** '''stopway''': Length of the runway's stopway (the area before the threshold) in metres. Will return 0 if there is none.&lt;br /&gt;
*** '''reciprocal''': &amp;lt;code&amp;gt;runway&amp;lt;/code&amp;gt; ghost of the reciprocal runway.&lt;br /&gt;
*** '''ils_frequency_mhz''': ILS frequency in megahertz.&lt;br /&gt;
*** '''ils''': &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt; ghost of the ILS transmitter.&lt;br /&gt;
* '''helipads'''&lt;br /&gt;
** '''&amp;lt;helipad name&amp;gt;'''&lt;br /&gt;
*** '''id''': Name of helipad.&lt;br /&gt;
*** '''lat''': Latitude of the helipad.&lt;br /&gt;
*** '''lon''': Longitude of the helipad.&lt;br /&gt;
*** '''heading''': Heading of the helipad.&lt;br /&gt;
*** '''length''': Length of the helipad in metres.&lt;br /&gt;
*** '''width''': Width of the helipad in metres.&lt;br /&gt;
*** '''surface''': Helipad surface type.&lt;br /&gt;
* '''taxiways'''&lt;br /&gt;
** '''&amp;lt;taxiway name&amp;gt;'''&lt;br /&gt;
*** '''id''': Name of taxiway.&lt;br /&gt;
*** '''lat''': Latitude of the taxiway.&lt;br /&gt;
*** '''lon''': Longitude of the taxiway.&lt;br /&gt;
*** '''heading''': Heading of the taxiway.&lt;br /&gt;
*** '''length''': Length of the taxiway in metres.&lt;br /&gt;
*** '''width''': Width of the taxiway in metres.&lt;br /&gt;
*** '''surface''': Taxiway surface type.&lt;br /&gt;
&lt;br /&gt;
Information is extracted in the same way as accessing members of a Nasal hash. For example:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
# prints to lengths of the runways of the nearest airport in feet and metres&lt;br /&gt;
var info = airportinfo();&lt;br /&gt;
print(&amp;quot;-- Lengths of the runways at &amp;quot;, info.name, &amp;quot; (&amp;quot;, info.id, &amp;quot;) --&amp;quot;);&lt;br /&gt;
foreach(var rwy; keys(info.runways)){&lt;br /&gt;
    print(rwy, &amp;quot;: &amp;quot;, math.round(info.runways[rwy].length * M2FT), &amp;quot; ft (&amp;quot;, info.runways[rwy].length, &amp;quot; m)&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that searches for locations that are a long way away (e.g., the nearest seaplane base to the middle of the Sahara) may cause FlightGear to pause for an amount of time.&lt;br /&gt;
|param1 = id&lt;br /&gt;
|param1text = The {{wikipedia|International Civil Aviation Organization airport code|ICAO code|noicon=1}} of an airport to retrieve information about.&lt;br /&gt;
|param2 = type&lt;br /&gt;
|param2text = When this argument is used, the function will return the closest airport of a certain type. Can be one of &amp;quot;heliport,&amp;quot; &amp;quot;seaport,&amp;quot; or &amp;quot;airport&amp;quot; (default).&lt;br /&gt;
: {{inote|Running this function without any parameters is equivalent to this:&lt;br /&gt;
: &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
airportinfo(&amp;quot;airport&amp;quot;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
|param3 = lat ''and'' lon&lt;br /&gt;
|param3text = When these parameters are used, the function will return information on the nearest airport, heliport or seaplane base (depending on the '''type''' parameter) to those coordinates.&lt;br /&gt;
|example1 = var info = airportinfo();&lt;br /&gt;
print(&amp;quot;Nearest airport: &amp;quot;, info.name, &amp;quot; (&amp;quot;, info.id, &amp;quot;)&amp;quot;); # prints the name and ICAO code of the nearest airport&lt;br /&gt;
|example2 = var info = airportinfo(&amp;quot;heliport&amp;quot;);&lt;br /&gt;
print(&amp;quot;Elevation of the nearest heliport: &amp;quot;, math.round(info.elevation * M2FT), &amp;quot; ft&amp;quot;); # prints the elevation and name of the nearest heliport&lt;br /&gt;
|example3 = var info = airportinfo(&amp;quot;KSQL&amp;quot;);&lt;br /&gt;
print(&amp;quot;-- Runways of &amp;quot;, info.name, &amp;quot; (&amp;quot;, info.id, &amp;quot;): --&amp;quot;);&lt;br /&gt;
foreach(var rwy; keys(info.runways)) {&lt;br /&gt;
    print(rwy); # prints the runways of KSQL&lt;br /&gt;
}&lt;br /&gt;
|example4 = var info = airportinfo(37.81909385, -122.4722484);&lt;br /&gt;
print(&amp;quot;Coordinates of the nearest airport: &amp;quot;, info.lat, &amp;quot;, &amp;quot;, info.lon); # print the name and ICAO of the nearest airport to the Golden Gate Bridge&lt;br /&gt;
|example5 = var info = airportinfo(37.81909385, -122.4722484, &amp;quot;seaport&amp;quot;);&lt;br /&gt;
print(&amp;quot;Nearest seaplane base: &amp;quot;, info.name, &amp;quot; (&amp;quot;, info.id, &amp;quot;)&amp;quot;); # print the name and ID of the nearest seaplane base to the Golden Gate Bridge&lt;br /&gt;
|example6text = This example prints the all information from an &amp;lt;code&amp;gt;airportinfo()&amp;lt;/code&amp;gt; call.&lt;br /&gt;
|example6 = var info = airportinfo(&amp;quot;KSFO&amp;quot;);&lt;br /&gt;
print(info.name);&lt;br /&gt;
print(info.id);&lt;br /&gt;
print(info.lat);&lt;br /&gt;
print(info.lon);&lt;br /&gt;
print(info.has_metar);&lt;br /&gt;
print(info.elevation);&lt;br /&gt;
foreach(var rwy; keys(info.runways)){&lt;br /&gt;
    print(&amp;quot;-- &amp;quot;, rwy, &amp;quot; --&amp;quot;);&lt;br /&gt;
    print(info.runways[rwy].lat);&lt;br /&gt;
    print(info.runways[rwy].lon);&lt;br /&gt;
    print(info.runways[rwy].length);&lt;br /&gt;
    print(info.runways[rwy].width);&lt;br /&gt;
    print(info.runways[rwy].heading);&lt;br /&gt;
    print(info.runways[rwy].stopway);&lt;br /&gt;
    print(info.runways[rwy].threshold);&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===airwaysRoute() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = airwaysRoute(start, end[, type]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1933|t=Source}}&lt;br /&gt;
|text = {{see also|Nasal Flightplan}}&lt;br /&gt;
This function returns a vector containing waypoints between two given waypoints. The returned waypoints are ghosts, but can be accessed in the same way as a Nasal hash. See [[Nasal Flightplan]] for more information.&lt;br /&gt;
|param1 = start&lt;br /&gt;
|param1text = Start waypoint, in the form of a waypoint ghost, such as that provided by {{func link|flightplan()}}.&lt;br /&gt;
|param2 = end&lt;br /&gt;
|param2text = Same as above.&lt;br /&gt;
|param3 = type&lt;br /&gt;
|param3text = Instructs the function to compute a high level route (when set to &amp;quot;highlevel&amp;quot;), or a low level route (when set to &amp;quot;lowlevel&amp;quot;). Defaults to &amp;quot;highlevel.&amp;quot;&lt;br /&gt;
|example1text = In the [[route manager]] dialog, add two waypoints to the flightplan, ideally ones that are far apart (tip: use the [[Map]] for this). Then run this code in your Nasal Console.&lt;br /&gt;
|example1 = var fp = flightplan();&lt;br /&gt;
var start = fp.getWP(0);&lt;br /&gt;
var end = fp.getWP(fp.getPlanSize() - 1);&lt;br /&gt;
var rt = airwaysRoute(start, end);&lt;br /&gt;
foreach(var wp; rt){&lt;br /&gt;
    print(wp.wp_name); # print the waypoints in the computed route&lt;br /&gt;
}&lt;br /&gt;
|example2text = Exactly the same as above, but computes a low level path.&lt;br /&gt;
|example2 = var fp = flightplan();&lt;br /&gt;
var start = fp.getWP(0);&lt;br /&gt;
var end = fp.getWP(fp.getPlanSize() - 1);&lt;br /&gt;
var rt = airwaysRoute(start, end, &amp;quot;lowlevel&amp;quot;);&lt;br /&gt;
foreach(var wp; rt){&lt;br /&gt;
    print(wp.wp_name); # print the waypoints in the computed route&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===airway()===&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = airway(ident [, pos]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=2644|t=Source}}&lt;br /&gt;
|text = {{see also|Nasal Flightplan}}&lt;br /&gt;
This function returns a ghost containing an airway of a specified id.&lt;br /&gt;
|param1 = ident&lt;br /&gt;
|param1text = a Positioned ghost (leg, navaid, airport) and so on, passed to the search function.&lt;br /&gt;
|param2 = pos&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===assert()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = assert(condition[, message]);&lt;br /&gt;
|source = {{fgdata file|Nasal/globals.nas|t=Source}}&lt;br /&gt;
|version = 3.2&lt;br /&gt;
|commit = {{fgdata commit|8b16a7|t=commit}}&lt;br /&gt;
|text = Returns either true if the condition evaluates as true, or aborts with a {{func link|die()}} call, which can be customised.&lt;br /&gt;
|param1 = condition&lt;br /&gt;
|param1text = Condition to evaluate.&lt;br /&gt;
|param2 = message&lt;br /&gt;
|param2text = Optional message that will be used in any {{func link|die()}} call. Defaults to &amp;quot;assertion failed!&amp;quot;&lt;br /&gt;
|example1 = var a = 1;&lt;br /&gt;
var b = 2;&lt;br /&gt;
print(assert(a &amp;lt; b)); # prints &amp;quot;1&amp;quot; (true)&lt;br /&gt;
|example2 = var a = 1;&lt;br /&gt;
var b = 2;&lt;br /&gt;
assert(a &amp;gt; b, 'a is not greater than b'); # aborts with a custom error message&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===carttogeod()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = carttogeod(x, y, z);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=945|t=Source}}&lt;br /&gt;
|text = Converts {{wikipedia|ECEF|Earth-centered, Earth-fixed}} coordinates (x, y and z) to {{wikipedia|geodetic coordinates}} (latitude, longitude, and altitude). A vector is returned containing latitude and longitude, both in degrees, and altitude, which is returned in metres above the equatorial radius of Earth as defined by the {{wikipedia|WGS 84}} (6,378,137 metres).&amp;lt;ref&amp;gt;{{simgear file|simgear/math/sg_geodesy.hxx|l=43}}&amp;lt;/ref&amp;gt;&lt;br /&gt;
|param1 = x&lt;br /&gt;
|param1text = Mandatory x-axis value, in metres.&lt;br /&gt;
|param2 = y&lt;br /&gt;
|param2text = Mandatory y-axis value, in metres.&lt;br /&gt;
|param3 = z&lt;br /&gt;
|param3text = Mandatory z-axis value, in metres.&lt;br /&gt;
|example1 = var (lat, lon, alt) = carttogeod(6378137, 0, 0); # point is the intersection of the prime meridian and equator.&lt;br /&gt;
print(&amp;quot;Latitude: &amp;quot;, lat); # prints lat, lon and alt, which are all zero, see above&lt;br /&gt;
print(&amp;quot;Longitude: &amp;quot;, lon);&lt;br /&gt;
print(&amp;quot;Altitude: &amp;quot;, alt);&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===cmdarg()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|private = _cmdarg()&lt;br /&gt;
|syntax = cmdarg();&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=513|t=Part 1}} {{!}} {{fgdata file|Nasal/globals.nas|t=Part 2}}&lt;br /&gt;
|text = &amp;lt;code&amp;gt;cmdarg()&amp;lt;/code&amp;gt; returns the property root of certain types of XML files. These could be nodes in the [[Property Tree]], or temporary and/or non-public nodes outside the Property tree. &lt;br /&gt;
It is used by Nasal scripts embedded in XML files. It returns a &amp;lt;code&amp;gt;props.Node&amp;lt;/code&amp;gt; object (see {{fgdata file|Nasal/props.nas}}), and you can use all of its methods on the returned value. &amp;lt;code&amp;gt;cmdarg()&amp;lt;/code&amp;gt; should only be used in four types/places of XML files:&lt;br /&gt;
* Bindings: This is needed so that the value of a joystick's axis can be accessed internally.&lt;br /&gt;
* Dialogs: This will return the root of the dialog in the Property Tree. This is useful for dialogs that are created/modified procedurally (e.g. for populating/changing widgets while loading the dialog). &lt;br /&gt;
* Embedded Canvases: The Nasal code behind [[Canvas]] windows [[Howto:Adding a canvas to a GUI dialog|embedded in PUI dialogs]] can use it to accessing the root directory of their Canvas.&lt;br /&gt;
* Animation XML files: If the animation XML file is used in an AI/MP model, &amp;lt;code&amp;gt;cmdarg()&amp;lt;/code&amp;gt; will return the root of the AI model in the &amp;lt;code&amp;gt;/ai/models/&amp;lt;/code&amp;gt; directory. Examples: &amp;lt;code&amp;gt;/ai/models/aircraft[3]/&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/ai/models/multiplayer[1]/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You should not use &amp;lt;code&amp;gt;cmdarg()&amp;lt;/code&amp;gt; in places other than those stated above. Although it won't cause an error, it will return the value of the last legitimate &amp;lt;code&amp;gt;cmdarg()&amp;lt;/code&amp;gt; call. &lt;br /&gt;
&lt;br /&gt;
Also, you should not delay &amp;lt;code&amp;gt;cmdarg()&amp;lt;/code&amp;gt; using {{func link|maketimer()}}, {{func link|settimer()}} or {{func link|setlistener()}}, because it will return an unrelated property.&lt;br /&gt;
|example1 = fgcommand(&amp;quot;dialog-show&amp;quot;, {&amp;quot;dialog-name&amp;quot;: &amp;quot;cmdarg-demo&amp;quot;});&lt;br /&gt;
|example1text = &amp;lt;br&amp;gt;This example demonstrates the usage of &amp;lt;code&amp;gt;cmdarg()&amp;lt;/code&amp;gt; in a binding.  Save the below XML snippet as &amp;lt;tt&amp;gt;[[$FG_ROOT]]/gui/dialogs/cmdarg-demo.xml&amp;lt;/tt&amp;gt;. Then run the Nasal snippet below in your [[Nasal Console]]. Upon clicking {{button|Close}}, a message will be printed sowing the root of the binding in the Property Tree.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;PropertyList&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;name&amp;gt;cmdarg-demo&amp;lt;/name&amp;gt;&lt;br /&gt;
&amp;lt;layout&amp;gt;vbox&amp;lt;/layout&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;text&amp;gt;&lt;br /&gt;
  &amp;lt;label&amp;gt;Click &amp;quot;Close&amp;quot; to activate the demonstration (a message in the console).&amp;lt;/label&amp;gt;&lt;br /&gt;
&amp;lt;/text&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;button&amp;gt;&lt;br /&gt;
  &amp;lt;legend&amp;gt;Close&amp;lt;/legend&amp;gt;&lt;br /&gt;
  &amp;lt;binding&amp;gt;&lt;br /&gt;
    &amp;lt;command&amp;gt;nasal&amp;lt;/command&amp;gt;&lt;br /&gt;
    &amp;lt;script&amp;gt;print(&amp;quot;Button binding root: '&amp;quot; ~ cmdarg().getPath() ~ &amp;quot;'&amp;quot;);&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;/binding&amp;gt;&lt;br /&gt;
  &amp;lt;binding&amp;gt;&lt;br /&gt;
    &amp;lt;command&amp;gt;dialog-close&amp;lt;/command&amp;gt;&lt;br /&gt;
  &amp;lt;/binding&amp;gt;&lt;br /&gt;
&amp;lt;/button&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/PropertyList&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|example2text = This example demonstrates the usage of &amp;lt;code&amp;gt;cmdarg()&amp;lt;/code&amp;gt; in Nasal code within dialogs.  Open &amp;lt;tt&amp;gt;[[$FG_ROOT]]/gui/dialogs/cmdarg-demo.xml&amp;lt;/tt&amp;gt; from the previous example, copy &amp;amp; paste the code below, and save it. Then run the same Nasal snippet as the previous example in your Nasal Console. If you click {{button|Click me!}}, the button's label will change to &amp;quot;I've been changed!&amp;quot;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;PropertyList&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;name&amp;gt;cmdarg-demo&amp;lt;/name&amp;gt;&lt;br /&gt;
&amp;lt;layout&amp;gt;vbox&amp;lt;/layout&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;text&amp;gt;&lt;br /&gt;
  &amp;lt;label&amp;gt;Click &amp;quot;Click me!&amp;quot; to activate the demonstration (the button's label will change).&amp;lt;/label&amp;gt;&lt;br /&gt;
&amp;lt;/text&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;button&amp;gt;&lt;br /&gt;
  &amp;lt;legend&amp;gt;Click me!&amp;lt;/legend&amp;gt;&lt;br /&gt;
  &amp;lt;binding&amp;gt;&lt;br /&gt;
    &amp;lt;command&amp;gt;nasal&amp;lt;/command&amp;gt;&lt;br /&gt;
    &amp;lt;script&amp;gt;change_label();&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;/binding&amp;gt;&lt;br /&gt;
&amp;lt;/button&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;button&amp;gt;&lt;br /&gt;
  &amp;lt;legend&amp;gt;Close&amp;lt;/legend&amp;gt;&lt;br /&gt;
  &amp;lt;binding&amp;gt;&lt;br /&gt;
    &amp;lt;command&amp;gt;dialog-close&amp;lt;/command&amp;gt;&lt;br /&gt;
  &amp;lt;/binding&amp;gt;&lt;br /&gt;
&amp;lt;/button&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nasal&amp;gt;&lt;br /&gt;
  &amp;lt;open&amp;gt;&amp;lt;![CDATA[&lt;br /&gt;
    var dlg_root = cmdarg();&lt;br /&gt;
    var dlg_name = {&amp;quot;dialog-name&amp;quot;: &amp;quot;cmdarg-demo&amp;quot;};&lt;br /&gt;
    var change_label = func {&lt;br /&gt;
        dlg_root.getNode(&amp;quot;button[0]/legend&amp;quot;).setValue(&amp;quot;I've been changed!&amp;quot;);&lt;br /&gt;
        fgcommand(&amp;quot;dialog-close&amp;quot;, dlg_name);&lt;br /&gt;
        fgcommand(&amp;quot;dialog-show&amp;quot;, dlg_name);&lt;br /&gt;
    }&lt;br /&gt;
  ]]&amp;gt;&amp;lt;/open&amp;gt;&lt;br /&gt;
&amp;lt;/nasal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/PropertyList&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|example3text = For an example of &amp;lt;code&amp;gt;cmdarg()&amp;lt;/code&amp;gt; used with Canvas, please see [[Howto:Adding a canvas to a GUI dialog#FGPlot|Howto:Adding a canvas to a GUI dialog]].&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===courseAndDistance()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = courseAndDistance(to);&lt;br /&gt;
courseAndDistance(from, to);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1668|t=Source}}&lt;br /&gt;
|text = Returns a vector containing the course from one point to another and the distance between them in nautical miles. The course is the initial bearing (see [http://www.movable-type.co.uk/scripts/latlong.html#bearing here]), and is in the range 0–360. Both arguments can be one of:&lt;br /&gt;
:* An &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;runway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;taxiway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fix&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;waypoint&amp;lt;/code&amp;gt; ghost type&lt;br /&gt;
:* A hash with ''lat'' and ''lon'' members&lt;br /&gt;
:* A geo.Coord object with geodetic coordinates (cartesian coordinates will not be accepted)&lt;br /&gt;
|param1 = from&lt;br /&gt;
|param1text = Optional parameter defining the from where the function should calculate its results. If the function is given one argument ('''to'''), the aircraft's current position will be used. As well as the argument types as defined above, this argument can be two numbers separated with a comma, as if the function is taking three arguments. See example 5 below.&lt;br /&gt;
|param2 = to&lt;br /&gt;
|param2text = Like the first parameter, but defines the second point.&lt;br /&gt;
|example1text = This example demonstrates the usage of the function with the &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt; ghost type.&lt;br /&gt;
|example1 = var from = airportinfo(&amp;quot;KSFO&amp;quot;);&lt;br /&gt;
var to = airportinfo(&amp;quot;KSQL&amp;quot;);&lt;br /&gt;
var (course, dist) = courseAndDistance(from, to);&lt;br /&gt;
print(course); # prints course from KSFO to KSQL&lt;br /&gt;
print(dist); # prints distance in nm from KSFO to KSQL&lt;br /&gt;
|example2text = This example demonstrates the usage of the function with hashes containing ''lat'' and ''lon''.&lt;br /&gt;
|example2 = var from = {lat: 0, lon: 0};&lt;br /&gt;
var to = {lat: 1, lon: 1};&lt;br /&gt;
var (course, dist) = courseAndDistance(from, to);&lt;br /&gt;
print(course);&lt;br /&gt;
print(dist);&lt;br /&gt;
|example3text = This example demonstrates usage of a geo.Coord object.&lt;br /&gt;
|example3 = var from = geo.Coord.new().set_latlon(0, 0);&lt;br /&gt;
var to = geo.Coord.new().set_latlon(1, 1);&lt;br /&gt;
var (course, dist) = courseAndDistance(from, to);&lt;br /&gt;
print(course);&lt;br /&gt;
print(dist);&lt;br /&gt;
|example4text = This example demonstrates usage of differing parameter types.&lt;br /&gt;
|example4 = var from = airportinfo(&amp;quot;KSFO&amp;quot;);&lt;br /&gt;
var to = geo.Coord.new().set_latlon(0, 0);&lt;br /&gt;
var (course, dist) = courseAndDistance(from, to);&lt;br /&gt;
print(course);&lt;br /&gt;
print(dist);&lt;br /&gt;
|example5text = The same as above, but the other way round.&lt;br /&gt;
|example5 = var to = {lat: 1, lon: 1};&lt;br /&gt;
var (course, dist) = courseAndDistance(0, 0, to);&lt;br /&gt;
print(course);&lt;br /&gt;
print(dist);&lt;br /&gt;
|example6text = Usage of just one parameter.&lt;br /&gt;
|example6 = var dest = airportinfo(&amp;quot;KSQL&amp;quot;);&lt;br /&gt;
var (course, dist) = courseAndDistance(dest);&lt;br /&gt;
print(&amp;quot;Turn to heading &amp;quot;, math.round(course), &amp;quot;. You have &amp;quot;, sprintf(&amp;quot;%.2f&amp;quot;, dist), &amp;quot; nm to go&amp;quot;);&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===createFlightplan()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = createFlightplan(path);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=2331|t=Source}}&lt;br /&gt;
|text = Creates an empty flightplan object. It accepts one argument, ''path'' passed an absolute path to a .fgfp / .gpx file, it will populate the flightplan with waypoints from the file.&lt;br /&gt;
|param1 = path&lt;br /&gt;
|param1text = Optional parameter defining the file from which a flightplan will be populated.&lt;br /&gt;
|example1 = &lt;br /&gt;
var path = getprop(&amp;quot;/sim/fg-home&amp;quot;) ~ &amp;quot;/Export/test.fgfp&amp;quot;;&lt;br /&gt;
var flightplan = createFlightplan(path);&lt;br /&gt;
debug.dump(flightplan);&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===createDiscontinuity()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = createDiscontinuity();&lt;br /&gt;
|text = Returns a &amp;lt;code&amp;gt;waypoint&amp;lt;/code&amp;gt; ghost object. A route discontinuity is inserted by an {{abbr|FMS|Flight Management System}} when it is unsure how to connect two waypoints.&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=2045|t=Source}}&lt;br /&gt;
|version = 2016.1&lt;br /&gt;
|commit = {{flightgear commit|caead6|t=commit}}&lt;br /&gt;
}}&lt;br /&gt;
===createViaTo()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = createViaTo(airway, waypoint);&lt;br /&gt;
|text = Returns a &amp;lt;code&amp;gt;waypoint&amp;lt;/code&amp;gt; ghost object. It represents a route &amp;quot;via '''airway''' to '''waypoint'''&amp;quot;.&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=2009|t=Source}}&lt;br /&gt;
|version = 2016.1&lt;br /&gt;
|commit = {{flightgear commit|caead6|t=commit}}&lt;br /&gt;
|param1 = airway&lt;br /&gt;
|param1text = The name of an airway.&lt;br /&gt;
|param2 = waypoint&lt;br /&gt;
|param2text = Must be in the airway and one of:&lt;br /&gt;
* The name of a waypoint.&lt;br /&gt;
* An &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;runway&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;fix&amp;lt;/code&amp;gt; ghost object.&lt;br /&gt;
}}&lt;br /&gt;
===createWP()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = createWP(pos, name[, flag]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1964|t=Source}}&lt;br /&gt;
|text = Creates a new waypoint ghost object.&lt;br /&gt;
|param1 = pos&lt;br /&gt;
|param1text = Dictates the position of the new waypoint. It can be one of the following:&lt;br /&gt;
:* An &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;runway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;taxiway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fix&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;waypoint&amp;lt;/code&amp;gt; ghost type&lt;br /&gt;
:* A hash with ''lat'' and ''lon'' members&lt;br /&gt;
:* A geo.Coord object&lt;br /&gt;
:* Two numbers separated by a comma, as if the function is taking three arguments. See example 4 below.&lt;br /&gt;
|param2 = name&lt;br /&gt;
|param2text = String that will become the name of the new waypoint.&lt;br /&gt;
|param3 = flag&lt;br /&gt;
|param3text = Optional string that will tell FlightGear what type of waypoint it is. Must be one of &amp;quot;sid,&amp;quot; &amp;quot;star,&amp;quot; &amp;quot;approach,&amp;quot; &amp;quot;missed,&amp;quot; or &amp;quot;pseudo.&amp;quot;&lt;br /&gt;
|example1text = Creates a waypoint directly in front and 1 km away and appends it to the flight plan.&lt;br /&gt;
|example1 = var pos = geo.aircraft_position().apply_course_distance(getprop(&amp;quot;/orientation/heading-deg&amp;quot;), 1000);&lt;br /&gt;
var wp = createWP(pos, &amp;quot;NEWWP&amp;quot;);&lt;br /&gt;
var fp = flightplan();&lt;br /&gt;
fp.appendWP(wp);&lt;br /&gt;
|example2 = var pos = geo.aircraft_position().apply_course_distance(getprop(&amp;quot;/orientation/heading-deg&amp;quot;), 1000);&lt;br /&gt;
var wp = createWP({lat: pos.lat(), lon: pos.lon()}, &amp;quot;NEWWP&amp;quot;);&lt;br /&gt;
var fp = flightplan();&lt;br /&gt;
fp.appendWP(wp);&lt;br /&gt;
|example3 = var apt = airportinfo();&lt;br /&gt;
var wp = createWP(apt, &amp;quot;NEWWP&amp;quot;);&lt;br /&gt;
var fp = flightplan();&lt;br /&gt;
fp.appendWP(wp);&lt;br /&gt;
|example4 = var pos = geo.aircraft_position().apply_course_distance(getprop(&amp;quot;/orientation/heading-deg&amp;quot;), 1000);&lt;br /&gt;
var wp = createWP(pos.lat(), pos.lon(), &amp;quot;NEWWP&amp;quot;);&lt;br /&gt;
var fp = flightplan();&lt;br /&gt;
fp.appendWP(wp);&lt;br /&gt;
|example5text = Creates a new waypoint and adds it to the flight plan. Waypoints of the type &amp;quot;pseudo&amp;quot; are then removed from the flight plan, including the new waypoint. The {{func link|print()}} statements show this.&lt;br /&gt;
|example5 = var pos = geo.aircraft_position().apply_course_distance(getprop(&amp;quot;/orientation/heading-deg&amp;quot;), 1000);&lt;br /&gt;
var wp = createWP(pos, &amp;quot;NEWWP&amp;quot;, &amp;quot;pseudo&amp;quot;);&lt;br /&gt;
var fp = flightplan();&lt;br /&gt;
fp.appendWP(wp);&lt;br /&gt;
print(fp.getPlanSize());&lt;br /&gt;
fp.clearWPType(&amp;quot;pseudo&amp;quot;);&lt;br /&gt;
print(fp.getPlanSize());&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===createWPFrom()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = createWPFrom(object[, flag]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1989|t=Source}}&lt;br /&gt;
|text = Creates a new waypoint object from another object.&lt;br /&gt;
|param1 = object&lt;br /&gt;
|param1text = A ghost object. Must be a ghost type that is one of &amp;quot;airport,&amp;quot; &amp;quot;navaid,&amp;quot; &amp;quot;runway,&amp;quot; or &amp;quot;fix.&amp;quot;&lt;br /&gt;
|param2 = flag&lt;br /&gt;
|param2text = Optional string that will tell FlightGear what type of waypoint it is. Must be one of &amp;quot;sid,&amp;quot; &amp;quot;star,&amp;quot; &amp;quot;approach,&amp;quot; &amp;quot;missed,&amp;quot; or &amp;quot;pseudo.&amp;quot;&lt;br /&gt;
|example1text = Creates a new waypoint and appends it to the flight plan.&lt;br /&gt;
|example1 = var apt = airportinfo(&amp;quot;KSFO&amp;quot;);&lt;br /&gt;
var wp = createWPFrom(apt);&lt;br /&gt;
var fp = flightplan();&lt;br /&gt;
fp.appendWP(wp);&lt;br /&gt;
|example2text = Creates a new waypoint and appends it to the flight plan. This way point is then removed; the {{func link|print()}} statements prove this.&lt;br /&gt;
|example2 = var apt = airportinfo(&amp;quot;KSFO&amp;quot;);&lt;br /&gt;
var wp = createWPFrom(apt, &amp;quot;pseudo&amp;quot;);&lt;br /&gt;
print(wp.wp_name);&lt;br /&gt;
var fp = flightplan();&lt;br /&gt;
fp.appendWP(wp);&lt;br /&gt;
print(fp.getPlanSize());&lt;br /&gt;
fp.clearWPType(&amp;quot;pseudo&amp;quot;);&lt;br /&gt;
print(fp.getPlanSize());&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===defined()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = defined(symbol);&lt;br /&gt;
|source = {{fgdata file|Nasal/globals.nas|t=Source}}&lt;br /&gt;
|text = Returns 1 (true) or 0 (false) depending on whether a variable exists.&lt;br /&gt;
|param1 = symbol&lt;br /&gt;
|param1text = A string that will be what the function searches for.&lt;br /&gt;
|example1 = var number = 12;&lt;br /&gt;
var check_exist = func {&lt;br /&gt;
    print(&amp;quot;Variable 'number' &amp;quot;, defined(&amp;quot;number&amp;quot;) == 1 ? &amp;quot;exists&amp;quot; : &amp;quot;does not exist&amp;quot;); # 'number' does exist&lt;br /&gt;
    print(&amp;quot;Variable 'number2' &amp;quot;, defined(&amp;quot;number2&amp;quot;) == 1 ? &amp;quot;exists&amp;quot; : &amp;quot;does not exist&amp;quot;); # 'number2' does not exist&lt;br /&gt;
}&lt;br /&gt;
check_exist();&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===directory()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = directory(path);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=572|t=Source}}&lt;br /&gt;
|text = Returns a vector containing a list of the folders and files in a given file path or &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; if the path doesn't exist. Hidden folders and files are not added to the vector.&lt;br /&gt;
{{inote|The first two elements of the vector will be &amp;lt;code&amp;gt;'.'&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;'..'&amp;lt;/code&amp;gt;. These are for navigating back up the file tree, but have no use in Nasal. They can be safely removed from the vector.}}&lt;br /&gt;
|param1 = path&lt;br /&gt;
|param1text = Absolute file path.&lt;br /&gt;
|example1text = Gets the folders and files in [[$FG_ROOT]], and then removes the extra first two elements (see note above). &lt;br /&gt;
|example1 = var dir = directory(getprop(&amp;quot;/sim/fg-root&amp;quot;)); # get directory&lt;br /&gt;
dir = subvec(dir, 2); # strips off the first two elements&lt;br /&gt;
debug.dump(dir); # dump the vector&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===fgcommand()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = fgcommand(cmd[, args]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=456|t=Part 1}} {{!}} {{fgdata file|Nasal/globals.nas|t=Part 2}}&lt;br /&gt;
|text = Runs an fgcommand. See also {{readme file|commands}} and [[Bindings]] for more information. See {{flightgear file|src/Main/fg_commands.cxx|l=1425}} for the full list of fgcommands. Note that fgcommands generated by {{func link|addcommand()}} can also be run using this function. Also, the full list of fgcommands depends on the version of FlightGear you have. Returns 1 (true) if the fgcommand succeeded or 0 (false) if it failed.&lt;br /&gt;
|param1 = cmd&lt;br /&gt;
|param1text = String that is the name of the command that is to be run.&lt;br /&gt;
|param2 = args&lt;br /&gt;
|param2text = If the fgcommand takes arguments, they are inputted using this argument. Can either be a &amp;lt;code&amp;gt;props.Node&amp;lt;/code&amp;gt; object, or a hash (see examples below).&lt;br /&gt;
|example1 = fgcommand(&amp;quot;null&amp;quot;); # does nothing&lt;br /&gt;
|example2 = var args = props.Node.new({'script': 'print(&amp;quot;Running fgcommand&amp;quot;);'});&lt;br /&gt;
if (fgcommand(&amp;quot;nasal&amp;quot;, args)) { # prints &amp;quot;Running fgcommand&amp;quot; and then one of these print statements&lt;br /&gt;
    print(&amp;quot;Fgcommand succeeded&amp;quot;);&lt;br /&gt;
} else {&lt;br /&gt;
    print(&amp;quot;Fgcommand encountered a problem&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
|example3 = var args = { 'dialog-name': 'about' };&lt;br /&gt;
fgcommand(&amp;quot;dialog-show&amp;quot;, args); # shows the 'about' dialog&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===findAirportsByICAO() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = findAirportsByICAO(search[, type]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1096|t=Source}}&lt;br /&gt;
|text = Returns a vector containing &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt; ghost objects which are (by default) airports whose ICAO code matches the search string. The results are sorted by range from closest to furthest.&lt;br /&gt;
|param1 = search&lt;br /&gt;
|param1text = Search string for the function. Can either be a partial or a full ICAO code.&lt;br /&gt;
:{{icaution|The more matches there are for the given code, the longer the function will take. Passing just one character (e.g., &amp;quot;K&amp;quot;), might make FlightGear hang for a certain amount of time.}}&lt;br /&gt;
|param2 = type&lt;br /&gt;
|param2text = This will narrow the search to airports of a certain type. By default, only airports are searched for. May be one of &amp;quot;airport,&amp;quot; &amp;quot;heliport,&amp;quot; or &amp;quot;seaport.&amp;quot;&lt;br /&gt;
|example1 = var apts = findAirportsByICAO(&amp;quot;KSF&amp;quot;); # finds all airports matching &amp;quot;KSF&amp;quot;&lt;br /&gt;
foreach(var apt; apts){&lt;br /&gt;
    print(apt.name, &amp;quot; (&amp;quot;, apt.id, &amp;quot;)&amp;quot;); # prints them&lt;br /&gt;
}&lt;br /&gt;
|example2 = var apts = findAirportsByICAO(&amp;quot;SP0&amp;quot;, &amp;quot;seaport&amp;quot;); # finds all seaplane bases matching &amp;quot;SP0&amp;quot;&lt;br /&gt;
foreach(var apt; apts){&lt;br /&gt;
    print(apt.name, &amp;quot; (&amp;quot;, apt.id, &amp;quot;)&amp;quot;); # prints them&lt;br /&gt;
}&lt;br /&gt;
|example3 = var apt = findAirportsByICAO(&amp;quot;XBET&amp;quot;); # one way to check if an airport does exist&amp;quot;&lt;br /&gt;
if (size(apt) == 0) {&lt;br /&gt;
    print(&amp;quot;Airport does not exist&amp;quot;); # this one will be printed&lt;br /&gt;
} else {&lt;br /&gt;
    print(&amp;quot;Airport does exist&amp;quot;);&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===findAirportsWithinRange()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = findAirportsWithinRange([pos, ]range[, type]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1066|t=Source}}&lt;br /&gt;
|text = Returns a vector of &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt; ghost object which are (by default) airports that are within a given range of a given position, or the aircraft's current position. The results are sorted by range from closest to furthest.&lt;br /&gt;
|param1 = pos&lt;br /&gt;
|param1text = Optional position to search around. If not given, the aircraft's current position will be used. Can be one of:&lt;br /&gt;
:* An &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;runway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;taxiway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fix&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;waypoint&amp;lt;/code&amp;gt; ghost type&lt;br /&gt;
:* A hash with ''lat'' and ''lon'' members&lt;br /&gt;
:* A geo.Coord object&lt;br /&gt;
:* Two numbers separated by a comma, as if the function is taking three arguments. Example: &amp;lt;code&amp;gt;findAirportsWithinRange(lat, lon, range, type);&amp;lt;/code&amp;gt;.&lt;br /&gt;
|param2 = range&lt;br /&gt;
|param2text = Mandatory number giving the range in nautical miles within which to search for airports/heliports/seaplane bases.only airports are searched for.&lt;br /&gt;
|param3 = type&lt;br /&gt;
|param3text = This will narrow the search to airports of a certain type. By default, only airports are searched for. May be one of &amp;quot;airport,&amp;quot; &amp;quot;heliport,&amp;quot; or &amp;quot;seaport.&amp;quot;&lt;br /&gt;
|example1text = Searches for airports within 10 nm of [[KSFO]].&lt;br /&gt;
|example1 = var pos = airportinfo(&amp;quot;KSFO&amp;quot;);&lt;br /&gt;
var apts = findAirportsWithinRange(pos, 10);&lt;br /&gt;
foreach(var apt; apts){&lt;br /&gt;
    print(apt.name, &amp;quot; (&amp;quot;, apt.id, &amp;quot;)&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
|example2text = Searches for seaplane bases within 15 nm of [[KSFO]].&lt;br /&gt;
|example2 = var pos = airportinfo(&amp;quot;KSFO&amp;quot;);&lt;br /&gt;
var apts = findAirportsWithinRange(pos, 15, &amp;quot;seaport&amp;quot;);&lt;br /&gt;
foreach(var apt; apts){&lt;br /&gt;
    print(apt.name, &amp;quot; (&amp;quot;, apt.id, &amp;quot;)&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
|example3text = Searches for airports within 10 nm of your current position.&lt;br /&gt;
|example3 = var apts = findAirportsWithinRange(10);&lt;br /&gt;
foreach(var apt; apts){&lt;br /&gt;
    print(apt.name, &amp;quot; (&amp;quot;, apt.id, &amp;quot;)&amp;quot;);&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===findCommByFrequencyMHz()===&lt;br /&gt;
 findCommByFrequencyMHz([pos, ]freq[, type]);&lt;br /&gt;
[[sourceforge:p/flightgear/flightgear/ci/next/tree/src/Scripting/NasalPositioned.cxx#l1547|source]]&lt;br /&gt;
&lt;br /&gt;
Returns a &amp;lt;code&amp;gt;comm&amp;lt;/code&amp;gt; ghost object for a station matching a given frequency. If there is more than one station with that frequency, the nearest station is returned.&lt;br /&gt;
&lt;br /&gt;
'''Pos'''&lt;br /&gt;
&lt;br /&gt;
: Optional position to search around. If not given, the aircraft's current position will be used. Can be one of:&lt;br /&gt;
:* An &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;runway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;taxiway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fix&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;waypoint&amp;lt;/code&amp;gt; ghost type&lt;br /&gt;
:* A hash with ''lat'' and ''lon'' members&lt;br /&gt;
:* A geo.Coord object&lt;br /&gt;
:* Two numbers separated by a comma, as if the function is taking three arguments. Example: &amp;lt;code&amp;gt;findCommByFrequencyMHz(lat, lon, freq, type);&amp;lt;/code&amp;gt;.&lt;br /&gt;
; freq&lt;br /&gt;
: Frequency, in megahertz, of the station to search for.&lt;br /&gt;
; type&lt;br /&gt;
: This will narrow the search to station of a certain type. Defaults to &amp;quot;all.&amp;quot; For the full list of accepted type arguments, see flightgear/src/Navaids/positioned.cxx (line 160)&lt;br /&gt;
'''Example'''&lt;br /&gt;
 var com = findCommByFrequencyMHz(123.6);&lt;br /&gt;
 print(&amp;quot;ID: &amp;quot;, com.id); # prints info about the comm station&lt;br /&gt;
 print(&amp;quot;Name: &amp;quot;, com.name);&lt;br /&gt;
 print(&amp;quot;Latitude: &amp;quot;, com.lat);&lt;br /&gt;
 print(&amp;quot;Longitude: &amp;quot;, com.lon);&lt;br /&gt;
 print(&amp;quot;Type: &amp;quot;, com.type);&lt;br /&gt;
 print(&amp;quot;Frequency: &amp;quot;, sprintf(&amp;quot;%.3f&amp;quot;, com.frequency), &amp;quot; Mhz&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
=== findFixesByID() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = findFixesByID([pos, ]id);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1627|t=Source}}&lt;br /&gt;
|text = Returns a vector containing &amp;lt;code&amp;gt;fix&amp;lt;/code&amp;gt; ghost objects matching a given ID, sorted by range from a certain position.&lt;br /&gt;
{{inote|Fixes are (usually) also known as waypoints.}}&lt;br /&gt;
|param1 = pos&lt;br /&gt;
|param1text = Optional position to search around. If not given, the aircraft's current position will be used. Can be one of:&lt;br /&gt;
:* An &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;runway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;taxiway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fix&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;waypoint&amp;lt;/code&amp;gt; ghost type&lt;br /&gt;
:* A hash with ''lat'' and ''lon'' members&lt;br /&gt;
:* A geo.Coord object&lt;br /&gt;
:* Two numbers separated by a comma, as if the function is taking three arguments. Example: &amp;lt;code&amp;gt;findFixesByID(lat, lon, id);&amp;lt;/code&amp;gt;.&lt;br /&gt;
|param2 = id&lt;br /&gt;
|param2text = Full or partial ID of the fix to search for.&lt;br /&gt;
:{{inote|1=Inputting a partial ID does not work correctly (see [http://forum.flightgear.org/viewtopic.php?f=30&amp;amp;t=28129 here]). It is best to just input a full ID.}}&lt;br /&gt;
|example1 = var fixes = findFixesByID(&amp;quot;POGIC&amp;quot;);&lt;br /&gt;
foreach(var fix; fixes){&lt;br /&gt;
    print(fix.id, &amp;quot; - lat: &amp;quot;, fix.lat, &amp;quot; {{!}} lon: &amp;quot;, fix.lon); # prints information about POGIC&lt;br /&gt;
}&lt;br /&gt;
|example2 = var fix = findFixesByID(&amp;quot;ZUNAP&amp;quot;);&lt;br /&gt;
fix = fix[0];&lt;br /&gt;
var (course, dist) = courseAndDistance(fix);&lt;br /&gt;
print(&amp;quot;Turn to heading &amp;quot;, math.round(course), &amp;quot;. You have &amp;quot;, sprintf(&amp;quot;%.2f&amp;quot;, dist), &amp;quot; nm to go to reach &amp;quot;, fixes[0].id);&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===findNavaidByFrequencyMHz()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = findNavaidByFrequencyMHz([pos, ]freq[, type]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1547|t=Source}}&lt;br /&gt;
|text = Returns a &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt; ghost object for a navaid matching a given frequency. If there is more than one navaid with that frequency, the nearest station is returned.&lt;br /&gt;
|param1 = pos&lt;br /&gt;
|param1text = Optional position to search around. If not given, the aircraft's current position will be used. Can be one of:&lt;br /&gt;
:* An &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;runway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;taxiway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fix&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;waypoint&amp;lt;/code&amp;gt; ghost type&lt;br /&gt;
:* A hash with ''lat'' and ''lon'' members&lt;br /&gt;
:* A geo.Coord object&lt;br /&gt;
:* Two numbers separated by a comma, as if the function is taking three arguments. Example: &amp;lt;code&amp;gt;findNavaidByFrequencyMHz(lat, lon, freq, type);&amp;lt;/code&amp;gt;.&lt;br /&gt;
|param2 = freq&lt;br /&gt;
|param2text = Frequency, in megahertz, of the navaid to search for.&lt;br /&gt;
|param3 = type&lt;br /&gt;
|param3text = This will narrow the search to navaids of a certain type. Defaults to &amp;quot;all.&amp;quot; For the full list of accepted type arguments, see {{flightgear file|src/Navaids/positioned.cxx|l=127}}.&lt;br /&gt;
|example1 = var navaid = findNavaidByFrequencyMHz(109.55);&lt;br /&gt;
print(&amp;quot;ID: &amp;quot;, navaid.id); # prints info about the navaid&lt;br /&gt;
print(&amp;quot;Name: &amp;quot;, navaid.name);&lt;br /&gt;
print(&amp;quot;Latitude: &amp;quot;, navaid.lat);&lt;br /&gt;
print(&amp;quot;Longitude: &amp;quot;, navaid.lon);&lt;br /&gt;
print(&amp;quot;Elevation (AMSL): &amp;quot;, navaid.elevation, &amp;quot; m&amp;quot;);&lt;br /&gt;
print(&amp;quot;Type: &amp;quot;, navaid.type);&lt;br /&gt;
print(&amp;quot;Frequency: &amp;quot;, sprintf(&amp;quot;%.3f&amp;quot;, navaid.frequency / 100), &amp;quot; Mhz&amp;quot;);&lt;br /&gt;
print(&amp;quot;Range: &amp;quot;, navaid.range_nm, &amp;quot; nm&amp;quot;);&lt;br /&gt;
if(navaid.course) print(&amp;quot;Course: &amp;quot;, navaid.course);&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== findNavaidsByFrequencyMHz()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = findNavaidsByFrequencyMHz([pos, ]freq[, type]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1572|t=Source}}&lt;br /&gt;
|text = Returns a vector conatining &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt; ghost objects for navaids that match a given frequency, sorted from nearest to furthest.&lt;br /&gt;
|param1 = pos&lt;br /&gt;
|param1text = Optional position to search around. If not given, the aircraft's current position will be used. Can be one of:&lt;br /&gt;
:* An &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;runway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;taxiway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fix&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;waypoint&amp;lt;/code&amp;gt; ghost type&lt;br /&gt;
:* A hash with ''lat'' and ''lon'' members&lt;br /&gt;
:* A geo.Coord object&lt;br /&gt;
:* Two numbers separated by a comma, as if the function is taking three arguments. Example: &amp;lt;code&amp;gt;findNavaidsByFrequencyMHz(lat, lon, freq, type);&amp;lt;/code&amp;gt;.&lt;br /&gt;
|param2 = freq&lt;br /&gt;
|param2text = Frequency, in megahertz, of the navaid to search for.&lt;br /&gt;
|param3 = type&lt;br /&gt;
|param3text = This will narrow the search to navaids of a certain type. Defaults to &amp;quot;all.&amp;quot; For the full list of accepted type arguments, see {{flightgear file|src/Navaids/positioned.cxx|l=127}}.&lt;br /&gt;
|example1 = var navaids = findNavaidsByFrequencyMHz(109.55);&lt;br /&gt;
foreach(var navaid; navaids){&lt;br /&gt;
    print(&amp;quot;--&amp;quot;);&lt;br /&gt;
    print(&amp;quot;ID: &amp;quot;, navaid.id); # prints info about the navaid&lt;br /&gt;
    print(&amp;quot;Name: &amp;quot;, navaid.name);&lt;br /&gt;
    print(&amp;quot;Latitude: &amp;quot;, navaid.lat);&lt;br /&gt;
    print(&amp;quot;Longitude: &amp;quot;, navaid.lon);&lt;br /&gt;
    print(&amp;quot;Elevation (AMSL): &amp;quot;, navaid.elevation, &amp;quot; m&amp;quot;);&lt;br /&gt;
    print(&amp;quot;Type: &amp;quot;, navaid.type);&lt;br /&gt;
    print(&amp;quot;Frequency: &amp;quot;, sprintf(&amp;quot;%.3f&amp;quot;, navaid.frequency / 100), &amp;quot; Mhz&amp;quot;);&lt;br /&gt;
    print(&amp;quot;Range: &amp;quot;, navaid.range_nm, &amp;quot; nm&amp;quot;);&lt;br /&gt;
    if(navaid.course) print(&amp;quot;Course: &amp;quot;, navaid.course);&lt;br /&gt;
    print(&amp;quot;--&amp;quot;);&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===findNavaidsByID()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = findNavaidsByID([pos, ]id[, type]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1600|t=Source}}&lt;br /&gt;
|text = Returns a vector containing &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt; ghost objects matching a given ID, sorted by range from a certain position.&lt;br /&gt;
|param1 = pos&lt;br /&gt;
|param1text = Optional position to search around. If not given, the aircraft's current position will be used. Can be one of:&lt;br /&gt;
:* An &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;runway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;taxiway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fix&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;waypoint&amp;lt;/code&amp;gt; ghost type&lt;br /&gt;
:* A hash with ''lat'' and ''lon'' members&lt;br /&gt;
:* A geo.Coord object&lt;br /&gt;
:* Two numbers separated by a comma, as if the function is taking three arguments. Example: &amp;lt;code&amp;gt;findNavaidsByID(lat, lon, id, type);&amp;lt;/code&amp;gt;.&lt;br /&gt;
|param2 = id&lt;br /&gt;
|param2text = Full or partial ID of the fix to search for.&lt;br /&gt;
:{{inote|1=Inputting a partial ID does not work correctly (see [http://forum.flightgear.org/viewtopic.php?f=30&amp;amp;t=28129 here]). It is best to just input a full ID.}}&lt;br /&gt;
|param3 = type&lt;br /&gt;
|param3text = This will narrow the search to navaids of a certain type. Defaults to &amp;quot;all.&amp;quot; For the full list of accepted type arguments, see {{flightgear file|src/Navaids/positioned.cxx|l=127}}.&lt;br /&gt;
|example1 = var navaid = findNavaidsByID(&amp;quot;MXW&amp;quot;);&lt;br /&gt;
navaid = navaid[0];&lt;br /&gt;
print(&amp;quot;ID: &amp;quot;, navaid.id); # prints info about 'MXW' (a VOR station)&lt;br /&gt;
print(&amp;quot;Name: &amp;quot;, navaid.name);&lt;br /&gt;
print(&amp;quot;Latitude: &amp;quot;, navaid.lat);&lt;br /&gt;
print(&amp;quot;Longitude: &amp;quot;, navaid.lon);&lt;br /&gt;
print(&amp;quot;Elevation (AMSL): &amp;quot;, navaid.elevation, &amp;quot; m&amp;quot;);&lt;br /&gt;
print(&amp;quot;Type: &amp;quot;, navaid.type);&lt;br /&gt;
print(&amp;quot;Frequency: &amp;quot;, sprintf(&amp;quot;%.3f&amp;quot;, navaid.frequency / 1000), &amp;quot; Mhz&amp;quot;);&lt;br /&gt;
print(&amp;quot;Range: &amp;quot;, navaid.range_nm, &amp;quot; nm&amp;quot;);&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===findNavaidsWithinRange()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = findNavaidsWithinRange([pos, ]range[, type]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1518|t=Source}}&lt;br /&gt;
|text = Returns a vector of &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt; ghost objects which are within a given range of a given position (by default the aircraft's current position). The results are sorted from closest to furthest.&lt;br /&gt;
|param1 = pos&lt;br /&gt;
|param1text = Optional position to search around. If not given, the aircraft's current position will be used. Can be one of:&lt;br /&gt;
:* An &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;runway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;taxiway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fix&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;waypoint&amp;lt;/code&amp;gt; ghost type&lt;br /&gt;
:* A hash with ''lat'' and ''lon'' members&lt;br /&gt;
:* A geo.Coord object&lt;br /&gt;
:* Two numbers separated by a comma, as if the function is taking three arguments. Example: &amp;lt;code&amp;gt;findNavaidsWithinRange(lat, lon, range, type);&amp;lt;/code&amp;gt;.&lt;br /&gt;
|param2 = range&lt;br /&gt;
|param2text = Mandatory number giving the range in nautical miles within which to search for navaids. Defaults to &amp;quot;all.&amp;quot; For the full list of accepted type arguments, see {{flightgear file|src/Navaids/positioned.cxx|l=127}}.&lt;br /&gt;
|param3 = type&lt;br /&gt;
|param3text = This will narrow the search to navaids of a certain type.&lt;br /&gt;
|example1text = Searches for navaids within 10 nm of [[KSFO]].&lt;br /&gt;
|example1 = var pos = airportinfo(&amp;quot;KSFO&amp;quot;);&lt;br /&gt;
var navs = findNavaidsWithinRange(pos, 10);&lt;br /&gt;
foreach(var nav; navs){&lt;br /&gt;
    print(nav.name, &amp;quot; (ID: &amp;quot;, nav.id, &amp;quot;)&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
|example2text = Searches for navaids within 10 nm of your current position.&lt;br /&gt;
|example2 = var navs = findNavaidsWithinRange(10);&lt;br /&gt;
foreach(var nav; navs){&lt;br /&gt;
    print(nav.name, &amp;quot; (ID: &amp;quot;, nav.id, &amp;quot;)&amp;quot;);&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===finddata()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = finddata(path);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=603|t=Source}}&lt;br /&gt;
|text = Takes a relative path and tries to return an absolute one. It works by appending the relative path to some paths and testing to see if they exist. As of FlightGear v3.7, these paths are the TerraSync directory (tested first) and [[$FG_ROOT]]. &lt;br /&gt;
|param1 = path&lt;br /&gt;
|param1text = A relative path as a string.&lt;br /&gt;
|example1 = var path = finddata(&amp;quot;Aircraft/Generic&amp;quot;);&lt;br /&gt;
print(path); # prints the absolute path to $FG_ROOT/Aircraft/Generic&lt;br /&gt;
|example2 = var path = finddata(&amp;quot;Airports&amp;quot;);&lt;br /&gt;
print(path); # prints the absolute path to &amp;lt;TerraSync dir&amp;gt;/Airports&lt;br /&gt;
|example3 = var path = finddata(&amp;quot;preferences.xml&amp;quot;);&lt;br /&gt;
print(path); # prints the absolute path to $FG_ROOT/preferences.xml&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===flightplan()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = flightplan([path]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1738|t=Source}}&lt;br /&gt;
|text = {{see also|Nasal Flightplan}}&lt;br /&gt;
Returns a flight plan object, either one for the current flight plan, or one loaded from a given path.&lt;br /&gt;
|param1 = path&lt;br /&gt;
|param1text = Optional path to flight plan XML file.&lt;br /&gt;
|example1text = Gets the active flight plan and gets the ID of the current waypoint. Note that this example requires a flight plan to be set in the [[Route Manager]] first.&lt;br /&gt;
|example1 = var fp = flightplan();&lt;br /&gt;
print(fp.getWP(fp.current).id);&lt;br /&gt;
|example2text = Creates a new flight plan from an XML file and prints the IDs of the waypoints. Note that this example requires a flight plan to have been created and saved as &amp;lt;tt&amp;gt;''[[$FG_HOME]]/fp-demo.xml''&amp;lt;/tt&amp;gt;.&lt;br /&gt;
|example2 = var path = getprop('/sim/fg-home') ~ '/fp-demo.xml';&lt;br /&gt;
var fp = flightplan(path);&lt;br /&gt;
for(var i = 0; i &amp;lt; fp.getPlanSize(); i += 1){&lt;br /&gt;
    print(fp.getWP(i).id);&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===geodinfo()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = geodinfo(lat, lon[, max_alt]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=981|t=Source}}&lt;br /&gt;
|text = Returns a vector containing two entries or &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; if no information could be obtained because the terrain tile wasn't loaded. The first entry in the vector is the elevation (in meters) for the given point, and the second is a hash with information about the assigned material (as defined in &amp;lt;tt&amp;gt;''[[$FG_ROOT]]/Materials''&amp;lt;/tt&amp;gt;), or &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; if there was no material information available (for example, because there is an untextured building at that location). The structure of the hash is as follows (see also {{readme file|materials}}):&lt;br /&gt;
* '''light_coverage:''' The coverage of a single point of light in m&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;.&lt;br /&gt;
* '''bumpiness:''' Normalized bumpiness factor for the material.&lt;br /&gt;
* '''load_resistance:''' The amount of pressure in N/m&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt; the material can withstand without deformation.&lt;br /&gt;
* '''solid:''' 1 (true) or false (0) depending on whether the material is solid or not.&lt;br /&gt;
* '''names:''' Vector of scenery types (usually generated by [[TerraGear]]) that will use this material. &lt;br /&gt;
* '''friction_factor:''' Normalized friction factor of the material.&lt;br /&gt;
* '''rolling_friction:''' The rolling friction coefficient of the material.&lt;br /&gt;
&lt;br /&gt;
Note that this function is a ''very'' CPU-intensive operation, particularly in FlightGear v2.4 and earlier. It is advised to use this function as little as possible.&lt;br /&gt;
|param1 = lat&lt;br /&gt;
|param1text = Latitude, inputted as a number.&lt;br /&gt;
|param2 = lon&lt;br /&gt;
|param2text = Longitude, inputted as a number.&lt;br /&gt;
|param3 = max_alt&lt;br /&gt;
|param3text = The altitude, in metres, from which the function will begin searching for the height of the terrain. Defaults to 10,000 metres. If the terrain is higher than this argument specifies, &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; will be returned.&lt;br /&gt;
|example1text = Dumps information about ground underneath the aircraft.&lt;br /&gt;
|example1 = var pos = geo.aircraft_position();&lt;br /&gt;
var info = geodinfo(pos.lat(), pos.lon());&lt;br /&gt;
debug.dump(info);&lt;br /&gt;
|example2text = Prints whether the ground underneath the aircraft is solid or is water.&lt;br /&gt;
|example2 = var pos = geo.aircraft_position();&lt;br /&gt;
var info = geodinfo(pos.lat(), pos.lon());&lt;br /&gt;
if (info != nil and info[1] != nil) {&lt;br /&gt;
    print(&amp;quot;The ground underneath the aircraft is &amp;quot;, info[1].solid == 1 ? &amp;quot;solid.&amp;quot; : &amp;quot;water.&amp;quot;);&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== geodtocart() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = geodtocart(lat, lon, alt);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=962|t=Source}}&lt;br /&gt;
|text = Converts {{wikipedia|geodetic coordinates}} (latitude, longitude, and altitude) to {{wikipedia|ECEF|Earth-centered, Earth-fixed}} coordinates (x, y and z). A vector is returned containing x, y, and z in metres. The equatorial radius of earth used is that defined by the {{wikipedia|WGS 84}} (6,378,137 metres). All argument are mandatory.&lt;br /&gt;
|param1 = lat&lt;br /&gt;
|param1text = Latitude, in degrees.&lt;br /&gt;
|param2 = lon&lt;br /&gt;
|param2text = Longitude, in degrees.&lt;br /&gt;
|param3 = alt&lt;br /&gt;
|param3text = Altitude, in metres.&lt;br /&gt;
|example1 = var (x, y, z) = geodtocart(0, 0, 0); # point is the intersection of the prime meridian and equator.&lt;br /&gt;
print(&amp;quot;x: &amp;quot;, x); # prints &amp;quot;x: 6378137&amp;quot;&lt;br /&gt;
print(&amp;quot;y: &amp;quot;, y); # prints &amp;quot;y: 0&amp;quot;&lt;br /&gt;
print(&amp;quot;z: &amp;quot;, z); # prints &amp;quot;y: 0&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== get_cart_ground_intersection()===&lt;br /&gt;
Introduced in 2017.2.1, see [[Terrain Detection]].&lt;br /&gt;
&lt;br /&gt;
===getprop()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = &amp;lt;nowiki&amp;gt;getprop(path[, path[, ...]]);&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=345|t=Source}}&lt;br /&gt;
|text = Returns the value of a node in the [[Property Tree]] or &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; if the node does not exist or the value is not a number (NaN).&lt;br /&gt;
|param1 = path&lt;br /&gt;
|param1text = There needs to be at least one argument, but there is no limit to the maximum amount of arguments that can be given. The arguments will be concatenated together to form a property tree path. The arguments must be strings, but in FlightGear v3.2 onwards, there is also support (added by {{flightgear commit|34ed79}}) for numeric arguments as indices. See example 2 below.&lt;br /&gt;
|example1 = print(&amp;quot;You have FlightGear v&amp;quot;, getprop(&amp;quot;/sim/version/flightgear&amp;quot;)); # prints FlightGear version&lt;br /&gt;
|example2text = Note that the example below will only work in FlightGear 3.2 and above.&lt;br /&gt;
|example2 = for(var i = 0; i &amp;lt; 8; i += 1){&lt;br /&gt;
    print(&amp;quot;View #&amp;quot;, i + 1, &amp;quot; is named &amp;quot;, getprop(&amp;quot;/sim/view&amp;quot;, i, &amp;quot;name&amp;quot;));&lt;br /&gt;
}&lt;br /&gt;
|example3text = Same as above, but is supported by all versions of FlightGear.&lt;br /&gt;
|example3 = for(var i = 0; i &amp;lt; 8; i += 1){&lt;br /&gt;
    print(&amp;quot;View #&amp;quot;, i + 1, &amp;quot; is named &amp;quot;, getprop(&amp;quot;/sim/view[&amp;quot; ~ i ~ &amp;quot;]/name&amp;quot;));&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
==== See also====&lt;br /&gt;
{{note| If you have to read/write the same property multiple times (e.g. in an update loop), it is more efficient to use a node object: &lt;br /&gt;
To get a Node rather than its value, use &amp;lt;code&amp;gt;props.globals.getNode()&amp;lt;/code&amp;gt; - see [[Nasal_library/props]]. }}&lt;br /&gt;
&lt;br /&gt;
===greatCircleMove()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = greatCircleMove([pos, ]course, dist);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1681|t=Source}}&lt;br /&gt;
|text = Calculates a new set of geodetic coordinates using inputs of course and distance, either from the aircraft's current position (by default) or from another set of coordinates. Returns a hash containing two members, ''lat'' and ''lon'' (latitude and longitude respectively).&lt;br /&gt;
|param1 = pos&lt;br /&gt;
|param1text = Optional position to calculate from. If not given, the aircraft's current position will be used. Can be one of:&lt;br /&gt;
:* An &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;runway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;taxiway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fix&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;waypoint&amp;lt;/code&amp;gt; ghost object.&lt;br /&gt;
:* A hash with ''lat'' and ''lon'' members&lt;br /&gt;
:* A &amp;lt;code&amp;gt;geo.Coord&amp;lt;/code&amp;gt; object&lt;br /&gt;
:* A lat/lon pair, that is, a pair of numbers (latitude followed by longitude) separated by a comma: &amp;lt;code&amp;gt;greatCircleMove(lat,lon, ...)&amp;lt;/code&amp;gt;.&lt;br /&gt;
|param2 = course&lt;br /&gt;
|param2text = Course to new set of coordinates, in degrees (in the range 0–360).&lt;br /&gt;
|param3 = dist&lt;br /&gt;
|param3text = Distance in nautical miles to the new set of coordinates.&lt;br /&gt;
|example1 = var pos = greatCircleMove(0,0, 0, 1);&lt;br /&gt;
debug.dump(pos); # print hash with coordinates&lt;br /&gt;
|example2 = var fix = findFixesByID(&amp;quot;POGIC&amp;quot;);&lt;br /&gt;
fix = fix[0];&lt;br /&gt;
var pos = greatCircleMove(fix, 45, 10);&lt;br /&gt;
debug.dump(pos); # print hash with coordinates&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===interpolate()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|private = _interpolate()&lt;br /&gt;
|syntax = &amp;lt;nowiki&amp;gt;interpolate(prop, value1, time1[, value2, time2[, ...]]);&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=522|t=Part 1}} {{!}} {{fgdata file|Nasal/globals.nas|t=Part 2}}&lt;br /&gt;
|text = Linearly interpolates a node in the property tree to a given value in a specified time. The value/time pairs will be run one after the other in the order that they are passed to the function. Note that the interpolation will continue even when the simulation is paused.&lt;br /&gt;
|param1 = prop&lt;br /&gt;
|param1text = String or &amp;lt;code&amp;gt;props.Node&amp;lt;/code&amp;gt; object that indicates a node in the property tree to be used.&lt;br /&gt;
|param2 = value''n''&lt;br /&gt;
|param2text = Target value to change the property to in the set amount of time. This should be a number.&lt;br /&gt;
|param3 = time''n''&lt;br /&gt;
|param3text = Time in seconds, that will be taken for the interpolation.&lt;br /&gt;
|example1text = Paste the code below into the Nasal Console and execute. Then, open the Property Browser and look for the property. Finally, run the code again, and watch the value of the property change.&lt;br /&gt;
|example1 = setprop(&amp;quot;/test&amp;quot;, 0); # (re-)set property&lt;br /&gt;
interpolate(&amp;quot;/test&amp;quot;,&lt;br /&gt;
    50, 5, # interpolate to 50 in 5 seconds&lt;br /&gt;
    10, 2, # interpolate to 10 in 2 seconds&lt;br /&gt;
    0, 5); # interpolate to 0 in 5 seconds&lt;br /&gt;
|example2 = # Apply the left brake at 20% per second&lt;br /&gt;
var prop = &amp;quot;controls/gear/brake-left&amp;quot;;&lt;br /&gt;
var dist = 1 - getprop(prop);&lt;br /&gt;
if (dist == 1) {&lt;br /&gt;
    interpolate(prop, 1, dist / 0.2);&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===isa()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = isa(object, class);&lt;br /&gt;
|source = {{fgdata file|Nasal/globals.nas|t=Source}}&lt;br /&gt;
|text = Checks if an object is an instance of, or inherits from, a second object (or class), returning 1 (true) if it is and 0 (false) if otherwise.&lt;br /&gt;
|param1 = object&lt;br /&gt;
|param1text = Object to check.&lt;br /&gt;
|param2 = class&lt;br /&gt;
|param2text = Class/object to check that '''object''' inherits from or is an instance of.&lt;br /&gt;
|example1 = var coord = geo.Coord.new();&lt;br /&gt;
if(isa(coord, geo.Coord)){&lt;br /&gt;
    print(&amp;quot;Variable 'coord' is an instance of class 'geo.Coord'&amp;quot;); # this one will be printed&lt;br /&gt;
} else {&lt;br /&gt;
    print(&amp;quot;Variable 'coord' is not an instance of class 'geo.Coord'&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
|example2 = var coord = geo.Coord.new();&lt;br /&gt;
if(isa(coord, props.Node)){&lt;br /&gt;
    print(&amp;quot;Variable 'coord' is an instance of class 'props.Node'&amp;quot;);&lt;br /&gt;
} else {&lt;br /&gt;
    print(&amp;quot;Variable 'coord' is not an instance of class 'props.Node'&amp;quot;); # this one will be printed&lt;br /&gt;
}&lt;br /&gt;
|example3text = The example below demonstrates checking of inheritance.&lt;br /&gt;
|example3 = var Const = {&lt;br /&gt;
    constant: 2,&lt;br /&gt;
    getConst: func {&lt;br /&gt;
        return me.constant;&lt;br /&gt;
    }&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
var Add = {&lt;br /&gt;
    new: func {&lt;br /&gt;
        return { parents: [Add, Const] };&lt;br /&gt;
    },&lt;br /&gt;
&lt;br /&gt;
    addToConst: func(a){&lt;br /&gt;
        return a * me.getConst();&lt;br /&gt;
    }&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
var m = Add.new();&lt;br /&gt;
print(m.addToConst(4));&lt;br /&gt;
&lt;br /&gt;
if(isa(m, Add)) print(&amp;quot;Variable 'm' is an instance of class 'Add'&amp;quot;); # will be printed&lt;br /&gt;
if(isa(m, Const)) print(&amp;quot;Variable 'm' is an instance of class 'Const'&amp;quot;); # will also be printed&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===logprint()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = &amp;lt;nowiki&amp;gt;logprint(priority[, msg[, msg[, ...]]]);&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=431|t=Source}}&lt;br /&gt;
|text = Concatenates a message and logs it with a given priority level. Unlike {{func link|print()}} and {{func link|printlog()}}, message outputted by this function will be logged in your &amp;lt;code&amp;gt;[[Commonly used debugging tools#fgfs.log|fgfs.log]]&amp;lt;/code&amp;gt; file as coming from the Nasal file itself rather than from {{flightgear file|src/Scripting/NasalSys.cxx}}.&lt;br /&gt;
|param1 = priority&lt;br /&gt;
|param1text = Number specifying the priority level of the outputted message:&lt;br /&gt;
:{{{!}} class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Number !! Debug type&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} 1 {{!!}} Bulk&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} 2 {{!!}} Debug&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} 3 {{!!}} Info&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} 4 {{!!}} Warn&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} 5 {{!!}} Alert&lt;br /&gt;
{{!}}}&lt;br /&gt;
|param2 = msg&lt;br /&gt;
|param2text = The message. There is no limit to the arguments you give give. They will be concatenated together before logging.&lt;br /&gt;
|example1 = # logs the value of pi to three decimal places with log level 3&lt;br /&gt;
logprint(3, &amp;quot;pi = &amp;quot;, sprintf(&amp;quot;%.3f&amp;quot;, math.pi));&lt;br /&gt;
|example2 = logprint(5, &amp;quot;Alert! This is an important message!&amp;quot;);&lt;br /&gt;
}}&lt;br /&gt;
{{note| &lt;br /&gt;
The following constants have been added to the development branch of FlightGear (&amp;quot;next&amp;quot;) and will be releases with FG 2020.1 so you won't have to remember the numbers anymore:&lt;br /&gt;
&lt;br /&gt;
LOG_BULK, LOG_WARN, LOG_DEBUG, LOG_INFO, LOG_ALERT, DEV_WARN, DEV_ALERT }}&lt;br /&gt;
&lt;br /&gt;
===magvar() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = magvar([pos]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1642|t=Source}}&lt;br /&gt;
|text = Returns the {{wikipedia|magnetic variation}} at a given set of coordinates. The table below gives the magnetic model used depending on the version of FlightGear.&lt;br /&gt;
{{{!}} class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! FlightGear versions !! Model !! Reference date&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} 3.6 and above {{!!}} {{wikipedia|World Magnetic Model}} (WMM) 2015 {{!!}} 1 January 2015&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} 0.9.11-pre1 to 3.4 {{!!}} WMM 2005 {{!!}} 1 January 2005&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} 0.7.3 to 0.9.10 {{!!}} WMM 2000 {{!!}} 1 January 2000&lt;br /&gt;
{{!}}}&lt;br /&gt;
|param1 = pos&lt;br /&gt;
|param1text = Optional position to calculate from. If not given, the aircraft's current position will be used. Can be one of:&lt;br /&gt;
:* An &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;runway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;taxiway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fix&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;waypoint&amp;lt;/code&amp;gt; ghost object.&lt;br /&gt;
:* A hash with ''lat'' and ''lon'' members&lt;br /&gt;
:* A &amp;lt;code&amp;gt;geo.Coord&amp;lt;/code&amp;gt; object&lt;br /&gt;
:* A lat/lon pair, that is, a pair of numbers (latitude followed by longitude) separated by a comma: &amp;lt;code&amp;gt;magvar(lat,lon)&amp;lt;/code&amp;gt;.&lt;br /&gt;
|example1 = print(magvar(0, 0)); # prints the magnetic variation at 0, 0&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===maketimer() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = maketimer(interval[, self], function);&lt;br /&gt;
|source = ''Implemented using the {{API Link|flightgear|class|TimerObj}} class.''&amp;lt;br&amp;gt;{{flightgear file|src/Scripting/NasalSys.cxx|l=90|t=Part 1}} {{!}} {{flightgear file|src/Scripting/NasalSys.cxx|l=533|t=Part 2}}&lt;br /&gt;
|version = 2.12&lt;br /&gt;
|commit = {{flightgear commit|ab939f|t=commit}}&lt;br /&gt;
|text = Returns a timer object containing the following methods and members:&lt;br /&gt;
* '''start()''': Starts the timer.&lt;br /&gt;
* '''stop()''': Stops the timer.&lt;br /&gt;
* '''restart(interval)''': Restarts the timer with the given interval.&lt;br /&gt;
* '''singleShot''': Bool showing whether the timer is only to be run once, or continuously until told to stop. Can be both set and read from (see examples).&lt;br /&gt;
* '''isRunning''': Read-only bool telling whether the timer is currently running.&lt;br /&gt;
* '''simulatedTime''': (FG 2017.1+; {{flightgear commit|0af316|t=commit}}) Bool telling whether the timer is using simulated time (which accounts for pause, etc.). Defaults to false (use real time). Can be both read and set. This cannot be changed while the timer is running.&lt;br /&gt;
Unlike {{func link|settimer()}}, which it replaces, &amp;lt;code&amp;gt;maketimer()&amp;lt;/code&amp;gt; provides more control over the timer. In addition, it can help reduce memory usage.&lt;br /&gt;
|param1 = interval&lt;br /&gt;
|param1text = Interval in seconds for the timer.&lt;br /&gt;
|param2 = self&lt;br /&gt;
|param2text = Optional parameter specifying what any &amp;lt;code&amp;gt;'''me'''&amp;lt;/code&amp;gt; references in the function being called will refer to.&lt;br /&gt;
|param3 = function&lt;br /&gt;
|param3text = Function to be called at the given interval.&lt;br /&gt;
|example1 = var timer = maketimer(1, func(){&lt;br /&gt;
    print(&amp;quot;Hello, World!&amp;quot;); # print &amp;quot;Hello, World!&amp;quot; once every second (call timer.stop() to stop it)&lt;br /&gt;
});&lt;br /&gt;
timer.start();&lt;br /&gt;
|example2 = var timer = maketimer(1, math, func(){&lt;br /&gt;
    print(me.math); # 'me' reference is the 'math' namespace&lt;br /&gt;
});&lt;br /&gt;
timer.singleShot = 1; # timer will only be run once&lt;br /&gt;
timer.start();&lt;br /&gt;
|example3 = var timer = maketimer(1, func(){&lt;br /&gt;
    print(&amp;quot;Hello, World!&amp;quot;); # print &amp;quot;Hello, World!&amp;quot; once every second (call timer.stop() to stop it)&lt;br /&gt;
});&lt;br /&gt;
timer.start();&lt;br /&gt;
print(timer.isRunning); # prints 1&lt;br /&gt;
|example4text = In the example below, &amp;quot;Hello, World!&amp;quot; will be printed after one second the first time, and after two seconds thereafter.&lt;br /&gt;
|example4 = var update = func(){&lt;br /&gt;
    print(&amp;quot;Hello, World!&amp;quot;);&lt;br /&gt;
    timer.restart(2); # restarts the timer with a two second interval&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var timer = maketimer(1, update);&lt;br /&gt;
timer.singleShot = 1;&lt;br /&gt;
timer.start();&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===maketimestamp()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = maketimestamp()&lt;br /&gt;
|source = ''Implemented using the {{API Link|flightgear|class|TimeStampObj}} class.''&amp;lt;br&amp;gt;{{flightgear file|src/Scripting/NasalSys.cxx|l=214|t=Part 1}} {{!}} {{flightgear file|src/Scripting/NasalSys.cxx|l=589|t=Part 2}}&lt;br /&gt;
|version = 2019.2&lt;br /&gt;
|commit = {{flightgear commit|7db06300|t=commit}}&lt;br /&gt;
|text = Returns a time stamp object to allow high resolution timing of Nasal operations. When created the timer will automatically be stamped. The object has the following methods:&lt;br /&gt;
* '''stamp()''': Resets the timing operation. Call this first.&lt;br /&gt;
* '''elapsedMSec()''': returns number of milliseconds elapsed since stamp() called. Resolution may vary depending on platform but is usually at least millisecond accuracy.&lt;br /&gt;
* '''elapsedUSec()''': returns number of microseconds elapsed since stamp() called. Resolution may vary depending on platform but is usually at least millisecond accuracy.&lt;br /&gt;
|&lt;br /&gt;
|example1text = In the example below the number of milliseconds elapsed will be printed.&lt;br /&gt;
|example1 = var timestamp = maketimestamp();&lt;br /&gt;
timestamp.stamp();&lt;br /&gt;
print(timestamp.elapsedMSec(), &amp;quot;ms elapsed&amp;quot;);&lt;br /&gt;
print(timestamp.elapsedMSec(), &amp;quot;ms elapsed&amp;quot;);&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===md5()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = md5(string);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=758|t=Source}}&lt;br /&gt;
|version = 3.2&lt;br /&gt;
|commit = {{flightgear commit|cfbf9e|t=commit}}&lt;br /&gt;
|text = Returns a the {{wikipedia|MD5}} hash (as a string) of the inputted string.&lt;br /&gt;
|param1 = string&lt;br /&gt;
|param1text = String the generate the hash of. Mandatory.&lt;br /&gt;
|example1text = The below code should output &amp;lt;code&amp;gt;65a8e27d8879283831b664bd8b7f0ad4&amp;lt;/code&amp;gt;.&lt;br /&gt;
|example1 = print(md5(&amp;quot;Hello, World!&amp;quot;));&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===navinfo()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = navinfo(lat, lon, type, id);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1453|t=Source}}&lt;br /&gt;
|text = Returns vector &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt; ghost objects matching the given '''type''' and '''id''' or &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; on error.&lt;br /&gt;
|param1 = lat ''and'' lon&lt;br /&gt;
|param1text = If given, the returned navaids will be put into order of ascending distance from the location.&lt;br /&gt;
|param2 = type&lt;br /&gt;
|param2text = Narrows the search to the given type. Must be one of &amp;quot;any,&amp;quot; &amp;quot;fix,&amp;quot; &amp;quot;vor,&amp;quot; &amp;quot;ndb,&amp;quot; &amp;quot;ils,&amp;quot; &amp;quot;dme,&amp;quot; or &amp;quot;tacan.&amp;quot; Defaults to the equivalent of &amp;quot;any.&amp;quot;&lt;br /&gt;
|param3 = id&lt;br /&gt;
|param3text = ID to search for. Note that, although all the parameters are technically optional, this parameter must be given, otherwise an empty vector will be returned.&lt;br /&gt;
|example1 = navinfo(&amp;quot;vor&amp;quot;); # returns all VORs&lt;br /&gt;
|example2 = navinfo(&amp;quot;HAM&amp;quot;); # return all navaids whose names start with &amp;quot;HAM&amp;quot;&lt;br /&gt;
|example3 = navinfo(&amp;quot;vor&amp;quot;, &amp;quot;HAM&amp;quot;); # return all VORs whose names start with &amp;quot;HAM&amp;quot;&lt;br /&gt;
|example4 = navinfo(34,48,&amp;quot;vor&amp;quot;,&amp;quot;HAM&amp;quot;); # return all VORs whose names start with &amp;quot;HAM&amp;quot; and sorted by distance relative to 34°, 48°&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===parse_markdown()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = parse_markdown(markdown);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=745|t=Part 1}} {{!}} {{simgear file|simgear/misc/SimpleMarkdown.cxx|t=Part 2}} &lt;br /&gt;
|version = 3.2&lt;br /&gt;
|text = Parses a string containing {{wikipedia|Markdown}} and returns the result as a string. As of FlightGear 2016.1, it is just a simple parser, and does the following:&lt;br /&gt;
* It strips whitespace from the beginning of the string.&lt;br /&gt;
* It supports [http://daringfireball.net/projects/markdown/syntax#p paragraphs and line breaks].&lt;br /&gt;
* It collapses whitespace.&lt;br /&gt;
* It converts unordered [http://daringfireball.net/projects/markdown/syntax#list lists] to use a bullet character (&amp;amp;bull;). Note that the bullet character is implemented in hexadecimal UTF-8 character bytes (&amp;lt;code&amp;gt;E2 80 A2&amp;lt;/code&amp;gt;), as so may not work properly when the being displayed in an encoding other than UTF-8.&lt;br /&gt;
|param1 = markdown&lt;br /&gt;
|param1text = String containing Markdown to be parsed.&lt;br /&gt;
|example1text = &lt;br /&gt;
Save the below code as &amp;lt;tt&amp;gt;''[[$FG_ROOT]]/gui/dialogs/test.xml''&amp;lt;/tt&amp;gt;, then run the Nasal code below it to open the dialog. To change the markdown to be parsed, simply change the code in the highlighted section, save it, and reload the GUI (&amp;lt;tt&amp;gt;Debug &amp;gt; Reload GUI&amp;lt;/tt&amp;gt;).&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot; highlight=&amp;quot;41-50&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;PropertyList&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;name&amp;gt;test&amp;lt;/name&amp;gt;&lt;br /&gt;
&amp;lt;layout&amp;gt;vbox&amp;lt;/layout&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;group&amp;gt;&lt;br /&gt;
    &amp;lt;layout&amp;gt;hbox&amp;lt;/layout&amp;gt;&lt;br /&gt;
    &lt;br /&gt;
    &amp;lt;empty&amp;gt;&lt;br /&gt;
        &amp;lt;stretch&amp;gt;true&amp;lt;/stretch&amp;gt;&lt;br /&gt;
    &amp;lt;/empty&amp;gt;&lt;br /&gt;
    &lt;br /&gt;
    &amp;lt;text&amp;gt;&lt;br /&gt;
        &amp;lt;label&amp;gt;parse_markdown() test dialog&amp;lt;/label&amp;gt;&lt;br /&gt;
    &amp;lt;/text&amp;gt;&lt;br /&gt;
    &lt;br /&gt;
    &amp;lt;empty&amp;gt;&lt;br /&gt;
        &amp;lt;stretch&amp;gt;true&amp;lt;/stretch&amp;gt;&lt;br /&gt;
    &amp;lt;/empty&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    &amp;lt;button&amp;gt;&lt;br /&gt;
        &amp;lt;legend&amp;gt;&amp;lt;/legend&amp;gt;&lt;br /&gt;
        &amp;lt;pref-width&amp;gt;16&amp;lt;/pref-width&amp;gt;&lt;br /&gt;
        &amp;lt;pref-height&amp;gt;16&amp;lt;/pref-height&amp;gt;&lt;br /&gt;
        &amp;lt;binding&amp;gt;&lt;br /&gt;
            &amp;lt;command&amp;gt;dialog-close&amp;lt;/command&amp;gt;&lt;br /&gt;
        &amp;lt;/binding&amp;gt;&lt;br /&gt;
    &amp;lt;/button&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/group&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;canvas&amp;gt;&lt;br /&gt;
    &amp;lt;name&amp;gt;Canvas plot&amp;lt;/name&amp;gt;&lt;br /&gt;
    &amp;lt;stretch&amp;gt;true&amp;lt;/stretch&amp;gt;&lt;br /&gt;
    &amp;lt;pref-width&amp;gt;400&amp;lt;/pref-width&amp;gt;&lt;br /&gt;
    &amp;lt;pref-height&amp;gt;300&amp;lt;/pref-height&amp;gt;&lt;br /&gt;
    &amp;lt;nasal&amp;gt;&lt;br /&gt;
        &amp;lt;load&amp;gt;&amp;lt;![CDATA[&lt;br /&gt;
var text = 'Items:&lt;br /&gt;
* apples&lt;br /&gt;
* oranges&lt;br /&gt;
* pears&lt;br /&gt;
&lt;br /&gt;
Some text.&lt;br /&gt;
Some more items:&lt;br /&gt;
* apples&lt;br /&gt;
* oranges&lt;br /&gt;
* pears';&lt;br /&gt;
&lt;br /&gt;
var parsed = parse_markdown(text);&lt;br /&gt;
&lt;br /&gt;
var root_canvas = canvas.get(cmdarg());&lt;br /&gt;
root_canvas.setColorBackground(255, 255, 255);&lt;br /&gt;
var root = root_canvas.createGroup();&lt;br /&gt;
&lt;br /&gt;
var text_dis = root.createChild(&amp;quot;text&amp;quot;)&lt;br /&gt;
    .setText(parsed)&lt;br /&gt;
    .setTranslation(5, 5)&lt;br /&gt;
    .setFont(&amp;quot;LiberationFonts\LiberationSans-Regular.ttf&amp;quot;)&lt;br /&gt;
    .setFontSize(15)&lt;br /&gt;
    .setColor(0, 0, 0)&lt;br /&gt;
    .setDrawMode(canvas.Text.TEXT)&lt;br /&gt;
    .setAlignment(&amp;quot;left-top&amp;quot;);&lt;br /&gt;
        ]]&amp;gt;&amp;lt;/load&amp;gt;&lt;br /&gt;
    &amp;lt;/nasal&amp;gt;&lt;br /&gt;
&amp;lt;/canvas&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/PropertyList&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|example1 = fgcommand(&amp;quot;dialog-show&amp;quot;, {&amp;quot;dialog-name&amp;quot;: &amp;quot;test&amp;quot;});&lt;br /&gt;
|example2text = The example below parses Markdown and outputs it in a HTML document. The parsed text is placed in &amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot; inline&amp;gt;&amp;lt;pre&amp;gt;&amp;lt;/pre&amp;gt;&amp;lt;/syntaxhighlight&amp;gt; tags. To change the Markdown to be parsed, simply edit the variable &amp;lt;tt&amp;gt;markdown&amp;lt;/tt&amp;gt; at the top of the code.&lt;br /&gt;
|example2 = &amp;lt;nowiki&amp;gt;var markdown = 'Items:&lt;br /&gt;
* apples&lt;br /&gt;
* oranges&lt;br /&gt;
* pears&lt;br /&gt;
&lt;br /&gt;
Some text.&lt;br /&gt;
Some more items:&lt;br /&gt;
* apples&lt;br /&gt;
* oranges&lt;br /&gt;
* pears';&lt;br /&gt;
&lt;br /&gt;
var parsed = parse_markdown(markdown);&lt;br /&gt;
&lt;br /&gt;
debug.dump(parsed);&lt;br /&gt;
&lt;br /&gt;
var path = string.normpath(getprop(&amp;quot;/sim/fg-home&amp;quot;) ~ '/Export/parse_markdown()-test.html');&lt;br /&gt;
&lt;br /&gt;
var file = io.open(path, &amp;quot;w&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
var html = &amp;quot;&amp;lt;!DOCTYPE html&amp;gt;\n\n&amp;lt;html&amp;gt;\n\n&amp;lt;head&amp;gt;\n\t&amp;lt;meta charset=\&amp;quot;UTF-8\&amp;quot;&amp;gt;\n\t&amp;lt;title&amp;gt;parse_markdown() test generated by Nasal&amp;lt;/title&amp;gt;\n&amp;lt;/head&amp;gt;\n\n&amp;lt;body&amp;gt;\n\t&amp;lt;pre&amp;gt;&amp;quot; ~ parsed ~ &amp;quot;&amp;lt;/pre&amp;gt;\n&amp;lt;/body&amp;gt;\n\n&amp;lt;/html&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
io.write(file, html);&lt;br /&gt;
io.close(file);&lt;br /&gt;
print(&amp;quot;Done, file ready for viewing (&amp;quot; ~ path ~ &amp;quot;)&amp;quot;);&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===parsexml()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = &amp;lt;nowiki&amp;gt;parsexml(path[, start[, end[, data[, pro_ins]]]]);&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=710|t=Source}}&lt;br /&gt;
|text = This function is an interface into the built-in [http://expat.sourceforge.net/ Expat XML parser]. The absolute path to the file is returned as string, or &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; is returned on error.&lt;br /&gt;
|param1 = path&lt;br /&gt;
|param1text = Mandatory absolute path to the XML file to be parsed.&lt;br /&gt;
|param2 = start&lt;br /&gt;
|param2text = Optional callback function that will be called for every starting tag. The function should take two argument: the tag name and a hash containing attributes.&lt;br /&gt;
|param3 = end&lt;br /&gt;
|param3text = Optional callback function that will be called for every ending tag. The function should take one argument: the tag name.&lt;br /&gt;
|param4 = data&lt;br /&gt;
|param4text = Optional callback function that will be called for every piece of data within a set of tags. The function should take one argument: the data as a string.&lt;br /&gt;
|param5 = pro_ins&lt;br /&gt;
|param5text = Optional callback function that will be called for every {{wikipedia|Processing Instruction|processing instruction}}. The function should take two argument: the target and the data string.&lt;br /&gt;
|example1text = Save the below XML code in &amp;lt;tt&amp;gt;''[[$FG_HOME]]/Export/demo.xml''&amp;lt;/tt&amp;gt;. Then, execute the Nasal code below in the Nasal Console. The XML will be parsed and each bit of the code will be printed.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;?xml-stylesheet type=&amp;quot;text/xsl&amp;quot; href=&amp;quot;style.xsl&amp;quot;?&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;foo&amp;gt;&lt;br /&gt;
  &amp;lt;blah type=&amp;quot;string&amp;quot;&amp;gt;&amp;lt;![CDATA[ &amp;lt;sender&amp;gt;John Smith&amp;lt;/sender&amp;gt; ]]&amp;gt;&amp;lt;/blah&amp;gt;&lt;br /&gt;
  &amp;lt;blah2 type=&amp;quot;string&amp;quot;&amp;gt;Orange &amp;amp;amp; lemons&amp;lt;/blah2&amp;gt;&lt;br /&gt;
&amp;lt;/foo&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|example1 = var start = func(name, attr){&lt;br /&gt;
    print(&amp;quot;Starting tag: '&amp;quot;, name, &amp;quot;'&amp;quot;);&lt;br /&gt;
    foreach(var a; keys(attr)){&lt;br /&gt;
        print(&amp;quot;\twith attribute &amp;quot;, a, '=&amp;quot;', attr[a], '&amp;quot;');&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var end = func(name){&lt;br /&gt;
    print(&amp;quot;Ending tag: '&amp;quot;, name, &amp;quot;'&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var data = func(data){&lt;br /&gt;
    print(&amp;quot;Data = '&amp;quot;, data, &amp;quot;'&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var pro_instr = func(target, data){&lt;br /&gt;
    print(&amp;quot;Processing instruction: target = '&amp;quot;, target, &amp;quot;', data = '&amp;quot;, data, &amp;quot;'&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
parsexml(getprop(&amp;quot;/sim/fg-home&amp;quot;) ~ '/Export/demo.xml', start, end, data, pro_instr);&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===print()===&lt;br /&gt;
{{Note|As of 07/2020, we are in the process of slowly deprecating and removing Nasal print() (in fgdata) where possible in favor of log [[#logprint()]] which takes a priority level.&lt;br /&gt;
&lt;br /&gt;
This is part of the goal that we achieve zero output at log-level at alert, and everything shown at ‘warn; is really a warning, not just information.&amp;lt;ref&amp;gt;https://sourceforge.net/p/flightgear/mailman/message/37042224/&amp;lt;/ref&amp;gt;}}&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = &amp;lt;nowiki&amp;gt;print(data[, data[, ...]]);&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=415|t=Source}}&lt;br /&gt;
|text = Concatenates its arguments and then prints it to the terminal and the [[Commonly used debugging tools#fgfs.log|log]]. Note that a newline is automatically added.&lt;br /&gt;
|param1 = data&lt;br /&gt;
|param1text = Data to print. Only strings and numbers can be printed; other data types will not be. There many be any number of arguments; they will just be concatenated together.&lt;br /&gt;
|example1 = print(&amp;quot;Just&amp;quot;, &amp;quot; a &amp;quot;, &amp;quot;test&amp;quot;); # prints &amp;quot;Just a test&amp;quot;&lt;br /&gt;
|example2 = print(&amp;quot;pi = &amp;quot;, math.pi); # prints &amp;quot;pi = 3.141592...&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== printf() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = &amp;lt;nowiki&amp;gt;printf(format[, arg[, arg, [...]]]);&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|source = {{fgdata file|Nasal/globals.nas|t=Source}}&lt;br /&gt;
|text = Creates and prints a formatted string. For a description of its arguments, see {{func link|sprintf()}} (it is, in fact, implemented using &amp;lt;code&amp;gt;sprintf()&amp;lt;/code&amp;gt;).&lt;br /&gt;
|example1 = printf(&amp;quot;In hexadecimal, 100000 = %X&amp;quot;, 186A0); # prints &amp;quot;In hexadecimal, 100000 = 186A0&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===printlog()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = &amp;lt;nowiki&amp;gt;printlog(level, data[, data[, ...]]);&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|source = {{fgdata file|Nasal/globals.nas|t=Source}}&lt;br /&gt;
|text = Prints the given message with the given log level. If the log level is higher or equal to &amp;lt;code&amp;gt;/sim/logging/priority&amp;lt;/code&amp;gt;, it is printed.&lt;br /&gt;
|param1 = level&lt;br /&gt;
|param1text = Mandatory log level as a string. Must be one of &amp;quot;none,&amp;quot; &amp;quot;bulk,&amp;quot; &amp;quot;debug,&amp;quot; &amp;quot;info,&amp;quot; &amp;quot;warn,&amp;quot; or &amp;quot;alert.&amp;quot; Note that &amp;quot;none&amp;quot; will mean that the message will never be printed.&lt;br /&gt;
|param2 = data&lt;br /&gt;
|param2text = Data to be printed. Only strings and numbers will be printed; others will not be. There may be any number of arguments; they will just be concatenated together.&lt;br /&gt;
|example1 = printlog(&amp;quot;alert&amp;quot;, &amp;quot;This is an alert&amp;quot;); # message will be printed&lt;br /&gt;
|example2 = printlog(&amp;quot;info&amp;quot;, &amp;quot;Just informing you about something&amp;quot;); # message will be printed only if log level is set to &amp;quot;info&amp;quot; or less&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===rand()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = rand();&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=554|t=Source}}&lt;br /&gt;
|text = Returns a random floating point number between 0 (inclusive) and 1 (exclusive). It takes no arguments.&lt;br /&gt;
|example1 = print(rand()); # prints random number&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===registerFlightPlanDelegate()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = registerFlightPlanDelegate(init_func);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1879|t=Source}}&lt;br /&gt;
|text = Registers a flight plan delegate. See &amp;lt;tt&amp;gt;''{{fgdata file|Nasal/route_manager.nas|t=$FG_ROOT/Nasal/route_manager.nas}}''&amp;lt;/tt&amp;gt; for examples.&lt;br /&gt;
|param1 = init_func&lt;br /&gt;
|param1text = Initialization function which will be called during FlightGear's startup.&lt;br /&gt;
}}&lt;br /&gt;
===removecommand()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = removecommand(cmd);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=668|t=Source}}&lt;br /&gt;
|text = Removes the given fgcommand. Returns &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt;.&lt;br /&gt;
{{caution|This will remove '''any''' [[fgcommands|fgcommand]], even those implemented in C++, so use with caution!}}&lt;br /&gt;
|param1 = cmd&lt;br /&gt;
|param1text = String specifying the name of the command to remove.&lt;br /&gt;
|example1 = addcommand(&amp;quot;hello&amp;quot;, func(){&lt;br /&gt;
    print(&amp;quot;Hello&amp;quot;);&lt;br /&gt;
});&lt;br /&gt;
fgcommand(&amp;quot;hello&amp;quot;); # &amp;quot;Hello&amp;quot; will be printed&lt;br /&gt;
removecommand(&amp;quot;hello&amp;quot;); # removes it&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===removelistener()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = removelistener(id);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=1384|t=Part 1}} {{!}} {{flightgear file|src/Scripting/NasalSys.cxx|l=506|t=Part 2}}&lt;br /&gt;
|text = Removes and deactivates the given listener and returns the number of listeners left or &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; on error.&lt;br /&gt;
{{note|It is good practice to remove listeners when they are not required anymore. This prevents the listeners reducing FlightGear's run performance.}}&lt;br /&gt;
|param1 = id&lt;br /&gt;
|param1text = ID of listener as returned by {{func link|setlistener()}}.&lt;br /&gt;
|example1 = var ls = setlistener(&amp;quot;/sim/test&amp;quot;, func(){&lt;br /&gt;
    print(&amp;quot;Property '/sim/test' has been changed&amp;quot;);&lt;br /&gt;
});&lt;br /&gt;
setprop(&amp;quot;/sim/test&amp;quot;, &amp;quot;blah&amp;quot;); # trigger listener&lt;br /&gt;
var rem = removelistener(ls); # remove listener&lt;br /&gt;
print(&amp;quot;There are &amp;quot;, rem, &amp;quot; listeners remaining&amp;quot;);&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===resolvepath()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = resolvepath(path);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=604|t=Source}}&lt;br /&gt;
|text = Takes a relative path as a string and uses [[SimGear]]'s path-resolving framework to return an absolute path as a string. If the path could not be resolved, an empty string is returned. See [[Resolving Paths]] for a detailed description of the algorithm. This function can also be used to check if a file exists.&lt;br /&gt;
|param1 = path&lt;br /&gt;
|param1text = Relative path to be completed.&lt;br /&gt;
|example1 = print(resolvepath(&amp;quot;Nasal/globals.nas&amp;quot;)); # prints the equivalent of $FG_ROOT/Nasal/globals.nas&lt;br /&gt;
|example2 = print(resolvepath(&amp;quot;blah&amp;quot;)); # prints nothing; could not be resolved&lt;br /&gt;
|example3 = var file_path = resolvepath(&amp;quot;Aircraft/SenecaII/some-file&amp;quot;);&lt;br /&gt;
if (file_path != &amp;quot;&amp;quot;){&lt;br /&gt;
    gui.popupTip(&amp;quot;some-file found&amp;quot;, 2);&lt;br /&gt;
} else {&lt;br /&gt;
    gui.popupTip(&amp;quot;some-file not found&amp;quot;, 2);&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===setlistener()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = &amp;lt;nowiki&amp;gt;setlistener(node, code[, init[, type]]);&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|private = _setlistener()&lt;br /&gt;
|source = {{flightgear file|src/Scripting/Nasal|l=499|t=Part 1}} {{!}} {{flightgear file|src/Scripting/Nasal|l=1350|t=Part 2}}&amp;lt;br&amp;gt;''Listener implemented using the {{API Link|flightgear|class|FGNasalListener}} class.''&lt;br /&gt;
|text = Creates a listener which will be triggered when the given property is changed (depending on the '''type'''). A unique integer ID is returned; this can later be used as the argument to {{func link|removelistener()}}.&lt;br /&gt;
{{note|Listeners are known to be a source of resource leaks. To avoid this, please take measures such as:&lt;br /&gt;
* Using {{func link|removelistener()}} when they are not needed any more.&lt;br /&gt;
* Using a single initialization listener.&lt;br /&gt;
* Avoiding tying listeners to properties that are rapidly updated (e.g., many times per frame).&lt;br /&gt;
}}&lt;br /&gt;
|param1 = node&lt;br /&gt;
|param1text = Mandatory string or &amp;lt;code&amp;gt;props.Node&amp;lt;/code&amp;gt; object pointing to a property in the [[Property Tree]].&lt;br /&gt;
|param2 = code&lt;br /&gt;
|param2text = Mandatory callback function to execute when the listener is triggered. The function can take up to four arguments in the following order:&lt;br /&gt;
* '''changed''': a &amp;lt;code&amp;gt;props.Node&amp;lt;/code&amp;gt; object pointing to the changed node.&lt;br /&gt;
* '''listen''': a &amp;lt;code&amp;gt;props.Node&amp;lt;/code&amp;gt; object pointing to the listened-to node. Note that this argument maybe different depending on the '''type'''.&lt;br /&gt;
* '''mode''': an integer telling how the listener was triggered. 0 means that the value was changed. 1 means that a child property was added. -1 means that a child property was removed.&lt;br /&gt;
* '''is_child''': boolean telling whether '''changed''' is a child property of the listened-to '''node''' or not. 1 (true) if it is, 0 (false) otherwise.&lt;br /&gt;
|param3 = init&lt;br /&gt;
|param3text = If set to 1 (true), the listener will additionally be triggered when it is created. This argument is optional and defaults to 0 (false).&lt;br /&gt;
|param4 = type&lt;br /&gt;
|param4text = Integer specifying the listener's behavior. 0 means that the listener will only trigger when the property is changed. 1 means that the trigger will always be triggered when the property is written to. 2 will mean that the listener will be triggered even if child properties are modified. This argument is optional and defaults to 1.&lt;br /&gt;
|example1 = var prop = props.globals.initNode(&amp;quot;/sim/test&amp;quot;, &amp;quot;&amp;quot;, &amp;quot;STRING&amp;quot;); # create property&lt;br /&gt;
var id = setlistener(&amp;quot;/sim/test&amp;quot;, func(n){ # create listener&lt;br /&gt;
    print(&amp;quot;Value: &amp;quot;, n.getValue());&lt;br /&gt;
});&lt;br /&gt;
setprop(&amp;quot;/sim/test&amp;quot;, &amp;quot;blah&amp;quot;); # trigger listener&lt;br /&gt;
removelistener(id); # remove listener&lt;br /&gt;
prop.remove(); # remove property&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===setprop()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = &amp;lt;nowiki&amp;gt;setprop(path[, path[, ...]], value);&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=385|t=Source}}&lt;br /&gt;
|text = Sets the value of a property in the [[Property Tree]]. If the property does not exist, it will be created. Returns 1 (true) on success or 0 (false) if there was an error.&lt;br /&gt;
{{note|If you want to remove a property, you will have to use one of the &amp;lt;code&amp;gt;props&amp;lt;/code&amp;gt; helpers:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
props.globals.getNode(&amp;quot;foo/bar&amp;quot;).remove(); # take out the complete node&lt;br /&gt;
props.globals.getNode(&amp;quot;foo&amp;quot;).removeChild(&amp;quot;bar&amp;quot;); # take out a certain child node&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
|param1 = path&lt;br /&gt;
|param1text = There needs to be at least one '''path''' argument, but there is no limit to the maximum amount that can be given. They will be concatenated together to form a property tree path. The arguments must be strings, but in FlightGear v3.2 onwards, there also is support (added by {{flightgear commit|34ed79}}) for numeric arguments as indices. See example 2 below.&lt;br /&gt;
|param2 = value&lt;br /&gt;
|param2text = Value to write to the given property. Must be either a string or a number.&lt;br /&gt;
|example1 = setprop(&amp;quot;/sim/demo&amp;quot;, &amp;quot;This is a demo&amp;quot;);&lt;br /&gt;
|example2text = Note that the example below will only work in FlightGear 3.2 and above.&lt;br /&gt;
|example2 = for(var i = 0; i &amp;lt; 3; i += 1){&lt;br /&gt;
    setprop(&amp;quot;/sim/demo&amp;quot;, i, &amp;quot;Demo #&amp;quot; ~ i));&lt;br /&gt;
}&lt;br /&gt;
|example3text = Same as above, but is supported by all versions of FlightGear.&lt;br /&gt;
|example3 = for(var i = 0; i &amp;lt; 3; i += 1){&lt;br /&gt;
    setprop(&amp;quot;/sim/demo[&amp;quot; ~ i ~ &amp;quot;]&amp;quot;, &amp;quot;Demo #&amp;quot; ~ i));&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
====See also====&lt;br /&gt;
{{note| If you have to read/write the same property multiple times (e.g. in an update loop), it is more efficient to use a node object: &lt;br /&gt;
To get a Node rather than its value, use &amp;lt;code&amp;gt;props.globals.getNode()&amp;lt;/code&amp;gt; - see [[Nasal_library/props]]. }}&lt;br /&gt;
&lt;br /&gt;
===settimer() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = settimer(function, delta[, realtime]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=499|t=Part 1}} {{!}} {{flightgear file|src/Scripting/NasalSys.cxx|l=1286|t=Part 2}}&lt;br /&gt;
|text = Runs the given function a specified amount of seconds after the current time. Returns &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt;.&lt;br /&gt;
{{note|Improper use of &amp;lt;code&amp;gt;settimer()&amp;lt;/code&amp;gt; may cause resource leaks. It is also highly recommended that the newer {{func link|maketimer()}} should be used instead of this function.}} &lt;br /&gt;
|param1 = function&lt;br /&gt;
|param1text = Mandatory function that will be called. It may be necessary to enclose code in an anonymous function (see example).&lt;br /&gt;
|param2 = delta&lt;br /&gt;
|param2text = Mandatory amount of time in seconds after which the function will be called.&lt;br /&gt;
|param3 = realtime&lt;br /&gt;
|param3text = If 1 (true), &amp;quot;real time&amp;quot; will be used instead of &amp;quot;simulation time.&amp;quot; Defaults to 0 (false). Note that if &amp;quot;simulation time&amp;quot; is used, the timer will not run while FlightGear is paused.&lt;br /&gt;
|example1 = var myFunc = func(){&lt;br /&gt;
    print(&amp;quot;Hello&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
settimer(myFunc, 2); # runs myFunc after 2 seconds&lt;br /&gt;
|example2 = var sqr = func(a){&lt;br /&gt;
    return a * a;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
settimer(func(){&lt;br /&gt;
    print(sqr(2)); # will print 4 after 2 seconds&lt;br /&gt;
}, 2);&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===srand() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = srand();&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=559|t=Source}}&lt;br /&gt;
|text = Makes the pseudorandom number generator (see {{func link|rand()}}) generate a new {{wikipedia|random seed|noicon=1}} based on time. Returns 0.&lt;br /&gt;
}}&lt;br /&gt;
===systime()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = systime();&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=770|t=Source}}&lt;br /&gt;
|text = Returns the {{wikipedia|Unix time}} (seconds since since 00:00:00 UTC, 1/1/1970) as a floating point number with high resolution. This function is useful for benchmarking purposes (see example 2).&lt;br /&gt;
{{note|1=High resolution timers under Windows can produce inaccurate or fixed sub-millisecond results.&amp;lt;ref&amp;gt;{{cite web|url=http://forum.flightgear.org/viewtopic.php?f=30&amp;amp;t=29259|title=Nasal: systime() ??!?|author=Necolatis|date=Apr 2nd, 2016}}&amp;lt;/ref&amp;gt; This is due to the underlying {{func link|GetSystemTimeAsFileTime()|link=https://msdn.microsoft.com/en-us/library/windows/desktop/ms724397(v=vs.85).aspx}} API call, which depends on hardware availability of suitable high resolution timers. See also [https://msdn.microsoft.com/en-us/library/windows/desktop/dn553408(v=vs.85).aspx Acquiring high-resolution time stamps]}}&lt;br /&gt;
|example1 = print(&amp;quot;Unix time: &amp;quot;, systime()); # prints Unix time&lt;br /&gt;
|example2 = var myFunc = func(){&lt;br /&gt;
    for(var i = 0; i &amp;lt;= 10; i += 1){&lt;br /&gt;
        print(&amp;quot;Interation #&amp;quot;, i);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
var t = systime(); # record time&lt;br /&gt;
myFunc(); # run function&lt;br /&gt;
var t2 = systime(); # record new time&lt;br /&gt;
print(&amp;quot;myFunc() took &amp;quot;, t2 - t, &amp;quot; seconds&amp;quot;); # print result&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===thisfunc()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = thisfunc();&lt;br /&gt;
|source = {{fgdata file|Nasal/globals.nas|t=Source}}&lt;br /&gt;
|text = Returns the function from which this function is called. This allows a function to reliably and safely call itself from within a closure.&lt;br /&gt;
|example1 = var stringify_vec = func(input){&lt;br /&gt;
    if (typeof(input) == &amp;quot;scalar&amp;quot;){&lt;br /&gt;
        return sprintf(&amp;quot;%s&amp;quot;, input);&lt;br /&gt;
    } elsif (typeof(input) == &amp;quot;vector&amp;quot;) {&lt;br /&gt;
        if (size(input) == 0) return &amp;quot;[]&amp;quot;;&lt;br /&gt;
        var this = thisfunc();&lt;br /&gt;
        var buffer = &amp;quot;[&amp;quot;;&lt;br /&gt;
        for(var i = 0; i &amp;lt; size(input); i += 1){&lt;br /&gt;
            buffer ~= this(input[i]);&lt;br /&gt;
            if (i == size(input) - 1) {&lt;br /&gt;
                buffer ~= &amp;quot;]&amp;quot;;&lt;br /&gt;
            } else {&lt;br /&gt;
                buffer ~= &amp;quot;, &amp;quot;;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        return buffer;&lt;br /&gt;
    } else {&lt;br /&gt;
        die(&amp;quot;stringify_vec(): Error! Invalid input. Must be a vector or scalar&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var test_vec = [&amp;quot;a&amp;quot;, &amp;quot;b&amp;quot;, &amp;quot;c&amp;quot;, 1, 2, 3];&lt;br /&gt;
debug.dump(stringify_vec(test_vec)); # prints &amp;quot;[a, b, c, 1, 2, 3]&amp;quot;&lt;br /&gt;
test_vec = [];&lt;br /&gt;
debug.dump(stringify_vec(test_vec)); # prints &amp;quot;[]&amp;quot;&lt;br /&gt;
test_vec = {};&lt;br /&gt;
debug.dump(stringify_vec(test_vec)); # will throw an error&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===tileIndex() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = tileIndex();&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1720|t=Source}}&lt;br /&gt;
|text = Returns the index of the tile at the aircraft's current position as a string. This corresponds to the name of the STG file of the tile. For example, at [[KSFO]], this would be &amp;lt;code&amp;gt;942050&amp;lt;/code&amp;gt;, corresponding to &amp;lt;tt&amp;gt;''[[$FG_SCENERY]]/Terrain/w130n30/w123n3/942050.stg''&amp;lt;/tt&amp;gt;.&lt;br /&gt;
|example1 = print(tileIndex()); # print index&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== tilePath()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = tilePath();&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1712|t=Source}}&lt;br /&gt;
|text = Returns the base path of the tile at the aircraft's current position as a string. For example, at KSFO, this would be &amp;lt;code&amp;gt;w130n30/w123n3&amp;lt;/code&amp;gt;, corresponding to &amp;lt;tt&amp;gt;''[[$FG_SCENERY]]/Terrain/w130n30/w123n3''&amp;lt;/tt&amp;gt;.&lt;br /&gt;
|example1 = print(tilePath()); # print path&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== values()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = values(hash);&lt;br /&gt;
|source = {{fgdata file|Nasal/globals.nas|t=Source}}&lt;br /&gt;
|text = Returns a vector containing the values of the given hash.&lt;br /&gt;
|param1 = hash&lt;br /&gt;
|param1text = Mandatory hash to get the values of.&lt;br /&gt;
|example1 = var hash = {&lt;br /&gt;
    &amp;quot;a&amp;quot;: 1,&lt;br /&gt;
    &amp;quot;b&amp;quot;: 2,&lt;br /&gt;
    &amp;quot;c&amp;quot;: 3&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
foreach(var val; values(hash)){&lt;br /&gt;
    print(val);&lt;br /&gt;
}&lt;br /&gt;
|example2text = The below example does exactly the same thing as the above example, but does not use &amp;lt;code&amp;gt;values()&amp;lt;/code&amp;gt;:&lt;br /&gt;
|example2 = var hash = {&lt;br /&gt;
    &amp;quot;a&amp;quot;: 1,&lt;br /&gt;
    &amp;quot;b&amp;quot;: 2,&lt;br /&gt;
    &amp;quot;c&amp;quot;: 3&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
foreach(var key; keys(hash)){&lt;br /&gt;
    print(hash[key]);&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
==Extension functions new in FG 2020.1 ==&lt;br /&gt;
The following functions have been added to the nasal core library and will be released with FlightGear version 2020.1. &lt;br /&gt;
Before the release they are available in the development branch &amp;quot;next&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
===isfunc()===&lt;br /&gt;
Returns 1 if type or argument is a function, otherwise 0.&lt;br /&gt;
&lt;br /&gt;
===isghost()===&lt;br /&gt;
Returns 1 if type or argument is a ghost, otherwise 0.&lt;br /&gt;
&lt;br /&gt;
=== ishash()===&lt;br /&gt;
Returns 1 if type or argument is a hash, otherwise 0.&lt;br /&gt;
&lt;br /&gt;
===isint()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = isint(x);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|t=Source}}&lt;br /&gt;
|text = Returns 1 if argument has a numeric value and x == floor(x), e.g. integer.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===isnum()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = isnum(x);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|t=Source}}&lt;br /&gt;
|text = Returns 1 if typeof(x) is &amp;quot;scalar&amp;quot; and x has a numeric value otherwise 0. &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===isscalar()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = isscalar(x);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|t=Source}}&lt;br /&gt;
|text = Returns 1 if type or argument is a scalar (string or numeric), otherwise (vector, hash, func, ...) it returns 0. This is useful to check if a variable can be converted to string e.g. when useing the string concat operator &amp;quot;~&amp;quot;. &lt;br /&gt;
|example1 = var a = &amp;quot;foo&amp;quot;; &lt;br /&gt;
var b=42;&lt;br /&gt;
if (isscalar(a) and isscalar(b)) print(a~b);&lt;br /&gt;
if (isstr(a)) print(&amp;quot;a is a string&amp;quot;);&lt;br /&gt;
if (isint(b)) print(&amp;quot;b is an integer&amp;quot;);&lt;br /&gt;
# if (isscalar(a))... is equivalent to if (typeof(a) == &amp;quot;scalar&amp;quot;)...&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===isstr()===&lt;br /&gt;
Returns 1 if type or argument is a string, otherwise 0. &lt;br /&gt;
&lt;br /&gt;
===isvec()===&lt;br /&gt;
Returns 1 if type or argument is a vector, otherwise 0.&lt;br /&gt;
&lt;br /&gt;
===vecindex()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = vecindex(vector, value);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|t=Source}}&lt;br /&gt;
|text = Returns the index of value or nil, if value is not found in vector.&lt;br /&gt;
|example1=&lt;br /&gt;
var myvector = [&amp;quot;apple&amp;quot;, &amp;quot;bananna&amp;quot;, &amp;quot;coconut&amp;quot;];&lt;br /&gt;
# to check if a vector contains a certain value compare vecindex to nil&lt;br /&gt;
if (vecindex(myvector, &amp;quot;apple&amp;quot;) != nil) {&lt;br /&gt;
    print(&amp;quot;found apple&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
# WARNING: this will not work as desired because index is 0&lt;br /&gt;
if (vecindex(myvector, &amp;quot;apple&amp;quot;)) {&lt;br /&gt;
    print(&amp;quot;found apple&amp;quot;);&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
==Variables==&lt;br /&gt;
Various global constants (technically variables) are provided for use in converting between different units. They are all found in {{fgdata file|Nasal/globals.nas|text=$FG_ROOT/Nasal/globals.nas}}.&lt;br /&gt;
&lt;br /&gt;
===D2R===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var radians = degrees * D2R;&lt;br /&gt;
|text = Converts an angle from degrees to radians when multiplied by the angle in degrees. Equal to &amp;lt;code&amp;gt;π / 180&amp;lt;/code&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
===FPS2KT===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var knots = feet_per_second * FPS2KT;&lt;br /&gt;
|text = Converts a velocity from feet per second to knots when multiplied by the velocity in feet per second. Approximately equal to 0.5925.&lt;br /&gt;
}}&lt;br /&gt;
===FT2M===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var metres = feet * FT2M;&lt;br /&gt;
|text = Converts a length from feet to metres when multiplied by the length in feet. Equal to 0.3048.&lt;br /&gt;
}}&lt;br /&gt;
===GAL2L ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var litres = gallons * GAL2L;&lt;br /&gt;
|text = Converts a volume from US liquid gallons to litres when multiplied by the volume in gallons. Approximately equal to 3.7854.&lt;br /&gt;
}}&lt;br /&gt;
===IN2M===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var metres = inches * IN2M;&lt;br /&gt;
|text = Converts a length from inches to metres when multiplied by the length in inches. Equal to 0.0254.&lt;br /&gt;
}}&lt;br /&gt;
===KG2LB===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var pounds = kilograms * KG2LB;&lt;br /&gt;
|text = Converts a mass from kilograms to pounds when multiplied by the mass in kilograms. Approximately equal to 2.2046.&lt;br /&gt;
}}&lt;br /&gt;
===KT2FPS===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var feet_per_second = knots * KT2FPS;&lt;br /&gt;
|text = Converts a velocity from knots to feet per second when multiplied by the velocity in knots. Approximately equal to 1.6878.&lt;br /&gt;
}}&lt;br /&gt;
===KT2MPS===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var metres_per_second = knots * KT2MPS;&lt;br /&gt;
|text = Converts a velocity from knots to metres per second when multiplied by the velocity in knots. Approximately equal to 0.5144.&lt;br /&gt;
}}&lt;br /&gt;
===L2GAL===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var gallons = litres * L2GAL;&lt;br /&gt;
|text = Converts a volume from litres to US liquid gallons when multiplied by the volume in litres. Approximately equal to 0.2642.&lt;br /&gt;
}}&lt;br /&gt;
===LB2KG===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var kilograms = pounds * LB2KG;&lt;br /&gt;
|text = Converts a mass from pounds to kilograms when multiplied by the mass in pounds. Approximately equal to 0.4536.&lt;br /&gt;
}}&lt;br /&gt;
===M2FT ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var feet = metres * M2FT;&lt;br /&gt;
|text = Converts a length from metres to feet when multiplied by the length in metres. Approximately equal to 3.2808.&lt;br /&gt;
}}&lt;br /&gt;
===M2IN===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var inches = metres * M2IN;&lt;br /&gt;
|text = Converts a length from metres to inches when multiplied by the length in metres. Approximately equal to 39.3701.&lt;br /&gt;
}}&lt;br /&gt;
===M2NM ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var nautical_miles = metres * M2NM;&lt;br /&gt;
|text = Converts a length from metres to nautical miles when multiplied by the length in metres. Approximately equal to 0.00054.&lt;br /&gt;
}}&lt;br /&gt;
===MPS2KT ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var knots = metres_per_second * MPS2KT;&lt;br /&gt;
|text = Converts a velocity from metres per second to knots when multiplied by the velocity in metres per second. Approximately equal to 1.9438.&lt;br /&gt;
}}&lt;br /&gt;
===NM2M ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var metres = nautical_miles * NM2M;&lt;br /&gt;
|text = Converts a length from nautical miles to metres when multiplied by the length in nautical miles. Equal to 1,852.&lt;br /&gt;
}}&lt;br /&gt;
===R2D===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var degrees = radians * R2D;&lt;br /&gt;
|text = Converts an angle from radians to degrees when multiplied by the angle in radians. Equal to &amp;lt;code&amp;gt;180 / π&amp;lt;/code&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Appendix}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Nasal namespaces}}&lt;/div&gt;</summary>
		<author><name>Clm76</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Nasal_library&amp;diff=138672</id>
		<title>Nasal library</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Nasal_library&amp;diff=138672"/>
		<updated>2023-11-12T10:18:36Z</updated>

		<summary type="html">&lt;p&gt;Clm76: Add findCommByFrequencyMHz()&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Nasal Navigation|nocat=1}}&lt;br /&gt;
This page documents the global '''library functions and variables''' of FlightGear's built-in scripting language, [[Nasal]]. This includes ''[[#Core library functions|core library functions]]'', which were included in Nasal before its integration into FlightGear, the ''[[#Extension functions|extension functions]]'', which have been subsequently added, and are specifically designed for FlightGear, and the ''[[#variables|global variables]]'', which are conversion variables, added with extension functions, for converting between units. The relevant folders in [[Git]] are:&lt;br /&gt;
* {{flightgear file|src/Scripting}}&lt;br /&gt;
* {{simgear file|simgear/nasal}}&lt;br /&gt;
&lt;br /&gt;
All these functions and variables are in the global namespace, that is, they are directly accessible (e.g., one can call &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; inline&amp;gt;magvar()&amp;lt;/syntaxhighlight&amp;gt; instead of &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; inline&amp;gt;namespace.magvar()&amp;lt;/syntaxhighlight&amp;gt;). However, if a namespace must be used, &amp;lt;code&amp;gt;globals&amp;lt;/code&amp;gt; is the correct namespace, but using it is not recommended. For a more complete explanation, see [[Nasal Namespaces in-depth]].&lt;br /&gt;
&lt;br /&gt;
{{tip|Copy &amp;amp; paste the examples into your [[Nasal Console]] and execute them to see what they do.|width=70%}}&lt;br /&gt;
&lt;br /&gt;
== Core library functions ==&lt;br /&gt;
This is the list of the basic '''core library functions.''' Most of these functions were part of the original Nasal library (before its integration in to FlightGear), while some have been added or changed over time.  See also:&lt;br /&gt;
* http://plausible.org/nasal/lib.html ([http://web.archive.org/web/20101010094553/http://plausible.org/nasal/lib.html archive])&lt;br /&gt;
* {{simgear file|simgear/nasal/lib.c}} ([http://sourceforge.net/p/flightgear/simgear/ci/next/log/?path=/simgear/nasal/lib.c history])&lt;br /&gt;
&lt;br /&gt;
=== append() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = append(vector, element[, element[, ...]]);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=42|t=Source}}&lt;br /&gt;
|text = This function appends, or adds, the given element(s) to the end of the vector given in the first argument.  Returns the vector operated on.&lt;br /&gt;
|param1 = vector&lt;br /&gt;
|param1text = The vector to which the arguments will be appended.&lt;br /&gt;
|param2 = element&lt;br /&gt;
|param2text = An element to be added to the vector.&lt;br /&gt;
|example1 = &lt;br /&gt;
var vector = [1, 2, 3]; # Initialize the vector&lt;br /&gt;
append(vector, 4); # Append the number 4 to the end of the vector&lt;br /&gt;
debug.dump(vector); # Print the contents of the vector&lt;br /&gt;
|example2 = &lt;br /&gt;
var vector = [1, 2, 3]; # Initialize the vector&lt;br /&gt;
append(vector, 4, 5, 6); # Append the numbers 4, 5, and 6 to the end of the vector&lt;br /&gt;
debug.dump(vector); # Print the contents of the vector&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== bind() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = bind(function, locals[, outer_scope]);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=502|t=Source}}&lt;br /&gt;
|text = This creates a new function object. A function in Nasal is three things: the actual code, a hash/namespace of local variables available to the function namespace, and the closure object of that namespace. These correspond to the three arguments respectively.&lt;br /&gt;
|param1 = function&lt;br /&gt;
|param1text = Function to evaluate.&lt;br /&gt;
|param2 = locals&lt;br /&gt;
|param2text = Hash containing values that will become the namespace (first closure) for the function.&lt;br /&gt;
|param3 = outer_scope&lt;br /&gt;
|param3text = Optional function which is bound to the next closure. This can be bound to yet another, making a linked list.&lt;br /&gt;
|example1 = # This is a namespace/hash with a single member, named &amp;quot;key,&amp;quot; which is initialized to 12 &lt;br /&gt;
var Namespace = {&lt;br /&gt;
    key: 12&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
# This is different namespace/hash containing a function&lt;br /&gt;
# dividing a variable &amp;quot;key&amp;quot; (which is unavailable/nil in this namespace) by 2&lt;br /&gt;
var AnotherNamespace = {&lt;br /&gt;
    ret: func {&lt;br /&gt;
        key /= 2;&lt;br /&gt;
    }&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
# To see that key is not available, try to call AnotherNamespace.ret() first&lt;br /&gt;
call(AnotherNamespace.ret, [], nil, nil, var errors = []);&lt;br /&gt;
if(size(errors)){&lt;br /&gt;
    print(&amp;quot;Key could not be divided/resolved!&amp;quot;);&lt;br /&gt;
    debug.printerror(errors);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# Associate the AnotherNamespace.ret() function with the first namespace&lt;br /&gt;
# so that &amp;quot;key&amp;quot; is now available&lt;br /&gt;
var function = bind(AnotherNamespace.ret, Namespace);&lt;br /&gt;
&lt;br /&gt;
# Invoke the new function&lt;br /&gt;
function();&lt;br /&gt;
&lt;br /&gt;
# Print out the value of Namespace.key&lt;br /&gt;
# It was changed to 12 from 6 by AnotherNamespace.ret()&lt;br /&gt;
print(Namespace.key);&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== call() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = call(func[, args[, me[, locals[, error]]]);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=247|t=Source}}&lt;br /&gt;
|text = Calls the given function with the given arguments and returns the result.  This function is very useful as it allows much more control over function calls and catches any errors or {{func link|die()}} calls that would normally trigger run-time errors cancelling execution of the script otherwise. &lt;br /&gt;
|param1 = func&lt;br /&gt;
|param1text = Function to execute.&lt;br /&gt;
|param2 = args&lt;br /&gt;
|param2text = Vector containing arguments to give to the called function.&lt;br /&gt;
|param3 = me&lt;br /&gt;
|param3text = &amp;lt;code&amp;gt;'''me'''&amp;lt;/code&amp;gt; reference for the function call (i.e., for method calls). If given, this will override any &amp;lt;code&amp;gt;'''me'''&amp;lt;/code&amp;gt; value existing in the namespace (locals argument).&lt;br /&gt;
|param4 = locals&lt;br /&gt;
|param4text = A hash with key/value pairs that will be available to the called function, typically used as the namespace for the function to be called.&lt;br /&gt;
|param5 = error&lt;br /&gt;
|param5text = A vector to append errors to.  If the called function generates an error, the error, place, and line will be written to this.  These errors can be printed using {{func link|printerror()|debug}}.&lt;br /&gt;
|example1 =&lt;br /&gt;
# prints &amp;quot;Called from call()&amp;quot;&lt;br /&gt;
call(func {&lt;br /&gt;
    print(&amp;quot;Called from call()&amp;quot;);&lt;br /&gt;
});&lt;br /&gt;
|example2 =&lt;br /&gt;
# prints &amp;quot;a = 1 : b = 2&lt;br /&gt;
call(func(a, b){&lt;br /&gt;
        print(&amp;quot;a = &amp;quot;, a, &amp;quot; : b = &amp;quot;, b);&lt;br /&gt;
    },&lt;br /&gt;
    [1, 2]&lt;br /&gt;
);&lt;br /&gt;
|example3 =&lt;br /&gt;
var Hash = {&lt;br /&gt;
    new: func {&lt;br /&gt;
        var m = { parents: [Hash] };&lt;br /&gt;
&lt;br /&gt;
        m.el1 = &amp;quot;string1&amp;quot;;&lt;br /&gt;
        m.el2 = &amp;quot;string2&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
        return m;&lt;br /&gt;
    }&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
# prints &amp;quot;me.el1 = string1&amp;quot;, then &amp;quot;me.el2 = string2&amp;quot; on the next line&lt;br /&gt;
call(func(a, b){        &lt;br /&gt;
        print(&amp;quot;me.el&amp;quot;, a, &amp;quot; = &amp;quot;, me[&amp;quot;el&amp;quot; ~ a]);      &lt;br /&gt;
        print(&amp;quot;me.el&amp;quot;, b, &amp;quot; = &amp;quot;, me[&amp;quot;el&amp;quot; ~ b]);&lt;br /&gt;
    },&lt;br /&gt;
    [1, 2],&lt;br /&gt;
    Hash.new()&lt;br /&gt;
);&lt;br /&gt;
|example4 =&lt;br /&gt;
# prints the value of math.pi&lt;br /&gt;
call(func {&lt;br /&gt;
        print(pi);&lt;br /&gt;
    }, nil, nil, &lt;br /&gt;
    math&lt;br /&gt;
);&lt;br /&gt;
|example5 =&lt;br /&gt;
call(func {&lt;br /&gt;
        print(math.ip); # math.ip doesn't exist&lt;br /&gt;
    }, nil, nil, nil,&lt;br /&gt;
    var errs = []&lt;br /&gt;
);&lt;br /&gt;
debug.printerror(errs); # The error is caught and printed using debug.printerror()&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== caller() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = caller([level]);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=404|t=Source}}&lt;br /&gt;
|text = Returns a vector containing a record from the current call stack.  The level numbering starts from the currently executing function (level 0).  Level 1 (the default) is the caller of the current function, and so on.&lt;br /&gt;
&lt;br /&gt;
The result is a four-element vector containing '''[0]''' a hash of local variables, '''[1]''' the function object, '''[2]''' the full source file name (incl. path) and '''[3]''' the line number. &lt;br /&gt;
|param1 = level&lt;br /&gt;
|param1text = Optional integer specifying the stack level to return a result from.  Defaults to 1 (i.e. the caller of the currently executing function).&lt;br /&gt;
|example1 =&lt;br /&gt;
var myFunction = func(a, b){&lt;br /&gt;
    debug.dump(caller(0)[0]); # prints a hash of local variables, including arguments a and b&lt;br /&gt;
    return 2 * 2;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;2 x 2 = &amp;quot;, myFunction(2, 2));&lt;br /&gt;
|example2 =&lt;br /&gt;
var get_arg_value = func(){&lt;br /&gt;
    print(&amp;quot;Argument to myFunc = &amp;quot;, caller(1)[0]['a']); # print the value of myFunc's single argument, using caller()&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
var myFunc = func(a){&lt;br /&gt;
    get_arg_value();&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
myFunc(3);&lt;br /&gt;
|example3text = This is a real example taken from {{fgdata file|Nasal/canvas/MapStructure.nas}}.  Function &amp;lt;code&amp;gt;r()&amp;lt;/code&amp;gt; (above the TODOs) returns a hash with the key/value pairs as per its arguments. For example, something like this is returned: &amp;lt;code&amp;gt;{ name: &amp;quot;&amp;lt;name&amp;gt;&amp;quot;, vis: 1, zindex: nil }&amp;lt;/code&amp;gt;.&lt;br /&gt;
|example3 =&lt;br /&gt;
var MapStructure_selfTest = func() {&lt;br /&gt;
	var temp = {};&lt;br /&gt;
	temp.dlg = canvas.Window.new([600,400],&amp;quot;dialog&amp;quot;);&lt;br /&gt;
	temp.canvas = temp.dlg.createCanvas().setColorBackground(1,1,1,0.5);&lt;br /&gt;
	temp.root = temp.canvas.createGroup();&lt;br /&gt;
	var TestMap = temp.root.createChild(&amp;quot;map&amp;quot;);&lt;br /&gt;
	TestMap.setController(&amp;quot;Aircraft position&amp;quot;);&lt;br /&gt;
	TestMap.setRange(25); # TODO: implement zooming/panning via mouse/wheel here, for lack of buttons :-/&lt;br /&gt;
	TestMap.setTranslation(&lt;br /&gt;
		temp.canvas.get(&amp;quot;view[0]&amp;quot;)/2,&lt;br /&gt;
		temp.canvas.get(&amp;quot;view[1]&amp;quot;)/2&lt;br /&gt;
	);&lt;br /&gt;
	var r = func(name,vis=1,zindex=nil) return caller(0)[0];&lt;br /&gt;
	# TODO: we'll need some z-indexing here, right now it's just random&lt;br /&gt;
	# TODO: use foreach/keys to show all layers in this case by traversing SymbolLayer.registry direclty ?&lt;br /&gt;
	# maybe encode implicit z-indexing for each lcontroller ctor call ? - i.e. preferred above/below order ?&lt;br /&gt;
	foreach(var type; [r('TFC',0),r('APT'),r('DME'),r('VOR'),r('NDB'),r('FIX',0),r('RTE'),r('WPT'),r('FLT'),r('WXR'),r('APS'), ] ) &lt;br /&gt;
		TestMap.addLayer(factory: canvas.SymbolLayer, type_arg: type.name,&lt;br /&gt;
					visible: type.vis, priority: type.zindex,&lt;br /&gt;
		);&lt;br /&gt;
}; # MapStructure_selfTest&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== chr() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = chr(code);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=175|t=Source}}&lt;br /&gt;
|text = Returns a character as per the single argument. Extended ASCII is supported (see http://www.asciitable.com/ for a list of supported characters), although this may vary between different systems.  For a list of the most commonly used characters, see the {{wikipedia|ASCII#ASCII printable code chart|ASCII printable code chart}} ('''Dec''' column). The following table lists supported control characters, along with their equivalent control characters in Nasal strings.  {{Note|In Nasal, only strings enclosed with double-quotes (&amp;lt;code&amp;gt;&amp;quot;string&amp;quot;&amp;lt;/code&amp;gt;) supports control chracters.  Strings in single quotes (&amp;lt;code&amp;gt;'string'&amp;lt;/code&amp;gt;) do not.}}&lt;br /&gt;
{{{!}} class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Code !! Name !! Equivalent to&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} 10 {{!!}} {{Wikipedia|Newline}} {{!!}} &amp;lt;code&amp;gt;\n&amp;lt;/code&amp;gt;&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} 9 {{!!}} {{Wikipedia|Tab key#Tab characters|Horizontal tab}} {{!!}} &amp;lt;code&amp;gt;\t&amp;lt;/code&amp;gt;&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} 13 {{!!}} {{Wikipedia|Carriage return}} {{!!}} &amp;lt;code&amp;gt;\r&amp;lt;/code&amp;gt;&lt;br /&gt;
{{!}}}&lt;br /&gt;
|param1 = code&lt;br /&gt;
|param1text = Integer character code for the desired glyph.&lt;br /&gt;
|example1 = print(&amp;quot;Code 65 = &amp;quot;, chr(65)); # prints &amp;quot;Code 65 = A&amp;quot;&lt;br /&gt;
|example2text = This example displays all of the characters in a list, in the format &amp;lt;code&amp;gt;Code '''n''' = &amp;gt;'''char'''&amp;lt;&amp;lt;/code&amp;gt;, '''n''' being the index, and '''char''' being the character.&lt;br /&gt;
|example2 =&lt;br /&gt;
for(var i = 0; i &amp;lt;= 255; i += 1){&lt;br /&gt;
    print(&amp;quot;Code &amp;quot;, i, &amp;quot; = &amp;gt;&amp;quot;, chr(i), &amp;quot;&amp;lt;&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== closure() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = closure(func[, level]);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=421|t=Source}}&lt;br /&gt;
|text = Returns the hash table containing the lexical namespace of the given function. The level numbering start with level 0 being the namespace of '''func'''. &lt;br /&gt;
|param1 = func&lt;br /&gt;
|param1text = Function to evaluate.&lt;br /&gt;
|param2 = level&lt;br /&gt;
|param2text = Optional integer specifying the scope level.  Defaults to 0 (the namespace of '''func''').&lt;br /&gt;
|example1 =&lt;br /&gt;
var get_math_e = func {&lt;br /&gt;
    return e; # return the value of math.e&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var myFunction = bind(get_math_e, math); # bind get_math_e to the math namespace, so that math.e is immediately available to get_math_e&lt;br /&gt;
debug.dump(closure(myFunction)); # print the namespace of get_math_e&lt;br /&gt;
&lt;br /&gt;
print(myFunction());&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== cmp() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = cmp(a, b);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=112|t=Source}}&lt;br /&gt;
|text = Compares two strings, returning -1 if '''a''' is less than '''b''', 0 if they are identical and 1 if '''a''' is greater than '''b'''. &lt;br /&gt;
|param1 = a&lt;br /&gt;
|param1text = First string argument for comparison.&lt;br /&gt;
|param2 = b&lt;br /&gt;
|param2text = Second string argument for comparison.&lt;br /&gt;
|example1 = print(cmp(&amp;quot;1&amp;quot;, &amp;quot;two&amp;quot;)); # prints -1&lt;br /&gt;
|example2 = print(cmp(&amp;quot;string&amp;quot;, &amp;quot;string&amp;quot;)); # prints 0&lt;br /&gt;
|example3 = print(cmp(&amp;quot;one&amp;quot;, &amp;quot;2&amp;quot;)); # prints 1&lt;br /&gt;
|example4 = print(cmp(&amp;quot;string1&amp;quot;, &amp;quot;string2&amp;quot;)); # prints -1&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== compile() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = compile(code[, filename]);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=220|t=Source}}&lt;br /&gt;
|text = Compiles the specified code string and returns a function object bound to the current lexical context.  If there is an error, the function dies, with the argument to {{func link|die()}} being '''filename'''.&lt;br /&gt;
|param1 = code&lt;br /&gt;
|param1text = String containing Nasal code to be compiled.&lt;br /&gt;
|param2 = filename&lt;br /&gt;
|param2text = Optional string used for error messages/logging. Defaults to &amp;lt;code&amp;gt;&amp;lt;compile&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
|example1 = &lt;br /&gt;
var myCode = 'print(&amp;quot;hello&amp;quot;);';&lt;br /&gt;
var helloFunc = compile(myCode, &amp;quot;myCode&amp;quot;);&lt;br /&gt;
helloFunc();&lt;br /&gt;
|example2text = &amp;lt;code&amp;gt;compile&amp;lt;/code&amp;gt; is very convenient to support Nasal loaded from other files.  For instance, [[PropertyList XML files]] (such as GUI dialogs) may contain embedded Nasal sections that need to be parsed, processed and compiled.  For an example of how to do this, save the below XML code as &amp;lt;tt&amp;gt;''[[$FG_ROOT]]/gui/dialogs/test.xml''&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?xml version=&amp;quot;1.0&amp;quot;?&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;PropertyList&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nasal&amp;gt;&amp;lt;![CDATA[&lt;br /&gt;
print(&amp;quot;You have FlightGear v&amp;quot;, getprop(&amp;quot;/sim/version/flightgear&amp;quot;));&lt;br /&gt;
]]&amp;gt;&amp;lt;/nasal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/PropertyList&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Now, start FlightGear and execute this code in the [[Nasal Console]].&lt;br /&gt;
|example2 =&lt;br /&gt;
# Build the path&lt;br /&gt;
var FGRoot = getprop(&amp;quot;/sim/fg-root&amp;quot;);&lt;br /&gt;
var filename = &amp;quot;/gui/dialogs/test.xml&amp;quot;;&lt;br /&gt;
var path = FGRoot ~ filename;&lt;br /&gt;
&lt;br /&gt;
var blob = io.read_properties(path);&lt;br /&gt;
var script = blob.getValues().nasal; # Get the nasal string&lt;br /&gt;
&lt;br /&gt;
# Compile the script.  We're passing the filename here for better runtime diagnostics &lt;br /&gt;
var code = call(func {&lt;br /&gt;
    compile(script, filename);&lt;br /&gt;
}, nil, nil, var compilation_errors = []);&lt;br /&gt;
&lt;br /&gt;
if(size(compilation_errors)){&lt;br /&gt;
    die(&amp;quot;Error compiling code in: &amp;quot; ~ filename);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# Invoke the compiled script, equivalent to code(); &lt;br /&gt;
# We're using call() here to detect errors:&lt;br /&gt;
call(code, [], nil, nil, var runtime_errors = []);&lt;br /&gt;
&lt;br /&gt;
if(size(runtime_errors)){&lt;br /&gt;
    die(&amp;quot;Error calling code compiled loaded from: &amp;quot; ~ filename);&lt;br /&gt;
}&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== contains() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = contains(hash, key);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=184|t=Source}}&lt;br /&gt;
|text = Returns 1 (True) if the hash contains the specified key, or 0 (False) if not.&lt;br /&gt;
|param1 = hash&lt;br /&gt;
|param1text = The hash to search in.&lt;br /&gt;
|param2 = key&lt;br /&gt;
|param2text = The scalar to be searched for, contained as a key in the hash.&lt;br /&gt;
|example1 =&lt;br /&gt;
# Initialize a hash&lt;br /&gt;
var hash = {&lt;br /&gt;
    element: &amp;quot;value&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
print(contains(hash, &amp;quot;element&amp;quot;) ? &amp;quot;Yes&amp;quot; : &amp;quot;No&amp;quot;); # This will print &amp;quot;Yes&amp;quot;&lt;br /&gt;
|example2 =&lt;br /&gt;
# Initialize a hash&lt;br /&gt;
var hash = {&lt;br /&gt;
    element: &amp;quot;value&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
print(contains(hash, &amp;quot;element2&amp;quot;) ? &amp;quot;Yes&amp;quot; : &amp;quot;No&amp;quot;); # This will print &amp;quot;No&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===contains()===&lt;br /&gt;
{{Requires commit|desc=Vector support|details=see {{Merge-request|project=fgdata|id=305}}|commit=ee39abbd3b70c9b6d5e3a1c4ccedddaac1a92b11|repo=Simgear}}&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = contains(vector, item);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=184|t=Source}}&lt;br /&gt;
|text = Returns 1 (True) if the vector contains the specified item, or 0 (False) if not.&lt;br /&gt;
|param1 = vector&lt;br /&gt;
|param1text = The vector to search in.&lt;br /&gt;
|param2 = item&lt;br /&gt;
|param2text = The object to be searched for in the vector.&lt;br /&gt;
|example1 =&lt;br /&gt;
# Initialize a hash&lt;br /&gt;
var vec = [&amp;quot;element&amp;quot;, &amp;quot;foo&amp;quot;];&lt;br /&gt;
print(contains(hash, &amp;quot;element&amp;quot;) ? &amp;quot;Yes&amp;quot; : &amp;quot;No&amp;quot;); # This will print &amp;quot;Yes&amp;quot;&lt;br /&gt;
|example2 =&lt;br /&gt;
# Initialize a hash&lt;br /&gt;
var vec = [&amp;quot;element&amp;quot;, &amp;quot;foo&amp;quot;];&lt;br /&gt;
print(contains(hash, &amp;quot;element2&amp;quot;) ? &amp;quot;Yes&amp;quot; : &amp;quot;No&amp;quot;); # This will print &amp;quot;No&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===delete() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = delete(hash, key);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=83|t=Source}}&lt;br /&gt;
|text = Deletes the key from the hash if it exists. Operationally, this is NOT identical to setting the hash value specified by the key to &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; as the key will stay in the hash (at least for a while). This variant potentially frees storage by deleting the reference to the key and by shrinking the hash.  Returns the hash that has been operated on.&lt;br /&gt;
|param1 = hash&lt;br /&gt;
|param1text = The hash from which to delete the key.&lt;br /&gt;
|param2 = key&lt;br /&gt;
|param2text = The scalar to be deleted, contained as a key in the hash.&lt;br /&gt;
|example1 =&lt;br /&gt;
# Initialize the hash&lt;br /&gt;
var hash = {&lt;br /&gt;
    element1: &amp;quot;value1&amp;quot;,&lt;br /&gt;
    element2: &amp;quot;value2&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
delete(hash, &amp;quot;element1&amp;quot;); # Delete element1&lt;br /&gt;
debug.dump(hash); # prints the hash, which is now minus element1&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===die()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = die(error);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=288|t=Source}}&lt;br /&gt;
|text = Terminates execution and unwinds the stack.  The place and the line will be added to the '''error'''.  This invokes the same internal exception handler used for internal runtime errors. Use this to signal fatal errors, or to implement exception handling. The error thrown (including internal runtime errors) can be caught with {{func link|call()}}.&lt;br /&gt;
|param1 = error&lt;br /&gt;
|param1text = String describing the error.&lt;br /&gt;
:{{inote|This parameter is technically optional, but it is highly recommended to use it.}}&lt;br /&gt;
|example1 = &lt;br /&gt;
print(&amp;quot;Will print&amp;quot;);&lt;br /&gt;
die(&amp;quot;Don't go any further!&amp;quot;); &lt;br /&gt;
print(&amp;quot;Won't print&amp;quot;); # Will not be printed because die() stops the process&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== find()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = find(needle, haystack);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=450|t=Source}}&lt;br /&gt;
|text = Finds and returns the index of the first occurrence of the string '''needle''' in the string '''haystack''', or -1 if no such occurrence was found.&lt;br /&gt;
|param1 = needle&lt;br /&gt;
|param1text = String to search for.&lt;br /&gt;
|param2 = haystack&lt;br /&gt;
|param2text = String to search in.&lt;br /&gt;
|example1 = print(find(&amp;quot;c&amp;quot;, &amp;quot;abcdef&amp;quot;)); # prints 2&lt;br /&gt;
|example2 = print(find(&amp;quot;x&amp;quot;, &amp;quot;abcdef&amp;quot;)); # prints -1&lt;br /&gt;
|example3 = print(find(&amp;quot;cd&amp;quot;, &amp;quot;abcdef&amp;quot;)); # prints 2&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===ghosttype()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = ghosttype(ghost);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=207|t=Source}}&lt;br /&gt;
|text = Returns a string containing either a descriptive name of a ghost (a raw C/C++ object), or a unique id (the pointer to the C/C++ &amp;lt;code&amp;gt;naGhostType&amp;lt;/code&amp;gt; instance) if no name has been set.  Ghost is an acronym that stands for '''G'''arbage-collected '''H'''andle to '''O'''ut'''S'''ide '''T'''hingy.&lt;br /&gt;
|param1 = ghost&lt;br /&gt;
|param1text = Ghost to return a description for.&lt;br /&gt;
|example1 = print(ghosttype(airportinfo())); # prints &amp;quot;airport&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===id()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = id(object);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=570|t=Source}}&lt;br /&gt;
|text = Returns a string containing information on the type and ID of the object provided in the single argument.  The information is returned in the form of &amp;lt;code&amp;gt;'''&amp;lt;type&amp;gt;''':'''&amp;lt;id&amp;gt;'''&amp;lt;/code&amp;gt;, where '''&amp;lt;type&amp;gt;''' is the type of object, and '''&amp;lt;id&amp;gt;''' is the ID.&lt;br /&gt;
|param1 = object&lt;br /&gt;
|param1text = Can be either of a string, a vector, a hash, a code, a function, or a ghost.&lt;br /&gt;
|example1 = print(id(&amp;quot;A&amp;quot;)); # prints &amp;quot;str:000000001624A590&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===int() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = int(number);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=90|t=Source}}&lt;br /&gt;
|text = Returns the integer part of the numeric value of the single argument, or &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; if none exists.&lt;br /&gt;
|param1 = number&lt;br /&gt;
|param1text = Number or string with just a number in it to return an integer from.&lt;br /&gt;
|example1 = print(int(23)); # prints &amp;quot;23&amp;quot;&lt;br /&gt;
|example2 = print(int(23.123)); # prints &amp;quot;23&amp;quot;&lt;br /&gt;
|example3 = debug.dump(int(&amp;quot;string&amp;quot;)); # prints &amp;quot;nil&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===keys()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = keys(hash);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=33|t=Source}}&lt;br /&gt;
|text = Returns a vector containing the list of keys found in the single hash argument. &lt;br /&gt;
|param1 = hash&lt;br /&gt;
|param1text = The hash to return the keys from.&lt;br /&gt;
|example1 = &lt;br /&gt;
# Initialize a hash&lt;br /&gt;
var hash = {&lt;br /&gt;
    element1: &amp;quot;value&amp;quot;,&lt;br /&gt;
    element2: &amp;quot;value&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
debug.dump(keys(hash)); # print the vector&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===left()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = left(string, length);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=149|t=Source}}&lt;br /&gt;
|version = 2.12&lt;br /&gt;
|commit = {{simgear commit|bd7163|t=commit}}&lt;br /&gt;
|text = Returns a substring of '''string''', starting from the left.&lt;br /&gt;
|param1 = string&lt;br /&gt;
|param1text = String to return part of.&lt;br /&gt;
|param2 = length&lt;br /&gt;
|param2text = Integer specifying the length of the substring to return.&lt;br /&gt;
|example1 = print(left(&amp;quot;string&amp;quot;, 2)); # prints &amp;quot;st&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== num()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = num(number);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=102|t=Source}}&lt;br /&gt;
|text = Returns the numerical value of the single string argument, or &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; if none exists. &lt;br /&gt;
|param1 = number&lt;br /&gt;
|param1text = String with just a number in it to return a number from.&lt;br /&gt;
|example1 = print(num(&amp;quot;23&amp;quot;)); # prints &amp;quot;23&amp;quot;&lt;br /&gt;
|example2 = print(num(&amp;quot;23.123&amp;quot;)); # prints &amp;quot;23.123&amp;quot;&lt;br /&gt;
|example3 = debug.dump(num(&amp;quot;string&amp;quot;)); # prints &amp;quot;nil&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===pop()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = pop(vector);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=50|t=Source}}&lt;br /&gt;
|text = Removes and returns the last element of the single vector argument, or &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; if the vector is empty. &lt;br /&gt;
|param1 = vector&lt;br /&gt;
|param1text = Vector to remove an element from.&lt;br /&gt;
|example1 = &lt;br /&gt;
var vector = [1, 2, 3];&lt;br /&gt;
pop(vector);&lt;br /&gt;
debug.dump(vector); # prints &amp;quot;[1, 2]&amp;quot;&lt;br /&gt;
|example2 = &lt;br /&gt;
var vector = [1, 2, 3];&lt;br /&gt;
debug.dump(pop(vector)); # prints &amp;quot;3&amp;quot;&lt;br /&gt;
|example3 = &lt;br /&gt;
var vector = [];&lt;br /&gt;
debug.dump(pop(vector)); # prints &amp;quot;nil&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===right()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = right(string, length);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=161|t=Source}}&lt;br /&gt;
|version = 2.12&lt;br /&gt;
|commit = {{simgear commit|bd7163|t=commit}}&lt;br /&gt;
|text = Returns a substring of '''string''', starting from the right.&lt;br /&gt;
|param1 = string&lt;br /&gt;
|param1text = String to return part of.&lt;br /&gt;
|param2 = length&lt;br /&gt;
|param2text = Integer specifying the length of the substring to return.&lt;br /&gt;
|example1 = print(right(&amp;quot;string&amp;quot;, 2)); # prints &amp;quot;ng&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== setsize()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = setsize(vector, size);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=56|t=Source}}&lt;br /&gt;
|text = Sets the size of a vector. The first argument specifies a vector, the second a number representing the desired size of that vector. If the vector is currently larger than the specified size, it is truncated. If it is smaller, it is padded with &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; entries. Returns the vector operated upon. &lt;br /&gt;
|param1 = vector&lt;br /&gt;
|param1text = The vector to be operated on.&lt;br /&gt;
|param2 = size&lt;br /&gt;
|param2text = The desired size of the vector in number of entries.&lt;br /&gt;
|example1 = &lt;br /&gt;
var vector = [1, 2, 3]; # Initialize a vector&lt;br /&gt;
setsize(vector, 4);&lt;br /&gt;
debug.dump(vector); # print the vector&lt;br /&gt;
|example2 = &lt;br /&gt;
var vector = [1, 2, 3]; # Initialize a vector&lt;br /&gt;
setsize(vector, 2);&lt;br /&gt;
debug.dump(vector); # print the vector&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===size()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = size(object);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=23|t=Source}}&lt;br /&gt;
|text = Returns the size of the single argument. For strings, this is the length in bytes. For vectors, this is the number of elements. For hashes, it is the number of key/value pairs. If the argument is &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; or a number, this error will be thrown: &amp;lt;code&amp;gt;object has no size()&amp;lt;/code&amp;gt;.&lt;br /&gt;
|param1 = object&lt;br /&gt;
|param1text = Object to find the size of.  Must be a string, a vector or a hash.&lt;br /&gt;
|example1 = &lt;br /&gt;
var string = &amp;quot;string&amp;quot;;&lt;br /&gt;
print(size(string)); # prints &amp;quot;6&amp;quot;&lt;br /&gt;
|example2 =&lt;br /&gt;
var vector = [1, 2, 3];&lt;br /&gt;
print(size(vector)); # prints &amp;quot;3&amp;quot;&lt;br /&gt;
|example3 =&lt;br /&gt;
var hash = {&lt;br /&gt;
    element1: &amp;quot;value1&amp;quot;,&lt;br /&gt;
    element2: &amp;quot;value2&amp;quot;,&lt;br /&gt;
    element3: &amp;quot;value3&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
print(size(hash)); # prints &amp;quot;3&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== sort()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = sort(vector, function);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=542|t=Source}}&lt;br /&gt;
|text = Returns a vector containing the elements in the input '''vector''' sorted in according to the rule given by '''function'''. Implemented with the ANSI C {{func link|qsort()|link=http://www.cplusplus.com/reference/cstdlib/qsort/}}, &amp;lt;code&amp;gt;sort()&amp;lt;/code&amp;gt; is stable.  This means that if the rules in the first example are used, equal elements in the output vector will appear in the same relative order as they do in the input.  It is run in a loop, so '''function''' is run several times.&lt;br /&gt;
|param1 = vector&lt;br /&gt;
|param1text = Input vector to sort.&lt;br /&gt;
|param2 = function&lt;br /&gt;
|param2text = Function according to which the elements will be sorted by.  It should take two arguments and should return one of 1, 0, or -1.&lt;br /&gt;
{{{!}} class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Return value !! Meaning&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} less than 0 {{!!}} first argument should go before second argument&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} 0 {{!!}} first argument equals second argument&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} greater than 0 {{!!}} first argument should go after second argument&lt;br /&gt;
{{!}}}&lt;br /&gt;
&lt;br /&gt;
|example1text = This example sorts elements from smallest to greatest.&lt;br /&gt;
|example1 = &lt;br /&gt;
var sort_rules = func(a, b){&lt;br /&gt;
    if(a &amp;lt; b){&lt;br /&gt;
        return -1; # A should before b in the returned vector&lt;br /&gt;
    }elsif(a == b){&lt;br /&gt;
        return 0; # A is equivalent to b &lt;br /&gt;
    }else{&lt;br /&gt;
        return 1; # A should after b in the returned vector&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
debug.dump(sort([3, 2, 5, 6, 4, 1], sort_rules)); # prints &amp;quot;[1, 2, 3, 4, 5, 6]&amp;quot;&lt;br /&gt;
|example2text = This example sorts elements from greatest to smallest.&lt;br /&gt;
|example2 = &lt;br /&gt;
# Outputs the elements in reverse order (greatest to smallest)&lt;br /&gt;
var sort_rules = func(a, b){&lt;br /&gt;
    if(a &amp;lt; b){&lt;br /&gt;
        return 1; # -1 in the above example&lt;br /&gt;
    }elsif(a == b){&lt;br /&gt;
        return 0;&lt;br /&gt;
    }else{&lt;br /&gt;
        return -1; # 1 in the above example&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
debug.dump(sort([3, 2, 5, 6, 4, 1], sort_rules)); # prints &amp;quot;[6, 5, 4, 3, 2, 1]&amp;quot;&lt;br /&gt;
|example3text = This example sorts a vector of strings (runways for example) from smallest to greatest.&lt;br /&gt;
|example3 = &lt;br /&gt;
var runways = [&amp;quot;09R&amp;quot;,&amp;quot;27R&amp;quot;,&amp;quot;26L&amp;quot;,&amp;quot;09L&amp;quot;,&amp;quot;15&amp;quot;];&lt;br /&gt;
var rwy = sort(runways,func(a,b) cmp(a,b));&lt;br /&gt;
debug.dump(rwy); # prints ['09L','09R','15','26L','27R']&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== split()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = split(delimiter, string);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=460|t=Source}}&lt;br /&gt;
|text = Splits the input string into a vector of substrings bounded by occurrences of the delimiter substring.&lt;br /&gt;
|param1 = delimiter&lt;br /&gt;
|param1text = String that will split the substrings in the returned vector.&lt;br /&gt;
|param2 = string&lt;br /&gt;
|param2text = String to split up.&lt;br /&gt;
|example1 = debug.dump(split(&amp;quot;cd&amp;quot;, &amp;quot;abcdef&amp;quot;)); # prints &amp;quot;['ab', 'ef']&amp;quot;&lt;br /&gt;
|example2 = debug.dump(split(&amp;quot;.&amp;quot;, &amp;quot;3.2.0&amp;quot;)); # prints &amp;quot;[3, 2, 0]&amp;quot;&lt;br /&gt;
|example3 = debug.dump(split(&amp;quot;/&amp;quot;, &amp;quot;path/to/file&amp;quot;)); # prints &amp;quot;['path', 'to', 'file']&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===sprintf()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = &amp;lt;nowiki&amp;gt;sprintf(format[, arg[, arg, [...]]]);&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=355|t=Source}}&lt;br /&gt;
|text = Creates and returns a string formatted using ANSI C {{func link|vsnprintf()|link=http://en.cppreference.com/w/c/io/vfprintf}} &amp;lt;ref&amp;gt;&lt;br /&gt;
{{Cite web&lt;br /&gt;
|url = http://sourceforge.net/p/flightgear/simgear/ci/next/tree/simgear/nasal/lib.c#l308&lt;br /&gt;
|title = fgdata/simgear/simgear/nasal/lib.c, line 308&lt;br /&gt;
|accessdate = October 2015&lt;br /&gt;
}}&lt;br /&gt;
&amp;lt;/ref&amp;gt;.  Below is a table of supported format specifiers.&lt;br /&gt;
{{{!}} class=&amp;quot;wikitable&amp;quot; width=&amp;quot;75%&amp;quot;&lt;br /&gt;
{{!}}+ %[flags][width][.precision]specifier&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; {{!}} Flags&lt;br /&gt;
{{!-}}&lt;br /&gt;
! Flag !! Output&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;+&amp;lt;/code&amp;gt; {{!!}} Forces to precede the result with a plus or minus sign ('''+''' or '''-''') even for positive numbers. By default, only negative numbers are preceded with a '''-''' sign.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} ''space'' {{!!}} Prefixes non-signed numbers with a space.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;-&amp;lt;/code&amp;gt; {{!!}} Left-align the output of this placeholder (the default is to right-align the output) when the width option is specified.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt; {{!!}} Use 0 instead of spaces to pad a field when the width option is specified.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;#&amp;lt;/code&amp;gt; {{!!}} Used with &amp;lt;code&amp;gt;o&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;X&amp;lt;/code&amp;gt; specifiers the value is preceded with &amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;0x&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;0X&amp;lt;/tt&amp;gt; respectively for values different than zero. Used with &amp;lt;code&amp;gt;e&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;E&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;f&amp;lt;/code&amp;gt;, it forces the written output to contain a decimal point even if no digits would follow. By default, if no digits follow, no decimal point is written. Used with &amp;lt;code&amp;gt;g&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;G&amp;lt;/code&amp;gt; the result is the same as with &amp;lt;code&amp;gt;e&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;E&amp;lt;/code&amp;gt; but trailing zeros are not removed.&lt;br /&gt;
{{!-}}&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; {{!}} Width&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} colspan=&amp;quot;2&amp;quot; {{!}} Integer specifying the minimum number of characters to be returned. This includes the decimal point and decimal fraction as well as + or - signs.&lt;br /&gt;
{{!-}}&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; {{!}} Precision&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} colspan=&amp;quot;2&amp;quot; {{!}} Integer preceded by a dot specifying the number of decimal places to be written.&lt;br /&gt;
{{!-}}&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; {{!}} Specifiers&lt;br /&gt;
{{!-}}&lt;br /&gt;
! Specifier !! Output&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;d&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;i&amp;lt;/code&amp;gt; {{!!}} Signed decimal number.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;s&amp;lt;/code&amp;gt; {{!!}} A string&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;%&amp;lt;/code&amp;gt; {{!!}} Percent (%) character.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;c&amp;lt;/code&amp;gt; {{!!}} A single character assigned to a character code, the code given in an integer argument.  See http://www.asciitable.com/ for a list of supported characters and their codes.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;o&amp;lt;/code&amp;gt; {{!!}} Unsigned integer as an octal number.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;u&amp;lt;/code&amp;gt; {{!!}} Unsigned decimal integer.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;X&amp;lt;/code&amp;gt; {{!!}} Unsigned integer as a hexadecimal number.  If &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt; is used, any letters in the number are lowercase, while &amp;lt;code&amp;gt;X&amp;lt;/code&amp;gt; gives uppercase.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;e&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;E&amp;lt;/code&amp;gt; {{!!}} Double value in scientific notation (i.e., ''[-]ddd.ddd'''e'''[+/-]ddd''), with an exponent being denoted by &amp;lt;tt&amp;gt;e&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;E&amp;lt;/tt&amp;gt; depending on whether an upper or lowercase is used respectively.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;f&amp;lt;/code&amp;gt; {{!!}} Floating-point number, in fixed decimal notation, by default with 6 decimal places.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;F&amp;lt;/code&amp;gt; {{!!}} Appears to be available&amp;lt;ref&amp;gt;&lt;br /&gt;
{{Cite web&lt;br /&gt;
|url = http://sourceforge.net/p/flightgear/simgear/ci/next/tree/simgear/nasal/lib.c#l389&lt;br /&gt;
|title = fgdata/simgear/simgear/nasal/lib.c, line 389&lt;br /&gt;
|accessdate = October 2015&lt;br /&gt;
}}&lt;br /&gt;
&amp;lt;/ref&amp;gt;, but doesn't work.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;g&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;G&amp;lt;/code&amp;gt; {{!!}} Double in either normal or exponential notation, whichever is more appropriate for its magnitude. &amp;lt;code&amp;gt;g&amp;lt;/code&amp;gt; uses lower-case letters, &amp;lt;code&amp;gt;G&amp;lt;/code&amp;gt; uses upper-case letters. This type differs slightly from fixed-point notation in that insignificant zeroes to the right of the decimal point are not included.  Also, the decimal point is not included on whole numbers.&lt;br /&gt;
{{!}}}&lt;br /&gt;
&lt;br /&gt;
|param1 = format&lt;br /&gt;
|param1text = String specifying the format.  Can be used with or without a format specifiers.  See below for examples.&lt;br /&gt;
|param2 = arg&lt;br /&gt;
|param2text = Argument specifying a value to replace a format placeholder (such as &amp;lt;code&amp;gt;%d&amp;lt;/code&amp;gt;) in the format string.  Not required if there are no format specifiers.&lt;br /&gt;
&lt;br /&gt;
|example1 = print(sprintf(&amp;quot;%i&amp;quot;, 54)); # prints &amp;quot;54&amp;quot;&lt;br /&gt;
|example2 = print(sprintf(&amp;quot;Pi = %+.10f&amp;quot;, math.pi)); # prints &amp;quot;Pi = +3.1415926536&amp;quot;&lt;br /&gt;
|example3 = &lt;br /&gt;
print(sprintf(&amp;quot;%6d&amp;quot;, 23)); # prints &amp;quot;    23&amp;quot;&lt;br /&gt;
print(sprintf(&amp;quot;%06d&amp;quot;, 23)); # prints &amp;quot;000023&amp;quot;&lt;br /&gt;
|example4 =&lt;br /&gt;
var FGVer = getprop(&amp;quot;/sim/version/flightgear&amp;quot;);&lt;br /&gt;
print(sprintf(&amp;quot;You have FlightGear v%s&amp;quot;, FGVer)); # prints &amp;quot;You have FlightGear v&amp;lt;your version&amp;gt;&amp;quot;&lt;br /&gt;
|example5 = &lt;br /&gt;
print(sprintf(&amp;quot;Hexadecimal 100000 = %X&amp;quot;, 100000)); # prints &amp;quot;Hexadecimal 100000 = 186A0&amp;quot;&lt;br /&gt;
print(sprintf(&amp;quot;Hexadecimal 100000 = %x&amp;quot;, 100000)); # prints &amp;quot;Hexadecimal 100000 = 186a0&amp;quot;&lt;br /&gt;
|example6 = print(sprintf(&amp;quot;Code 65 is %c&amp;quot;, 65)); # prints &amp;quot;Code 65 is A&amp;quot;&lt;br /&gt;
|example7 = &lt;br /&gt;
print(sprintf(&amp;quot;%e&amp;quot;, 54)); # prints &amp;quot;5.400000e+001&amp;quot;&lt;br /&gt;
print(sprintf(&amp;quot;%E&amp;quot;, 54)); # prints &amp;quot;5.400000E+001&amp;quot;&lt;br /&gt;
|example8 = print(sprintf(&amp;quot;%o&amp;quot;, 54)); # prints &amp;quot;66&amp;quot;&lt;br /&gt;
|example9 = print(sprintf(&amp;quot;50%% of 100 is %i&amp;quot;, 100 / 2)); # prints &amp;quot;50% of 100 is 50&amp;quot;&lt;br /&gt;
|example10 =&lt;br /&gt;
print(sprintf(&amp;quot;%.2f&amp;quot;, 1.4));   #prints &amp;quot;1.40&amp;quot;&lt;br /&gt;
print(sprintf(&amp;quot;%.1f&amp;quot;, 1.4));   #prints &amp;quot;1.4&amp;quot;&lt;br /&gt;
print(sprintf(&amp;quot;% 4.1f&amp;quot;, 1.4)); #prints &amp;quot; 1.4&amp;quot;&lt;br /&gt;
print(sprintf(&amp;quot;%04.1f&amp;quot;, 1.4)); #prints &amp;quot;01.4&amp;quot;&lt;br /&gt;
print(sprintf(&amp;quot;% 6.1f&amp;quot;, 1.4)); #prints &amp;quot;   1.4&amp;quot;&lt;br /&gt;
print(sprintf(&amp;quot;%06.1f&amp;quot;, 1.4)); #prints &amp;quot;0001.4&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===streq()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = streq(a, b);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=129|t=Source}}&lt;br /&gt;
|text = Tests the string values of the two arguments for equality. This function is needed because the &amp;lt;code&amp;gt;'''=='''&amp;lt;/code&amp;gt; operator (see [[Nasal_Operators#Equality|Nasal Operators]]) tests for numeric equality first.  If either or both the arguments are not strings, 0 (False) will be returned.  Returns either 0 (False) or 1 (True).  {{Note|This function is rarely required in typical code.}}&lt;br /&gt;
|param1 = a&lt;br /&gt;
|param1text = First argument for testing equality.&lt;br /&gt;
|param2 = b&lt;br /&gt;
|param2text = Second argument for testing equality.&lt;br /&gt;
|example1 = print(streq(&amp;quot;0&amp;quot;, &amp;quot;0&amp;quot;)); # prints &amp;quot;1&amp;quot; (True)&lt;br /&gt;
|example2 = &lt;br /&gt;
print(0 == 0.0); # prints &amp;quot;1&amp;quot; (True)&lt;br /&gt;
print(streq(&amp;quot;0&amp;quot;, &amp;quot;0.0&amp;quot;)); # prints &amp;quot;0&amp;quot; (False)&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===substr()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = substr(string, start [, length]);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=129|t=Source}}&lt;br /&gt;
|text = Similar the {{func link|subvec()}}, but operates on strings. Computes and returns a substring. The first argument specifies a string, the second is the index of the start of a substring, the optional third argument specifies a length (the default is to return the rest of the string from the start).&lt;br /&gt;
|param1 = string&lt;br /&gt;
|param1text = String to return a substring from.&lt;br /&gt;
|param2 = start&lt;br /&gt;
|param2text = Integer specifying the start of a substring. Negative values specify a position from the end of the string.&lt;br /&gt;
|param3 = length&lt;br /&gt;
|param3text = Optional argument specifying the length of the substring. Defaults to the end of the string.&lt;br /&gt;
|example1 = print(substr(&amp;quot;abcde&amp;quot;, 1, 3)); # prints &amp;quot;bcd&amp;quot;&lt;br /&gt;
|example2 = print(substr(&amp;quot;abcde&amp;quot;, 1)); # prints &amp;quot;bcde&amp;quot;&lt;br /&gt;
|example3 = print(substr(&amp;quot;abcde&amp;quot;, 2, 1)); # prints &amp;quot;c&amp;quot;&lt;br /&gt;
|example4 = print(substr(&amp;quot;abcde&amp;quot;, -2)); # prints &amp;quot;de&amp;quot;&lt;br /&gt;
|example5 = print(substr(&amp;quot;abcde&amp;quot;, -3, 2)); # prints &amp;quot;cd&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===subvec()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = subvec(vector, start[, length]);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=63|t=Source}}&lt;br /&gt;
|text = Returns a sub-range of a vector. The first argument specifies a vector, the second a starting index, and the optional third argument indicates a length (the default is to the end of the vector). &lt;br /&gt;
|param1 = vector&lt;br /&gt;
|param1text = The vector to take the sub-vector from.&lt;br /&gt;
|param2 = start&lt;br /&gt;
|param2text = The starting point of the sub-vector within the given vector.&lt;br /&gt;
|param3 = length&lt;br /&gt;
|param3text = Optional argument specifying the length of the sub-vector, from the starting point.&lt;br /&gt;
'''Notes:'''&lt;br /&gt;
* Omitting the ''vector'' and ''start'' arguments is not an error (possibly it should be) but the return value is ''nil''.&lt;br /&gt;
* A negative ''start'' argument ''is'' an error. This seems wrong. Perhaps the language designer could comment.&lt;br /&gt;
* A value of ''start'' greater than ''size(vector)'' causes an error. A value equal to ''size(vector)'' returns an empty vector.&lt;br /&gt;
* If the value of ''length'' is greater than ''size(vector) - start'' then it is ignored. That is, all elements from ''start'' to the end of ''vector'' are returned. If ''length'' is zero then an empty vector is returned. A negative value of ''length'' causes an error.&lt;br /&gt;
|example1 = &lt;br /&gt;
var vector = [1, 2, 3];&lt;br /&gt;
debug.dump(subvec(vector, 0)); # prints &amp;quot;[1, 2, 3]&amp;quot;&lt;br /&gt;
|example2 = &lt;br /&gt;
var vector = [1, 2, 3];&lt;br /&gt;
debug.dump(subvec(vector, 1)); # prints &amp;quot;[2, 3]&amp;quot;&lt;br /&gt;
|example3 = &lt;br /&gt;
var vector = [1, 2, 3];&lt;br /&gt;
debug.dump(subvec(vector, 1, 1)); # prints &amp;quot;[2]&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== typeof()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = typeof(object);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=193|t=Source}}&lt;br /&gt;
|text = Returns a string indicating the whether the object is &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt;, a scalar (number or string), a vector, a hash, a function, or a ghost.&lt;br /&gt;
|param1 = object&lt;br /&gt;
|param1text = Object to return the type of.&lt;br /&gt;
|example1 = &lt;br /&gt;
var object = nil;&lt;br /&gt;
print(typeof(object)); # prints &amp;quot;nil&amp;quot;&lt;br /&gt;
|example2 = &lt;br /&gt;
var object = &amp;quot;Hello world!&amp;quot;;&lt;br /&gt;
print(typeof(object)); # prints &amp;quot;scalar&amp;quot;&lt;br /&gt;
|example3 = &lt;br /&gt;
var object = math.pi;&lt;br /&gt;
print(typeof(object)); # prints &amp;quot;scalar&amp;quot;&lt;br /&gt;
|example4 = &lt;br /&gt;
var object = [1, 2, 3];&lt;br /&gt;
print(typeof(object)); # prints &amp;quot;vector&amp;quot;&lt;br /&gt;
|example5 = &lt;br /&gt;
var object = {};&lt;br /&gt;
print(typeof(object)); # prints &amp;quot;hash&amp;quot;&lt;br /&gt;
|example6 = &lt;br /&gt;
var object = func {};&lt;br /&gt;
print(typeof(object)); # prints &amp;quot;func&amp;quot;&lt;br /&gt;
|example7 =&lt;br /&gt;
var object = airportinfo();&lt;br /&gt;
print(typeof(object)); # prints &amp;quot;ghost&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- == Extension modules ==&lt;br /&gt;
=== thread ===&lt;br /&gt;
{{WIP}}&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = thread.newthread(func);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/threadlib.c|l=101|t=Source}}&lt;br /&gt;
|text = &lt;br /&gt;
|example1text = start a new worker thread&lt;br /&gt;
|example1 = thread.newthread( func() {} );&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = thread.newlock();&lt;br /&gt;
|source = {{simgear file|simgear/nasal/threadlib.c|l=101|t=Source}}&lt;br /&gt;
|text = &lt;br /&gt;
|example1text = create a new lock&lt;br /&gt;
|example1 = var lock = thread.newlock()&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = thread.lock();&lt;br /&gt;
|source = {{simgear file|simgear/nasal/threadlib.c|l=101|t=Source}}&lt;br /&gt;
|text = &lt;br /&gt;
|example1text = lock a lock&lt;br /&gt;
|example1 = var lock = thread.newlock()&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = thread.unlock();&lt;br /&gt;
|source = {{simgear file|simgear/nasal/threadlib.c|l=101|t=Source}}&lt;br /&gt;
|text = &lt;br /&gt;
|example1text = unlock a lock&lt;br /&gt;
|example1 = var lock = thread.unlock()&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = thread.newsem();&lt;br /&gt;
|source = {{simgear file|simgear/nasal/threadlib.c|l=101|t=Source}}&lt;br /&gt;
|text = &lt;br /&gt;
|example1text = create a new {{Wikipedia|semaphore}}&lt;br /&gt;
|example1 = var semaphore = thread.newsem()&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = thread.semdown();&lt;br /&gt;
|source = {{simgear file|simgear/nasal/threadlib.c|l=101|t=Source}}&lt;br /&gt;
|text = &lt;br /&gt;
|example1text = semaphore down&lt;br /&gt;
|example1 = thread.semdown(semaphore)&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = thread.semup();&lt;br /&gt;
|source = {{simgear file|simgear/nasal/threadlib.c|l=101|t=Source}}&lt;br /&gt;
|text = &lt;br /&gt;
|example1text = semaphore up&lt;br /&gt;
|example1 = thread.semup(semaphore)&lt;br /&gt;
}} --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Extension functions==&lt;br /&gt;
The '''extension functions''' are global functions that have been added to Nasal since its integration into FlightGear. Unlike the core library functions, they are generally specifically designed to interact directly with FlightGear. Extension functions come from three source files:&lt;br /&gt;
*{{flightgear file|src/Scripting/NasalPositioned.cxx}}&lt;br /&gt;
*{{flightgear file|src/Scripting/NasalSys.cxx}}&lt;br /&gt;
*{{fgdata file|Nasal/globals.nas}}&lt;br /&gt;
&lt;br /&gt;
===abort()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = abort();&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=565|t=Source}}&lt;br /&gt;
|text = This function is a wrapper for the C++ {{func link|abort()|link=http://www.cplusplus.com/reference/cstdlib/abort/}} function. It simply aborts FlightGear with an error, which varies depending on the operating system. This function should not really be used; instead, please use the &amp;quot;exit&amp;quot; [[Fgcommands|fgcommand]], which will exit FlightGear more gracefully (see example below).&lt;br /&gt;
|example1text = This example will immediately stop FlightGear with an error, such as &amp;quot;FlightGear has stopped working.&amp;quot;&lt;br /&gt;
|example1 = abort();&lt;br /&gt;
|example2text = For exiting FlightGear in a better way, please use the following code:&lt;br /&gt;
|example2 = fgcommand(&amp;quot;exit&amp;quot;);&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== abs() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = abs(number);&lt;br /&gt;
|source = {{fgdata file|Nasal/globals.nas|t=Source}}&lt;br /&gt;
|text = This simple function returns the {{wikipedia|absolute value|noicon=1}} of the provided number.&lt;br /&gt;
|param1 = number&lt;br /&gt;
|param1text = This argument is required and should be a number.&lt;br /&gt;
|example1 = print(abs(1)); # prints &amp;quot;1&amp;quot;&lt;br /&gt;
|example2 = print(abs(-1)); # prints &amp;quot;1&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===aircraftToCart() ===&lt;br /&gt;
This new function in FG 2017.2.1 takes coordinates in aircraft structural coordinate system, and translate them into geocentric coordinates.&lt;br /&gt;
Example for (5,6,7):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
var pos = aircraftToCart({x: -5, y: 6, z: -7});&lt;br /&gt;
var coord = geo.Coord.new();&lt;br /&gt;
coord.set_xyz(pos.x, pos.y, pos.z);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Notice: x and z is inverted sign on purpose.&lt;br /&gt;
if you want lat. lon, alt from that, just call: (degrees and meters)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
coord.lat()&lt;br /&gt;
coord.lon()&lt;br /&gt;
coord.alt()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===addcommand() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = addcommand(name, code);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=659|t=Source}}&lt;br /&gt;
|version = 2.12&lt;br /&gt;
|commit = {{flightgear commit|7b663c|t=commit}}&lt;br /&gt;
|text = {{see also|Howto:Add new fgcommands to FlightGear}}&lt;br /&gt;
&lt;br /&gt;
This function enables the addition of a new custom [[fgcommands|fgcommand]] to FlightGear from within Nasal. An fgcommand created using this method can be used in exactly the same way as the built-in fgcommands. Also, an fgcommand created via this method will always return True or 1, like all other fgcommands.&lt;br /&gt;
|param1 = name&lt;br /&gt;
|param1text = This will become the name of the new fgcommand. Must be a string.&lt;br /&gt;
|param2 = code&lt;br /&gt;
|param2text = The code that will be executed when the fgcommand is run. Must be a function.&lt;br /&gt;
|example1text = This example adds a new fgcommand and then runs it. Although it executes a simple {{func link|print()}} statement, any valid Nasal code can be used.&lt;br /&gt;
|example1 = addcommand(&amp;quot;myFGCmd&amp;quot;, func(node) {&lt;br /&gt;
    print(&amp;quot;fgcommand 'myFGCmd' has been run.&amp;quot;);&lt;br /&gt;
    props.dump( node );&lt;br /&gt;
});&lt;br /&gt;
fgcommand(&amp;quot;myFGCmd&amp;quot;, props.Node.new({foo:1, bar:2}) );&lt;br /&gt;
|example2text = This example demonstrates how parameters are defined in a new fgcommand.&lt;br /&gt;
|example2 = addcommand(&amp;quot;myFGCmd&amp;quot;, func(node){&lt;br /&gt;
    print(node.getNode(&amp;quot;number&amp;quot;).getValue()); # prints the value of &amp;quot;number,&amp;quot; which is 12&lt;br /&gt;
});&lt;br /&gt;
fgcommand(&amp;quot;myFGCmd&amp;quot;, props.Node.new({&amp;quot;number&amp;quot;: 12}));&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===airportinfo()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = airportinfo();&lt;br /&gt;
airportinfo(type);&lt;br /&gt;
airportinfo(id);&lt;br /&gt;
airportinfo(lat, lon[, type]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1024|t=Source}}&lt;br /&gt;
|text = Function for retrieval of airport, heliport, or seaplane base information. It returns a Nasal ghost; however, its structure is like that of a Nasal hash. The following information is returned:&lt;br /&gt;
* '''parents''': A vector containing a hash of various functions to access information about the runway. See {{flightgear file|src/Scripting/NasalPositioned.cxx|l=2659}} for full list.&lt;br /&gt;
* '''lon''': Longitude of the location.&lt;br /&gt;
* '''lat''': Latitude of the location.&lt;br /&gt;
* '''has_metar''': True or false depending whether the airport has a [[METAR]] code defined for it.&lt;br /&gt;
* '''elevation''': Elevation of the location in metres.&lt;br /&gt;
* '''id''': ICAO code of the airport (or ID of the seaplane base/heliport).&lt;br /&gt;
* '''name''': Name of the airport/heliport/seaplane base.&lt;br /&gt;
* '''runways'''&lt;br /&gt;
** '''&amp;lt;runway name&amp;gt;'''&lt;br /&gt;
*** '''id''': Name of runway.&lt;br /&gt;
*** '''lat''': Latitude of the runway.&lt;br /&gt;
*** '''lon''': Longitude of the runway.&lt;br /&gt;
*** '''heading''': Heading of the runway.&lt;br /&gt;
*** '''length''': Length of the runway in metres.&lt;br /&gt;
*** '''width''': Width of the runway in metres.&lt;br /&gt;
*** '''surface''': Runway surface type.&lt;br /&gt;
*** '''threshold''': Length of the runway's {{wikipedia|displaced threshold}} in metres. Will return 0 if there is none.&lt;br /&gt;
*** '''stopway''': Length of the runway's stopway (the area before the threshold) in metres. Will return 0 if there is none.&lt;br /&gt;
*** '''reciprocal''': &amp;lt;code&amp;gt;runway&amp;lt;/code&amp;gt; ghost of the reciprocal runway.&lt;br /&gt;
*** '''ils_frequency_mhz''': ILS frequency in megahertz.&lt;br /&gt;
*** '''ils''': &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt; ghost of the ILS transmitter.&lt;br /&gt;
* '''helipads'''&lt;br /&gt;
** '''&amp;lt;helipad name&amp;gt;'''&lt;br /&gt;
*** '''id''': Name of helipad.&lt;br /&gt;
*** '''lat''': Latitude of the helipad.&lt;br /&gt;
*** '''lon''': Longitude of the helipad.&lt;br /&gt;
*** '''heading''': Heading of the helipad.&lt;br /&gt;
*** '''length''': Length of the helipad in metres.&lt;br /&gt;
*** '''width''': Width of the helipad in metres.&lt;br /&gt;
*** '''surface''': Helipad surface type.&lt;br /&gt;
* '''taxiways'''&lt;br /&gt;
** '''&amp;lt;taxiway name&amp;gt;'''&lt;br /&gt;
*** '''id''': Name of taxiway.&lt;br /&gt;
*** '''lat''': Latitude of the taxiway.&lt;br /&gt;
*** '''lon''': Longitude of the taxiway.&lt;br /&gt;
*** '''heading''': Heading of the taxiway.&lt;br /&gt;
*** '''length''': Length of the taxiway in metres.&lt;br /&gt;
*** '''width''': Width of the taxiway in metres.&lt;br /&gt;
*** '''surface''': Taxiway surface type.&lt;br /&gt;
&lt;br /&gt;
Information is extracted in the same way as accessing members of a Nasal hash. For example:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
# prints to lengths of the runways of the nearest airport in feet and metres&lt;br /&gt;
var info = airportinfo();&lt;br /&gt;
print(&amp;quot;-- Lengths of the runways at &amp;quot;, info.name, &amp;quot; (&amp;quot;, info.id, &amp;quot;) --&amp;quot;);&lt;br /&gt;
foreach(var rwy; keys(info.runways)){&lt;br /&gt;
    print(rwy, &amp;quot;: &amp;quot;, math.round(info.runways[rwy].length * M2FT), &amp;quot; ft (&amp;quot;, info.runways[rwy].length, &amp;quot; m)&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that searches for locations that are a long way away (e.g., the nearest seaplane base to the middle of the Sahara) may cause FlightGear to pause for an amount of time.&lt;br /&gt;
|param1 = id&lt;br /&gt;
|param1text = The {{wikipedia|International Civil Aviation Organization airport code|ICAO code|noicon=1}} of an airport to retrieve information about.&lt;br /&gt;
|param2 = type&lt;br /&gt;
|param2text = When this argument is used, the function will return the closest airport of a certain type. Can be one of &amp;quot;heliport,&amp;quot; &amp;quot;seaport,&amp;quot; or &amp;quot;airport&amp;quot; (default).&lt;br /&gt;
: {{inote|Running this function without any parameters is equivalent to this:&lt;br /&gt;
: &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
airportinfo(&amp;quot;airport&amp;quot;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
|param3 = lat ''and'' lon&lt;br /&gt;
|param3text = When these parameters are used, the function will return information on the nearest airport, heliport or seaplane base (depending on the '''type''' parameter) to those coordinates.&lt;br /&gt;
|example1 = var info = airportinfo();&lt;br /&gt;
print(&amp;quot;Nearest airport: &amp;quot;, info.name, &amp;quot; (&amp;quot;, info.id, &amp;quot;)&amp;quot;); # prints the name and ICAO code of the nearest airport&lt;br /&gt;
|example2 = var info = airportinfo(&amp;quot;heliport&amp;quot;);&lt;br /&gt;
print(&amp;quot;Elevation of the nearest heliport: &amp;quot;, math.round(info.elevation * M2FT), &amp;quot; ft&amp;quot;); # prints the elevation and name of the nearest heliport&lt;br /&gt;
|example3 = var info = airportinfo(&amp;quot;KSQL&amp;quot;);&lt;br /&gt;
print(&amp;quot;-- Runways of &amp;quot;, info.name, &amp;quot; (&amp;quot;, info.id, &amp;quot;): --&amp;quot;);&lt;br /&gt;
foreach(var rwy; keys(info.runways)) {&lt;br /&gt;
    print(rwy); # prints the runways of KSQL&lt;br /&gt;
}&lt;br /&gt;
|example4 = var info = airportinfo(37.81909385, -122.4722484);&lt;br /&gt;
print(&amp;quot;Coordinates of the nearest airport: &amp;quot;, info.lat, &amp;quot;, &amp;quot;, info.lon); # print the name and ICAO of the nearest airport to the Golden Gate Bridge&lt;br /&gt;
|example5 = var info = airportinfo(37.81909385, -122.4722484, &amp;quot;seaport&amp;quot;);&lt;br /&gt;
print(&amp;quot;Nearest seaplane base: &amp;quot;, info.name, &amp;quot; (&amp;quot;, info.id, &amp;quot;)&amp;quot;); # print the name and ID of the nearest seaplane base to the Golden Gate Bridge&lt;br /&gt;
|example6text = This example prints the all information from an &amp;lt;code&amp;gt;airportinfo()&amp;lt;/code&amp;gt; call.&lt;br /&gt;
|example6 = var info = airportinfo(&amp;quot;KSFO&amp;quot;);&lt;br /&gt;
print(info.name);&lt;br /&gt;
print(info.id);&lt;br /&gt;
print(info.lat);&lt;br /&gt;
print(info.lon);&lt;br /&gt;
print(info.has_metar);&lt;br /&gt;
print(info.elevation);&lt;br /&gt;
foreach(var rwy; keys(info.runways)){&lt;br /&gt;
    print(&amp;quot;-- &amp;quot;, rwy, &amp;quot; --&amp;quot;);&lt;br /&gt;
    print(info.runways[rwy].lat);&lt;br /&gt;
    print(info.runways[rwy].lon);&lt;br /&gt;
    print(info.runways[rwy].length);&lt;br /&gt;
    print(info.runways[rwy].width);&lt;br /&gt;
    print(info.runways[rwy].heading);&lt;br /&gt;
    print(info.runways[rwy].stopway);&lt;br /&gt;
    print(info.runways[rwy].threshold);&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===airwaysRoute() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = airwaysRoute(start, end[, type]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1933|t=Source}}&lt;br /&gt;
|text = {{see also|Nasal Flightplan}}&lt;br /&gt;
This function returns a vector containing waypoints between two given waypoints. The returned waypoints are ghosts, but can be accessed in the same way as a Nasal hash. See [[Nasal Flightplan]] for more information.&lt;br /&gt;
|param1 = start&lt;br /&gt;
|param1text = Start waypoint, in the form of a waypoint ghost, such as that provided by {{func link|flightplan()}}.&lt;br /&gt;
|param2 = end&lt;br /&gt;
|param2text = Same as above.&lt;br /&gt;
|param3 = type&lt;br /&gt;
|param3text = Instructs the function to compute a high level route (when set to &amp;quot;highlevel&amp;quot;), or a low level route (when set to &amp;quot;lowlevel&amp;quot;). Defaults to &amp;quot;highlevel.&amp;quot;&lt;br /&gt;
|example1text = In the [[route manager]] dialog, add two waypoints to the flightplan, ideally ones that are far apart (tip: use the [[Map]] for this). Then run this code in your Nasal Console.&lt;br /&gt;
|example1 = var fp = flightplan();&lt;br /&gt;
var start = fp.getWP(0);&lt;br /&gt;
var end = fp.getWP(fp.getPlanSize() - 1);&lt;br /&gt;
var rt = airwaysRoute(start, end);&lt;br /&gt;
foreach(var wp; rt){&lt;br /&gt;
    print(wp.wp_name); # print the waypoints in the computed route&lt;br /&gt;
}&lt;br /&gt;
|example2text = Exactly the same as above, but computes a low level path.&lt;br /&gt;
|example2 = var fp = flightplan();&lt;br /&gt;
var start = fp.getWP(0);&lt;br /&gt;
var end = fp.getWP(fp.getPlanSize() - 1);&lt;br /&gt;
var rt = airwaysRoute(start, end, &amp;quot;lowlevel&amp;quot;);&lt;br /&gt;
foreach(var wp; rt){&lt;br /&gt;
    print(wp.wp_name); # print the waypoints in the computed route&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===airway()===&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = airway(ident [, pos]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=2644|t=Source}}&lt;br /&gt;
|text = {{see also|Nasal Flightplan}}&lt;br /&gt;
This function returns a ghost containing an airway of a specified id.&lt;br /&gt;
|param1 = ident&lt;br /&gt;
|param1text = a Positioned ghost (leg, navaid, airport) and so on, passed to the search function.&lt;br /&gt;
|param2 = pos&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===assert()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = assert(condition[, message]);&lt;br /&gt;
|source = {{fgdata file|Nasal/globals.nas|t=Source}}&lt;br /&gt;
|version = 3.2&lt;br /&gt;
|commit = {{fgdata commit|8b16a7|t=commit}}&lt;br /&gt;
|text = Returns either true if the condition evaluates as true, or aborts with a {{func link|die()}} call, which can be customised.&lt;br /&gt;
|param1 = condition&lt;br /&gt;
|param1text = Condition to evaluate.&lt;br /&gt;
|param2 = message&lt;br /&gt;
|param2text = Optional message that will be used in any {{func link|die()}} call. Defaults to &amp;quot;assertion failed!&amp;quot;&lt;br /&gt;
|example1 = var a = 1;&lt;br /&gt;
var b = 2;&lt;br /&gt;
print(assert(a &amp;lt; b)); # prints &amp;quot;1&amp;quot; (true)&lt;br /&gt;
|example2 = var a = 1;&lt;br /&gt;
var b = 2;&lt;br /&gt;
assert(a &amp;gt; b, 'a is not greater than b'); # aborts with a custom error message&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===carttogeod()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = carttogeod(x, y, z);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=945|t=Source}}&lt;br /&gt;
|text = Converts {{wikipedia|ECEF|Earth-centered, Earth-fixed}} coordinates (x, y and z) to {{wikipedia|geodetic coordinates}} (latitude, longitude, and altitude). A vector is returned containing latitude and longitude, both in degrees, and altitude, which is returned in metres above the equatorial radius of Earth as defined by the {{wikipedia|WGS 84}} (6,378,137 metres).&amp;lt;ref&amp;gt;{{simgear file|simgear/math/sg_geodesy.hxx|l=43}}&amp;lt;/ref&amp;gt;&lt;br /&gt;
|param1 = x&lt;br /&gt;
|param1text = Mandatory x-axis value, in metres.&lt;br /&gt;
|param2 = y&lt;br /&gt;
|param2text = Mandatory y-axis value, in metres.&lt;br /&gt;
|param3 = z&lt;br /&gt;
|param3text = Mandatory z-axis value, in metres.&lt;br /&gt;
|example1 = var (lat, lon, alt) = carttogeod(6378137, 0, 0); # point is the intersection of the prime meridian and equator.&lt;br /&gt;
print(&amp;quot;Latitude: &amp;quot;, lat); # prints lat, lon and alt, which are all zero, see above&lt;br /&gt;
print(&amp;quot;Longitude: &amp;quot;, lon);&lt;br /&gt;
print(&amp;quot;Altitude: &amp;quot;, alt);&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===cmdarg()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|private = _cmdarg()&lt;br /&gt;
|syntax = cmdarg();&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=513|t=Part 1}} {{!}} {{fgdata file|Nasal/globals.nas|t=Part 2}}&lt;br /&gt;
|text = &amp;lt;code&amp;gt;cmdarg()&amp;lt;/code&amp;gt; returns the property root of certain types of XML files. These could be nodes in the [[Property Tree]], or temporary and/or non-public nodes outside the Property tree. &lt;br /&gt;
It is used by Nasal scripts embedded in XML files. It returns a &amp;lt;code&amp;gt;props.Node&amp;lt;/code&amp;gt; object (see {{fgdata file|Nasal/props.nas}}), and you can use all of its methods on the returned value. &amp;lt;code&amp;gt;cmdarg()&amp;lt;/code&amp;gt; should only be used in four types/places of XML files:&lt;br /&gt;
* Bindings: This is needed so that the value of a joystick's axis can be accessed internally.&lt;br /&gt;
* Dialogs: This will return the root of the dialog in the Property Tree. This is useful for dialogs that are created/modified procedurally (e.g. for populating/changing widgets while loading the dialog). &lt;br /&gt;
* Embedded Canvases: The Nasal code behind [[Canvas]] windows [[Howto:Adding a canvas to a GUI dialog|embedded in PUI dialogs]] can use it to accessing the root directory of their Canvas.&lt;br /&gt;
* Animation XML files: If the animation XML file is used in an AI/MP model, &amp;lt;code&amp;gt;cmdarg()&amp;lt;/code&amp;gt; will return the root of the AI model in the &amp;lt;code&amp;gt;/ai/models/&amp;lt;/code&amp;gt; directory. Examples: &amp;lt;code&amp;gt;/ai/models/aircraft[3]/&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/ai/models/multiplayer[1]/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You should not use &amp;lt;code&amp;gt;cmdarg()&amp;lt;/code&amp;gt; in places other than those stated above. Although it won't cause an error, it will return the value of the last legitimate &amp;lt;code&amp;gt;cmdarg()&amp;lt;/code&amp;gt; call. &lt;br /&gt;
&lt;br /&gt;
Also, you should not delay &amp;lt;code&amp;gt;cmdarg()&amp;lt;/code&amp;gt; using {{func link|maketimer()}}, {{func link|settimer()}} or {{func link|setlistener()}}, because it will return an unrelated property.&lt;br /&gt;
|example1 = fgcommand(&amp;quot;dialog-show&amp;quot;, {&amp;quot;dialog-name&amp;quot;: &amp;quot;cmdarg-demo&amp;quot;});&lt;br /&gt;
|example1text = &amp;lt;br&amp;gt;This example demonstrates the usage of &amp;lt;code&amp;gt;cmdarg()&amp;lt;/code&amp;gt; in a binding.  Save the below XML snippet as &amp;lt;tt&amp;gt;[[$FG_ROOT]]/gui/dialogs/cmdarg-demo.xml&amp;lt;/tt&amp;gt;. Then run the Nasal snippet below in your [[Nasal Console]]. Upon clicking {{button|Close}}, a message will be printed sowing the root of the binding in the Property Tree.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;PropertyList&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;name&amp;gt;cmdarg-demo&amp;lt;/name&amp;gt;&lt;br /&gt;
&amp;lt;layout&amp;gt;vbox&amp;lt;/layout&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;text&amp;gt;&lt;br /&gt;
  &amp;lt;label&amp;gt;Click &amp;quot;Close&amp;quot; to activate the demonstration (a message in the console).&amp;lt;/label&amp;gt;&lt;br /&gt;
&amp;lt;/text&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;button&amp;gt;&lt;br /&gt;
  &amp;lt;legend&amp;gt;Close&amp;lt;/legend&amp;gt;&lt;br /&gt;
  &amp;lt;binding&amp;gt;&lt;br /&gt;
    &amp;lt;command&amp;gt;nasal&amp;lt;/command&amp;gt;&lt;br /&gt;
    &amp;lt;script&amp;gt;print(&amp;quot;Button binding root: '&amp;quot; ~ cmdarg().getPath() ~ &amp;quot;'&amp;quot;);&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;/binding&amp;gt;&lt;br /&gt;
  &amp;lt;binding&amp;gt;&lt;br /&gt;
    &amp;lt;command&amp;gt;dialog-close&amp;lt;/command&amp;gt;&lt;br /&gt;
  &amp;lt;/binding&amp;gt;&lt;br /&gt;
&amp;lt;/button&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/PropertyList&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|example2text = This example demonstrates the usage of &amp;lt;code&amp;gt;cmdarg()&amp;lt;/code&amp;gt; in Nasal code within dialogs.  Open &amp;lt;tt&amp;gt;[[$FG_ROOT]]/gui/dialogs/cmdarg-demo.xml&amp;lt;/tt&amp;gt; from the previous example, copy &amp;amp; paste the code below, and save it. Then run the same Nasal snippet as the previous example in your Nasal Console. If you click {{button|Click me!}}, the button's label will change to &amp;quot;I've been changed!&amp;quot;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;PropertyList&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;name&amp;gt;cmdarg-demo&amp;lt;/name&amp;gt;&lt;br /&gt;
&amp;lt;layout&amp;gt;vbox&amp;lt;/layout&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;text&amp;gt;&lt;br /&gt;
  &amp;lt;label&amp;gt;Click &amp;quot;Click me!&amp;quot; to activate the demonstration (the button's label will change).&amp;lt;/label&amp;gt;&lt;br /&gt;
&amp;lt;/text&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;button&amp;gt;&lt;br /&gt;
  &amp;lt;legend&amp;gt;Click me!&amp;lt;/legend&amp;gt;&lt;br /&gt;
  &amp;lt;binding&amp;gt;&lt;br /&gt;
    &amp;lt;command&amp;gt;nasal&amp;lt;/command&amp;gt;&lt;br /&gt;
    &amp;lt;script&amp;gt;change_label();&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;/binding&amp;gt;&lt;br /&gt;
&amp;lt;/button&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;button&amp;gt;&lt;br /&gt;
  &amp;lt;legend&amp;gt;Close&amp;lt;/legend&amp;gt;&lt;br /&gt;
  &amp;lt;binding&amp;gt;&lt;br /&gt;
    &amp;lt;command&amp;gt;dialog-close&amp;lt;/command&amp;gt;&lt;br /&gt;
  &amp;lt;/binding&amp;gt;&lt;br /&gt;
&amp;lt;/button&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nasal&amp;gt;&lt;br /&gt;
  &amp;lt;open&amp;gt;&amp;lt;![CDATA[&lt;br /&gt;
    var dlg_root = cmdarg();&lt;br /&gt;
    var dlg_name = {&amp;quot;dialog-name&amp;quot;: &amp;quot;cmdarg-demo&amp;quot;};&lt;br /&gt;
    var change_label = func {&lt;br /&gt;
        dlg_root.getNode(&amp;quot;button[0]/legend&amp;quot;).setValue(&amp;quot;I've been changed!&amp;quot;);&lt;br /&gt;
        fgcommand(&amp;quot;dialog-close&amp;quot;, dlg_name);&lt;br /&gt;
        fgcommand(&amp;quot;dialog-show&amp;quot;, dlg_name);&lt;br /&gt;
    }&lt;br /&gt;
  ]]&amp;gt;&amp;lt;/open&amp;gt;&lt;br /&gt;
&amp;lt;/nasal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/PropertyList&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|example3text = For an example of &amp;lt;code&amp;gt;cmdarg()&amp;lt;/code&amp;gt; used with Canvas, please see [[Howto:Adding a canvas to a GUI dialog#FGPlot|Howto:Adding a canvas to a GUI dialog]].&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===courseAndDistance()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = courseAndDistance(to);&lt;br /&gt;
courseAndDistance(from, to);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1668|t=Source}}&lt;br /&gt;
|text = Returns a vector containing the course from one point to another and the distance between them in nautical miles. The course is the initial bearing (see [http://www.movable-type.co.uk/scripts/latlong.html#bearing here]), and is in the range 0–360. Both arguments can be one of:&lt;br /&gt;
:* An &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;runway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;taxiway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fix&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;waypoint&amp;lt;/code&amp;gt; ghost type&lt;br /&gt;
:* A hash with ''lat'' and ''lon'' members&lt;br /&gt;
:* A geo.Coord object with geodetic coordinates (cartesian coordinates will not be accepted)&lt;br /&gt;
|param1 = from&lt;br /&gt;
|param1text = Optional parameter defining the from where the function should calculate its results. If the function is given one argument ('''to'''), the aircraft's current position will be used. As well as the argument types as defined above, this argument can be two numbers separated with a comma, as if the function is taking three arguments. See example 5 below.&lt;br /&gt;
|param2 = to&lt;br /&gt;
|param2text = Like the first parameter, but defines the second point.&lt;br /&gt;
|example1text = This example demonstrates the usage of the function with the &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt; ghost type.&lt;br /&gt;
|example1 = var from = airportinfo(&amp;quot;KSFO&amp;quot;);&lt;br /&gt;
var to = airportinfo(&amp;quot;KSQL&amp;quot;);&lt;br /&gt;
var (course, dist) = courseAndDistance(from, to);&lt;br /&gt;
print(course); # prints course from KSFO to KSQL&lt;br /&gt;
print(dist); # prints distance in nm from KSFO to KSQL&lt;br /&gt;
|example2text = This example demonstrates the usage of the function with hashes containing ''lat'' and ''lon''.&lt;br /&gt;
|example2 = var from = {lat: 0, lon: 0};&lt;br /&gt;
var to = {lat: 1, lon: 1};&lt;br /&gt;
var (course, dist) = courseAndDistance(from, to);&lt;br /&gt;
print(course);&lt;br /&gt;
print(dist);&lt;br /&gt;
|example3text = This example demonstrates usage of a geo.Coord object.&lt;br /&gt;
|example3 = var from = geo.Coord.new().set_latlon(0, 0);&lt;br /&gt;
var to = geo.Coord.new().set_latlon(1, 1);&lt;br /&gt;
var (course, dist) = courseAndDistance(from, to);&lt;br /&gt;
print(course);&lt;br /&gt;
print(dist);&lt;br /&gt;
|example4text = This example demonstrates usage of differing parameter types.&lt;br /&gt;
|example4 = var from = airportinfo(&amp;quot;KSFO&amp;quot;);&lt;br /&gt;
var to = geo.Coord.new().set_latlon(0, 0);&lt;br /&gt;
var (course, dist) = courseAndDistance(from, to);&lt;br /&gt;
print(course);&lt;br /&gt;
print(dist);&lt;br /&gt;
|example5text = The same as above, but the other way round.&lt;br /&gt;
|example5 = var to = {lat: 1, lon: 1};&lt;br /&gt;
var (course, dist) = courseAndDistance(0, 0, to);&lt;br /&gt;
print(course);&lt;br /&gt;
print(dist);&lt;br /&gt;
|example6text = Usage of just one parameter.&lt;br /&gt;
|example6 = var dest = airportinfo(&amp;quot;KSQL&amp;quot;);&lt;br /&gt;
var (course, dist) = courseAndDistance(dest);&lt;br /&gt;
print(&amp;quot;Turn to heading &amp;quot;, math.round(course), &amp;quot;. You have &amp;quot;, sprintf(&amp;quot;%.2f&amp;quot;, dist), &amp;quot; nm to go&amp;quot;);&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===createFlightplan()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = createFlightplan(path);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=2331|t=Source}}&lt;br /&gt;
|text = Creates an empty flightplan object. It accepts one argument, ''path'' passed an absolute path to a .fgfp / .gpx file, it will populate the flightplan with waypoints from the file.&lt;br /&gt;
|param1 = path&lt;br /&gt;
|param1text = Optional parameter defining the file from which a flightplan will be populated.&lt;br /&gt;
|example1 = &lt;br /&gt;
var path = getprop(&amp;quot;/sim/fg-home&amp;quot;) ~ &amp;quot;/Export/test.fgfp&amp;quot;;&lt;br /&gt;
var flightplan = createFlightplan(path);&lt;br /&gt;
debug.dump(flightplan);&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===createDiscontinuity()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = createDiscontinuity();&lt;br /&gt;
|text = Returns a &amp;lt;code&amp;gt;waypoint&amp;lt;/code&amp;gt; ghost object. A route discontinuity is inserted by an {{abbr|FMS|Flight Management System}} when it is unsure how to connect two waypoints.&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=2045|t=Source}}&lt;br /&gt;
|version = 2016.1&lt;br /&gt;
|commit = {{flightgear commit|caead6|t=commit}}&lt;br /&gt;
}}&lt;br /&gt;
===createViaTo()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = createViaTo(airway, waypoint);&lt;br /&gt;
|text = Returns a &amp;lt;code&amp;gt;waypoint&amp;lt;/code&amp;gt; ghost object. It represents a route &amp;quot;via '''airway''' to '''waypoint'''&amp;quot;.&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=2009|t=Source}}&lt;br /&gt;
|version = 2016.1&lt;br /&gt;
|commit = {{flightgear commit|caead6|t=commit}}&lt;br /&gt;
|param1 = airway&lt;br /&gt;
|param1text = The name of an airway.&lt;br /&gt;
|param2 = waypoint&lt;br /&gt;
|param2text = Must be in the airway and one of:&lt;br /&gt;
* The name of a waypoint.&lt;br /&gt;
* An &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;runway&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;fix&amp;lt;/code&amp;gt; ghost object.&lt;br /&gt;
}}&lt;br /&gt;
===createWP()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = createWP(pos, name[, flag]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1964|t=Source}}&lt;br /&gt;
|text = Creates a new waypoint ghost object.&lt;br /&gt;
|param1 = pos&lt;br /&gt;
|param1text = Dictates the position of the new waypoint. It can be one of the following:&lt;br /&gt;
:* An &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;runway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;taxiway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fix&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;waypoint&amp;lt;/code&amp;gt; ghost type&lt;br /&gt;
:* A hash with ''lat'' and ''lon'' members&lt;br /&gt;
:* A geo.Coord object&lt;br /&gt;
:* Two numbers separated by a comma, as if the function is taking three arguments. See example 4 below.&lt;br /&gt;
|param2 = name&lt;br /&gt;
|param2text = String that will become the name of the new waypoint.&lt;br /&gt;
|param3 = flag&lt;br /&gt;
|param3text = Optional string that will tell FlightGear what type of waypoint it is. Must be one of &amp;quot;sid,&amp;quot; &amp;quot;star,&amp;quot; &amp;quot;approach,&amp;quot; &amp;quot;missed,&amp;quot; or &amp;quot;pseudo.&amp;quot;&lt;br /&gt;
|example1text = Creates a waypoint directly in front and 1 km away and appends it to the flight plan.&lt;br /&gt;
|example1 = var pos = geo.aircraft_position().apply_course_distance(getprop(&amp;quot;/orientation/heading-deg&amp;quot;), 1000);&lt;br /&gt;
var wp = createWP(pos, &amp;quot;NEWWP&amp;quot;);&lt;br /&gt;
var fp = flightplan();&lt;br /&gt;
fp.appendWP(wp);&lt;br /&gt;
|example2 = var pos = geo.aircraft_position().apply_course_distance(getprop(&amp;quot;/orientation/heading-deg&amp;quot;), 1000);&lt;br /&gt;
var wp = createWP({lat: pos.lat(), lon: pos.lon()}, &amp;quot;NEWWP&amp;quot;);&lt;br /&gt;
var fp = flightplan();&lt;br /&gt;
fp.appendWP(wp);&lt;br /&gt;
|example3 = var apt = airportinfo();&lt;br /&gt;
var wp = createWP(apt, &amp;quot;NEWWP&amp;quot;);&lt;br /&gt;
var fp = flightplan();&lt;br /&gt;
fp.appendWP(wp);&lt;br /&gt;
|example4 = var pos = geo.aircraft_position().apply_course_distance(getprop(&amp;quot;/orientation/heading-deg&amp;quot;), 1000);&lt;br /&gt;
var wp = createWP(pos.lat(), pos.lon(), &amp;quot;NEWWP&amp;quot;);&lt;br /&gt;
var fp = flightplan();&lt;br /&gt;
fp.appendWP(wp);&lt;br /&gt;
|example5text = Creates a new waypoint and adds it to the flight plan. Waypoints of the type &amp;quot;pseudo&amp;quot; are then removed from the flight plan, including the new waypoint. The {{func link|print()}} statements show this.&lt;br /&gt;
|example5 = var pos = geo.aircraft_position().apply_course_distance(getprop(&amp;quot;/orientation/heading-deg&amp;quot;), 1000);&lt;br /&gt;
var wp = createWP(pos, &amp;quot;NEWWP&amp;quot;, &amp;quot;pseudo&amp;quot;);&lt;br /&gt;
var fp = flightplan();&lt;br /&gt;
fp.appendWP(wp);&lt;br /&gt;
print(fp.getPlanSize());&lt;br /&gt;
fp.clearWPType(&amp;quot;pseudo&amp;quot;);&lt;br /&gt;
print(fp.getPlanSize());&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===createWPFrom()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = createWPFrom(object[, flag]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1989|t=Source}}&lt;br /&gt;
|text = Creates a new waypoint object from another object.&lt;br /&gt;
|param1 = object&lt;br /&gt;
|param1text = A ghost object. Must be a ghost type that is one of &amp;quot;airport,&amp;quot; &amp;quot;navaid,&amp;quot; &amp;quot;runway,&amp;quot; or &amp;quot;fix.&amp;quot;&lt;br /&gt;
|param2 = flag&lt;br /&gt;
|param2text = Optional string that will tell FlightGear what type of waypoint it is. Must be one of &amp;quot;sid,&amp;quot; &amp;quot;star,&amp;quot; &amp;quot;approach,&amp;quot; &amp;quot;missed,&amp;quot; or &amp;quot;pseudo.&amp;quot;&lt;br /&gt;
|example1text = Creates a new waypoint and appends it to the flight plan.&lt;br /&gt;
|example1 = var apt = airportinfo(&amp;quot;KSFO&amp;quot;);&lt;br /&gt;
var wp = createWPFrom(apt);&lt;br /&gt;
var fp = flightplan();&lt;br /&gt;
fp.appendWP(wp);&lt;br /&gt;
|example2text = Creates a new waypoint and appends it to the flight plan. This way point is then removed; the {{func link|print()}} statements prove this.&lt;br /&gt;
|example2 = var apt = airportinfo(&amp;quot;KSFO&amp;quot;);&lt;br /&gt;
var wp = createWPFrom(apt, &amp;quot;pseudo&amp;quot;);&lt;br /&gt;
print(wp.wp_name);&lt;br /&gt;
var fp = flightplan();&lt;br /&gt;
fp.appendWP(wp);&lt;br /&gt;
print(fp.getPlanSize());&lt;br /&gt;
fp.clearWPType(&amp;quot;pseudo&amp;quot;);&lt;br /&gt;
print(fp.getPlanSize());&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===defined()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = defined(symbol);&lt;br /&gt;
|source = {{fgdata file|Nasal/globals.nas|t=Source}}&lt;br /&gt;
|text = Returns 1 (true) or 0 (false) depending on whether a variable exists.&lt;br /&gt;
|param1 = symbol&lt;br /&gt;
|param1text = A string that will be what the function searches for.&lt;br /&gt;
|example1 = var number = 12;&lt;br /&gt;
var check_exist = func {&lt;br /&gt;
    print(&amp;quot;Variable 'number' &amp;quot;, defined(&amp;quot;number&amp;quot;) == 1 ? &amp;quot;exists&amp;quot; : &amp;quot;does not exist&amp;quot;); # 'number' does exist&lt;br /&gt;
    print(&amp;quot;Variable 'number2' &amp;quot;, defined(&amp;quot;number2&amp;quot;) == 1 ? &amp;quot;exists&amp;quot; : &amp;quot;does not exist&amp;quot;); # 'number2' does not exist&lt;br /&gt;
}&lt;br /&gt;
check_exist();&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===directory()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = directory(path);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=572|t=Source}}&lt;br /&gt;
|text = Returns a vector containing a list of the folders and files in a given file path or &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; if the path doesn't exist. Hidden folders and files are not added to the vector.&lt;br /&gt;
{{inote|The first two elements of the vector will be &amp;lt;code&amp;gt;'.'&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;'..'&amp;lt;/code&amp;gt;. These are for navigating back up the file tree, but have no use in Nasal. They can be safely removed from the vector.}}&lt;br /&gt;
|param1 = path&lt;br /&gt;
|param1text = Absolute file path.&lt;br /&gt;
|example1text = Gets the folders and files in [[$FG_ROOT]], and then removes the extra first two elements (see note above). &lt;br /&gt;
|example1 = var dir = directory(getprop(&amp;quot;/sim/fg-root&amp;quot;)); # get directory&lt;br /&gt;
dir = subvec(dir, 2); # strips off the first two elements&lt;br /&gt;
debug.dump(dir); # dump the vector&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===fgcommand()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = fgcommand(cmd[, args]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=456|t=Part 1}} {{!}} {{fgdata file|Nasal/globals.nas|t=Part 2}}&lt;br /&gt;
|text = Runs an fgcommand. See also {{readme file|commands}} and [[Bindings]] for more information. See {{flightgear file|src/Main/fg_commands.cxx|l=1425}} for the full list of fgcommands. Note that fgcommands generated by {{func link|addcommand()}} can also be run using this function. Also, the full list of fgcommands depends on the version of FlightGear you have. Returns 1 (true) if the fgcommand succeeded or 0 (false) if it failed.&lt;br /&gt;
|param1 = cmd&lt;br /&gt;
|param1text = String that is the name of the command that is to be run.&lt;br /&gt;
|param2 = args&lt;br /&gt;
|param2text = If the fgcommand takes arguments, they are inputted using this argument. Can either be a &amp;lt;code&amp;gt;props.Node&amp;lt;/code&amp;gt; object, or a hash (see examples below).&lt;br /&gt;
|example1 = fgcommand(&amp;quot;null&amp;quot;); # does nothing&lt;br /&gt;
|example2 = var args = props.Node.new({'script': 'print(&amp;quot;Running fgcommand&amp;quot;);'});&lt;br /&gt;
if (fgcommand(&amp;quot;nasal&amp;quot;, args)) { # prints &amp;quot;Running fgcommand&amp;quot; and then one of these print statements&lt;br /&gt;
    print(&amp;quot;Fgcommand succeeded&amp;quot;);&lt;br /&gt;
} else {&lt;br /&gt;
    print(&amp;quot;Fgcommand encountered a problem&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
|example3 = var args = { 'dialog-name': 'about' };&lt;br /&gt;
fgcommand(&amp;quot;dialog-show&amp;quot;, args); # shows the 'about' dialog&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===findAirportsByICAO() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = findAirportsByICAO(search[, type]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1096|t=Source}}&lt;br /&gt;
|text = Returns a vector containing &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt; ghost objects which are (by default) airports whose ICAO code matches the search string. The results are sorted by range from closest to furthest.&lt;br /&gt;
|param1 = search&lt;br /&gt;
|param1text = Search string for the function. Can either be a partial or a full ICAO code.&lt;br /&gt;
:{{icaution|The more matches there are for the given code, the longer the function will take. Passing just one character (e.g., &amp;quot;K&amp;quot;), might make FlightGear hang for a certain amount of time.}}&lt;br /&gt;
|param2 = type&lt;br /&gt;
|param2text = This will narrow the search to airports of a certain type. By default, only airports are searched for. May be one of &amp;quot;airport,&amp;quot; &amp;quot;heliport,&amp;quot; or &amp;quot;seaport.&amp;quot;&lt;br /&gt;
|example1 = var apts = findAirportsByICAO(&amp;quot;KSF&amp;quot;); # finds all airports matching &amp;quot;KSF&amp;quot;&lt;br /&gt;
foreach(var apt; apts){&lt;br /&gt;
    print(apt.name, &amp;quot; (&amp;quot;, apt.id, &amp;quot;)&amp;quot;); # prints them&lt;br /&gt;
}&lt;br /&gt;
|example2 = var apts = findAirportsByICAO(&amp;quot;SP0&amp;quot;, &amp;quot;seaport&amp;quot;); # finds all seaplane bases matching &amp;quot;SP0&amp;quot;&lt;br /&gt;
foreach(var apt; apts){&lt;br /&gt;
    print(apt.name, &amp;quot; (&amp;quot;, apt.id, &amp;quot;)&amp;quot;); # prints them&lt;br /&gt;
}&lt;br /&gt;
|example3 = var apt = findAirportsByICAO(&amp;quot;XBET&amp;quot;); # one way to check if an airport does exist&amp;quot;&lt;br /&gt;
if (size(apt) == 0) {&lt;br /&gt;
    print(&amp;quot;Airport does not exist&amp;quot;); # this one will be printed&lt;br /&gt;
} else {&lt;br /&gt;
    print(&amp;quot;Airport does exist&amp;quot;);&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===findAirportsWithinRange()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = findAirportsWithinRange([pos, ]range[, type]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1066|t=Source}}&lt;br /&gt;
|text = Returns a vector of &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt; ghost object which are (by default) airports that are within a given range of a given position, or the aircraft's current position. The results are sorted by range from closest to furthest.&lt;br /&gt;
|param1 = pos&lt;br /&gt;
|param1text = Optional position to search around. If not given, the aircraft's current position will be used. Can be one of:&lt;br /&gt;
:* An &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;runway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;taxiway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fix&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;waypoint&amp;lt;/code&amp;gt; ghost type&lt;br /&gt;
:* A hash with ''lat'' and ''lon'' members&lt;br /&gt;
:* A geo.Coord object&lt;br /&gt;
:* Two numbers separated by a comma, as if the function is taking three arguments. Example: &amp;lt;code&amp;gt;findAirportsWithinRange(lat, lon, range, type);&amp;lt;/code&amp;gt;.&lt;br /&gt;
|param2 = range&lt;br /&gt;
|param2text = Mandatory number giving the range in nautical miles within which to search for airports/heliports/seaplane bases.only airports are searched for.&lt;br /&gt;
|param3 = type&lt;br /&gt;
|param3text = This will narrow the search to airports of a certain type. By default, only airports are searched for. May be one of &amp;quot;airport,&amp;quot; &amp;quot;heliport,&amp;quot; or &amp;quot;seaport.&amp;quot;&lt;br /&gt;
|example1text = Searches for airports within 10 nm of [[KSFO]].&lt;br /&gt;
|example1 = var pos = airportinfo(&amp;quot;KSFO&amp;quot;);&lt;br /&gt;
var apts = findAirportsWithinRange(pos, 10);&lt;br /&gt;
foreach(var apt; apts){&lt;br /&gt;
    print(apt.name, &amp;quot; (&amp;quot;, apt.id, &amp;quot;)&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
|example2text = Searches for seaplane bases within 15 nm of [[KSFO]].&lt;br /&gt;
|example2 = var pos = airportinfo(&amp;quot;KSFO&amp;quot;);&lt;br /&gt;
var apts = findAirportsWithinRange(pos, 15, &amp;quot;seaport&amp;quot;);&lt;br /&gt;
foreach(var apt; apts){&lt;br /&gt;
    print(apt.name, &amp;quot; (&amp;quot;, apt.id, &amp;quot;)&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
|example3text = Searches for airports within 10 nm of your current position.&lt;br /&gt;
|example3 = var apts = findAirportsWithinRange(10);&lt;br /&gt;
foreach(var apt; apts){&lt;br /&gt;
    print(apt.name, &amp;quot; (&amp;quot;, apt.id, &amp;quot;)&amp;quot;);&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===findCommByFrequencyMHz()===&lt;br /&gt;
 findCommByFrequencyMHz([pos, ]freq[, type]);&lt;br /&gt;
[[sourceforge:p/flightgear/flightgear/ci/next/tree/src/Scripting/NasalPositioned.cxx#l1547|source]]&lt;br /&gt;
&lt;br /&gt;
Returns a &amp;lt;code&amp;gt;comm&amp;lt;/code&amp;gt; ghost object for a station matching a given frequency. If there is more than one station with that frequency, the nearest station is returned.&lt;br /&gt;
pos&lt;br /&gt;
&lt;br /&gt;
: Optional position to search around. If not given, the aircraft's current position will be used. Can be one of:&lt;br /&gt;
:* An &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;runway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;taxiway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fix&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;waypoint&amp;lt;/code&amp;gt; ghost type&lt;br /&gt;
:* A hash with ''lat'' and ''lon'' members&lt;br /&gt;
:* A geo.Coord object&lt;br /&gt;
:* Two numbers separated by a comma, as if the function is taking three arguments. Example: &amp;lt;code&amp;gt;findCommByFrequencyMHz(lat, lon, freq, type);&amp;lt;/code&amp;gt;.&lt;br /&gt;
; freq&lt;br /&gt;
: Frequency, in megahertz, of the station to search for.&lt;br /&gt;
; type&lt;br /&gt;
: This will narrow the search to station of a certain type. Defaults to &amp;quot;all.&amp;quot; For the full list of accepted type arguments, see flightgear/src/Navaids/positioned.cxx (line 160)&lt;br /&gt;
'''Example'''&lt;br /&gt;
 var com = findCommByFrequencyMHz(123.6);&lt;br /&gt;
 print(&amp;quot;ID: &amp;quot;, com.id); # prints info about the comm station&lt;br /&gt;
 print(&amp;quot;Name: &amp;quot;, com.name);&lt;br /&gt;
 print(&amp;quot;Latitude: &amp;quot;, com.lat);&lt;br /&gt;
 print(&amp;quot;Longitude: &amp;quot;, com.lon);&lt;br /&gt;
 print(&amp;quot;Type: &amp;quot;, com.type);&lt;br /&gt;
 print(&amp;quot;Frequency: &amp;quot;, sprintf(&amp;quot;%.3f&amp;quot;, com.frequency), &amp;quot; Mhz&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
=== findFixesByID() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = findFixesByID([pos, ]id);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1627|t=Source}}&lt;br /&gt;
|text = Returns a vector containing &amp;lt;code&amp;gt;fix&amp;lt;/code&amp;gt; ghost objects matching a given ID, sorted by range from a certain position.&lt;br /&gt;
{{inote|Fixes are (usually) also known as waypoints.}}&lt;br /&gt;
|param1 = pos&lt;br /&gt;
|param1text = Optional position to search around. If not given, the aircraft's current position will be used. Can be one of:&lt;br /&gt;
:* An &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;runway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;taxiway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fix&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;waypoint&amp;lt;/code&amp;gt; ghost type&lt;br /&gt;
:* A hash with ''lat'' and ''lon'' members&lt;br /&gt;
:* A geo.Coord object&lt;br /&gt;
:* Two numbers separated by a comma, as if the function is taking three arguments. Example: &amp;lt;code&amp;gt;findFixesByID(lat, lon, id);&amp;lt;/code&amp;gt;.&lt;br /&gt;
|param2 = id&lt;br /&gt;
|param2text = Full or partial ID of the fix to search for.&lt;br /&gt;
:{{inote|1=Inputting a partial ID does not work correctly (see [http://forum.flightgear.org/viewtopic.php?f=30&amp;amp;t=28129 here]). It is best to just input a full ID.}}&lt;br /&gt;
|example1 = var fixes = findFixesByID(&amp;quot;POGIC&amp;quot;);&lt;br /&gt;
foreach(var fix; fixes){&lt;br /&gt;
    print(fix.id, &amp;quot; - lat: &amp;quot;, fix.lat, &amp;quot; {{!}} lon: &amp;quot;, fix.lon); # prints information about POGIC&lt;br /&gt;
}&lt;br /&gt;
|example2 = var fix = findFixesByID(&amp;quot;ZUNAP&amp;quot;);&lt;br /&gt;
fix = fix[0];&lt;br /&gt;
var (course, dist) = courseAndDistance(fix);&lt;br /&gt;
print(&amp;quot;Turn to heading &amp;quot;, math.round(course), &amp;quot;. You have &amp;quot;, sprintf(&amp;quot;%.2f&amp;quot;, dist), &amp;quot; nm to go to reach &amp;quot;, fixes[0].id);&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===findNavaidByFrequencyMHz()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = findNavaidByFrequencyMHz([pos, ]freq[, type]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1547|t=Source}}&lt;br /&gt;
|text = Returns a &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt; ghost object for a navaid matching a given frequency. If there is more than one navaid with that frequency, the nearest station is returned.&lt;br /&gt;
|param1 = pos&lt;br /&gt;
|param1text = Optional position to search around. If not given, the aircraft's current position will be used. Can be one of:&lt;br /&gt;
:* An &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;runway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;taxiway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fix&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;waypoint&amp;lt;/code&amp;gt; ghost type&lt;br /&gt;
:* A hash with ''lat'' and ''lon'' members&lt;br /&gt;
:* A geo.Coord object&lt;br /&gt;
:* Two numbers separated by a comma, as if the function is taking three arguments. Example: &amp;lt;code&amp;gt;findNavaidByFrequencyMHz(lat, lon, freq, type);&amp;lt;/code&amp;gt;.&lt;br /&gt;
|param2 = freq&lt;br /&gt;
|param2text = Frequency, in megahertz, of the navaid to search for.&lt;br /&gt;
|param3 = type&lt;br /&gt;
|param3text = This will narrow the search to navaids of a certain type. Defaults to &amp;quot;all.&amp;quot; For the full list of accepted type arguments, see {{flightgear file|src/Navaids/positioned.cxx|l=127}}.&lt;br /&gt;
|example1 = var navaid = findNavaidByFrequencyMHz(109.55);&lt;br /&gt;
print(&amp;quot;ID: &amp;quot;, navaid.id); # prints info about the navaid&lt;br /&gt;
print(&amp;quot;Name: &amp;quot;, navaid.name);&lt;br /&gt;
print(&amp;quot;Latitude: &amp;quot;, navaid.lat);&lt;br /&gt;
print(&amp;quot;Longitude: &amp;quot;, navaid.lon);&lt;br /&gt;
print(&amp;quot;Elevation (AMSL): &amp;quot;, navaid.elevation, &amp;quot; m&amp;quot;);&lt;br /&gt;
print(&amp;quot;Type: &amp;quot;, navaid.type);&lt;br /&gt;
print(&amp;quot;Frequency: &amp;quot;, sprintf(&amp;quot;%.3f&amp;quot;, navaid.frequency / 100), &amp;quot; Mhz&amp;quot;);&lt;br /&gt;
print(&amp;quot;Range: &amp;quot;, navaid.range_nm, &amp;quot; nm&amp;quot;);&lt;br /&gt;
if(navaid.course) print(&amp;quot;Course: &amp;quot;, navaid.course);&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== findNavaidsByFrequencyMHz()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = findNavaidsByFrequencyMHz([pos, ]freq[, type]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1572|t=Source}}&lt;br /&gt;
|text = Returns a vector conatining &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt; ghost objects for navaids that match a given frequency, sorted from nearest to furthest.&lt;br /&gt;
|param1 = pos&lt;br /&gt;
|param1text = Optional position to search around. If not given, the aircraft's current position will be used. Can be one of:&lt;br /&gt;
:* An &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;runway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;taxiway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fix&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;waypoint&amp;lt;/code&amp;gt; ghost type&lt;br /&gt;
:* A hash with ''lat'' and ''lon'' members&lt;br /&gt;
:* A geo.Coord object&lt;br /&gt;
:* Two numbers separated by a comma, as if the function is taking three arguments. Example: &amp;lt;code&amp;gt;findNavaidsByFrequencyMHz(lat, lon, freq, type);&amp;lt;/code&amp;gt;.&lt;br /&gt;
|param2 = freq&lt;br /&gt;
|param2text = Frequency, in megahertz, of the navaid to search for.&lt;br /&gt;
|param3 = type&lt;br /&gt;
|param3text = This will narrow the search to navaids of a certain type. Defaults to &amp;quot;all.&amp;quot; For the full list of accepted type arguments, see {{flightgear file|src/Navaids/positioned.cxx|l=127}}.&lt;br /&gt;
|example1 = var navaids = findNavaidsByFrequencyMHz(109.55);&lt;br /&gt;
foreach(var navaid; navaids){&lt;br /&gt;
    print(&amp;quot;--&amp;quot;);&lt;br /&gt;
    print(&amp;quot;ID: &amp;quot;, navaid.id); # prints info about the navaid&lt;br /&gt;
    print(&amp;quot;Name: &amp;quot;, navaid.name);&lt;br /&gt;
    print(&amp;quot;Latitude: &amp;quot;, navaid.lat);&lt;br /&gt;
    print(&amp;quot;Longitude: &amp;quot;, navaid.lon);&lt;br /&gt;
    print(&amp;quot;Elevation (AMSL): &amp;quot;, navaid.elevation, &amp;quot; m&amp;quot;);&lt;br /&gt;
    print(&amp;quot;Type: &amp;quot;, navaid.type);&lt;br /&gt;
    print(&amp;quot;Frequency: &amp;quot;, sprintf(&amp;quot;%.3f&amp;quot;, navaid.frequency / 100), &amp;quot; Mhz&amp;quot;);&lt;br /&gt;
    print(&amp;quot;Range: &amp;quot;, navaid.range_nm, &amp;quot; nm&amp;quot;);&lt;br /&gt;
    if(navaid.course) print(&amp;quot;Course: &amp;quot;, navaid.course);&lt;br /&gt;
    print(&amp;quot;--&amp;quot;);&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===findNavaidsByID()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = findNavaidsByID([pos, ]id[, type]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1600|t=Source}}&lt;br /&gt;
|text = Returns a vector containing &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt; ghost objects matching a given ID, sorted by range from a certain position.&lt;br /&gt;
|param1 = pos&lt;br /&gt;
|param1text = Optional position to search around. If not given, the aircraft's current position will be used. Can be one of:&lt;br /&gt;
:* An &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;runway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;taxiway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fix&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;waypoint&amp;lt;/code&amp;gt; ghost type&lt;br /&gt;
:* A hash with ''lat'' and ''lon'' members&lt;br /&gt;
:* A geo.Coord object&lt;br /&gt;
:* Two numbers separated by a comma, as if the function is taking three arguments. Example: &amp;lt;code&amp;gt;findNavaidsByID(lat, lon, id, type);&amp;lt;/code&amp;gt;.&lt;br /&gt;
|param2 = id&lt;br /&gt;
|param2text = Full or partial ID of the fix to search for.&lt;br /&gt;
:{{inote|1=Inputting a partial ID does not work correctly (see [http://forum.flightgear.org/viewtopic.php?f=30&amp;amp;t=28129 here]). It is best to just input a full ID.}}&lt;br /&gt;
|param3 = type&lt;br /&gt;
|param3text = This will narrow the search to navaids of a certain type. Defaults to &amp;quot;all.&amp;quot; For the full list of accepted type arguments, see {{flightgear file|src/Navaids/positioned.cxx|l=127}}.&lt;br /&gt;
|example1 = var navaid = findNavaidsByID(&amp;quot;MXW&amp;quot;);&lt;br /&gt;
navaid = navaid[0];&lt;br /&gt;
print(&amp;quot;ID: &amp;quot;, navaid.id); # prints info about 'MXW' (a VOR station)&lt;br /&gt;
print(&amp;quot;Name: &amp;quot;, navaid.name);&lt;br /&gt;
print(&amp;quot;Latitude: &amp;quot;, navaid.lat);&lt;br /&gt;
print(&amp;quot;Longitude: &amp;quot;, navaid.lon);&lt;br /&gt;
print(&amp;quot;Elevation (AMSL): &amp;quot;, navaid.elevation, &amp;quot; m&amp;quot;);&lt;br /&gt;
print(&amp;quot;Type: &amp;quot;, navaid.type);&lt;br /&gt;
print(&amp;quot;Frequency: &amp;quot;, sprintf(&amp;quot;%.3f&amp;quot;, navaid.frequency / 1000), &amp;quot; Mhz&amp;quot;);&lt;br /&gt;
print(&amp;quot;Range: &amp;quot;, navaid.range_nm, &amp;quot; nm&amp;quot;);&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===findNavaidsWithinRange()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = findNavaidsWithinRange([pos, ]range[, type]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1518|t=Source}}&lt;br /&gt;
|text = Returns a vector of &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt; ghost objects which are within a given range of a given position (by default the aircraft's current position). The results are sorted from closest to furthest.&lt;br /&gt;
|param1 = pos&lt;br /&gt;
|param1text = Optional position to search around. If not given, the aircraft's current position will be used. Can be one of:&lt;br /&gt;
:* An &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;runway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;taxiway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fix&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;waypoint&amp;lt;/code&amp;gt; ghost type&lt;br /&gt;
:* A hash with ''lat'' and ''lon'' members&lt;br /&gt;
:* A geo.Coord object&lt;br /&gt;
:* Two numbers separated by a comma, as if the function is taking three arguments. Example: &amp;lt;code&amp;gt;findNavaidsWithinRange(lat, lon, range, type);&amp;lt;/code&amp;gt;.&lt;br /&gt;
|param2 = range&lt;br /&gt;
|param2text = Mandatory number giving the range in nautical miles within which to search for navaids. Defaults to &amp;quot;all.&amp;quot; For the full list of accepted type arguments, see {{flightgear file|src/Navaids/positioned.cxx|l=127}}.&lt;br /&gt;
|param3 = type&lt;br /&gt;
|param3text = This will narrow the search to navaids of a certain type.&lt;br /&gt;
|example1text = Searches for navaids within 10 nm of [[KSFO]].&lt;br /&gt;
|example1 = var pos = airportinfo(&amp;quot;KSFO&amp;quot;);&lt;br /&gt;
var navs = findNavaidsWithinRange(pos, 10);&lt;br /&gt;
foreach(var nav; navs){&lt;br /&gt;
    print(nav.name, &amp;quot; (ID: &amp;quot;, nav.id, &amp;quot;)&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
|example2text = Searches for navaids within 10 nm of your current position.&lt;br /&gt;
|example2 = var navs = findNavaidsWithinRange(10);&lt;br /&gt;
foreach(var nav; navs){&lt;br /&gt;
    print(nav.name, &amp;quot; (ID: &amp;quot;, nav.id, &amp;quot;)&amp;quot;);&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===finddata()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = finddata(path);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=603|t=Source}}&lt;br /&gt;
|text = Takes a relative path and tries to return an absolute one. It works by appending the relative path to some paths and testing to see if they exist. As of FlightGear v3.7, these paths are the TerraSync directory (tested first) and [[$FG_ROOT]]. &lt;br /&gt;
|param1 = path&lt;br /&gt;
|param1text = A relative path as a string.&lt;br /&gt;
|example1 = var path = finddata(&amp;quot;Aircraft/Generic&amp;quot;);&lt;br /&gt;
print(path); # prints the absolute path to $FG_ROOT/Aircraft/Generic&lt;br /&gt;
|example2 = var path = finddata(&amp;quot;Airports&amp;quot;);&lt;br /&gt;
print(path); # prints the absolute path to &amp;lt;TerraSync dir&amp;gt;/Airports&lt;br /&gt;
|example3 = var path = finddata(&amp;quot;preferences.xml&amp;quot;);&lt;br /&gt;
print(path); # prints the absolute path to $FG_ROOT/preferences.xml&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===flightplan()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = flightplan([path]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1738|t=Source}}&lt;br /&gt;
|text = {{see also|Nasal Flightplan}}&lt;br /&gt;
Returns a flight plan object, either one for the current flight plan, or one loaded from a given path.&lt;br /&gt;
|param1 = path&lt;br /&gt;
|param1text = Optional path to flight plan XML file.&lt;br /&gt;
|example1text = Gets the active flight plan and gets the ID of the current waypoint. Note that this example requires a flight plan to be set in the [[Route Manager]] first.&lt;br /&gt;
|example1 = var fp = flightplan();&lt;br /&gt;
print(fp.getWP(fp.current).id);&lt;br /&gt;
|example2text = Creates a new flight plan from an XML file and prints the IDs of the waypoints. Note that this example requires a flight plan to have been created and saved as &amp;lt;tt&amp;gt;''[[$FG_HOME]]/fp-demo.xml''&amp;lt;/tt&amp;gt;.&lt;br /&gt;
|example2 = var path = getprop('/sim/fg-home') ~ '/fp-demo.xml';&lt;br /&gt;
var fp = flightplan(path);&lt;br /&gt;
for(var i = 0; i &amp;lt; fp.getPlanSize(); i += 1){&lt;br /&gt;
    print(fp.getWP(i).id);&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===geodinfo()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = geodinfo(lat, lon[, max_alt]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=981|t=Source}}&lt;br /&gt;
|text = Returns a vector containing two entries or &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; if no information could be obtained because the terrain tile wasn't loaded. The first entry in the vector is the elevation (in meters) for the given point, and the second is a hash with information about the assigned material (as defined in &amp;lt;tt&amp;gt;''[[$FG_ROOT]]/Materials''&amp;lt;/tt&amp;gt;), or &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; if there was no material information available (for example, because there is an untextured building at that location). The structure of the hash is as follows (see also {{readme file|materials}}):&lt;br /&gt;
* '''light_coverage:''' The coverage of a single point of light in m&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;.&lt;br /&gt;
* '''bumpiness:''' Normalized bumpiness factor for the material.&lt;br /&gt;
* '''load_resistance:''' The amount of pressure in N/m&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt; the material can withstand without deformation.&lt;br /&gt;
* '''solid:''' 1 (true) or false (0) depending on whether the material is solid or not.&lt;br /&gt;
* '''names:''' Vector of scenery types (usually generated by [[TerraGear]]) that will use this material. &lt;br /&gt;
* '''friction_factor:''' Normalized friction factor of the material.&lt;br /&gt;
* '''rolling_friction:''' The rolling friction coefficient of the material.&lt;br /&gt;
&lt;br /&gt;
Note that this function is a ''very'' CPU-intensive operation, particularly in FlightGear v2.4 and earlier. It is advised to use this function as little as possible.&lt;br /&gt;
|param1 = lat&lt;br /&gt;
|param1text = Latitude, inputted as a number.&lt;br /&gt;
|param2 = lon&lt;br /&gt;
|param2text = Longitude, inputted as a number.&lt;br /&gt;
|param3 = max_alt&lt;br /&gt;
|param3text = The altitude, in metres, from which the function will begin searching for the height of the terrain. Defaults to 10,000 metres. If the terrain is higher than this argument specifies, &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; will be returned.&lt;br /&gt;
|example1text = Dumps information about ground underneath the aircraft.&lt;br /&gt;
|example1 = var pos = geo.aircraft_position();&lt;br /&gt;
var info = geodinfo(pos.lat(), pos.lon());&lt;br /&gt;
debug.dump(info);&lt;br /&gt;
|example2text = Prints whether the ground underneath the aircraft is solid or is water.&lt;br /&gt;
|example2 = var pos = geo.aircraft_position();&lt;br /&gt;
var info = geodinfo(pos.lat(), pos.lon());&lt;br /&gt;
if (info != nil and info[1] != nil) {&lt;br /&gt;
    print(&amp;quot;The ground underneath the aircraft is &amp;quot;, info[1].solid == 1 ? &amp;quot;solid.&amp;quot; : &amp;quot;water.&amp;quot;);&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== geodtocart() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = geodtocart(lat, lon, alt);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=962|t=Source}}&lt;br /&gt;
|text = Converts {{wikipedia|geodetic coordinates}} (latitude, longitude, and altitude) to {{wikipedia|ECEF|Earth-centered, Earth-fixed}} coordinates (x, y and z). A vector is returned containing x, y, and z in metres. The equatorial radius of earth used is that defined by the {{wikipedia|WGS 84}} (6,378,137 metres). All argument are mandatory.&lt;br /&gt;
|param1 = lat&lt;br /&gt;
|param1text = Latitude, in degrees.&lt;br /&gt;
|param2 = lon&lt;br /&gt;
|param2text = Longitude, in degrees.&lt;br /&gt;
|param3 = alt&lt;br /&gt;
|param3text = Altitude, in metres.&lt;br /&gt;
|example1 = var (x, y, z) = geodtocart(0, 0, 0); # point is the intersection of the prime meridian and equator.&lt;br /&gt;
print(&amp;quot;x: &amp;quot;, x); # prints &amp;quot;x: 6378137&amp;quot;&lt;br /&gt;
print(&amp;quot;y: &amp;quot;, y); # prints &amp;quot;y: 0&amp;quot;&lt;br /&gt;
print(&amp;quot;z: &amp;quot;, z); # prints &amp;quot;y: 0&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== get_cart_ground_intersection()===&lt;br /&gt;
Introduced in 2017.2.1, see [[Terrain Detection]].&lt;br /&gt;
&lt;br /&gt;
===getprop()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = &amp;lt;nowiki&amp;gt;getprop(path[, path[, ...]]);&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=345|t=Source}}&lt;br /&gt;
|text = Returns the value of a node in the [[Property Tree]] or &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; if the node does not exist or the value is not a number (NaN).&lt;br /&gt;
|param1 = path&lt;br /&gt;
|param1text = There needs to be at least one argument, but there is no limit to the maximum amount of arguments that can be given. The arguments will be concatenated together to form a property tree path. The arguments must be strings, but in FlightGear v3.2 onwards, there is also support (added by {{flightgear commit|34ed79}}) for numeric arguments as indices. See example 2 below.&lt;br /&gt;
|example1 = print(&amp;quot;You have FlightGear v&amp;quot;, getprop(&amp;quot;/sim/version/flightgear&amp;quot;)); # prints FlightGear version&lt;br /&gt;
|example2text = Note that the example below will only work in FlightGear 3.2 and above.&lt;br /&gt;
|example2 = for(var i = 0; i &amp;lt; 8; i += 1){&lt;br /&gt;
    print(&amp;quot;View #&amp;quot;, i + 1, &amp;quot; is named &amp;quot;, getprop(&amp;quot;/sim/view&amp;quot;, i, &amp;quot;name&amp;quot;));&lt;br /&gt;
}&lt;br /&gt;
|example3text = Same as above, but is supported by all versions of FlightGear.&lt;br /&gt;
|example3 = for(var i = 0; i &amp;lt; 8; i += 1){&lt;br /&gt;
    print(&amp;quot;View #&amp;quot;, i + 1, &amp;quot; is named &amp;quot;, getprop(&amp;quot;/sim/view[&amp;quot; ~ i ~ &amp;quot;]/name&amp;quot;));&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
==== See also====&lt;br /&gt;
{{note| If you have to read/write the same property multiple times (e.g. in an update loop), it is more efficient to use a node object: &lt;br /&gt;
To get a Node rather than its value, use &amp;lt;code&amp;gt;props.globals.getNode()&amp;lt;/code&amp;gt; - see [[Nasal_library/props]]. }}&lt;br /&gt;
&lt;br /&gt;
===greatCircleMove()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = greatCircleMove([pos, ]course, dist);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1681|t=Source}}&lt;br /&gt;
|text = Calculates a new set of geodetic coordinates using inputs of course and distance, either from the aircraft's current position (by default) or from another set of coordinates. Returns a hash containing two members, ''lat'' and ''lon'' (latitude and longitude respectively).&lt;br /&gt;
|param1 = pos&lt;br /&gt;
|param1text = Optional position to calculate from. If not given, the aircraft's current position will be used. Can be one of:&lt;br /&gt;
:* An &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;runway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;taxiway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fix&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;waypoint&amp;lt;/code&amp;gt; ghost object.&lt;br /&gt;
:* A hash with ''lat'' and ''lon'' members&lt;br /&gt;
:* A &amp;lt;code&amp;gt;geo.Coord&amp;lt;/code&amp;gt; object&lt;br /&gt;
:* A lat/lon pair, that is, a pair of numbers (latitude followed by longitude) separated by a comma: &amp;lt;code&amp;gt;greatCircleMove(lat,lon, ...)&amp;lt;/code&amp;gt;.&lt;br /&gt;
|param2 = course&lt;br /&gt;
|param2text = Course to new set of coordinates, in degrees (in the range 0–360).&lt;br /&gt;
|param3 = dist&lt;br /&gt;
|param3text = Distance in nautical miles to the new set of coordinates.&lt;br /&gt;
|example1 = var pos = greatCircleMove(0,0, 0, 1);&lt;br /&gt;
debug.dump(pos); # print hash with coordinates&lt;br /&gt;
|example2 = var fix = findFixesByID(&amp;quot;POGIC&amp;quot;);&lt;br /&gt;
fix = fix[0];&lt;br /&gt;
var pos = greatCircleMove(fix, 45, 10);&lt;br /&gt;
debug.dump(pos); # print hash with coordinates&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===interpolate()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|private = _interpolate()&lt;br /&gt;
|syntax = &amp;lt;nowiki&amp;gt;interpolate(prop, value1, time1[, value2, time2[, ...]]);&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=522|t=Part 1}} {{!}} {{fgdata file|Nasal/globals.nas|t=Part 2}}&lt;br /&gt;
|text = Linearly interpolates a node in the property tree to a given value in a specified time. The value/time pairs will be run one after the other in the order that they are passed to the function. Note that the interpolation will continue even when the simulation is paused.&lt;br /&gt;
|param1 = prop&lt;br /&gt;
|param1text = String or &amp;lt;code&amp;gt;props.Node&amp;lt;/code&amp;gt; object that indicates a node in the property tree to be used.&lt;br /&gt;
|param2 = value''n''&lt;br /&gt;
|param2text = Target value to change the property to in the set amount of time. This should be a number.&lt;br /&gt;
|param3 = time''n''&lt;br /&gt;
|param3text = Time in seconds, that will be taken for the interpolation.&lt;br /&gt;
|example1text = Paste the code below into the Nasal Console and execute. Then, open the Property Browser and look for the property. Finally, run the code again, and watch the value of the property change.&lt;br /&gt;
|example1 = setprop(&amp;quot;/test&amp;quot;, 0); # (re-)set property&lt;br /&gt;
interpolate(&amp;quot;/test&amp;quot;,&lt;br /&gt;
    50, 5, # interpolate to 50 in 5 seconds&lt;br /&gt;
    10, 2, # interpolate to 10 in 2 seconds&lt;br /&gt;
    0, 5); # interpolate to 0 in 5 seconds&lt;br /&gt;
|example2 = # Apply the left brake at 20% per second&lt;br /&gt;
var prop = &amp;quot;controls/gear/brake-left&amp;quot;;&lt;br /&gt;
var dist = 1 - getprop(prop);&lt;br /&gt;
if (dist == 1) {&lt;br /&gt;
    interpolate(prop, 1, dist / 0.2);&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===isa()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = isa(object, class);&lt;br /&gt;
|source = {{fgdata file|Nasal/globals.nas|t=Source}}&lt;br /&gt;
|text = Checks if an object is an instance of, or inherits from, a second object (or class), returning 1 (true) if it is and 0 (false) if otherwise.&lt;br /&gt;
|param1 = object&lt;br /&gt;
|param1text = Object to check.&lt;br /&gt;
|param2 = class&lt;br /&gt;
|param2text = Class/object to check that '''object''' inherits from or is an instance of.&lt;br /&gt;
|example1 = var coord = geo.Coord.new();&lt;br /&gt;
if(isa(coord, geo.Coord)){&lt;br /&gt;
    print(&amp;quot;Variable 'coord' is an instance of class 'geo.Coord'&amp;quot;); # this one will be printed&lt;br /&gt;
} else {&lt;br /&gt;
    print(&amp;quot;Variable 'coord' is not an instance of class 'geo.Coord'&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
|example2 = var coord = geo.Coord.new();&lt;br /&gt;
if(isa(coord, props.Node)){&lt;br /&gt;
    print(&amp;quot;Variable 'coord' is an instance of class 'props.Node'&amp;quot;);&lt;br /&gt;
} else {&lt;br /&gt;
    print(&amp;quot;Variable 'coord' is not an instance of class 'props.Node'&amp;quot;); # this one will be printed&lt;br /&gt;
}&lt;br /&gt;
|example3text = The example below demonstrates checking of inheritance.&lt;br /&gt;
|example3 = var Const = {&lt;br /&gt;
    constant: 2,&lt;br /&gt;
    getConst: func {&lt;br /&gt;
        return me.constant;&lt;br /&gt;
    }&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
var Add = {&lt;br /&gt;
    new: func {&lt;br /&gt;
        return { parents: [Add, Const] };&lt;br /&gt;
    },&lt;br /&gt;
&lt;br /&gt;
    addToConst: func(a){&lt;br /&gt;
        return a * me.getConst();&lt;br /&gt;
    }&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
var m = Add.new();&lt;br /&gt;
print(m.addToConst(4));&lt;br /&gt;
&lt;br /&gt;
if(isa(m, Add)) print(&amp;quot;Variable 'm' is an instance of class 'Add'&amp;quot;); # will be printed&lt;br /&gt;
if(isa(m, Const)) print(&amp;quot;Variable 'm' is an instance of class 'Const'&amp;quot;); # will also be printed&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===logprint()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = &amp;lt;nowiki&amp;gt;logprint(priority[, msg[, msg[, ...]]]);&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=431|t=Source}}&lt;br /&gt;
|text = Concatenates a message and logs it with a given priority level. Unlike {{func link|print()}} and {{func link|printlog()}}, message outputted by this function will be logged in your &amp;lt;code&amp;gt;[[Commonly used debugging tools#fgfs.log|fgfs.log]]&amp;lt;/code&amp;gt; file as coming from the Nasal file itself rather than from {{flightgear file|src/Scripting/NasalSys.cxx}}.&lt;br /&gt;
|param1 = priority&lt;br /&gt;
|param1text = Number specifying the priority level of the outputted message:&lt;br /&gt;
:{{{!}} class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Number !! Debug type&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} 1 {{!!}} Bulk&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} 2 {{!!}} Debug&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} 3 {{!!}} Info&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} 4 {{!!}} Warn&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} 5 {{!!}} Alert&lt;br /&gt;
{{!}}}&lt;br /&gt;
|param2 = msg&lt;br /&gt;
|param2text = The message. There is no limit to the arguments you give give. They will be concatenated together before logging.&lt;br /&gt;
|example1 = # logs the value of pi to three decimal places with log level 3&lt;br /&gt;
logprint(3, &amp;quot;pi = &amp;quot;, sprintf(&amp;quot;%.3f&amp;quot;, math.pi));&lt;br /&gt;
|example2 = logprint(5, &amp;quot;Alert! This is an important message!&amp;quot;);&lt;br /&gt;
}}&lt;br /&gt;
{{note| &lt;br /&gt;
The following constants have been added to the development branch of FlightGear (&amp;quot;next&amp;quot;) and will be releases with FG 2020.1 so you won't have to remember the numbers anymore:&lt;br /&gt;
&lt;br /&gt;
LOG_BULK, LOG_WARN, LOG_DEBUG, LOG_INFO, LOG_ALERT, DEV_WARN, DEV_ALERT }}&lt;br /&gt;
&lt;br /&gt;
===magvar() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = magvar([pos]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1642|t=Source}}&lt;br /&gt;
|text = Returns the {{wikipedia|magnetic variation}} at a given set of coordinates. The table below gives the magnetic model used depending on the version of FlightGear.&lt;br /&gt;
{{{!}} class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! FlightGear versions !! Model !! Reference date&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} 3.6 and above {{!!}} {{wikipedia|World Magnetic Model}} (WMM) 2015 {{!!}} 1 January 2015&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} 0.9.11-pre1 to 3.4 {{!!}} WMM 2005 {{!!}} 1 January 2005&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} 0.7.3 to 0.9.10 {{!!}} WMM 2000 {{!!}} 1 January 2000&lt;br /&gt;
{{!}}}&lt;br /&gt;
|param1 = pos&lt;br /&gt;
|param1text = Optional position to calculate from. If not given, the aircraft's current position will be used. Can be one of:&lt;br /&gt;
:* An &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;runway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;taxiway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fix&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;waypoint&amp;lt;/code&amp;gt; ghost object.&lt;br /&gt;
:* A hash with ''lat'' and ''lon'' members&lt;br /&gt;
:* A &amp;lt;code&amp;gt;geo.Coord&amp;lt;/code&amp;gt; object&lt;br /&gt;
:* A lat/lon pair, that is, a pair of numbers (latitude followed by longitude) separated by a comma: &amp;lt;code&amp;gt;magvar(lat,lon)&amp;lt;/code&amp;gt;.&lt;br /&gt;
|example1 = print(magvar(0, 0)); # prints the magnetic variation at 0, 0&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===maketimer() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = maketimer(interval[, self], function);&lt;br /&gt;
|source = ''Implemented using the {{API Link|flightgear|class|TimerObj}} class.''&amp;lt;br&amp;gt;{{flightgear file|src/Scripting/NasalSys.cxx|l=90|t=Part 1}} {{!}} {{flightgear file|src/Scripting/NasalSys.cxx|l=533|t=Part 2}}&lt;br /&gt;
|version = 2.12&lt;br /&gt;
|commit = {{flightgear commit|ab939f|t=commit}}&lt;br /&gt;
|text = Returns a timer object containing the following methods and members:&lt;br /&gt;
* '''start()''': Starts the timer.&lt;br /&gt;
* '''stop()''': Stops the timer.&lt;br /&gt;
* '''restart(interval)''': Restarts the timer with the given interval.&lt;br /&gt;
* '''singleShot''': Bool showing whether the timer is only to be run once, or continuously until told to stop. Can be both set and read from (see examples).&lt;br /&gt;
* '''isRunning''': Read-only bool telling whether the timer is currently running.&lt;br /&gt;
* '''simulatedTime''': (FG 2017.1+; {{flightgear commit|0af316|t=commit}}) Bool telling whether the timer is using simulated time (which accounts for pause, etc.). Defaults to false (use real time). Can be both read and set. This cannot be changed while the timer is running.&lt;br /&gt;
Unlike {{func link|settimer()}}, which it replaces, &amp;lt;code&amp;gt;maketimer()&amp;lt;/code&amp;gt; provides more control over the timer. In addition, it can help reduce memory usage.&lt;br /&gt;
|param1 = interval&lt;br /&gt;
|param1text = Interval in seconds for the timer.&lt;br /&gt;
|param2 = self&lt;br /&gt;
|param2text = Optional parameter specifying what any &amp;lt;code&amp;gt;'''me'''&amp;lt;/code&amp;gt; references in the function being called will refer to.&lt;br /&gt;
|param3 = function&lt;br /&gt;
|param3text = Function to be called at the given interval.&lt;br /&gt;
|example1 = var timer = maketimer(1, func(){&lt;br /&gt;
    print(&amp;quot;Hello, World!&amp;quot;); # print &amp;quot;Hello, World!&amp;quot; once every second (call timer.stop() to stop it)&lt;br /&gt;
});&lt;br /&gt;
timer.start();&lt;br /&gt;
|example2 = var timer = maketimer(1, math, func(){&lt;br /&gt;
    print(me.math); # 'me' reference is the 'math' namespace&lt;br /&gt;
});&lt;br /&gt;
timer.singleShot = 1; # timer will only be run once&lt;br /&gt;
timer.start();&lt;br /&gt;
|example3 = var timer = maketimer(1, func(){&lt;br /&gt;
    print(&amp;quot;Hello, World!&amp;quot;); # print &amp;quot;Hello, World!&amp;quot; once every second (call timer.stop() to stop it)&lt;br /&gt;
});&lt;br /&gt;
timer.start();&lt;br /&gt;
print(timer.isRunning); # prints 1&lt;br /&gt;
|example4text = In the example below, &amp;quot;Hello, World!&amp;quot; will be printed after one second the first time, and after two seconds thereafter.&lt;br /&gt;
|example4 = var update = func(){&lt;br /&gt;
    print(&amp;quot;Hello, World!&amp;quot;);&lt;br /&gt;
    timer.restart(2); # restarts the timer with a two second interval&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var timer = maketimer(1, update);&lt;br /&gt;
timer.singleShot = 1;&lt;br /&gt;
timer.start();&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===maketimestamp()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = maketimestamp()&lt;br /&gt;
|source = ''Implemented using the {{API Link|flightgear|class|TimeStampObj}} class.''&amp;lt;br&amp;gt;{{flightgear file|src/Scripting/NasalSys.cxx|l=214|t=Part 1}} {{!}} {{flightgear file|src/Scripting/NasalSys.cxx|l=589|t=Part 2}}&lt;br /&gt;
|version = 2019.2&lt;br /&gt;
|commit = {{flightgear commit|7db06300|t=commit}}&lt;br /&gt;
|text = Returns a time stamp object to allow high resolution timing of Nasal operations. When created the timer will automatically be stamped. The object has the following methods:&lt;br /&gt;
* '''stamp()''': Resets the timing operation. Call this first.&lt;br /&gt;
* '''elapsedMSec()''': returns number of milliseconds elapsed since stamp() called. Resolution may vary depending on platform but is usually at least millisecond accuracy.&lt;br /&gt;
* '''elapsedUSec()''': returns number of microseconds elapsed since stamp() called. Resolution may vary depending on platform but is usually at least millisecond accuracy.&lt;br /&gt;
|&lt;br /&gt;
|example1text = In the example below the number of milliseconds elapsed will be printed.&lt;br /&gt;
|example1 = var timestamp = maketimestamp();&lt;br /&gt;
timestamp.stamp();&lt;br /&gt;
print(timestamp.elapsedMSec(), &amp;quot;ms elapsed&amp;quot;);&lt;br /&gt;
print(timestamp.elapsedMSec(), &amp;quot;ms elapsed&amp;quot;);&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===md5()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = md5(string);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=758|t=Source}}&lt;br /&gt;
|version = 3.2&lt;br /&gt;
|commit = {{flightgear commit|cfbf9e|t=commit}}&lt;br /&gt;
|text = Returns a the {{wikipedia|MD5}} hash (as a string) of the inputted string.&lt;br /&gt;
|param1 = string&lt;br /&gt;
|param1text = String the generate the hash of. Mandatory.&lt;br /&gt;
|example1text = The below code should output &amp;lt;code&amp;gt;65a8e27d8879283831b664bd8b7f0ad4&amp;lt;/code&amp;gt;.&lt;br /&gt;
|example1 = print(md5(&amp;quot;Hello, World!&amp;quot;));&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===navinfo()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = navinfo(lat, lon, type, id);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1453|t=Source}}&lt;br /&gt;
|text = Returns vector &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt; ghost objects matching the given '''type''' and '''id''' or &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; on error.&lt;br /&gt;
|param1 = lat ''and'' lon&lt;br /&gt;
|param1text = If given, the returned navaids will be put into order of ascending distance from the location.&lt;br /&gt;
|param2 = type&lt;br /&gt;
|param2text = Narrows the search to the given type. Must be one of &amp;quot;any,&amp;quot; &amp;quot;fix,&amp;quot; &amp;quot;vor,&amp;quot; &amp;quot;ndb,&amp;quot; &amp;quot;ils,&amp;quot; &amp;quot;dme,&amp;quot; or &amp;quot;tacan.&amp;quot; Defaults to the equivalent of &amp;quot;any.&amp;quot;&lt;br /&gt;
|param3 = id&lt;br /&gt;
|param3text = ID to search for. Note that, although all the parameters are technically optional, this parameter must be given, otherwise an empty vector will be returned.&lt;br /&gt;
|example1 = navinfo(&amp;quot;vor&amp;quot;); # returns all VORs&lt;br /&gt;
|example2 = navinfo(&amp;quot;HAM&amp;quot;); # return all navaids whose names start with &amp;quot;HAM&amp;quot;&lt;br /&gt;
|example3 = navinfo(&amp;quot;vor&amp;quot;, &amp;quot;HAM&amp;quot;); # return all VORs whose names start with &amp;quot;HAM&amp;quot;&lt;br /&gt;
|example4 = navinfo(34,48,&amp;quot;vor&amp;quot;,&amp;quot;HAM&amp;quot;); # return all VORs whose names start with &amp;quot;HAM&amp;quot; and sorted by distance relative to 34°, 48°&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===parse_markdown()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = parse_markdown(markdown);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=745|t=Part 1}} {{!}} {{simgear file|simgear/misc/SimpleMarkdown.cxx|t=Part 2}} &lt;br /&gt;
|version = 3.2&lt;br /&gt;
|text = Parses a string containing {{wikipedia|Markdown}} and returns the result as a string. As of FlightGear 2016.1, it is just a simple parser, and does the following:&lt;br /&gt;
* It strips whitespace from the beginning of the string.&lt;br /&gt;
* It supports [http://daringfireball.net/projects/markdown/syntax#p paragraphs and line breaks].&lt;br /&gt;
* It collapses whitespace.&lt;br /&gt;
* It converts unordered [http://daringfireball.net/projects/markdown/syntax#list lists] to use a bullet character (&amp;amp;bull;). Note that the bullet character is implemented in hexadecimal UTF-8 character bytes (&amp;lt;code&amp;gt;E2 80 A2&amp;lt;/code&amp;gt;), as so may not work properly when the being displayed in an encoding other than UTF-8.&lt;br /&gt;
|param1 = markdown&lt;br /&gt;
|param1text = String containing Markdown to be parsed.&lt;br /&gt;
|example1text = &lt;br /&gt;
Save the below code as &amp;lt;tt&amp;gt;''[[$FG_ROOT]]/gui/dialogs/test.xml''&amp;lt;/tt&amp;gt;, then run the Nasal code below it to open the dialog. To change the markdown to be parsed, simply change the code in the highlighted section, save it, and reload the GUI (&amp;lt;tt&amp;gt;Debug &amp;gt; Reload GUI&amp;lt;/tt&amp;gt;).&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot; highlight=&amp;quot;41-50&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;PropertyList&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;name&amp;gt;test&amp;lt;/name&amp;gt;&lt;br /&gt;
&amp;lt;layout&amp;gt;vbox&amp;lt;/layout&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;group&amp;gt;&lt;br /&gt;
    &amp;lt;layout&amp;gt;hbox&amp;lt;/layout&amp;gt;&lt;br /&gt;
    &lt;br /&gt;
    &amp;lt;empty&amp;gt;&lt;br /&gt;
        &amp;lt;stretch&amp;gt;true&amp;lt;/stretch&amp;gt;&lt;br /&gt;
    &amp;lt;/empty&amp;gt;&lt;br /&gt;
    &lt;br /&gt;
    &amp;lt;text&amp;gt;&lt;br /&gt;
        &amp;lt;label&amp;gt;parse_markdown() test dialog&amp;lt;/label&amp;gt;&lt;br /&gt;
    &amp;lt;/text&amp;gt;&lt;br /&gt;
    &lt;br /&gt;
    &amp;lt;empty&amp;gt;&lt;br /&gt;
        &amp;lt;stretch&amp;gt;true&amp;lt;/stretch&amp;gt;&lt;br /&gt;
    &amp;lt;/empty&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    &amp;lt;button&amp;gt;&lt;br /&gt;
        &amp;lt;legend&amp;gt;&amp;lt;/legend&amp;gt;&lt;br /&gt;
        &amp;lt;pref-width&amp;gt;16&amp;lt;/pref-width&amp;gt;&lt;br /&gt;
        &amp;lt;pref-height&amp;gt;16&amp;lt;/pref-height&amp;gt;&lt;br /&gt;
        &amp;lt;binding&amp;gt;&lt;br /&gt;
            &amp;lt;command&amp;gt;dialog-close&amp;lt;/command&amp;gt;&lt;br /&gt;
        &amp;lt;/binding&amp;gt;&lt;br /&gt;
    &amp;lt;/button&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/group&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;canvas&amp;gt;&lt;br /&gt;
    &amp;lt;name&amp;gt;Canvas plot&amp;lt;/name&amp;gt;&lt;br /&gt;
    &amp;lt;stretch&amp;gt;true&amp;lt;/stretch&amp;gt;&lt;br /&gt;
    &amp;lt;pref-width&amp;gt;400&amp;lt;/pref-width&amp;gt;&lt;br /&gt;
    &amp;lt;pref-height&amp;gt;300&amp;lt;/pref-height&amp;gt;&lt;br /&gt;
    &amp;lt;nasal&amp;gt;&lt;br /&gt;
        &amp;lt;load&amp;gt;&amp;lt;![CDATA[&lt;br /&gt;
var text = 'Items:&lt;br /&gt;
* apples&lt;br /&gt;
* oranges&lt;br /&gt;
* pears&lt;br /&gt;
&lt;br /&gt;
Some text.&lt;br /&gt;
Some more items:&lt;br /&gt;
* apples&lt;br /&gt;
* oranges&lt;br /&gt;
* pears';&lt;br /&gt;
&lt;br /&gt;
var parsed = parse_markdown(text);&lt;br /&gt;
&lt;br /&gt;
var root_canvas = canvas.get(cmdarg());&lt;br /&gt;
root_canvas.setColorBackground(255, 255, 255);&lt;br /&gt;
var root = root_canvas.createGroup();&lt;br /&gt;
&lt;br /&gt;
var text_dis = root.createChild(&amp;quot;text&amp;quot;)&lt;br /&gt;
    .setText(parsed)&lt;br /&gt;
    .setTranslation(5, 5)&lt;br /&gt;
    .setFont(&amp;quot;LiberationFonts\LiberationSans-Regular.ttf&amp;quot;)&lt;br /&gt;
    .setFontSize(15)&lt;br /&gt;
    .setColor(0, 0, 0)&lt;br /&gt;
    .setDrawMode(canvas.Text.TEXT)&lt;br /&gt;
    .setAlignment(&amp;quot;left-top&amp;quot;);&lt;br /&gt;
        ]]&amp;gt;&amp;lt;/load&amp;gt;&lt;br /&gt;
    &amp;lt;/nasal&amp;gt;&lt;br /&gt;
&amp;lt;/canvas&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/PropertyList&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|example1 = fgcommand(&amp;quot;dialog-show&amp;quot;, {&amp;quot;dialog-name&amp;quot;: &amp;quot;test&amp;quot;});&lt;br /&gt;
|example2text = The example below parses Markdown and outputs it in a HTML document. The parsed text is placed in &amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot; inline&amp;gt;&amp;lt;pre&amp;gt;&amp;lt;/pre&amp;gt;&amp;lt;/syntaxhighlight&amp;gt; tags. To change the Markdown to be parsed, simply edit the variable &amp;lt;tt&amp;gt;markdown&amp;lt;/tt&amp;gt; at the top of the code.&lt;br /&gt;
|example2 = &amp;lt;nowiki&amp;gt;var markdown = 'Items:&lt;br /&gt;
* apples&lt;br /&gt;
* oranges&lt;br /&gt;
* pears&lt;br /&gt;
&lt;br /&gt;
Some text.&lt;br /&gt;
Some more items:&lt;br /&gt;
* apples&lt;br /&gt;
* oranges&lt;br /&gt;
* pears';&lt;br /&gt;
&lt;br /&gt;
var parsed = parse_markdown(markdown);&lt;br /&gt;
&lt;br /&gt;
debug.dump(parsed);&lt;br /&gt;
&lt;br /&gt;
var path = string.normpath(getprop(&amp;quot;/sim/fg-home&amp;quot;) ~ '/Export/parse_markdown()-test.html');&lt;br /&gt;
&lt;br /&gt;
var file = io.open(path, &amp;quot;w&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
var html = &amp;quot;&amp;lt;!DOCTYPE html&amp;gt;\n\n&amp;lt;html&amp;gt;\n\n&amp;lt;head&amp;gt;\n\t&amp;lt;meta charset=\&amp;quot;UTF-8\&amp;quot;&amp;gt;\n\t&amp;lt;title&amp;gt;parse_markdown() test generated by Nasal&amp;lt;/title&amp;gt;\n&amp;lt;/head&amp;gt;\n\n&amp;lt;body&amp;gt;\n\t&amp;lt;pre&amp;gt;&amp;quot; ~ parsed ~ &amp;quot;&amp;lt;/pre&amp;gt;\n&amp;lt;/body&amp;gt;\n\n&amp;lt;/html&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
io.write(file, html);&lt;br /&gt;
io.close(file);&lt;br /&gt;
print(&amp;quot;Done, file ready for viewing (&amp;quot; ~ path ~ &amp;quot;)&amp;quot;);&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===parsexml()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = &amp;lt;nowiki&amp;gt;parsexml(path[, start[, end[, data[, pro_ins]]]]);&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=710|t=Source}}&lt;br /&gt;
|text = This function is an interface into the built-in [http://expat.sourceforge.net/ Expat XML parser]. The absolute path to the file is returned as string, or &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; is returned on error.&lt;br /&gt;
|param1 = path&lt;br /&gt;
|param1text = Mandatory absolute path to the XML file to be parsed.&lt;br /&gt;
|param2 = start&lt;br /&gt;
|param2text = Optional callback function that will be called for every starting tag. The function should take two argument: the tag name and a hash containing attributes.&lt;br /&gt;
|param3 = end&lt;br /&gt;
|param3text = Optional callback function that will be called for every ending tag. The function should take one argument: the tag name.&lt;br /&gt;
|param4 = data&lt;br /&gt;
|param4text = Optional callback function that will be called for every piece of data within a set of tags. The function should take one argument: the data as a string.&lt;br /&gt;
|param5 = pro_ins&lt;br /&gt;
|param5text = Optional callback function that will be called for every {{wikipedia|Processing Instruction|processing instruction}}. The function should take two argument: the target and the data string.&lt;br /&gt;
|example1text = Save the below XML code in &amp;lt;tt&amp;gt;''[[$FG_HOME]]/Export/demo.xml''&amp;lt;/tt&amp;gt;. Then, execute the Nasal code below in the Nasal Console. The XML will be parsed and each bit of the code will be printed.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;?xml-stylesheet type=&amp;quot;text/xsl&amp;quot; href=&amp;quot;style.xsl&amp;quot;?&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;foo&amp;gt;&lt;br /&gt;
  &amp;lt;blah type=&amp;quot;string&amp;quot;&amp;gt;&amp;lt;![CDATA[ &amp;lt;sender&amp;gt;John Smith&amp;lt;/sender&amp;gt; ]]&amp;gt;&amp;lt;/blah&amp;gt;&lt;br /&gt;
  &amp;lt;blah2 type=&amp;quot;string&amp;quot;&amp;gt;Orange &amp;amp;amp; lemons&amp;lt;/blah2&amp;gt;&lt;br /&gt;
&amp;lt;/foo&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|example1 = var start = func(name, attr){&lt;br /&gt;
    print(&amp;quot;Starting tag: '&amp;quot;, name, &amp;quot;'&amp;quot;);&lt;br /&gt;
    foreach(var a; keys(attr)){&lt;br /&gt;
        print(&amp;quot;\twith attribute &amp;quot;, a, '=&amp;quot;', attr[a], '&amp;quot;');&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var end = func(name){&lt;br /&gt;
    print(&amp;quot;Ending tag: '&amp;quot;, name, &amp;quot;'&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var data = func(data){&lt;br /&gt;
    print(&amp;quot;Data = '&amp;quot;, data, &amp;quot;'&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var pro_instr = func(target, data){&lt;br /&gt;
    print(&amp;quot;Processing instruction: target = '&amp;quot;, target, &amp;quot;', data = '&amp;quot;, data, &amp;quot;'&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
parsexml(getprop(&amp;quot;/sim/fg-home&amp;quot;) ~ '/Export/demo.xml', start, end, data, pro_instr);&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===print()===&lt;br /&gt;
{{Note|As of 07/2020, we are in the process of slowly deprecating and removing Nasal print() (in fgdata) where possible in favor of log [[#logprint()]] which takes a priority level.&lt;br /&gt;
&lt;br /&gt;
This is part of the goal that we achieve zero output at log-level at alert, and everything shown at ‘warn; is really a warning, not just information.&amp;lt;ref&amp;gt;https://sourceforge.net/p/flightgear/mailman/message/37042224/&amp;lt;/ref&amp;gt;}}&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = &amp;lt;nowiki&amp;gt;print(data[, data[, ...]]);&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=415|t=Source}}&lt;br /&gt;
|text = Concatenates its arguments and then prints it to the terminal and the [[Commonly used debugging tools#fgfs.log|log]]. Note that a newline is automatically added.&lt;br /&gt;
|param1 = data&lt;br /&gt;
|param1text = Data to print. Only strings and numbers can be printed; other data types will not be. There many be any number of arguments; they will just be concatenated together.&lt;br /&gt;
|example1 = print(&amp;quot;Just&amp;quot;, &amp;quot; a &amp;quot;, &amp;quot;test&amp;quot;); # prints &amp;quot;Just a test&amp;quot;&lt;br /&gt;
|example2 = print(&amp;quot;pi = &amp;quot;, math.pi); # prints &amp;quot;pi = 3.141592...&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== printf() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = &amp;lt;nowiki&amp;gt;printf(format[, arg[, arg, [...]]]);&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|source = {{fgdata file|Nasal/globals.nas|t=Source}}&lt;br /&gt;
|text = Creates and prints a formatted string. For a description of its arguments, see {{func link|sprintf()}} (it is, in fact, implemented using &amp;lt;code&amp;gt;sprintf()&amp;lt;/code&amp;gt;).&lt;br /&gt;
|example1 = printf(&amp;quot;In hexadecimal, 100000 = %X&amp;quot;, 186A0); # prints &amp;quot;In hexadecimal, 100000 = 186A0&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===printlog()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = &amp;lt;nowiki&amp;gt;printlog(level, data[, data[, ...]]);&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|source = {{fgdata file|Nasal/globals.nas|t=Source}}&lt;br /&gt;
|text = Prints the given message with the given log level. If the log level is higher or equal to &amp;lt;code&amp;gt;/sim/logging/priority&amp;lt;/code&amp;gt;, it is printed.&lt;br /&gt;
|param1 = level&lt;br /&gt;
|param1text = Mandatory log level as a string. Must be one of &amp;quot;none,&amp;quot; &amp;quot;bulk,&amp;quot; &amp;quot;debug,&amp;quot; &amp;quot;info,&amp;quot; &amp;quot;warn,&amp;quot; or &amp;quot;alert.&amp;quot; Note that &amp;quot;none&amp;quot; will mean that the message will never be printed.&lt;br /&gt;
|param2 = data&lt;br /&gt;
|param2text = Data to be printed. Only strings and numbers will be printed; others will not be. There may be any number of arguments; they will just be concatenated together.&lt;br /&gt;
|example1 = printlog(&amp;quot;alert&amp;quot;, &amp;quot;This is an alert&amp;quot;); # message will be printed&lt;br /&gt;
|example2 = printlog(&amp;quot;info&amp;quot;, &amp;quot;Just informing you about something&amp;quot;); # message will be printed only if log level is set to &amp;quot;info&amp;quot; or less&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===rand()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = rand();&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=554|t=Source}}&lt;br /&gt;
|text = Returns a random floating point number between 0 (inclusive) and 1 (exclusive). It takes no arguments.&lt;br /&gt;
|example1 = print(rand()); # prints random number&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===registerFlightPlanDelegate()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = registerFlightPlanDelegate(init_func);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1879|t=Source}}&lt;br /&gt;
|text = Registers a flight plan delegate. See &amp;lt;tt&amp;gt;''{{fgdata file|Nasal/route_manager.nas|t=$FG_ROOT/Nasal/route_manager.nas}}''&amp;lt;/tt&amp;gt; for examples.&lt;br /&gt;
|param1 = init_func&lt;br /&gt;
|param1text = Initialization function which will be called during FlightGear's startup.&lt;br /&gt;
}}&lt;br /&gt;
===removecommand()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = removecommand(cmd);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=668|t=Source}}&lt;br /&gt;
|text = Removes the given fgcommand. Returns &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt;.&lt;br /&gt;
{{caution|This will remove '''any''' [[fgcommands|fgcommand]], even those implemented in C++, so use with caution!}}&lt;br /&gt;
|param1 = cmd&lt;br /&gt;
|param1text = String specifying the name of the command to remove.&lt;br /&gt;
|example1 = addcommand(&amp;quot;hello&amp;quot;, func(){&lt;br /&gt;
    print(&amp;quot;Hello&amp;quot;);&lt;br /&gt;
});&lt;br /&gt;
fgcommand(&amp;quot;hello&amp;quot;); # &amp;quot;Hello&amp;quot; will be printed&lt;br /&gt;
removecommand(&amp;quot;hello&amp;quot;); # removes it&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===removelistener()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = removelistener(id);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=1384|t=Part 1}} {{!}} {{flightgear file|src/Scripting/NasalSys.cxx|l=506|t=Part 2}}&lt;br /&gt;
|text = Removes and deactivates the given listener and returns the number of listeners left or &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; on error.&lt;br /&gt;
{{note|It is good practice to remove listeners when they are not required anymore. This prevents the listeners reducing FlightGear's run performance.}}&lt;br /&gt;
|param1 = id&lt;br /&gt;
|param1text = ID of listener as returned by {{func link|setlistener()}}.&lt;br /&gt;
|example1 = var ls = setlistener(&amp;quot;/sim/test&amp;quot;, func(){&lt;br /&gt;
    print(&amp;quot;Property '/sim/test' has been changed&amp;quot;);&lt;br /&gt;
});&lt;br /&gt;
setprop(&amp;quot;/sim/test&amp;quot;, &amp;quot;blah&amp;quot;); # trigger listener&lt;br /&gt;
var rem = removelistener(ls); # remove listener&lt;br /&gt;
print(&amp;quot;There are &amp;quot;, rem, &amp;quot; listeners remaining&amp;quot;);&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===resolvepath()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = resolvepath(path);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=604|t=Source}}&lt;br /&gt;
|text = Takes a relative path as a string and uses [[SimGear]]'s path-resolving framework to return an absolute path as a string. If the path could not be resolved, an empty string is returned. See [[Resolving Paths]] for a detailed description of the algorithm. This function can also be used to check if a file exists.&lt;br /&gt;
|param1 = path&lt;br /&gt;
|param1text = Relative path to be completed.&lt;br /&gt;
|example1 = print(resolvepath(&amp;quot;Nasal/globals.nas&amp;quot;)); # prints the equivalent of $FG_ROOT/Nasal/globals.nas&lt;br /&gt;
|example2 = print(resolvepath(&amp;quot;blah&amp;quot;)); # prints nothing; could not be resolved&lt;br /&gt;
|example3 = var file_path = resolvepath(&amp;quot;Aircraft/SenecaII/some-file&amp;quot;);&lt;br /&gt;
if (file_path != &amp;quot;&amp;quot;){&lt;br /&gt;
    gui.popupTip(&amp;quot;some-file found&amp;quot;, 2);&lt;br /&gt;
} else {&lt;br /&gt;
    gui.popupTip(&amp;quot;some-file not found&amp;quot;, 2);&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===setlistener()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = &amp;lt;nowiki&amp;gt;setlistener(node, code[, init[, type]]);&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|private = _setlistener()&lt;br /&gt;
|source = {{flightgear file|src/Scripting/Nasal|l=499|t=Part 1}} {{!}} {{flightgear file|src/Scripting/Nasal|l=1350|t=Part 2}}&amp;lt;br&amp;gt;''Listener implemented using the {{API Link|flightgear|class|FGNasalListener}} class.''&lt;br /&gt;
|text = Creates a listener which will be triggered when the given property is changed (depending on the '''type'''). A unique integer ID is returned; this can later be used as the argument to {{func link|removelistener()}}.&lt;br /&gt;
{{note|Listeners are known to be a source of resource leaks. To avoid this, please take measures such as:&lt;br /&gt;
* Using {{func link|removelistener()}} when they are not needed any more.&lt;br /&gt;
* Using a single initialization listener.&lt;br /&gt;
* Avoiding tying listeners to properties that are rapidly updated (e.g., many times per frame).&lt;br /&gt;
}}&lt;br /&gt;
|param1 = node&lt;br /&gt;
|param1text = Mandatory string or &amp;lt;code&amp;gt;props.Node&amp;lt;/code&amp;gt; object pointing to a property in the [[Property Tree]].&lt;br /&gt;
|param2 = code&lt;br /&gt;
|param2text = Mandatory callback function to execute when the listener is triggered. The function can take up to four arguments in the following order:&lt;br /&gt;
* '''changed''': a &amp;lt;code&amp;gt;props.Node&amp;lt;/code&amp;gt; object pointing to the changed node.&lt;br /&gt;
* '''listen''': a &amp;lt;code&amp;gt;props.Node&amp;lt;/code&amp;gt; object pointing to the listened-to node. Note that this argument maybe different depending on the '''type'''.&lt;br /&gt;
* '''mode''': an integer telling how the listener was triggered. 0 means that the value was changed. 1 means that a child property was added. -1 means that a child property was removed.&lt;br /&gt;
* '''is_child''': boolean telling whether '''changed''' is a child property of the listened-to '''node''' or not. 1 (true) if it is, 0 (false) otherwise.&lt;br /&gt;
|param3 = init&lt;br /&gt;
|param3text = If set to 1 (true), the listener will additionally be triggered when it is created. This argument is optional and defaults to 0 (false).&lt;br /&gt;
|param4 = type&lt;br /&gt;
|param4text = Integer specifying the listener's behavior. 0 means that the listener will only trigger when the property is changed. 1 means that the trigger will always be triggered when the property is written to. 2 will mean that the listener will be triggered even if child properties are modified. This argument is optional and defaults to 1.&lt;br /&gt;
|example1 = var prop = props.globals.initNode(&amp;quot;/sim/test&amp;quot;, &amp;quot;&amp;quot;, &amp;quot;STRING&amp;quot;); # create property&lt;br /&gt;
var id = setlistener(&amp;quot;/sim/test&amp;quot;, func(n){ # create listener&lt;br /&gt;
    print(&amp;quot;Value: &amp;quot;, n.getValue());&lt;br /&gt;
});&lt;br /&gt;
setprop(&amp;quot;/sim/test&amp;quot;, &amp;quot;blah&amp;quot;); # trigger listener&lt;br /&gt;
removelistener(id); # remove listener&lt;br /&gt;
prop.remove(); # remove property&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===setprop()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = &amp;lt;nowiki&amp;gt;setprop(path[, path[, ...]], value);&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=385|t=Source}}&lt;br /&gt;
|text = Sets the value of a property in the [[Property Tree]]. If the property does not exist, it will be created. Returns 1 (true) on success or 0 (false) if there was an error.&lt;br /&gt;
{{note|If you want to remove a property, you will have to use one of the &amp;lt;code&amp;gt;props&amp;lt;/code&amp;gt; helpers:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
props.globals.getNode(&amp;quot;foo/bar&amp;quot;).remove(); # take out the complete node&lt;br /&gt;
props.globals.getNode(&amp;quot;foo&amp;quot;).removeChild(&amp;quot;bar&amp;quot;); # take out a certain child node&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
|param1 = path&lt;br /&gt;
|param1text = There needs to be at least one '''path''' argument, but there is no limit to the maximum amount that can be given. They will be concatenated together to form a property tree path. The arguments must be strings, but in FlightGear v3.2 onwards, there also is support (added by {{flightgear commit|34ed79}}) for numeric arguments as indices. See example 2 below.&lt;br /&gt;
|param2 = value&lt;br /&gt;
|param2text = Value to write to the given property. Must be either a string or a number.&lt;br /&gt;
|example1 = setprop(&amp;quot;/sim/demo&amp;quot;, &amp;quot;This is a demo&amp;quot;);&lt;br /&gt;
|example2text = Note that the example below will only work in FlightGear 3.2 and above.&lt;br /&gt;
|example2 = for(var i = 0; i &amp;lt; 3; i += 1){&lt;br /&gt;
    setprop(&amp;quot;/sim/demo&amp;quot;, i, &amp;quot;Demo #&amp;quot; ~ i));&lt;br /&gt;
}&lt;br /&gt;
|example3text = Same as above, but is supported by all versions of FlightGear.&lt;br /&gt;
|example3 = for(var i = 0; i &amp;lt; 3; i += 1){&lt;br /&gt;
    setprop(&amp;quot;/sim/demo[&amp;quot; ~ i ~ &amp;quot;]&amp;quot;, &amp;quot;Demo #&amp;quot; ~ i));&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
====See also====&lt;br /&gt;
{{note| If you have to read/write the same property multiple times (e.g. in an update loop), it is more efficient to use a node object: &lt;br /&gt;
To get a Node rather than its value, use &amp;lt;code&amp;gt;props.globals.getNode()&amp;lt;/code&amp;gt; - see [[Nasal_library/props]]. }}&lt;br /&gt;
&lt;br /&gt;
===settimer() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = settimer(function, delta[, realtime]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=499|t=Part 1}} {{!}} {{flightgear file|src/Scripting/NasalSys.cxx|l=1286|t=Part 2}}&lt;br /&gt;
|text = Runs the given function a specified amount of seconds after the current time. Returns &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt;.&lt;br /&gt;
{{note|Improper use of &amp;lt;code&amp;gt;settimer()&amp;lt;/code&amp;gt; may cause resource leaks. It is also highly recommended that the newer {{func link|maketimer()}} should be used instead of this function.}} &lt;br /&gt;
|param1 = function&lt;br /&gt;
|param1text = Mandatory function that will be called. It may be necessary to enclose code in an anonymous function (see example).&lt;br /&gt;
|param2 = delta&lt;br /&gt;
|param2text = Mandatory amount of time in seconds after which the function will be called.&lt;br /&gt;
|param3 = realtime&lt;br /&gt;
|param3text = If 1 (true), &amp;quot;real time&amp;quot; will be used instead of &amp;quot;simulation time.&amp;quot; Defaults to 0 (false). Note that if &amp;quot;simulation time&amp;quot; is used, the timer will not run while FlightGear is paused.&lt;br /&gt;
|example1 = var myFunc = func(){&lt;br /&gt;
    print(&amp;quot;Hello&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
settimer(myFunc, 2); # runs myFunc after 2 seconds&lt;br /&gt;
|example2 = var sqr = func(a){&lt;br /&gt;
    return a * a;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
settimer(func(){&lt;br /&gt;
    print(sqr(2)); # will print 4 after 2 seconds&lt;br /&gt;
}, 2);&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===srand() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = srand();&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=559|t=Source}}&lt;br /&gt;
|text = Makes the pseudorandom number generator (see {{func link|rand()}}) generate a new {{wikipedia|random seed|noicon=1}} based on time. Returns 0.&lt;br /&gt;
}}&lt;br /&gt;
===systime()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = systime();&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=770|t=Source}}&lt;br /&gt;
|text = Returns the {{wikipedia|Unix time}} (seconds since since 00:00:00 UTC, 1/1/1970) as a floating point number with high resolution. This function is useful for benchmarking purposes (see example 2).&lt;br /&gt;
{{note|1=High resolution timers under Windows can produce inaccurate or fixed sub-millisecond results.&amp;lt;ref&amp;gt;{{cite web|url=http://forum.flightgear.org/viewtopic.php?f=30&amp;amp;t=29259|title=Nasal: systime() ??!?|author=Necolatis|date=Apr 2nd, 2016}}&amp;lt;/ref&amp;gt; This is due to the underlying {{func link|GetSystemTimeAsFileTime()|link=https://msdn.microsoft.com/en-us/library/windows/desktop/ms724397(v=vs.85).aspx}} API call, which depends on hardware availability of suitable high resolution timers. See also [https://msdn.microsoft.com/en-us/library/windows/desktop/dn553408(v=vs.85).aspx Acquiring high-resolution time stamps]}}&lt;br /&gt;
|example1 = print(&amp;quot;Unix time: &amp;quot;, systime()); # prints Unix time&lt;br /&gt;
|example2 = var myFunc = func(){&lt;br /&gt;
    for(var i = 0; i &amp;lt;= 10; i += 1){&lt;br /&gt;
        print(&amp;quot;Interation #&amp;quot;, i);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
var t = systime(); # record time&lt;br /&gt;
myFunc(); # run function&lt;br /&gt;
var t2 = systime(); # record new time&lt;br /&gt;
print(&amp;quot;myFunc() took &amp;quot;, t2 - t, &amp;quot; seconds&amp;quot;); # print result&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===thisfunc()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = thisfunc();&lt;br /&gt;
|source = {{fgdata file|Nasal/globals.nas|t=Source}}&lt;br /&gt;
|text = Returns the function from which this function is called. This allows a function to reliably and safely call itself from within a closure.&lt;br /&gt;
|example1 = var stringify_vec = func(input){&lt;br /&gt;
    if (typeof(input) == &amp;quot;scalar&amp;quot;){&lt;br /&gt;
        return sprintf(&amp;quot;%s&amp;quot;, input);&lt;br /&gt;
    } elsif (typeof(input) == &amp;quot;vector&amp;quot;) {&lt;br /&gt;
        if (size(input) == 0) return &amp;quot;[]&amp;quot;;&lt;br /&gt;
        var this = thisfunc();&lt;br /&gt;
        var buffer = &amp;quot;[&amp;quot;;&lt;br /&gt;
        for(var i = 0; i &amp;lt; size(input); i += 1){&lt;br /&gt;
            buffer ~= this(input[i]);&lt;br /&gt;
            if (i == size(input) - 1) {&lt;br /&gt;
                buffer ~= &amp;quot;]&amp;quot;;&lt;br /&gt;
            } else {&lt;br /&gt;
                buffer ~= &amp;quot;, &amp;quot;;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        return buffer;&lt;br /&gt;
    } else {&lt;br /&gt;
        die(&amp;quot;stringify_vec(): Error! Invalid input. Must be a vector or scalar&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var test_vec = [&amp;quot;a&amp;quot;, &amp;quot;b&amp;quot;, &amp;quot;c&amp;quot;, 1, 2, 3];&lt;br /&gt;
debug.dump(stringify_vec(test_vec)); # prints &amp;quot;[a, b, c, 1, 2, 3]&amp;quot;&lt;br /&gt;
test_vec = [];&lt;br /&gt;
debug.dump(stringify_vec(test_vec)); # prints &amp;quot;[]&amp;quot;&lt;br /&gt;
test_vec = {};&lt;br /&gt;
debug.dump(stringify_vec(test_vec)); # will throw an error&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===tileIndex() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = tileIndex();&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1720|t=Source}}&lt;br /&gt;
|text = Returns the index of the tile at the aircraft's current position as a string. This corresponds to the name of the STG file of the tile. For example, at [[KSFO]], this would be &amp;lt;code&amp;gt;942050&amp;lt;/code&amp;gt;, corresponding to &amp;lt;tt&amp;gt;''[[$FG_SCENERY]]/Terrain/w130n30/w123n3/942050.stg''&amp;lt;/tt&amp;gt;.&lt;br /&gt;
|example1 = print(tileIndex()); # print index&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== tilePath()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = tilePath();&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1712|t=Source}}&lt;br /&gt;
|text = Returns the base path of the tile at the aircraft's current position as a string. For example, at KSFO, this would be &amp;lt;code&amp;gt;w130n30/w123n3&amp;lt;/code&amp;gt;, corresponding to &amp;lt;tt&amp;gt;''[[$FG_SCENERY]]/Terrain/w130n30/w123n3''&amp;lt;/tt&amp;gt;.&lt;br /&gt;
|example1 = print(tilePath()); # print path&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== values()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = values(hash);&lt;br /&gt;
|source = {{fgdata file|Nasal/globals.nas|t=Source}}&lt;br /&gt;
|text = Returns a vector containing the values of the given hash.&lt;br /&gt;
|param1 = hash&lt;br /&gt;
|param1text = Mandatory hash to get the values of.&lt;br /&gt;
|example1 = var hash = {&lt;br /&gt;
    &amp;quot;a&amp;quot;: 1,&lt;br /&gt;
    &amp;quot;b&amp;quot;: 2,&lt;br /&gt;
    &amp;quot;c&amp;quot;: 3&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
foreach(var val; values(hash)){&lt;br /&gt;
    print(val);&lt;br /&gt;
}&lt;br /&gt;
|example2text = The below example does exactly the same thing as the above example, but does not use &amp;lt;code&amp;gt;values()&amp;lt;/code&amp;gt;:&lt;br /&gt;
|example2 = var hash = {&lt;br /&gt;
    &amp;quot;a&amp;quot;: 1,&lt;br /&gt;
    &amp;quot;b&amp;quot;: 2,&lt;br /&gt;
    &amp;quot;c&amp;quot;: 3&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
foreach(var key; keys(hash)){&lt;br /&gt;
    print(hash[key]);&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
==Extension functions new in FG 2020.1 ==&lt;br /&gt;
The following functions have been added to the nasal core library and will be released with FlightGear version 2020.1. &lt;br /&gt;
Before the release they are available in the development branch &amp;quot;next&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
===isfunc()===&lt;br /&gt;
Returns 1 if type or argument is a function, otherwise 0.&lt;br /&gt;
&lt;br /&gt;
===isghost()===&lt;br /&gt;
Returns 1 if type or argument is a ghost, otherwise 0.&lt;br /&gt;
&lt;br /&gt;
=== ishash()===&lt;br /&gt;
Returns 1 if type or argument is a hash, otherwise 0.&lt;br /&gt;
&lt;br /&gt;
===isint()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = isint(x);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|t=Source}}&lt;br /&gt;
|text = Returns 1 if argument has a numeric value and x == floor(x), e.g. integer.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===isnum()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = isnum(x);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|t=Source}}&lt;br /&gt;
|text = Returns 1 if typeof(x) is &amp;quot;scalar&amp;quot; and x has a numeric value otherwise 0. &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===isscalar()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = isscalar(x);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|t=Source}}&lt;br /&gt;
|text = Returns 1 if type or argument is a scalar (string or numeric), otherwise (vector, hash, func, ...) it returns 0. This is useful to check if a variable can be converted to string e.g. when useing the string concat operator &amp;quot;~&amp;quot;. &lt;br /&gt;
|example1 = var a = &amp;quot;foo&amp;quot;; &lt;br /&gt;
var b=42;&lt;br /&gt;
if (isscalar(a) and isscalar(b)) print(a~b);&lt;br /&gt;
if (isstr(a)) print(&amp;quot;a is a string&amp;quot;);&lt;br /&gt;
if (isint(b)) print(&amp;quot;b is an integer&amp;quot;);&lt;br /&gt;
# if (isscalar(a))... is equivalent to if (typeof(a) == &amp;quot;scalar&amp;quot;)...&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===isstr()===&lt;br /&gt;
Returns 1 if type or argument is a string, otherwise 0. &lt;br /&gt;
&lt;br /&gt;
===isvec()===&lt;br /&gt;
Returns 1 if type or argument is a vector, otherwise 0.&lt;br /&gt;
&lt;br /&gt;
===vecindex()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = vecindex(vector, value);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|t=Source}}&lt;br /&gt;
|text = Returns the index of value or nil, if value is not found in vector.&lt;br /&gt;
|example1=&lt;br /&gt;
var myvector = [&amp;quot;apple&amp;quot;, &amp;quot;bananna&amp;quot;, &amp;quot;coconut&amp;quot;];&lt;br /&gt;
# to check if a vector contains a certain value compare vecindex to nil&lt;br /&gt;
if (vecindex(myvector, &amp;quot;apple&amp;quot;) != nil) {&lt;br /&gt;
    print(&amp;quot;found apple&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
# WARNING: this will not work as desired because index is 0&lt;br /&gt;
if (vecindex(myvector, &amp;quot;apple&amp;quot;)) {&lt;br /&gt;
    print(&amp;quot;found apple&amp;quot;);&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
==Variables==&lt;br /&gt;
Various global constants (technically variables) are provided for use in converting between different units. They are all found in {{fgdata file|Nasal/globals.nas|text=$FG_ROOT/Nasal/globals.nas}}.&lt;br /&gt;
&lt;br /&gt;
===D2R===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var radians = degrees * D2R;&lt;br /&gt;
|text = Converts an angle from degrees to radians when multiplied by the angle in degrees. Equal to &amp;lt;code&amp;gt;π / 180&amp;lt;/code&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
===FPS2KT===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var knots = feet_per_second * FPS2KT;&lt;br /&gt;
|text = Converts a velocity from feet per second to knots when multiplied by the velocity in feet per second. Approximately equal to 0.5925.&lt;br /&gt;
}}&lt;br /&gt;
===FT2M===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var metres = feet * FT2M;&lt;br /&gt;
|text = Converts a length from feet to metres when multiplied by the length in feet. Equal to 0.3048.&lt;br /&gt;
}}&lt;br /&gt;
===GAL2L ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var litres = gallons * GAL2L;&lt;br /&gt;
|text = Converts a volume from US liquid gallons to litres when multiplied by the volume in gallons. Approximately equal to 3.7854.&lt;br /&gt;
}}&lt;br /&gt;
===IN2M===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var metres = inches * IN2M;&lt;br /&gt;
|text = Converts a length from inches to metres when multiplied by the length in inches. Equal to 0.0254.&lt;br /&gt;
}}&lt;br /&gt;
===KG2LB===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var pounds = kilograms * KG2LB;&lt;br /&gt;
|text = Converts a mass from kilograms to pounds when multiplied by the mass in kilograms. Approximately equal to 2.2046.&lt;br /&gt;
}}&lt;br /&gt;
===KT2FPS===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var feet_per_second = knots * KT2FPS;&lt;br /&gt;
|text = Converts a velocity from knots to feet per second when multiplied by the velocity in knots. Approximately equal to 1.6878.&lt;br /&gt;
}}&lt;br /&gt;
===KT2MPS===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var metres_per_second = knots * KT2MPS;&lt;br /&gt;
|text = Converts a velocity from knots to metres per second when multiplied by the velocity in knots. Approximately equal to 0.5144.&lt;br /&gt;
}}&lt;br /&gt;
===L2GAL===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var gallons = litres * L2GAL;&lt;br /&gt;
|text = Converts a volume from litres to US liquid gallons when multiplied by the volume in litres. Approximately equal to 0.2642.&lt;br /&gt;
}}&lt;br /&gt;
===LB2KG===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var kilograms = pounds * LB2KG;&lt;br /&gt;
|text = Converts a mass from pounds to kilograms when multiplied by the mass in pounds. Approximately equal to 0.4536.&lt;br /&gt;
}}&lt;br /&gt;
===M2FT ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var feet = metres * M2FT;&lt;br /&gt;
|text = Converts a length from metres to feet when multiplied by the length in metres. Approximately equal to 3.2808.&lt;br /&gt;
}}&lt;br /&gt;
===M2IN===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var inches = metres * M2IN;&lt;br /&gt;
|text = Converts a length from metres to inches when multiplied by the length in metres. Approximately equal to 39.3701.&lt;br /&gt;
}}&lt;br /&gt;
===M2NM ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var nautical_miles = metres * M2NM;&lt;br /&gt;
|text = Converts a length from metres to nautical miles when multiplied by the length in metres. Approximately equal to 0.00054.&lt;br /&gt;
}}&lt;br /&gt;
===MPS2KT ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var knots = metres_per_second * MPS2KT;&lt;br /&gt;
|text = Converts a velocity from metres per second to knots when multiplied by the velocity in metres per second. Approximately equal to 1.9438.&lt;br /&gt;
}}&lt;br /&gt;
===NM2M ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var metres = nautical_miles * NM2M;&lt;br /&gt;
|text = Converts a length from nautical miles to metres when multiplied by the length in nautical miles. Equal to 1,852.&lt;br /&gt;
}}&lt;br /&gt;
===R2D===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var degrees = radians * R2D;&lt;br /&gt;
|text = Converts an angle from radians to degrees when multiplied by the angle in radians. Equal to &amp;lt;code&amp;gt;180 / π&amp;lt;/code&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Appendix}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Nasal namespaces}}&lt;/div&gt;</summary>
		<author><name>Clm76</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Nasal_library&amp;diff=138566</id>
		<title>Nasal library</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Nasal_library&amp;diff=138566"/>
		<updated>2023-10-30T10:08:26Z</updated>

		<summary type="html">&lt;p&gt;Clm76: /* findNavaidByFrequencyMHz() */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Nasal Navigation|nocat=1}}&lt;br /&gt;
This page documents the global '''library functions and variables''' of FlightGear's built-in scripting language, [[Nasal]]. This includes ''[[#Core library functions|core library functions]]'', which were included in Nasal before its integration into FlightGear, the ''[[#Extension functions|extension functions]]'', which have been subsequently added, and are specifically designed for FlightGear, and the ''[[#variables|global variables]]'', which are conversion variables, added with extension functions, for converting between units. The relevant folders in [[Git]] are:&lt;br /&gt;
* {{flightgear file|src/Scripting}}&lt;br /&gt;
* {{simgear file|simgear/nasal}}&lt;br /&gt;
&lt;br /&gt;
All these functions and variables are in the global namespace, that is, they are directly accessible (e.g., one can call &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; inline&amp;gt;magvar()&amp;lt;/syntaxhighlight&amp;gt; instead of &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; inline&amp;gt;namespace.magvar()&amp;lt;/syntaxhighlight&amp;gt;). However, if a namespace must be used, &amp;lt;code&amp;gt;globals&amp;lt;/code&amp;gt; is the correct namespace, but using it is not recommended. For a more complete explanation, see [[Nasal Namespaces in-depth]].&lt;br /&gt;
&lt;br /&gt;
{{tip|Copy &amp;amp; paste the examples into your [[Nasal Console]] and execute them to see what they do.|width=70%}}&lt;br /&gt;
&lt;br /&gt;
== Core library functions ==&lt;br /&gt;
This is the list of the basic '''core library functions.''' Most of these functions were part of the original Nasal library (before its integration in to FlightGear), while some have been added or changed over time.  See also:&lt;br /&gt;
* http://plausible.org/nasal/lib.html ([http://web.archive.org/web/20101010094553/http://plausible.org/nasal/lib.html archive])&lt;br /&gt;
* {{simgear file|simgear/nasal/lib.c}} ([http://sourceforge.net/p/flightgear/simgear/ci/next/log/?path=/simgear/nasal/lib.c history])&lt;br /&gt;
&lt;br /&gt;
=== append() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = append(vector, element[, element[, ...]]);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=42|t=Source}}&lt;br /&gt;
|text = This function appends, or adds, the given element(s) to the end of the vector given in the first argument.  Returns the vector operated on.&lt;br /&gt;
|param1 = vector&lt;br /&gt;
|param1text = The vector to which the arguments will be appended.&lt;br /&gt;
|param2 = element&lt;br /&gt;
|param2text = An element to be added to the vector.&lt;br /&gt;
|example1 = &lt;br /&gt;
var vector = [1, 2, 3]; # Initialize the vector&lt;br /&gt;
append(vector, 4); # Append the number 4 to the end of the vector&lt;br /&gt;
debug.dump(vector); # Print the contents of the vector&lt;br /&gt;
|example2 = &lt;br /&gt;
var vector = [1, 2, 3]; # Initialize the vector&lt;br /&gt;
append(vector, 4, 5, 6); # Append the numbers 4, 5, and 6 to the end of the vector&lt;br /&gt;
debug.dump(vector); # Print the contents of the vector&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== bind() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = bind(function, locals[, outer_scope]);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=502|t=Source}}&lt;br /&gt;
|text = This creates a new function object. A function in Nasal is three things: the actual code, a hash/namespace of local variables available to the function namespace, and the closure object of that namespace. These correspond to the three arguments respectively.&lt;br /&gt;
|param1 = function&lt;br /&gt;
|param1text = Function to evaluate.&lt;br /&gt;
|param2 = locals&lt;br /&gt;
|param2text = Hash containing values that will become the namespace (first closure) for the function.&lt;br /&gt;
|param3 = outer_scope&lt;br /&gt;
|param3text = Optional function which is bound to the next closure. This can be bound to yet another, making a linked list.&lt;br /&gt;
|example1 = # This is a namespace/hash with a single member, named &amp;quot;key,&amp;quot; which is initialized to 12 &lt;br /&gt;
var Namespace = {&lt;br /&gt;
    key: 12&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
# This is different namespace/hash containing a function&lt;br /&gt;
# dividing a variable &amp;quot;key&amp;quot; (which is unavailable/nil in this namespace) by 2&lt;br /&gt;
var AnotherNamespace = {&lt;br /&gt;
    ret: func {&lt;br /&gt;
        key /= 2;&lt;br /&gt;
    }&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
# To see that key is not available, try to call AnotherNamespace.ret() first&lt;br /&gt;
call(AnotherNamespace.ret, [], nil, nil, var errors = []);&lt;br /&gt;
if(size(errors)){&lt;br /&gt;
    print(&amp;quot;Key could not be divided/resolved!&amp;quot;);&lt;br /&gt;
    debug.printerror(errors);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# Associate the AnotherNamespace.ret() function with the first namespace&lt;br /&gt;
# so that &amp;quot;key&amp;quot; is now available&lt;br /&gt;
var function = bind(AnotherNamespace.ret, Namespace);&lt;br /&gt;
&lt;br /&gt;
# Invoke the new function&lt;br /&gt;
function();&lt;br /&gt;
&lt;br /&gt;
# Print out the value of Namespace.key&lt;br /&gt;
# It was changed to 12 from 6 by AnotherNamespace.ret()&lt;br /&gt;
print(Namespace.key);&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== call() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = call(func[, args[, me[, locals[, error]]]);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=247|t=Source}}&lt;br /&gt;
|text = Calls the given function with the given arguments and returns the result.  This function is very useful as it allows much more control over function calls and catches any errors or {{func link|die()}} calls that would normally trigger run-time errors cancelling execution of the script otherwise. &lt;br /&gt;
|param1 = func&lt;br /&gt;
|param1text = Function to execute.&lt;br /&gt;
|param2 = args&lt;br /&gt;
|param2text = Vector containing arguments to give to the called function.&lt;br /&gt;
|param3 = me&lt;br /&gt;
|param3text = &amp;lt;code&amp;gt;'''me'''&amp;lt;/code&amp;gt; reference for the function call (i.e., for method calls). If given, this will override any &amp;lt;code&amp;gt;'''me'''&amp;lt;/code&amp;gt; value existing in the namespace (locals argument).&lt;br /&gt;
|param4 = locals&lt;br /&gt;
|param4text = A hash with key/value pairs that will be available to the called function, typically used as the namespace for the function to be called.&lt;br /&gt;
|param5 = error&lt;br /&gt;
|param5text = A vector to append errors to.  If the called function generates an error, the error, place, and line will be written to this.  These errors can be printed using {{func link|printerror()|debug}}.&lt;br /&gt;
|example1 =&lt;br /&gt;
# prints &amp;quot;Called from call()&amp;quot;&lt;br /&gt;
call(func {&lt;br /&gt;
    print(&amp;quot;Called from call()&amp;quot;);&lt;br /&gt;
});&lt;br /&gt;
|example2 =&lt;br /&gt;
# prints &amp;quot;a = 1 : b = 2&lt;br /&gt;
call(func(a, b){&lt;br /&gt;
        print(&amp;quot;a = &amp;quot;, a, &amp;quot; : b = &amp;quot;, b);&lt;br /&gt;
    },&lt;br /&gt;
    [1, 2]&lt;br /&gt;
);&lt;br /&gt;
|example3 =&lt;br /&gt;
var Hash = {&lt;br /&gt;
    new: func {&lt;br /&gt;
        var m = { parents: [Hash] };&lt;br /&gt;
&lt;br /&gt;
        m.el1 = &amp;quot;string1&amp;quot;;&lt;br /&gt;
        m.el2 = &amp;quot;string2&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
        return m;&lt;br /&gt;
    }&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
# prints &amp;quot;me.el1 = string1&amp;quot;, then &amp;quot;me.el2 = string2&amp;quot; on the next line&lt;br /&gt;
call(func(a, b){        &lt;br /&gt;
        print(&amp;quot;me.el&amp;quot;, a, &amp;quot; = &amp;quot;, me[&amp;quot;el&amp;quot; ~ a]);      &lt;br /&gt;
        print(&amp;quot;me.el&amp;quot;, b, &amp;quot; = &amp;quot;, me[&amp;quot;el&amp;quot; ~ b]);&lt;br /&gt;
    },&lt;br /&gt;
    [1, 2],&lt;br /&gt;
    Hash.new()&lt;br /&gt;
);&lt;br /&gt;
|example4 =&lt;br /&gt;
# prints the value of math.pi&lt;br /&gt;
call(func {&lt;br /&gt;
        print(pi);&lt;br /&gt;
    }, nil, nil, &lt;br /&gt;
    math&lt;br /&gt;
);&lt;br /&gt;
|example5 =&lt;br /&gt;
call(func {&lt;br /&gt;
        print(math.ip); # math.ip doesn't exist&lt;br /&gt;
    }, nil, nil, nil,&lt;br /&gt;
    var errs = []&lt;br /&gt;
);&lt;br /&gt;
debug.printerror(errs); # The error is caught and printed using debug.printerror()&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== caller() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = caller([level]);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=404|t=Source}}&lt;br /&gt;
|text = Returns a vector containing a record from the current call stack.  The level numbering starts from the currently executing function (level 0).  Level 1 (the default) is the caller of the current function, and so on.&lt;br /&gt;
&lt;br /&gt;
The result is a four-element vector containing '''[0]''' a hash of local variables, '''[1]''' the function object, '''[2]''' the full source file name (incl. path) and '''[3]''' the line number. &lt;br /&gt;
|param1 = level&lt;br /&gt;
|param1text = Optional integer specifying the stack level to return a result from.  Defaults to 1 (i.e. the caller of the currently executing function).&lt;br /&gt;
|example1 =&lt;br /&gt;
var myFunction = func(a, b){&lt;br /&gt;
    debug.dump(caller(0)[0]); # prints a hash of local variables, including arguments a and b&lt;br /&gt;
    return 2 * 2;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;2 x 2 = &amp;quot;, myFunction(2, 2));&lt;br /&gt;
|example2 =&lt;br /&gt;
var get_arg_value = func(){&lt;br /&gt;
    print(&amp;quot;Argument to myFunc = &amp;quot;, caller(1)[0]['a']); # print the value of myFunc's single argument, using caller()&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
var myFunc = func(a){&lt;br /&gt;
    get_arg_value();&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
myFunc(3);&lt;br /&gt;
|example3text = This is a real example taken from {{fgdata file|Nasal/canvas/MapStructure.nas}}.  Function &amp;lt;code&amp;gt;r()&amp;lt;/code&amp;gt; (above the TODOs) returns a hash with the key/value pairs as per its arguments. For example, something like this is returned: &amp;lt;code&amp;gt;{ name: &amp;quot;&amp;lt;name&amp;gt;&amp;quot;, vis: 1, zindex: nil }&amp;lt;/code&amp;gt;.&lt;br /&gt;
|example3 =&lt;br /&gt;
var MapStructure_selfTest = func() {&lt;br /&gt;
	var temp = {};&lt;br /&gt;
	temp.dlg = canvas.Window.new([600,400],&amp;quot;dialog&amp;quot;);&lt;br /&gt;
	temp.canvas = temp.dlg.createCanvas().setColorBackground(1,1,1,0.5);&lt;br /&gt;
	temp.root = temp.canvas.createGroup();&lt;br /&gt;
	var TestMap = temp.root.createChild(&amp;quot;map&amp;quot;);&lt;br /&gt;
	TestMap.setController(&amp;quot;Aircraft position&amp;quot;);&lt;br /&gt;
	TestMap.setRange(25); # TODO: implement zooming/panning via mouse/wheel here, for lack of buttons :-/&lt;br /&gt;
	TestMap.setTranslation(&lt;br /&gt;
		temp.canvas.get(&amp;quot;view[0]&amp;quot;)/2,&lt;br /&gt;
		temp.canvas.get(&amp;quot;view[1]&amp;quot;)/2&lt;br /&gt;
	);&lt;br /&gt;
	var r = func(name,vis=1,zindex=nil) return caller(0)[0];&lt;br /&gt;
	# TODO: we'll need some z-indexing here, right now it's just random&lt;br /&gt;
	# TODO: use foreach/keys to show all layers in this case by traversing SymbolLayer.registry direclty ?&lt;br /&gt;
	# maybe encode implicit z-indexing for each lcontroller ctor call ? - i.e. preferred above/below order ?&lt;br /&gt;
	foreach(var type; [r('TFC',0),r('APT'),r('DME'),r('VOR'),r('NDB'),r('FIX',0),r('RTE'),r('WPT'),r('FLT'),r('WXR'),r('APS'), ] ) &lt;br /&gt;
		TestMap.addLayer(factory: canvas.SymbolLayer, type_arg: type.name,&lt;br /&gt;
					visible: type.vis, priority: type.zindex,&lt;br /&gt;
		);&lt;br /&gt;
}; # MapStructure_selfTest&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== chr() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = chr(code);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=175|t=Source}}&lt;br /&gt;
|text = Returns a character as per the single argument. Extended ASCII is supported (see http://www.asciitable.com/ for a list of supported characters), although this may vary between different systems.  For a list of the most commonly used characters, see the {{wikipedia|ASCII#ASCII printable code chart|ASCII printable code chart}} ('''Dec''' column). The following table lists supported control characters, along with their equivalent control characters in Nasal strings.  {{Note|In Nasal, only strings enclosed with double-quotes (&amp;lt;code&amp;gt;&amp;quot;string&amp;quot;&amp;lt;/code&amp;gt;) supports control chracters.  Strings in single quotes (&amp;lt;code&amp;gt;'string'&amp;lt;/code&amp;gt;) do not.}}&lt;br /&gt;
{{{!}} class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Code !! Name !! Equivalent to&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} 10 {{!!}} {{Wikipedia|Newline}} {{!!}} &amp;lt;code&amp;gt;\n&amp;lt;/code&amp;gt;&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} 9 {{!!}} {{Wikipedia|Tab key#Tab characters|Horizontal tab}} {{!!}} &amp;lt;code&amp;gt;\t&amp;lt;/code&amp;gt;&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} 13 {{!!}} {{Wikipedia|Carriage return}} {{!!}} &amp;lt;code&amp;gt;\r&amp;lt;/code&amp;gt;&lt;br /&gt;
{{!}}}&lt;br /&gt;
|param1 = code&lt;br /&gt;
|param1text = Integer character code for the desired glyph.&lt;br /&gt;
|example1 = print(&amp;quot;Code 65 = &amp;quot;, chr(65)); # prints &amp;quot;Code 65 = A&amp;quot;&lt;br /&gt;
|example2text = This example displays all of the characters in a list, in the format &amp;lt;code&amp;gt;Code '''n''' = &amp;gt;'''char'''&amp;lt;&amp;lt;/code&amp;gt;, '''n''' being the index, and '''char''' being the character.&lt;br /&gt;
|example2 =&lt;br /&gt;
for(var i = 0; i &amp;lt;= 255; i += 1){&lt;br /&gt;
    print(&amp;quot;Code &amp;quot;, i, &amp;quot; = &amp;gt;&amp;quot;, chr(i), &amp;quot;&amp;lt;&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== closure() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = closure(func[, level]);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=421|t=Source}}&lt;br /&gt;
|text = Returns the hash table containing the lexical namespace of the given function. The level numbering start with level 0 being the namespace of '''func'''. &lt;br /&gt;
|param1 = func&lt;br /&gt;
|param1text = Function to evaluate.&lt;br /&gt;
|param2 = level&lt;br /&gt;
|param2text = Optional integer specifying the scope level.  Defaults to 0 (the namespace of '''func''').&lt;br /&gt;
|example1 =&lt;br /&gt;
var get_math_e = func {&lt;br /&gt;
    return e; # return the value of math.e&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var myFunction = bind(get_math_e, math); # bind get_math_e to the math namespace, so that math.e is immediately available to get_math_e&lt;br /&gt;
debug.dump(closure(myFunction)); # print the namespace of get_math_e&lt;br /&gt;
&lt;br /&gt;
print(myFunction());&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== cmp() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = cmp(a, b);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=112|t=Source}}&lt;br /&gt;
|text = Compares two strings, returning -1 if '''a''' is less than '''b''', 0 if they are identical and 1 if '''a''' is greater than '''b'''. &lt;br /&gt;
|param1 = a&lt;br /&gt;
|param1text = First string argument for comparison.&lt;br /&gt;
|param2 = b&lt;br /&gt;
|param2text = Second string argument for comparison.&lt;br /&gt;
|example1 = print(cmp(&amp;quot;1&amp;quot;, &amp;quot;two&amp;quot;)); # prints -1&lt;br /&gt;
|example2 = print(cmp(&amp;quot;string&amp;quot;, &amp;quot;string&amp;quot;)); # prints 0&lt;br /&gt;
|example3 = print(cmp(&amp;quot;one&amp;quot;, &amp;quot;2&amp;quot;)); # prints 1&lt;br /&gt;
|example4 = print(cmp(&amp;quot;string1&amp;quot;, &amp;quot;string2&amp;quot;)); # prints -1&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== compile() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = compile(code[, filename]);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=220|t=Source}}&lt;br /&gt;
|text = Compiles the specified code string and returns a function object bound to the current lexical context.  If there is an error, the function dies, with the argument to {{func link|die()}} being '''filename'''.&lt;br /&gt;
|param1 = code&lt;br /&gt;
|param1text = String containing Nasal code to be compiled.&lt;br /&gt;
|param2 = filename&lt;br /&gt;
|param2text = Optional string used for error messages/logging. Defaults to &amp;lt;code&amp;gt;&amp;lt;compile&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
|example1 = &lt;br /&gt;
var myCode = 'print(&amp;quot;hello&amp;quot;);';&lt;br /&gt;
var helloFunc = compile(myCode, &amp;quot;myCode&amp;quot;);&lt;br /&gt;
helloFunc();&lt;br /&gt;
|example2text = &amp;lt;code&amp;gt;compile&amp;lt;/code&amp;gt; is very convenient to support Nasal loaded from other files.  For instance, [[PropertyList XML files]] (such as GUI dialogs) may contain embedded Nasal sections that need to be parsed, processed and compiled.  For an example of how to do this, save the below XML code as &amp;lt;tt&amp;gt;''[[$FG_ROOT]]/gui/dialogs/test.xml''&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?xml version=&amp;quot;1.0&amp;quot;?&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;PropertyList&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nasal&amp;gt;&amp;lt;![CDATA[&lt;br /&gt;
print(&amp;quot;You have FlightGear v&amp;quot;, getprop(&amp;quot;/sim/version/flightgear&amp;quot;));&lt;br /&gt;
]]&amp;gt;&amp;lt;/nasal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/PropertyList&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Now, start FlightGear and execute this code in the [[Nasal Console]].&lt;br /&gt;
|example2 =&lt;br /&gt;
# Build the path&lt;br /&gt;
var FGRoot = getprop(&amp;quot;/sim/fg-root&amp;quot;);&lt;br /&gt;
var filename = &amp;quot;/gui/dialogs/test.xml&amp;quot;;&lt;br /&gt;
var path = FGRoot ~ filename;&lt;br /&gt;
&lt;br /&gt;
var blob = io.read_properties(path);&lt;br /&gt;
var script = blob.getValues().nasal; # Get the nasal string&lt;br /&gt;
&lt;br /&gt;
# Compile the script.  We're passing the filename here for better runtime diagnostics &lt;br /&gt;
var code = call(func {&lt;br /&gt;
    compile(script, filename);&lt;br /&gt;
}, nil, nil, var compilation_errors = []);&lt;br /&gt;
&lt;br /&gt;
if(size(compilation_errors)){&lt;br /&gt;
    die(&amp;quot;Error compiling code in: &amp;quot; ~ filename);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# Invoke the compiled script, equivalent to code(); &lt;br /&gt;
# We're using call() here to detect errors:&lt;br /&gt;
call(code, [], nil, nil, var runtime_errors = []);&lt;br /&gt;
&lt;br /&gt;
if(size(runtime_errors)){&lt;br /&gt;
    die(&amp;quot;Error calling code compiled loaded from: &amp;quot; ~ filename);&lt;br /&gt;
}&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== contains() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = contains(hash, key);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=184|t=Source}}&lt;br /&gt;
|text = Returns 1 (True) if the hash contains the specified key, or 0 (False) if not.&lt;br /&gt;
|param1 = hash&lt;br /&gt;
|param1text = The hash to search in.&lt;br /&gt;
|param2 = key&lt;br /&gt;
|param2text = The scalar to be searched for, contained as a key in the hash.&lt;br /&gt;
|example1 =&lt;br /&gt;
# Initialize a hash&lt;br /&gt;
var hash = {&lt;br /&gt;
    element: &amp;quot;value&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
print(contains(hash, &amp;quot;element&amp;quot;) ? &amp;quot;Yes&amp;quot; : &amp;quot;No&amp;quot;); # This will print &amp;quot;Yes&amp;quot;&lt;br /&gt;
|example2 =&lt;br /&gt;
# Initialize a hash&lt;br /&gt;
var hash = {&lt;br /&gt;
    element: &amp;quot;value&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
print(contains(hash, &amp;quot;element2&amp;quot;) ? &amp;quot;Yes&amp;quot; : &amp;quot;No&amp;quot;); # This will print &amp;quot;No&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===contains()===&lt;br /&gt;
{{Requires commit|desc=Vector support|details=see {{Merge-request|project=fgdata|id=305}}|commit=ee39abbd3b70c9b6d5e3a1c4ccedddaac1a92b11|repo=Simgear}}&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = contains(vector, item);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=184|t=Source}}&lt;br /&gt;
|text = Returns 1 (True) if the vector contains the specified item, or 0 (False) if not.&lt;br /&gt;
|param1 = vector&lt;br /&gt;
|param1text = The vector to search in.&lt;br /&gt;
|param2 = item&lt;br /&gt;
|param2text = The object to be searched for in the vector.&lt;br /&gt;
|example1 =&lt;br /&gt;
# Initialize a hash&lt;br /&gt;
var vec = [&amp;quot;element&amp;quot;, &amp;quot;foo&amp;quot;];&lt;br /&gt;
print(contains(hash, &amp;quot;element&amp;quot;) ? &amp;quot;Yes&amp;quot; : &amp;quot;No&amp;quot;); # This will print &amp;quot;Yes&amp;quot;&lt;br /&gt;
|example2 =&lt;br /&gt;
# Initialize a hash&lt;br /&gt;
var vec = [&amp;quot;element&amp;quot;, &amp;quot;foo&amp;quot;];&lt;br /&gt;
print(contains(hash, &amp;quot;element2&amp;quot;) ? &amp;quot;Yes&amp;quot; : &amp;quot;No&amp;quot;); # This will print &amp;quot;No&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===delete() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = delete(hash, key);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=83|t=Source}}&lt;br /&gt;
|text = Deletes the key from the hash if it exists. Operationally, this is NOT identical to setting the hash value specified by the key to &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; as the key will stay in the hash (at least for a while). This variant potentially frees storage by deleting the reference to the key and by shrinking the hash.  Returns the hash that has been operated on.&lt;br /&gt;
|param1 = hash&lt;br /&gt;
|param1text = The hash from which to delete the key.&lt;br /&gt;
|param2 = key&lt;br /&gt;
|param2text = The scalar to be deleted, contained as a key in the hash.&lt;br /&gt;
|example1 =&lt;br /&gt;
# Initialize the hash&lt;br /&gt;
var hash = {&lt;br /&gt;
    element1: &amp;quot;value1&amp;quot;,&lt;br /&gt;
    element2: &amp;quot;value2&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
delete(hash, &amp;quot;element1&amp;quot;); # Delete element1&lt;br /&gt;
debug.dump(hash); # prints the hash, which is now minus element1&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===die()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = die(error);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=288|t=Source}}&lt;br /&gt;
|text = Terminates execution and unwinds the stack.  The place and the line will be added to the '''error'''.  This invokes the same internal exception handler used for internal runtime errors. Use this to signal fatal errors, or to implement exception handling. The error thrown (including internal runtime errors) can be caught with {{func link|call()}}.&lt;br /&gt;
|param1 = error&lt;br /&gt;
|param1text = String describing the error.&lt;br /&gt;
:{{inote|This parameter is technically optional, but it is highly recommended to use it.}}&lt;br /&gt;
|example1 = &lt;br /&gt;
print(&amp;quot;Will print&amp;quot;);&lt;br /&gt;
die(&amp;quot;Don't go any further!&amp;quot;); &lt;br /&gt;
print(&amp;quot;Won't print&amp;quot;); # Will not be printed because die() stops the process&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== find()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = find(needle, haystack);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=450|t=Source}}&lt;br /&gt;
|text = Finds and returns the index of the first occurrence of the string '''needle''' in the string '''haystack''', or -1 if no such occurrence was found.&lt;br /&gt;
|param1 = needle&lt;br /&gt;
|param1text = String to search for.&lt;br /&gt;
|param2 = haystack&lt;br /&gt;
|param2text = String to search in.&lt;br /&gt;
|example1 = print(find(&amp;quot;c&amp;quot;, &amp;quot;abcdef&amp;quot;)); # prints 2&lt;br /&gt;
|example2 = print(find(&amp;quot;x&amp;quot;, &amp;quot;abcdef&amp;quot;)); # prints -1&lt;br /&gt;
|example3 = print(find(&amp;quot;cd&amp;quot;, &amp;quot;abcdef&amp;quot;)); # prints 2&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===ghosttype()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = ghosttype(ghost);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=207|t=Source}}&lt;br /&gt;
|text = Returns a string containing either a descriptive name of a ghost (a raw C/C++ object), or a unique id (the pointer to the C/C++ &amp;lt;code&amp;gt;naGhostType&amp;lt;/code&amp;gt; instance) if no name has been set.  Ghost is an acronym that stands for '''G'''arbage-collected '''H'''andle to '''O'''ut'''S'''ide '''T'''hingy.&lt;br /&gt;
|param1 = ghost&lt;br /&gt;
|param1text = Ghost to return a description for.&lt;br /&gt;
|example1 = print(ghosttype(airportinfo())); # prints &amp;quot;airport&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===id()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = id(object);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=570|t=Source}}&lt;br /&gt;
|text = Returns a string containing information on the type and ID of the object provided in the single argument.  The information is returned in the form of &amp;lt;code&amp;gt;'''&amp;lt;type&amp;gt;''':'''&amp;lt;id&amp;gt;'''&amp;lt;/code&amp;gt;, where '''&amp;lt;type&amp;gt;''' is the type of object, and '''&amp;lt;id&amp;gt;''' is the ID.&lt;br /&gt;
|param1 = object&lt;br /&gt;
|param1text = Can be either of a string, a vector, a hash, a code, a function, or a ghost.&lt;br /&gt;
|example1 = print(id(&amp;quot;A&amp;quot;)); # prints &amp;quot;str:000000001624A590&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===int() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = int(number);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=90|t=Source}}&lt;br /&gt;
|text = Returns the integer part of the numeric value of the single argument, or &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; if none exists.&lt;br /&gt;
|param1 = number&lt;br /&gt;
|param1text = Number or string with just a number in it to return an integer from.&lt;br /&gt;
|example1 = print(int(23)); # prints &amp;quot;23&amp;quot;&lt;br /&gt;
|example2 = print(int(23.123)); # prints &amp;quot;23&amp;quot;&lt;br /&gt;
|example3 = debug.dump(int(&amp;quot;string&amp;quot;)); # prints &amp;quot;nil&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===keys()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = keys(hash);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=33|t=Source}}&lt;br /&gt;
|text = Returns a vector containing the list of keys found in the single hash argument. &lt;br /&gt;
|param1 = hash&lt;br /&gt;
|param1text = The hash to return the keys from.&lt;br /&gt;
|example1 = &lt;br /&gt;
# Initialize a hash&lt;br /&gt;
var hash = {&lt;br /&gt;
    element1: &amp;quot;value&amp;quot;,&lt;br /&gt;
    element2: &amp;quot;value&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
debug.dump(keys(hash)); # print the vector&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===left()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = left(string, length);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=149|t=Source}}&lt;br /&gt;
|version = 2.12&lt;br /&gt;
|commit = {{simgear commit|bd7163|t=commit}}&lt;br /&gt;
|text = Returns a substring of '''string''', starting from the left.&lt;br /&gt;
|param1 = string&lt;br /&gt;
|param1text = String to return part of.&lt;br /&gt;
|param2 = length&lt;br /&gt;
|param2text = Integer specifying the length of the substring to return.&lt;br /&gt;
|example1 = print(left(&amp;quot;string&amp;quot;, 2)); # prints &amp;quot;st&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== num()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = num(number);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=102|t=Source}}&lt;br /&gt;
|text = Returns the numerical value of the single string argument, or &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; if none exists. &lt;br /&gt;
|param1 = number&lt;br /&gt;
|param1text = String with just a number in it to return a number from.&lt;br /&gt;
|example1 = print(num(&amp;quot;23&amp;quot;)); # prints &amp;quot;23&amp;quot;&lt;br /&gt;
|example2 = print(num(&amp;quot;23.123&amp;quot;)); # prints &amp;quot;23.123&amp;quot;&lt;br /&gt;
|example3 = debug.dump(num(&amp;quot;string&amp;quot;)); # prints &amp;quot;nil&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===pop()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = pop(vector);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=50|t=Source}}&lt;br /&gt;
|text = Removes and returns the last element of the single vector argument, or &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; if the vector is empty. &lt;br /&gt;
|param1 = vector&lt;br /&gt;
|param1text = Vector to remove an element from.&lt;br /&gt;
|example1 = &lt;br /&gt;
var vector = [1, 2, 3];&lt;br /&gt;
pop(vector);&lt;br /&gt;
debug.dump(vector); # prints &amp;quot;[1, 2]&amp;quot;&lt;br /&gt;
|example2 = &lt;br /&gt;
var vector = [1, 2, 3];&lt;br /&gt;
debug.dump(pop(vector)); # prints &amp;quot;3&amp;quot;&lt;br /&gt;
|example3 = &lt;br /&gt;
var vector = [];&lt;br /&gt;
debug.dump(pop(vector)); # prints &amp;quot;nil&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===right()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = right(string, length);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=161|t=Source}}&lt;br /&gt;
|version = 2.12&lt;br /&gt;
|commit = {{simgear commit|bd7163|t=commit}}&lt;br /&gt;
|text = Returns a substring of '''string''', starting from the right.&lt;br /&gt;
|param1 = string&lt;br /&gt;
|param1text = String to return part of.&lt;br /&gt;
|param2 = length&lt;br /&gt;
|param2text = Integer specifying the length of the substring to return.&lt;br /&gt;
|example1 = print(right(&amp;quot;string&amp;quot;, 2)); # prints &amp;quot;ng&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== setsize()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = setsize(vector, size);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=56|t=Source}}&lt;br /&gt;
|text = Sets the size of a vector. The first argument specifies a vector, the second a number representing the desired size of that vector. If the vector is currently larger than the specified size, it is truncated. If it is smaller, it is padded with &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; entries. Returns the vector operated upon. &lt;br /&gt;
|param1 = vector&lt;br /&gt;
|param1text = The vector to be operated on.&lt;br /&gt;
|param2 = size&lt;br /&gt;
|param2text = The desired size of the vector in number of entries.&lt;br /&gt;
|example1 = &lt;br /&gt;
var vector = [1, 2, 3]; # Initialize a vector&lt;br /&gt;
setsize(vector, 4);&lt;br /&gt;
debug.dump(vector); # print the vector&lt;br /&gt;
|example2 = &lt;br /&gt;
var vector = [1, 2, 3]; # Initialize a vector&lt;br /&gt;
setsize(vector, 2);&lt;br /&gt;
debug.dump(vector); # print the vector&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===size()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = size(object);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=23|t=Source}}&lt;br /&gt;
|text = Returns the size of the single argument. For strings, this is the length in bytes. For vectors, this is the number of elements. For hashes, it is the number of key/value pairs. If the argument is &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; or a number, this error will be thrown: &amp;lt;code&amp;gt;object has no size()&amp;lt;/code&amp;gt;.&lt;br /&gt;
|param1 = object&lt;br /&gt;
|param1text = Object to find the size of.  Must be a string, a vector or a hash.&lt;br /&gt;
|example1 = &lt;br /&gt;
var string = &amp;quot;string&amp;quot;;&lt;br /&gt;
print(size(string)); # prints &amp;quot;6&amp;quot;&lt;br /&gt;
|example2 =&lt;br /&gt;
var vector = [1, 2, 3];&lt;br /&gt;
print(size(vector)); # prints &amp;quot;3&amp;quot;&lt;br /&gt;
|example3 =&lt;br /&gt;
var hash = {&lt;br /&gt;
    element1: &amp;quot;value1&amp;quot;,&lt;br /&gt;
    element2: &amp;quot;value2&amp;quot;,&lt;br /&gt;
    element3: &amp;quot;value3&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
print(size(hash)); # prints &amp;quot;3&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== sort()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = sort(vector, function);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=542|t=Source}}&lt;br /&gt;
|text = Returns a vector containing the elements in the input '''vector''' sorted in according to the rule given by '''function'''. Implemented with the ANSI C {{func link|qsort()|link=http://www.cplusplus.com/reference/cstdlib/qsort/}}, &amp;lt;code&amp;gt;sort()&amp;lt;/code&amp;gt; is stable.  This means that if the rules in the first example are used, equal elements in the output vector will appear in the same relative order as they do in the input.  It is run in a loop, so '''function''' is run several times.&lt;br /&gt;
|param1 = vector&lt;br /&gt;
|param1text = Input vector to sort.&lt;br /&gt;
|param2 = function&lt;br /&gt;
|param2text = Function according to which the elements will be sorted by.  It should take two arguments and should return one of 1, 0, or -1.&lt;br /&gt;
{{{!}} class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Return value !! Meaning&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} less than 0 {{!!}} first argument should go before second argument&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} 0 {{!!}} first argument equals second argument&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} greater than 0 {{!!}} first argument should go after second argument&lt;br /&gt;
{{!}}}&lt;br /&gt;
&lt;br /&gt;
|example1text = This example sorts elements from smallest to greatest.&lt;br /&gt;
|example1 = &lt;br /&gt;
var sort_rules = func(a, b){&lt;br /&gt;
    if(a &amp;lt; b){&lt;br /&gt;
        return -1; # A should before b in the returned vector&lt;br /&gt;
    }elsif(a == b){&lt;br /&gt;
        return 0; # A is equivalent to b &lt;br /&gt;
    }else{&lt;br /&gt;
        return 1; # A should after b in the returned vector&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
debug.dump(sort([3, 2, 5, 6, 4, 1], sort_rules)); # prints &amp;quot;[1, 2, 3, 4, 5, 6]&amp;quot;&lt;br /&gt;
|example2text = This example sorts elements from greatest to smallest.&lt;br /&gt;
|example2 = &lt;br /&gt;
# Outputs the elements in reverse order (greatest to smallest)&lt;br /&gt;
var sort_rules = func(a, b){&lt;br /&gt;
    if(a &amp;lt; b){&lt;br /&gt;
        return 1; # -1 in the above example&lt;br /&gt;
    }elsif(a == b){&lt;br /&gt;
        return 0;&lt;br /&gt;
    }else{&lt;br /&gt;
        return -1; # 1 in the above example&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
debug.dump(sort([3, 2, 5, 6, 4, 1], sort_rules)); # prints &amp;quot;[6, 5, 4, 3, 2, 1]&amp;quot;&lt;br /&gt;
|example3text = This example sorts a vector of strings (runways for example) from smallest to greatest.&lt;br /&gt;
|example3 = &lt;br /&gt;
var runways = [&amp;quot;09R&amp;quot;,&amp;quot;27R&amp;quot;,&amp;quot;26L&amp;quot;,&amp;quot;09L&amp;quot;,&amp;quot;15&amp;quot;];&lt;br /&gt;
var rwy = sort(runways,func(a,b) cmp(a,b));&lt;br /&gt;
debug.dump(rwy); # prints ['09L','09R','15','26L','27R']&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== split()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = split(delimiter, string);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=460|t=Source}}&lt;br /&gt;
|text = Splits the input string into a vector of substrings bounded by occurrences of the delimiter substring.&lt;br /&gt;
|param1 = delimiter&lt;br /&gt;
|param1text = String that will split the substrings in the returned vector.&lt;br /&gt;
|param2 = string&lt;br /&gt;
|param2text = String to split up.&lt;br /&gt;
|example1 = debug.dump(split(&amp;quot;cd&amp;quot;, &amp;quot;abcdef&amp;quot;)); # prints &amp;quot;['ab', 'ef']&amp;quot;&lt;br /&gt;
|example2 = debug.dump(split(&amp;quot;.&amp;quot;, &amp;quot;3.2.0&amp;quot;)); # prints &amp;quot;[3, 2, 0]&amp;quot;&lt;br /&gt;
|example3 = debug.dump(split(&amp;quot;/&amp;quot;, &amp;quot;path/to/file&amp;quot;)); # prints &amp;quot;['path', 'to', 'file']&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===sprintf()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = &amp;lt;nowiki&amp;gt;sprintf(format[, arg[, arg, [...]]]);&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=355|t=Source}}&lt;br /&gt;
|text = Creates and returns a string formatted using ANSI C {{func link|vsnprintf()|link=http://en.cppreference.com/w/c/io/vfprintf}} &amp;lt;ref&amp;gt;&lt;br /&gt;
{{Cite web&lt;br /&gt;
|url = http://sourceforge.net/p/flightgear/simgear/ci/next/tree/simgear/nasal/lib.c#l308&lt;br /&gt;
|title = fgdata/simgear/simgear/nasal/lib.c, line 308&lt;br /&gt;
|accessdate = October 2015&lt;br /&gt;
}}&lt;br /&gt;
&amp;lt;/ref&amp;gt;.  Below is a table of supported format specifiers.&lt;br /&gt;
{{{!}} class=&amp;quot;wikitable&amp;quot; width=&amp;quot;75%&amp;quot;&lt;br /&gt;
{{!}}+ %[flags][width][.precision]specifier&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; {{!}} Flags&lt;br /&gt;
{{!-}}&lt;br /&gt;
! Flag !! Output&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;+&amp;lt;/code&amp;gt; {{!!}} Forces to precede the result with a plus or minus sign ('''+''' or '''-''') even for positive numbers. By default, only negative numbers are preceded with a '''-''' sign.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} ''space'' {{!!}} Prefixes non-signed numbers with a space.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;-&amp;lt;/code&amp;gt; {{!!}} Left-align the output of this placeholder (the default is to right-align the output) when the width option is specified.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt; {{!!}} Use 0 instead of spaces to pad a field when the width option is specified.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;#&amp;lt;/code&amp;gt; {{!!}} Used with &amp;lt;code&amp;gt;o&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;X&amp;lt;/code&amp;gt; specifiers the value is preceded with &amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;0x&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;0X&amp;lt;/tt&amp;gt; respectively for values different than zero. Used with &amp;lt;code&amp;gt;e&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;E&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;f&amp;lt;/code&amp;gt;, it forces the written output to contain a decimal point even if no digits would follow. By default, if no digits follow, no decimal point is written. Used with &amp;lt;code&amp;gt;g&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;G&amp;lt;/code&amp;gt; the result is the same as with &amp;lt;code&amp;gt;e&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;E&amp;lt;/code&amp;gt; but trailing zeros are not removed.&lt;br /&gt;
{{!-}}&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; {{!}} Width&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} colspan=&amp;quot;2&amp;quot; {{!}} Integer specifying the minimum number of characters to be returned. This includes the decimal point and decimal fraction as well as + or - signs.&lt;br /&gt;
{{!-}}&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; {{!}} Precision&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} colspan=&amp;quot;2&amp;quot; {{!}} Integer preceded by a dot specifying the number of decimal places to be written.&lt;br /&gt;
{{!-}}&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; {{!}} Specifiers&lt;br /&gt;
{{!-}}&lt;br /&gt;
! Specifier !! Output&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;d&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;i&amp;lt;/code&amp;gt; {{!!}} Signed decimal number.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;s&amp;lt;/code&amp;gt; {{!!}} A string&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;%&amp;lt;/code&amp;gt; {{!!}} Percent (%) character.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;c&amp;lt;/code&amp;gt; {{!!}} A single character assigned to a character code, the code given in an integer argument.  See http://www.asciitable.com/ for a list of supported characters and their codes.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;o&amp;lt;/code&amp;gt; {{!!}} Unsigned integer as an octal number.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;u&amp;lt;/code&amp;gt; {{!!}} Unsigned decimal integer.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;X&amp;lt;/code&amp;gt; {{!!}} Unsigned integer as a hexadecimal number.  If &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt; is used, any letters in the number are lowercase, while &amp;lt;code&amp;gt;X&amp;lt;/code&amp;gt; gives uppercase.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;e&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;E&amp;lt;/code&amp;gt; {{!!}} Double value in scientific notation (i.e., ''[-]ddd.ddd'''e'''[+/-]ddd''), with an exponent being denoted by &amp;lt;tt&amp;gt;e&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;E&amp;lt;/tt&amp;gt; depending on whether an upper or lowercase is used respectively.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;f&amp;lt;/code&amp;gt; {{!!}} Floating-point number, in fixed decimal notation, by default with 6 decimal places.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;F&amp;lt;/code&amp;gt; {{!!}} Appears to be available&amp;lt;ref&amp;gt;&lt;br /&gt;
{{Cite web&lt;br /&gt;
|url = http://sourceforge.net/p/flightgear/simgear/ci/next/tree/simgear/nasal/lib.c#l389&lt;br /&gt;
|title = fgdata/simgear/simgear/nasal/lib.c, line 389&lt;br /&gt;
|accessdate = October 2015&lt;br /&gt;
}}&lt;br /&gt;
&amp;lt;/ref&amp;gt;, but doesn't work.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;g&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;G&amp;lt;/code&amp;gt; {{!!}} Double in either normal or exponential notation, whichever is more appropriate for its magnitude. &amp;lt;code&amp;gt;g&amp;lt;/code&amp;gt; uses lower-case letters, &amp;lt;code&amp;gt;G&amp;lt;/code&amp;gt; uses upper-case letters. This type differs slightly from fixed-point notation in that insignificant zeroes to the right of the decimal point are not included.  Also, the decimal point is not included on whole numbers.&lt;br /&gt;
{{!}}}&lt;br /&gt;
&lt;br /&gt;
|param1 = format&lt;br /&gt;
|param1text = String specifying the format.  Can be used with or without a format specifiers.  See below for examples.&lt;br /&gt;
|param2 = arg&lt;br /&gt;
|param2text = Argument specifying a value to replace a format placeholder (such as &amp;lt;code&amp;gt;%d&amp;lt;/code&amp;gt;) in the format string.  Not required if there are no format specifiers.&lt;br /&gt;
&lt;br /&gt;
|example1 = print(sprintf(&amp;quot;%i&amp;quot;, 54)); # prints &amp;quot;54&amp;quot;&lt;br /&gt;
|example2 = print(sprintf(&amp;quot;Pi = %+.10f&amp;quot;, math.pi)); # prints &amp;quot;Pi = +3.1415926536&amp;quot;&lt;br /&gt;
|example3 = &lt;br /&gt;
print(sprintf(&amp;quot;%6d&amp;quot;, 23)); # prints &amp;quot;    23&amp;quot;&lt;br /&gt;
print(sprintf(&amp;quot;%06d&amp;quot;, 23)); # prints &amp;quot;000023&amp;quot;&lt;br /&gt;
|example4 =&lt;br /&gt;
var FGVer = getprop(&amp;quot;/sim/version/flightgear&amp;quot;);&lt;br /&gt;
print(sprintf(&amp;quot;You have FlightGear v%s&amp;quot;, FGVer)); # prints &amp;quot;You have FlightGear v&amp;lt;your version&amp;gt;&amp;quot;&lt;br /&gt;
|example5 = &lt;br /&gt;
print(sprintf(&amp;quot;Hexadecimal 100000 = %X&amp;quot;, 100000)); # prints &amp;quot;Hexadecimal 100000 = 186A0&amp;quot;&lt;br /&gt;
print(sprintf(&amp;quot;Hexadecimal 100000 = %x&amp;quot;, 100000)); # prints &amp;quot;Hexadecimal 100000 = 186a0&amp;quot;&lt;br /&gt;
|example6 = print(sprintf(&amp;quot;Code 65 is %c&amp;quot;, 65)); # prints &amp;quot;Code 65 is A&amp;quot;&lt;br /&gt;
|example7 = &lt;br /&gt;
print(sprintf(&amp;quot;%e&amp;quot;, 54)); # prints &amp;quot;5.400000e+001&amp;quot;&lt;br /&gt;
print(sprintf(&amp;quot;%E&amp;quot;, 54)); # prints &amp;quot;5.400000E+001&amp;quot;&lt;br /&gt;
|example8 = print(sprintf(&amp;quot;%o&amp;quot;, 54)); # prints &amp;quot;66&amp;quot;&lt;br /&gt;
|example9 = print(sprintf(&amp;quot;50%% of 100 is %i&amp;quot;, 100 / 2)); # prints &amp;quot;50% of 100 is 50&amp;quot;&lt;br /&gt;
|example10 =&lt;br /&gt;
print(sprintf(&amp;quot;%.2f&amp;quot;, 1.4));   #prints &amp;quot;1.40&amp;quot;&lt;br /&gt;
print(sprintf(&amp;quot;%.1f&amp;quot;, 1.4));   #prints &amp;quot;1.4&amp;quot;&lt;br /&gt;
print(sprintf(&amp;quot;% 4.1f&amp;quot;, 1.4)); #prints &amp;quot; 1.4&amp;quot;&lt;br /&gt;
print(sprintf(&amp;quot;%04.1f&amp;quot;, 1.4)); #prints &amp;quot;01.4&amp;quot;&lt;br /&gt;
print(sprintf(&amp;quot;% 6.1f&amp;quot;, 1.4)); #prints &amp;quot;   1.4&amp;quot;&lt;br /&gt;
print(sprintf(&amp;quot;%06.1f&amp;quot;, 1.4)); #prints &amp;quot;0001.4&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===streq()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = streq(a, b);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=129|t=Source}}&lt;br /&gt;
|text = Tests the string values of the two arguments for equality. This function is needed because the &amp;lt;code&amp;gt;'''=='''&amp;lt;/code&amp;gt; operator (see [[Nasal_Operators#Equality|Nasal Operators]]) tests for numeric equality first.  If either or both the arguments are not strings, 0 (False) will be returned.  Returns either 0 (False) or 1 (True).  {{Note|This function is rarely required in typical code.}}&lt;br /&gt;
|param1 = a&lt;br /&gt;
|param1text = First argument for testing equality.&lt;br /&gt;
|param2 = b&lt;br /&gt;
|param2text = Second argument for testing equality.&lt;br /&gt;
|example1 = print(streq(&amp;quot;0&amp;quot;, &amp;quot;0&amp;quot;)); # prints &amp;quot;1&amp;quot; (True)&lt;br /&gt;
|example2 = &lt;br /&gt;
print(0 == 0.0); # prints &amp;quot;1&amp;quot; (True)&lt;br /&gt;
print(streq(&amp;quot;0&amp;quot;, &amp;quot;0.0&amp;quot;)); # prints &amp;quot;0&amp;quot; (False)&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===substr()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = substr(string, start [, length]);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=129|t=Source}}&lt;br /&gt;
|text = Similar the {{func link|subvec()}}, but operates on strings. Computes and returns a substring. The first argument specifies a string, the second is the index of the start of a substring, the optional third argument specifies a length (the default is to return the rest of the string from the start).&lt;br /&gt;
|param1 = string&lt;br /&gt;
|param1text = String to return a substring from.&lt;br /&gt;
|param2 = start&lt;br /&gt;
|param2text = Integer specifying the start of a substring. Negative values specify a position from the end of the string.&lt;br /&gt;
|param3 = length&lt;br /&gt;
|param3text = Optional argument specifying the length of the substring. Defaults to the end of the string.&lt;br /&gt;
|example1 = print(substr(&amp;quot;abcde&amp;quot;, 1, 3)); # prints &amp;quot;bcd&amp;quot;&lt;br /&gt;
|example2 = print(substr(&amp;quot;abcde&amp;quot;, 1)); # prints &amp;quot;bcde&amp;quot;&lt;br /&gt;
|example3 = print(substr(&amp;quot;abcde&amp;quot;, 2, 1)); # prints &amp;quot;c&amp;quot;&lt;br /&gt;
|example4 = print(substr(&amp;quot;abcde&amp;quot;, -2)); # prints &amp;quot;de&amp;quot;&lt;br /&gt;
|example5 = print(substr(&amp;quot;abcde&amp;quot;, -3, 2)); # prints &amp;quot;cd&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===subvec()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = subvec(vector, start[, length]);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=63|t=Source}}&lt;br /&gt;
|text = Returns a sub-range of a vector. The first argument specifies a vector, the second a starting index, and the optional third argument indicates a length (the default is to the end of the vector). &lt;br /&gt;
|param1 = vector&lt;br /&gt;
|param1text = The vector to take the sub-vector from.&lt;br /&gt;
|param2 = start&lt;br /&gt;
|param2text = The starting point of the sub-vector within the given vector.&lt;br /&gt;
|param3 = length&lt;br /&gt;
|param3text = Optional argument specifying the length of the sub-vector, from the starting point.&lt;br /&gt;
'''Notes:'''&lt;br /&gt;
* Omitting the ''vector'' and ''start'' arguments is not an error (possibly it should be) but the return value is ''nil''.&lt;br /&gt;
* A negative ''start'' argument ''is'' an error. This seems wrong. Perhaps the language designer could comment.&lt;br /&gt;
* A value of ''start'' greater than ''size(vector)'' causes an error. A value equal to ''size(vector)'' returns an empty vector.&lt;br /&gt;
* If the value of ''length'' is greater than ''size(vector) - start'' then it is ignored. That is, all elements from ''start'' to the end of ''vector'' are returned. If ''length'' is zero then an empty vector is returned. A negative value of ''length'' causes an error.&lt;br /&gt;
|example1 = &lt;br /&gt;
var vector = [1, 2, 3];&lt;br /&gt;
debug.dump(subvec(vector, 0)); # prints &amp;quot;[1, 2, 3]&amp;quot;&lt;br /&gt;
|example2 = &lt;br /&gt;
var vector = [1, 2, 3];&lt;br /&gt;
debug.dump(subvec(vector, 1)); # prints &amp;quot;[2, 3]&amp;quot;&lt;br /&gt;
|example3 = &lt;br /&gt;
var vector = [1, 2, 3];&lt;br /&gt;
debug.dump(subvec(vector, 1, 1)); # prints &amp;quot;[2]&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== typeof()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = typeof(object);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=193|t=Source}}&lt;br /&gt;
|text = Returns a string indicating the whether the object is &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt;, a scalar (number or string), a vector, a hash, a function, or a ghost.&lt;br /&gt;
|param1 = object&lt;br /&gt;
|param1text = Object to return the type of.&lt;br /&gt;
|example1 = &lt;br /&gt;
var object = nil;&lt;br /&gt;
print(typeof(object)); # prints &amp;quot;nil&amp;quot;&lt;br /&gt;
|example2 = &lt;br /&gt;
var object = &amp;quot;Hello world!&amp;quot;;&lt;br /&gt;
print(typeof(object)); # prints &amp;quot;scalar&amp;quot;&lt;br /&gt;
|example3 = &lt;br /&gt;
var object = math.pi;&lt;br /&gt;
print(typeof(object)); # prints &amp;quot;scalar&amp;quot;&lt;br /&gt;
|example4 = &lt;br /&gt;
var object = [1, 2, 3];&lt;br /&gt;
print(typeof(object)); # prints &amp;quot;vector&amp;quot;&lt;br /&gt;
|example5 = &lt;br /&gt;
var object = {};&lt;br /&gt;
print(typeof(object)); # prints &amp;quot;hash&amp;quot;&lt;br /&gt;
|example6 = &lt;br /&gt;
var object = func {};&lt;br /&gt;
print(typeof(object)); # prints &amp;quot;func&amp;quot;&lt;br /&gt;
|example7 =&lt;br /&gt;
var object = airportinfo();&lt;br /&gt;
print(typeof(object)); # prints &amp;quot;ghost&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- == Extension modules ==&lt;br /&gt;
=== thread ===&lt;br /&gt;
{{WIP}}&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = thread.newthread(func);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/threadlib.c|l=101|t=Source}}&lt;br /&gt;
|text = &lt;br /&gt;
|example1text = start a new worker thread&lt;br /&gt;
|example1 = thread.newthread( func() {} );&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = thread.newlock();&lt;br /&gt;
|source = {{simgear file|simgear/nasal/threadlib.c|l=101|t=Source}}&lt;br /&gt;
|text = &lt;br /&gt;
|example1text = create a new lock&lt;br /&gt;
|example1 = var lock = thread.newlock()&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = thread.lock();&lt;br /&gt;
|source = {{simgear file|simgear/nasal/threadlib.c|l=101|t=Source}}&lt;br /&gt;
|text = &lt;br /&gt;
|example1text = lock a lock&lt;br /&gt;
|example1 = var lock = thread.newlock()&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = thread.unlock();&lt;br /&gt;
|source = {{simgear file|simgear/nasal/threadlib.c|l=101|t=Source}}&lt;br /&gt;
|text = &lt;br /&gt;
|example1text = unlock a lock&lt;br /&gt;
|example1 = var lock = thread.unlock()&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = thread.newsem();&lt;br /&gt;
|source = {{simgear file|simgear/nasal/threadlib.c|l=101|t=Source}}&lt;br /&gt;
|text = &lt;br /&gt;
|example1text = create a new {{Wikipedia|semaphore}}&lt;br /&gt;
|example1 = var semaphore = thread.newsem()&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = thread.semdown();&lt;br /&gt;
|source = {{simgear file|simgear/nasal/threadlib.c|l=101|t=Source}}&lt;br /&gt;
|text = &lt;br /&gt;
|example1text = semaphore down&lt;br /&gt;
|example1 = thread.semdown(semaphore)&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = thread.semup();&lt;br /&gt;
|source = {{simgear file|simgear/nasal/threadlib.c|l=101|t=Source}}&lt;br /&gt;
|text = &lt;br /&gt;
|example1text = semaphore up&lt;br /&gt;
|example1 = thread.semup(semaphore)&lt;br /&gt;
}} --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Extension functions==&lt;br /&gt;
The '''extension functions''' are global functions that have been added to Nasal since its integration into FlightGear. Unlike the core library functions, they are generally specifically designed to interact directly with FlightGear. Extension functions come from three source files:&lt;br /&gt;
*{{flightgear file|src/Scripting/NasalPositioned.cxx}}&lt;br /&gt;
*{{flightgear file|src/Scripting/NasalSys.cxx}}&lt;br /&gt;
*{{fgdata file|Nasal/globals.nas}}&lt;br /&gt;
&lt;br /&gt;
===abort()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = abort();&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=565|t=Source}}&lt;br /&gt;
|text = This function is a wrapper for the C++ {{func link|abort()|link=http://www.cplusplus.com/reference/cstdlib/abort/}} function. It simply aborts FlightGear with an error, which varies depending on the operating system. This function should not really be used; instead, please use the &amp;quot;exit&amp;quot; [[Fgcommands|fgcommand]], which will exit FlightGear more gracefully (see example below).&lt;br /&gt;
|example1text = This example will immediately stop FlightGear with an error, such as &amp;quot;FlightGear has stopped working.&amp;quot;&lt;br /&gt;
|example1 = abort();&lt;br /&gt;
|example2text = For exiting FlightGear in a better way, please use the following code:&lt;br /&gt;
|example2 = fgcommand(&amp;quot;exit&amp;quot;);&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== abs() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = abs(number);&lt;br /&gt;
|source = {{fgdata file|Nasal/globals.nas|t=Source}}&lt;br /&gt;
|text = This simple function returns the {{wikipedia|absolute value|noicon=1}} of the provided number.&lt;br /&gt;
|param1 = number&lt;br /&gt;
|param1text = This argument is required and should be a number.&lt;br /&gt;
|example1 = print(abs(1)); # prints &amp;quot;1&amp;quot;&lt;br /&gt;
|example2 = print(abs(-1)); # prints &amp;quot;1&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===aircraftToCart() ===&lt;br /&gt;
This new function in FG 2017.2.1 takes coordinates in aircraft structural coordinate system, and translate them into geocentric coordinates.&lt;br /&gt;
Example for (5,6,7):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
var pos = aircraftToCart({x: -5, y: 6, z: -7});&lt;br /&gt;
var coord = geo.Coord.new();&lt;br /&gt;
coord.set_xyz(pos.x, pos.y, pos.z);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Notice: x and z is inverted sign on purpose.&lt;br /&gt;
if you want lat. lon, alt from that, just call: (degrees and meters)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
coord.lat()&lt;br /&gt;
coord.lon()&lt;br /&gt;
coord.alt()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===addcommand() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = addcommand(name, code);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=659|t=Source}}&lt;br /&gt;
|version = 2.12&lt;br /&gt;
|commit = {{flightgear commit|7b663c|t=commit}}&lt;br /&gt;
|text = {{see also|Howto:Add new fgcommands to FlightGear}}&lt;br /&gt;
&lt;br /&gt;
This function enables the addition of a new custom [[fgcommands|fgcommand]] to FlightGear from within Nasal. An fgcommand created using this method can be used in exactly the same way as the built-in fgcommands. Also, an fgcommand created via this method will always return True or 1, like all other fgcommands.&lt;br /&gt;
|param1 = name&lt;br /&gt;
|param1text = This will become the name of the new fgcommand. Must be a string.&lt;br /&gt;
|param2 = code&lt;br /&gt;
|param2text = The code that will be executed when the fgcommand is run. Must be a function.&lt;br /&gt;
|example1text = This example adds a new fgcommand and then runs it. Although it executes a simple {{func link|print()}} statement, any valid Nasal code can be used.&lt;br /&gt;
|example1 = addcommand(&amp;quot;myFGCmd&amp;quot;, func(node) {&lt;br /&gt;
    print(&amp;quot;fgcommand 'myFGCmd' has been run.&amp;quot;);&lt;br /&gt;
    props.dump( node );&lt;br /&gt;
});&lt;br /&gt;
fgcommand(&amp;quot;myFGCmd&amp;quot;, props.Node.new({foo:1, bar:2}) );&lt;br /&gt;
|example2text = This example demonstrates how parameters are defined in a new fgcommand.&lt;br /&gt;
|example2 = addcommand(&amp;quot;myFGCmd&amp;quot;, func(node){&lt;br /&gt;
    print(node.getNode(&amp;quot;number&amp;quot;).getValue()); # prints the value of &amp;quot;number,&amp;quot; which is 12&lt;br /&gt;
});&lt;br /&gt;
fgcommand(&amp;quot;myFGCmd&amp;quot;, props.Node.new({&amp;quot;number&amp;quot;: 12}));&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===airportinfo()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = airportinfo();&lt;br /&gt;
airportinfo(type);&lt;br /&gt;
airportinfo(id);&lt;br /&gt;
airportinfo(lat, lon[, type]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1024|t=Source}}&lt;br /&gt;
|text = Function for retrieval of airport, heliport, or seaplane base information. It returns a Nasal ghost; however, its structure is like that of a Nasal hash. The following information is returned:&lt;br /&gt;
* '''parents''': A vector containing a hash of various functions to access information about the runway. See {{flightgear file|src/Scripting/NasalPositioned.cxx|l=2659}} for full list.&lt;br /&gt;
* '''lon''': Longitude of the location.&lt;br /&gt;
* '''lat''': Latitude of the location.&lt;br /&gt;
* '''has_metar''': True or false depending whether the airport has a [[METAR]] code defined for it.&lt;br /&gt;
* '''elevation''': Elevation of the location in metres.&lt;br /&gt;
* '''id''': ICAO code of the airport (or ID of the seaplane base/heliport).&lt;br /&gt;
* '''name''': Name of the airport/heliport/seaplane base.&lt;br /&gt;
* '''runways'''&lt;br /&gt;
** '''&amp;lt;runway name&amp;gt;'''&lt;br /&gt;
*** '''id''': Name of runway.&lt;br /&gt;
*** '''lat''': Latitude of the runway.&lt;br /&gt;
*** '''lon''': Longitude of the runway.&lt;br /&gt;
*** '''heading''': Heading of the runway.&lt;br /&gt;
*** '''length''': Length of the runway in metres.&lt;br /&gt;
*** '''width''': Width of the runway in metres.&lt;br /&gt;
*** '''surface''': Runway surface type.&lt;br /&gt;
*** '''threshold''': Length of the runway's {{wikipedia|displaced threshold}} in metres. Will return 0 if there is none.&lt;br /&gt;
*** '''stopway''': Length of the runway's stopway (the area before the threshold) in metres. Will return 0 if there is none.&lt;br /&gt;
*** '''reciprocal''': &amp;lt;code&amp;gt;runway&amp;lt;/code&amp;gt; ghost of the reciprocal runway.&lt;br /&gt;
*** '''ils_frequency_mhz''': ILS frequency in megahertz.&lt;br /&gt;
*** '''ils''': &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt; ghost of the ILS transmitter.&lt;br /&gt;
* '''helipads'''&lt;br /&gt;
** '''&amp;lt;helipad name&amp;gt;'''&lt;br /&gt;
*** '''id''': Name of helipad.&lt;br /&gt;
*** '''lat''': Latitude of the helipad.&lt;br /&gt;
*** '''lon''': Longitude of the helipad.&lt;br /&gt;
*** '''heading''': Heading of the helipad.&lt;br /&gt;
*** '''length''': Length of the helipad in metres.&lt;br /&gt;
*** '''width''': Width of the helipad in metres.&lt;br /&gt;
*** '''surface''': Helipad surface type.&lt;br /&gt;
* '''taxiways'''&lt;br /&gt;
** '''&amp;lt;taxiway name&amp;gt;'''&lt;br /&gt;
*** '''id''': Name of taxiway.&lt;br /&gt;
*** '''lat''': Latitude of the taxiway.&lt;br /&gt;
*** '''lon''': Longitude of the taxiway.&lt;br /&gt;
*** '''heading''': Heading of the taxiway.&lt;br /&gt;
*** '''length''': Length of the taxiway in metres.&lt;br /&gt;
*** '''width''': Width of the taxiway in metres.&lt;br /&gt;
*** '''surface''': Taxiway surface type.&lt;br /&gt;
&lt;br /&gt;
Information is extracted in the same way as accessing members of a Nasal hash. For example:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
# prints to lengths of the runways of the nearest airport in feet and metres&lt;br /&gt;
var info = airportinfo();&lt;br /&gt;
print(&amp;quot;-- Lengths of the runways at &amp;quot;, info.name, &amp;quot; (&amp;quot;, info.id, &amp;quot;) --&amp;quot;);&lt;br /&gt;
foreach(var rwy; keys(info.runways)){&lt;br /&gt;
    print(rwy, &amp;quot;: &amp;quot;, math.round(info.runways[rwy].length * M2FT), &amp;quot; ft (&amp;quot;, info.runways[rwy].length, &amp;quot; m)&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that searches for locations that are a long way away (e.g., the nearest seaplane base to the middle of the Sahara) may cause FlightGear to pause for an amount of time.&lt;br /&gt;
|param1 = id&lt;br /&gt;
|param1text = The {{wikipedia|International Civil Aviation Organization airport code|ICAO code|noicon=1}} of an airport to retrieve information about.&lt;br /&gt;
|param2 = type&lt;br /&gt;
|param2text = When this argument is used, the function will return the closest airport of a certain type. Can be one of &amp;quot;heliport,&amp;quot; &amp;quot;seaport,&amp;quot; or &amp;quot;airport&amp;quot; (default).&lt;br /&gt;
: {{inote|Running this function without any parameters is equivalent to this:&lt;br /&gt;
: &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
airportinfo(&amp;quot;airport&amp;quot;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
|param3 = lat ''and'' lon&lt;br /&gt;
|param3text = When these parameters are used, the function will return information on the nearest airport, heliport or seaplane base (depending on the '''type''' parameter) to those coordinates.&lt;br /&gt;
|example1 = var info = airportinfo();&lt;br /&gt;
print(&amp;quot;Nearest airport: &amp;quot;, info.name, &amp;quot; (&amp;quot;, info.id, &amp;quot;)&amp;quot;); # prints the name and ICAO code of the nearest airport&lt;br /&gt;
|example2 = var info = airportinfo(&amp;quot;heliport&amp;quot;);&lt;br /&gt;
print(&amp;quot;Elevation of the nearest heliport: &amp;quot;, math.round(info.elevation * M2FT), &amp;quot; ft&amp;quot;); # prints the elevation and name of the nearest heliport&lt;br /&gt;
|example3 = var info = airportinfo(&amp;quot;KSQL&amp;quot;);&lt;br /&gt;
print(&amp;quot;-- Runways of &amp;quot;, info.name, &amp;quot; (&amp;quot;, info.id, &amp;quot;): --&amp;quot;);&lt;br /&gt;
foreach(var rwy; keys(info.runways)) {&lt;br /&gt;
    print(rwy); # prints the runways of KSQL&lt;br /&gt;
}&lt;br /&gt;
|example4 = var info = airportinfo(37.81909385, -122.4722484);&lt;br /&gt;
print(&amp;quot;Coordinates of the nearest airport: &amp;quot;, info.lat, &amp;quot;, &amp;quot;, info.lon); # print the name and ICAO of the nearest airport to the Golden Gate Bridge&lt;br /&gt;
|example5 = var info = airportinfo(37.81909385, -122.4722484, &amp;quot;seaport&amp;quot;);&lt;br /&gt;
print(&amp;quot;Nearest seaplane base: &amp;quot;, info.name, &amp;quot; (&amp;quot;, info.id, &amp;quot;)&amp;quot;); # print the name and ID of the nearest seaplane base to the Golden Gate Bridge&lt;br /&gt;
|example6text = This example prints the all information from an &amp;lt;code&amp;gt;airportinfo()&amp;lt;/code&amp;gt; call.&lt;br /&gt;
|example6 = var info = airportinfo(&amp;quot;KSFO&amp;quot;);&lt;br /&gt;
print(info.name);&lt;br /&gt;
print(info.id);&lt;br /&gt;
print(info.lat);&lt;br /&gt;
print(info.lon);&lt;br /&gt;
print(info.has_metar);&lt;br /&gt;
print(info.elevation);&lt;br /&gt;
foreach(var rwy; keys(info.runways)){&lt;br /&gt;
    print(&amp;quot;-- &amp;quot;, rwy, &amp;quot; --&amp;quot;);&lt;br /&gt;
    print(info.runways[rwy].lat);&lt;br /&gt;
    print(info.runways[rwy].lon);&lt;br /&gt;
    print(info.runways[rwy].length);&lt;br /&gt;
    print(info.runways[rwy].width);&lt;br /&gt;
    print(info.runways[rwy].heading);&lt;br /&gt;
    print(info.runways[rwy].stopway);&lt;br /&gt;
    print(info.runways[rwy].threshold);&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===airwaysRoute() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = airwaysRoute(start, end[, type]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1933|t=Source}}&lt;br /&gt;
|text = {{see also|Nasal Flightplan}}&lt;br /&gt;
This function returns a vector containing waypoints between two given waypoints. The returned waypoints are ghosts, but can be accessed in the same way as a Nasal hash. See [[Nasal Flightplan]] for more information.&lt;br /&gt;
|param1 = start&lt;br /&gt;
|param1text = Start waypoint, in the form of a waypoint ghost, such as that provided by {{func link|flightplan()}}.&lt;br /&gt;
|param2 = end&lt;br /&gt;
|param2text = Same as above.&lt;br /&gt;
|param3 = type&lt;br /&gt;
|param3text = Instructs the function to compute a high level route (when set to &amp;quot;highlevel&amp;quot;), or a low level route (when set to &amp;quot;lowlevel&amp;quot;). Defaults to &amp;quot;highlevel.&amp;quot;&lt;br /&gt;
|example1text = In the [[route manager]] dialog, add two waypoints to the flightplan, ideally ones that are far apart (tip: use the [[Map]] for this). Then run this code in your Nasal Console.&lt;br /&gt;
|example1 = var fp = flightplan();&lt;br /&gt;
var start = fp.getWP(0);&lt;br /&gt;
var end = fp.getWP(fp.getPlanSize() - 1);&lt;br /&gt;
var rt = airwaysRoute(start, end);&lt;br /&gt;
foreach(var wp; rt){&lt;br /&gt;
    print(wp.wp_name); # print the waypoints in the computed route&lt;br /&gt;
}&lt;br /&gt;
|example2text = Exactly the same as above, but computes a low level path.&lt;br /&gt;
|example2 = var fp = flightplan();&lt;br /&gt;
var start = fp.getWP(0);&lt;br /&gt;
var end = fp.getWP(fp.getPlanSize() - 1);&lt;br /&gt;
var rt = airwaysRoute(start, end, &amp;quot;lowlevel&amp;quot;);&lt;br /&gt;
foreach(var wp; rt){&lt;br /&gt;
    print(wp.wp_name); # print the waypoints in the computed route&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===airway()===&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = airway(ident [, pos]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=2644|t=Source}}&lt;br /&gt;
|text = {{see also|Nasal Flightplan}}&lt;br /&gt;
This function returns a ghost containing an airway of a specified id.&lt;br /&gt;
|param1 = ident&lt;br /&gt;
|param1text = a Positioned ghost (leg, navaid, airport) and so on, passed to the search function.&lt;br /&gt;
|param2 = pos&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===assert()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = assert(condition[, message]);&lt;br /&gt;
|source = {{fgdata file|Nasal/globals.nas|t=Source}}&lt;br /&gt;
|version = 3.2&lt;br /&gt;
|commit = {{fgdata commit|8b16a7|t=commit}}&lt;br /&gt;
|text = Returns either true if the condition evaluates as true, or aborts with a {{func link|die()}} call, which can be customised.&lt;br /&gt;
|param1 = condition&lt;br /&gt;
|param1text = Condition to evaluate.&lt;br /&gt;
|param2 = message&lt;br /&gt;
|param2text = Optional message that will be used in any {{func link|die()}} call. Defaults to &amp;quot;assertion failed!&amp;quot;&lt;br /&gt;
|example1 = var a = 1;&lt;br /&gt;
var b = 2;&lt;br /&gt;
print(assert(a &amp;lt; b)); # prints &amp;quot;1&amp;quot; (true)&lt;br /&gt;
|example2 = var a = 1;&lt;br /&gt;
var b = 2;&lt;br /&gt;
assert(a &amp;gt; b, 'a is not greater than b'); # aborts with a custom error message&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===carttogeod()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = carttogeod(x, y, z);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=945|t=Source}}&lt;br /&gt;
|text = Converts {{wikipedia|ECEF|Earth-centered, Earth-fixed}} coordinates (x, y and z) to {{wikipedia|geodetic coordinates}} (latitude, longitude, and altitude). A vector is returned containing latitude and longitude, both in degrees, and altitude, which is returned in metres above the equatorial radius of Earth as defined by the {{wikipedia|WGS 84}} (6,378,137 metres).&amp;lt;ref&amp;gt;{{simgear file|simgear/math/sg_geodesy.hxx|l=43}}&amp;lt;/ref&amp;gt;&lt;br /&gt;
|param1 = x&lt;br /&gt;
|param1text = Mandatory x-axis value, in metres.&lt;br /&gt;
|param2 = y&lt;br /&gt;
|param2text = Mandatory y-axis value, in metres.&lt;br /&gt;
|param3 = z&lt;br /&gt;
|param3text = Mandatory z-axis value, in metres.&lt;br /&gt;
|example1 = var (lat, lon, alt) = carttogeod(6378137, 0, 0); # point is the intersection of the prime meridian and equator.&lt;br /&gt;
print(&amp;quot;Latitude: &amp;quot;, lat); # prints lat, lon and alt, which are all zero, see above&lt;br /&gt;
print(&amp;quot;Longitude: &amp;quot;, lon);&lt;br /&gt;
print(&amp;quot;Altitude: &amp;quot;, alt);&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===cmdarg()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|private = _cmdarg()&lt;br /&gt;
|syntax = cmdarg();&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=513|t=Part 1}} {{!}} {{fgdata file|Nasal/globals.nas|t=Part 2}}&lt;br /&gt;
|text = &amp;lt;code&amp;gt;cmdarg()&amp;lt;/code&amp;gt; returns the property root of certain types of XML files. These could be nodes in the [[Property Tree]], or temporary and/or non-public nodes outside the Property tree. &lt;br /&gt;
It is used by Nasal scripts embedded in XML files. It returns a &amp;lt;code&amp;gt;props.Node&amp;lt;/code&amp;gt; object (see {{fgdata file|Nasal/props.nas}}), and you can use all of its methods on the returned value. &amp;lt;code&amp;gt;cmdarg()&amp;lt;/code&amp;gt; should only be used in four types/places of XML files:&lt;br /&gt;
* Bindings: This is needed so that the value of a joystick's axis can be accessed internally.&lt;br /&gt;
* Dialogs: This will return the root of the dialog in the Property Tree. This is useful for dialogs that are created/modified procedurally (e.g. for populating/changing widgets while loading the dialog). &lt;br /&gt;
* Embedded Canvases: The Nasal code behind [[Canvas]] windows [[Howto:Adding a canvas to a GUI dialog|embedded in PUI dialogs]] can use it to accessing the root directory of their Canvas.&lt;br /&gt;
* Animation XML files: If the animation XML file is used in an AI/MP model, &amp;lt;code&amp;gt;cmdarg()&amp;lt;/code&amp;gt; will return the root of the AI model in the &amp;lt;code&amp;gt;/ai/models/&amp;lt;/code&amp;gt; directory. Examples: &amp;lt;code&amp;gt;/ai/models/aircraft[3]/&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/ai/models/multiplayer[1]/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You should not use &amp;lt;code&amp;gt;cmdarg()&amp;lt;/code&amp;gt; in places other than those stated above. Although it won't cause an error, it will return the value of the last legitimate &amp;lt;code&amp;gt;cmdarg()&amp;lt;/code&amp;gt; call. &lt;br /&gt;
&lt;br /&gt;
Also, you should not delay &amp;lt;code&amp;gt;cmdarg()&amp;lt;/code&amp;gt; using {{func link|maketimer()}}, {{func link|settimer()}} or {{func link|setlistener()}}, because it will return an unrelated property.&lt;br /&gt;
|example1 = fgcommand(&amp;quot;dialog-show&amp;quot;, {&amp;quot;dialog-name&amp;quot;: &amp;quot;cmdarg-demo&amp;quot;});&lt;br /&gt;
|example1text = &amp;lt;br&amp;gt;This example demonstrates the usage of &amp;lt;code&amp;gt;cmdarg()&amp;lt;/code&amp;gt; in a binding.  Save the below XML snippet as &amp;lt;tt&amp;gt;[[$FG_ROOT]]/gui/dialogs/cmdarg-demo.xml&amp;lt;/tt&amp;gt;. Then run the Nasal snippet below in your [[Nasal Console]]. Upon clicking {{button|Close}}, a message will be printed sowing the root of the binding in the Property Tree.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;PropertyList&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;name&amp;gt;cmdarg-demo&amp;lt;/name&amp;gt;&lt;br /&gt;
&amp;lt;layout&amp;gt;vbox&amp;lt;/layout&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;text&amp;gt;&lt;br /&gt;
  &amp;lt;label&amp;gt;Click &amp;quot;Close&amp;quot; to activate the demonstration (a message in the console).&amp;lt;/label&amp;gt;&lt;br /&gt;
&amp;lt;/text&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;button&amp;gt;&lt;br /&gt;
  &amp;lt;legend&amp;gt;Close&amp;lt;/legend&amp;gt;&lt;br /&gt;
  &amp;lt;binding&amp;gt;&lt;br /&gt;
    &amp;lt;command&amp;gt;nasal&amp;lt;/command&amp;gt;&lt;br /&gt;
    &amp;lt;script&amp;gt;print(&amp;quot;Button binding root: '&amp;quot; ~ cmdarg().getPath() ~ &amp;quot;'&amp;quot;);&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;/binding&amp;gt;&lt;br /&gt;
  &amp;lt;binding&amp;gt;&lt;br /&gt;
    &amp;lt;command&amp;gt;dialog-close&amp;lt;/command&amp;gt;&lt;br /&gt;
  &amp;lt;/binding&amp;gt;&lt;br /&gt;
&amp;lt;/button&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/PropertyList&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|example2text = This example demonstrates the usage of &amp;lt;code&amp;gt;cmdarg()&amp;lt;/code&amp;gt; in Nasal code within dialogs.  Open &amp;lt;tt&amp;gt;[[$FG_ROOT]]/gui/dialogs/cmdarg-demo.xml&amp;lt;/tt&amp;gt; from the previous example, copy &amp;amp; paste the code below, and save it. Then run the same Nasal snippet as the previous example in your Nasal Console. If you click {{button|Click me!}}, the button's label will change to &amp;quot;I've been changed!&amp;quot;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;PropertyList&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;name&amp;gt;cmdarg-demo&amp;lt;/name&amp;gt;&lt;br /&gt;
&amp;lt;layout&amp;gt;vbox&amp;lt;/layout&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;text&amp;gt;&lt;br /&gt;
  &amp;lt;label&amp;gt;Click &amp;quot;Click me!&amp;quot; to activate the demonstration (the button's label will change).&amp;lt;/label&amp;gt;&lt;br /&gt;
&amp;lt;/text&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;button&amp;gt;&lt;br /&gt;
  &amp;lt;legend&amp;gt;Click me!&amp;lt;/legend&amp;gt;&lt;br /&gt;
  &amp;lt;binding&amp;gt;&lt;br /&gt;
    &amp;lt;command&amp;gt;nasal&amp;lt;/command&amp;gt;&lt;br /&gt;
    &amp;lt;script&amp;gt;change_label();&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;/binding&amp;gt;&lt;br /&gt;
&amp;lt;/button&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;button&amp;gt;&lt;br /&gt;
  &amp;lt;legend&amp;gt;Close&amp;lt;/legend&amp;gt;&lt;br /&gt;
  &amp;lt;binding&amp;gt;&lt;br /&gt;
    &amp;lt;command&amp;gt;dialog-close&amp;lt;/command&amp;gt;&lt;br /&gt;
  &amp;lt;/binding&amp;gt;&lt;br /&gt;
&amp;lt;/button&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nasal&amp;gt;&lt;br /&gt;
  &amp;lt;open&amp;gt;&amp;lt;![CDATA[&lt;br /&gt;
    var dlg_root = cmdarg();&lt;br /&gt;
    var dlg_name = {&amp;quot;dialog-name&amp;quot;: &amp;quot;cmdarg-demo&amp;quot;};&lt;br /&gt;
    var change_label = func {&lt;br /&gt;
        dlg_root.getNode(&amp;quot;button[0]/legend&amp;quot;).setValue(&amp;quot;I've been changed!&amp;quot;);&lt;br /&gt;
        fgcommand(&amp;quot;dialog-close&amp;quot;, dlg_name);&lt;br /&gt;
        fgcommand(&amp;quot;dialog-show&amp;quot;, dlg_name);&lt;br /&gt;
    }&lt;br /&gt;
  ]]&amp;gt;&amp;lt;/open&amp;gt;&lt;br /&gt;
&amp;lt;/nasal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/PropertyList&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|example3text = For an example of &amp;lt;code&amp;gt;cmdarg()&amp;lt;/code&amp;gt; used with Canvas, please see [[Howto:Adding a canvas to a GUI dialog#FGPlot|Howto:Adding a canvas to a GUI dialog]].&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===courseAndDistance()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = courseAndDistance(to);&lt;br /&gt;
courseAndDistance(from, to);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1668|t=Source}}&lt;br /&gt;
|text = Returns a vector containing the course from one point to another and the distance between them in nautical miles. The course is the initial bearing (see [http://www.movable-type.co.uk/scripts/latlong.html#bearing here]), and is in the range 0–360. Both arguments can be one of:&lt;br /&gt;
:* An &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;runway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;taxiway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fix&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;waypoint&amp;lt;/code&amp;gt; ghost type&lt;br /&gt;
:* A hash with ''lat'' and ''lon'' members&lt;br /&gt;
:* A geo.Coord object with geodetic coordinates (cartesian coordinates will not be accepted)&lt;br /&gt;
|param1 = from&lt;br /&gt;
|param1text = Optional parameter defining the from where the function should calculate its results. If the function is given one argument ('''to'''), the aircraft's current position will be used. As well as the argument types as defined above, this argument can be two numbers separated with a comma, as if the function is taking three arguments. See example 5 below.&lt;br /&gt;
|param2 = to&lt;br /&gt;
|param2text = Like the first parameter, but defines the second point.&lt;br /&gt;
|example1text = This example demonstrates the usage of the function with the &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt; ghost type.&lt;br /&gt;
|example1 = var from = airportinfo(&amp;quot;KSFO&amp;quot;);&lt;br /&gt;
var to = airportinfo(&amp;quot;KSQL&amp;quot;);&lt;br /&gt;
var (course, dist) = courseAndDistance(from, to);&lt;br /&gt;
print(course); # prints course from KSFO to KSQL&lt;br /&gt;
print(dist); # prints distance in nm from KSFO to KSQL&lt;br /&gt;
|example2text = This example demonstrates the usage of the function with hashes containing ''lat'' and ''lon''.&lt;br /&gt;
|example2 = var from = {lat: 0, lon: 0};&lt;br /&gt;
var to = {lat: 1, lon: 1};&lt;br /&gt;
var (course, dist) = courseAndDistance(from, to);&lt;br /&gt;
print(course);&lt;br /&gt;
print(dist);&lt;br /&gt;
|example3text = This example demonstrates usage of a geo.Coord object.&lt;br /&gt;
|example3 = var from = geo.Coord.new().set_latlon(0, 0);&lt;br /&gt;
var to = geo.Coord.new().set_latlon(1, 1);&lt;br /&gt;
var (course, dist) = courseAndDistance(from, to);&lt;br /&gt;
print(course);&lt;br /&gt;
print(dist);&lt;br /&gt;
|example4text = This example demonstrates usage of differing parameter types.&lt;br /&gt;
|example4 = var from = airportinfo(&amp;quot;KSFO&amp;quot;);&lt;br /&gt;
var to = geo.Coord.new().set_latlon(0, 0);&lt;br /&gt;
var (course, dist) = courseAndDistance(from, to);&lt;br /&gt;
print(course);&lt;br /&gt;
print(dist);&lt;br /&gt;
|example5text = The same as above, but the other way round.&lt;br /&gt;
|example5 = var to = {lat: 1, lon: 1};&lt;br /&gt;
var (course, dist) = courseAndDistance(0, 0, to);&lt;br /&gt;
print(course);&lt;br /&gt;
print(dist);&lt;br /&gt;
|example6text = Usage of just one parameter.&lt;br /&gt;
|example6 = var dest = airportinfo(&amp;quot;KSQL&amp;quot;);&lt;br /&gt;
var (course, dist) = courseAndDistance(dest);&lt;br /&gt;
print(&amp;quot;Turn to heading &amp;quot;, math.round(course), &amp;quot;. You have &amp;quot;, sprintf(&amp;quot;%.2f&amp;quot;, dist), &amp;quot; nm to go&amp;quot;);&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===createFlightplan()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = createFlightplan(path);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=2331|t=Source}}&lt;br /&gt;
|text = Creates an empty flightplan object. It accepts one argument, ''path'' passed an absolute path to a .fgfp / .gpx file, it will populate the flightplan with waypoints from the file.&lt;br /&gt;
|param1 = path&lt;br /&gt;
|param1text = Optional parameter defining the file from which a flightplan will be populated.&lt;br /&gt;
|example1 = &lt;br /&gt;
var path = getprop(&amp;quot;/sim/fg-home&amp;quot;) ~ &amp;quot;/Export/test.fgfp&amp;quot;;&lt;br /&gt;
var flightplan = createFlightplan(path);&lt;br /&gt;
debug.dump(flightplan);&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===createDiscontinuity()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = createDiscontinuity();&lt;br /&gt;
|text = Returns a &amp;lt;code&amp;gt;waypoint&amp;lt;/code&amp;gt; ghost object. A route discontinuity is inserted by an {{abbr|FMS|Flight Management System}} when it is unsure how to connect two waypoints.&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=2045|t=Source}}&lt;br /&gt;
|version = 2016.1&lt;br /&gt;
|commit = {{flightgear commit|caead6|t=commit}}&lt;br /&gt;
}}&lt;br /&gt;
===createViaTo()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = createViaTo(airway, waypoint);&lt;br /&gt;
|text = Returns a &amp;lt;code&amp;gt;waypoint&amp;lt;/code&amp;gt; ghost object. It represents a route &amp;quot;via '''airway''' to '''waypoint'''&amp;quot;.&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=2009|t=Source}}&lt;br /&gt;
|version = 2016.1&lt;br /&gt;
|commit = {{flightgear commit|caead6|t=commit}}&lt;br /&gt;
|param1 = airway&lt;br /&gt;
|param1text = The name of an airway.&lt;br /&gt;
|param2 = waypoint&lt;br /&gt;
|param2text = Must be in the airway and one of:&lt;br /&gt;
* The name of a waypoint.&lt;br /&gt;
* An &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;runway&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;fix&amp;lt;/code&amp;gt; ghost object.&lt;br /&gt;
}}&lt;br /&gt;
===createWP()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = createWP(pos, name[, flag]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1964|t=Source}}&lt;br /&gt;
|text = Creates a new waypoint ghost object.&lt;br /&gt;
|param1 = pos&lt;br /&gt;
|param1text = Dictates the position of the new waypoint. It can be one of the following:&lt;br /&gt;
:* An &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;runway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;taxiway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fix&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;waypoint&amp;lt;/code&amp;gt; ghost type&lt;br /&gt;
:* A hash with ''lat'' and ''lon'' members&lt;br /&gt;
:* A geo.Coord object&lt;br /&gt;
:* Two numbers separated by a comma, as if the function is taking three arguments. See example 4 below.&lt;br /&gt;
|param2 = name&lt;br /&gt;
|param2text = String that will become the name of the new waypoint.&lt;br /&gt;
|param3 = flag&lt;br /&gt;
|param3text = Optional string that will tell FlightGear what type of waypoint it is. Must be one of &amp;quot;sid,&amp;quot; &amp;quot;star,&amp;quot; &amp;quot;approach,&amp;quot; &amp;quot;missed,&amp;quot; or &amp;quot;pseudo.&amp;quot;&lt;br /&gt;
|example1text = Creates a waypoint directly in front and 1 km away and appends it to the flight plan.&lt;br /&gt;
|example1 = var pos = geo.aircraft_position().apply_course_distance(getprop(&amp;quot;/orientation/heading-deg&amp;quot;), 1000);&lt;br /&gt;
var wp = createWP(pos, &amp;quot;NEWWP&amp;quot;);&lt;br /&gt;
var fp = flightplan();&lt;br /&gt;
fp.appendWP(wp);&lt;br /&gt;
|example2 = var pos = geo.aircraft_position().apply_course_distance(getprop(&amp;quot;/orientation/heading-deg&amp;quot;), 1000);&lt;br /&gt;
var wp = createWP({lat: pos.lat(), lon: pos.lon()}, &amp;quot;NEWWP&amp;quot;);&lt;br /&gt;
var fp = flightplan();&lt;br /&gt;
fp.appendWP(wp);&lt;br /&gt;
|example3 = var apt = airportinfo();&lt;br /&gt;
var wp = createWP(apt, &amp;quot;NEWWP&amp;quot;);&lt;br /&gt;
var fp = flightplan();&lt;br /&gt;
fp.appendWP(wp);&lt;br /&gt;
|example4 = var pos = geo.aircraft_position().apply_course_distance(getprop(&amp;quot;/orientation/heading-deg&amp;quot;), 1000);&lt;br /&gt;
var wp = createWP(pos.lat(), pos.lon(), &amp;quot;NEWWP&amp;quot;);&lt;br /&gt;
var fp = flightplan();&lt;br /&gt;
fp.appendWP(wp);&lt;br /&gt;
|example5text = Creates a new waypoint and adds it to the flight plan. Waypoints of the type &amp;quot;pseudo&amp;quot; are then removed from the flight plan, including the new waypoint. The {{func link|print()}} statements show this.&lt;br /&gt;
|example5 = var pos = geo.aircraft_position().apply_course_distance(getprop(&amp;quot;/orientation/heading-deg&amp;quot;), 1000);&lt;br /&gt;
var wp = createWP(pos, &amp;quot;NEWWP&amp;quot;, &amp;quot;pseudo&amp;quot;);&lt;br /&gt;
var fp = flightplan();&lt;br /&gt;
fp.appendWP(wp);&lt;br /&gt;
print(fp.getPlanSize());&lt;br /&gt;
fp.clearWPType(&amp;quot;pseudo&amp;quot;);&lt;br /&gt;
print(fp.getPlanSize());&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===createWPFrom()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = createWPFrom(object[, flag]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1989|t=Source}}&lt;br /&gt;
|text = Creates a new waypoint object from another object.&lt;br /&gt;
|param1 = object&lt;br /&gt;
|param1text = A ghost object. Must be a ghost type that is one of &amp;quot;airport,&amp;quot; &amp;quot;navaid,&amp;quot; &amp;quot;runway,&amp;quot; or &amp;quot;fix.&amp;quot;&lt;br /&gt;
|param2 = flag&lt;br /&gt;
|param2text = Optional string that will tell FlightGear what type of waypoint it is. Must be one of &amp;quot;sid,&amp;quot; &amp;quot;star,&amp;quot; &amp;quot;approach,&amp;quot; &amp;quot;missed,&amp;quot; or &amp;quot;pseudo.&amp;quot;&lt;br /&gt;
|example1text = Creates a new waypoint and appends it to the flight plan.&lt;br /&gt;
|example1 = var apt = airportinfo(&amp;quot;KSFO&amp;quot;);&lt;br /&gt;
var wp = createWPFrom(apt);&lt;br /&gt;
var fp = flightplan();&lt;br /&gt;
fp.appendWP(wp);&lt;br /&gt;
|example2text = Creates a new waypoint and appends it to the flight plan. This way point is then removed; the {{func link|print()}} statements prove this.&lt;br /&gt;
|example2 = var apt = airportinfo(&amp;quot;KSFO&amp;quot;);&lt;br /&gt;
var wp = createWPFrom(apt, &amp;quot;pseudo&amp;quot;);&lt;br /&gt;
print(wp.wp_name);&lt;br /&gt;
var fp = flightplan();&lt;br /&gt;
fp.appendWP(wp);&lt;br /&gt;
print(fp.getPlanSize());&lt;br /&gt;
fp.clearWPType(&amp;quot;pseudo&amp;quot;);&lt;br /&gt;
print(fp.getPlanSize());&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===defined()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = defined(symbol);&lt;br /&gt;
|source = {{fgdata file|Nasal/globals.nas|t=Source}}&lt;br /&gt;
|text = Returns 1 (true) or 0 (false) depending on whether a variable exists.&lt;br /&gt;
|param1 = symbol&lt;br /&gt;
|param1text = A string that will be what the function searches for.&lt;br /&gt;
|example1 = var number = 12;&lt;br /&gt;
var check_exist = func {&lt;br /&gt;
    print(&amp;quot;Variable 'number' &amp;quot;, defined(&amp;quot;number&amp;quot;) == 1 ? &amp;quot;exists&amp;quot; : &amp;quot;does not exist&amp;quot;); # 'number' does exist&lt;br /&gt;
    print(&amp;quot;Variable 'number2' &amp;quot;, defined(&amp;quot;number2&amp;quot;) == 1 ? &amp;quot;exists&amp;quot; : &amp;quot;does not exist&amp;quot;); # 'number2' does not exist&lt;br /&gt;
}&lt;br /&gt;
check_exist();&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===directory()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = directory(path);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=572|t=Source}}&lt;br /&gt;
|text = Returns a vector containing a list of the folders and files in a given file path or &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; if the path doesn't exist. Hidden folders and files are not added to the vector.&lt;br /&gt;
{{inote|The first two elements of the vector will be &amp;lt;code&amp;gt;'.'&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;'..'&amp;lt;/code&amp;gt;. These are for navigating back up the file tree, but have no use in Nasal. They can be safely removed from the vector.}}&lt;br /&gt;
|param1 = path&lt;br /&gt;
|param1text = Absolute file path.&lt;br /&gt;
|example1text = Gets the folders and files in [[$FG_ROOT]], and then removes the extra first two elements (see note above). &lt;br /&gt;
|example1 = var dir = directory(getprop(&amp;quot;/sim/fg-root&amp;quot;)); # get directory&lt;br /&gt;
dir = subvec(dir, 2); # strips off the first two elements&lt;br /&gt;
debug.dump(dir); # dump the vector&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===fgcommand()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = fgcommand(cmd[, args]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=456|t=Part 1}} {{!}} {{fgdata file|Nasal/globals.nas|t=Part 2}}&lt;br /&gt;
|text = Runs an fgcommand. See also {{readme file|commands}} and [[Bindings]] for more information. See {{flightgear file|src/Main/fg_commands.cxx|l=1425}} for the full list of fgcommands. Note that fgcommands generated by {{func link|addcommand()}} can also be run using this function. Also, the full list of fgcommands depends on the version of FlightGear you have. Returns 1 (true) if the fgcommand succeeded or 0 (false) if it failed.&lt;br /&gt;
|param1 = cmd&lt;br /&gt;
|param1text = String that is the name of the command that is to be run.&lt;br /&gt;
|param2 = args&lt;br /&gt;
|param2text = If the fgcommand takes arguments, they are inputted using this argument. Can either be a &amp;lt;code&amp;gt;props.Node&amp;lt;/code&amp;gt; object, or a hash (see examples below).&lt;br /&gt;
|example1 = fgcommand(&amp;quot;null&amp;quot;); # does nothing&lt;br /&gt;
|example2 = var args = props.Node.new({'script': 'print(&amp;quot;Running fgcommand&amp;quot;);'});&lt;br /&gt;
if (fgcommand(&amp;quot;nasal&amp;quot;, args)) { # prints &amp;quot;Running fgcommand&amp;quot; and then one of these print statements&lt;br /&gt;
    print(&amp;quot;Fgcommand succeeded&amp;quot;);&lt;br /&gt;
} else {&lt;br /&gt;
    print(&amp;quot;Fgcommand encountered a problem&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
|example3 = var args = { 'dialog-name': 'about' };&lt;br /&gt;
fgcommand(&amp;quot;dialog-show&amp;quot;, args); # shows the 'about' dialog&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===findAirportsByICAO() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = findAirportsByICAO(search[, type]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1096|t=Source}}&lt;br /&gt;
|text = Returns a vector containing &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt; ghost objects which are (by default) airports whose ICAO code matches the search string. The results are sorted by range from closest to furthest.&lt;br /&gt;
|param1 = search&lt;br /&gt;
|param1text = Search string for the function. Can either be a partial or a full ICAO code.&lt;br /&gt;
:{{icaution|The more matches there are for the given code, the longer the function will take. Passing just one character (e.g., &amp;quot;K&amp;quot;), might make FlightGear hang for a certain amount of time.}}&lt;br /&gt;
|param2 = type&lt;br /&gt;
|param2text = This will narrow the search to airports of a certain type. By default, only airports are searched for. May be one of &amp;quot;airport,&amp;quot; &amp;quot;heliport,&amp;quot; or &amp;quot;seaport.&amp;quot;&lt;br /&gt;
|example1 = var apts = findAirportsByICAO(&amp;quot;KSF&amp;quot;); # finds all airports matching &amp;quot;KSF&amp;quot;&lt;br /&gt;
foreach(var apt; apts){&lt;br /&gt;
    print(apt.name, &amp;quot; (&amp;quot;, apt.id, &amp;quot;)&amp;quot;); # prints them&lt;br /&gt;
}&lt;br /&gt;
|example2 = var apts = findAirportsByICAO(&amp;quot;SP0&amp;quot;, &amp;quot;seaport&amp;quot;); # finds all seaplane bases matching &amp;quot;SP0&amp;quot;&lt;br /&gt;
foreach(var apt; apts){&lt;br /&gt;
    print(apt.name, &amp;quot; (&amp;quot;, apt.id, &amp;quot;)&amp;quot;); # prints them&lt;br /&gt;
}&lt;br /&gt;
|example3 = var apt = findAirportsByICAO(&amp;quot;XBET&amp;quot;); # one way to check if an airport does exist&amp;quot;&lt;br /&gt;
if (size(apt) == 0) {&lt;br /&gt;
    print(&amp;quot;Airport does not exist&amp;quot;); # this one will be printed&lt;br /&gt;
} else {&lt;br /&gt;
    print(&amp;quot;Airport does exist&amp;quot;);&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===findAirportsWithinRange()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = findAirportsWithinRange([pos, ]range[, type]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1066|t=Source}}&lt;br /&gt;
|text = Returns a vector of &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt; ghost object which are (by default) airports that are within a given range of a given position, or the aircraft's current position. The results are sorted by range from closest to furthest.&lt;br /&gt;
|param1 = pos&lt;br /&gt;
|param1text = Optional position to search around. If not given, the aircraft's current position will be used. Can be one of:&lt;br /&gt;
:* An &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;runway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;taxiway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fix&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;waypoint&amp;lt;/code&amp;gt; ghost type&lt;br /&gt;
:* A hash with ''lat'' and ''lon'' members&lt;br /&gt;
:* A geo.Coord object&lt;br /&gt;
:* Two numbers separated by a comma, as if the function is taking three arguments. Example: &amp;lt;code&amp;gt;findAirportsWithinRange(lat, lon, range, type);&amp;lt;/code&amp;gt;.&lt;br /&gt;
|param2 = range&lt;br /&gt;
|param2text = Mandatory number giving the range in nautical miles within which to search for airports/heliports/seaplane bases.only airports are searched for.&lt;br /&gt;
|param3 = type&lt;br /&gt;
|param3text = This will narrow the search to airports of a certain type. By default, only airports are searched for. May be one of &amp;quot;airport,&amp;quot; &amp;quot;heliport,&amp;quot; or &amp;quot;seaport.&amp;quot;&lt;br /&gt;
|example1text = Searches for airports within 10 nm of [[KSFO]].&lt;br /&gt;
|example1 = var pos = airportinfo(&amp;quot;KSFO&amp;quot;);&lt;br /&gt;
var apts = findAirportsWithinRange(pos, 10);&lt;br /&gt;
foreach(var apt; apts){&lt;br /&gt;
    print(apt.name, &amp;quot; (&amp;quot;, apt.id, &amp;quot;)&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
|example2text = Searches for seaplane bases within 15 nm of [[KSFO]].&lt;br /&gt;
|example2 = var pos = airportinfo(&amp;quot;KSFO&amp;quot;);&lt;br /&gt;
var apts = findAirportsWithinRange(pos, 15, &amp;quot;seaport&amp;quot;);&lt;br /&gt;
foreach(var apt; apts){&lt;br /&gt;
    print(apt.name, &amp;quot; (&amp;quot;, apt.id, &amp;quot;)&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
|example3text = Searches for airports within 10 nm of your current position.&lt;br /&gt;
|example3 = var apts = findAirportsWithinRange(10);&lt;br /&gt;
foreach(var apt; apts){&lt;br /&gt;
    print(apt.name, &amp;quot; (&amp;quot;, apt.id, &amp;quot;)&amp;quot;);&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===findFixesByID()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = findFixesByID([pos, ]id);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1627|t=Source}}&lt;br /&gt;
|text = Returns a vector containing &amp;lt;code&amp;gt;fix&amp;lt;/code&amp;gt; ghost objects matching a given ID, sorted by range from a certain position.&lt;br /&gt;
{{inote|Fixes are (usually) also known as waypoints.}}&lt;br /&gt;
|param1 = pos&lt;br /&gt;
|param1text = Optional position to search around. If not given, the aircraft's current position will be used. Can be one of:&lt;br /&gt;
:* An &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;runway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;taxiway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fix&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;waypoint&amp;lt;/code&amp;gt; ghost type&lt;br /&gt;
:* A hash with ''lat'' and ''lon'' members&lt;br /&gt;
:* A geo.Coord object&lt;br /&gt;
:* Two numbers separated by a comma, as if the function is taking three arguments. Example: &amp;lt;code&amp;gt;findFixesByID(lat, lon, id);&amp;lt;/code&amp;gt;.&lt;br /&gt;
|param2 = id&lt;br /&gt;
|param2text = Full or partial ID of the fix to search for.&lt;br /&gt;
:{{inote|1=Inputting a partial ID does not work correctly (see [http://forum.flightgear.org/viewtopic.php?f=30&amp;amp;t=28129 here]). It is best to just input a full ID.}}&lt;br /&gt;
|example1 = var fixes = findFixesByID(&amp;quot;POGIC&amp;quot;);&lt;br /&gt;
foreach(var fix; fixes){&lt;br /&gt;
    print(fix.id, &amp;quot; - lat: &amp;quot;, fix.lat, &amp;quot; {{!}} lon: &amp;quot;, fix.lon); # prints information about POGIC&lt;br /&gt;
}&lt;br /&gt;
|example2 = var fix = findFixesByID(&amp;quot;ZUNAP&amp;quot;);&lt;br /&gt;
fix = fix[0];&lt;br /&gt;
var (course, dist) = courseAndDistance(fix);&lt;br /&gt;
print(&amp;quot;Turn to heading &amp;quot;, math.round(course), &amp;quot;. You have &amp;quot;, sprintf(&amp;quot;%.2f&amp;quot;, dist), &amp;quot; nm to go to reach &amp;quot;, fixes[0].id);&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===findNavaidByFrequencyMHz()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = findNavaidByFrequencyMHz([pos, ]freq[, type]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1547|t=Source}}&lt;br /&gt;
|text = Returns a &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt; ghost object for a navaid matching a given frequency. If there is more than one navaid with that frequency, the nearest station is returned.&lt;br /&gt;
|param1 = pos&lt;br /&gt;
|param1text = Optional position to search around. If not given, the aircraft's current position will be used. Can be one of:&lt;br /&gt;
:* An &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;runway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;taxiway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fix&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;waypoint&amp;lt;/code&amp;gt; ghost type&lt;br /&gt;
:* A hash with ''lat'' and ''lon'' members&lt;br /&gt;
:* A geo.Coord object&lt;br /&gt;
:* Two numbers separated by a comma, as if the function is taking three arguments. Example: &amp;lt;code&amp;gt;findNavaidByFrequencyMHz(lat, lon, freq, type);&amp;lt;/code&amp;gt;.&lt;br /&gt;
|param2 = freq&lt;br /&gt;
|param2text = Frequency, in megahertz, of the navaid to search for.&lt;br /&gt;
|param3 = type&lt;br /&gt;
|param3text = This will narrow the search to navaids of a certain type. Defaults to &amp;quot;all.&amp;quot; For the full list of accepted type arguments, see {{flightgear file|src/Navaids/positioned.cxx|l=127}}.&lt;br /&gt;
|example1 = var navaid = findNavaidByFrequencyMHz(109.55);&lt;br /&gt;
print(&amp;quot;ID: &amp;quot;, navaid.id); # prints info about the navaid&lt;br /&gt;
print(&amp;quot;Name: &amp;quot;, navaid.name);&lt;br /&gt;
print(&amp;quot;Latitude: &amp;quot;, navaid.lat);&lt;br /&gt;
print(&amp;quot;Longitude: &amp;quot;, navaid.lon);&lt;br /&gt;
print(&amp;quot;Elevation (AMSL): &amp;quot;, navaid.elevation, &amp;quot; m&amp;quot;);&lt;br /&gt;
print(&amp;quot;Type: &amp;quot;, navaid.type);&lt;br /&gt;
print(&amp;quot;Frequency: &amp;quot;, sprintf(&amp;quot;%.3f&amp;quot;, navaid.frequency / 100), &amp;quot; Mhz&amp;quot;);&lt;br /&gt;
print(&amp;quot;Range: &amp;quot;, navaid.range_nm, &amp;quot; nm&amp;quot;);&lt;br /&gt;
if(navaid.course) print(&amp;quot;Course: &amp;quot;, navaid.course);&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== findNavaidsByFrequencyMHz()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = findNavaidsByFrequencyMHz([pos, ]freq[, type]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1572|t=Source}}&lt;br /&gt;
|text = Returns a vector conatining &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt; ghost objects for navaids that match a given frequency, sorted from nearest to furthest.&lt;br /&gt;
|param1 = pos&lt;br /&gt;
|param1text = Optional position to search around. If not given, the aircraft's current position will be used. Can be one of:&lt;br /&gt;
:* An &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;runway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;taxiway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fix&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;waypoint&amp;lt;/code&amp;gt; ghost type&lt;br /&gt;
:* A hash with ''lat'' and ''lon'' members&lt;br /&gt;
:* A geo.Coord object&lt;br /&gt;
:* Two numbers separated by a comma, as if the function is taking three arguments. Example: &amp;lt;code&amp;gt;findNavaidsByFrequencyMHz(lat, lon, freq, type);&amp;lt;/code&amp;gt;.&lt;br /&gt;
|param2 = freq&lt;br /&gt;
|param2text = Frequency, in megahertz, of the navaid to search for.&lt;br /&gt;
|param3 = type&lt;br /&gt;
|param3text = This will narrow the search to navaids of a certain type. Defaults to &amp;quot;all.&amp;quot; For the full list of accepted type arguments, see {{flightgear file|src/Navaids/positioned.cxx|l=127}}.&lt;br /&gt;
|example1 = var navaids = findNavaidsByFrequencyMHz(109.55);&lt;br /&gt;
foreach(var navaid; navaids){&lt;br /&gt;
    print(&amp;quot;--&amp;quot;);&lt;br /&gt;
    print(&amp;quot;ID: &amp;quot;, navaid.id); # prints info about the navaid&lt;br /&gt;
    print(&amp;quot;Name: &amp;quot;, navaid.name);&lt;br /&gt;
    print(&amp;quot;Latitude: &amp;quot;, navaid.lat);&lt;br /&gt;
    print(&amp;quot;Longitude: &amp;quot;, navaid.lon);&lt;br /&gt;
    print(&amp;quot;Elevation (AMSL): &amp;quot;, navaid.elevation, &amp;quot; m&amp;quot;);&lt;br /&gt;
    print(&amp;quot;Type: &amp;quot;, navaid.type);&lt;br /&gt;
    print(&amp;quot;Frequency: &amp;quot;, sprintf(&amp;quot;%.3f&amp;quot;, navaid.frequency / 100), &amp;quot; Mhz&amp;quot;);&lt;br /&gt;
    print(&amp;quot;Range: &amp;quot;, navaid.range_nm, &amp;quot; nm&amp;quot;);&lt;br /&gt;
    if(navaid.course) print(&amp;quot;Course: &amp;quot;, navaid.course);&lt;br /&gt;
    print(&amp;quot;--&amp;quot;);&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===findNavaidsByID()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = findNavaidsByID([pos, ]id[, type]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1600|t=Source}}&lt;br /&gt;
|text = Returns a vector containing &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt; ghost objects matching a given ID, sorted by range from a certain position.&lt;br /&gt;
|param1 = pos&lt;br /&gt;
|param1text = Optional position to search around. If not given, the aircraft's current position will be used. Can be one of:&lt;br /&gt;
:* An &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;runway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;taxiway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fix&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;waypoint&amp;lt;/code&amp;gt; ghost type&lt;br /&gt;
:* A hash with ''lat'' and ''lon'' members&lt;br /&gt;
:* A geo.Coord object&lt;br /&gt;
:* Two numbers separated by a comma, as if the function is taking three arguments. Example: &amp;lt;code&amp;gt;findNavaidsByID(lat, lon, id, type);&amp;lt;/code&amp;gt;.&lt;br /&gt;
|param2 = id&lt;br /&gt;
|param2text = Full or partial ID of the fix to search for.&lt;br /&gt;
:{{inote|1=Inputting a partial ID does not work correctly (see [http://forum.flightgear.org/viewtopic.php?f=30&amp;amp;t=28129 here]). It is best to just input a full ID.}}&lt;br /&gt;
|param3 = type&lt;br /&gt;
|param3text = This will narrow the search to navaids of a certain type. Defaults to &amp;quot;all.&amp;quot; For the full list of accepted type arguments, see {{flightgear file|src/Navaids/positioned.cxx|l=127}}.&lt;br /&gt;
|example1 = var navaid = findNavaidsByID(&amp;quot;MXW&amp;quot;);&lt;br /&gt;
navaid = navaid[0];&lt;br /&gt;
print(&amp;quot;ID: &amp;quot;, navaid.id); # prints info about 'MXW' (a VOR station)&lt;br /&gt;
print(&amp;quot;Name: &amp;quot;, navaid.name);&lt;br /&gt;
print(&amp;quot;Latitude: &amp;quot;, navaid.lat);&lt;br /&gt;
print(&amp;quot;Longitude: &amp;quot;, navaid.lon);&lt;br /&gt;
print(&amp;quot;Elevation (AMSL): &amp;quot;, navaid.elevation, &amp;quot; m&amp;quot;);&lt;br /&gt;
print(&amp;quot;Type: &amp;quot;, navaid.type);&lt;br /&gt;
print(&amp;quot;Frequency: &amp;quot;, sprintf(&amp;quot;%.3f&amp;quot;, navaid.frequency / 1000), &amp;quot; Mhz&amp;quot;);&lt;br /&gt;
print(&amp;quot;Range: &amp;quot;, navaid.range_nm, &amp;quot; nm&amp;quot;);&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===findNavaidsWithinRange()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = findNavaidsWithinRange([pos, ]range[, type]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1518|t=Source}}&lt;br /&gt;
|text = Returns a vector of &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt; ghost objects which are within a given range of a given position (by default the aircraft's current position). The results are sorted from closest to furthest.&lt;br /&gt;
|param1 = pos&lt;br /&gt;
|param1text = Optional position to search around. If not given, the aircraft's current position will be used. Can be one of:&lt;br /&gt;
:* An &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;runway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;taxiway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fix&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;waypoint&amp;lt;/code&amp;gt; ghost type&lt;br /&gt;
:* A hash with ''lat'' and ''lon'' members&lt;br /&gt;
:* A geo.Coord object&lt;br /&gt;
:* Two numbers separated by a comma, as if the function is taking three arguments. Example: &amp;lt;code&amp;gt;findNavaidsWithinRange(lat, lon, range, type);&amp;lt;/code&amp;gt;.&lt;br /&gt;
|param2 = range&lt;br /&gt;
|param2text = Mandatory number giving the range in nautical miles within which to search for navaids. Defaults to &amp;quot;all.&amp;quot; For the full list of accepted type arguments, see {{flightgear file|src/Navaids/positioned.cxx|l=127}}.&lt;br /&gt;
|param3 = type&lt;br /&gt;
|param3text = This will narrow the search to navaids of a certain type.&lt;br /&gt;
|example1text = Searches for navaids within 10 nm of [[KSFO]].&lt;br /&gt;
|example1 = var pos = airportinfo(&amp;quot;KSFO&amp;quot;);&lt;br /&gt;
var navs = findNavaidsWithinRange(pos, 10);&lt;br /&gt;
foreach(var nav; navs){&lt;br /&gt;
    print(nav.name, &amp;quot; (ID: &amp;quot;, nav.id, &amp;quot;)&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
|example2text = Searches for navaids within 10 nm of your current position.&lt;br /&gt;
|example2 = var navs = findNavaidsWithinRange(10);&lt;br /&gt;
foreach(var nav; navs){&lt;br /&gt;
    print(nav.name, &amp;quot; (ID: &amp;quot;, nav.id, &amp;quot;)&amp;quot;);&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===finddata()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = finddata(path);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=603|t=Source}}&lt;br /&gt;
|text = Takes a relative path and tries to return an absolute one. It works by appending the relative path to some paths and testing to see if they exist. As of FlightGear v3.7, these paths are the TerraSync directory (tested first) and [[$FG_ROOT]]. &lt;br /&gt;
|param1 = path&lt;br /&gt;
|param1text = A relative path as a string.&lt;br /&gt;
|example1 = var path = finddata(&amp;quot;Aircraft/Generic&amp;quot;);&lt;br /&gt;
print(path); # prints the absolute path to $FG_ROOT/Aircraft/Generic&lt;br /&gt;
|example2 = var path = finddata(&amp;quot;Airports&amp;quot;);&lt;br /&gt;
print(path); # prints the absolute path to &amp;lt;TerraSync dir&amp;gt;/Airports&lt;br /&gt;
|example3 = var path = finddata(&amp;quot;preferences.xml&amp;quot;);&lt;br /&gt;
print(path); # prints the absolute path to $FG_ROOT/preferences.xml&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===flightplan()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = flightplan([path]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1738|t=Source}}&lt;br /&gt;
|text = {{see also|Nasal Flightplan}}&lt;br /&gt;
Returns a flight plan object, either one for the current flight plan, or one loaded from a given path.&lt;br /&gt;
|param1 = path&lt;br /&gt;
|param1text = Optional path to flight plan XML file.&lt;br /&gt;
|example1text = Gets the active flight plan and gets the ID of the current waypoint. Note that this example requires a flight plan to be set in the [[Route Manager]] first.&lt;br /&gt;
|example1 = var fp = flightplan();&lt;br /&gt;
print(fp.getWP(fp.current).id);&lt;br /&gt;
|example2text = Creates a new flight plan from an XML file and prints the IDs of the waypoints. Note that this example requires a flight plan to have been created and saved as &amp;lt;tt&amp;gt;''[[$FG_HOME]]/fp-demo.xml''&amp;lt;/tt&amp;gt;.&lt;br /&gt;
|example2 = var path = getprop('/sim/fg-home') ~ '/fp-demo.xml';&lt;br /&gt;
var fp = flightplan(path);&lt;br /&gt;
for(var i = 0; i &amp;lt; fp.getPlanSize(); i += 1){&lt;br /&gt;
    print(fp.getWP(i).id);&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===geodinfo()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = geodinfo(lat, lon[, max_alt]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=981|t=Source}}&lt;br /&gt;
|text = Returns a vector containing two entries or &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; if no information could be obtained because the terrain tile wasn't loaded. The first entry in the vector is the elevation (in meters) for the given point, and the second is a hash with information about the assigned material (as defined in &amp;lt;tt&amp;gt;''[[$FG_ROOT]]/Materials''&amp;lt;/tt&amp;gt;), or &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; if there was no material information available (for example, because there is an untextured building at that location). The structure of the hash is as follows (see also {{readme file|materials}}):&lt;br /&gt;
* '''light_coverage:''' The coverage of a single point of light in m&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;.&lt;br /&gt;
* '''bumpiness:''' Normalized bumpiness factor for the material.&lt;br /&gt;
* '''load_resistance:''' The amount of pressure in N/m&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt; the material can withstand without deformation.&lt;br /&gt;
* '''solid:''' 1 (true) or false (0) depending on whether the material is solid or not.&lt;br /&gt;
* '''names:''' Vector of scenery types (usually generated by [[TerraGear]]) that will use this material. &lt;br /&gt;
* '''friction_factor:''' Normalized friction factor of the material.&lt;br /&gt;
* '''rolling_friction:''' The rolling friction coefficient of the material.&lt;br /&gt;
&lt;br /&gt;
Note that this function is a ''very'' CPU-intensive operation, particularly in FlightGear v2.4 and earlier. It is advised to use this function as little as possible.&lt;br /&gt;
|param1 = lat&lt;br /&gt;
|param1text = Latitude, inputted as a number.&lt;br /&gt;
|param2 = lon&lt;br /&gt;
|param2text = Longitude, inputted as a number.&lt;br /&gt;
|param3 = max_alt&lt;br /&gt;
|param3text = The altitude, in metres, from which the function will begin searching for the height of the terrain. Defaults to 10,000 metres. If the terrain is higher than this argument specifies, &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; will be returned.&lt;br /&gt;
|example1text = Dumps information about ground underneath the aircraft.&lt;br /&gt;
|example1 = var pos = geo.aircraft_position();&lt;br /&gt;
var info = geodinfo(pos.lat(), pos.lon());&lt;br /&gt;
debug.dump(info);&lt;br /&gt;
|example2text = Prints whether the ground underneath the aircraft is solid or is water.&lt;br /&gt;
|example2 = var pos = geo.aircraft_position();&lt;br /&gt;
var info = geodinfo(pos.lat(), pos.lon());&lt;br /&gt;
if (info != nil and info[1] != nil) {&lt;br /&gt;
    print(&amp;quot;The ground underneath the aircraft is &amp;quot;, info[1].solid == 1 ? &amp;quot;solid.&amp;quot; : &amp;quot;water.&amp;quot;);&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== geodtocart() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = geodtocart(lat, lon, alt);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=962|t=Source}}&lt;br /&gt;
|text = Converts {{wikipedia|geodetic coordinates}} (latitude, longitude, and altitude) to {{wikipedia|ECEF|Earth-centered, Earth-fixed}} coordinates (x, y and z). A vector is returned containing x, y, and z in metres. The equatorial radius of earth used is that defined by the {{wikipedia|WGS 84}} (6,378,137 metres). All argument are mandatory.&lt;br /&gt;
|param1 = lat&lt;br /&gt;
|param1text = Latitude, in degrees.&lt;br /&gt;
|param2 = lon&lt;br /&gt;
|param2text = Longitude, in degrees.&lt;br /&gt;
|param3 = alt&lt;br /&gt;
|param3text = Altitude, in metres.&lt;br /&gt;
|example1 = var (x, y, z) = geodtocart(0, 0, 0); # point is the intersection of the prime meridian and equator.&lt;br /&gt;
print(&amp;quot;x: &amp;quot;, x); # prints &amp;quot;x: 6378137&amp;quot;&lt;br /&gt;
print(&amp;quot;y: &amp;quot;, y); # prints &amp;quot;y: 0&amp;quot;&lt;br /&gt;
print(&amp;quot;z: &amp;quot;, z); # prints &amp;quot;y: 0&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== get_cart_ground_intersection()===&lt;br /&gt;
Introduced in 2017.2.1, see [[Terrain Detection]].&lt;br /&gt;
&lt;br /&gt;
===getprop()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = &amp;lt;nowiki&amp;gt;getprop(path[, path[, ...]]);&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=345|t=Source}}&lt;br /&gt;
|text = Returns the value of a node in the [[Property Tree]] or &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; if the node does not exist or the value is not a number (NaN).&lt;br /&gt;
|param1 = path&lt;br /&gt;
|param1text = There needs to be at least one argument, but there is no limit to the maximum amount of arguments that can be given. The arguments will be concatenated together to form a property tree path. The arguments must be strings, but in FlightGear v3.2 onwards, there is also support (added by {{flightgear commit|34ed79}}) for numeric arguments as indices. See example 2 below.&lt;br /&gt;
|example1 = print(&amp;quot;You have FlightGear v&amp;quot;, getprop(&amp;quot;/sim/version/flightgear&amp;quot;)); # prints FlightGear version&lt;br /&gt;
|example2text = Note that the example below will only work in FlightGear 3.2 and above.&lt;br /&gt;
|example2 = for(var i = 0; i &amp;lt; 8; i += 1){&lt;br /&gt;
    print(&amp;quot;View #&amp;quot;, i + 1, &amp;quot; is named &amp;quot;, getprop(&amp;quot;/sim/view&amp;quot;, i, &amp;quot;name&amp;quot;));&lt;br /&gt;
}&lt;br /&gt;
|example3text = Same as above, but is supported by all versions of FlightGear.&lt;br /&gt;
|example3 = for(var i = 0; i &amp;lt; 8; i += 1){&lt;br /&gt;
    print(&amp;quot;View #&amp;quot;, i + 1, &amp;quot; is named &amp;quot;, getprop(&amp;quot;/sim/view[&amp;quot; ~ i ~ &amp;quot;]/name&amp;quot;));&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
==== See also====&lt;br /&gt;
{{note| If you have to read/write the same property multiple times (e.g. in an update loop), it is more efficient to use a node object: &lt;br /&gt;
To get a Node rather than its value, use &amp;lt;code&amp;gt;props.globals.getNode()&amp;lt;/code&amp;gt; - see [[Nasal_library/props]]. }}&lt;br /&gt;
&lt;br /&gt;
===greatCircleMove()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = greatCircleMove([pos, ]course, dist);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1681|t=Source}}&lt;br /&gt;
|text = Calculates a new set of geodetic coordinates using inputs of course and distance, either from the aircraft's current position (by default) or from another set of coordinates. Returns a hash containing two members, ''lat'' and ''lon'' (latitude and longitude respectively).&lt;br /&gt;
|param1 = pos&lt;br /&gt;
|param1text = Optional position to calculate from. If not given, the aircraft's current position will be used. Can be one of:&lt;br /&gt;
:* An &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;runway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;taxiway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fix&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;waypoint&amp;lt;/code&amp;gt; ghost object.&lt;br /&gt;
:* A hash with ''lat'' and ''lon'' members&lt;br /&gt;
:* A &amp;lt;code&amp;gt;geo.Coord&amp;lt;/code&amp;gt; object&lt;br /&gt;
:* A lat/lon pair, that is, a pair of numbers (latitude followed by longitude) separated by a comma: &amp;lt;code&amp;gt;greatCircleMove(lat,lon, ...)&amp;lt;/code&amp;gt;.&lt;br /&gt;
|param2 = course&lt;br /&gt;
|param2text = Course to new set of coordinates, in degrees (in the range 0–360).&lt;br /&gt;
|param3 = dist&lt;br /&gt;
|param3text = Distance in nautical miles to the new set of coordinates.&lt;br /&gt;
|example1 = var pos = greatCircleMove(0,0, 0, 1);&lt;br /&gt;
debug.dump(pos); # print hash with coordinates&lt;br /&gt;
|example2 = var fix = findFixesByID(&amp;quot;POGIC&amp;quot;);&lt;br /&gt;
fix = fix[0];&lt;br /&gt;
var pos = greatCircleMove(fix, 45, 10);&lt;br /&gt;
debug.dump(pos); # print hash with coordinates&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===interpolate()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|private = _interpolate()&lt;br /&gt;
|syntax = &amp;lt;nowiki&amp;gt;interpolate(prop, value1, time1[, value2, time2[, ...]]);&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=522|t=Part 1}} {{!}} {{fgdata file|Nasal/globals.nas|t=Part 2}}&lt;br /&gt;
|text = Linearly interpolates a node in the property tree to a given value in a specified time. The value/time pairs will be run one after the other in the order that they are passed to the function. Note that the interpolation will continue even when the simulation is paused.&lt;br /&gt;
|param1 = prop&lt;br /&gt;
|param1text = String or &amp;lt;code&amp;gt;props.Node&amp;lt;/code&amp;gt; object that indicates a node in the property tree to be used.&lt;br /&gt;
|param2 = value''n''&lt;br /&gt;
|param2text = Target value to change the property to in the set amount of time. This should be a number.&lt;br /&gt;
|param3 = time''n''&lt;br /&gt;
|param3text = Time in seconds, that will be taken for the interpolation.&lt;br /&gt;
|example1text = Paste the code below into the Nasal Console and execute. Then, open the Property Browser and look for the property. Finally, run the code again, and watch the value of the property change.&lt;br /&gt;
|example1 = setprop(&amp;quot;/test&amp;quot;, 0); # (re-)set property&lt;br /&gt;
interpolate(&amp;quot;/test&amp;quot;,&lt;br /&gt;
    50, 5, # interpolate to 50 in 5 seconds&lt;br /&gt;
    10, 2, # interpolate to 10 in 2 seconds&lt;br /&gt;
    0, 5); # interpolate to 0 in 5 seconds&lt;br /&gt;
|example2 = # Apply the left brake at 20% per second&lt;br /&gt;
var prop = &amp;quot;controls/gear/brake-left&amp;quot;;&lt;br /&gt;
var dist = 1 - getprop(prop);&lt;br /&gt;
if (dist == 1) {&lt;br /&gt;
    interpolate(prop, 1, dist / 0.2);&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===isa()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = isa(object, class);&lt;br /&gt;
|source = {{fgdata file|Nasal/globals.nas|t=Source}}&lt;br /&gt;
|text = Checks if an object is an instance of, or inherits from, a second object (or class), returning 1 (true) if it is and 0 (false) if otherwise.&lt;br /&gt;
|param1 = object&lt;br /&gt;
|param1text = Object to check.&lt;br /&gt;
|param2 = class&lt;br /&gt;
|param2text = Class/object to check that '''object''' inherits from or is an instance of.&lt;br /&gt;
|example1 = var coord = geo.Coord.new();&lt;br /&gt;
if(isa(coord, geo.Coord)){&lt;br /&gt;
    print(&amp;quot;Variable 'coord' is an instance of class 'geo.Coord'&amp;quot;); # this one will be printed&lt;br /&gt;
} else {&lt;br /&gt;
    print(&amp;quot;Variable 'coord' is not an instance of class 'geo.Coord'&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
|example2 = var coord = geo.Coord.new();&lt;br /&gt;
if(isa(coord, props.Node)){&lt;br /&gt;
    print(&amp;quot;Variable 'coord' is an instance of class 'props.Node'&amp;quot;);&lt;br /&gt;
} else {&lt;br /&gt;
    print(&amp;quot;Variable 'coord' is not an instance of class 'props.Node'&amp;quot;); # this one will be printed&lt;br /&gt;
}&lt;br /&gt;
|example3text = The example below demonstrates checking of inheritance.&lt;br /&gt;
|example3 = var Const = {&lt;br /&gt;
    constant: 2,&lt;br /&gt;
    getConst: func {&lt;br /&gt;
        return me.constant;&lt;br /&gt;
    }&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
var Add = {&lt;br /&gt;
    new: func {&lt;br /&gt;
        return { parents: [Add, Const] };&lt;br /&gt;
    },&lt;br /&gt;
&lt;br /&gt;
    addToConst: func(a){&lt;br /&gt;
        return a * me.getConst();&lt;br /&gt;
    }&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
var m = Add.new();&lt;br /&gt;
print(m.addToConst(4));&lt;br /&gt;
&lt;br /&gt;
if(isa(m, Add)) print(&amp;quot;Variable 'm' is an instance of class 'Add'&amp;quot;); # will be printed&lt;br /&gt;
if(isa(m, Const)) print(&amp;quot;Variable 'm' is an instance of class 'Const'&amp;quot;); # will also be printed&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===logprint()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = &amp;lt;nowiki&amp;gt;logprint(priority[, msg[, msg[, ...]]]);&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=431|t=Source}}&lt;br /&gt;
|text = Concatenates a message and logs it with a given priority level. Unlike {{func link|print()}} and {{func link|printlog()}}, message outputted by this function will be logged in your &amp;lt;code&amp;gt;[[Commonly used debugging tools#fgfs.log|fgfs.log]]&amp;lt;/code&amp;gt; file as coming from the Nasal file itself rather than from {{flightgear file|src/Scripting/NasalSys.cxx}}.&lt;br /&gt;
|param1 = priority&lt;br /&gt;
|param1text = Number specifying the priority level of the outputted message:&lt;br /&gt;
:{{{!}} class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Number !! Debug type&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} 1 {{!!}} Bulk&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} 2 {{!!}} Debug&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} 3 {{!!}} Info&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} 4 {{!!}} Warn&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} 5 {{!!}} Alert&lt;br /&gt;
{{!}}}&lt;br /&gt;
|param2 = msg&lt;br /&gt;
|param2text = The message. There is no limit to the arguments you give give. They will be concatenated together before logging.&lt;br /&gt;
|example1 = # logs the value of pi to three decimal places with log level 3&lt;br /&gt;
logprint(3, &amp;quot;pi = &amp;quot;, sprintf(&amp;quot;%.3f&amp;quot;, math.pi));&lt;br /&gt;
|example2 = logprint(5, &amp;quot;Alert! This is an important message!&amp;quot;);&lt;br /&gt;
}}&lt;br /&gt;
{{note| &lt;br /&gt;
The following constants have been added to the development branch of FlightGear (&amp;quot;next&amp;quot;) and will be releases with FG 2020.1 so you won't have to remember the numbers anymore:&lt;br /&gt;
&lt;br /&gt;
LOG_BULK, LOG_WARN, LOG_DEBUG, LOG_INFO, LOG_ALERT, DEV_WARN, DEV_ALERT }}&lt;br /&gt;
&lt;br /&gt;
===magvar() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = magvar([pos]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1642|t=Source}}&lt;br /&gt;
|text = Returns the {{wikipedia|magnetic variation}} at a given set of coordinates. The table below gives the magnetic model used depending on the version of FlightGear.&lt;br /&gt;
{{{!}} class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! FlightGear versions !! Model !! Reference date&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} 3.6 and above {{!!}} {{wikipedia|World Magnetic Model}} (WMM) 2015 {{!!}} 1 January 2015&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} 0.9.11-pre1 to 3.4 {{!!}} WMM 2005 {{!!}} 1 January 2005&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} 0.7.3 to 0.9.10 {{!!}} WMM 2000 {{!!}} 1 January 2000&lt;br /&gt;
{{!}}}&lt;br /&gt;
|param1 = pos&lt;br /&gt;
|param1text = Optional position to calculate from. If not given, the aircraft's current position will be used. Can be one of:&lt;br /&gt;
:* An &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;runway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;taxiway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fix&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;waypoint&amp;lt;/code&amp;gt; ghost object.&lt;br /&gt;
:* A hash with ''lat'' and ''lon'' members&lt;br /&gt;
:* A &amp;lt;code&amp;gt;geo.Coord&amp;lt;/code&amp;gt; object&lt;br /&gt;
:* A lat/lon pair, that is, a pair of numbers (latitude followed by longitude) separated by a comma: &amp;lt;code&amp;gt;magvar(lat,lon)&amp;lt;/code&amp;gt;.&lt;br /&gt;
|example1 = print(magvar(0, 0)); # prints the magnetic variation at 0, 0&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===maketimer() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = maketimer(interval[, self], function);&lt;br /&gt;
|source = ''Implemented using the {{API Link|flightgear|class|TimerObj}} class.''&amp;lt;br&amp;gt;{{flightgear file|src/Scripting/NasalSys.cxx|l=90|t=Part 1}} {{!}} {{flightgear file|src/Scripting/NasalSys.cxx|l=533|t=Part 2}}&lt;br /&gt;
|version = 2.12&lt;br /&gt;
|commit = {{flightgear commit|ab939f|t=commit}}&lt;br /&gt;
|text = Returns a timer object containing the following methods and members:&lt;br /&gt;
* '''start()''': Starts the timer.&lt;br /&gt;
* '''stop()''': Stops the timer.&lt;br /&gt;
* '''restart(interval)''': Restarts the timer with the given interval.&lt;br /&gt;
* '''singleShot''': Bool showing whether the timer is only to be run once, or continuously until told to stop. Can be both set and read from (see examples).&lt;br /&gt;
* '''isRunning''': Read-only bool telling whether the timer is currently running.&lt;br /&gt;
* '''simulatedTime''': (FG 2017.1+; {{flightgear commit|0af316|t=commit}}) Bool telling whether the timer is using simulated time (which accounts for pause, etc.). Defaults to false (use real time). Can be both read and set. This cannot be changed while the timer is running.&lt;br /&gt;
Unlike {{func link|settimer()}}, which it replaces, &amp;lt;code&amp;gt;maketimer()&amp;lt;/code&amp;gt; provides more control over the timer. In addition, it can help reduce memory usage.&lt;br /&gt;
|param1 = interval&lt;br /&gt;
|param1text = Interval in seconds for the timer.&lt;br /&gt;
|param2 = self&lt;br /&gt;
|param2text = Optional parameter specifying what any &amp;lt;code&amp;gt;'''me'''&amp;lt;/code&amp;gt; references in the function being called will refer to.&lt;br /&gt;
|param3 = function&lt;br /&gt;
|param3text = Function to be called at the given interval.&lt;br /&gt;
|example1 = var timer = maketimer(1, func(){&lt;br /&gt;
    print(&amp;quot;Hello, World!&amp;quot;); # print &amp;quot;Hello, World!&amp;quot; once every second (call timer.stop() to stop it)&lt;br /&gt;
});&lt;br /&gt;
timer.start();&lt;br /&gt;
|example2 = var timer = maketimer(1, math, func(){&lt;br /&gt;
    print(me.math); # 'me' reference is the 'math' namespace&lt;br /&gt;
});&lt;br /&gt;
timer.singleShot = 1; # timer will only be run once&lt;br /&gt;
timer.start();&lt;br /&gt;
|example3 = var timer = maketimer(1, func(){&lt;br /&gt;
    print(&amp;quot;Hello, World!&amp;quot;); # print &amp;quot;Hello, World!&amp;quot; once every second (call timer.stop() to stop it)&lt;br /&gt;
});&lt;br /&gt;
timer.start();&lt;br /&gt;
print(timer.isRunning); # prints 1&lt;br /&gt;
|example4text = In the example below, &amp;quot;Hello, World!&amp;quot; will be printed after one second the first time, and after two seconds thereafter.&lt;br /&gt;
|example4 = var update = func(){&lt;br /&gt;
    print(&amp;quot;Hello, World!&amp;quot;);&lt;br /&gt;
    timer.restart(2); # restarts the timer with a two second interval&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var timer = maketimer(1, update);&lt;br /&gt;
timer.singleShot = 1;&lt;br /&gt;
timer.start();&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===maketimestamp()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = maketimestamp()&lt;br /&gt;
|source = ''Implemented using the {{API Link|flightgear|class|TimeStampObj}} class.''&amp;lt;br&amp;gt;{{flightgear file|src/Scripting/NasalSys.cxx|l=214|t=Part 1}} {{!}} {{flightgear file|src/Scripting/NasalSys.cxx|l=589|t=Part 2}}&lt;br /&gt;
|version = 2019.2&lt;br /&gt;
|commit = {{flightgear commit|7db06300|t=commit}}&lt;br /&gt;
|text = Returns a time stamp object to allow high resolution timing of Nasal operations. When created the timer will automatically be stamped. The object has the following methods:&lt;br /&gt;
* '''stamp()''': Resets the timing operation. Call this first.&lt;br /&gt;
* '''elapsedMSec()''': returns number of milliseconds elapsed since stamp() called. Resolution may vary depending on platform but is usually at least millisecond accuracy.&lt;br /&gt;
* '''elapsedUSec()''': returns number of microseconds elapsed since stamp() called. Resolution may vary depending on platform but is usually at least millisecond accuracy.&lt;br /&gt;
|&lt;br /&gt;
|example1text = In the example below the number of milliseconds elapsed will be printed.&lt;br /&gt;
|example1 = var timestamp = maketimestamp();&lt;br /&gt;
timestamp.stamp();&lt;br /&gt;
print(timestamp.elapsedMSec(), &amp;quot;ms elapsed&amp;quot;);&lt;br /&gt;
print(timestamp.elapsedMSec(), &amp;quot;ms elapsed&amp;quot;);&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===md5()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = md5(string);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=758|t=Source}}&lt;br /&gt;
|version = 3.2&lt;br /&gt;
|commit = {{flightgear commit|cfbf9e|t=commit}}&lt;br /&gt;
|text = Returns a the {{wikipedia|MD5}} hash (as a string) of the inputted string.&lt;br /&gt;
|param1 = string&lt;br /&gt;
|param1text = String the generate the hash of. Mandatory.&lt;br /&gt;
|example1text = The below code should output &amp;lt;code&amp;gt;65a8e27d8879283831b664bd8b7f0ad4&amp;lt;/code&amp;gt;.&lt;br /&gt;
|example1 = print(md5(&amp;quot;Hello, World!&amp;quot;));&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===navinfo()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = navinfo(lat, lon, type, id);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1453|t=Source}}&lt;br /&gt;
|text = Returns vector &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt; ghost objects matching the given '''type''' and '''id''' or &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; on error.&lt;br /&gt;
|param1 = lat ''and'' lon&lt;br /&gt;
|param1text = If given, the returned navaids will be put into order of ascending distance from the location.&lt;br /&gt;
|param2 = type&lt;br /&gt;
|param2text = Narrows the search to the given type. Must be one of &amp;quot;any,&amp;quot; &amp;quot;fix,&amp;quot; &amp;quot;vor,&amp;quot; &amp;quot;ndb,&amp;quot; &amp;quot;ils,&amp;quot; &amp;quot;dme,&amp;quot; or &amp;quot;tacan.&amp;quot; Defaults to the equivalent of &amp;quot;any.&amp;quot;&lt;br /&gt;
|param3 = id&lt;br /&gt;
|param3text = ID to search for. Note that, although all the parameters are technically optional, this parameter must be given, otherwise an empty vector will be returned.&lt;br /&gt;
|example1 = navinfo(&amp;quot;vor&amp;quot;); # returns all VORs&lt;br /&gt;
|example2 = navinfo(&amp;quot;HAM&amp;quot;); # return all navaids whose names start with &amp;quot;HAM&amp;quot;&lt;br /&gt;
|example3 = navinfo(&amp;quot;vor&amp;quot;, &amp;quot;HAM&amp;quot;); # return all VORs whose names start with &amp;quot;HAM&amp;quot;&lt;br /&gt;
|example4 = navinfo(34,48,&amp;quot;vor&amp;quot;,&amp;quot;HAM&amp;quot;); # return all VORs whose names start with &amp;quot;HAM&amp;quot; and sorted by distance relative to 34°, 48°&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===parse_markdown()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = parse_markdown(markdown);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=745|t=Part 1}} {{!}} {{simgear file|simgear/misc/SimpleMarkdown.cxx|t=Part 2}} &lt;br /&gt;
|version = 3.2&lt;br /&gt;
|text = Parses a string containing {{wikipedia|Markdown}} and returns the result as a string. As of FlightGear 2016.1, it is just a simple parser, and does the following:&lt;br /&gt;
* It strips whitespace from the beginning of the string.&lt;br /&gt;
* It supports [http://daringfireball.net/projects/markdown/syntax#p paragraphs and line breaks].&lt;br /&gt;
* It collapses whitespace.&lt;br /&gt;
* It converts unordered [http://daringfireball.net/projects/markdown/syntax#list lists] to use a bullet character (&amp;amp;bull;). Note that the bullet character is implemented in hexadecimal UTF-8 character bytes (&amp;lt;code&amp;gt;E2 80 A2&amp;lt;/code&amp;gt;), as so may not work properly when the being displayed in an encoding other than UTF-8.&lt;br /&gt;
|param1 = markdown&lt;br /&gt;
|param1text = String containing Markdown to be parsed.&lt;br /&gt;
|example1text = &lt;br /&gt;
Save the below code as &amp;lt;tt&amp;gt;''[[$FG_ROOT]]/gui/dialogs/test.xml''&amp;lt;/tt&amp;gt;, then run the Nasal code below it to open the dialog. To change the markdown to be parsed, simply change the code in the highlighted section, save it, and reload the GUI (&amp;lt;tt&amp;gt;Debug &amp;gt; Reload GUI&amp;lt;/tt&amp;gt;).&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot; highlight=&amp;quot;41-50&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;PropertyList&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;name&amp;gt;test&amp;lt;/name&amp;gt;&lt;br /&gt;
&amp;lt;layout&amp;gt;vbox&amp;lt;/layout&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;group&amp;gt;&lt;br /&gt;
    &amp;lt;layout&amp;gt;hbox&amp;lt;/layout&amp;gt;&lt;br /&gt;
    &lt;br /&gt;
    &amp;lt;empty&amp;gt;&lt;br /&gt;
        &amp;lt;stretch&amp;gt;true&amp;lt;/stretch&amp;gt;&lt;br /&gt;
    &amp;lt;/empty&amp;gt;&lt;br /&gt;
    &lt;br /&gt;
    &amp;lt;text&amp;gt;&lt;br /&gt;
        &amp;lt;label&amp;gt;parse_markdown() test dialog&amp;lt;/label&amp;gt;&lt;br /&gt;
    &amp;lt;/text&amp;gt;&lt;br /&gt;
    &lt;br /&gt;
    &amp;lt;empty&amp;gt;&lt;br /&gt;
        &amp;lt;stretch&amp;gt;true&amp;lt;/stretch&amp;gt;&lt;br /&gt;
    &amp;lt;/empty&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    &amp;lt;button&amp;gt;&lt;br /&gt;
        &amp;lt;legend&amp;gt;&amp;lt;/legend&amp;gt;&lt;br /&gt;
        &amp;lt;pref-width&amp;gt;16&amp;lt;/pref-width&amp;gt;&lt;br /&gt;
        &amp;lt;pref-height&amp;gt;16&amp;lt;/pref-height&amp;gt;&lt;br /&gt;
        &amp;lt;binding&amp;gt;&lt;br /&gt;
            &amp;lt;command&amp;gt;dialog-close&amp;lt;/command&amp;gt;&lt;br /&gt;
        &amp;lt;/binding&amp;gt;&lt;br /&gt;
    &amp;lt;/button&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/group&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;canvas&amp;gt;&lt;br /&gt;
    &amp;lt;name&amp;gt;Canvas plot&amp;lt;/name&amp;gt;&lt;br /&gt;
    &amp;lt;stretch&amp;gt;true&amp;lt;/stretch&amp;gt;&lt;br /&gt;
    &amp;lt;pref-width&amp;gt;400&amp;lt;/pref-width&amp;gt;&lt;br /&gt;
    &amp;lt;pref-height&amp;gt;300&amp;lt;/pref-height&amp;gt;&lt;br /&gt;
    &amp;lt;nasal&amp;gt;&lt;br /&gt;
        &amp;lt;load&amp;gt;&amp;lt;![CDATA[&lt;br /&gt;
var text = 'Items:&lt;br /&gt;
* apples&lt;br /&gt;
* oranges&lt;br /&gt;
* pears&lt;br /&gt;
&lt;br /&gt;
Some text.&lt;br /&gt;
Some more items:&lt;br /&gt;
* apples&lt;br /&gt;
* oranges&lt;br /&gt;
* pears';&lt;br /&gt;
&lt;br /&gt;
var parsed = parse_markdown(text);&lt;br /&gt;
&lt;br /&gt;
var root_canvas = canvas.get(cmdarg());&lt;br /&gt;
root_canvas.setColorBackground(255, 255, 255);&lt;br /&gt;
var root = root_canvas.createGroup();&lt;br /&gt;
&lt;br /&gt;
var text_dis = root.createChild(&amp;quot;text&amp;quot;)&lt;br /&gt;
    .setText(parsed)&lt;br /&gt;
    .setTranslation(5, 5)&lt;br /&gt;
    .setFont(&amp;quot;LiberationFonts\LiberationSans-Regular.ttf&amp;quot;)&lt;br /&gt;
    .setFontSize(15)&lt;br /&gt;
    .setColor(0, 0, 0)&lt;br /&gt;
    .setDrawMode(canvas.Text.TEXT)&lt;br /&gt;
    .setAlignment(&amp;quot;left-top&amp;quot;);&lt;br /&gt;
        ]]&amp;gt;&amp;lt;/load&amp;gt;&lt;br /&gt;
    &amp;lt;/nasal&amp;gt;&lt;br /&gt;
&amp;lt;/canvas&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/PropertyList&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|example1 = fgcommand(&amp;quot;dialog-show&amp;quot;, {&amp;quot;dialog-name&amp;quot;: &amp;quot;test&amp;quot;});&lt;br /&gt;
|example2text = The example below parses Markdown and outputs it in a HTML document. The parsed text is placed in &amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot; inline&amp;gt;&amp;lt;pre&amp;gt;&amp;lt;/pre&amp;gt;&amp;lt;/syntaxhighlight&amp;gt; tags. To change the Markdown to be parsed, simply edit the variable &amp;lt;tt&amp;gt;markdown&amp;lt;/tt&amp;gt; at the top of the code.&lt;br /&gt;
|example2 = &amp;lt;nowiki&amp;gt;var markdown = 'Items:&lt;br /&gt;
* apples&lt;br /&gt;
* oranges&lt;br /&gt;
* pears&lt;br /&gt;
&lt;br /&gt;
Some text.&lt;br /&gt;
Some more items:&lt;br /&gt;
* apples&lt;br /&gt;
* oranges&lt;br /&gt;
* pears';&lt;br /&gt;
&lt;br /&gt;
var parsed = parse_markdown(markdown);&lt;br /&gt;
&lt;br /&gt;
debug.dump(parsed);&lt;br /&gt;
&lt;br /&gt;
var path = string.normpath(getprop(&amp;quot;/sim/fg-home&amp;quot;) ~ '/Export/parse_markdown()-test.html');&lt;br /&gt;
&lt;br /&gt;
var file = io.open(path, &amp;quot;w&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
var html = &amp;quot;&amp;lt;!DOCTYPE html&amp;gt;\n\n&amp;lt;html&amp;gt;\n\n&amp;lt;head&amp;gt;\n\t&amp;lt;meta charset=\&amp;quot;UTF-8\&amp;quot;&amp;gt;\n\t&amp;lt;title&amp;gt;parse_markdown() test generated by Nasal&amp;lt;/title&amp;gt;\n&amp;lt;/head&amp;gt;\n\n&amp;lt;body&amp;gt;\n\t&amp;lt;pre&amp;gt;&amp;quot; ~ parsed ~ &amp;quot;&amp;lt;/pre&amp;gt;\n&amp;lt;/body&amp;gt;\n\n&amp;lt;/html&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
io.write(file, html);&lt;br /&gt;
io.close(file);&lt;br /&gt;
print(&amp;quot;Done, file ready for viewing (&amp;quot; ~ path ~ &amp;quot;)&amp;quot;);&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===parsexml()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = &amp;lt;nowiki&amp;gt;parsexml(path[, start[, end[, data[, pro_ins]]]]);&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=710|t=Source}}&lt;br /&gt;
|text = This function is an interface into the built-in [http://expat.sourceforge.net/ Expat XML parser]. The absolute path to the file is returned as string, or &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; is returned on error.&lt;br /&gt;
|param1 = path&lt;br /&gt;
|param1text = Mandatory absolute path to the XML file to be parsed.&lt;br /&gt;
|param2 = start&lt;br /&gt;
|param2text = Optional callback function that will be called for every starting tag. The function should take two argument: the tag name and a hash containing attributes.&lt;br /&gt;
|param3 = end&lt;br /&gt;
|param3text = Optional callback function that will be called for every ending tag. The function should take one argument: the tag name.&lt;br /&gt;
|param4 = data&lt;br /&gt;
|param4text = Optional callback function that will be called for every piece of data within a set of tags. The function should take one argument: the data as a string.&lt;br /&gt;
|param5 = pro_ins&lt;br /&gt;
|param5text = Optional callback function that will be called for every {{wikipedia|Processing Instruction|processing instruction}}. The function should take two argument: the target and the data string.&lt;br /&gt;
|example1text = Save the below XML code in &amp;lt;tt&amp;gt;''[[$FG_HOME]]/Export/demo.xml''&amp;lt;/tt&amp;gt;. Then, execute the Nasal code below in the Nasal Console. The XML will be parsed and each bit of the code will be printed.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;?xml-stylesheet type=&amp;quot;text/xsl&amp;quot; href=&amp;quot;style.xsl&amp;quot;?&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;foo&amp;gt;&lt;br /&gt;
  &amp;lt;blah type=&amp;quot;string&amp;quot;&amp;gt;&amp;lt;![CDATA[ &amp;lt;sender&amp;gt;John Smith&amp;lt;/sender&amp;gt; ]]&amp;gt;&amp;lt;/blah&amp;gt;&lt;br /&gt;
  &amp;lt;blah2 type=&amp;quot;string&amp;quot;&amp;gt;Orange &amp;amp;amp; lemons&amp;lt;/blah2&amp;gt;&lt;br /&gt;
&amp;lt;/foo&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|example1 = var start = func(name, attr){&lt;br /&gt;
    print(&amp;quot;Starting tag: '&amp;quot;, name, &amp;quot;'&amp;quot;);&lt;br /&gt;
    foreach(var a; keys(attr)){&lt;br /&gt;
        print(&amp;quot;\twith attribute &amp;quot;, a, '=&amp;quot;', attr[a], '&amp;quot;');&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var end = func(name){&lt;br /&gt;
    print(&amp;quot;Ending tag: '&amp;quot;, name, &amp;quot;'&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var data = func(data){&lt;br /&gt;
    print(&amp;quot;Data = '&amp;quot;, data, &amp;quot;'&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var pro_instr = func(target, data){&lt;br /&gt;
    print(&amp;quot;Processing instruction: target = '&amp;quot;, target, &amp;quot;', data = '&amp;quot;, data, &amp;quot;'&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
parsexml(getprop(&amp;quot;/sim/fg-home&amp;quot;) ~ '/Export/demo.xml', start, end, data, pro_instr);&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===print()===&lt;br /&gt;
{{Note|As of 07/2020, we are in the process of slowly deprecating and removing Nasal print() (in fgdata) where possible in favor of log [[#logprint()]] which takes a priority level.&lt;br /&gt;
&lt;br /&gt;
This is part of the goal that we achieve zero output at log-level at alert, and everything shown at ‘warn; is really a warning, not just information.&amp;lt;ref&amp;gt;https://sourceforge.net/p/flightgear/mailman/message/37042224/&amp;lt;/ref&amp;gt;}}&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = &amp;lt;nowiki&amp;gt;print(data[, data[, ...]]);&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=415|t=Source}}&lt;br /&gt;
|text = Concatenates its arguments and then prints it to the terminal and the [[Commonly used debugging tools#fgfs.log|log]]. Note that a newline is automatically added.&lt;br /&gt;
|param1 = data&lt;br /&gt;
|param1text = Data to print. Only strings and numbers can be printed; other data types will not be. There many be any number of arguments; they will just be concatenated together.&lt;br /&gt;
|example1 = print(&amp;quot;Just&amp;quot;, &amp;quot; a &amp;quot;, &amp;quot;test&amp;quot;); # prints &amp;quot;Just a test&amp;quot;&lt;br /&gt;
|example2 = print(&amp;quot;pi = &amp;quot;, math.pi); # prints &amp;quot;pi = 3.141592...&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== printf() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = &amp;lt;nowiki&amp;gt;printf(format[, arg[, arg, [...]]]);&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|source = {{fgdata file|Nasal/globals.nas|t=Source}}&lt;br /&gt;
|text = Creates and prints a formatted string. For a description of its arguments, see {{func link|sprintf()}} (it is, in fact, implemented using &amp;lt;code&amp;gt;sprintf()&amp;lt;/code&amp;gt;).&lt;br /&gt;
|example1 = printf(&amp;quot;In hexadecimal, 100000 = %X&amp;quot;, 186A0); # prints &amp;quot;In hexadecimal, 100000 = 186A0&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===printlog()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = &amp;lt;nowiki&amp;gt;printlog(level, data[, data[, ...]]);&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|source = {{fgdata file|Nasal/globals.nas|t=Source}}&lt;br /&gt;
|text = Prints the given message with the given log level. If the log level is higher or equal to &amp;lt;code&amp;gt;/sim/logging/priority&amp;lt;/code&amp;gt;, it is printed.&lt;br /&gt;
|param1 = level&lt;br /&gt;
|param1text = Mandatory log level as a string. Must be one of &amp;quot;none,&amp;quot; &amp;quot;bulk,&amp;quot; &amp;quot;debug,&amp;quot; &amp;quot;info,&amp;quot; &amp;quot;warn,&amp;quot; or &amp;quot;alert.&amp;quot; Note that &amp;quot;none&amp;quot; will mean that the message will never be printed.&lt;br /&gt;
|param2 = data&lt;br /&gt;
|param2text = Data to be printed. Only strings and numbers will be printed; others will not be. There may be any number of arguments; they will just be concatenated together.&lt;br /&gt;
|example1 = printlog(&amp;quot;alert&amp;quot;, &amp;quot;This is an alert&amp;quot;); # message will be printed&lt;br /&gt;
|example2 = printlog(&amp;quot;info&amp;quot;, &amp;quot;Just informing you about something&amp;quot;); # message will be printed only if log level is set to &amp;quot;info&amp;quot; or less&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===rand()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = rand();&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=554|t=Source}}&lt;br /&gt;
|text = Returns a random floating point number between 0 (inclusive) and 1 (exclusive). It takes no arguments.&lt;br /&gt;
|example1 = print(rand()); # prints random number&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===registerFlightPlanDelegate()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = registerFlightPlanDelegate(init_func);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1879|t=Source}}&lt;br /&gt;
|text = Registers a flight plan delegate. See &amp;lt;tt&amp;gt;''{{fgdata file|Nasal/route_manager.nas|t=$FG_ROOT/Nasal/route_manager.nas}}''&amp;lt;/tt&amp;gt; for examples.&lt;br /&gt;
|param1 = init_func&lt;br /&gt;
|param1text = Initialization function which will be called during FlightGear's startup.&lt;br /&gt;
}}&lt;br /&gt;
===removecommand()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = removecommand(cmd);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=668|t=Source}}&lt;br /&gt;
|text = Removes the given fgcommand. Returns &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt;.&lt;br /&gt;
{{caution|This will remove '''any''' [[fgcommands|fgcommand]], even those implemented in C++, so use with caution!}}&lt;br /&gt;
|param1 = cmd&lt;br /&gt;
|param1text = String specifying the name of the command to remove.&lt;br /&gt;
|example1 = addcommand(&amp;quot;hello&amp;quot;, func(){&lt;br /&gt;
    print(&amp;quot;Hello&amp;quot;);&lt;br /&gt;
});&lt;br /&gt;
fgcommand(&amp;quot;hello&amp;quot;); # &amp;quot;Hello&amp;quot; will be printed&lt;br /&gt;
removecommand(&amp;quot;hello&amp;quot;); # removes it&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===removelistener()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = removelistener(id);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=1384|t=Part 1}} {{!}} {{flightgear file|src/Scripting/NasalSys.cxx|l=506|t=Part 2}}&lt;br /&gt;
|text = Removes and deactivates the given listener and returns the number of listeners left or &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; on error.&lt;br /&gt;
{{note|It is good practice to remove listeners when they are not required anymore. This prevents the listeners reducing FlightGear's run performance.}}&lt;br /&gt;
|param1 = id&lt;br /&gt;
|param1text = ID of listener as returned by {{func link|setlistener()}}.&lt;br /&gt;
|example1 = var ls = setlistener(&amp;quot;/sim/test&amp;quot;, func(){&lt;br /&gt;
    print(&amp;quot;Property '/sim/test' has been changed&amp;quot;);&lt;br /&gt;
});&lt;br /&gt;
setprop(&amp;quot;/sim/test&amp;quot;, &amp;quot;blah&amp;quot;); # trigger listener&lt;br /&gt;
var rem = removelistener(ls); # remove listener&lt;br /&gt;
print(&amp;quot;There are &amp;quot;, rem, &amp;quot; listeners remaining&amp;quot;);&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===resolvepath()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = resolvepath(path);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=604|t=Source}}&lt;br /&gt;
|text = Takes a relative path as a string and uses [[SimGear]]'s path-resolving framework to return an absolute path as a string. If the path could not be resolved, an empty string is returned. See [[Resolving Paths]] for a detailed description of the algorithm. This function can also be used to check if a file exists.&lt;br /&gt;
|param1 = path&lt;br /&gt;
|param1text = Relative path to be completed.&lt;br /&gt;
|example1 = print(resolvepath(&amp;quot;Nasal/globals.nas&amp;quot;)); # prints the equivalent of $FG_ROOT/Nasal/globals.nas&lt;br /&gt;
|example2 = print(resolvepath(&amp;quot;blah&amp;quot;)); # prints nothing; could not be resolved&lt;br /&gt;
|example3 = var file_path = resolvepath(&amp;quot;Aircraft/SenecaII/some-file&amp;quot;);&lt;br /&gt;
if (file_path != &amp;quot;&amp;quot;){&lt;br /&gt;
    gui.popupTip(&amp;quot;some-file found&amp;quot;, 2);&lt;br /&gt;
} else {&lt;br /&gt;
    gui.popupTip(&amp;quot;some-file not found&amp;quot;, 2);&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===setlistener()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = &amp;lt;nowiki&amp;gt;setlistener(node, code[, init[, type]]);&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|private = _setlistener()&lt;br /&gt;
|source = {{flightgear file|src/Scripting/Nasal|l=499|t=Part 1}} {{!}} {{flightgear file|src/Scripting/Nasal|l=1350|t=Part 2}}&amp;lt;br&amp;gt;''Listener implemented using the {{API Link|flightgear|class|FGNasalListener}} class.''&lt;br /&gt;
|text = Creates a listener which will be triggered when the given property is changed (depending on the '''type'''). A unique integer ID is returned; this can later be used as the argument to {{func link|removelistener()}}.&lt;br /&gt;
{{note|Listeners are known to be a source of resource leaks. To avoid this, please take measures such as:&lt;br /&gt;
* Using {{func link|removelistener()}} when they are not needed any more.&lt;br /&gt;
* Using a single initialization listener.&lt;br /&gt;
* Avoiding tying listeners to properties that are rapidly updated (e.g., many times per frame).&lt;br /&gt;
}}&lt;br /&gt;
|param1 = node&lt;br /&gt;
|param1text = Mandatory string or &amp;lt;code&amp;gt;props.Node&amp;lt;/code&amp;gt; object pointing to a property in the [[Property Tree]].&lt;br /&gt;
|param2 = code&lt;br /&gt;
|param2text = Mandatory callback function to execute when the listener is triggered. The function can take up to four arguments in the following order:&lt;br /&gt;
* '''changed''': a &amp;lt;code&amp;gt;props.Node&amp;lt;/code&amp;gt; object pointing to the changed node.&lt;br /&gt;
* '''listen''': a &amp;lt;code&amp;gt;props.Node&amp;lt;/code&amp;gt; object pointing to the listened-to node. Note that this argument maybe different depending on the '''type'''.&lt;br /&gt;
* '''mode''': an integer telling how the listener was triggered. 0 means that the value was changed. 1 means that a child property was added. -1 means that a child property was removed.&lt;br /&gt;
* '''is_child''': boolean telling whether '''changed''' is a child property of the listened-to '''node''' or not. 1 (true) if it is, 0 (false) otherwise.&lt;br /&gt;
|param3 = init&lt;br /&gt;
|param3text = If set to 1 (true), the listener will additionally be triggered when it is created. This argument is optional and defaults to 0 (false).&lt;br /&gt;
|param4 = type&lt;br /&gt;
|param4text = Integer specifying the listener's behavior. 0 means that the listener will only trigger when the property is changed. 1 means that the trigger will always be triggered when the property is written to. 2 will mean that the listener will be triggered even if child properties are modified. This argument is optional and defaults to 1.&lt;br /&gt;
|example1 = var prop = props.globals.initNode(&amp;quot;/sim/test&amp;quot;, &amp;quot;&amp;quot;, &amp;quot;STRING&amp;quot;); # create property&lt;br /&gt;
var id = setlistener(&amp;quot;/sim/test&amp;quot;, func(n){ # create listener&lt;br /&gt;
    print(&amp;quot;Value: &amp;quot;, n.getValue());&lt;br /&gt;
});&lt;br /&gt;
setprop(&amp;quot;/sim/test&amp;quot;, &amp;quot;blah&amp;quot;); # trigger listener&lt;br /&gt;
removelistener(id); # remove listener&lt;br /&gt;
prop.remove(); # remove property&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===setprop()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = &amp;lt;nowiki&amp;gt;setprop(path[, path[, ...]], value);&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=385|t=Source}}&lt;br /&gt;
|text = Sets the value of a property in the [[Property Tree]]. If the property does not exist, it will be created. Returns 1 (true) on success or 0 (false) if there was an error.&lt;br /&gt;
{{note|If you want to remove a property, you will have to use one of the &amp;lt;code&amp;gt;props&amp;lt;/code&amp;gt; helpers:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
props.globals.getNode(&amp;quot;foo/bar&amp;quot;).remove(); # take out the complete node&lt;br /&gt;
props.globals.getNode(&amp;quot;foo&amp;quot;).removeChild(&amp;quot;bar&amp;quot;); # take out a certain child node&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
|param1 = path&lt;br /&gt;
|param1text = There needs to be at least one '''path''' argument, but there is no limit to the maximum amount that can be given. They will be concatenated together to form a property tree path. The arguments must be strings, but in FlightGear v3.2 onwards, there also is support (added by {{flightgear commit|34ed79}}) for numeric arguments as indices. See example 2 below.&lt;br /&gt;
|param2 = value&lt;br /&gt;
|param2text = Value to write to the given property. Must be either a string or a number.&lt;br /&gt;
|example1 = setprop(&amp;quot;/sim/demo&amp;quot;, &amp;quot;This is a demo&amp;quot;);&lt;br /&gt;
|example2text = Note that the example below will only work in FlightGear 3.2 and above.&lt;br /&gt;
|example2 = for(var i = 0; i &amp;lt; 3; i += 1){&lt;br /&gt;
    setprop(&amp;quot;/sim/demo&amp;quot;, i, &amp;quot;Demo #&amp;quot; ~ i));&lt;br /&gt;
}&lt;br /&gt;
|example3text = Same as above, but is supported by all versions of FlightGear.&lt;br /&gt;
|example3 = for(var i = 0; i &amp;lt; 3; i += 1){&lt;br /&gt;
    setprop(&amp;quot;/sim/demo[&amp;quot; ~ i ~ &amp;quot;]&amp;quot;, &amp;quot;Demo #&amp;quot; ~ i));&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
====See also====&lt;br /&gt;
{{note| If you have to read/write the same property multiple times (e.g. in an update loop), it is more efficient to use a node object: &lt;br /&gt;
To get a Node rather than its value, use &amp;lt;code&amp;gt;props.globals.getNode()&amp;lt;/code&amp;gt; - see [[Nasal_library/props]]. }}&lt;br /&gt;
&lt;br /&gt;
===settimer() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = settimer(function, delta[, realtime]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=499|t=Part 1}} {{!}} {{flightgear file|src/Scripting/NasalSys.cxx|l=1286|t=Part 2}}&lt;br /&gt;
|text = Runs the given function a specified amount of seconds after the current time. Returns &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt;.&lt;br /&gt;
{{note|Improper use of &amp;lt;code&amp;gt;settimer()&amp;lt;/code&amp;gt; may cause resource leaks. It is also highly recommended that the newer {{func link|maketimer()}} should be used instead of this function.}} &lt;br /&gt;
|param1 = function&lt;br /&gt;
|param1text = Mandatory function that will be called. It may be necessary to enclose code in an anonymous function (see example).&lt;br /&gt;
|param2 = delta&lt;br /&gt;
|param2text = Mandatory amount of time in seconds after which the function will be called.&lt;br /&gt;
|param3 = realtime&lt;br /&gt;
|param3text = If 1 (true), &amp;quot;real time&amp;quot; will be used instead of &amp;quot;simulation time.&amp;quot; Defaults to 0 (false). Note that if &amp;quot;simulation time&amp;quot; is used, the timer will not run while FlightGear is paused.&lt;br /&gt;
|example1 = var myFunc = func(){&lt;br /&gt;
    print(&amp;quot;Hello&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
settimer(myFunc, 2); # runs myFunc after 2 seconds&lt;br /&gt;
|example2 = var sqr = func(a){&lt;br /&gt;
    return a * a;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
settimer(func(){&lt;br /&gt;
    print(sqr(2)); # will print 4 after 2 seconds&lt;br /&gt;
}, 2);&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===srand() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = srand();&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=559|t=Source}}&lt;br /&gt;
|text = Makes the pseudorandom number generator (see {{func link|rand()}}) generate a new {{wikipedia|random seed|noicon=1}} based on time. Returns 0.&lt;br /&gt;
}}&lt;br /&gt;
===systime()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = systime();&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=770|t=Source}}&lt;br /&gt;
|text = Returns the {{wikipedia|Unix time}} (seconds since since 00:00:00 UTC, 1/1/1970) as a floating point number with high resolution. This function is useful for benchmarking purposes (see example 2).&lt;br /&gt;
{{note|1=High resolution timers under Windows can produce inaccurate or fixed sub-millisecond results.&amp;lt;ref&amp;gt;{{cite web|url=http://forum.flightgear.org/viewtopic.php?f=30&amp;amp;t=29259|title=Nasal: systime() ??!?|author=Necolatis|date=Apr 2nd, 2016}}&amp;lt;/ref&amp;gt; This is due to the underlying {{func link|GetSystemTimeAsFileTime()|link=https://msdn.microsoft.com/en-us/library/windows/desktop/ms724397(v=vs.85).aspx}} API call, which depends on hardware availability of suitable high resolution timers. See also [https://msdn.microsoft.com/en-us/library/windows/desktop/dn553408(v=vs.85).aspx Acquiring high-resolution time stamps]}}&lt;br /&gt;
|example1 = print(&amp;quot;Unix time: &amp;quot;, systime()); # prints Unix time&lt;br /&gt;
|example2 = var myFunc = func(){&lt;br /&gt;
    for(var i = 0; i &amp;lt;= 10; i += 1){&lt;br /&gt;
        print(&amp;quot;Interation #&amp;quot;, i);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
var t = systime(); # record time&lt;br /&gt;
myFunc(); # run function&lt;br /&gt;
var t2 = systime(); # record new time&lt;br /&gt;
print(&amp;quot;myFunc() took &amp;quot;, t2 - t, &amp;quot; seconds&amp;quot;); # print result&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===thisfunc()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = thisfunc();&lt;br /&gt;
|source = {{fgdata file|Nasal/globals.nas|t=Source}}&lt;br /&gt;
|text = Returns the function from which this function is called. This allows a function to reliably and safely call itself from within a closure.&lt;br /&gt;
|example1 = var stringify_vec = func(input){&lt;br /&gt;
    if (typeof(input) == &amp;quot;scalar&amp;quot;){&lt;br /&gt;
        return sprintf(&amp;quot;%s&amp;quot;, input);&lt;br /&gt;
    } elsif (typeof(input) == &amp;quot;vector&amp;quot;) {&lt;br /&gt;
        if (size(input) == 0) return &amp;quot;[]&amp;quot;;&lt;br /&gt;
        var this = thisfunc();&lt;br /&gt;
        var buffer = &amp;quot;[&amp;quot;;&lt;br /&gt;
        for(var i = 0; i &amp;lt; size(input); i += 1){&lt;br /&gt;
            buffer ~= this(input[i]);&lt;br /&gt;
            if (i == size(input) - 1) {&lt;br /&gt;
                buffer ~= &amp;quot;]&amp;quot;;&lt;br /&gt;
            } else {&lt;br /&gt;
                buffer ~= &amp;quot;, &amp;quot;;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        return buffer;&lt;br /&gt;
    } else {&lt;br /&gt;
        die(&amp;quot;stringify_vec(): Error! Invalid input. Must be a vector or scalar&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var test_vec = [&amp;quot;a&amp;quot;, &amp;quot;b&amp;quot;, &amp;quot;c&amp;quot;, 1, 2, 3];&lt;br /&gt;
debug.dump(stringify_vec(test_vec)); # prints &amp;quot;[a, b, c, 1, 2, 3]&amp;quot;&lt;br /&gt;
test_vec = [];&lt;br /&gt;
debug.dump(stringify_vec(test_vec)); # prints &amp;quot;[]&amp;quot;&lt;br /&gt;
test_vec = {};&lt;br /&gt;
debug.dump(stringify_vec(test_vec)); # will throw an error&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===tileIndex() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = tileIndex();&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1720|t=Source}}&lt;br /&gt;
|text = Returns the index of the tile at the aircraft's current position as a string. This corresponds to the name of the STG file of the tile. For example, at [[KSFO]], this would be &amp;lt;code&amp;gt;942050&amp;lt;/code&amp;gt;, corresponding to &amp;lt;tt&amp;gt;''[[$FG_SCENERY]]/Terrain/w130n30/w123n3/942050.stg''&amp;lt;/tt&amp;gt;.&lt;br /&gt;
|example1 = print(tileIndex()); # print index&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== tilePath()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = tilePath();&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1712|t=Source}}&lt;br /&gt;
|text = Returns the base path of the tile at the aircraft's current position as a string. For example, at KSFO, this would be &amp;lt;code&amp;gt;w130n30/w123n3&amp;lt;/code&amp;gt;, corresponding to &amp;lt;tt&amp;gt;''[[$FG_SCENERY]]/Terrain/w130n30/w123n3''&amp;lt;/tt&amp;gt;.&lt;br /&gt;
|example1 = print(tilePath()); # print path&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== values()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = values(hash);&lt;br /&gt;
|source = {{fgdata file|Nasal/globals.nas|t=Source}}&lt;br /&gt;
|text = Returns a vector containing the values of the given hash.&lt;br /&gt;
|param1 = hash&lt;br /&gt;
|param1text = Mandatory hash to get the values of.&lt;br /&gt;
|example1 = var hash = {&lt;br /&gt;
    &amp;quot;a&amp;quot;: 1,&lt;br /&gt;
    &amp;quot;b&amp;quot;: 2,&lt;br /&gt;
    &amp;quot;c&amp;quot;: 3&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
foreach(var val; values(hash)){&lt;br /&gt;
    print(val);&lt;br /&gt;
}&lt;br /&gt;
|example2text = The below example does exactly the same thing as the above example, but does not use &amp;lt;code&amp;gt;values()&amp;lt;/code&amp;gt;:&lt;br /&gt;
|example2 = var hash = {&lt;br /&gt;
    &amp;quot;a&amp;quot;: 1,&lt;br /&gt;
    &amp;quot;b&amp;quot;: 2,&lt;br /&gt;
    &amp;quot;c&amp;quot;: 3&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
foreach(var key; keys(hash)){&lt;br /&gt;
    print(hash[key]);&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
==Extension functions new in FG 2020.1 ==&lt;br /&gt;
The following functions have been added to the nasal core library and will be released with FlightGear version 2020.1. &lt;br /&gt;
Before the release they are available in the development branch &amp;quot;next&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
===isfunc()===&lt;br /&gt;
Returns 1 if type or argument is a function, otherwise 0.&lt;br /&gt;
&lt;br /&gt;
===isghost()===&lt;br /&gt;
Returns 1 if type or argument is a ghost, otherwise 0.&lt;br /&gt;
&lt;br /&gt;
=== ishash()===&lt;br /&gt;
Returns 1 if type or argument is a hash, otherwise 0.&lt;br /&gt;
&lt;br /&gt;
===isint()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = isint(x);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|t=Source}}&lt;br /&gt;
|text = Returns 1 if argument has a numeric value and x == floor(x), e.g. integer.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===isnum()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = isnum(x);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|t=Source}}&lt;br /&gt;
|text = Returns 1 if typeof(x) is &amp;quot;scalar&amp;quot; and x has a numeric value otherwise 0. &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===isscalar()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = isscalar(x);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|t=Source}}&lt;br /&gt;
|text = Returns 1 if type or argument is a scalar (string or numeric), otherwise (vector, hash, func, ...) it returns 0. This is useful to check if a variable can be converted to string e.g. when useing the string concat operator &amp;quot;~&amp;quot;. &lt;br /&gt;
|example1 = var a = &amp;quot;foo&amp;quot;; &lt;br /&gt;
var b=42;&lt;br /&gt;
if (isscalar(a) and isscalar(b)) print(a~b);&lt;br /&gt;
if (isstr(a)) print(&amp;quot;a is a string&amp;quot;);&lt;br /&gt;
if (isint(b)) print(&amp;quot;b is an integer&amp;quot;);&lt;br /&gt;
# if (isscalar(a))... is equivalent to if (typeof(a) == &amp;quot;scalar&amp;quot;)...&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===isstr()===&lt;br /&gt;
Returns 1 if type or argument is a string, otherwise 0. &lt;br /&gt;
&lt;br /&gt;
===isvec()===&lt;br /&gt;
Returns 1 if type or argument is a vector, otherwise 0.&lt;br /&gt;
&lt;br /&gt;
===vecindex()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = vecindex(vector, value);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|t=Source}}&lt;br /&gt;
|text = Returns the index of value or nil, if value is not found in vector.&lt;br /&gt;
|example1=&lt;br /&gt;
var myvector = [&amp;quot;apple&amp;quot;, &amp;quot;bananna&amp;quot;, &amp;quot;coconut&amp;quot;];&lt;br /&gt;
# to check if a vector contains a certain value compare vecindex to nil&lt;br /&gt;
if (vecindex(myvector, &amp;quot;apple&amp;quot;) != nil) {&lt;br /&gt;
    print(&amp;quot;found apple&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
# WARNING: this will not work as desired because index is 0&lt;br /&gt;
if (vecindex(myvector, &amp;quot;apple&amp;quot;)) {&lt;br /&gt;
    print(&amp;quot;found apple&amp;quot;);&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
==Variables==&lt;br /&gt;
Various global constants (technically variables) are provided for use in converting between different units. They are all found in {{fgdata file|Nasal/globals.nas|text=$FG_ROOT/Nasal/globals.nas}}.&lt;br /&gt;
&lt;br /&gt;
===D2R===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var radians = degrees * D2R;&lt;br /&gt;
|text = Converts an angle from degrees to radians when multiplied by the angle in degrees. Equal to &amp;lt;code&amp;gt;π / 180&amp;lt;/code&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
===FPS2KT===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var knots = feet_per_second * FPS2KT;&lt;br /&gt;
|text = Converts a velocity from feet per second to knots when multiplied by the velocity in feet per second. Approximately equal to 0.5925.&lt;br /&gt;
}}&lt;br /&gt;
===FT2M===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var metres = feet * FT2M;&lt;br /&gt;
|text = Converts a length from feet to metres when multiplied by the length in feet. Equal to 0.3048.&lt;br /&gt;
}}&lt;br /&gt;
===GAL2L ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var litres = gallons * GAL2L;&lt;br /&gt;
|text = Converts a volume from US liquid gallons to litres when multiplied by the volume in gallons. Approximately equal to 3.7854.&lt;br /&gt;
}}&lt;br /&gt;
===IN2M===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var metres = inches * IN2M;&lt;br /&gt;
|text = Converts a length from inches to metres when multiplied by the length in inches. Equal to 0.0254.&lt;br /&gt;
}}&lt;br /&gt;
===KG2LB===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var pounds = kilograms * KG2LB;&lt;br /&gt;
|text = Converts a mass from kilograms to pounds when multiplied by the mass in kilograms. Approximately equal to 2.2046.&lt;br /&gt;
}}&lt;br /&gt;
===KT2FPS===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var feet_per_second = knots * KT2FPS;&lt;br /&gt;
|text = Converts a velocity from knots to feet per second when multiplied by the velocity in knots. Approximately equal to 1.6878.&lt;br /&gt;
}}&lt;br /&gt;
===KT2MPS===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var metres_per_second = knots * KT2MPS;&lt;br /&gt;
|text = Converts a velocity from knots to metres per second when multiplied by the velocity in knots. Approximately equal to 0.5144.&lt;br /&gt;
}}&lt;br /&gt;
===L2GAL===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var gallons = litres * L2GAL;&lt;br /&gt;
|text = Converts a volume from litres to US liquid gallons when multiplied by the volume in litres. Approximately equal to 0.2642.&lt;br /&gt;
}}&lt;br /&gt;
===LB2KG===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var kilograms = pounds * LB2KG;&lt;br /&gt;
|text = Converts a mass from pounds to kilograms when multiplied by the mass in pounds. Approximately equal to 0.4536.&lt;br /&gt;
}}&lt;br /&gt;
===M2FT ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var feet = metres * M2FT;&lt;br /&gt;
|text = Converts a length from metres to feet when multiplied by the length in metres. Approximately equal to 3.2808.&lt;br /&gt;
}}&lt;br /&gt;
===M2IN===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var inches = metres * M2IN;&lt;br /&gt;
|text = Converts a length from metres to inches when multiplied by the length in metres. Approximately equal to 39.3701.&lt;br /&gt;
}}&lt;br /&gt;
===M2NM ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var nautical_miles = metres * M2NM;&lt;br /&gt;
|text = Converts a length from metres to nautical miles when multiplied by the length in metres. Approximately equal to 0.00054.&lt;br /&gt;
}}&lt;br /&gt;
===MPS2KT ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var knots = metres_per_second * MPS2KT;&lt;br /&gt;
|text = Converts a velocity from metres per second to knots when multiplied by the velocity in metres per second. Approximately equal to 1.9438.&lt;br /&gt;
}}&lt;br /&gt;
===NM2M ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var metres = nautical_miles * NM2M;&lt;br /&gt;
|text = Converts a length from nautical miles to metres when multiplied by the length in nautical miles. Equal to 1,852.&lt;br /&gt;
}}&lt;br /&gt;
===R2D===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var degrees = radians * R2D;&lt;br /&gt;
|text = Converts an angle from radians to degrees when multiplied by the angle in radians. Equal to &amp;lt;code&amp;gt;180 / π&amp;lt;/code&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Appendix}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Nasal namespaces}}&lt;/div&gt;</summary>
		<author><name>Clm76</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Nasal_library&amp;diff=138565</id>
		<title>Nasal library</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Nasal_library&amp;diff=138565"/>
		<updated>2023-10-30T09:59:42Z</updated>

		<summary type="html">&lt;p&gt;Clm76: /* findNavaidsByFrequencyMHz() */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Nasal Navigation|nocat=1}}&lt;br /&gt;
This page documents the global '''library functions and variables''' of FlightGear's built-in scripting language, [[Nasal]]. This includes ''[[#Core library functions|core library functions]]'', which were included in Nasal before its integration into FlightGear, the ''[[#Extension functions|extension functions]]'', which have been subsequently added, and are specifically designed for FlightGear, and the ''[[#variables|global variables]]'', which are conversion variables, added with extension functions, for converting between units. The relevant folders in [[Git]] are:&lt;br /&gt;
* {{flightgear file|src/Scripting}}&lt;br /&gt;
* {{simgear file|simgear/nasal}}&lt;br /&gt;
&lt;br /&gt;
All these functions and variables are in the global namespace, that is, they are directly accessible (e.g., one can call &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; inline&amp;gt;magvar()&amp;lt;/syntaxhighlight&amp;gt; instead of &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; inline&amp;gt;namespace.magvar()&amp;lt;/syntaxhighlight&amp;gt;). However, if a namespace must be used, &amp;lt;code&amp;gt;globals&amp;lt;/code&amp;gt; is the correct namespace, but using it is not recommended. For a more complete explanation, see [[Nasal Namespaces in-depth]].&lt;br /&gt;
&lt;br /&gt;
{{tip|Copy &amp;amp; paste the examples into your [[Nasal Console]] and execute them to see what they do.|width=70%}}&lt;br /&gt;
&lt;br /&gt;
== Core library functions ==&lt;br /&gt;
This is the list of the basic '''core library functions.''' Most of these functions were part of the original Nasal library (before its integration in to FlightGear), while some have been added or changed over time.  See also:&lt;br /&gt;
* http://plausible.org/nasal/lib.html ([http://web.archive.org/web/20101010094553/http://plausible.org/nasal/lib.html archive])&lt;br /&gt;
* {{simgear file|simgear/nasal/lib.c}} ([http://sourceforge.net/p/flightgear/simgear/ci/next/log/?path=/simgear/nasal/lib.c history])&lt;br /&gt;
&lt;br /&gt;
=== append() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = append(vector, element[, element[, ...]]);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=42|t=Source}}&lt;br /&gt;
|text = This function appends, or adds, the given element(s) to the end of the vector given in the first argument.  Returns the vector operated on.&lt;br /&gt;
|param1 = vector&lt;br /&gt;
|param1text = The vector to which the arguments will be appended.&lt;br /&gt;
|param2 = element&lt;br /&gt;
|param2text = An element to be added to the vector.&lt;br /&gt;
|example1 = &lt;br /&gt;
var vector = [1, 2, 3]; # Initialize the vector&lt;br /&gt;
append(vector, 4); # Append the number 4 to the end of the vector&lt;br /&gt;
debug.dump(vector); # Print the contents of the vector&lt;br /&gt;
|example2 = &lt;br /&gt;
var vector = [1, 2, 3]; # Initialize the vector&lt;br /&gt;
append(vector, 4, 5, 6); # Append the numbers 4, 5, and 6 to the end of the vector&lt;br /&gt;
debug.dump(vector); # Print the contents of the vector&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== bind() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = bind(function, locals[, outer_scope]);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=502|t=Source}}&lt;br /&gt;
|text = This creates a new function object. A function in Nasal is three things: the actual code, a hash/namespace of local variables available to the function namespace, and the closure object of that namespace. These correspond to the three arguments respectively.&lt;br /&gt;
|param1 = function&lt;br /&gt;
|param1text = Function to evaluate.&lt;br /&gt;
|param2 = locals&lt;br /&gt;
|param2text = Hash containing values that will become the namespace (first closure) for the function.&lt;br /&gt;
|param3 = outer_scope&lt;br /&gt;
|param3text = Optional function which is bound to the next closure. This can be bound to yet another, making a linked list.&lt;br /&gt;
|example1 = # This is a namespace/hash with a single member, named &amp;quot;key,&amp;quot; which is initialized to 12 &lt;br /&gt;
var Namespace = {&lt;br /&gt;
    key: 12&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
# This is different namespace/hash containing a function&lt;br /&gt;
# dividing a variable &amp;quot;key&amp;quot; (which is unavailable/nil in this namespace) by 2&lt;br /&gt;
var AnotherNamespace = {&lt;br /&gt;
    ret: func {&lt;br /&gt;
        key /= 2;&lt;br /&gt;
    }&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
# To see that key is not available, try to call AnotherNamespace.ret() first&lt;br /&gt;
call(AnotherNamespace.ret, [], nil, nil, var errors = []);&lt;br /&gt;
if(size(errors)){&lt;br /&gt;
    print(&amp;quot;Key could not be divided/resolved!&amp;quot;);&lt;br /&gt;
    debug.printerror(errors);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# Associate the AnotherNamespace.ret() function with the first namespace&lt;br /&gt;
# so that &amp;quot;key&amp;quot; is now available&lt;br /&gt;
var function = bind(AnotherNamespace.ret, Namespace);&lt;br /&gt;
&lt;br /&gt;
# Invoke the new function&lt;br /&gt;
function();&lt;br /&gt;
&lt;br /&gt;
# Print out the value of Namespace.key&lt;br /&gt;
# It was changed to 12 from 6 by AnotherNamespace.ret()&lt;br /&gt;
print(Namespace.key);&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== call() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = call(func[, args[, me[, locals[, error]]]);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=247|t=Source}}&lt;br /&gt;
|text = Calls the given function with the given arguments and returns the result.  This function is very useful as it allows much more control over function calls and catches any errors or {{func link|die()}} calls that would normally trigger run-time errors cancelling execution of the script otherwise. &lt;br /&gt;
|param1 = func&lt;br /&gt;
|param1text = Function to execute.&lt;br /&gt;
|param2 = args&lt;br /&gt;
|param2text = Vector containing arguments to give to the called function.&lt;br /&gt;
|param3 = me&lt;br /&gt;
|param3text = &amp;lt;code&amp;gt;'''me'''&amp;lt;/code&amp;gt; reference for the function call (i.e., for method calls). If given, this will override any &amp;lt;code&amp;gt;'''me'''&amp;lt;/code&amp;gt; value existing in the namespace (locals argument).&lt;br /&gt;
|param4 = locals&lt;br /&gt;
|param4text = A hash with key/value pairs that will be available to the called function, typically used as the namespace for the function to be called.&lt;br /&gt;
|param5 = error&lt;br /&gt;
|param5text = A vector to append errors to.  If the called function generates an error, the error, place, and line will be written to this.  These errors can be printed using {{func link|printerror()|debug}}.&lt;br /&gt;
|example1 =&lt;br /&gt;
# prints &amp;quot;Called from call()&amp;quot;&lt;br /&gt;
call(func {&lt;br /&gt;
    print(&amp;quot;Called from call()&amp;quot;);&lt;br /&gt;
});&lt;br /&gt;
|example2 =&lt;br /&gt;
# prints &amp;quot;a = 1 : b = 2&lt;br /&gt;
call(func(a, b){&lt;br /&gt;
        print(&amp;quot;a = &amp;quot;, a, &amp;quot; : b = &amp;quot;, b);&lt;br /&gt;
    },&lt;br /&gt;
    [1, 2]&lt;br /&gt;
);&lt;br /&gt;
|example3 =&lt;br /&gt;
var Hash = {&lt;br /&gt;
    new: func {&lt;br /&gt;
        var m = { parents: [Hash] };&lt;br /&gt;
&lt;br /&gt;
        m.el1 = &amp;quot;string1&amp;quot;;&lt;br /&gt;
        m.el2 = &amp;quot;string2&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
        return m;&lt;br /&gt;
    }&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
# prints &amp;quot;me.el1 = string1&amp;quot;, then &amp;quot;me.el2 = string2&amp;quot; on the next line&lt;br /&gt;
call(func(a, b){        &lt;br /&gt;
        print(&amp;quot;me.el&amp;quot;, a, &amp;quot; = &amp;quot;, me[&amp;quot;el&amp;quot; ~ a]);      &lt;br /&gt;
        print(&amp;quot;me.el&amp;quot;, b, &amp;quot; = &amp;quot;, me[&amp;quot;el&amp;quot; ~ b]);&lt;br /&gt;
    },&lt;br /&gt;
    [1, 2],&lt;br /&gt;
    Hash.new()&lt;br /&gt;
);&lt;br /&gt;
|example4 =&lt;br /&gt;
# prints the value of math.pi&lt;br /&gt;
call(func {&lt;br /&gt;
        print(pi);&lt;br /&gt;
    }, nil, nil, &lt;br /&gt;
    math&lt;br /&gt;
);&lt;br /&gt;
|example5 =&lt;br /&gt;
call(func {&lt;br /&gt;
        print(math.ip); # math.ip doesn't exist&lt;br /&gt;
    }, nil, nil, nil,&lt;br /&gt;
    var errs = []&lt;br /&gt;
);&lt;br /&gt;
debug.printerror(errs); # The error is caught and printed using debug.printerror()&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== caller() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = caller([level]);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=404|t=Source}}&lt;br /&gt;
|text = Returns a vector containing a record from the current call stack.  The level numbering starts from the currently executing function (level 0).  Level 1 (the default) is the caller of the current function, and so on.&lt;br /&gt;
&lt;br /&gt;
The result is a four-element vector containing '''[0]''' a hash of local variables, '''[1]''' the function object, '''[2]''' the full source file name (incl. path) and '''[3]''' the line number. &lt;br /&gt;
|param1 = level&lt;br /&gt;
|param1text = Optional integer specifying the stack level to return a result from.  Defaults to 1 (i.e. the caller of the currently executing function).&lt;br /&gt;
|example1 =&lt;br /&gt;
var myFunction = func(a, b){&lt;br /&gt;
    debug.dump(caller(0)[0]); # prints a hash of local variables, including arguments a and b&lt;br /&gt;
    return 2 * 2;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;2 x 2 = &amp;quot;, myFunction(2, 2));&lt;br /&gt;
|example2 =&lt;br /&gt;
var get_arg_value = func(){&lt;br /&gt;
    print(&amp;quot;Argument to myFunc = &amp;quot;, caller(1)[0]['a']); # print the value of myFunc's single argument, using caller()&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
var myFunc = func(a){&lt;br /&gt;
    get_arg_value();&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
myFunc(3);&lt;br /&gt;
|example3text = This is a real example taken from {{fgdata file|Nasal/canvas/MapStructure.nas}}.  Function &amp;lt;code&amp;gt;r()&amp;lt;/code&amp;gt; (above the TODOs) returns a hash with the key/value pairs as per its arguments. For example, something like this is returned: &amp;lt;code&amp;gt;{ name: &amp;quot;&amp;lt;name&amp;gt;&amp;quot;, vis: 1, zindex: nil }&amp;lt;/code&amp;gt;.&lt;br /&gt;
|example3 =&lt;br /&gt;
var MapStructure_selfTest = func() {&lt;br /&gt;
	var temp = {};&lt;br /&gt;
	temp.dlg = canvas.Window.new([600,400],&amp;quot;dialog&amp;quot;);&lt;br /&gt;
	temp.canvas = temp.dlg.createCanvas().setColorBackground(1,1,1,0.5);&lt;br /&gt;
	temp.root = temp.canvas.createGroup();&lt;br /&gt;
	var TestMap = temp.root.createChild(&amp;quot;map&amp;quot;);&lt;br /&gt;
	TestMap.setController(&amp;quot;Aircraft position&amp;quot;);&lt;br /&gt;
	TestMap.setRange(25); # TODO: implement zooming/panning via mouse/wheel here, for lack of buttons :-/&lt;br /&gt;
	TestMap.setTranslation(&lt;br /&gt;
		temp.canvas.get(&amp;quot;view[0]&amp;quot;)/2,&lt;br /&gt;
		temp.canvas.get(&amp;quot;view[1]&amp;quot;)/2&lt;br /&gt;
	);&lt;br /&gt;
	var r = func(name,vis=1,zindex=nil) return caller(0)[0];&lt;br /&gt;
	# TODO: we'll need some z-indexing here, right now it's just random&lt;br /&gt;
	# TODO: use foreach/keys to show all layers in this case by traversing SymbolLayer.registry direclty ?&lt;br /&gt;
	# maybe encode implicit z-indexing for each lcontroller ctor call ? - i.e. preferred above/below order ?&lt;br /&gt;
	foreach(var type; [r('TFC',0),r('APT'),r('DME'),r('VOR'),r('NDB'),r('FIX',0),r('RTE'),r('WPT'),r('FLT'),r('WXR'),r('APS'), ] ) &lt;br /&gt;
		TestMap.addLayer(factory: canvas.SymbolLayer, type_arg: type.name,&lt;br /&gt;
					visible: type.vis, priority: type.zindex,&lt;br /&gt;
		);&lt;br /&gt;
}; # MapStructure_selfTest&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== chr() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = chr(code);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=175|t=Source}}&lt;br /&gt;
|text = Returns a character as per the single argument. Extended ASCII is supported (see http://www.asciitable.com/ for a list of supported characters), although this may vary between different systems.  For a list of the most commonly used characters, see the {{wikipedia|ASCII#ASCII printable code chart|ASCII printable code chart}} ('''Dec''' column). The following table lists supported control characters, along with their equivalent control characters in Nasal strings.  {{Note|In Nasal, only strings enclosed with double-quotes (&amp;lt;code&amp;gt;&amp;quot;string&amp;quot;&amp;lt;/code&amp;gt;) supports control chracters.  Strings in single quotes (&amp;lt;code&amp;gt;'string'&amp;lt;/code&amp;gt;) do not.}}&lt;br /&gt;
{{{!}} class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Code !! Name !! Equivalent to&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} 10 {{!!}} {{Wikipedia|Newline}} {{!!}} &amp;lt;code&amp;gt;\n&amp;lt;/code&amp;gt;&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} 9 {{!!}} {{Wikipedia|Tab key#Tab characters|Horizontal tab}} {{!!}} &amp;lt;code&amp;gt;\t&amp;lt;/code&amp;gt;&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} 13 {{!!}} {{Wikipedia|Carriage return}} {{!!}} &amp;lt;code&amp;gt;\r&amp;lt;/code&amp;gt;&lt;br /&gt;
{{!}}}&lt;br /&gt;
|param1 = code&lt;br /&gt;
|param1text = Integer character code for the desired glyph.&lt;br /&gt;
|example1 = print(&amp;quot;Code 65 = &amp;quot;, chr(65)); # prints &amp;quot;Code 65 = A&amp;quot;&lt;br /&gt;
|example2text = This example displays all of the characters in a list, in the format &amp;lt;code&amp;gt;Code '''n''' = &amp;gt;'''char'''&amp;lt;&amp;lt;/code&amp;gt;, '''n''' being the index, and '''char''' being the character.&lt;br /&gt;
|example2 =&lt;br /&gt;
for(var i = 0; i &amp;lt;= 255; i += 1){&lt;br /&gt;
    print(&amp;quot;Code &amp;quot;, i, &amp;quot; = &amp;gt;&amp;quot;, chr(i), &amp;quot;&amp;lt;&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== closure() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = closure(func[, level]);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=421|t=Source}}&lt;br /&gt;
|text = Returns the hash table containing the lexical namespace of the given function. The level numbering start with level 0 being the namespace of '''func'''. &lt;br /&gt;
|param1 = func&lt;br /&gt;
|param1text = Function to evaluate.&lt;br /&gt;
|param2 = level&lt;br /&gt;
|param2text = Optional integer specifying the scope level.  Defaults to 0 (the namespace of '''func''').&lt;br /&gt;
|example1 =&lt;br /&gt;
var get_math_e = func {&lt;br /&gt;
    return e; # return the value of math.e&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var myFunction = bind(get_math_e, math); # bind get_math_e to the math namespace, so that math.e is immediately available to get_math_e&lt;br /&gt;
debug.dump(closure(myFunction)); # print the namespace of get_math_e&lt;br /&gt;
&lt;br /&gt;
print(myFunction());&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== cmp() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = cmp(a, b);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=112|t=Source}}&lt;br /&gt;
|text = Compares two strings, returning -1 if '''a''' is less than '''b''', 0 if they are identical and 1 if '''a''' is greater than '''b'''. &lt;br /&gt;
|param1 = a&lt;br /&gt;
|param1text = First string argument for comparison.&lt;br /&gt;
|param2 = b&lt;br /&gt;
|param2text = Second string argument for comparison.&lt;br /&gt;
|example1 = print(cmp(&amp;quot;1&amp;quot;, &amp;quot;two&amp;quot;)); # prints -1&lt;br /&gt;
|example2 = print(cmp(&amp;quot;string&amp;quot;, &amp;quot;string&amp;quot;)); # prints 0&lt;br /&gt;
|example3 = print(cmp(&amp;quot;one&amp;quot;, &amp;quot;2&amp;quot;)); # prints 1&lt;br /&gt;
|example4 = print(cmp(&amp;quot;string1&amp;quot;, &amp;quot;string2&amp;quot;)); # prints -1&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== compile() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = compile(code[, filename]);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=220|t=Source}}&lt;br /&gt;
|text = Compiles the specified code string and returns a function object bound to the current lexical context.  If there is an error, the function dies, with the argument to {{func link|die()}} being '''filename'''.&lt;br /&gt;
|param1 = code&lt;br /&gt;
|param1text = String containing Nasal code to be compiled.&lt;br /&gt;
|param2 = filename&lt;br /&gt;
|param2text = Optional string used for error messages/logging. Defaults to &amp;lt;code&amp;gt;&amp;lt;compile&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
|example1 = &lt;br /&gt;
var myCode = 'print(&amp;quot;hello&amp;quot;);';&lt;br /&gt;
var helloFunc = compile(myCode, &amp;quot;myCode&amp;quot;);&lt;br /&gt;
helloFunc();&lt;br /&gt;
|example2text = &amp;lt;code&amp;gt;compile&amp;lt;/code&amp;gt; is very convenient to support Nasal loaded from other files.  For instance, [[PropertyList XML files]] (such as GUI dialogs) may contain embedded Nasal sections that need to be parsed, processed and compiled.  For an example of how to do this, save the below XML code as &amp;lt;tt&amp;gt;''[[$FG_ROOT]]/gui/dialogs/test.xml''&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?xml version=&amp;quot;1.0&amp;quot;?&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;PropertyList&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nasal&amp;gt;&amp;lt;![CDATA[&lt;br /&gt;
print(&amp;quot;You have FlightGear v&amp;quot;, getprop(&amp;quot;/sim/version/flightgear&amp;quot;));&lt;br /&gt;
]]&amp;gt;&amp;lt;/nasal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/PropertyList&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Now, start FlightGear and execute this code in the [[Nasal Console]].&lt;br /&gt;
|example2 =&lt;br /&gt;
# Build the path&lt;br /&gt;
var FGRoot = getprop(&amp;quot;/sim/fg-root&amp;quot;);&lt;br /&gt;
var filename = &amp;quot;/gui/dialogs/test.xml&amp;quot;;&lt;br /&gt;
var path = FGRoot ~ filename;&lt;br /&gt;
&lt;br /&gt;
var blob = io.read_properties(path);&lt;br /&gt;
var script = blob.getValues().nasal; # Get the nasal string&lt;br /&gt;
&lt;br /&gt;
# Compile the script.  We're passing the filename here for better runtime diagnostics &lt;br /&gt;
var code = call(func {&lt;br /&gt;
    compile(script, filename);&lt;br /&gt;
}, nil, nil, var compilation_errors = []);&lt;br /&gt;
&lt;br /&gt;
if(size(compilation_errors)){&lt;br /&gt;
    die(&amp;quot;Error compiling code in: &amp;quot; ~ filename);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# Invoke the compiled script, equivalent to code(); &lt;br /&gt;
# We're using call() here to detect errors:&lt;br /&gt;
call(code, [], nil, nil, var runtime_errors = []);&lt;br /&gt;
&lt;br /&gt;
if(size(runtime_errors)){&lt;br /&gt;
    die(&amp;quot;Error calling code compiled loaded from: &amp;quot; ~ filename);&lt;br /&gt;
}&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== contains() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = contains(hash, key);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=184|t=Source}}&lt;br /&gt;
|text = Returns 1 (True) if the hash contains the specified key, or 0 (False) if not.&lt;br /&gt;
|param1 = hash&lt;br /&gt;
|param1text = The hash to search in.&lt;br /&gt;
|param2 = key&lt;br /&gt;
|param2text = The scalar to be searched for, contained as a key in the hash.&lt;br /&gt;
|example1 =&lt;br /&gt;
# Initialize a hash&lt;br /&gt;
var hash = {&lt;br /&gt;
    element: &amp;quot;value&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
print(contains(hash, &amp;quot;element&amp;quot;) ? &amp;quot;Yes&amp;quot; : &amp;quot;No&amp;quot;); # This will print &amp;quot;Yes&amp;quot;&lt;br /&gt;
|example2 =&lt;br /&gt;
# Initialize a hash&lt;br /&gt;
var hash = {&lt;br /&gt;
    element: &amp;quot;value&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
print(contains(hash, &amp;quot;element2&amp;quot;) ? &amp;quot;Yes&amp;quot; : &amp;quot;No&amp;quot;); # This will print &amp;quot;No&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===contains()===&lt;br /&gt;
{{Requires commit|desc=Vector support|details=see {{Merge-request|project=fgdata|id=305}}|commit=ee39abbd3b70c9b6d5e3a1c4ccedddaac1a92b11|repo=Simgear}}&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = contains(vector, item);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=184|t=Source}}&lt;br /&gt;
|text = Returns 1 (True) if the vector contains the specified item, or 0 (False) if not.&lt;br /&gt;
|param1 = vector&lt;br /&gt;
|param1text = The vector to search in.&lt;br /&gt;
|param2 = item&lt;br /&gt;
|param2text = The object to be searched for in the vector.&lt;br /&gt;
|example1 =&lt;br /&gt;
# Initialize a hash&lt;br /&gt;
var vec = [&amp;quot;element&amp;quot;, &amp;quot;foo&amp;quot;];&lt;br /&gt;
print(contains(hash, &amp;quot;element&amp;quot;) ? &amp;quot;Yes&amp;quot; : &amp;quot;No&amp;quot;); # This will print &amp;quot;Yes&amp;quot;&lt;br /&gt;
|example2 =&lt;br /&gt;
# Initialize a hash&lt;br /&gt;
var vec = [&amp;quot;element&amp;quot;, &amp;quot;foo&amp;quot;];&lt;br /&gt;
print(contains(hash, &amp;quot;element2&amp;quot;) ? &amp;quot;Yes&amp;quot; : &amp;quot;No&amp;quot;); # This will print &amp;quot;No&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===delete() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = delete(hash, key);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=83|t=Source}}&lt;br /&gt;
|text = Deletes the key from the hash if it exists. Operationally, this is NOT identical to setting the hash value specified by the key to &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; as the key will stay in the hash (at least for a while). This variant potentially frees storage by deleting the reference to the key and by shrinking the hash.  Returns the hash that has been operated on.&lt;br /&gt;
|param1 = hash&lt;br /&gt;
|param1text = The hash from which to delete the key.&lt;br /&gt;
|param2 = key&lt;br /&gt;
|param2text = The scalar to be deleted, contained as a key in the hash.&lt;br /&gt;
|example1 =&lt;br /&gt;
# Initialize the hash&lt;br /&gt;
var hash = {&lt;br /&gt;
    element1: &amp;quot;value1&amp;quot;,&lt;br /&gt;
    element2: &amp;quot;value2&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
delete(hash, &amp;quot;element1&amp;quot;); # Delete element1&lt;br /&gt;
debug.dump(hash); # prints the hash, which is now minus element1&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===die()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = die(error);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=288|t=Source}}&lt;br /&gt;
|text = Terminates execution and unwinds the stack.  The place and the line will be added to the '''error'''.  This invokes the same internal exception handler used for internal runtime errors. Use this to signal fatal errors, or to implement exception handling. The error thrown (including internal runtime errors) can be caught with {{func link|call()}}.&lt;br /&gt;
|param1 = error&lt;br /&gt;
|param1text = String describing the error.&lt;br /&gt;
:{{inote|This parameter is technically optional, but it is highly recommended to use it.}}&lt;br /&gt;
|example1 = &lt;br /&gt;
print(&amp;quot;Will print&amp;quot;);&lt;br /&gt;
die(&amp;quot;Don't go any further!&amp;quot;); &lt;br /&gt;
print(&amp;quot;Won't print&amp;quot;); # Will not be printed because die() stops the process&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== find()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = find(needle, haystack);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=450|t=Source}}&lt;br /&gt;
|text = Finds and returns the index of the first occurrence of the string '''needle''' in the string '''haystack''', or -1 if no such occurrence was found.&lt;br /&gt;
|param1 = needle&lt;br /&gt;
|param1text = String to search for.&lt;br /&gt;
|param2 = haystack&lt;br /&gt;
|param2text = String to search in.&lt;br /&gt;
|example1 = print(find(&amp;quot;c&amp;quot;, &amp;quot;abcdef&amp;quot;)); # prints 2&lt;br /&gt;
|example2 = print(find(&amp;quot;x&amp;quot;, &amp;quot;abcdef&amp;quot;)); # prints -1&lt;br /&gt;
|example3 = print(find(&amp;quot;cd&amp;quot;, &amp;quot;abcdef&amp;quot;)); # prints 2&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===ghosttype()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = ghosttype(ghost);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=207|t=Source}}&lt;br /&gt;
|text = Returns a string containing either a descriptive name of a ghost (a raw C/C++ object), or a unique id (the pointer to the C/C++ &amp;lt;code&amp;gt;naGhostType&amp;lt;/code&amp;gt; instance) if no name has been set.  Ghost is an acronym that stands for '''G'''arbage-collected '''H'''andle to '''O'''ut'''S'''ide '''T'''hingy.&lt;br /&gt;
|param1 = ghost&lt;br /&gt;
|param1text = Ghost to return a description for.&lt;br /&gt;
|example1 = print(ghosttype(airportinfo())); # prints &amp;quot;airport&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===id()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = id(object);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=570|t=Source}}&lt;br /&gt;
|text = Returns a string containing information on the type and ID of the object provided in the single argument.  The information is returned in the form of &amp;lt;code&amp;gt;'''&amp;lt;type&amp;gt;''':'''&amp;lt;id&amp;gt;'''&amp;lt;/code&amp;gt;, where '''&amp;lt;type&amp;gt;''' is the type of object, and '''&amp;lt;id&amp;gt;''' is the ID.&lt;br /&gt;
|param1 = object&lt;br /&gt;
|param1text = Can be either of a string, a vector, a hash, a code, a function, or a ghost.&lt;br /&gt;
|example1 = print(id(&amp;quot;A&amp;quot;)); # prints &amp;quot;str:000000001624A590&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===int() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = int(number);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=90|t=Source}}&lt;br /&gt;
|text = Returns the integer part of the numeric value of the single argument, or &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; if none exists.&lt;br /&gt;
|param1 = number&lt;br /&gt;
|param1text = Number or string with just a number in it to return an integer from.&lt;br /&gt;
|example1 = print(int(23)); # prints &amp;quot;23&amp;quot;&lt;br /&gt;
|example2 = print(int(23.123)); # prints &amp;quot;23&amp;quot;&lt;br /&gt;
|example3 = debug.dump(int(&amp;quot;string&amp;quot;)); # prints &amp;quot;nil&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===keys()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = keys(hash);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=33|t=Source}}&lt;br /&gt;
|text = Returns a vector containing the list of keys found in the single hash argument. &lt;br /&gt;
|param1 = hash&lt;br /&gt;
|param1text = The hash to return the keys from.&lt;br /&gt;
|example1 = &lt;br /&gt;
# Initialize a hash&lt;br /&gt;
var hash = {&lt;br /&gt;
    element1: &amp;quot;value&amp;quot;,&lt;br /&gt;
    element2: &amp;quot;value&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
debug.dump(keys(hash)); # print the vector&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===left()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = left(string, length);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=149|t=Source}}&lt;br /&gt;
|version = 2.12&lt;br /&gt;
|commit = {{simgear commit|bd7163|t=commit}}&lt;br /&gt;
|text = Returns a substring of '''string''', starting from the left.&lt;br /&gt;
|param1 = string&lt;br /&gt;
|param1text = String to return part of.&lt;br /&gt;
|param2 = length&lt;br /&gt;
|param2text = Integer specifying the length of the substring to return.&lt;br /&gt;
|example1 = print(left(&amp;quot;string&amp;quot;, 2)); # prints &amp;quot;st&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== num()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = num(number);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=102|t=Source}}&lt;br /&gt;
|text = Returns the numerical value of the single string argument, or &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; if none exists. &lt;br /&gt;
|param1 = number&lt;br /&gt;
|param1text = String with just a number in it to return a number from.&lt;br /&gt;
|example1 = print(num(&amp;quot;23&amp;quot;)); # prints &amp;quot;23&amp;quot;&lt;br /&gt;
|example2 = print(num(&amp;quot;23.123&amp;quot;)); # prints &amp;quot;23.123&amp;quot;&lt;br /&gt;
|example3 = debug.dump(num(&amp;quot;string&amp;quot;)); # prints &amp;quot;nil&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===pop()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = pop(vector);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=50|t=Source}}&lt;br /&gt;
|text = Removes and returns the last element of the single vector argument, or &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; if the vector is empty. &lt;br /&gt;
|param1 = vector&lt;br /&gt;
|param1text = Vector to remove an element from.&lt;br /&gt;
|example1 = &lt;br /&gt;
var vector = [1, 2, 3];&lt;br /&gt;
pop(vector);&lt;br /&gt;
debug.dump(vector); # prints &amp;quot;[1, 2]&amp;quot;&lt;br /&gt;
|example2 = &lt;br /&gt;
var vector = [1, 2, 3];&lt;br /&gt;
debug.dump(pop(vector)); # prints &amp;quot;3&amp;quot;&lt;br /&gt;
|example3 = &lt;br /&gt;
var vector = [];&lt;br /&gt;
debug.dump(pop(vector)); # prints &amp;quot;nil&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===right()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = right(string, length);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=161|t=Source}}&lt;br /&gt;
|version = 2.12&lt;br /&gt;
|commit = {{simgear commit|bd7163|t=commit}}&lt;br /&gt;
|text = Returns a substring of '''string''', starting from the right.&lt;br /&gt;
|param1 = string&lt;br /&gt;
|param1text = String to return part of.&lt;br /&gt;
|param2 = length&lt;br /&gt;
|param2text = Integer specifying the length of the substring to return.&lt;br /&gt;
|example1 = print(right(&amp;quot;string&amp;quot;, 2)); # prints &amp;quot;ng&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== setsize()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = setsize(vector, size);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=56|t=Source}}&lt;br /&gt;
|text = Sets the size of a vector. The first argument specifies a vector, the second a number representing the desired size of that vector. If the vector is currently larger than the specified size, it is truncated. If it is smaller, it is padded with &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; entries. Returns the vector operated upon. &lt;br /&gt;
|param1 = vector&lt;br /&gt;
|param1text = The vector to be operated on.&lt;br /&gt;
|param2 = size&lt;br /&gt;
|param2text = The desired size of the vector in number of entries.&lt;br /&gt;
|example1 = &lt;br /&gt;
var vector = [1, 2, 3]; # Initialize a vector&lt;br /&gt;
setsize(vector, 4);&lt;br /&gt;
debug.dump(vector); # print the vector&lt;br /&gt;
|example2 = &lt;br /&gt;
var vector = [1, 2, 3]; # Initialize a vector&lt;br /&gt;
setsize(vector, 2);&lt;br /&gt;
debug.dump(vector); # print the vector&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===size()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = size(object);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=23|t=Source}}&lt;br /&gt;
|text = Returns the size of the single argument. For strings, this is the length in bytes. For vectors, this is the number of elements. For hashes, it is the number of key/value pairs. If the argument is &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; or a number, this error will be thrown: &amp;lt;code&amp;gt;object has no size()&amp;lt;/code&amp;gt;.&lt;br /&gt;
|param1 = object&lt;br /&gt;
|param1text = Object to find the size of.  Must be a string, a vector or a hash.&lt;br /&gt;
|example1 = &lt;br /&gt;
var string = &amp;quot;string&amp;quot;;&lt;br /&gt;
print(size(string)); # prints &amp;quot;6&amp;quot;&lt;br /&gt;
|example2 =&lt;br /&gt;
var vector = [1, 2, 3];&lt;br /&gt;
print(size(vector)); # prints &amp;quot;3&amp;quot;&lt;br /&gt;
|example3 =&lt;br /&gt;
var hash = {&lt;br /&gt;
    element1: &amp;quot;value1&amp;quot;,&lt;br /&gt;
    element2: &amp;quot;value2&amp;quot;,&lt;br /&gt;
    element3: &amp;quot;value3&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
print(size(hash)); # prints &amp;quot;3&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== sort()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = sort(vector, function);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=542|t=Source}}&lt;br /&gt;
|text = Returns a vector containing the elements in the input '''vector''' sorted in according to the rule given by '''function'''. Implemented with the ANSI C {{func link|qsort()|link=http://www.cplusplus.com/reference/cstdlib/qsort/}}, &amp;lt;code&amp;gt;sort()&amp;lt;/code&amp;gt; is stable.  This means that if the rules in the first example are used, equal elements in the output vector will appear in the same relative order as they do in the input.  It is run in a loop, so '''function''' is run several times.&lt;br /&gt;
|param1 = vector&lt;br /&gt;
|param1text = Input vector to sort.&lt;br /&gt;
|param2 = function&lt;br /&gt;
|param2text = Function according to which the elements will be sorted by.  It should take two arguments and should return one of 1, 0, or -1.&lt;br /&gt;
{{{!}} class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Return value !! Meaning&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} less than 0 {{!!}} first argument should go before second argument&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} 0 {{!!}} first argument equals second argument&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} greater than 0 {{!!}} first argument should go after second argument&lt;br /&gt;
{{!}}}&lt;br /&gt;
&lt;br /&gt;
|example1text = This example sorts elements from smallest to greatest.&lt;br /&gt;
|example1 = &lt;br /&gt;
var sort_rules = func(a, b){&lt;br /&gt;
    if(a &amp;lt; b){&lt;br /&gt;
        return -1; # A should before b in the returned vector&lt;br /&gt;
    }elsif(a == b){&lt;br /&gt;
        return 0; # A is equivalent to b &lt;br /&gt;
    }else{&lt;br /&gt;
        return 1; # A should after b in the returned vector&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
debug.dump(sort([3, 2, 5, 6, 4, 1], sort_rules)); # prints &amp;quot;[1, 2, 3, 4, 5, 6]&amp;quot;&lt;br /&gt;
|example2text = This example sorts elements from greatest to smallest.&lt;br /&gt;
|example2 = &lt;br /&gt;
# Outputs the elements in reverse order (greatest to smallest)&lt;br /&gt;
var sort_rules = func(a, b){&lt;br /&gt;
    if(a &amp;lt; b){&lt;br /&gt;
        return 1; # -1 in the above example&lt;br /&gt;
    }elsif(a == b){&lt;br /&gt;
        return 0;&lt;br /&gt;
    }else{&lt;br /&gt;
        return -1; # 1 in the above example&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
debug.dump(sort([3, 2, 5, 6, 4, 1], sort_rules)); # prints &amp;quot;[6, 5, 4, 3, 2, 1]&amp;quot;&lt;br /&gt;
|example3text = This example sorts a vector of strings (runways for example) from smallest to greatest.&lt;br /&gt;
|example3 = &lt;br /&gt;
var runways = [&amp;quot;09R&amp;quot;,&amp;quot;27R&amp;quot;,&amp;quot;26L&amp;quot;,&amp;quot;09L&amp;quot;,&amp;quot;15&amp;quot;];&lt;br /&gt;
var rwy = sort(runways,func(a,b) cmp(a,b));&lt;br /&gt;
debug.dump(rwy); # prints ['09L','09R','15','26L','27R']&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== split()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = split(delimiter, string);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=460|t=Source}}&lt;br /&gt;
|text = Splits the input string into a vector of substrings bounded by occurrences of the delimiter substring.&lt;br /&gt;
|param1 = delimiter&lt;br /&gt;
|param1text = String that will split the substrings in the returned vector.&lt;br /&gt;
|param2 = string&lt;br /&gt;
|param2text = String to split up.&lt;br /&gt;
|example1 = debug.dump(split(&amp;quot;cd&amp;quot;, &amp;quot;abcdef&amp;quot;)); # prints &amp;quot;['ab', 'ef']&amp;quot;&lt;br /&gt;
|example2 = debug.dump(split(&amp;quot;.&amp;quot;, &amp;quot;3.2.0&amp;quot;)); # prints &amp;quot;[3, 2, 0]&amp;quot;&lt;br /&gt;
|example3 = debug.dump(split(&amp;quot;/&amp;quot;, &amp;quot;path/to/file&amp;quot;)); # prints &amp;quot;['path', 'to', 'file']&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===sprintf()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = &amp;lt;nowiki&amp;gt;sprintf(format[, arg[, arg, [...]]]);&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=355|t=Source}}&lt;br /&gt;
|text = Creates and returns a string formatted using ANSI C {{func link|vsnprintf()|link=http://en.cppreference.com/w/c/io/vfprintf}} &amp;lt;ref&amp;gt;&lt;br /&gt;
{{Cite web&lt;br /&gt;
|url = http://sourceforge.net/p/flightgear/simgear/ci/next/tree/simgear/nasal/lib.c#l308&lt;br /&gt;
|title = fgdata/simgear/simgear/nasal/lib.c, line 308&lt;br /&gt;
|accessdate = October 2015&lt;br /&gt;
}}&lt;br /&gt;
&amp;lt;/ref&amp;gt;.  Below is a table of supported format specifiers.&lt;br /&gt;
{{{!}} class=&amp;quot;wikitable&amp;quot; width=&amp;quot;75%&amp;quot;&lt;br /&gt;
{{!}}+ %[flags][width][.precision]specifier&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; {{!}} Flags&lt;br /&gt;
{{!-}}&lt;br /&gt;
! Flag !! Output&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;+&amp;lt;/code&amp;gt; {{!!}} Forces to precede the result with a plus or minus sign ('''+''' or '''-''') even for positive numbers. By default, only negative numbers are preceded with a '''-''' sign.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} ''space'' {{!!}} Prefixes non-signed numbers with a space.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;-&amp;lt;/code&amp;gt; {{!!}} Left-align the output of this placeholder (the default is to right-align the output) when the width option is specified.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt; {{!!}} Use 0 instead of spaces to pad a field when the width option is specified.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;#&amp;lt;/code&amp;gt; {{!!}} Used with &amp;lt;code&amp;gt;o&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;X&amp;lt;/code&amp;gt; specifiers the value is preceded with &amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;0x&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;0X&amp;lt;/tt&amp;gt; respectively for values different than zero. Used with &amp;lt;code&amp;gt;e&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;E&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;f&amp;lt;/code&amp;gt;, it forces the written output to contain a decimal point even if no digits would follow. By default, if no digits follow, no decimal point is written. Used with &amp;lt;code&amp;gt;g&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;G&amp;lt;/code&amp;gt; the result is the same as with &amp;lt;code&amp;gt;e&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;E&amp;lt;/code&amp;gt; but trailing zeros are not removed.&lt;br /&gt;
{{!-}}&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; {{!}} Width&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} colspan=&amp;quot;2&amp;quot; {{!}} Integer specifying the minimum number of characters to be returned. This includes the decimal point and decimal fraction as well as + or - signs.&lt;br /&gt;
{{!-}}&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; {{!}} Precision&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} colspan=&amp;quot;2&amp;quot; {{!}} Integer preceded by a dot specifying the number of decimal places to be written.&lt;br /&gt;
{{!-}}&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; {{!}} Specifiers&lt;br /&gt;
{{!-}}&lt;br /&gt;
! Specifier !! Output&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;d&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;i&amp;lt;/code&amp;gt; {{!!}} Signed decimal number.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;s&amp;lt;/code&amp;gt; {{!!}} A string&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;%&amp;lt;/code&amp;gt; {{!!}} Percent (%) character.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;c&amp;lt;/code&amp;gt; {{!!}} A single character assigned to a character code, the code given in an integer argument.  See http://www.asciitable.com/ for a list of supported characters and their codes.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;o&amp;lt;/code&amp;gt; {{!!}} Unsigned integer as an octal number.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;u&amp;lt;/code&amp;gt; {{!!}} Unsigned decimal integer.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;X&amp;lt;/code&amp;gt; {{!!}} Unsigned integer as a hexadecimal number.  If &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt; is used, any letters in the number are lowercase, while &amp;lt;code&amp;gt;X&amp;lt;/code&amp;gt; gives uppercase.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;e&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;E&amp;lt;/code&amp;gt; {{!!}} Double value in scientific notation (i.e., ''[-]ddd.ddd'''e'''[+/-]ddd''), with an exponent being denoted by &amp;lt;tt&amp;gt;e&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;E&amp;lt;/tt&amp;gt; depending on whether an upper or lowercase is used respectively.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;f&amp;lt;/code&amp;gt; {{!!}} Floating-point number, in fixed decimal notation, by default with 6 decimal places.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;F&amp;lt;/code&amp;gt; {{!!}} Appears to be available&amp;lt;ref&amp;gt;&lt;br /&gt;
{{Cite web&lt;br /&gt;
|url = http://sourceforge.net/p/flightgear/simgear/ci/next/tree/simgear/nasal/lib.c#l389&lt;br /&gt;
|title = fgdata/simgear/simgear/nasal/lib.c, line 389&lt;br /&gt;
|accessdate = October 2015&lt;br /&gt;
}}&lt;br /&gt;
&amp;lt;/ref&amp;gt;, but doesn't work.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;g&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;G&amp;lt;/code&amp;gt; {{!!}} Double in either normal or exponential notation, whichever is more appropriate for its magnitude. &amp;lt;code&amp;gt;g&amp;lt;/code&amp;gt; uses lower-case letters, &amp;lt;code&amp;gt;G&amp;lt;/code&amp;gt; uses upper-case letters. This type differs slightly from fixed-point notation in that insignificant zeroes to the right of the decimal point are not included.  Also, the decimal point is not included on whole numbers.&lt;br /&gt;
{{!}}}&lt;br /&gt;
&lt;br /&gt;
|param1 = format&lt;br /&gt;
|param1text = String specifying the format.  Can be used with or without a format specifiers.  See below for examples.&lt;br /&gt;
|param2 = arg&lt;br /&gt;
|param2text = Argument specifying a value to replace a format placeholder (such as &amp;lt;code&amp;gt;%d&amp;lt;/code&amp;gt;) in the format string.  Not required if there are no format specifiers.&lt;br /&gt;
&lt;br /&gt;
|example1 = print(sprintf(&amp;quot;%i&amp;quot;, 54)); # prints &amp;quot;54&amp;quot;&lt;br /&gt;
|example2 = print(sprintf(&amp;quot;Pi = %+.10f&amp;quot;, math.pi)); # prints &amp;quot;Pi = +3.1415926536&amp;quot;&lt;br /&gt;
|example3 = &lt;br /&gt;
print(sprintf(&amp;quot;%6d&amp;quot;, 23)); # prints &amp;quot;    23&amp;quot;&lt;br /&gt;
print(sprintf(&amp;quot;%06d&amp;quot;, 23)); # prints &amp;quot;000023&amp;quot;&lt;br /&gt;
|example4 =&lt;br /&gt;
var FGVer = getprop(&amp;quot;/sim/version/flightgear&amp;quot;);&lt;br /&gt;
print(sprintf(&amp;quot;You have FlightGear v%s&amp;quot;, FGVer)); # prints &amp;quot;You have FlightGear v&amp;lt;your version&amp;gt;&amp;quot;&lt;br /&gt;
|example5 = &lt;br /&gt;
print(sprintf(&amp;quot;Hexadecimal 100000 = %X&amp;quot;, 100000)); # prints &amp;quot;Hexadecimal 100000 = 186A0&amp;quot;&lt;br /&gt;
print(sprintf(&amp;quot;Hexadecimal 100000 = %x&amp;quot;, 100000)); # prints &amp;quot;Hexadecimal 100000 = 186a0&amp;quot;&lt;br /&gt;
|example6 = print(sprintf(&amp;quot;Code 65 is %c&amp;quot;, 65)); # prints &amp;quot;Code 65 is A&amp;quot;&lt;br /&gt;
|example7 = &lt;br /&gt;
print(sprintf(&amp;quot;%e&amp;quot;, 54)); # prints &amp;quot;5.400000e+001&amp;quot;&lt;br /&gt;
print(sprintf(&amp;quot;%E&amp;quot;, 54)); # prints &amp;quot;5.400000E+001&amp;quot;&lt;br /&gt;
|example8 = print(sprintf(&amp;quot;%o&amp;quot;, 54)); # prints &amp;quot;66&amp;quot;&lt;br /&gt;
|example9 = print(sprintf(&amp;quot;50%% of 100 is %i&amp;quot;, 100 / 2)); # prints &amp;quot;50% of 100 is 50&amp;quot;&lt;br /&gt;
|example10 =&lt;br /&gt;
print(sprintf(&amp;quot;%.2f&amp;quot;, 1.4));   #prints &amp;quot;1.40&amp;quot;&lt;br /&gt;
print(sprintf(&amp;quot;%.1f&amp;quot;, 1.4));   #prints &amp;quot;1.4&amp;quot;&lt;br /&gt;
print(sprintf(&amp;quot;% 4.1f&amp;quot;, 1.4)); #prints &amp;quot; 1.4&amp;quot;&lt;br /&gt;
print(sprintf(&amp;quot;%04.1f&amp;quot;, 1.4)); #prints &amp;quot;01.4&amp;quot;&lt;br /&gt;
print(sprintf(&amp;quot;% 6.1f&amp;quot;, 1.4)); #prints &amp;quot;   1.4&amp;quot;&lt;br /&gt;
print(sprintf(&amp;quot;%06.1f&amp;quot;, 1.4)); #prints &amp;quot;0001.4&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===streq()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = streq(a, b);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=129|t=Source}}&lt;br /&gt;
|text = Tests the string values of the two arguments for equality. This function is needed because the &amp;lt;code&amp;gt;'''=='''&amp;lt;/code&amp;gt; operator (see [[Nasal_Operators#Equality|Nasal Operators]]) tests for numeric equality first.  If either or both the arguments are not strings, 0 (False) will be returned.  Returns either 0 (False) or 1 (True).  {{Note|This function is rarely required in typical code.}}&lt;br /&gt;
|param1 = a&lt;br /&gt;
|param1text = First argument for testing equality.&lt;br /&gt;
|param2 = b&lt;br /&gt;
|param2text = Second argument for testing equality.&lt;br /&gt;
|example1 = print(streq(&amp;quot;0&amp;quot;, &amp;quot;0&amp;quot;)); # prints &amp;quot;1&amp;quot; (True)&lt;br /&gt;
|example2 = &lt;br /&gt;
print(0 == 0.0); # prints &amp;quot;1&amp;quot; (True)&lt;br /&gt;
print(streq(&amp;quot;0&amp;quot;, &amp;quot;0.0&amp;quot;)); # prints &amp;quot;0&amp;quot; (False)&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===substr()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = substr(string, start [, length]);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=129|t=Source}}&lt;br /&gt;
|text = Similar the {{func link|subvec()}}, but operates on strings. Computes and returns a substring. The first argument specifies a string, the second is the index of the start of a substring, the optional third argument specifies a length (the default is to return the rest of the string from the start).&lt;br /&gt;
|param1 = string&lt;br /&gt;
|param1text = String to return a substring from.&lt;br /&gt;
|param2 = start&lt;br /&gt;
|param2text = Integer specifying the start of a substring. Negative values specify a position from the end of the string.&lt;br /&gt;
|param3 = length&lt;br /&gt;
|param3text = Optional argument specifying the length of the substring. Defaults to the end of the string.&lt;br /&gt;
|example1 = print(substr(&amp;quot;abcde&amp;quot;, 1, 3)); # prints &amp;quot;bcd&amp;quot;&lt;br /&gt;
|example2 = print(substr(&amp;quot;abcde&amp;quot;, 1)); # prints &amp;quot;bcde&amp;quot;&lt;br /&gt;
|example3 = print(substr(&amp;quot;abcde&amp;quot;, 2, 1)); # prints &amp;quot;c&amp;quot;&lt;br /&gt;
|example4 = print(substr(&amp;quot;abcde&amp;quot;, -2)); # prints &amp;quot;de&amp;quot;&lt;br /&gt;
|example5 = print(substr(&amp;quot;abcde&amp;quot;, -3, 2)); # prints &amp;quot;cd&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===subvec()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = subvec(vector, start[, length]);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=63|t=Source}}&lt;br /&gt;
|text = Returns a sub-range of a vector. The first argument specifies a vector, the second a starting index, and the optional third argument indicates a length (the default is to the end of the vector). &lt;br /&gt;
|param1 = vector&lt;br /&gt;
|param1text = The vector to take the sub-vector from.&lt;br /&gt;
|param2 = start&lt;br /&gt;
|param2text = The starting point of the sub-vector within the given vector.&lt;br /&gt;
|param3 = length&lt;br /&gt;
|param3text = Optional argument specifying the length of the sub-vector, from the starting point.&lt;br /&gt;
'''Notes:'''&lt;br /&gt;
* Omitting the ''vector'' and ''start'' arguments is not an error (possibly it should be) but the return value is ''nil''.&lt;br /&gt;
* A negative ''start'' argument ''is'' an error. This seems wrong. Perhaps the language designer could comment.&lt;br /&gt;
* A value of ''start'' greater than ''size(vector)'' causes an error. A value equal to ''size(vector)'' returns an empty vector.&lt;br /&gt;
* If the value of ''length'' is greater than ''size(vector) - start'' then it is ignored. That is, all elements from ''start'' to the end of ''vector'' are returned. If ''length'' is zero then an empty vector is returned. A negative value of ''length'' causes an error.&lt;br /&gt;
|example1 = &lt;br /&gt;
var vector = [1, 2, 3];&lt;br /&gt;
debug.dump(subvec(vector, 0)); # prints &amp;quot;[1, 2, 3]&amp;quot;&lt;br /&gt;
|example2 = &lt;br /&gt;
var vector = [1, 2, 3];&lt;br /&gt;
debug.dump(subvec(vector, 1)); # prints &amp;quot;[2, 3]&amp;quot;&lt;br /&gt;
|example3 = &lt;br /&gt;
var vector = [1, 2, 3];&lt;br /&gt;
debug.dump(subvec(vector, 1, 1)); # prints &amp;quot;[2]&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== typeof()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = typeof(object);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=193|t=Source}}&lt;br /&gt;
|text = Returns a string indicating the whether the object is &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt;, a scalar (number or string), a vector, a hash, a function, or a ghost.&lt;br /&gt;
|param1 = object&lt;br /&gt;
|param1text = Object to return the type of.&lt;br /&gt;
|example1 = &lt;br /&gt;
var object = nil;&lt;br /&gt;
print(typeof(object)); # prints &amp;quot;nil&amp;quot;&lt;br /&gt;
|example2 = &lt;br /&gt;
var object = &amp;quot;Hello world!&amp;quot;;&lt;br /&gt;
print(typeof(object)); # prints &amp;quot;scalar&amp;quot;&lt;br /&gt;
|example3 = &lt;br /&gt;
var object = math.pi;&lt;br /&gt;
print(typeof(object)); # prints &amp;quot;scalar&amp;quot;&lt;br /&gt;
|example4 = &lt;br /&gt;
var object = [1, 2, 3];&lt;br /&gt;
print(typeof(object)); # prints &amp;quot;vector&amp;quot;&lt;br /&gt;
|example5 = &lt;br /&gt;
var object = {};&lt;br /&gt;
print(typeof(object)); # prints &amp;quot;hash&amp;quot;&lt;br /&gt;
|example6 = &lt;br /&gt;
var object = func {};&lt;br /&gt;
print(typeof(object)); # prints &amp;quot;func&amp;quot;&lt;br /&gt;
|example7 =&lt;br /&gt;
var object = airportinfo();&lt;br /&gt;
print(typeof(object)); # prints &amp;quot;ghost&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- == Extension modules ==&lt;br /&gt;
=== thread ===&lt;br /&gt;
{{WIP}}&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = thread.newthread(func);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/threadlib.c|l=101|t=Source}}&lt;br /&gt;
|text = &lt;br /&gt;
|example1text = start a new worker thread&lt;br /&gt;
|example1 = thread.newthread( func() {} );&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = thread.newlock();&lt;br /&gt;
|source = {{simgear file|simgear/nasal/threadlib.c|l=101|t=Source}}&lt;br /&gt;
|text = &lt;br /&gt;
|example1text = create a new lock&lt;br /&gt;
|example1 = var lock = thread.newlock()&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = thread.lock();&lt;br /&gt;
|source = {{simgear file|simgear/nasal/threadlib.c|l=101|t=Source}}&lt;br /&gt;
|text = &lt;br /&gt;
|example1text = lock a lock&lt;br /&gt;
|example1 = var lock = thread.newlock()&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = thread.unlock();&lt;br /&gt;
|source = {{simgear file|simgear/nasal/threadlib.c|l=101|t=Source}}&lt;br /&gt;
|text = &lt;br /&gt;
|example1text = unlock a lock&lt;br /&gt;
|example1 = var lock = thread.unlock()&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = thread.newsem();&lt;br /&gt;
|source = {{simgear file|simgear/nasal/threadlib.c|l=101|t=Source}}&lt;br /&gt;
|text = &lt;br /&gt;
|example1text = create a new {{Wikipedia|semaphore}}&lt;br /&gt;
|example1 = var semaphore = thread.newsem()&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = thread.semdown();&lt;br /&gt;
|source = {{simgear file|simgear/nasal/threadlib.c|l=101|t=Source}}&lt;br /&gt;
|text = &lt;br /&gt;
|example1text = semaphore down&lt;br /&gt;
|example1 = thread.semdown(semaphore)&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = thread.semup();&lt;br /&gt;
|source = {{simgear file|simgear/nasal/threadlib.c|l=101|t=Source}}&lt;br /&gt;
|text = &lt;br /&gt;
|example1text = semaphore up&lt;br /&gt;
|example1 = thread.semup(semaphore)&lt;br /&gt;
}} --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Extension functions==&lt;br /&gt;
The '''extension functions''' are global functions that have been added to Nasal since its integration into FlightGear. Unlike the core library functions, they are generally specifically designed to interact directly with FlightGear. Extension functions come from three source files:&lt;br /&gt;
*{{flightgear file|src/Scripting/NasalPositioned.cxx}}&lt;br /&gt;
*{{flightgear file|src/Scripting/NasalSys.cxx}}&lt;br /&gt;
*{{fgdata file|Nasal/globals.nas}}&lt;br /&gt;
&lt;br /&gt;
===abort()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = abort();&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=565|t=Source}}&lt;br /&gt;
|text = This function is a wrapper for the C++ {{func link|abort()|link=http://www.cplusplus.com/reference/cstdlib/abort/}} function. It simply aborts FlightGear with an error, which varies depending on the operating system. This function should not really be used; instead, please use the &amp;quot;exit&amp;quot; [[Fgcommands|fgcommand]], which will exit FlightGear more gracefully (see example below).&lt;br /&gt;
|example1text = This example will immediately stop FlightGear with an error, such as &amp;quot;FlightGear has stopped working.&amp;quot;&lt;br /&gt;
|example1 = abort();&lt;br /&gt;
|example2text = For exiting FlightGear in a better way, please use the following code:&lt;br /&gt;
|example2 = fgcommand(&amp;quot;exit&amp;quot;);&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== abs() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = abs(number);&lt;br /&gt;
|source = {{fgdata file|Nasal/globals.nas|t=Source}}&lt;br /&gt;
|text = This simple function returns the {{wikipedia|absolute value|noicon=1}} of the provided number.&lt;br /&gt;
|param1 = number&lt;br /&gt;
|param1text = This argument is required and should be a number.&lt;br /&gt;
|example1 = print(abs(1)); # prints &amp;quot;1&amp;quot;&lt;br /&gt;
|example2 = print(abs(-1)); # prints &amp;quot;1&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===aircraftToCart() ===&lt;br /&gt;
This new function in FG 2017.2.1 takes coordinates in aircraft structural coordinate system, and translate them into geocentric coordinates.&lt;br /&gt;
Example for (5,6,7):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
var pos = aircraftToCart({x: -5, y: 6, z: -7});&lt;br /&gt;
var coord = geo.Coord.new();&lt;br /&gt;
coord.set_xyz(pos.x, pos.y, pos.z);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Notice: x and z is inverted sign on purpose.&lt;br /&gt;
if you want lat. lon, alt from that, just call: (degrees and meters)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
coord.lat()&lt;br /&gt;
coord.lon()&lt;br /&gt;
coord.alt()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===addcommand() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = addcommand(name, code);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=659|t=Source}}&lt;br /&gt;
|version = 2.12&lt;br /&gt;
|commit = {{flightgear commit|7b663c|t=commit}}&lt;br /&gt;
|text = {{see also|Howto:Add new fgcommands to FlightGear}}&lt;br /&gt;
&lt;br /&gt;
This function enables the addition of a new custom [[fgcommands|fgcommand]] to FlightGear from within Nasal. An fgcommand created using this method can be used in exactly the same way as the built-in fgcommands. Also, an fgcommand created via this method will always return True or 1, like all other fgcommands.&lt;br /&gt;
|param1 = name&lt;br /&gt;
|param1text = This will become the name of the new fgcommand. Must be a string.&lt;br /&gt;
|param2 = code&lt;br /&gt;
|param2text = The code that will be executed when the fgcommand is run. Must be a function.&lt;br /&gt;
|example1text = This example adds a new fgcommand and then runs it. Although it executes a simple {{func link|print()}} statement, any valid Nasal code can be used.&lt;br /&gt;
|example1 = addcommand(&amp;quot;myFGCmd&amp;quot;, func(node) {&lt;br /&gt;
    print(&amp;quot;fgcommand 'myFGCmd' has been run.&amp;quot;);&lt;br /&gt;
    props.dump( node );&lt;br /&gt;
});&lt;br /&gt;
fgcommand(&amp;quot;myFGCmd&amp;quot;, props.Node.new({foo:1, bar:2}) );&lt;br /&gt;
|example2text = This example demonstrates how parameters are defined in a new fgcommand.&lt;br /&gt;
|example2 = addcommand(&amp;quot;myFGCmd&amp;quot;, func(node){&lt;br /&gt;
    print(node.getNode(&amp;quot;number&amp;quot;).getValue()); # prints the value of &amp;quot;number,&amp;quot; which is 12&lt;br /&gt;
});&lt;br /&gt;
fgcommand(&amp;quot;myFGCmd&amp;quot;, props.Node.new({&amp;quot;number&amp;quot;: 12}));&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===airportinfo()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = airportinfo();&lt;br /&gt;
airportinfo(type);&lt;br /&gt;
airportinfo(id);&lt;br /&gt;
airportinfo(lat, lon[, type]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1024|t=Source}}&lt;br /&gt;
|text = Function for retrieval of airport, heliport, or seaplane base information. It returns a Nasal ghost; however, its structure is like that of a Nasal hash. The following information is returned:&lt;br /&gt;
* '''parents''': A vector containing a hash of various functions to access information about the runway. See {{flightgear file|src/Scripting/NasalPositioned.cxx|l=2659}} for full list.&lt;br /&gt;
* '''lon''': Longitude of the location.&lt;br /&gt;
* '''lat''': Latitude of the location.&lt;br /&gt;
* '''has_metar''': True or false depending whether the airport has a [[METAR]] code defined for it.&lt;br /&gt;
* '''elevation''': Elevation of the location in metres.&lt;br /&gt;
* '''id''': ICAO code of the airport (or ID of the seaplane base/heliport).&lt;br /&gt;
* '''name''': Name of the airport/heliport/seaplane base.&lt;br /&gt;
* '''runways'''&lt;br /&gt;
** '''&amp;lt;runway name&amp;gt;'''&lt;br /&gt;
*** '''id''': Name of runway.&lt;br /&gt;
*** '''lat''': Latitude of the runway.&lt;br /&gt;
*** '''lon''': Longitude of the runway.&lt;br /&gt;
*** '''heading''': Heading of the runway.&lt;br /&gt;
*** '''length''': Length of the runway in metres.&lt;br /&gt;
*** '''width''': Width of the runway in metres.&lt;br /&gt;
*** '''surface''': Runway surface type.&lt;br /&gt;
*** '''threshold''': Length of the runway's {{wikipedia|displaced threshold}} in metres. Will return 0 if there is none.&lt;br /&gt;
*** '''stopway''': Length of the runway's stopway (the area before the threshold) in metres. Will return 0 if there is none.&lt;br /&gt;
*** '''reciprocal''': &amp;lt;code&amp;gt;runway&amp;lt;/code&amp;gt; ghost of the reciprocal runway.&lt;br /&gt;
*** '''ils_frequency_mhz''': ILS frequency in megahertz.&lt;br /&gt;
*** '''ils''': &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt; ghost of the ILS transmitter.&lt;br /&gt;
* '''helipads'''&lt;br /&gt;
** '''&amp;lt;helipad name&amp;gt;'''&lt;br /&gt;
*** '''id''': Name of helipad.&lt;br /&gt;
*** '''lat''': Latitude of the helipad.&lt;br /&gt;
*** '''lon''': Longitude of the helipad.&lt;br /&gt;
*** '''heading''': Heading of the helipad.&lt;br /&gt;
*** '''length''': Length of the helipad in metres.&lt;br /&gt;
*** '''width''': Width of the helipad in metres.&lt;br /&gt;
*** '''surface''': Helipad surface type.&lt;br /&gt;
* '''taxiways'''&lt;br /&gt;
** '''&amp;lt;taxiway name&amp;gt;'''&lt;br /&gt;
*** '''id''': Name of taxiway.&lt;br /&gt;
*** '''lat''': Latitude of the taxiway.&lt;br /&gt;
*** '''lon''': Longitude of the taxiway.&lt;br /&gt;
*** '''heading''': Heading of the taxiway.&lt;br /&gt;
*** '''length''': Length of the taxiway in metres.&lt;br /&gt;
*** '''width''': Width of the taxiway in metres.&lt;br /&gt;
*** '''surface''': Taxiway surface type.&lt;br /&gt;
&lt;br /&gt;
Information is extracted in the same way as accessing members of a Nasal hash. For example:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
# prints to lengths of the runways of the nearest airport in feet and metres&lt;br /&gt;
var info = airportinfo();&lt;br /&gt;
print(&amp;quot;-- Lengths of the runways at &amp;quot;, info.name, &amp;quot; (&amp;quot;, info.id, &amp;quot;) --&amp;quot;);&lt;br /&gt;
foreach(var rwy; keys(info.runways)){&lt;br /&gt;
    print(rwy, &amp;quot;: &amp;quot;, math.round(info.runways[rwy].length * M2FT), &amp;quot; ft (&amp;quot;, info.runways[rwy].length, &amp;quot; m)&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that searches for locations that are a long way away (e.g., the nearest seaplane base to the middle of the Sahara) may cause FlightGear to pause for an amount of time.&lt;br /&gt;
|param1 = id&lt;br /&gt;
|param1text = The {{wikipedia|International Civil Aviation Organization airport code|ICAO code|noicon=1}} of an airport to retrieve information about.&lt;br /&gt;
|param2 = type&lt;br /&gt;
|param2text = When this argument is used, the function will return the closest airport of a certain type. Can be one of &amp;quot;heliport,&amp;quot; &amp;quot;seaport,&amp;quot; or &amp;quot;airport&amp;quot; (default).&lt;br /&gt;
: {{inote|Running this function without any parameters is equivalent to this:&lt;br /&gt;
: &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
airportinfo(&amp;quot;airport&amp;quot;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
|param3 = lat ''and'' lon&lt;br /&gt;
|param3text = When these parameters are used, the function will return information on the nearest airport, heliport or seaplane base (depending on the '''type''' parameter) to those coordinates.&lt;br /&gt;
|example1 = var info = airportinfo();&lt;br /&gt;
print(&amp;quot;Nearest airport: &amp;quot;, info.name, &amp;quot; (&amp;quot;, info.id, &amp;quot;)&amp;quot;); # prints the name and ICAO code of the nearest airport&lt;br /&gt;
|example2 = var info = airportinfo(&amp;quot;heliport&amp;quot;);&lt;br /&gt;
print(&amp;quot;Elevation of the nearest heliport: &amp;quot;, math.round(info.elevation * M2FT), &amp;quot; ft&amp;quot;); # prints the elevation and name of the nearest heliport&lt;br /&gt;
|example3 = var info = airportinfo(&amp;quot;KSQL&amp;quot;);&lt;br /&gt;
print(&amp;quot;-- Runways of &amp;quot;, info.name, &amp;quot; (&amp;quot;, info.id, &amp;quot;): --&amp;quot;);&lt;br /&gt;
foreach(var rwy; keys(info.runways)) {&lt;br /&gt;
    print(rwy); # prints the runways of KSQL&lt;br /&gt;
}&lt;br /&gt;
|example4 = var info = airportinfo(37.81909385, -122.4722484);&lt;br /&gt;
print(&amp;quot;Coordinates of the nearest airport: &amp;quot;, info.lat, &amp;quot;, &amp;quot;, info.lon); # print the name and ICAO of the nearest airport to the Golden Gate Bridge&lt;br /&gt;
|example5 = var info = airportinfo(37.81909385, -122.4722484, &amp;quot;seaport&amp;quot;);&lt;br /&gt;
print(&amp;quot;Nearest seaplane base: &amp;quot;, info.name, &amp;quot; (&amp;quot;, info.id, &amp;quot;)&amp;quot;); # print the name and ID of the nearest seaplane base to the Golden Gate Bridge&lt;br /&gt;
|example6text = This example prints the all information from an &amp;lt;code&amp;gt;airportinfo()&amp;lt;/code&amp;gt; call.&lt;br /&gt;
|example6 = var info = airportinfo(&amp;quot;KSFO&amp;quot;);&lt;br /&gt;
print(info.name);&lt;br /&gt;
print(info.id);&lt;br /&gt;
print(info.lat);&lt;br /&gt;
print(info.lon);&lt;br /&gt;
print(info.has_metar);&lt;br /&gt;
print(info.elevation);&lt;br /&gt;
foreach(var rwy; keys(info.runways)){&lt;br /&gt;
    print(&amp;quot;-- &amp;quot;, rwy, &amp;quot; --&amp;quot;);&lt;br /&gt;
    print(info.runways[rwy].lat);&lt;br /&gt;
    print(info.runways[rwy].lon);&lt;br /&gt;
    print(info.runways[rwy].length);&lt;br /&gt;
    print(info.runways[rwy].width);&lt;br /&gt;
    print(info.runways[rwy].heading);&lt;br /&gt;
    print(info.runways[rwy].stopway);&lt;br /&gt;
    print(info.runways[rwy].threshold);&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===airwaysRoute() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = airwaysRoute(start, end[, type]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1933|t=Source}}&lt;br /&gt;
|text = {{see also|Nasal Flightplan}}&lt;br /&gt;
This function returns a vector containing waypoints between two given waypoints. The returned waypoints are ghosts, but can be accessed in the same way as a Nasal hash. See [[Nasal Flightplan]] for more information.&lt;br /&gt;
|param1 = start&lt;br /&gt;
|param1text = Start waypoint, in the form of a waypoint ghost, such as that provided by {{func link|flightplan()}}.&lt;br /&gt;
|param2 = end&lt;br /&gt;
|param2text = Same as above.&lt;br /&gt;
|param3 = type&lt;br /&gt;
|param3text = Instructs the function to compute a high level route (when set to &amp;quot;highlevel&amp;quot;), or a low level route (when set to &amp;quot;lowlevel&amp;quot;). Defaults to &amp;quot;highlevel.&amp;quot;&lt;br /&gt;
|example1text = In the [[route manager]] dialog, add two waypoints to the flightplan, ideally ones that are far apart (tip: use the [[Map]] for this). Then run this code in your Nasal Console.&lt;br /&gt;
|example1 = var fp = flightplan();&lt;br /&gt;
var start = fp.getWP(0);&lt;br /&gt;
var end = fp.getWP(fp.getPlanSize() - 1);&lt;br /&gt;
var rt = airwaysRoute(start, end);&lt;br /&gt;
foreach(var wp; rt){&lt;br /&gt;
    print(wp.wp_name); # print the waypoints in the computed route&lt;br /&gt;
}&lt;br /&gt;
|example2text = Exactly the same as above, but computes a low level path.&lt;br /&gt;
|example2 = var fp = flightplan();&lt;br /&gt;
var start = fp.getWP(0);&lt;br /&gt;
var end = fp.getWP(fp.getPlanSize() - 1);&lt;br /&gt;
var rt = airwaysRoute(start, end, &amp;quot;lowlevel&amp;quot;);&lt;br /&gt;
foreach(var wp; rt){&lt;br /&gt;
    print(wp.wp_name); # print the waypoints in the computed route&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===airway()===&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = airway(ident [, pos]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=2644|t=Source}}&lt;br /&gt;
|text = {{see also|Nasal Flightplan}}&lt;br /&gt;
This function returns a ghost containing an airway of a specified id.&lt;br /&gt;
|param1 = ident&lt;br /&gt;
|param1text = a Positioned ghost (leg, navaid, airport) and so on, passed to the search function.&lt;br /&gt;
|param2 = pos&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===assert()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = assert(condition[, message]);&lt;br /&gt;
|source = {{fgdata file|Nasal/globals.nas|t=Source}}&lt;br /&gt;
|version = 3.2&lt;br /&gt;
|commit = {{fgdata commit|8b16a7|t=commit}}&lt;br /&gt;
|text = Returns either true if the condition evaluates as true, or aborts with a {{func link|die()}} call, which can be customised.&lt;br /&gt;
|param1 = condition&lt;br /&gt;
|param1text = Condition to evaluate.&lt;br /&gt;
|param2 = message&lt;br /&gt;
|param2text = Optional message that will be used in any {{func link|die()}} call. Defaults to &amp;quot;assertion failed!&amp;quot;&lt;br /&gt;
|example1 = var a = 1;&lt;br /&gt;
var b = 2;&lt;br /&gt;
print(assert(a &amp;lt; b)); # prints &amp;quot;1&amp;quot; (true)&lt;br /&gt;
|example2 = var a = 1;&lt;br /&gt;
var b = 2;&lt;br /&gt;
assert(a &amp;gt; b, 'a is not greater than b'); # aborts with a custom error message&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===carttogeod()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = carttogeod(x, y, z);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=945|t=Source}}&lt;br /&gt;
|text = Converts {{wikipedia|ECEF|Earth-centered, Earth-fixed}} coordinates (x, y and z) to {{wikipedia|geodetic coordinates}} (latitude, longitude, and altitude). A vector is returned containing latitude and longitude, both in degrees, and altitude, which is returned in metres above the equatorial radius of Earth as defined by the {{wikipedia|WGS 84}} (6,378,137 metres).&amp;lt;ref&amp;gt;{{simgear file|simgear/math/sg_geodesy.hxx|l=43}}&amp;lt;/ref&amp;gt;&lt;br /&gt;
|param1 = x&lt;br /&gt;
|param1text = Mandatory x-axis value, in metres.&lt;br /&gt;
|param2 = y&lt;br /&gt;
|param2text = Mandatory y-axis value, in metres.&lt;br /&gt;
|param3 = z&lt;br /&gt;
|param3text = Mandatory z-axis value, in metres.&lt;br /&gt;
|example1 = var (lat, lon, alt) = carttogeod(6378137, 0, 0); # point is the intersection of the prime meridian and equator.&lt;br /&gt;
print(&amp;quot;Latitude: &amp;quot;, lat); # prints lat, lon and alt, which are all zero, see above&lt;br /&gt;
print(&amp;quot;Longitude: &amp;quot;, lon);&lt;br /&gt;
print(&amp;quot;Altitude: &amp;quot;, alt);&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===cmdarg()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|private = _cmdarg()&lt;br /&gt;
|syntax = cmdarg();&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=513|t=Part 1}} {{!}} {{fgdata file|Nasal/globals.nas|t=Part 2}}&lt;br /&gt;
|text = &amp;lt;code&amp;gt;cmdarg()&amp;lt;/code&amp;gt; returns the property root of certain types of XML files. These could be nodes in the [[Property Tree]], or temporary and/or non-public nodes outside the Property tree. &lt;br /&gt;
It is used by Nasal scripts embedded in XML files. It returns a &amp;lt;code&amp;gt;props.Node&amp;lt;/code&amp;gt; object (see {{fgdata file|Nasal/props.nas}}), and you can use all of its methods on the returned value. &amp;lt;code&amp;gt;cmdarg()&amp;lt;/code&amp;gt; should only be used in four types/places of XML files:&lt;br /&gt;
* Bindings: This is needed so that the value of a joystick's axis can be accessed internally.&lt;br /&gt;
* Dialogs: This will return the root of the dialog in the Property Tree. This is useful for dialogs that are created/modified procedurally (e.g. for populating/changing widgets while loading the dialog). &lt;br /&gt;
* Embedded Canvases: The Nasal code behind [[Canvas]] windows [[Howto:Adding a canvas to a GUI dialog|embedded in PUI dialogs]] can use it to accessing the root directory of their Canvas.&lt;br /&gt;
* Animation XML files: If the animation XML file is used in an AI/MP model, &amp;lt;code&amp;gt;cmdarg()&amp;lt;/code&amp;gt; will return the root of the AI model in the &amp;lt;code&amp;gt;/ai/models/&amp;lt;/code&amp;gt; directory. Examples: &amp;lt;code&amp;gt;/ai/models/aircraft[3]/&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/ai/models/multiplayer[1]/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You should not use &amp;lt;code&amp;gt;cmdarg()&amp;lt;/code&amp;gt; in places other than those stated above. Although it won't cause an error, it will return the value of the last legitimate &amp;lt;code&amp;gt;cmdarg()&amp;lt;/code&amp;gt; call. &lt;br /&gt;
&lt;br /&gt;
Also, you should not delay &amp;lt;code&amp;gt;cmdarg()&amp;lt;/code&amp;gt; using {{func link|maketimer()}}, {{func link|settimer()}} or {{func link|setlistener()}}, because it will return an unrelated property.&lt;br /&gt;
|example1 = fgcommand(&amp;quot;dialog-show&amp;quot;, {&amp;quot;dialog-name&amp;quot;: &amp;quot;cmdarg-demo&amp;quot;});&lt;br /&gt;
|example1text = &amp;lt;br&amp;gt;This example demonstrates the usage of &amp;lt;code&amp;gt;cmdarg()&amp;lt;/code&amp;gt; in a binding.  Save the below XML snippet as &amp;lt;tt&amp;gt;[[$FG_ROOT]]/gui/dialogs/cmdarg-demo.xml&amp;lt;/tt&amp;gt;. Then run the Nasal snippet below in your [[Nasal Console]]. Upon clicking {{button|Close}}, a message will be printed sowing the root of the binding in the Property Tree.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;PropertyList&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;name&amp;gt;cmdarg-demo&amp;lt;/name&amp;gt;&lt;br /&gt;
&amp;lt;layout&amp;gt;vbox&amp;lt;/layout&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;text&amp;gt;&lt;br /&gt;
  &amp;lt;label&amp;gt;Click &amp;quot;Close&amp;quot; to activate the demonstration (a message in the console).&amp;lt;/label&amp;gt;&lt;br /&gt;
&amp;lt;/text&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;button&amp;gt;&lt;br /&gt;
  &amp;lt;legend&amp;gt;Close&amp;lt;/legend&amp;gt;&lt;br /&gt;
  &amp;lt;binding&amp;gt;&lt;br /&gt;
    &amp;lt;command&amp;gt;nasal&amp;lt;/command&amp;gt;&lt;br /&gt;
    &amp;lt;script&amp;gt;print(&amp;quot;Button binding root: '&amp;quot; ~ cmdarg().getPath() ~ &amp;quot;'&amp;quot;);&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;/binding&amp;gt;&lt;br /&gt;
  &amp;lt;binding&amp;gt;&lt;br /&gt;
    &amp;lt;command&amp;gt;dialog-close&amp;lt;/command&amp;gt;&lt;br /&gt;
  &amp;lt;/binding&amp;gt;&lt;br /&gt;
&amp;lt;/button&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/PropertyList&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|example2text = This example demonstrates the usage of &amp;lt;code&amp;gt;cmdarg()&amp;lt;/code&amp;gt; in Nasal code within dialogs.  Open &amp;lt;tt&amp;gt;[[$FG_ROOT]]/gui/dialogs/cmdarg-demo.xml&amp;lt;/tt&amp;gt; from the previous example, copy &amp;amp; paste the code below, and save it. Then run the same Nasal snippet as the previous example in your Nasal Console. If you click {{button|Click me!}}, the button's label will change to &amp;quot;I've been changed!&amp;quot;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;PropertyList&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;name&amp;gt;cmdarg-demo&amp;lt;/name&amp;gt;&lt;br /&gt;
&amp;lt;layout&amp;gt;vbox&amp;lt;/layout&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;text&amp;gt;&lt;br /&gt;
  &amp;lt;label&amp;gt;Click &amp;quot;Click me!&amp;quot; to activate the demonstration (the button's label will change).&amp;lt;/label&amp;gt;&lt;br /&gt;
&amp;lt;/text&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;button&amp;gt;&lt;br /&gt;
  &amp;lt;legend&amp;gt;Click me!&amp;lt;/legend&amp;gt;&lt;br /&gt;
  &amp;lt;binding&amp;gt;&lt;br /&gt;
    &amp;lt;command&amp;gt;nasal&amp;lt;/command&amp;gt;&lt;br /&gt;
    &amp;lt;script&amp;gt;change_label();&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;/binding&amp;gt;&lt;br /&gt;
&amp;lt;/button&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;button&amp;gt;&lt;br /&gt;
  &amp;lt;legend&amp;gt;Close&amp;lt;/legend&amp;gt;&lt;br /&gt;
  &amp;lt;binding&amp;gt;&lt;br /&gt;
    &amp;lt;command&amp;gt;dialog-close&amp;lt;/command&amp;gt;&lt;br /&gt;
  &amp;lt;/binding&amp;gt;&lt;br /&gt;
&amp;lt;/button&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nasal&amp;gt;&lt;br /&gt;
  &amp;lt;open&amp;gt;&amp;lt;![CDATA[&lt;br /&gt;
    var dlg_root = cmdarg();&lt;br /&gt;
    var dlg_name = {&amp;quot;dialog-name&amp;quot;: &amp;quot;cmdarg-demo&amp;quot;};&lt;br /&gt;
    var change_label = func {&lt;br /&gt;
        dlg_root.getNode(&amp;quot;button[0]/legend&amp;quot;).setValue(&amp;quot;I've been changed!&amp;quot;);&lt;br /&gt;
        fgcommand(&amp;quot;dialog-close&amp;quot;, dlg_name);&lt;br /&gt;
        fgcommand(&amp;quot;dialog-show&amp;quot;, dlg_name);&lt;br /&gt;
    }&lt;br /&gt;
  ]]&amp;gt;&amp;lt;/open&amp;gt;&lt;br /&gt;
&amp;lt;/nasal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/PropertyList&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|example3text = For an example of &amp;lt;code&amp;gt;cmdarg()&amp;lt;/code&amp;gt; used with Canvas, please see [[Howto:Adding a canvas to a GUI dialog#FGPlot|Howto:Adding a canvas to a GUI dialog]].&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===courseAndDistance()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = courseAndDistance(to);&lt;br /&gt;
courseAndDistance(from, to);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1668|t=Source}}&lt;br /&gt;
|text = Returns a vector containing the course from one point to another and the distance between them in nautical miles. The course is the initial bearing (see [http://www.movable-type.co.uk/scripts/latlong.html#bearing here]), and is in the range 0–360. Both arguments can be one of:&lt;br /&gt;
:* An &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;runway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;taxiway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fix&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;waypoint&amp;lt;/code&amp;gt; ghost type&lt;br /&gt;
:* A hash with ''lat'' and ''lon'' members&lt;br /&gt;
:* A geo.Coord object with geodetic coordinates (cartesian coordinates will not be accepted)&lt;br /&gt;
|param1 = from&lt;br /&gt;
|param1text = Optional parameter defining the from where the function should calculate its results. If the function is given one argument ('''to'''), the aircraft's current position will be used. As well as the argument types as defined above, this argument can be two numbers separated with a comma, as if the function is taking three arguments. See example 5 below.&lt;br /&gt;
|param2 = to&lt;br /&gt;
|param2text = Like the first parameter, but defines the second point.&lt;br /&gt;
|example1text = This example demonstrates the usage of the function with the &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt; ghost type.&lt;br /&gt;
|example1 = var from = airportinfo(&amp;quot;KSFO&amp;quot;);&lt;br /&gt;
var to = airportinfo(&amp;quot;KSQL&amp;quot;);&lt;br /&gt;
var (course, dist) = courseAndDistance(from, to);&lt;br /&gt;
print(course); # prints course from KSFO to KSQL&lt;br /&gt;
print(dist); # prints distance in nm from KSFO to KSQL&lt;br /&gt;
|example2text = This example demonstrates the usage of the function with hashes containing ''lat'' and ''lon''.&lt;br /&gt;
|example2 = var from = {lat: 0, lon: 0};&lt;br /&gt;
var to = {lat: 1, lon: 1};&lt;br /&gt;
var (course, dist) = courseAndDistance(from, to);&lt;br /&gt;
print(course);&lt;br /&gt;
print(dist);&lt;br /&gt;
|example3text = This example demonstrates usage of a geo.Coord object.&lt;br /&gt;
|example3 = var from = geo.Coord.new().set_latlon(0, 0);&lt;br /&gt;
var to = geo.Coord.new().set_latlon(1, 1);&lt;br /&gt;
var (course, dist) = courseAndDistance(from, to);&lt;br /&gt;
print(course);&lt;br /&gt;
print(dist);&lt;br /&gt;
|example4text = This example demonstrates usage of differing parameter types.&lt;br /&gt;
|example4 = var from = airportinfo(&amp;quot;KSFO&amp;quot;);&lt;br /&gt;
var to = geo.Coord.new().set_latlon(0, 0);&lt;br /&gt;
var (course, dist) = courseAndDistance(from, to);&lt;br /&gt;
print(course);&lt;br /&gt;
print(dist);&lt;br /&gt;
|example5text = The same as above, but the other way round.&lt;br /&gt;
|example5 = var to = {lat: 1, lon: 1};&lt;br /&gt;
var (course, dist) = courseAndDistance(0, 0, to);&lt;br /&gt;
print(course);&lt;br /&gt;
print(dist);&lt;br /&gt;
|example6text = Usage of just one parameter.&lt;br /&gt;
|example6 = var dest = airportinfo(&amp;quot;KSQL&amp;quot;);&lt;br /&gt;
var (course, dist) = courseAndDistance(dest);&lt;br /&gt;
print(&amp;quot;Turn to heading &amp;quot;, math.round(course), &amp;quot;. You have &amp;quot;, sprintf(&amp;quot;%.2f&amp;quot;, dist), &amp;quot; nm to go&amp;quot;);&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===createFlightplan()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = createFlightplan(path);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=2331|t=Source}}&lt;br /&gt;
|text = Creates an empty flightplan object. It accepts one argument, ''path'' passed an absolute path to a .fgfp / .gpx file, it will populate the flightplan with waypoints from the file.&lt;br /&gt;
|param1 = path&lt;br /&gt;
|param1text = Optional parameter defining the file from which a flightplan will be populated.&lt;br /&gt;
|example1 = &lt;br /&gt;
var path = getprop(&amp;quot;/sim/fg-home&amp;quot;) ~ &amp;quot;/Export/test.fgfp&amp;quot;;&lt;br /&gt;
var flightplan = createFlightplan(path);&lt;br /&gt;
debug.dump(flightplan);&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===createDiscontinuity()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = createDiscontinuity();&lt;br /&gt;
|text = Returns a &amp;lt;code&amp;gt;waypoint&amp;lt;/code&amp;gt; ghost object. A route discontinuity is inserted by an {{abbr|FMS|Flight Management System}} when it is unsure how to connect two waypoints.&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=2045|t=Source}}&lt;br /&gt;
|version = 2016.1&lt;br /&gt;
|commit = {{flightgear commit|caead6|t=commit}}&lt;br /&gt;
}}&lt;br /&gt;
===createViaTo()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = createViaTo(airway, waypoint);&lt;br /&gt;
|text = Returns a &amp;lt;code&amp;gt;waypoint&amp;lt;/code&amp;gt; ghost object. It represents a route &amp;quot;via '''airway''' to '''waypoint'''&amp;quot;.&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=2009|t=Source}}&lt;br /&gt;
|version = 2016.1&lt;br /&gt;
|commit = {{flightgear commit|caead6|t=commit}}&lt;br /&gt;
|param1 = airway&lt;br /&gt;
|param1text = The name of an airway.&lt;br /&gt;
|param2 = waypoint&lt;br /&gt;
|param2text = Must be in the airway and one of:&lt;br /&gt;
* The name of a waypoint.&lt;br /&gt;
* An &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;runway&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;fix&amp;lt;/code&amp;gt; ghost object.&lt;br /&gt;
}}&lt;br /&gt;
===createWP()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = createWP(pos, name[, flag]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1964|t=Source}}&lt;br /&gt;
|text = Creates a new waypoint ghost object.&lt;br /&gt;
|param1 = pos&lt;br /&gt;
|param1text = Dictates the position of the new waypoint. It can be one of the following:&lt;br /&gt;
:* An &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;runway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;taxiway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fix&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;waypoint&amp;lt;/code&amp;gt; ghost type&lt;br /&gt;
:* A hash with ''lat'' and ''lon'' members&lt;br /&gt;
:* A geo.Coord object&lt;br /&gt;
:* Two numbers separated by a comma, as if the function is taking three arguments. See example 4 below.&lt;br /&gt;
|param2 = name&lt;br /&gt;
|param2text = String that will become the name of the new waypoint.&lt;br /&gt;
|param3 = flag&lt;br /&gt;
|param3text = Optional string that will tell FlightGear what type of waypoint it is. Must be one of &amp;quot;sid,&amp;quot; &amp;quot;star,&amp;quot; &amp;quot;approach,&amp;quot; &amp;quot;missed,&amp;quot; or &amp;quot;pseudo.&amp;quot;&lt;br /&gt;
|example1text = Creates a waypoint directly in front and 1 km away and appends it to the flight plan.&lt;br /&gt;
|example1 = var pos = geo.aircraft_position().apply_course_distance(getprop(&amp;quot;/orientation/heading-deg&amp;quot;), 1000);&lt;br /&gt;
var wp = createWP(pos, &amp;quot;NEWWP&amp;quot;);&lt;br /&gt;
var fp = flightplan();&lt;br /&gt;
fp.appendWP(wp);&lt;br /&gt;
|example2 = var pos = geo.aircraft_position().apply_course_distance(getprop(&amp;quot;/orientation/heading-deg&amp;quot;), 1000);&lt;br /&gt;
var wp = createWP({lat: pos.lat(), lon: pos.lon()}, &amp;quot;NEWWP&amp;quot;);&lt;br /&gt;
var fp = flightplan();&lt;br /&gt;
fp.appendWP(wp);&lt;br /&gt;
|example3 = var apt = airportinfo();&lt;br /&gt;
var wp = createWP(apt, &amp;quot;NEWWP&amp;quot;);&lt;br /&gt;
var fp = flightplan();&lt;br /&gt;
fp.appendWP(wp);&lt;br /&gt;
|example4 = var pos = geo.aircraft_position().apply_course_distance(getprop(&amp;quot;/orientation/heading-deg&amp;quot;), 1000);&lt;br /&gt;
var wp = createWP(pos.lat(), pos.lon(), &amp;quot;NEWWP&amp;quot;);&lt;br /&gt;
var fp = flightplan();&lt;br /&gt;
fp.appendWP(wp);&lt;br /&gt;
|example5text = Creates a new waypoint and adds it to the flight plan. Waypoints of the type &amp;quot;pseudo&amp;quot; are then removed from the flight plan, including the new waypoint. The {{func link|print()}} statements show this.&lt;br /&gt;
|example5 = var pos = geo.aircraft_position().apply_course_distance(getprop(&amp;quot;/orientation/heading-deg&amp;quot;), 1000);&lt;br /&gt;
var wp = createWP(pos, &amp;quot;NEWWP&amp;quot;, &amp;quot;pseudo&amp;quot;);&lt;br /&gt;
var fp = flightplan();&lt;br /&gt;
fp.appendWP(wp);&lt;br /&gt;
print(fp.getPlanSize());&lt;br /&gt;
fp.clearWPType(&amp;quot;pseudo&amp;quot;);&lt;br /&gt;
print(fp.getPlanSize());&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===createWPFrom()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = createWPFrom(object[, flag]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1989|t=Source}}&lt;br /&gt;
|text = Creates a new waypoint object from another object.&lt;br /&gt;
|param1 = object&lt;br /&gt;
|param1text = A ghost object. Must be a ghost type that is one of &amp;quot;airport,&amp;quot; &amp;quot;navaid,&amp;quot; &amp;quot;runway,&amp;quot; or &amp;quot;fix.&amp;quot;&lt;br /&gt;
|param2 = flag&lt;br /&gt;
|param2text = Optional string that will tell FlightGear what type of waypoint it is. Must be one of &amp;quot;sid,&amp;quot; &amp;quot;star,&amp;quot; &amp;quot;approach,&amp;quot; &amp;quot;missed,&amp;quot; or &amp;quot;pseudo.&amp;quot;&lt;br /&gt;
|example1text = Creates a new waypoint and appends it to the flight plan.&lt;br /&gt;
|example1 = var apt = airportinfo(&amp;quot;KSFO&amp;quot;);&lt;br /&gt;
var wp = createWPFrom(apt);&lt;br /&gt;
var fp = flightplan();&lt;br /&gt;
fp.appendWP(wp);&lt;br /&gt;
|example2text = Creates a new waypoint and appends it to the flight plan. This way point is then removed; the {{func link|print()}} statements prove this.&lt;br /&gt;
|example2 = var apt = airportinfo(&amp;quot;KSFO&amp;quot;);&lt;br /&gt;
var wp = createWPFrom(apt, &amp;quot;pseudo&amp;quot;);&lt;br /&gt;
print(wp.wp_name);&lt;br /&gt;
var fp = flightplan();&lt;br /&gt;
fp.appendWP(wp);&lt;br /&gt;
print(fp.getPlanSize());&lt;br /&gt;
fp.clearWPType(&amp;quot;pseudo&amp;quot;);&lt;br /&gt;
print(fp.getPlanSize());&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===defined()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = defined(symbol);&lt;br /&gt;
|source = {{fgdata file|Nasal/globals.nas|t=Source}}&lt;br /&gt;
|text = Returns 1 (true) or 0 (false) depending on whether a variable exists.&lt;br /&gt;
|param1 = symbol&lt;br /&gt;
|param1text = A string that will be what the function searches for.&lt;br /&gt;
|example1 = var number = 12;&lt;br /&gt;
var check_exist = func {&lt;br /&gt;
    print(&amp;quot;Variable 'number' &amp;quot;, defined(&amp;quot;number&amp;quot;) == 1 ? &amp;quot;exists&amp;quot; : &amp;quot;does not exist&amp;quot;); # 'number' does exist&lt;br /&gt;
    print(&amp;quot;Variable 'number2' &amp;quot;, defined(&amp;quot;number2&amp;quot;) == 1 ? &amp;quot;exists&amp;quot; : &amp;quot;does not exist&amp;quot;); # 'number2' does not exist&lt;br /&gt;
}&lt;br /&gt;
check_exist();&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===directory()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = directory(path);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=572|t=Source}}&lt;br /&gt;
|text = Returns a vector containing a list of the folders and files in a given file path or &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; if the path doesn't exist. Hidden folders and files are not added to the vector.&lt;br /&gt;
{{inote|The first two elements of the vector will be &amp;lt;code&amp;gt;'.'&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;'..'&amp;lt;/code&amp;gt;. These are for navigating back up the file tree, but have no use in Nasal. They can be safely removed from the vector.}}&lt;br /&gt;
|param1 = path&lt;br /&gt;
|param1text = Absolute file path.&lt;br /&gt;
|example1text = Gets the folders and files in [[$FG_ROOT]], and then removes the extra first two elements (see note above). &lt;br /&gt;
|example1 = var dir = directory(getprop(&amp;quot;/sim/fg-root&amp;quot;)); # get directory&lt;br /&gt;
dir = subvec(dir, 2); # strips off the first two elements&lt;br /&gt;
debug.dump(dir); # dump the vector&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===fgcommand()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = fgcommand(cmd[, args]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=456|t=Part 1}} {{!}} {{fgdata file|Nasal/globals.nas|t=Part 2}}&lt;br /&gt;
|text = Runs an fgcommand. See also {{readme file|commands}} and [[Bindings]] for more information. See {{flightgear file|src/Main/fg_commands.cxx|l=1425}} for the full list of fgcommands. Note that fgcommands generated by {{func link|addcommand()}} can also be run using this function. Also, the full list of fgcommands depends on the version of FlightGear you have. Returns 1 (true) if the fgcommand succeeded or 0 (false) if it failed.&lt;br /&gt;
|param1 = cmd&lt;br /&gt;
|param1text = String that is the name of the command that is to be run.&lt;br /&gt;
|param2 = args&lt;br /&gt;
|param2text = If the fgcommand takes arguments, they are inputted using this argument. Can either be a &amp;lt;code&amp;gt;props.Node&amp;lt;/code&amp;gt; object, or a hash (see examples below).&lt;br /&gt;
|example1 = fgcommand(&amp;quot;null&amp;quot;); # does nothing&lt;br /&gt;
|example2 = var args = props.Node.new({'script': 'print(&amp;quot;Running fgcommand&amp;quot;);'});&lt;br /&gt;
if (fgcommand(&amp;quot;nasal&amp;quot;, args)) { # prints &amp;quot;Running fgcommand&amp;quot; and then one of these print statements&lt;br /&gt;
    print(&amp;quot;Fgcommand succeeded&amp;quot;);&lt;br /&gt;
} else {&lt;br /&gt;
    print(&amp;quot;Fgcommand encountered a problem&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
|example3 = var args = { 'dialog-name': 'about' };&lt;br /&gt;
fgcommand(&amp;quot;dialog-show&amp;quot;, args); # shows the 'about' dialog&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===findAirportsByICAO() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = findAirportsByICAO(search[, type]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1096|t=Source}}&lt;br /&gt;
|text = Returns a vector containing &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt; ghost objects which are (by default) airports whose ICAO code matches the search string. The results are sorted by range from closest to furthest.&lt;br /&gt;
|param1 = search&lt;br /&gt;
|param1text = Search string for the function. Can either be a partial or a full ICAO code.&lt;br /&gt;
:{{icaution|The more matches there are for the given code, the longer the function will take. Passing just one character (e.g., &amp;quot;K&amp;quot;), might make FlightGear hang for a certain amount of time.}}&lt;br /&gt;
|param2 = type&lt;br /&gt;
|param2text = This will narrow the search to airports of a certain type. By default, only airports are searched for. May be one of &amp;quot;airport,&amp;quot; &amp;quot;heliport,&amp;quot; or &amp;quot;seaport.&amp;quot;&lt;br /&gt;
|example1 = var apts = findAirportsByICAO(&amp;quot;KSF&amp;quot;); # finds all airports matching &amp;quot;KSF&amp;quot;&lt;br /&gt;
foreach(var apt; apts){&lt;br /&gt;
    print(apt.name, &amp;quot; (&amp;quot;, apt.id, &amp;quot;)&amp;quot;); # prints them&lt;br /&gt;
}&lt;br /&gt;
|example2 = var apts = findAirportsByICAO(&amp;quot;SP0&amp;quot;, &amp;quot;seaport&amp;quot;); # finds all seaplane bases matching &amp;quot;SP0&amp;quot;&lt;br /&gt;
foreach(var apt; apts){&lt;br /&gt;
    print(apt.name, &amp;quot; (&amp;quot;, apt.id, &amp;quot;)&amp;quot;); # prints them&lt;br /&gt;
}&lt;br /&gt;
|example3 = var apt = findAirportsByICAO(&amp;quot;XBET&amp;quot;); # one way to check if an airport does exist&amp;quot;&lt;br /&gt;
if (size(apt) == 0) {&lt;br /&gt;
    print(&amp;quot;Airport does not exist&amp;quot;); # this one will be printed&lt;br /&gt;
} else {&lt;br /&gt;
    print(&amp;quot;Airport does exist&amp;quot;);&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===findAirportsWithinRange()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = findAirportsWithinRange([pos, ]range[, type]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1066|t=Source}}&lt;br /&gt;
|text = Returns a vector of &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt; ghost object which are (by default) airports that are within a given range of a given position, or the aircraft's current position. The results are sorted by range from closest to furthest.&lt;br /&gt;
|param1 = pos&lt;br /&gt;
|param1text = Optional position to search around. If not given, the aircraft's current position will be used. Can be one of:&lt;br /&gt;
:* An &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;runway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;taxiway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fix&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;waypoint&amp;lt;/code&amp;gt; ghost type&lt;br /&gt;
:* A hash with ''lat'' and ''lon'' members&lt;br /&gt;
:* A geo.Coord object&lt;br /&gt;
:* Two numbers separated by a comma, as if the function is taking three arguments. Example: &amp;lt;code&amp;gt;findAirportsWithinRange(lat, lon, range, type);&amp;lt;/code&amp;gt;.&lt;br /&gt;
|param2 = range&lt;br /&gt;
|param2text = Mandatory number giving the range in nautical miles within which to search for airports/heliports/seaplane bases.only airports are searched for.&lt;br /&gt;
|param3 = type&lt;br /&gt;
|param3text = This will narrow the search to airports of a certain type. By default, only airports are searched for. May be one of &amp;quot;airport,&amp;quot; &amp;quot;heliport,&amp;quot; or &amp;quot;seaport.&amp;quot;&lt;br /&gt;
|example1text = Searches for airports within 10 nm of [[KSFO]].&lt;br /&gt;
|example1 = var pos = airportinfo(&amp;quot;KSFO&amp;quot;);&lt;br /&gt;
var apts = findAirportsWithinRange(pos, 10);&lt;br /&gt;
foreach(var apt; apts){&lt;br /&gt;
    print(apt.name, &amp;quot; (&amp;quot;, apt.id, &amp;quot;)&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
|example2text = Searches for seaplane bases within 15 nm of [[KSFO]].&lt;br /&gt;
|example2 = var pos = airportinfo(&amp;quot;KSFO&amp;quot;);&lt;br /&gt;
var apts = findAirportsWithinRange(pos, 15, &amp;quot;seaport&amp;quot;);&lt;br /&gt;
foreach(var apt; apts){&lt;br /&gt;
    print(apt.name, &amp;quot; (&amp;quot;, apt.id, &amp;quot;)&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
|example3text = Searches for airports within 10 nm of your current position.&lt;br /&gt;
|example3 = var apts = findAirportsWithinRange(10);&lt;br /&gt;
foreach(var apt; apts){&lt;br /&gt;
    print(apt.name, &amp;quot; (&amp;quot;, apt.id, &amp;quot;)&amp;quot;);&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===findFixesByID()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = findFixesByID([pos, ]id);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1627|t=Source}}&lt;br /&gt;
|text = Returns a vector containing &amp;lt;code&amp;gt;fix&amp;lt;/code&amp;gt; ghost objects matching a given ID, sorted by range from a certain position.&lt;br /&gt;
{{inote|Fixes are (usually) also known as waypoints.}}&lt;br /&gt;
|param1 = pos&lt;br /&gt;
|param1text = Optional position to search around. If not given, the aircraft's current position will be used. Can be one of:&lt;br /&gt;
:* An &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;runway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;taxiway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fix&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;waypoint&amp;lt;/code&amp;gt; ghost type&lt;br /&gt;
:* A hash with ''lat'' and ''lon'' members&lt;br /&gt;
:* A geo.Coord object&lt;br /&gt;
:* Two numbers separated by a comma, as if the function is taking three arguments. Example: &amp;lt;code&amp;gt;findFixesByID(lat, lon, id);&amp;lt;/code&amp;gt;.&lt;br /&gt;
|param2 = id&lt;br /&gt;
|param2text = Full or partial ID of the fix to search for.&lt;br /&gt;
:{{inote|1=Inputting a partial ID does not work correctly (see [http://forum.flightgear.org/viewtopic.php?f=30&amp;amp;t=28129 here]). It is best to just input a full ID.}}&lt;br /&gt;
|example1 = var fixes = findFixesByID(&amp;quot;POGIC&amp;quot;);&lt;br /&gt;
foreach(var fix; fixes){&lt;br /&gt;
    print(fix.id, &amp;quot; - lat: &amp;quot;, fix.lat, &amp;quot; {{!}} lon: &amp;quot;, fix.lon); # prints information about POGIC&lt;br /&gt;
}&lt;br /&gt;
|example2 = var fix = findFixesByID(&amp;quot;ZUNAP&amp;quot;);&lt;br /&gt;
fix = fix[0];&lt;br /&gt;
var (course, dist) = courseAndDistance(fix);&lt;br /&gt;
print(&amp;quot;Turn to heading &amp;quot;, math.round(course), &amp;quot;. You have &amp;quot;, sprintf(&amp;quot;%.2f&amp;quot;, dist), &amp;quot; nm to go to reach &amp;quot;, fixes[0].id);&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===findNavaidByFrequencyMHz()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = findNavaidByFrequencyMHz([pos, ]freq[, type]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1547|t=Source}}&lt;br /&gt;
|text = Returns a &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt; ghost object for a navaid matching a given frequency. If there is more than one navaid with that frequency, the nearest station is returned.&lt;br /&gt;
|param1 = pos&lt;br /&gt;
|param1text = Optional position to search around. If not given, the aircraft's current position will be used. Can be one of:&lt;br /&gt;
:* An &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;runway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;taxiway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fix&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;waypoint&amp;lt;/code&amp;gt; ghost type&lt;br /&gt;
:* A hash with ''lat'' and ''lon'' members&lt;br /&gt;
:* A geo.Coord object&lt;br /&gt;
:* Two numbers separated by a comma, as if the function is taking three arguments. Example: &amp;lt;code&amp;gt;findNavaidByFrequency(lat, lon, freq, type);&amp;lt;/code&amp;gt;.&lt;br /&gt;
|param2 = freq&lt;br /&gt;
|param2text = Frequency, in megahertz, of the navaid to search for.&lt;br /&gt;
|param3 = type&lt;br /&gt;
|param3text = This will narrow the search to navaids of a certain type. Defaults to &amp;quot;all.&amp;quot; For the full list of accepted type arguments, see {{flightgear file|src/Navaids/positioned.cxx|l=127}}.&lt;br /&gt;
|example1 = var navaid = findNavaidByFrequencyMHz(11.17);&lt;br /&gt;
print(&amp;quot;ID: &amp;quot;, navaid.id); # prints info about the navaid&lt;br /&gt;
print(&amp;quot;Name: &amp;quot;, navaid.name);&lt;br /&gt;
print(&amp;quot;Latitude: &amp;quot;, navaid.lat);&lt;br /&gt;
print(&amp;quot;Longitude: &amp;quot;, navaid.lon);&lt;br /&gt;
print(&amp;quot;Elevation (AMSL): &amp;quot;, navaid.elevation, &amp;quot; m&amp;quot;);&lt;br /&gt;
print(&amp;quot;Type: &amp;quot;, navaid.type);&lt;br /&gt;
print(&amp;quot;Frequency: &amp;quot;, sprintf(&amp;quot;%.3f&amp;quot;, navaid.frequency / 1000), &amp;quot; Mhz&amp;quot;);&lt;br /&gt;
print(&amp;quot;Range: &amp;quot;, navaid.range_nm, &amp;quot; nm&amp;quot;);&lt;br /&gt;
if(navaid.course) print(&amp;quot;Course: &amp;quot;, navaid.course);&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== findNavaidsByFrequencyMHz()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = findNavaidsByFrequencyMHz([pos, ]freq[, type]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1572|t=Source}}&lt;br /&gt;
|text = Returns a vector conatining &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt; ghost objects for navaids that match a given frequency, sorted from nearest to furthest.&lt;br /&gt;
|param1 = pos&lt;br /&gt;
|param1text = Optional position to search around. If not given, the aircraft's current position will be used. Can be one of:&lt;br /&gt;
:* An &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;runway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;taxiway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fix&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;waypoint&amp;lt;/code&amp;gt; ghost type&lt;br /&gt;
:* A hash with ''lat'' and ''lon'' members&lt;br /&gt;
:* A geo.Coord object&lt;br /&gt;
:* Two numbers separated by a comma, as if the function is taking three arguments. Example: &amp;lt;code&amp;gt;findNavaidsByFrequencyMHz(lat, lon, freq, type);&amp;lt;/code&amp;gt;.&lt;br /&gt;
|param2 = freq&lt;br /&gt;
|param2text = Frequency, in megahertz, of the navaid to search for.&lt;br /&gt;
|param3 = type&lt;br /&gt;
|param3text = This will narrow the search to navaids of a certain type. Defaults to &amp;quot;all.&amp;quot; For the full list of accepted type arguments, see {{flightgear file|src/Navaids/positioned.cxx|l=127}}.&lt;br /&gt;
|example1 = var navaids = findNavaidsByFrequencyMHz(109.55);&lt;br /&gt;
foreach(var navaid; navaids){&lt;br /&gt;
    print(&amp;quot;--&amp;quot;);&lt;br /&gt;
    print(&amp;quot;ID: &amp;quot;, navaid.id); # prints info about the navaid&lt;br /&gt;
    print(&amp;quot;Name: &amp;quot;, navaid.name);&lt;br /&gt;
    print(&amp;quot;Latitude: &amp;quot;, navaid.lat);&lt;br /&gt;
    print(&amp;quot;Longitude: &amp;quot;, navaid.lon);&lt;br /&gt;
    print(&amp;quot;Elevation (AMSL): &amp;quot;, navaid.elevation, &amp;quot; m&amp;quot;);&lt;br /&gt;
    print(&amp;quot;Type: &amp;quot;, navaid.type);&lt;br /&gt;
    print(&amp;quot;Frequency: &amp;quot;, sprintf(&amp;quot;%.3f&amp;quot;, navaid.frequency / 100), &amp;quot; Mhz&amp;quot;);&lt;br /&gt;
    print(&amp;quot;Range: &amp;quot;, navaid.range_nm, &amp;quot; nm&amp;quot;);&lt;br /&gt;
    if(navaid.course) print(&amp;quot;Course: &amp;quot;, navaid.course);&lt;br /&gt;
    print(&amp;quot;--&amp;quot;);&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===findNavaidsByID()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = findNavaidsByID([pos, ]id[, type]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1600|t=Source}}&lt;br /&gt;
|text = Returns a vector containing &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt; ghost objects matching a given ID, sorted by range from a certain position.&lt;br /&gt;
|param1 = pos&lt;br /&gt;
|param1text = Optional position to search around. If not given, the aircraft's current position will be used. Can be one of:&lt;br /&gt;
:* An &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;runway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;taxiway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fix&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;waypoint&amp;lt;/code&amp;gt; ghost type&lt;br /&gt;
:* A hash with ''lat'' and ''lon'' members&lt;br /&gt;
:* A geo.Coord object&lt;br /&gt;
:* Two numbers separated by a comma, as if the function is taking three arguments. Example: &amp;lt;code&amp;gt;findNavaidsByID(lat, lon, id, type);&amp;lt;/code&amp;gt;.&lt;br /&gt;
|param2 = id&lt;br /&gt;
|param2text = Full or partial ID of the fix to search for.&lt;br /&gt;
:{{inote|1=Inputting a partial ID does not work correctly (see [http://forum.flightgear.org/viewtopic.php?f=30&amp;amp;t=28129 here]). It is best to just input a full ID.}}&lt;br /&gt;
|param3 = type&lt;br /&gt;
|param3text = This will narrow the search to navaids of a certain type. Defaults to &amp;quot;all.&amp;quot; For the full list of accepted type arguments, see {{flightgear file|src/Navaids/positioned.cxx|l=127}}.&lt;br /&gt;
|example1 = var navaid = findNavaidsByID(&amp;quot;MXW&amp;quot;);&lt;br /&gt;
navaid = navaid[0];&lt;br /&gt;
print(&amp;quot;ID: &amp;quot;, navaid.id); # prints info about 'MXW' (a VOR station)&lt;br /&gt;
print(&amp;quot;Name: &amp;quot;, navaid.name);&lt;br /&gt;
print(&amp;quot;Latitude: &amp;quot;, navaid.lat);&lt;br /&gt;
print(&amp;quot;Longitude: &amp;quot;, navaid.lon);&lt;br /&gt;
print(&amp;quot;Elevation (AMSL): &amp;quot;, navaid.elevation, &amp;quot; m&amp;quot;);&lt;br /&gt;
print(&amp;quot;Type: &amp;quot;, navaid.type);&lt;br /&gt;
print(&amp;quot;Frequency: &amp;quot;, sprintf(&amp;quot;%.3f&amp;quot;, navaid.frequency / 1000), &amp;quot; Mhz&amp;quot;);&lt;br /&gt;
print(&amp;quot;Range: &amp;quot;, navaid.range_nm, &amp;quot; nm&amp;quot;);&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===findNavaidsWithinRange()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = findNavaidsWithinRange([pos, ]range[, type]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1518|t=Source}}&lt;br /&gt;
|text = Returns a vector of &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt; ghost objects which are within a given range of a given position (by default the aircraft's current position). The results are sorted from closest to furthest.&lt;br /&gt;
|param1 = pos&lt;br /&gt;
|param1text = Optional position to search around. If not given, the aircraft's current position will be used. Can be one of:&lt;br /&gt;
:* An &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;runway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;taxiway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fix&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;waypoint&amp;lt;/code&amp;gt; ghost type&lt;br /&gt;
:* A hash with ''lat'' and ''lon'' members&lt;br /&gt;
:* A geo.Coord object&lt;br /&gt;
:* Two numbers separated by a comma, as if the function is taking three arguments. Example: &amp;lt;code&amp;gt;findNavaidsWithinRange(lat, lon, range, type);&amp;lt;/code&amp;gt;.&lt;br /&gt;
|param2 = range&lt;br /&gt;
|param2text = Mandatory number giving the range in nautical miles within which to search for navaids. Defaults to &amp;quot;all.&amp;quot; For the full list of accepted type arguments, see {{flightgear file|src/Navaids/positioned.cxx|l=127}}.&lt;br /&gt;
|param3 = type&lt;br /&gt;
|param3text = This will narrow the search to navaids of a certain type.&lt;br /&gt;
|example1text = Searches for navaids within 10 nm of [[KSFO]].&lt;br /&gt;
|example1 = var pos = airportinfo(&amp;quot;KSFO&amp;quot;);&lt;br /&gt;
var navs = findNavaidsWithinRange(pos, 10);&lt;br /&gt;
foreach(var nav; navs){&lt;br /&gt;
    print(nav.name, &amp;quot; (ID: &amp;quot;, nav.id, &amp;quot;)&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
|example2text = Searches for navaids within 10 nm of your current position.&lt;br /&gt;
|example2 = var navs = findNavaidsWithinRange(10);&lt;br /&gt;
foreach(var nav; navs){&lt;br /&gt;
    print(nav.name, &amp;quot; (ID: &amp;quot;, nav.id, &amp;quot;)&amp;quot;);&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===finddata()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = finddata(path);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=603|t=Source}}&lt;br /&gt;
|text = Takes a relative path and tries to return an absolute one. It works by appending the relative path to some paths and testing to see if they exist. As of FlightGear v3.7, these paths are the TerraSync directory (tested first) and [[$FG_ROOT]]. &lt;br /&gt;
|param1 = path&lt;br /&gt;
|param1text = A relative path as a string.&lt;br /&gt;
|example1 = var path = finddata(&amp;quot;Aircraft/Generic&amp;quot;);&lt;br /&gt;
print(path); # prints the absolute path to $FG_ROOT/Aircraft/Generic&lt;br /&gt;
|example2 = var path = finddata(&amp;quot;Airports&amp;quot;);&lt;br /&gt;
print(path); # prints the absolute path to &amp;lt;TerraSync dir&amp;gt;/Airports&lt;br /&gt;
|example3 = var path = finddata(&amp;quot;preferences.xml&amp;quot;);&lt;br /&gt;
print(path); # prints the absolute path to $FG_ROOT/preferences.xml&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===flightplan()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = flightplan([path]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1738|t=Source}}&lt;br /&gt;
|text = {{see also|Nasal Flightplan}}&lt;br /&gt;
Returns a flight plan object, either one for the current flight plan, or one loaded from a given path.&lt;br /&gt;
|param1 = path&lt;br /&gt;
|param1text = Optional path to flight plan XML file.&lt;br /&gt;
|example1text = Gets the active flight plan and gets the ID of the current waypoint. Note that this example requires a flight plan to be set in the [[Route Manager]] first.&lt;br /&gt;
|example1 = var fp = flightplan();&lt;br /&gt;
print(fp.getWP(fp.current).id);&lt;br /&gt;
|example2text = Creates a new flight plan from an XML file and prints the IDs of the waypoints. Note that this example requires a flight plan to have been created and saved as &amp;lt;tt&amp;gt;''[[$FG_HOME]]/fp-demo.xml''&amp;lt;/tt&amp;gt;.&lt;br /&gt;
|example2 = var path = getprop('/sim/fg-home') ~ '/fp-demo.xml';&lt;br /&gt;
var fp = flightplan(path);&lt;br /&gt;
for(var i = 0; i &amp;lt; fp.getPlanSize(); i += 1){&lt;br /&gt;
    print(fp.getWP(i).id);&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===geodinfo()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = geodinfo(lat, lon[, max_alt]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=981|t=Source}}&lt;br /&gt;
|text = Returns a vector containing two entries or &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; if no information could be obtained because the terrain tile wasn't loaded. The first entry in the vector is the elevation (in meters) for the given point, and the second is a hash with information about the assigned material (as defined in &amp;lt;tt&amp;gt;''[[$FG_ROOT]]/Materials''&amp;lt;/tt&amp;gt;), or &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; if there was no material information available (for example, because there is an untextured building at that location). The structure of the hash is as follows (see also {{readme file|materials}}):&lt;br /&gt;
* '''light_coverage:''' The coverage of a single point of light in m&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;.&lt;br /&gt;
* '''bumpiness:''' Normalized bumpiness factor for the material.&lt;br /&gt;
* '''load_resistance:''' The amount of pressure in N/m&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt; the material can withstand without deformation.&lt;br /&gt;
* '''solid:''' 1 (true) or false (0) depending on whether the material is solid or not.&lt;br /&gt;
* '''names:''' Vector of scenery types (usually generated by [[TerraGear]]) that will use this material. &lt;br /&gt;
* '''friction_factor:''' Normalized friction factor of the material.&lt;br /&gt;
* '''rolling_friction:''' The rolling friction coefficient of the material.&lt;br /&gt;
&lt;br /&gt;
Note that this function is a ''very'' CPU-intensive operation, particularly in FlightGear v2.4 and earlier. It is advised to use this function as little as possible.&lt;br /&gt;
|param1 = lat&lt;br /&gt;
|param1text = Latitude, inputted as a number.&lt;br /&gt;
|param2 = lon&lt;br /&gt;
|param2text = Longitude, inputted as a number.&lt;br /&gt;
|param3 = max_alt&lt;br /&gt;
|param3text = The altitude, in metres, from which the function will begin searching for the height of the terrain. Defaults to 10,000 metres. If the terrain is higher than this argument specifies, &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; will be returned.&lt;br /&gt;
|example1text = Dumps information about ground underneath the aircraft.&lt;br /&gt;
|example1 = var pos = geo.aircraft_position();&lt;br /&gt;
var info = geodinfo(pos.lat(), pos.lon());&lt;br /&gt;
debug.dump(info);&lt;br /&gt;
|example2text = Prints whether the ground underneath the aircraft is solid or is water.&lt;br /&gt;
|example2 = var pos = geo.aircraft_position();&lt;br /&gt;
var info = geodinfo(pos.lat(), pos.lon());&lt;br /&gt;
if (info != nil and info[1] != nil) {&lt;br /&gt;
    print(&amp;quot;The ground underneath the aircraft is &amp;quot;, info[1].solid == 1 ? &amp;quot;solid.&amp;quot; : &amp;quot;water.&amp;quot;);&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== geodtocart() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = geodtocart(lat, lon, alt);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=962|t=Source}}&lt;br /&gt;
|text = Converts {{wikipedia|geodetic coordinates}} (latitude, longitude, and altitude) to {{wikipedia|ECEF|Earth-centered, Earth-fixed}} coordinates (x, y and z). A vector is returned containing x, y, and z in metres. The equatorial radius of earth used is that defined by the {{wikipedia|WGS 84}} (6,378,137 metres). All argument are mandatory.&lt;br /&gt;
|param1 = lat&lt;br /&gt;
|param1text = Latitude, in degrees.&lt;br /&gt;
|param2 = lon&lt;br /&gt;
|param2text = Longitude, in degrees.&lt;br /&gt;
|param3 = alt&lt;br /&gt;
|param3text = Altitude, in metres.&lt;br /&gt;
|example1 = var (x, y, z) = geodtocart(0, 0, 0); # point is the intersection of the prime meridian and equator.&lt;br /&gt;
print(&amp;quot;x: &amp;quot;, x); # prints &amp;quot;x: 6378137&amp;quot;&lt;br /&gt;
print(&amp;quot;y: &amp;quot;, y); # prints &amp;quot;y: 0&amp;quot;&lt;br /&gt;
print(&amp;quot;z: &amp;quot;, z); # prints &amp;quot;y: 0&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== get_cart_ground_intersection()===&lt;br /&gt;
Introduced in 2017.2.1, see [[Terrain Detection]].&lt;br /&gt;
&lt;br /&gt;
===getprop()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = &amp;lt;nowiki&amp;gt;getprop(path[, path[, ...]]);&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=345|t=Source}}&lt;br /&gt;
|text = Returns the value of a node in the [[Property Tree]] or &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; if the node does not exist or the value is not a number (NaN).&lt;br /&gt;
|param1 = path&lt;br /&gt;
|param1text = There needs to be at least one argument, but there is no limit to the maximum amount of arguments that can be given. The arguments will be concatenated together to form a property tree path. The arguments must be strings, but in FlightGear v3.2 onwards, there is also support (added by {{flightgear commit|34ed79}}) for numeric arguments as indices. See example 2 below.&lt;br /&gt;
|example1 = print(&amp;quot;You have FlightGear v&amp;quot;, getprop(&amp;quot;/sim/version/flightgear&amp;quot;)); # prints FlightGear version&lt;br /&gt;
|example2text = Note that the example below will only work in FlightGear 3.2 and above.&lt;br /&gt;
|example2 = for(var i = 0; i &amp;lt; 8; i += 1){&lt;br /&gt;
    print(&amp;quot;View #&amp;quot;, i + 1, &amp;quot; is named &amp;quot;, getprop(&amp;quot;/sim/view&amp;quot;, i, &amp;quot;name&amp;quot;));&lt;br /&gt;
}&lt;br /&gt;
|example3text = Same as above, but is supported by all versions of FlightGear.&lt;br /&gt;
|example3 = for(var i = 0; i &amp;lt; 8; i += 1){&lt;br /&gt;
    print(&amp;quot;View #&amp;quot;, i + 1, &amp;quot; is named &amp;quot;, getprop(&amp;quot;/sim/view[&amp;quot; ~ i ~ &amp;quot;]/name&amp;quot;));&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
==== See also====&lt;br /&gt;
{{note| If you have to read/write the same property multiple times (e.g. in an update loop), it is more efficient to use a node object: &lt;br /&gt;
To get a Node rather than its value, use &amp;lt;code&amp;gt;props.globals.getNode()&amp;lt;/code&amp;gt; - see [[Nasal_library/props]]. }}&lt;br /&gt;
&lt;br /&gt;
===greatCircleMove()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = greatCircleMove([pos, ]course, dist);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1681|t=Source}}&lt;br /&gt;
|text = Calculates a new set of geodetic coordinates using inputs of course and distance, either from the aircraft's current position (by default) or from another set of coordinates. Returns a hash containing two members, ''lat'' and ''lon'' (latitude and longitude respectively).&lt;br /&gt;
|param1 = pos&lt;br /&gt;
|param1text = Optional position to calculate from. If not given, the aircraft's current position will be used. Can be one of:&lt;br /&gt;
:* An &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;runway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;taxiway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fix&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;waypoint&amp;lt;/code&amp;gt; ghost object.&lt;br /&gt;
:* A hash with ''lat'' and ''lon'' members&lt;br /&gt;
:* A &amp;lt;code&amp;gt;geo.Coord&amp;lt;/code&amp;gt; object&lt;br /&gt;
:* A lat/lon pair, that is, a pair of numbers (latitude followed by longitude) separated by a comma: &amp;lt;code&amp;gt;greatCircleMove(lat,lon, ...)&amp;lt;/code&amp;gt;.&lt;br /&gt;
|param2 = course&lt;br /&gt;
|param2text = Course to new set of coordinates, in degrees (in the range 0–360).&lt;br /&gt;
|param3 = dist&lt;br /&gt;
|param3text = Distance in nautical miles to the new set of coordinates.&lt;br /&gt;
|example1 = var pos = greatCircleMove(0,0, 0, 1);&lt;br /&gt;
debug.dump(pos); # print hash with coordinates&lt;br /&gt;
|example2 = var fix = findFixesByID(&amp;quot;POGIC&amp;quot;);&lt;br /&gt;
fix = fix[0];&lt;br /&gt;
var pos = greatCircleMove(fix, 45, 10);&lt;br /&gt;
debug.dump(pos); # print hash with coordinates&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===interpolate()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|private = _interpolate()&lt;br /&gt;
|syntax = &amp;lt;nowiki&amp;gt;interpolate(prop, value1, time1[, value2, time2[, ...]]);&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=522|t=Part 1}} {{!}} {{fgdata file|Nasal/globals.nas|t=Part 2}}&lt;br /&gt;
|text = Linearly interpolates a node in the property tree to a given value in a specified time. The value/time pairs will be run one after the other in the order that they are passed to the function. Note that the interpolation will continue even when the simulation is paused.&lt;br /&gt;
|param1 = prop&lt;br /&gt;
|param1text = String or &amp;lt;code&amp;gt;props.Node&amp;lt;/code&amp;gt; object that indicates a node in the property tree to be used.&lt;br /&gt;
|param2 = value''n''&lt;br /&gt;
|param2text = Target value to change the property to in the set amount of time. This should be a number.&lt;br /&gt;
|param3 = time''n''&lt;br /&gt;
|param3text = Time in seconds, that will be taken for the interpolation.&lt;br /&gt;
|example1text = Paste the code below into the Nasal Console and execute. Then, open the Property Browser and look for the property. Finally, run the code again, and watch the value of the property change.&lt;br /&gt;
|example1 = setprop(&amp;quot;/test&amp;quot;, 0); # (re-)set property&lt;br /&gt;
interpolate(&amp;quot;/test&amp;quot;,&lt;br /&gt;
    50, 5, # interpolate to 50 in 5 seconds&lt;br /&gt;
    10, 2, # interpolate to 10 in 2 seconds&lt;br /&gt;
    0, 5); # interpolate to 0 in 5 seconds&lt;br /&gt;
|example2 = # Apply the left brake at 20% per second&lt;br /&gt;
var prop = &amp;quot;controls/gear/brake-left&amp;quot;;&lt;br /&gt;
var dist = 1 - getprop(prop);&lt;br /&gt;
if (dist == 1) {&lt;br /&gt;
    interpolate(prop, 1, dist / 0.2);&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===isa()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = isa(object, class);&lt;br /&gt;
|source = {{fgdata file|Nasal/globals.nas|t=Source}}&lt;br /&gt;
|text = Checks if an object is an instance of, or inherits from, a second object (or class), returning 1 (true) if it is and 0 (false) if otherwise.&lt;br /&gt;
|param1 = object&lt;br /&gt;
|param1text = Object to check.&lt;br /&gt;
|param2 = class&lt;br /&gt;
|param2text = Class/object to check that '''object''' inherits from or is an instance of.&lt;br /&gt;
|example1 = var coord = geo.Coord.new();&lt;br /&gt;
if(isa(coord, geo.Coord)){&lt;br /&gt;
    print(&amp;quot;Variable 'coord' is an instance of class 'geo.Coord'&amp;quot;); # this one will be printed&lt;br /&gt;
} else {&lt;br /&gt;
    print(&amp;quot;Variable 'coord' is not an instance of class 'geo.Coord'&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
|example2 = var coord = geo.Coord.new();&lt;br /&gt;
if(isa(coord, props.Node)){&lt;br /&gt;
    print(&amp;quot;Variable 'coord' is an instance of class 'props.Node'&amp;quot;);&lt;br /&gt;
} else {&lt;br /&gt;
    print(&amp;quot;Variable 'coord' is not an instance of class 'props.Node'&amp;quot;); # this one will be printed&lt;br /&gt;
}&lt;br /&gt;
|example3text = The example below demonstrates checking of inheritance.&lt;br /&gt;
|example3 = var Const = {&lt;br /&gt;
    constant: 2,&lt;br /&gt;
    getConst: func {&lt;br /&gt;
        return me.constant;&lt;br /&gt;
    }&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
var Add = {&lt;br /&gt;
    new: func {&lt;br /&gt;
        return { parents: [Add, Const] };&lt;br /&gt;
    },&lt;br /&gt;
&lt;br /&gt;
    addToConst: func(a){&lt;br /&gt;
        return a * me.getConst();&lt;br /&gt;
    }&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
var m = Add.new();&lt;br /&gt;
print(m.addToConst(4));&lt;br /&gt;
&lt;br /&gt;
if(isa(m, Add)) print(&amp;quot;Variable 'm' is an instance of class 'Add'&amp;quot;); # will be printed&lt;br /&gt;
if(isa(m, Const)) print(&amp;quot;Variable 'm' is an instance of class 'Const'&amp;quot;); # will also be printed&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===logprint()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = &amp;lt;nowiki&amp;gt;logprint(priority[, msg[, msg[, ...]]]);&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=431|t=Source}}&lt;br /&gt;
|text = Concatenates a message and logs it with a given priority level. Unlike {{func link|print()}} and {{func link|printlog()}}, message outputted by this function will be logged in your &amp;lt;code&amp;gt;[[Commonly used debugging tools#fgfs.log|fgfs.log]]&amp;lt;/code&amp;gt; file as coming from the Nasal file itself rather than from {{flightgear file|src/Scripting/NasalSys.cxx}}.&lt;br /&gt;
|param1 = priority&lt;br /&gt;
|param1text = Number specifying the priority level of the outputted message:&lt;br /&gt;
:{{{!}} class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Number !! Debug type&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} 1 {{!!}} Bulk&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} 2 {{!!}} Debug&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} 3 {{!!}} Info&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} 4 {{!!}} Warn&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} 5 {{!!}} Alert&lt;br /&gt;
{{!}}}&lt;br /&gt;
|param2 = msg&lt;br /&gt;
|param2text = The message. There is no limit to the arguments you give give. They will be concatenated together before logging.&lt;br /&gt;
|example1 = # logs the value of pi to three decimal places with log level 3&lt;br /&gt;
logprint(3, &amp;quot;pi = &amp;quot;, sprintf(&amp;quot;%.3f&amp;quot;, math.pi));&lt;br /&gt;
|example2 = logprint(5, &amp;quot;Alert! This is an important message!&amp;quot;);&lt;br /&gt;
}}&lt;br /&gt;
{{note| &lt;br /&gt;
The following constants have been added to the development branch of FlightGear (&amp;quot;next&amp;quot;) and will be releases with FG 2020.1 so you won't have to remember the numbers anymore:&lt;br /&gt;
&lt;br /&gt;
LOG_BULK, LOG_WARN, LOG_DEBUG, LOG_INFO, LOG_ALERT, DEV_WARN, DEV_ALERT }}&lt;br /&gt;
&lt;br /&gt;
===magvar() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = magvar([pos]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1642|t=Source}}&lt;br /&gt;
|text = Returns the {{wikipedia|magnetic variation}} at a given set of coordinates. The table below gives the magnetic model used depending on the version of FlightGear.&lt;br /&gt;
{{{!}} class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! FlightGear versions !! Model !! Reference date&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} 3.6 and above {{!!}} {{wikipedia|World Magnetic Model}} (WMM) 2015 {{!!}} 1 January 2015&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} 0.9.11-pre1 to 3.4 {{!!}} WMM 2005 {{!!}} 1 January 2005&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} 0.7.3 to 0.9.10 {{!!}} WMM 2000 {{!!}} 1 January 2000&lt;br /&gt;
{{!}}}&lt;br /&gt;
|param1 = pos&lt;br /&gt;
|param1text = Optional position to calculate from. If not given, the aircraft's current position will be used. Can be one of:&lt;br /&gt;
:* An &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;runway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;taxiway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fix&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;waypoint&amp;lt;/code&amp;gt; ghost object.&lt;br /&gt;
:* A hash with ''lat'' and ''lon'' members&lt;br /&gt;
:* A &amp;lt;code&amp;gt;geo.Coord&amp;lt;/code&amp;gt; object&lt;br /&gt;
:* A lat/lon pair, that is, a pair of numbers (latitude followed by longitude) separated by a comma: &amp;lt;code&amp;gt;magvar(lat,lon)&amp;lt;/code&amp;gt;.&lt;br /&gt;
|example1 = print(magvar(0, 0)); # prints the magnetic variation at 0, 0&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===maketimer() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = maketimer(interval[, self], function);&lt;br /&gt;
|source = ''Implemented using the {{API Link|flightgear|class|TimerObj}} class.''&amp;lt;br&amp;gt;{{flightgear file|src/Scripting/NasalSys.cxx|l=90|t=Part 1}} {{!}} {{flightgear file|src/Scripting/NasalSys.cxx|l=533|t=Part 2}}&lt;br /&gt;
|version = 2.12&lt;br /&gt;
|commit = {{flightgear commit|ab939f|t=commit}}&lt;br /&gt;
|text = Returns a timer object containing the following methods and members:&lt;br /&gt;
* '''start()''': Starts the timer.&lt;br /&gt;
* '''stop()''': Stops the timer.&lt;br /&gt;
* '''restart(interval)''': Restarts the timer with the given interval.&lt;br /&gt;
* '''singleShot''': Bool showing whether the timer is only to be run once, or continuously until told to stop. Can be both set and read from (see examples).&lt;br /&gt;
* '''isRunning''': Read-only bool telling whether the timer is currently running.&lt;br /&gt;
* '''simulatedTime''': (FG 2017.1+; {{flightgear commit|0af316|t=commit}}) Bool telling whether the timer is using simulated time (which accounts for pause, etc.). Defaults to false (use real time). Can be both read and set. This cannot be changed while the timer is running.&lt;br /&gt;
Unlike {{func link|settimer()}}, which it replaces, &amp;lt;code&amp;gt;maketimer()&amp;lt;/code&amp;gt; provides more control over the timer. In addition, it can help reduce memory usage.&lt;br /&gt;
|param1 = interval&lt;br /&gt;
|param1text = Interval in seconds for the timer.&lt;br /&gt;
|param2 = self&lt;br /&gt;
|param2text = Optional parameter specifying what any &amp;lt;code&amp;gt;'''me'''&amp;lt;/code&amp;gt; references in the function being called will refer to.&lt;br /&gt;
|param3 = function&lt;br /&gt;
|param3text = Function to be called at the given interval.&lt;br /&gt;
|example1 = var timer = maketimer(1, func(){&lt;br /&gt;
    print(&amp;quot;Hello, World!&amp;quot;); # print &amp;quot;Hello, World!&amp;quot; once every second (call timer.stop() to stop it)&lt;br /&gt;
});&lt;br /&gt;
timer.start();&lt;br /&gt;
|example2 = var timer = maketimer(1, math, func(){&lt;br /&gt;
    print(me.math); # 'me' reference is the 'math' namespace&lt;br /&gt;
});&lt;br /&gt;
timer.singleShot = 1; # timer will only be run once&lt;br /&gt;
timer.start();&lt;br /&gt;
|example3 = var timer = maketimer(1, func(){&lt;br /&gt;
    print(&amp;quot;Hello, World!&amp;quot;); # print &amp;quot;Hello, World!&amp;quot; once every second (call timer.stop() to stop it)&lt;br /&gt;
});&lt;br /&gt;
timer.start();&lt;br /&gt;
print(timer.isRunning); # prints 1&lt;br /&gt;
|example4text = In the example below, &amp;quot;Hello, World!&amp;quot; will be printed after one second the first time, and after two seconds thereafter.&lt;br /&gt;
|example4 = var update = func(){&lt;br /&gt;
    print(&amp;quot;Hello, World!&amp;quot;);&lt;br /&gt;
    timer.restart(2); # restarts the timer with a two second interval&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var timer = maketimer(1, update);&lt;br /&gt;
timer.singleShot = 1;&lt;br /&gt;
timer.start();&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===maketimestamp()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = maketimestamp()&lt;br /&gt;
|source = ''Implemented using the {{API Link|flightgear|class|TimeStampObj}} class.''&amp;lt;br&amp;gt;{{flightgear file|src/Scripting/NasalSys.cxx|l=214|t=Part 1}} {{!}} {{flightgear file|src/Scripting/NasalSys.cxx|l=589|t=Part 2}}&lt;br /&gt;
|version = 2019.2&lt;br /&gt;
|commit = {{flightgear commit|7db06300|t=commit}}&lt;br /&gt;
|text = Returns a time stamp object to allow high resolution timing of Nasal operations. When created the timer will automatically be stamped. The object has the following methods:&lt;br /&gt;
* '''stamp()''': Resets the timing operation. Call this first.&lt;br /&gt;
* '''elapsedMSec()''': returns number of milliseconds elapsed since stamp() called. Resolution may vary depending on platform but is usually at least millisecond accuracy.&lt;br /&gt;
* '''elapsedUSec()''': returns number of microseconds elapsed since stamp() called. Resolution may vary depending on platform but is usually at least millisecond accuracy.&lt;br /&gt;
|&lt;br /&gt;
|example1text = In the example below the number of milliseconds elapsed will be printed.&lt;br /&gt;
|example1 = var timestamp = maketimestamp();&lt;br /&gt;
timestamp.stamp();&lt;br /&gt;
print(timestamp.elapsedMSec(), &amp;quot;ms elapsed&amp;quot;);&lt;br /&gt;
print(timestamp.elapsedMSec(), &amp;quot;ms elapsed&amp;quot;);&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===md5()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = md5(string);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=758|t=Source}}&lt;br /&gt;
|version = 3.2&lt;br /&gt;
|commit = {{flightgear commit|cfbf9e|t=commit}}&lt;br /&gt;
|text = Returns a the {{wikipedia|MD5}} hash (as a string) of the inputted string.&lt;br /&gt;
|param1 = string&lt;br /&gt;
|param1text = String the generate the hash of. Mandatory.&lt;br /&gt;
|example1text = The below code should output &amp;lt;code&amp;gt;65a8e27d8879283831b664bd8b7f0ad4&amp;lt;/code&amp;gt;.&lt;br /&gt;
|example1 = print(md5(&amp;quot;Hello, World!&amp;quot;));&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===navinfo()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = navinfo(lat, lon, type, id);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1453|t=Source}}&lt;br /&gt;
|text = Returns vector &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt; ghost objects matching the given '''type''' and '''id''' or &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; on error.&lt;br /&gt;
|param1 = lat ''and'' lon&lt;br /&gt;
|param1text = If given, the returned navaids will be put into order of ascending distance from the location.&lt;br /&gt;
|param2 = type&lt;br /&gt;
|param2text = Narrows the search to the given type. Must be one of &amp;quot;any,&amp;quot; &amp;quot;fix,&amp;quot; &amp;quot;vor,&amp;quot; &amp;quot;ndb,&amp;quot; &amp;quot;ils,&amp;quot; &amp;quot;dme,&amp;quot; or &amp;quot;tacan.&amp;quot; Defaults to the equivalent of &amp;quot;any.&amp;quot;&lt;br /&gt;
|param3 = id&lt;br /&gt;
|param3text = ID to search for. Note that, although all the parameters are technically optional, this parameter must be given, otherwise an empty vector will be returned.&lt;br /&gt;
|example1 = navinfo(&amp;quot;vor&amp;quot;); # returns all VORs&lt;br /&gt;
|example2 = navinfo(&amp;quot;HAM&amp;quot;); # return all navaids whose names start with &amp;quot;HAM&amp;quot;&lt;br /&gt;
|example3 = navinfo(&amp;quot;vor&amp;quot;, &amp;quot;HAM&amp;quot;); # return all VORs whose names start with &amp;quot;HAM&amp;quot;&lt;br /&gt;
|example4 = navinfo(34,48,&amp;quot;vor&amp;quot;,&amp;quot;HAM&amp;quot;); # return all VORs whose names start with &amp;quot;HAM&amp;quot; and sorted by distance relative to 34°, 48°&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===parse_markdown()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = parse_markdown(markdown);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=745|t=Part 1}} {{!}} {{simgear file|simgear/misc/SimpleMarkdown.cxx|t=Part 2}} &lt;br /&gt;
|version = 3.2&lt;br /&gt;
|text = Parses a string containing {{wikipedia|Markdown}} and returns the result as a string. As of FlightGear 2016.1, it is just a simple parser, and does the following:&lt;br /&gt;
* It strips whitespace from the beginning of the string.&lt;br /&gt;
* It supports [http://daringfireball.net/projects/markdown/syntax#p paragraphs and line breaks].&lt;br /&gt;
* It collapses whitespace.&lt;br /&gt;
* It converts unordered [http://daringfireball.net/projects/markdown/syntax#list lists] to use a bullet character (&amp;amp;bull;). Note that the bullet character is implemented in hexadecimal UTF-8 character bytes (&amp;lt;code&amp;gt;E2 80 A2&amp;lt;/code&amp;gt;), as so may not work properly when the being displayed in an encoding other than UTF-8.&lt;br /&gt;
|param1 = markdown&lt;br /&gt;
|param1text = String containing Markdown to be parsed.&lt;br /&gt;
|example1text = &lt;br /&gt;
Save the below code as &amp;lt;tt&amp;gt;''[[$FG_ROOT]]/gui/dialogs/test.xml''&amp;lt;/tt&amp;gt;, then run the Nasal code below it to open the dialog. To change the markdown to be parsed, simply change the code in the highlighted section, save it, and reload the GUI (&amp;lt;tt&amp;gt;Debug &amp;gt; Reload GUI&amp;lt;/tt&amp;gt;).&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot; highlight=&amp;quot;41-50&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;PropertyList&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;name&amp;gt;test&amp;lt;/name&amp;gt;&lt;br /&gt;
&amp;lt;layout&amp;gt;vbox&amp;lt;/layout&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;group&amp;gt;&lt;br /&gt;
    &amp;lt;layout&amp;gt;hbox&amp;lt;/layout&amp;gt;&lt;br /&gt;
    &lt;br /&gt;
    &amp;lt;empty&amp;gt;&lt;br /&gt;
        &amp;lt;stretch&amp;gt;true&amp;lt;/stretch&amp;gt;&lt;br /&gt;
    &amp;lt;/empty&amp;gt;&lt;br /&gt;
    &lt;br /&gt;
    &amp;lt;text&amp;gt;&lt;br /&gt;
        &amp;lt;label&amp;gt;parse_markdown() test dialog&amp;lt;/label&amp;gt;&lt;br /&gt;
    &amp;lt;/text&amp;gt;&lt;br /&gt;
    &lt;br /&gt;
    &amp;lt;empty&amp;gt;&lt;br /&gt;
        &amp;lt;stretch&amp;gt;true&amp;lt;/stretch&amp;gt;&lt;br /&gt;
    &amp;lt;/empty&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    &amp;lt;button&amp;gt;&lt;br /&gt;
        &amp;lt;legend&amp;gt;&amp;lt;/legend&amp;gt;&lt;br /&gt;
        &amp;lt;pref-width&amp;gt;16&amp;lt;/pref-width&amp;gt;&lt;br /&gt;
        &amp;lt;pref-height&amp;gt;16&amp;lt;/pref-height&amp;gt;&lt;br /&gt;
        &amp;lt;binding&amp;gt;&lt;br /&gt;
            &amp;lt;command&amp;gt;dialog-close&amp;lt;/command&amp;gt;&lt;br /&gt;
        &amp;lt;/binding&amp;gt;&lt;br /&gt;
    &amp;lt;/button&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/group&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;canvas&amp;gt;&lt;br /&gt;
    &amp;lt;name&amp;gt;Canvas plot&amp;lt;/name&amp;gt;&lt;br /&gt;
    &amp;lt;stretch&amp;gt;true&amp;lt;/stretch&amp;gt;&lt;br /&gt;
    &amp;lt;pref-width&amp;gt;400&amp;lt;/pref-width&amp;gt;&lt;br /&gt;
    &amp;lt;pref-height&amp;gt;300&amp;lt;/pref-height&amp;gt;&lt;br /&gt;
    &amp;lt;nasal&amp;gt;&lt;br /&gt;
        &amp;lt;load&amp;gt;&amp;lt;![CDATA[&lt;br /&gt;
var text = 'Items:&lt;br /&gt;
* apples&lt;br /&gt;
* oranges&lt;br /&gt;
* pears&lt;br /&gt;
&lt;br /&gt;
Some text.&lt;br /&gt;
Some more items:&lt;br /&gt;
* apples&lt;br /&gt;
* oranges&lt;br /&gt;
* pears';&lt;br /&gt;
&lt;br /&gt;
var parsed = parse_markdown(text);&lt;br /&gt;
&lt;br /&gt;
var root_canvas = canvas.get(cmdarg());&lt;br /&gt;
root_canvas.setColorBackground(255, 255, 255);&lt;br /&gt;
var root = root_canvas.createGroup();&lt;br /&gt;
&lt;br /&gt;
var text_dis = root.createChild(&amp;quot;text&amp;quot;)&lt;br /&gt;
    .setText(parsed)&lt;br /&gt;
    .setTranslation(5, 5)&lt;br /&gt;
    .setFont(&amp;quot;LiberationFonts\LiberationSans-Regular.ttf&amp;quot;)&lt;br /&gt;
    .setFontSize(15)&lt;br /&gt;
    .setColor(0, 0, 0)&lt;br /&gt;
    .setDrawMode(canvas.Text.TEXT)&lt;br /&gt;
    .setAlignment(&amp;quot;left-top&amp;quot;);&lt;br /&gt;
        ]]&amp;gt;&amp;lt;/load&amp;gt;&lt;br /&gt;
    &amp;lt;/nasal&amp;gt;&lt;br /&gt;
&amp;lt;/canvas&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/PropertyList&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|example1 = fgcommand(&amp;quot;dialog-show&amp;quot;, {&amp;quot;dialog-name&amp;quot;: &amp;quot;test&amp;quot;});&lt;br /&gt;
|example2text = The example below parses Markdown and outputs it in a HTML document. The parsed text is placed in &amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot; inline&amp;gt;&amp;lt;pre&amp;gt;&amp;lt;/pre&amp;gt;&amp;lt;/syntaxhighlight&amp;gt; tags. To change the Markdown to be parsed, simply edit the variable &amp;lt;tt&amp;gt;markdown&amp;lt;/tt&amp;gt; at the top of the code.&lt;br /&gt;
|example2 = &amp;lt;nowiki&amp;gt;var markdown = 'Items:&lt;br /&gt;
* apples&lt;br /&gt;
* oranges&lt;br /&gt;
* pears&lt;br /&gt;
&lt;br /&gt;
Some text.&lt;br /&gt;
Some more items:&lt;br /&gt;
* apples&lt;br /&gt;
* oranges&lt;br /&gt;
* pears';&lt;br /&gt;
&lt;br /&gt;
var parsed = parse_markdown(markdown);&lt;br /&gt;
&lt;br /&gt;
debug.dump(parsed);&lt;br /&gt;
&lt;br /&gt;
var path = string.normpath(getprop(&amp;quot;/sim/fg-home&amp;quot;) ~ '/Export/parse_markdown()-test.html');&lt;br /&gt;
&lt;br /&gt;
var file = io.open(path, &amp;quot;w&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
var html = &amp;quot;&amp;lt;!DOCTYPE html&amp;gt;\n\n&amp;lt;html&amp;gt;\n\n&amp;lt;head&amp;gt;\n\t&amp;lt;meta charset=\&amp;quot;UTF-8\&amp;quot;&amp;gt;\n\t&amp;lt;title&amp;gt;parse_markdown() test generated by Nasal&amp;lt;/title&amp;gt;\n&amp;lt;/head&amp;gt;\n\n&amp;lt;body&amp;gt;\n\t&amp;lt;pre&amp;gt;&amp;quot; ~ parsed ~ &amp;quot;&amp;lt;/pre&amp;gt;\n&amp;lt;/body&amp;gt;\n\n&amp;lt;/html&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
io.write(file, html);&lt;br /&gt;
io.close(file);&lt;br /&gt;
print(&amp;quot;Done, file ready for viewing (&amp;quot; ~ path ~ &amp;quot;)&amp;quot;);&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===parsexml()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = &amp;lt;nowiki&amp;gt;parsexml(path[, start[, end[, data[, pro_ins]]]]);&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=710|t=Source}}&lt;br /&gt;
|text = This function is an interface into the built-in [http://expat.sourceforge.net/ Expat XML parser]. The absolute path to the file is returned as string, or &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; is returned on error.&lt;br /&gt;
|param1 = path&lt;br /&gt;
|param1text = Mandatory absolute path to the XML file to be parsed.&lt;br /&gt;
|param2 = start&lt;br /&gt;
|param2text = Optional callback function that will be called for every starting tag. The function should take two argument: the tag name and a hash containing attributes.&lt;br /&gt;
|param3 = end&lt;br /&gt;
|param3text = Optional callback function that will be called for every ending tag. The function should take one argument: the tag name.&lt;br /&gt;
|param4 = data&lt;br /&gt;
|param4text = Optional callback function that will be called for every piece of data within a set of tags. The function should take one argument: the data as a string.&lt;br /&gt;
|param5 = pro_ins&lt;br /&gt;
|param5text = Optional callback function that will be called for every {{wikipedia|Processing Instruction|processing instruction}}. The function should take two argument: the target and the data string.&lt;br /&gt;
|example1text = Save the below XML code in &amp;lt;tt&amp;gt;''[[$FG_HOME]]/Export/demo.xml''&amp;lt;/tt&amp;gt;. Then, execute the Nasal code below in the Nasal Console. The XML will be parsed and each bit of the code will be printed.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;?xml-stylesheet type=&amp;quot;text/xsl&amp;quot; href=&amp;quot;style.xsl&amp;quot;?&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;foo&amp;gt;&lt;br /&gt;
  &amp;lt;blah type=&amp;quot;string&amp;quot;&amp;gt;&amp;lt;![CDATA[ &amp;lt;sender&amp;gt;John Smith&amp;lt;/sender&amp;gt; ]]&amp;gt;&amp;lt;/blah&amp;gt;&lt;br /&gt;
  &amp;lt;blah2 type=&amp;quot;string&amp;quot;&amp;gt;Orange &amp;amp;amp; lemons&amp;lt;/blah2&amp;gt;&lt;br /&gt;
&amp;lt;/foo&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|example1 = var start = func(name, attr){&lt;br /&gt;
    print(&amp;quot;Starting tag: '&amp;quot;, name, &amp;quot;'&amp;quot;);&lt;br /&gt;
    foreach(var a; keys(attr)){&lt;br /&gt;
        print(&amp;quot;\twith attribute &amp;quot;, a, '=&amp;quot;', attr[a], '&amp;quot;');&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var end = func(name){&lt;br /&gt;
    print(&amp;quot;Ending tag: '&amp;quot;, name, &amp;quot;'&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var data = func(data){&lt;br /&gt;
    print(&amp;quot;Data = '&amp;quot;, data, &amp;quot;'&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var pro_instr = func(target, data){&lt;br /&gt;
    print(&amp;quot;Processing instruction: target = '&amp;quot;, target, &amp;quot;', data = '&amp;quot;, data, &amp;quot;'&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
parsexml(getprop(&amp;quot;/sim/fg-home&amp;quot;) ~ '/Export/demo.xml', start, end, data, pro_instr);&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===print()===&lt;br /&gt;
{{Note|As of 07/2020, we are in the process of slowly deprecating and removing Nasal print() (in fgdata) where possible in favor of log [[#logprint()]] which takes a priority level.&lt;br /&gt;
&lt;br /&gt;
This is part of the goal that we achieve zero output at log-level at alert, and everything shown at ‘warn; is really a warning, not just information.&amp;lt;ref&amp;gt;https://sourceforge.net/p/flightgear/mailman/message/37042224/&amp;lt;/ref&amp;gt;}}&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = &amp;lt;nowiki&amp;gt;print(data[, data[, ...]]);&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=415|t=Source}}&lt;br /&gt;
|text = Concatenates its arguments and then prints it to the terminal and the [[Commonly used debugging tools#fgfs.log|log]]. Note that a newline is automatically added.&lt;br /&gt;
|param1 = data&lt;br /&gt;
|param1text = Data to print. Only strings and numbers can be printed; other data types will not be. There many be any number of arguments; they will just be concatenated together.&lt;br /&gt;
|example1 = print(&amp;quot;Just&amp;quot;, &amp;quot; a &amp;quot;, &amp;quot;test&amp;quot;); # prints &amp;quot;Just a test&amp;quot;&lt;br /&gt;
|example2 = print(&amp;quot;pi = &amp;quot;, math.pi); # prints &amp;quot;pi = 3.141592...&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== printf() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = &amp;lt;nowiki&amp;gt;printf(format[, arg[, arg, [...]]]);&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|source = {{fgdata file|Nasal/globals.nas|t=Source}}&lt;br /&gt;
|text = Creates and prints a formatted string. For a description of its arguments, see {{func link|sprintf()}} (it is, in fact, implemented using &amp;lt;code&amp;gt;sprintf()&amp;lt;/code&amp;gt;).&lt;br /&gt;
|example1 = printf(&amp;quot;In hexadecimal, 100000 = %X&amp;quot;, 186A0); # prints &amp;quot;In hexadecimal, 100000 = 186A0&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===printlog()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = &amp;lt;nowiki&amp;gt;printlog(level, data[, data[, ...]]);&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|source = {{fgdata file|Nasal/globals.nas|t=Source}}&lt;br /&gt;
|text = Prints the given message with the given log level. If the log level is higher or equal to &amp;lt;code&amp;gt;/sim/logging/priority&amp;lt;/code&amp;gt;, it is printed.&lt;br /&gt;
|param1 = level&lt;br /&gt;
|param1text = Mandatory log level as a string. Must be one of &amp;quot;none,&amp;quot; &amp;quot;bulk,&amp;quot; &amp;quot;debug,&amp;quot; &amp;quot;info,&amp;quot; &amp;quot;warn,&amp;quot; or &amp;quot;alert.&amp;quot; Note that &amp;quot;none&amp;quot; will mean that the message will never be printed.&lt;br /&gt;
|param2 = data&lt;br /&gt;
|param2text = Data to be printed. Only strings and numbers will be printed; others will not be. There may be any number of arguments; they will just be concatenated together.&lt;br /&gt;
|example1 = printlog(&amp;quot;alert&amp;quot;, &amp;quot;This is an alert&amp;quot;); # message will be printed&lt;br /&gt;
|example2 = printlog(&amp;quot;info&amp;quot;, &amp;quot;Just informing you about something&amp;quot;); # message will be printed only if log level is set to &amp;quot;info&amp;quot; or less&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===rand()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = rand();&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=554|t=Source}}&lt;br /&gt;
|text = Returns a random floating point number between 0 (inclusive) and 1 (exclusive). It takes no arguments.&lt;br /&gt;
|example1 = print(rand()); # prints random number&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===registerFlightPlanDelegate()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = registerFlightPlanDelegate(init_func);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1879|t=Source}}&lt;br /&gt;
|text = Registers a flight plan delegate. See &amp;lt;tt&amp;gt;''{{fgdata file|Nasal/route_manager.nas|t=$FG_ROOT/Nasal/route_manager.nas}}''&amp;lt;/tt&amp;gt; for examples.&lt;br /&gt;
|param1 = init_func&lt;br /&gt;
|param1text = Initialization function which will be called during FlightGear's startup.&lt;br /&gt;
}}&lt;br /&gt;
===removecommand()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = removecommand(cmd);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=668|t=Source}}&lt;br /&gt;
|text = Removes the given fgcommand. Returns &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt;.&lt;br /&gt;
{{caution|This will remove '''any''' [[fgcommands|fgcommand]], even those implemented in C++, so use with caution!}}&lt;br /&gt;
|param1 = cmd&lt;br /&gt;
|param1text = String specifying the name of the command to remove.&lt;br /&gt;
|example1 = addcommand(&amp;quot;hello&amp;quot;, func(){&lt;br /&gt;
    print(&amp;quot;Hello&amp;quot;);&lt;br /&gt;
});&lt;br /&gt;
fgcommand(&amp;quot;hello&amp;quot;); # &amp;quot;Hello&amp;quot; will be printed&lt;br /&gt;
removecommand(&amp;quot;hello&amp;quot;); # removes it&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===removelistener()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = removelistener(id);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=1384|t=Part 1}} {{!}} {{flightgear file|src/Scripting/NasalSys.cxx|l=506|t=Part 2}}&lt;br /&gt;
|text = Removes and deactivates the given listener and returns the number of listeners left or &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; on error.&lt;br /&gt;
{{note|It is good practice to remove listeners when they are not required anymore. This prevents the listeners reducing FlightGear's run performance.}}&lt;br /&gt;
|param1 = id&lt;br /&gt;
|param1text = ID of listener as returned by {{func link|setlistener()}}.&lt;br /&gt;
|example1 = var ls = setlistener(&amp;quot;/sim/test&amp;quot;, func(){&lt;br /&gt;
    print(&amp;quot;Property '/sim/test' has been changed&amp;quot;);&lt;br /&gt;
});&lt;br /&gt;
setprop(&amp;quot;/sim/test&amp;quot;, &amp;quot;blah&amp;quot;); # trigger listener&lt;br /&gt;
var rem = removelistener(ls); # remove listener&lt;br /&gt;
print(&amp;quot;There are &amp;quot;, rem, &amp;quot; listeners remaining&amp;quot;);&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===resolvepath()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = resolvepath(path);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=604|t=Source}}&lt;br /&gt;
|text = Takes a relative path as a string and uses [[SimGear]]'s path-resolving framework to return an absolute path as a string. If the path could not be resolved, an empty string is returned. See [[Resolving Paths]] for a detailed description of the algorithm. This function can also be used to check if a file exists.&lt;br /&gt;
|param1 = path&lt;br /&gt;
|param1text = Relative path to be completed.&lt;br /&gt;
|example1 = print(resolvepath(&amp;quot;Nasal/globals.nas&amp;quot;)); # prints the equivalent of $FG_ROOT/Nasal/globals.nas&lt;br /&gt;
|example2 = print(resolvepath(&amp;quot;blah&amp;quot;)); # prints nothing; could not be resolved&lt;br /&gt;
|example3 = var file_path = resolvepath(&amp;quot;Aircraft/SenecaII/some-file&amp;quot;);&lt;br /&gt;
if (file_path != &amp;quot;&amp;quot;){&lt;br /&gt;
    gui.popupTip(&amp;quot;some-file found&amp;quot;, 2);&lt;br /&gt;
} else {&lt;br /&gt;
    gui.popupTip(&amp;quot;some-file not found&amp;quot;, 2);&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===setlistener()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = &amp;lt;nowiki&amp;gt;setlistener(node, code[, init[, type]]);&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|private = _setlistener()&lt;br /&gt;
|source = {{flightgear file|src/Scripting/Nasal|l=499|t=Part 1}} {{!}} {{flightgear file|src/Scripting/Nasal|l=1350|t=Part 2}}&amp;lt;br&amp;gt;''Listener implemented using the {{API Link|flightgear|class|FGNasalListener}} class.''&lt;br /&gt;
|text = Creates a listener which will be triggered when the given property is changed (depending on the '''type'''). A unique integer ID is returned; this can later be used as the argument to {{func link|removelistener()}}.&lt;br /&gt;
{{note|Listeners are known to be a source of resource leaks. To avoid this, please take measures such as:&lt;br /&gt;
* Using {{func link|removelistener()}} when they are not needed any more.&lt;br /&gt;
* Using a single initialization listener.&lt;br /&gt;
* Avoiding tying listeners to properties that are rapidly updated (e.g., many times per frame).&lt;br /&gt;
}}&lt;br /&gt;
|param1 = node&lt;br /&gt;
|param1text = Mandatory string or &amp;lt;code&amp;gt;props.Node&amp;lt;/code&amp;gt; object pointing to a property in the [[Property Tree]].&lt;br /&gt;
|param2 = code&lt;br /&gt;
|param2text = Mandatory callback function to execute when the listener is triggered. The function can take up to four arguments in the following order:&lt;br /&gt;
* '''changed''': a &amp;lt;code&amp;gt;props.Node&amp;lt;/code&amp;gt; object pointing to the changed node.&lt;br /&gt;
* '''listen''': a &amp;lt;code&amp;gt;props.Node&amp;lt;/code&amp;gt; object pointing to the listened-to node. Note that this argument maybe different depending on the '''type'''.&lt;br /&gt;
* '''mode''': an integer telling how the listener was triggered. 0 means that the value was changed. 1 means that a child property was added. -1 means that a child property was removed.&lt;br /&gt;
* '''is_child''': boolean telling whether '''changed''' is a child property of the listened-to '''node''' or not. 1 (true) if it is, 0 (false) otherwise.&lt;br /&gt;
|param3 = init&lt;br /&gt;
|param3text = If set to 1 (true), the listener will additionally be triggered when it is created. This argument is optional and defaults to 0 (false).&lt;br /&gt;
|param4 = type&lt;br /&gt;
|param4text = Integer specifying the listener's behavior. 0 means that the listener will only trigger when the property is changed. 1 means that the trigger will always be triggered when the property is written to. 2 will mean that the listener will be triggered even if child properties are modified. This argument is optional and defaults to 1.&lt;br /&gt;
|example1 = var prop = props.globals.initNode(&amp;quot;/sim/test&amp;quot;, &amp;quot;&amp;quot;, &amp;quot;STRING&amp;quot;); # create property&lt;br /&gt;
var id = setlistener(&amp;quot;/sim/test&amp;quot;, func(n){ # create listener&lt;br /&gt;
    print(&amp;quot;Value: &amp;quot;, n.getValue());&lt;br /&gt;
});&lt;br /&gt;
setprop(&amp;quot;/sim/test&amp;quot;, &amp;quot;blah&amp;quot;); # trigger listener&lt;br /&gt;
removelistener(id); # remove listener&lt;br /&gt;
prop.remove(); # remove property&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===setprop()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = &amp;lt;nowiki&amp;gt;setprop(path[, path[, ...]], value);&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=385|t=Source}}&lt;br /&gt;
|text = Sets the value of a property in the [[Property Tree]]. If the property does not exist, it will be created. Returns 1 (true) on success or 0 (false) if there was an error.&lt;br /&gt;
{{note|If you want to remove a property, you will have to use one of the &amp;lt;code&amp;gt;props&amp;lt;/code&amp;gt; helpers:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
props.globals.getNode(&amp;quot;foo/bar&amp;quot;).remove(); # take out the complete node&lt;br /&gt;
props.globals.getNode(&amp;quot;foo&amp;quot;).removeChild(&amp;quot;bar&amp;quot;); # take out a certain child node&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
|param1 = path&lt;br /&gt;
|param1text = There needs to be at least one '''path''' argument, but there is no limit to the maximum amount that can be given. They will be concatenated together to form a property tree path. The arguments must be strings, but in FlightGear v3.2 onwards, there also is support (added by {{flightgear commit|34ed79}}) for numeric arguments as indices. See example 2 below.&lt;br /&gt;
|param2 = value&lt;br /&gt;
|param2text = Value to write to the given property. Must be either a string or a number.&lt;br /&gt;
|example1 = setprop(&amp;quot;/sim/demo&amp;quot;, &amp;quot;This is a demo&amp;quot;);&lt;br /&gt;
|example2text = Note that the example below will only work in FlightGear 3.2 and above.&lt;br /&gt;
|example2 = for(var i = 0; i &amp;lt; 3; i += 1){&lt;br /&gt;
    setprop(&amp;quot;/sim/demo&amp;quot;, i, &amp;quot;Demo #&amp;quot; ~ i));&lt;br /&gt;
}&lt;br /&gt;
|example3text = Same as above, but is supported by all versions of FlightGear.&lt;br /&gt;
|example3 = for(var i = 0; i &amp;lt; 3; i += 1){&lt;br /&gt;
    setprop(&amp;quot;/sim/demo[&amp;quot; ~ i ~ &amp;quot;]&amp;quot;, &amp;quot;Demo #&amp;quot; ~ i));&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
====See also====&lt;br /&gt;
{{note| If you have to read/write the same property multiple times (e.g. in an update loop), it is more efficient to use a node object: &lt;br /&gt;
To get a Node rather than its value, use &amp;lt;code&amp;gt;props.globals.getNode()&amp;lt;/code&amp;gt; - see [[Nasal_library/props]]. }}&lt;br /&gt;
&lt;br /&gt;
===settimer() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = settimer(function, delta[, realtime]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=499|t=Part 1}} {{!}} {{flightgear file|src/Scripting/NasalSys.cxx|l=1286|t=Part 2}}&lt;br /&gt;
|text = Runs the given function a specified amount of seconds after the current time. Returns &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt;.&lt;br /&gt;
{{note|Improper use of &amp;lt;code&amp;gt;settimer()&amp;lt;/code&amp;gt; may cause resource leaks. It is also highly recommended that the newer {{func link|maketimer()}} should be used instead of this function.}} &lt;br /&gt;
|param1 = function&lt;br /&gt;
|param1text = Mandatory function that will be called. It may be necessary to enclose code in an anonymous function (see example).&lt;br /&gt;
|param2 = delta&lt;br /&gt;
|param2text = Mandatory amount of time in seconds after which the function will be called.&lt;br /&gt;
|param3 = realtime&lt;br /&gt;
|param3text = If 1 (true), &amp;quot;real time&amp;quot; will be used instead of &amp;quot;simulation time.&amp;quot; Defaults to 0 (false). Note that if &amp;quot;simulation time&amp;quot; is used, the timer will not run while FlightGear is paused.&lt;br /&gt;
|example1 = var myFunc = func(){&lt;br /&gt;
    print(&amp;quot;Hello&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
settimer(myFunc, 2); # runs myFunc after 2 seconds&lt;br /&gt;
|example2 = var sqr = func(a){&lt;br /&gt;
    return a * a;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
settimer(func(){&lt;br /&gt;
    print(sqr(2)); # will print 4 after 2 seconds&lt;br /&gt;
}, 2);&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===srand() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = srand();&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=559|t=Source}}&lt;br /&gt;
|text = Makes the pseudorandom number generator (see {{func link|rand()}}) generate a new {{wikipedia|random seed|noicon=1}} based on time. Returns 0.&lt;br /&gt;
}}&lt;br /&gt;
===systime()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = systime();&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=770|t=Source}}&lt;br /&gt;
|text = Returns the {{wikipedia|Unix time}} (seconds since since 00:00:00 UTC, 1/1/1970) as a floating point number with high resolution. This function is useful for benchmarking purposes (see example 2).&lt;br /&gt;
{{note|1=High resolution timers under Windows can produce inaccurate or fixed sub-millisecond results.&amp;lt;ref&amp;gt;{{cite web|url=http://forum.flightgear.org/viewtopic.php?f=30&amp;amp;t=29259|title=Nasal: systime() ??!?|author=Necolatis|date=Apr 2nd, 2016}}&amp;lt;/ref&amp;gt; This is due to the underlying {{func link|GetSystemTimeAsFileTime()|link=https://msdn.microsoft.com/en-us/library/windows/desktop/ms724397(v=vs.85).aspx}} API call, which depends on hardware availability of suitable high resolution timers. See also [https://msdn.microsoft.com/en-us/library/windows/desktop/dn553408(v=vs.85).aspx Acquiring high-resolution time stamps]}}&lt;br /&gt;
|example1 = print(&amp;quot;Unix time: &amp;quot;, systime()); # prints Unix time&lt;br /&gt;
|example2 = var myFunc = func(){&lt;br /&gt;
    for(var i = 0; i &amp;lt;= 10; i += 1){&lt;br /&gt;
        print(&amp;quot;Interation #&amp;quot;, i);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
var t = systime(); # record time&lt;br /&gt;
myFunc(); # run function&lt;br /&gt;
var t2 = systime(); # record new time&lt;br /&gt;
print(&amp;quot;myFunc() took &amp;quot;, t2 - t, &amp;quot; seconds&amp;quot;); # print result&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===thisfunc()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = thisfunc();&lt;br /&gt;
|source = {{fgdata file|Nasal/globals.nas|t=Source}}&lt;br /&gt;
|text = Returns the function from which this function is called. This allows a function to reliably and safely call itself from within a closure.&lt;br /&gt;
|example1 = var stringify_vec = func(input){&lt;br /&gt;
    if (typeof(input) == &amp;quot;scalar&amp;quot;){&lt;br /&gt;
        return sprintf(&amp;quot;%s&amp;quot;, input);&lt;br /&gt;
    } elsif (typeof(input) == &amp;quot;vector&amp;quot;) {&lt;br /&gt;
        if (size(input) == 0) return &amp;quot;[]&amp;quot;;&lt;br /&gt;
        var this = thisfunc();&lt;br /&gt;
        var buffer = &amp;quot;[&amp;quot;;&lt;br /&gt;
        for(var i = 0; i &amp;lt; size(input); i += 1){&lt;br /&gt;
            buffer ~= this(input[i]);&lt;br /&gt;
            if (i == size(input) - 1) {&lt;br /&gt;
                buffer ~= &amp;quot;]&amp;quot;;&lt;br /&gt;
            } else {&lt;br /&gt;
                buffer ~= &amp;quot;, &amp;quot;;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        return buffer;&lt;br /&gt;
    } else {&lt;br /&gt;
        die(&amp;quot;stringify_vec(): Error! Invalid input. Must be a vector or scalar&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var test_vec = [&amp;quot;a&amp;quot;, &amp;quot;b&amp;quot;, &amp;quot;c&amp;quot;, 1, 2, 3];&lt;br /&gt;
debug.dump(stringify_vec(test_vec)); # prints &amp;quot;[a, b, c, 1, 2, 3]&amp;quot;&lt;br /&gt;
test_vec = [];&lt;br /&gt;
debug.dump(stringify_vec(test_vec)); # prints &amp;quot;[]&amp;quot;&lt;br /&gt;
test_vec = {};&lt;br /&gt;
debug.dump(stringify_vec(test_vec)); # will throw an error&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===tileIndex() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = tileIndex();&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1720|t=Source}}&lt;br /&gt;
|text = Returns the index of the tile at the aircraft's current position as a string. This corresponds to the name of the STG file of the tile. For example, at [[KSFO]], this would be &amp;lt;code&amp;gt;942050&amp;lt;/code&amp;gt;, corresponding to &amp;lt;tt&amp;gt;''[[$FG_SCENERY]]/Terrain/w130n30/w123n3/942050.stg''&amp;lt;/tt&amp;gt;.&lt;br /&gt;
|example1 = print(tileIndex()); # print index&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== tilePath()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = tilePath();&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1712|t=Source}}&lt;br /&gt;
|text = Returns the base path of the tile at the aircraft's current position as a string. For example, at KSFO, this would be &amp;lt;code&amp;gt;w130n30/w123n3&amp;lt;/code&amp;gt;, corresponding to &amp;lt;tt&amp;gt;''[[$FG_SCENERY]]/Terrain/w130n30/w123n3''&amp;lt;/tt&amp;gt;.&lt;br /&gt;
|example1 = print(tilePath()); # print path&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== values()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = values(hash);&lt;br /&gt;
|source = {{fgdata file|Nasal/globals.nas|t=Source}}&lt;br /&gt;
|text = Returns a vector containing the values of the given hash.&lt;br /&gt;
|param1 = hash&lt;br /&gt;
|param1text = Mandatory hash to get the values of.&lt;br /&gt;
|example1 = var hash = {&lt;br /&gt;
    &amp;quot;a&amp;quot;: 1,&lt;br /&gt;
    &amp;quot;b&amp;quot;: 2,&lt;br /&gt;
    &amp;quot;c&amp;quot;: 3&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
foreach(var val; values(hash)){&lt;br /&gt;
    print(val);&lt;br /&gt;
}&lt;br /&gt;
|example2text = The below example does exactly the same thing as the above example, but does not use &amp;lt;code&amp;gt;values()&amp;lt;/code&amp;gt;:&lt;br /&gt;
|example2 = var hash = {&lt;br /&gt;
    &amp;quot;a&amp;quot;: 1,&lt;br /&gt;
    &amp;quot;b&amp;quot;: 2,&lt;br /&gt;
    &amp;quot;c&amp;quot;: 3&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
foreach(var key; keys(hash)){&lt;br /&gt;
    print(hash[key]);&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
==Extension functions new in FG 2020.1 ==&lt;br /&gt;
The following functions have been added to the nasal core library and will be released with FlightGear version 2020.1. &lt;br /&gt;
Before the release they are available in the development branch &amp;quot;next&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
===isfunc()===&lt;br /&gt;
Returns 1 if type or argument is a function, otherwise 0.&lt;br /&gt;
&lt;br /&gt;
===isghost()===&lt;br /&gt;
Returns 1 if type or argument is a ghost, otherwise 0.&lt;br /&gt;
&lt;br /&gt;
=== ishash()===&lt;br /&gt;
Returns 1 if type or argument is a hash, otherwise 0.&lt;br /&gt;
&lt;br /&gt;
===isint()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = isint(x);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|t=Source}}&lt;br /&gt;
|text = Returns 1 if argument has a numeric value and x == floor(x), e.g. integer.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===isnum()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = isnum(x);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|t=Source}}&lt;br /&gt;
|text = Returns 1 if typeof(x) is &amp;quot;scalar&amp;quot; and x has a numeric value otherwise 0. &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===isscalar()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = isscalar(x);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|t=Source}}&lt;br /&gt;
|text = Returns 1 if type or argument is a scalar (string or numeric), otherwise (vector, hash, func, ...) it returns 0. This is useful to check if a variable can be converted to string e.g. when useing the string concat operator &amp;quot;~&amp;quot;. &lt;br /&gt;
|example1 = var a = &amp;quot;foo&amp;quot;; &lt;br /&gt;
var b=42;&lt;br /&gt;
if (isscalar(a) and isscalar(b)) print(a~b);&lt;br /&gt;
if (isstr(a)) print(&amp;quot;a is a string&amp;quot;);&lt;br /&gt;
if (isint(b)) print(&amp;quot;b is an integer&amp;quot;);&lt;br /&gt;
# if (isscalar(a))... is equivalent to if (typeof(a) == &amp;quot;scalar&amp;quot;)...&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===isstr()===&lt;br /&gt;
Returns 1 if type or argument is a string, otherwise 0. &lt;br /&gt;
&lt;br /&gt;
===isvec()===&lt;br /&gt;
Returns 1 if type or argument is a vector, otherwise 0.&lt;br /&gt;
&lt;br /&gt;
===vecindex()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = vecindex(vector, value);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|t=Source}}&lt;br /&gt;
|text = Returns the index of value or nil, if value is not found in vector.&lt;br /&gt;
|example1=&lt;br /&gt;
var myvector = [&amp;quot;apple&amp;quot;, &amp;quot;bananna&amp;quot;, &amp;quot;coconut&amp;quot;];&lt;br /&gt;
# to check if a vector contains a certain value compare vecindex to nil&lt;br /&gt;
if (vecindex(myvector, &amp;quot;apple&amp;quot;) != nil) {&lt;br /&gt;
    print(&amp;quot;found apple&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
# WARNING: this will not work as desired because index is 0&lt;br /&gt;
if (vecindex(myvector, &amp;quot;apple&amp;quot;)) {&lt;br /&gt;
    print(&amp;quot;found apple&amp;quot;);&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
==Variables==&lt;br /&gt;
Various global constants (technically variables) are provided for use in converting between different units. They are all found in {{fgdata file|Nasal/globals.nas|text=$FG_ROOT/Nasal/globals.nas}}.&lt;br /&gt;
&lt;br /&gt;
===D2R===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var radians = degrees * D2R;&lt;br /&gt;
|text = Converts an angle from degrees to radians when multiplied by the angle in degrees. Equal to &amp;lt;code&amp;gt;π / 180&amp;lt;/code&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
===FPS2KT===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var knots = feet_per_second * FPS2KT;&lt;br /&gt;
|text = Converts a velocity from feet per second to knots when multiplied by the velocity in feet per second. Approximately equal to 0.5925.&lt;br /&gt;
}}&lt;br /&gt;
===FT2M===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var metres = feet * FT2M;&lt;br /&gt;
|text = Converts a length from feet to metres when multiplied by the length in feet. Equal to 0.3048.&lt;br /&gt;
}}&lt;br /&gt;
===GAL2L ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var litres = gallons * GAL2L;&lt;br /&gt;
|text = Converts a volume from US liquid gallons to litres when multiplied by the volume in gallons. Approximately equal to 3.7854.&lt;br /&gt;
}}&lt;br /&gt;
===IN2M===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var metres = inches * IN2M;&lt;br /&gt;
|text = Converts a length from inches to metres when multiplied by the length in inches. Equal to 0.0254.&lt;br /&gt;
}}&lt;br /&gt;
===KG2LB===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var pounds = kilograms * KG2LB;&lt;br /&gt;
|text = Converts a mass from kilograms to pounds when multiplied by the mass in kilograms. Approximately equal to 2.2046.&lt;br /&gt;
}}&lt;br /&gt;
===KT2FPS===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var feet_per_second = knots * KT2FPS;&lt;br /&gt;
|text = Converts a velocity from knots to feet per second when multiplied by the velocity in knots. Approximately equal to 1.6878.&lt;br /&gt;
}}&lt;br /&gt;
===KT2MPS===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var metres_per_second = knots * KT2MPS;&lt;br /&gt;
|text = Converts a velocity from knots to metres per second when multiplied by the velocity in knots. Approximately equal to 0.5144.&lt;br /&gt;
}}&lt;br /&gt;
===L2GAL===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var gallons = litres * L2GAL;&lt;br /&gt;
|text = Converts a volume from litres to US liquid gallons when multiplied by the volume in litres. Approximately equal to 0.2642.&lt;br /&gt;
}}&lt;br /&gt;
===LB2KG===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var kilograms = pounds * LB2KG;&lt;br /&gt;
|text = Converts a mass from pounds to kilograms when multiplied by the mass in pounds. Approximately equal to 0.4536.&lt;br /&gt;
}}&lt;br /&gt;
===M2FT ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var feet = metres * M2FT;&lt;br /&gt;
|text = Converts a length from metres to feet when multiplied by the length in metres. Approximately equal to 3.2808.&lt;br /&gt;
}}&lt;br /&gt;
===M2IN===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var inches = metres * M2IN;&lt;br /&gt;
|text = Converts a length from metres to inches when multiplied by the length in metres. Approximately equal to 39.3701.&lt;br /&gt;
}}&lt;br /&gt;
===M2NM ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var nautical_miles = metres * M2NM;&lt;br /&gt;
|text = Converts a length from metres to nautical miles when multiplied by the length in metres. Approximately equal to 0.00054.&lt;br /&gt;
}}&lt;br /&gt;
===MPS2KT ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var knots = metres_per_second * MPS2KT;&lt;br /&gt;
|text = Converts a velocity from metres per second to knots when multiplied by the velocity in metres per second. Approximately equal to 1.9438.&lt;br /&gt;
}}&lt;br /&gt;
===NM2M ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var metres = nautical_miles * NM2M;&lt;br /&gt;
|text = Converts a length from nautical miles to metres when multiplied by the length in nautical miles. Equal to 1,852.&lt;br /&gt;
}}&lt;br /&gt;
===R2D===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var degrees = radians * R2D;&lt;br /&gt;
|text = Converts an angle from radians to degrees when multiplied by the angle in radians. Equal to &amp;lt;code&amp;gt;180 / π&amp;lt;/code&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Appendix}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Nasal namespaces}}&lt;/div&gt;</summary>
		<author><name>Clm76</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Nasal_library&amp;diff=138564</id>
		<title>Nasal library</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Nasal_library&amp;diff=138564"/>
		<updated>2023-10-30T09:52:45Z</updated>

		<summary type="html">&lt;p&gt;Clm76: /* findNavaidByFrequencyMHz() */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Nasal Navigation|nocat=1}}&lt;br /&gt;
This page documents the global '''library functions and variables''' of FlightGear's built-in scripting language, [[Nasal]]. This includes ''[[#Core library functions|core library functions]]'', which were included in Nasal before its integration into FlightGear, the ''[[#Extension functions|extension functions]]'', which have been subsequently added, and are specifically designed for FlightGear, and the ''[[#variables|global variables]]'', which are conversion variables, added with extension functions, for converting between units. The relevant folders in [[Git]] are:&lt;br /&gt;
* {{flightgear file|src/Scripting}}&lt;br /&gt;
* {{simgear file|simgear/nasal}}&lt;br /&gt;
&lt;br /&gt;
All these functions and variables are in the global namespace, that is, they are directly accessible (e.g., one can call &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; inline&amp;gt;magvar()&amp;lt;/syntaxhighlight&amp;gt; instead of &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; inline&amp;gt;namespace.magvar()&amp;lt;/syntaxhighlight&amp;gt;). However, if a namespace must be used, &amp;lt;code&amp;gt;globals&amp;lt;/code&amp;gt; is the correct namespace, but using it is not recommended. For a more complete explanation, see [[Nasal Namespaces in-depth]].&lt;br /&gt;
&lt;br /&gt;
{{tip|Copy &amp;amp; paste the examples into your [[Nasal Console]] and execute them to see what they do.|width=70%}}&lt;br /&gt;
&lt;br /&gt;
== Core library functions ==&lt;br /&gt;
This is the list of the basic '''core library functions.''' Most of these functions were part of the original Nasal library (before its integration in to FlightGear), while some have been added or changed over time.  See also:&lt;br /&gt;
* http://plausible.org/nasal/lib.html ([http://web.archive.org/web/20101010094553/http://plausible.org/nasal/lib.html archive])&lt;br /&gt;
* {{simgear file|simgear/nasal/lib.c}} ([http://sourceforge.net/p/flightgear/simgear/ci/next/log/?path=/simgear/nasal/lib.c history])&lt;br /&gt;
&lt;br /&gt;
=== append() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = append(vector, element[, element[, ...]]);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=42|t=Source}}&lt;br /&gt;
|text = This function appends, or adds, the given element(s) to the end of the vector given in the first argument.  Returns the vector operated on.&lt;br /&gt;
|param1 = vector&lt;br /&gt;
|param1text = The vector to which the arguments will be appended.&lt;br /&gt;
|param2 = element&lt;br /&gt;
|param2text = An element to be added to the vector.&lt;br /&gt;
|example1 = &lt;br /&gt;
var vector = [1, 2, 3]; # Initialize the vector&lt;br /&gt;
append(vector, 4); # Append the number 4 to the end of the vector&lt;br /&gt;
debug.dump(vector); # Print the contents of the vector&lt;br /&gt;
|example2 = &lt;br /&gt;
var vector = [1, 2, 3]; # Initialize the vector&lt;br /&gt;
append(vector, 4, 5, 6); # Append the numbers 4, 5, and 6 to the end of the vector&lt;br /&gt;
debug.dump(vector); # Print the contents of the vector&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== bind() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = bind(function, locals[, outer_scope]);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=502|t=Source}}&lt;br /&gt;
|text = This creates a new function object. A function in Nasal is three things: the actual code, a hash/namespace of local variables available to the function namespace, and the closure object of that namespace. These correspond to the three arguments respectively.&lt;br /&gt;
|param1 = function&lt;br /&gt;
|param1text = Function to evaluate.&lt;br /&gt;
|param2 = locals&lt;br /&gt;
|param2text = Hash containing values that will become the namespace (first closure) for the function.&lt;br /&gt;
|param3 = outer_scope&lt;br /&gt;
|param3text = Optional function which is bound to the next closure. This can be bound to yet another, making a linked list.&lt;br /&gt;
|example1 = # This is a namespace/hash with a single member, named &amp;quot;key,&amp;quot; which is initialized to 12 &lt;br /&gt;
var Namespace = {&lt;br /&gt;
    key: 12&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
# This is different namespace/hash containing a function&lt;br /&gt;
# dividing a variable &amp;quot;key&amp;quot; (which is unavailable/nil in this namespace) by 2&lt;br /&gt;
var AnotherNamespace = {&lt;br /&gt;
    ret: func {&lt;br /&gt;
        key /= 2;&lt;br /&gt;
    }&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
# To see that key is not available, try to call AnotherNamespace.ret() first&lt;br /&gt;
call(AnotherNamespace.ret, [], nil, nil, var errors = []);&lt;br /&gt;
if(size(errors)){&lt;br /&gt;
    print(&amp;quot;Key could not be divided/resolved!&amp;quot;);&lt;br /&gt;
    debug.printerror(errors);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# Associate the AnotherNamespace.ret() function with the first namespace&lt;br /&gt;
# so that &amp;quot;key&amp;quot; is now available&lt;br /&gt;
var function = bind(AnotherNamespace.ret, Namespace);&lt;br /&gt;
&lt;br /&gt;
# Invoke the new function&lt;br /&gt;
function();&lt;br /&gt;
&lt;br /&gt;
# Print out the value of Namespace.key&lt;br /&gt;
# It was changed to 12 from 6 by AnotherNamespace.ret()&lt;br /&gt;
print(Namespace.key);&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== call() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = call(func[, args[, me[, locals[, error]]]);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=247|t=Source}}&lt;br /&gt;
|text = Calls the given function with the given arguments and returns the result.  This function is very useful as it allows much more control over function calls and catches any errors or {{func link|die()}} calls that would normally trigger run-time errors cancelling execution of the script otherwise. &lt;br /&gt;
|param1 = func&lt;br /&gt;
|param1text = Function to execute.&lt;br /&gt;
|param2 = args&lt;br /&gt;
|param2text = Vector containing arguments to give to the called function.&lt;br /&gt;
|param3 = me&lt;br /&gt;
|param3text = &amp;lt;code&amp;gt;'''me'''&amp;lt;/code&amp;gt; reference for the function call (i.e., for method calls). If given, this will override any &amp;lt;code&amp;gt;'''me'''&amp;lt;/code&amp;gt; value existing in the namespace (locals argument).&lt;br /&gt;
|param4 = locals&lt;br /&gt;
|param4text = A hash with key/value pairs that will be available to the called function, typically used as the namespace for the function to be called.&lt;br /&gt;
|param5 = error&lt;br /&gt;
|param5text = A vector to append errors to.  If the called function generates an error, the error, place, and line will be written to this.  These errors can be printed using {{func link|printerror()|debug}}.&lt;br /&gt;
|example1 =&lt;br /&gt;
# prints &amp;quot;Called from call()&amp;quot;&lt;br /&gt;
call(func {&lt;br /&gt;
    print(&amp;quot;Called from call()&amp;quot;);&lt;br /&gt;
});&lt;br /&gt;
|example2 =&lt;br /&gt;
# prints &amp;quot;a = 1 : b = 2&lt;br /&gt;
call(func(a, b){&lt;br /&gt;
        print(&amp;quot;a = &amp;quot;, a, &amp;quot; : b = &amp;quot;, b);&lt;br /&gt;
    },&lt;br /&gt;
    [1, 2]&lt;br /&gt;
);&lt;br /&gt;
|example3 =&lt;br /&gt;
var Hash = {&lt;br /&gt;
    new: func {&lt;br /&gt;
        var m = { parents: [Hash] };&lt;br /&gt;
&lt;br /&gt;
        m.el1 = &amp;quot;string1&amp;quot;;&lt;br /&gt;
        m.el2 = &amp;quot;string2&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
        return m;&lt;br /&gt;
    }&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
# prints &amp;quot;me.el1 = string1&amp;quot;, then &amp;quot;me.el2 = string2&amp;quot; on the next line&lt;br /&gt;
call(func(a, b){        &lt;br /&gt;
        print(&amp;quot;me.el&amp;quot;, a, &amp;quot; = &amp;quot;, me[&amp;quot;el&amp;quot; ~ a]);      &lt;br /&gt;
        print(&amp;quot;me.el&amp;quot;, b, &amp;quot; = &amp;quot;, me[&amp;quot;el&amp;quot; ~ b]);&lt;br /&gt;
    },&lt;br /&gt;
    [1, 2],&lt;br /&gt;
    Hash.new()&lt;br /&gt;
);&lt;br /&gt;
|example4 =&lt;br /&gt;
# prints the value of math.pi&lt;br /&gt;
call(func {&lt;br /&gt;
        print(pi);&lt;br /&gt;
    }, nil, nil, &lt;br /&gt;
    math&lt;br /&gt;
);&lt;br /&gt;
|example5 =&lt;br /&gt;
call(func {&lt;br /&gt;
        print(math.ip); # math.ip doesn't exist&lt;br /&gt;
    }, nil, nil, nil,&lt;br /&gt;
    var errs = []&lt;br /&gt;
);&lt;br /&gt;
debug.printerror(errs); # The error is caught and printed using debug.printerror()&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== caller() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = caller([level]);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=404|t=Source}}&lt;br /&gt;
|text = Returns a vector containing a record from the current call stack.  The level numbering starts from the currently executing function (level 0).  Level 1 (the default) is the caller of the current function, and so on.&lt;br /&gt;
&lt;br /&gt;
The result is a four-element vector containing '''[0]''' a hash of local variables, '''[1]''' the function object, '''[2]''' the full source file name (incl. path) and '''[3]''' the line number. &lt;br /&gt;
|param1 = level&lt;br /&gt;
|param1text = Optional integer specifying the stack level to return a result from.  Defaults to 1 (i.e. the caller of the currently executing function).&lt;br /&gt;
|example1 =&lt;br /&gt;
var myFunction = func(a, b){&lt;br /&gt;
    debug.dump(caller(0)[0]); # prints a hash of local variables, including arguments a and b&lt;br /&gt;
    return 2 * 2;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;2 x 2 = &amp;quot;, myFunction(2, 2));&lt;br /&gt;
|example2 =&lt;br /&gt;
var get_arg_value = func(){&lt;br /&gt;
    print(&amp;quot;Argument to myFunc = &amp;quot;, caller(1)[0]['a']); # print the value of myFunc's single argument, using caller()&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
var myFunc = func(a){&lt;br /&gt;
    get_arg_value();&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
myFunc(3);&lt;br /&gt;
|example3text = This is a real example taken from {{fgdata file|Nasal/canvas/MapStructure.nas}}.  Function &amp;lt;code&amp;gt;r()&amp;lt;/code&amp;gt; (above the TODOs) returns a hash with the key/value pairs as per its arguments. For example, something like this is returned: &amp;lt;code&amp;gt;{ name: &amp;quot;&amp;lt;name&amp;gt;&amp;quot;, vis: 1, zindex: nil }&amp;lt;/code&amp;gt;.&lt;br /&gt;
|example3 =&lt;br /&gt;
var MapStructure_selfTest = func() {&lt;br /&gt;
	var temp = {};&lt;br /&gt;
	temp.dlg = canvas.Window.new([600,400],&amp;quot;dialog&amp;quot;);&lt;br /&gt;
	temp.canvas = temp.dlg.createCanvas().setColorBackground(1,1,1,0.5);&lt;br /&gt;
	temp.root = temp.canvas.createGroup();&lt;br /&gt;
	var TestMap = temp.root.createChild(&amp;quot;map&amp;quot;);&lt;br /&gt;
	TestMap.setController(&amp;quot;Aircraft position&amp;quot;);&lt;br /&gt;
	TestMap.setRange(25); # TODO: implement zooming/panning via mouse/wheel here, for lack of buttons :-/&lt;br /&gt;
	TestMap.setTranslation(&lt;br /&gt;
		temp.canvas.get(&amp;quot;view[0]&amp;quot;)/2,&lt;br /&gt;
		temp.canvas.get(&amp;quot;view[1]&amp;quot;)/2&lt;br /&gt;
	);&lt;br /&gt;
	var r = func(name,vis=1,zindex=nil) return caller(0)[0];&lt;br /&gt;
	# TODO: we'll need some z-indexing here, right now it's just random&lt;br /&gt;
	# TODO: use foreach/keys to show all layers in this case by traversing SymbolLayer.registry direclty ?&lt;br /&gt;
	# maybe encode implicit z-indexing for each lcontroller ctor call ? - i.e. preferred above/below order ?&lt;br /&gt;
	foreach(var type; [r('TFC',0),r('APT'),r('DME'),r('VOR'),r('NDB'),r('FIX',0),r('RTE'),r('WPT'),r('FLT'),r('WXR'),r('APS'), ] ) &lt;br /&gt;
		TestMap.addLayer(factory: canvas.SymbolLayer, type_arg: type.name,&lt;br /&gt;
					visible: type.vis, priority: type.zindex,&lt;br /&gt;
		);&lt;br /&gt;
}; # MapStructure_selfTest&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== chr() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = chr(code);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=175|t=Source}}&lt;br /&gt;
|text = Returns a character as per the single argument. Extended ASCII is supported (see http://www.asciitable.com/ for a list of supported characters), although this may vary between different systems.  For a list of the most commonly used characters, see the {{wikipedia|ASCII#ASCII printable code chart|ASCII printable code chart}} ('''Dec''' column). The following table lists supported control characters, along with their equivalent control characters in Nasal strings.  {{Note|In Nasal, only strings enclosed with double-quotes (&amp;lt;code&amp;gt;&amp;quot;string&amp;quot;&amp;lt;/code&amp;gt;) supports control chracters.  Strings in single quotes (&amp;lt;code&amp;gt;'string'&amp;lt;/code&amp;gt;) do not.}}&lt;br /&gt;
{{{!}} class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Code !! Name !! Equivalent to&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} 10 {{!!}} {{Wikipedia|Newline}} {{!!}} &amp;lt;code&amp;gt;\n&amp;lt;/code&amp;gt;&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} 9 {{!!}} {{Wikipedia|Tab key#Tab characters|Horizontal tab}} {{!!}} &amp;lt;code&amp;gt;\t&amp;lt;/code&amp;gt;&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} 13 {{!!}} {{Wikipedia|Carriage return}} {{!!}} &amp;lt;code&amp;gt;\r&amp;lt;/code&amp;gt;&lt;br /&gt;
{{!}}}&lt;br /&gt;
|param1 = code&lt;br /&gt;
|param1text = Integer character code for the desired glyph.&lt;br /&gt;
|example1 = print(&amp;quot;Code 65 = &amp;quot;, chr(65)); # prints &amp;quot;Code 65 = A&amp;quot;&lt;br /&gt;
|example2text = This example displays all of the characters in a list, in the format &amp;lt;code&amp;gt;Code '''n''' = &amp;gt;'''char'''&amp;lt;&amp;lt;/code&amp;gt;, '''n''' being the index, and '''char''' being the character.&lt;br /&gt;
|example2 =&lt;br /&gt;
for(var i = 0; i &amp;lt;= 255; i += 1){&lt;br /&gt;
    print(&amp;quot;Code &amp;quot;, i, &amp;quot; = &amp;gt;&amp;quot;, chr(i), &amp;quot;&amp;lt;&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== closure() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = closure(func[, level]);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=421|t=Source}}&lt;br /&gt;
|text = Returns the hash table containing the lexical namespace of the given function. The level numbering start with level 0 being the namespace of '''func'''. &lt;br /&gt;
|param1 = func&lt;br /&gt;
|param1text = Function to evaluate.&lt;br /&gt;
|param2 = level&lt;br /&gt;
|param2text = Optional integer specifying the scope level.  Defaults to 0 (the namespace of '''func''').&lt;br /&gt;
|example1 =&lt;br /&gt;
var get_math_e = func {&lt;br /&gt;
    return e; # return the value of math.e&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var myFunction = bind(get_math_e, math); # bind get_math_e to the math namespace, so that math.e is immediately available to get_math_e&lt;br /&gt;
debug.dump(closure(myFunction)); # print the namespace of get_math_e&lt;br /&gt;
&lt;br /&gt;
print(myFunction());&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== cmp() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = cmp(a, b);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=112|t=Source}}&lt;br /&gt;
|text = Compares two strings, returning -1 if '''a''' is less than '''b''', 0 if they are identical and 1 if '''a''' is greater than '''b'''. &lt;br /&gt;
|param1 = a&lt;br /&gt;
|param1text = First string argument for comparison.&lt;br /&gt;
|param2 = b&lt;br /&gt;
|param2text = Second string argument for comparison.&lt;br /&gt;
|example1 = print(cmp(&amp;quot;1&amp;quot;, &amp;quot;two&amp;quot;)); # prints -1&lt;br /&gt;
|example2 = print(cmp(&amp;quot;string&amp;quot;, &amp;quot;string&amp;quot;)); # prints 0&lt;br /&gt;
|example3 = print(cmp(&amp;quot;one&amp;quot;, &amp;quot;2&amp;quot;)); # prints 1&lt;br /&gt;
|example4 = print(cmp(&amp;quot;string1&amp;quot;, &amp;quot;string2&amp;quot;)); # prints -1&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== compile() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = compile(code[, filename]);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=220|t=Source}}&lt;br /&gt;
|text = Compiles the specified code string and returns a function object bound to the current lexical context.  If there is an error, the function dies, with the argument to {{func link|die()}} being '''filename'''.&lt;br /&gt;
|param1 = code&lt;br /&gt;
|param1text = String containing Nasal code to be compiled.&lt;br /&gt;
|param2 = filename&lt;br /&gt;
|param2text = Optional string used for error messages/logging. Defaults to &amp;lt;code&amp;gt;&amp;lt;compile&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
|example1 = &lt;br /&gt;
var myCode = 'print(&amp;quot;hello&amp;quot;);';&lt;br /&gt;
var helloFunc = compile(myCode, &amp;quot;myCode&amp;quot;);&lt;br /&gt;
helloFunc();&lt;br /&gt;
|example2text = &amp;lt;code&amp;gt;compile&amp;lt;/code&amp;gt; is very convenient to support Nasal loaded from other files.  For instance, [[PropertyList XML files]] (such as GUI dialogs) may contain embedded Nasal sections that need to be parsed, processed and compiled.  For an example of how to do this, save the below XML code as &amp;lt;tt&amp;gt;''[[$FG_ROOT]]/gui/dialogs/test.xml''&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?xml version=&amp;quot;1.0&amp;quot;?&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;PropertyList&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nasal&amp;gt;&amp;lt;![CDATA[&lt;br /&gt;
print(&amp;quot;You have FlightGear v&amp;quot;, getprop(&amp;quot;/sim/version/flightgear&amp;quot;));&lt;br /&gt;
]]&amp;gt;&amp;lt;/nasal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/PropertyList&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Now, start FlightGear and execute this code in the [[Nasal Console]].&lt;br /&gt;
|example2 =&lt;br /&gt;
# Build the path&lt;br /&gt;
var FGRoot = getprop(&amp;quot;/sim/fg-root&amp;quot;);&lt;br /&gt;
var filename = &amp;quot;/gui/dialogs/test.xml&amp;quot;;&lt;br /&gt;
var path = FGRoot ~ filename;&lt;br /&gt;
&lt;br /&gt;
var blob = io.read_properties(path);&lt;br /&gt;
var script = blob.getValues().nasal; # Get the nasal string&lt;br /&gt;
&lt;br /&gt;
# Compile the script.  We're passing the filename here for better runtime diagnostics &lt;br /&gt;
var code = call(func {&lt;br /&gt;
    compile(script, filename);&lt;br /&gt;
}, nil, nil, var compilation_errors = []);&lt;br /&gt;
&lt;br /&gt;
if(size(compilation_errors)){&lt;br /&gt;
    die(&amp;quot;Error compiling code in: &amp;quot; ~ filename);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# Invoke the compiled script, equivalent to code(); &lt;br /&gt;
# We're using call() here to detect errors:&lt;br /&gt;
call(code, [], nil, nil, var runtime_errors = []);&lt;br /&gt;
&lt;br /&gt;
if(size(runtime_errors)){&lt;br /&gt;
    die(&amp;quot;Error calling code compiled loaded from: &amp;quot; ~ filename);&lt;br /&gt;
}&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== contains() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = contains(hash, key);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=184|t=Source}}&lt;br /&gt;
|text = Returns 1 (True) if the hash contains the specified key, or 0 (False) if not.&lt;br /&gt;
|param1 = hash&lt;br /&gt;
|param1text = The hash to search in.&lt;br /&gt;
|param2 = key&lt;br /&gt;
|param2text = The scalar to be searched for, contained as a key in the hash.&lt;br /&gt;
|example1 =&lt;br /&gt;
# Initialize a hash&lt;br /&gt;
var hash = {&lt;br /&gt;
    element: &amp;quot;value&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
print(contains(hash, &amp;quot;element&amp;quot;) ? &amp;quot;Yes&amp;quot; : &amp;quot;No&amp;quot;); # This will print &amp;quot;Yes&amp;quot;&lt;br /&gt;
|example2 =&lt;br /&gt;
# Initialize a hash&lt;br /&gt;
var hash = {&lt;br /&gt;
    element: &amp;quot;value&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
print(contains(hash, &amp;quot;element2&amp;quot;) ? &amp;quot;Yes&amp;quot; : &amp;quot;No&amp;quot;); # This will print &amp;quot;No&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===contains()===&lt;br /&gt;
{{Requires commit|desc=Vector support|details=see {{Merge-request|project=fgdata|id=305}}|commit=ee39abbd3b70c9b6d5e3a1c4ccedddaac1a92b11|repo=Simgear}}&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = contains(vector, item);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=184|t=Source}}&lt;br /&gt;
|text = Returns 1 (True) if the vector contains the specified item, or 0 (False) if not.&lt;br /&gt;
|param1 = vector&lt;br /&gt;
|param1text = The vector to search in.&lt;br /&gt;
|param2 = item&lt;br /&gt;
|param2text = The object to be searched for in the vector.&lt;br /&gt;
|example1 =&lt;br /&gt;
# Initialize a hash&lt;br /&gt;
var vec = [&amp;quot;element&amp;quot;, &amp;quot;foo&amp;quot;];&lt;br /&gt;
print(contains(hash, &amp;quot;element&amp;quot;) ? &amp;quot;Yes&amp;quot; : &amp;quot;No&amp;quot;); # This will print &amp;quot;Yes&amp;quot;&lt;br /&gt;
|example2 =&lt;br /&gt;
# Initialize a hash&lt;br /&gt;
var vec = [&amp;quot;element&amp;quot;, &amp;quot;foo&amp;quot;];&lt;br /&gt;
print(contains(hash, &amp;quot;element2&amp;quot;) ? &amp;quot;Yes&amp;quot; : &amp;quot;No&amp;quot;); # This will print &amp;quot;No&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===delete() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = delete(hash, key);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=83|t=Source}}&lt;br /&gt;
|text = Deletes the key from the hash if it exists. Operationally, this is NOT identical to setting the hash value specified by the key to &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; as the key will stay in the hash (at least for a while). This variant potentially frees storage by deleting the reference to the key and by shrinking the hash.  Returns the hash that has been operated on.&lt;br /&gt;
|param1 = hash&lt;br /&gt;
|param1text = The hash from which to delete the key.&lt;br /&gt;
|param2 = key&lt;br /&gt;
|param2text = The scalar to be deleted, contained as a key in the hash.&lt;br /&gt;
|example1 =&lt;br /&gt;
# Initialize the hash&lt;br /&gt;
var hash = {&lt;br /&gt;
    element1: &amp;quot;value1&amp;quot;,&lt;br /&gt;
    element2: &amp;quot;value2&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
delete(hash, &amp;quot;element1&amp;quot;); # Delete element1&lt;br /&gt;
debug.dump(hash); # prints the hash, which is now minus element1&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===die()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = die(error);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=288|t=Source}}&lt;br /&gt;
|text = Terminates execution and unwinds the stack.  The place and the line will be added to the '''error'''.  This invokes the same internal exception handler used for internal runtime errors. Use this to signal fatal errors, or to implement exception handling. The error thrown (including internal runtime errors) can be caught with {{func link|call()}}.&lt;br /&gt;
|param1 = error&lt;br /&gt;
|param1text = String describing the error.&lt;br /&gt;
:{{inote|This parameter is technically optional, but it is highly recommended to use it.}}&lt;br /&gt;
|example1 = &lt;br /&gt;
print(&amp;quot;Will print&amp;quot;);&lt;br /&gt;
die(&amp;quot;Don't go any further!&amp;quot;); &lt;br /&gt;
print(&amp;quot;Won't print&amp;quot;); # Will not be printed because die() stops the process&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== find()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = find(needle, haystack);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=450|t=Source}}&lt;br /&gt;
|text = Finds and returns the index of the first occurrence of the string '''needle''' in the string '''haystack''', or -1 if no such occurrence was found.&lt;br /&gt;
|param1 = needle&lt;br /&gt;
|param1text = String to search for.&lt;br /&gt;
|param2 = haystack&lt;br /&gt;
|param2text = String to search in.&lt;br /&gt;
|example1 = print(find(&amp;quot;c&amp;quot;, &amp;quot;abcdef&amp;quot;)); # prints 2&lt;br /&gt;
|example2 = print(find(&amp;quot;x&amp;quot;, &amp;quot;abcdef&amp;quot;)); # prints -1&lt;br /&gt;
|example3 = print(find(&amp;quot;cd&amp;quot;, &amp;quot;abcdef&amp;quot;)); # prints 2&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===ghosttype()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = ghosttype(ghost);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=207|t=Source}}&lt;br /&gt;
|text = Returns a string containing either a descriptive name of a ghost (a raw C/C++ object), or a unique id (the pointer to the C/C++ &amp;lt;code&amp;gt;naGhostType&amp;lt;/code&amp;gt; instance) if no name has been set.  Ghost is an acronym that stands for '''G'''arbage-collected '''H'''andle to '''O'''ut'''S'''ide '''T'''hingy.&lt;br /&gt;
|param1 = ghost&lt;br /&gt;
|param1text = Ghost to return a description for.&lt;br /&gt;
|example1 = print(ghosttype(airportinfo())); # prints &amp;quot;airport&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===id()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = id(object);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=570|t=Source}}&lt;br /&gt;
|text = Returns a string containing information on the type and ID of the object provided in the single argument.  The information is returned in the form of &amp;lt;code&amp;gt;'''&amp;lt;type&amp;gt;''':'''&amp;lt;id&amp;gt;'''&amp;lt;/code&amp;gt;, where '''&amp;lt;type&amp;gt;''' is the type of object, and '''&amp;lt;id&amp;gt;''' is the ID.&lt;br /&gt;
|param1 = object&lt;br /&gt;
|param1text = Can be either of a string, a vector, a hash, a code, a function, or a ghost.&lt;br /&gt;
|example1 = print(id(&amp;quot;A&amp;quot;)); # prints &amp;quot;str:000000001624A590&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===int() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = int(number);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=90|t=Source}}&lt;br /&gt;
|text = Returns the integer part of the numeric value of the single argument, or &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; if none exists.&lt;br /&gt;
|param1 = number&lt;br /&gt;
|param1text = Number or string with just a number in it to return an integer from.&lt;br /&gt;
|example1 = print(int(23)); # prints &amp;quot;23&amp;quot;&lt;br /&gt;
|example2 = print(int(23.123)); # prints &amp;quot;23&amp;quot;&lt;br /&gt;
|example3 = debug.dump(int(&amp;quot;string&amp;quot;)); # prints &amp;quot;nil&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===keys()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = keys(hash);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=33|t=Source}}&lt;br /&gt;
|text = Returns a vector containing the list of keys found in the single hash argument. &lt;br /&gt;
|param1 = hash&lt;br /&gt;
|param1text = The hash to return the keys from.&lt;br /&gt;
|example1 = &lt;br /&gt;
# Initialize a hash&lt;br /&gt;
var hash = {&lt;br /&gt;
    element1: &amp;quot;value&amp;quot;,&lt;br /&gt;
    element2: &amp;quot;value&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
debug.dump(keys(hash)); # print the vector&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===left()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = left(string, length);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=149|t=Source}}&lt;br /&gt;
|version = 2.12&lt;br /&gt;
|commit = {{simgear commit|bd7163|t=commit}}&lt;br /&gt;
|text = Returns a substring of '''string''', starting from the left.&lt;br /&gt;
|param1 = string&lt;br /&gt;
|param1text = String to return part of.&lt;br /&gt;
|param2 = length&lt;br /&gt;
|param2text = Integer specifying the length of the substring to return.&lt;br /&gt;
|example1 = print(left(&amp;quot;string&amp;quot;, 2)); # prints &amp;quot;st&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== num()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = num(number);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=102|t=Source}}&lt;br /&gt;
|text = Returns the numerical value of the single string argument, or &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; if none exists. &lt;br /&gt;
|param1 = number&lt;br /&gt;
|param1text = String with just a number in it to return a number from.&lt;br /&gt;
|example1 = print(num(&amp;quot;23&amp;quot;)); # prints &amp;quot;23&amp;quot;&lt;br /&gt;
|example2 = print(num(&amp;quot;23.123&amp;quot;)); # prints &amp;quot;23.123&amp;quot;&lt;br /&gt;
|example3 = debug.dump(num(&amp;quot;string&amp;quot;)); # prints &amp;quot;nil&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===pop()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = pop(vector);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=50|t=Source}}&lt;br /&gt;
|text = Removes and returns the last element of the single vector argument, or &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; if the vector is empty. &lt;br /&gt;
|param1 = vector&lt;br /&gt;
|param1text = Vector to remove an element from.&lt;br /&gt;
|example1 = &lt;br /&gt;
var vector = [1, 2, 3];&lt;br /&gt;
pop(vector);&lt;br /&gt;
debug.dump(vector); # prints &amp;quot;[1, 2]&amp;quot;&lt;br /&gt;
|example2 = &lt;br /&gt;
var vector = [1, 2, 3];&lt;br /&gt;
debug.dump(pop(vector)); # prints &amp;quot;3&amp;quot;&lt;br /&gt;
|example3 = &lt;br /&gt;
var vector = [];&lt;br /&gt;
debug.dump(pop(vector)); # prints &amp;quot;nil&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===right()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = right(string, length);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=161|t=Source}}&lt;br /&gt;
|version = 2.12&lt;br /&gt;
|commit = {{simgear commit|bd7163|t=commit}}&lt;br /&gt;
|text = Returns a substring of '''string''', starting from the right.&lt;br /&gt;
|param1 = string&lt;br /&gt;
|param1text = String to return part of.&lt;br /&gt;
|param2 = length&lt;br /&gt;
|param2text = Integer specifying the length of the substring to return.&lt;br /&gt;
|example1 = print(right(&amp;quot;string&amp;quot;, 2)); # prints &amp;quot;ng&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== setsize()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = setsize(vector, size);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=56|t=Source}}&lt;br /&gt;
|text = Sets the size of a vector. The first argument specifies a vector, the second a number representing the desired size of that vector. If the vector is currently larger than the specified size, it is truncated. If it is smaller, it is padded with &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; entries. Returns the vector operated upon. &lt;br /&gt;
|param1 = vector&lt;br /&gt;
|param1text = The vector to be operated on.&lt;br /&gt;
|param2 = size&lt;br /&gt;
|param2text = The desired size of the vector in number of entries.&lt;br /&gt;
|example1 = &lt;br /&gt;
var vector = [1, 2, 3]; # Initialize a vector&lt;br /&gt;
setsize(vector, 4);&lt;br /&gt;
debug.dump(vector); # print the vector&lt;br /&gt;
|example2 = &lt;br /&gt;
var vector = [1, 2, 3]; # Initialize a vector&lt;br /&gt;
setsize(vector, 2);&lt;br /&gt;
debug.dump(vector); # print the vector&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===size()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = size(object);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=23|t=Source}}&lt;br /&gt;
|text = Returns the size of the single argument. For strings, this is the length in bytes. For vectors, this is the number of elements. For hashes, it is the number of key/value pairs. If the argument is &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; or a number, this error will be thrown: &amp;lt;code&amp;gt;object has no size()&amp;lt;/code&amp;gt;.&lt;br /&gt;
|param1 = object&lt;br /&gt;
|param1text = Object to find the size of.  Must be a string, a vector or a hash.&lt;br /&gt;
|example1 = &lt;br /&gt;
var string = &amp;quot;string&amp;quot;;&lt;br /&gt;
print(size(string)); # prints &amp;quot;6&amp;quot;&lt;br /&gt;
|example2 =&lt;br /&gt;
var vector = [1, 2, 3];&lt;br /&gt;
print(size(vector)); # prints &amp;quot;3&amp;quot;&lt;br /&gt;
|example3 =&lt;br /&gt;
var hash = {&lt;br /&gt;
    element1: &amp;quot;value1&amp;quot;,&lt;br /&gt;
    element2: &amp;quot;value2&amp;quot;,&lt;br /&gt;
    element3: &amp;quot;value3&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
print(size(hash)); # prints &amp;quot;3&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== sort()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = sort(vector, function);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=542|t=Source}}&lt;br /&gt;
|text = Returns a vector containing the elements in the input '''vector''' sorted in according to the rule given by '''function'''. Implemented with the ANSI C {{func link|qsort()|link=http://www.cplusplus.com/reference/cstdlib/qsort/}}, &amp;lt;code&amp;gt;sort()&amp;lt;/code&amp;gt; is stable.  This means that if the rules in the first example are used, equal elements in the output vector will appear in the same relative order as they do in the input.  It is run in a loop, so '''function''' is run several times.&lt;br /&gt;
|param1 = vector&lt;br /&gt;
|param1text = Input vector to sort.&lt;br /&gt;
|param2 = function&lt;br /&gt;
|param2text = Function according to which the elements will be sorted by.  It should take two arguments and should return one of 1, 0, or -1.&lt;br /&gt;
{{{!}} class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Return value !! Meaning&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} less than 0 {{!!}} first argument should go before second argument&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} 0 {{!!}} first argument equals second argument&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} greater than 0 {{!!}} first argument should go after second argument&lt;br /&gt;
{{!}}}&lt;br /&gt;
&lt;br /&gt;
|example1text = This example sorts elements from smallest to greatest.&lt;br /&gt;
|example1 = &lt;br /&gt;
var sort_rules = func(a, b){&lt;br /&gt;
    if(a &amp;lt; b){&lt;br /&gt;
        return -1; # A should before b in the returned vector&lt;br /&gt;
    }elsif(a == b){&lt;br /&gt;
        return 0; # A is equivalent to b &lt;br /&gt;
    }else{&lt;br /&gt;
        return 1; # A should after b in the returned vector&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
debug.dump(sort([3, 2, 5, 6, 4, 1], sort_rules)); # prints &amp;quot;[1, 2, 3, 4, 5, 6]&amp;quot;&lt;br /&gt;
|example2text = This example sorts elements from greatest to smallest.&lt;br /&gt;
|example2 = &lt;br /&gt;
# Outputs the elements in reverse order (greatest to smallest)&lt;br /&gt;
var sort_rules = func(a, b){&lt;br /&gt;
    if(a &amp;lt; b){&lt;br /&gt;
        return 1; # -1 in the above example&lt;br /&gt;
    }elsif(a == b){&lt;br /&gt;
        return 0;&lt;br /&gt;
    }else{&lt;br /&gt;
        return -1; # 1 in the above example&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
debug.dump(sort([3, 2, 5, 6, 4, 1], sort_rules)); # prints &amp;quot;[6, 5, 4, 3, 2, 1]&amp;quot;&lt;br /&gt;
|example3text = This example sorts a vector of strings (runways for example) from smallest to greatest.&lt;br /&gt;
|example3 = &lt;br /&gt;
var runways = [&amp;quot;09R&amp;quot;,&amp;quot;27R&amp;quot;,&amp;quot;26L&amp;quot;,&amp;quot;09L&amp;quot;,&amp;quot;15&amp;quot;];&lt;br /&gt;
var rwy = sort(runways,func(a,b) cmp(a,b));&lt;br /&gt;
debug.dump(rwy); # prints ['09L','09R','15','26L','27R']&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== split()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = split(delimiter, string);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=460|t=Source}}&lt;br /&gt;
|text = Splits the input string into a vector of substrings bounded by occurrences of the delimiter substring.&lt;br /&gt;
|param1 = delimiter&lt;br /&gt;
|param1text = String that will split the substrings in the returned vector.&lt;br /&gt;
|param2 = string&lt;br /&gt;
|param2text = String to split up.&lt;br /&gt;
|example1 = debug.dump(split(&amp;quot;cd&amp;quot;, &amp;quot;abcdef&amp;quot;)); # prints &amp;quot;['ab', 'ef']&amp;quot;&lt;br /&gt;
|example2 = debug.dump(split(&amp;quot;.&amp;quot;, &amp;quot;3.2.0&amp;quot;)); # prints &amp;quot;[3, 2, 0]&amp;quot;&lt;br /&gt;
|example3 = debug.dump(split(&amp;quot;/&amp;quot;, &amp;quot;path/to/file&amp;quot;)); # prints &amp;quot;['path', 'to', 'file']&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===sprintf()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = &amp;lt;nowiki&amp;gt;sprintf(format[, arg[, arg, [...]]]);&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=355|t=Source}}&lt;br /&gt;
|text = Creates and returns a string formatted using ANSI C {{func link|vsnprintf()|link=http://en.cppreference.com/w/c/io/vfprintf}} &amp;lt;ref&amp;gt;&lt;br /&gt;
{{Cite web&lt;br /&gt;
|url = http://sourceforge.net/p/flightgear/simgear/ci/next/tree/simgear/nasal/lib.c#l308&lt;br /&gt;
|title = fgdata/simgear/simgear/nasal/lib.c, line 308&lt;br /&gt;
|accessdate = October 2015&lt;br /&gt;
}}&lt;br /&gt;
&amp;lt;/ref&amp;gt;.  Below is a table of supported format specifiers.&lt;br /&gt;
{{{!}} class=&amp;quot;wikitable&amp;quot; width=&amp;quot;75%&amp;quot;&lt;br /&gt;
{{!}}+ %[flags][width][.precision]specifier&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; {{!}} Flags&lt;br /&gt;
{{!-}}&lt;br /&gt;
! Flag !! Output&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;+&amp;lt;/code&amp;gt; {{!!}} Forces to precede the result with a plus or minus sign ('''+''' or '''-''') even for positive numbers. By default, only negative numbers are preceded with a '''-''' sign.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} ''space'' {{!!}} Prefixes non-signed numbers with a space.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;-&amp;lt;/code&amp;gt; {{!!}} Left-align the output of this placeholder (the default is to right-align the output) when the width option is specified.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt; {{!!}} Use 0 instead of spaces to pad a field when the width option is specified.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;#&amp;lt;/code&amp;gt; {{!!}} Used with &amp;lt;code&amp;gt;o&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;X&amp;lt;/code&amp;gt; specifiers the value is preceded with &amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;0x&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;0X&amp;lt;/tt&amp;gt; respectively for values different than zero. Used with &amp;lt;code&amp;gt;e&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;E&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;f&amp;lt;/code&amp;gt;, it forces the written output to contain a decimal point even if no digits would follow. By default, if no digits follow, no decimal point is written. Used with &amp;lt;code&amp;gt;g&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;G&amp;lt;/code&amp;gt; the result is the same as with &amp;lt;code&amp;gt;e&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;E&amp;lt;/code&amp;gt; but trailing zeros are not removed.&lt;br /&gt;
{{!-}}&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; {{!}} Width&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} colspan=&amp;quot;2&amp;quot; {{!}} Integer specifying the minimum number of characters to be returned. This includes the decimal point and decimal fraction as well as + or - signs.&lt;br /&gt;
{{!-}}&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; {{!}} Precision&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} colspan=&amp;quot;2&amp;quot; {{!}} Integer preceded by a dot specifying the number of decimal places to be written.&lt;br /&gt;
{{!-}}&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; {{!}} Specifiers&lt;br /&gt;
{{!-}}&lt;br /&gt;
! Specifier !! Output&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;d&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;i&amp;lt;/code&amp;gt; {{!!}} Signed decimal number.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;s&amp;lt;/code&amp;gt; {{!!}} A string&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;%&amp;lt;/code&amp;gt; {{!!}} Percent (%) character.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;c&amp;lt;/code&amp;gt; {{!!}} A single character assigned to a character code, the code given in an integer argument.  See http://www.asciitable.com/ for a list of supported characters and their codes.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;o&amp;lt;/code&amp;gt; {{!!}} Unsigned integer as an octal number.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;u&amp;lt;/code&amp;gt; {{!!}} Unsigned decimal integer.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;X&amp;lt;/code&amp;gt; {{!!}} Unsigned integer as a hexadecimal number.  If &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt; is used, any letters in the number are lowercase, while &amp;lt;code&amp;gt;X&amp;lt;/code&amp;gt; gives uppercase.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;e&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;E&amp;lt;/code&amp;gt; {{!!}} Double value in scientific notation (i.e., ''[-]ddd.ddd'''e'''[+/-]ddd''), with an exponent being denoted by &amp;lt;tt&amp;gt;e&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;E&amp;lt;/tt&amp;gt; depending on whether an upper or lowercase is used respectively.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;f&amp;lt;/code&amp;gt; {{!!}} Floating-point number, in fixed decimal notation, by default with 6 decimal places.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;F&amp;lt;/code&amp;gt; {{!!}} Appears to be available&amp;lt;ref&amp;gt;&lt;br /&gt;
{{Cite web&lt;br /&gt;
|url = http://sourceforge.net/p/flightgear/simgear/ci/next/tree/simgear/nasal/lib.c#l389&lt;br /&gt;
|title = fgdata/simgear/simgear/nasal/lib.c, line 389&lt;br /&gt;
|accessdate = October 2015&lt;br /&gt;
}}&lt;br /&gt;
&amp;lt;/ref&amp;gt;, but doesn't work.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;g&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;G&amp;lt;/code&amp;gt; {{!!}} Double in either normal or exponential notation, whichever is more appropriate for its magnitude. &amp;lt;code&amp;gt;g&amp;lt;/code&amp;gt; uses lower-case letters, &amp;lt;code&amp;gt;G&amp;lt;/code&amp;gt; uses upper-case letters. This type differs slightly from fixed-point notation in that insignificant zeroes to the right of the decimal point are not included.  Also, the decimal point is not included on whole numbers.&lt;br /&gt;
{{!}}}&lt;br /&gt;
&lt;br /&gt;
|param1 = format&lt;br /&gt;
|param1text = String specifying the format.  Can be used with or without a format specifiers.  See below for examples.&lt;br /&gt;
|param2 = arg&lt;br /&gt;
|param2text = Argument specifying a value to replace a format placeholder (such as &amp;lt;code&amp;gt;%d&amp;lt;/code&amp;gt;) in the format string.  Not required if there are no format specifiers.&lt;br /&gt;
&lt;br /&gt;
|example1 = print(sprintf(&amp;quot;%i&amp;quot;, 54)); # prints &amp;quot;54&amp;quot;&lt;br /&gt;
|example2 = print(sprintf(&amp;quot;Pi = %+.10f&amp;quot;, math.pi)); # prints &amp;quot;Pi = +3.1415926536&amp;quot;&lt;br /&gt;
|example3 = &lt;br /&gt;
print(sprintf(&amp;quot;%6d&amp;quot;, 23)); # prints &amp;quot;    23&amp;quot;&lt;br /&gt;
print(sprintf(&amp;quot;%06d&amp;quot;, 23)); # prints &amp;quot;000023&amp;quot;&lt;br /&gt;
|example4 =&lt;br /&gt;
var FGVer = getprop(&amp;quot;/sim/version/flightgear&amp;quot;);&lt;br /&gt;
print(sprintf(&amp;quot;You have FlightGear v%s&amp;quot;, FGVer)); # prints &amp;quot;You have FlightGear v&amp;lt;your version&amp;gt;&amp;quot;&lt;br /&gt;
|example5 = &lt;br /&gt;
print(sprintf(&amp;quot;Hexadecimal 100000 = %X&amp;quot;, 100000)); # prints &amp;quot;Hexadecimal 100000 = 186A0&amp;quot;&lt;br /&gt;
print(sprintf(&amp;quot;Hexadecimal 100000 = %x&amp;quot;, 100000)); # prints &amp;quot;Hexadecimal 100000 = 186a0&amp;quot;&lt;br /&gt;
|example6 = print(sprintf(&amp;quot;Code 65 is %c&amp;quot;, 65)); # prints &amp;quot;Code 65 is A&amp;quot;&lt;br /&gt;
|example7 = &lt;br /&gt;
print(sprintf(&amp;quot;%e&amp;quot;, 54)); # prints &amp;quot;5.400000e+001&amp;quot;&lt;br /&gt;
print(sprintf(&amp;quot;%E&amp;quot;, 54)); # prints &amp;quot;5.400000E+001&amp;quot;&lt;br /&gt;
|example8 = print(sprintf(&amp;quot;%o&amp;quot;, 54)); # prints &amp;quot;66&amp;quot;&lt;br /&gt;
|example9 = print(sprintf(&amp;quot;50%% of 100 is %i&amp;quot;, 100 / 2)); # prints &amp;quot;50% of 100 is 50&amp;quot;&lt;br /&gt;
|example10 =&lt;br /&gt;
print(sprintf(&amp;quot;%.2f&amp;quot;, 1.4));   #prints &amp;quot;1.40&amp;quot;&lt;br /&gt;
print(sprintf(&amp;quot;%.1f&amp;quot;, 1.4));   #prints &amp;quot;1.4&amp;quot;&lt;br /&gt;
print(sprintf(&amp;quot;% 4.1f&amp;quot;, 1.4)); #prints &amp;quot; 1.4&amp;quot;&lt;br /&gt;
print(sprintf(&amp;quot;%04.1f&amp;quot;, 1.4)); #prints &amp;quot;01.4&amp;quot;&lt;br /&gt;
print(sprintf(&amp;quot;% 6.1f&amp;quot;, 1.4)); #prints &amp;quot;   1.4&amp;quot;&lt;br /&gt;
print(sprintf(&amp;quot;%06.1f&amp;quot;, 1.4)); #prints &amp;quot;0001.4&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===streq()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = streq(a, b);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=129|t=Source}}&lt;br /&gt;
|text = Tests the string values of the two arguments for equality. This function is needed because the &amp;lt;code&amp;gt;'''=='''&amp;lt;/code&amp;gt; operator (see [[Nasal_Operators#Equality|Nasal Operators]]) tests for numeric equality first.  If either or both the arguments are not strings, 0 (False) will be returned.  Returns either 0 (False) or 1 (True).  {{Note|This function is rarely required in typical code.}}&lt;br /&gt;
|param1 = a&lt;br /&gt;
|param1text = First argument for testing equality.&lt;br /&gt;
|param2 = b&lt;br /&gt;
|param2text = Second argument for testing equality.&lt;br /&gt;
|example1 = print(streq(&amp;quot;0&amp;quot;, &amp;quot;0&amp;quot;)); # prints &amp;quot;1&amp;quot; (True)&lt;br /&gt;
|example2 = &lt;br /&gt;
print(0 == 0.0); # prints &amp;quot;1&amp;quot; (True)&lt;br /&gt;
print(streq(&amp;quot;0&amp;quot;, &amp;quot;0.0&amp;quot;)); # prints &amp;quot;0&amp;quot; (False)&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===substr()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = substr(string, start [, length]);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=129|t=Source}}&lt;br /&gt;
|text = Similar the {{func link|subvec()}}, but operates on strings. Computes and returns a substring. The first argument specifies a string, the second is the index of the start of a substring, the optional third argument specifies a length (the default is to return the rest of the string from the start).&lt;br /&gt;
|param1 = string&lt;br /&gt;
|param1text = String to return a substring from.&lt;br /&gt;
|param2 = start&lt;br /&gt;
|param2text = Integer specifying the start of a substring. Negative values specify a position from the end of the string.&lt;br /&gt;
|param3 = length&lt;br /&gt;
|param3text = Optional argument specifying the length of the substring. Defaults to the end of the string.&lt;br /&gt;
|example1 = print(substr(&amp;quot;abcde&amp;quot;, 1, 3)); # prints &amp;quot;bcd&amp;quot;&lt;br /&gt;
|example2 = print(substr(&amp;quot;abcde&amp;quot;, 1)); # prints &amp;quot;bcde&amp;quot;&lt;br /&gt;
|example3 = print(substr(&amp;quot;abcde&amp;quot;, 2, 1)); # prints &amp;quot;c&amp;quot;&lt;br /&gt;
|example4 = print(substr(&amp;quot;abcde&amp;quot;, -2)); # prints &amp;quot;de&amp;quot;&lt;br /&gt;
|example5 = print(substr(&amp;quot;abcde&amp;quot;, -3, 2)); # prints &amp;quot;cd&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===subvec()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = subvec(vector, start[, length]);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=63|t=Source}}&lt;br /&gt;
|text = Returns a sub-range of a vector. The first argument specifies a vector, the second a starting index, and the optional third argument indicates a length (the default is to the end of the vector). &lt;br /&gt;
|param1 = vector&lt;br /&gt;
|param1text = The vector to take the sub-vector from.&lt;br /&gt;
|param2 = start&lt;br /&gt;
|param2text = The starting point of the sub-vector within the given vector.&lt;br /&gt;
|param3 = length&lt;br /&gt;
|param3text = Optional argument specifying the length of the sub-vector, from the starting point.&lt;br /&gt;
'''Notes:'''&lt;br /&gt;
* Omitting the ''vector'' and ''start'' arguments is not an error (possibly it should be) but the return value is ''nil''.&lt;br /&gt;
* A negative ''start'' argument ''is'' an error. This seems wrong. Perhaps the language designer could comment.&lt;br /&gt;
* A value of ''start'' greater than ''size(vector)'' causes an error. A value equal to ''size(vector)'' returns an empty vector.&lt;br /&gt;
* If the value of ''length'' is greater than ''size(vector) - start'' then it is ignored. That is, all elements from ''start'' to the end of ''vector'' are returned. If ''length'' is zero then an empty vector is returned. A negative value of ''length'' causes an error.&lt;br /&gt;
|example1 = &lt;br /&gt;
var vector = [1, 2, 3];&lt;br /&gt;
debug.dump(subvec(vector, 0)); # prints &amp;quot;[1, 2, 3]&amp;quot;&lt;br /&gt;
|example2 = &lt;br /&gt;
var vector = [1, 2, 3];&lt;br /&gt;
debug.dump(subvec(vector, 1)); # prints &amp;quot;[2, 3]&amp;quot;&lt;br /&gt;
|example3 = &lt;br /&gt;
var vector = [1, 2, 3];&lt;br /&gt;
debug.dump(subvec(vector, 1, 1)); # prints &amp;quot;[2]&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== typeof()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = typeof(object);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=193|t=Source}}&lt;br /&gt;
|text = Returns a string indicating the whether the object is &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt;, a scalar (number or string), a vector, a hash, a function, or a ghost.&lt;br /&gt;
|param1 = object&lt;br /&gt;
|param1text = Object to return the type of.&lt;br /&gt;
|example1 = &lt;br /&gt;
var object = nil;&lt;br /&gt;
print(typeof(object)); # prints &amp;quot;nil&amp;quot;&lt;br /&gt;
|example2 = &lt;br /&gt;
var object = &amp;quot;Hello world!&amp;quot;;&lt;br /&gt;
print(typeof(object)); # prints &amp;quot;scalar&amp;quot;&lt;br /&gt;
|example3 = &lt;br /&gt;
var object = math.pi;&lt;br /&gt;
print(typeof(object)); # prints &amp;quot;scalar&amp;quot;&lt;br /&gt;
|example4 = &lt;br /&gt;
var object = [1, 2, 3];&lt;br /&gt;
print(typeof(object)); # prints &amp;quot;vector&amp;quot;&lt;br /&gt;
|example5 = &lt;br /&gt;
var object = {};&lt;br /&gt;
print(typeof(object)); # prints &amp;quot;hash&amp;quot;&lt;br /&gt;
|example6 = &lt;br /&gt;
var object = func {};&lt;br /&gt;
print(typeof(object)); # prints &amp;quot;func&amp;quot;&lt;br /&gt;
|example7 =&lt;br /&gt;
var object = airportinfo();&lt;br /&gt;
print(typeof(object)); # prints &amp;quot;ghost&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- == Extension modules ==&lt;br /&gt;
=== thread ===&lt;br /&gt;
{{WIP}}&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = thread.newthread(func);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/threadlib.c|l=101|t=Source}}&lt;br /&gt;
|text = &lt;br /&gt;
|example1text = start a new worker thread&lt;br /&gt;
|example1 = thread.newthread( func() {} );&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = thread.newlock();&lt;br /&gt;
|source = {{simgear file|simgear/nasal/threadlib.c|l=101|t=Source}}&lt;br /&gt;
|text = &lt;br /&gt;
|example1text = create a new lock&lt;br /&gt;
|example1 = var lock = thread.newlock()&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = thread.lock();&lt;br /&gt;
|source = {{simgear file|simgear/nasal/threadlib.c|l=101|t=Source}}&lt;br /&gt;
|text = &lt;br /&gt;
|example1text = lock a lock&lt;br /&gt;
|example1 = var lock = thread.newlock()&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = thread.unlock();&lt;br /&gt;
|source = {{simgear file|simgear/nasal/threadlib.c|l=101|t=Source}}&lt;br /&gt;
|text = &lt;br /&gt;
|example1text = unlock a lock&lt;br /&gt;
|example1 = var lock = thread.unlock()&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = thread.newsem();&lt;br /&gt;
|source = {{simgear file|simgear/nasal/threadlib.c|l=101|t=Source}}&lt;br /&gt;
|text = &lt;br /&gt;
|example1text = create a new {{Wikipedia|semaphore}}&lt;br /&gt;
|example1 = var semaphore = thread.newsem()&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = thread.semdown();&lt;br /&gt;
|source = {{simgear file|simgear/nasal/threadlib.c|l=101|t=Source}}&lt;br /&gt;
|text = &lt;br /&gt;
|example1text = semaphore down&lt;br /&gt;
|example1 = thread.semdown(semaphore)&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = thread.semup();&lt;br /&gt;
|source = {{simgear file|simgear/nasal/threadlib.c|l=101|t=Source}}&lt;br /&gt;
|text = &lt;br /&gt;
|example1text = semaphore up&lt;br /&gt;
|example1 = thread.semup(semaphore)&lt;br /&gt;
}} --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Extension functions==&lt;br /&gt;
The '''extension functions''' are global functions that have been added to Nasal since its integration into FlightGear. Unlike the core library functions, they are generally specifically designed to interact directly with FlightGear. Extension functions come from three source files:&lt;br /&gt;
*{{flightgear file|src/Scripting/NasalPositioned.cxx}}&lt;br /&gt;
*{{flightgear file|src/Scripting/NasalSys.cxx}}&lt;br /&gt;
*{{fgdata file|Nasal/globals.nas}}&lt;br /&gt;
&lt;br /&gt;
===abort()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = abort();&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=565|t=Source}}&lt;br /&gt;
|text = This function is a wrapper for the C++ {{func link|abort()|link=http://www.cplusplus.com/reference/cstdlib/abort/}} function. It simply aborts FlightGear with an error, which varies depending on the operating system. This function should not really be used; instead, please use the &amp;quot;exit&amp;quot; [[Fgcommands|fgcommand]], which will exit FlightGear more gracefully (see example below).&lt;br /&gt;
|example1text = This example will immediately stop FlightGear with an error, such as &amp;quot;FlightGear has stopped working.&amp;quot;&lt;br /&gt;
|example1 = abort();&lt;br /&gt;
|example2text = For exiting FlightGear in a better way, please use the following code:&lt;br /&gt;
|example2 = fgcommand(&amp;quot;exit&amp;quot;);&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== abs() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = abs(number);&lt;br /&gt;
|source = {{fgdata file|Nasal/globals.nas|t=Source}}&lt;br /&gt;
|text = This simple function returns the {{wikipedia|absolute value|noicon=1}} of the provided number.&lt;br /&gt;
|param1 = number&lt;br /&gt;
|param1text = This argument is required and should be a number.&lt;br /&gt;
|example1 = print(abs(1)); # prints &amp;quot;1&amp;quot;&lt;br /&gt;
|example2 = print(abs(-1)); # prints &amp;quot;1&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===aircraftToCart() ===&lt;br /&gt;
This new function in FG 2017.2.1 takes coordinates in aircraft structural coordinate system, and translate them into geocentric coordinates.&lt;br /&gt;
Example for (5,6,7):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
var pos = aircraftToCart({x: -5, y: 6, z: -7});&lt;br /&gt;
var coord = geo.Coord.new();&lt;br /&gt;
coord.set_xyz(pos.x, pos.y, pos.z);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Notice: x and z is inverted sign on purpose.&lt;br /&gt;
if you want lat. lon, alt from that, just call: (degrees and meters)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
coord.lat()&lt;br /&gt;
coord.lon()&lt;br /&gt;
coord.alt()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===addcommand() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = addcommand(name, code);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=659|t=Source}}&lt;br /&gt;
|version = 2.12&lt;br /&gt;
|commit = {{flightgear commit|7b663c|t=commit}}&lt;br /&gt;
|text = {{see also|Howto:Add new fgcommands to FlightGear}}&lt;br /&gt;
&lt;br /&gt;
This function enables the addition of a new custom [[fgcommands|fgcommand]] to FlightGear from within Nasal. An fgcommand created using this method can be used in exactly the same way as the built-in fgcommands. Also, an fgcommand created via this method will always return True or 1, like all other fgcommands.&lt;br /&gt;
|param1 = name&lt;br /&gt;
|param1text = This will become the name of the new fgcommand. Must be a string.&lt;br /&gt;
|param2 = code&lt;br /&gt;
|param2text = The code that will be executed when the fgcommand is run. Must be a function.&lt;br /&gt;
|example1text = This example adds a new fgcommand and then runs it. Although it executes a simple {{func link|print()}} statement, any valid Nasal code can be used.&lt;br /&gt;
|example1 = addcommand(&amp;quot;myFGCmd&amp;quot;, func(node) {&lt;br /&gt;
    print(&amp;quot;fgcommand 'myFGCmd' has been run.&amp;quot;);&lt;br /&gt;
    props.dump( node );&lt;br /&gt;
});&lt;br /&gt;
fgcommand(&amp;quot;myFGCmd&amp;quot;, props.Node.new({foo:1, bar:2}) );&lt;br /&gt;
|example2text = This example demonstrates how parameters are defined in a new fgcommand.&lt;br /&gt;
|example2 = addcommand(&amp;quot;myFGCmd&amp;quot;, func(node){&lt;br /&gt;
    print(node.getNode(&amp;quot;number&amp;quot;).getValue()); # prints the value of &amp;quot;number,&amp;quot; which is 12&lt;br /&gt;
});&lt;br /&gt;
fgcommand(&amp;quot;myFGCmd&amp;quot;, props.Node.new({&amp;quot;number&amp;quot;: 12}));&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===airportinfo()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = airportinfo();&lt;br /&gt;
airportinfo(type);&lt;br /&gt;
airportinfo(id);&lt;br /&gt;
airportinfo(lat, lon[, type]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1024|t=Source}}&lt;br /&gt;
|text = Function for retrieval of airport, heliport, or seaplane base information. It returns a Nasal ghost; however, its structure is like that of a Nasal hash. The following information is returned:&lt;br /&gt;
* '''parents''': A vector containing a hash of various functions to access information about the runway. See {{flightgear file|src/Scripting/NasalPositioned.cxx|l=2659}} for full list.&lt;br /&gt;
* '''lon''': Longitude of the location.&lt;br /&gt;
* '''lat''': Latitude of the location.&lt;br /&gt;
* '''has_metar''': True or false depending whether the airport has a [[METAR]] code defined for it.&lt;br /&gt;
* '''elevation''': Elevation of the location in metres.&lt;br /&gt;
* '''id''': ICAO code of the airport (or ID of the seaplane base/heliport).&lt;br /&gt;
* '''name''': Name of the airport/heliport/seaplane base.&lt;br /&gt;
* '''runways'''&lt;br /&gt;
** '''&amp;lt;runway name&amp;gt;'''&lt;br /&gt;
*** '''id''': Name of runway.&lt;br /&gt;
*** '''lat''': Latitude of the runway.&lt;br /&gt;
*** '''lon''': Longitude of the runway.&lt;br /&gt;
*** '''heading''': Heading of the runway.&lt;br /&gt;
*** '''length''': Length of the runway in metres.&lt;br /&gt;
*** '''width''': Width of the runway in metres.&lt;br /&gt;
*** '''surface''': Runway surface type.&lt;br /&gt;
*** '''threshold''': Length of the runway's {{wikipedia|displaced threshold}} in metres. Will return 0 if there is none.&lt;br /&gt;
*** '''stopway''': Length of the runway's stopway (the area before the threshold) in metres. Will return 0 if there is none.&lt;br /&gt;
*** '''reciprocal''': &amp;lt;code&amp;gt;runway&amp;lt;/code&amp;gt; ghost of the reciprocal runway.&lt;br /&gt;
*** '''ils_frequency_mhz''': ILS frequency in megahertz.&lt;br /&gt;
*** '''ils''': &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt; ghost of the ILS transmitter.&lt;br /&gt;
* '''helipads'''&lt;br /&gt;
** '''&amp;lt;helipad name&amp;gt;'''&lt;br /&gt;
*** '''id''': Name of helipad.&lt;br /&gt;
*** '''lat''': Latitude of the helipad.&lt;br /&gt;
*** '''lon''': Longitude of the helipad.&lt;br /&gt;
*** '''heading''': Heading of the helipad.&lt;br /&gt;
*** '''length''': Length of the helipad in metres.&lt;br /&gt;
*** '''width''': Width of the helipad in metres.&lt;br /&gt;
*** '''surface''': Helipad surface type.&lt;br /&gt;
* '''taxiways'''&lt;br /&gt;
** '''&amp;lt;taxiway name&amp;gt;'''&lt;br /&gt;
*** '''id''': Name of taxiway.&lt;br /&gt;
*** '''lat''': Latitude of the taxiway.&lt;br /&gt;
*** '''lon''': Longitude of the taxiway.&lt;br /&gt;
*** '''heading''': Heading of the taxiway.&lt;br /&gt;
*** '''length''': Length of the taxiway in metres.&lt;br /&gt;
*** '''width''': Width of the taxiway in metres.&lt;br /&gt;
*** '''surface''': Taxiway surface type.&lt;br /&gt;
&lt;br /&gt;
Information is extracted in the same way as accessing members of a Nasal hash. For example:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
# prints to lengths of the runways of the nearest airport in feet and metres&lt;br /&gt;
var info = airportinfo();&lt;br /&gt;
print(&amp;quot;-- Lengths of the runways at &amp;quot;, info.name, &amp;quot; (&amp;quot;, info.id, &amp;quot;) --&amp;quot;);&lt;br /&gt;
foreach(var rwy; keys(info.runways)){&lt;br /&gt;
    print(rwy, &amp;quot;: &amp;quot;, math.round(info.runways[rwy].length * M2FT), &amp;quot; ft (&amp;quot;, info.runways[rwy].length, &amp;quot; m)&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that searches for locations that are a long way away (e.g., the nearest seaplane base to the middle of the Sahara) may cause FlightGear to pause for an amount of time.&lt;br /&gt;
|param1 = id&lt;br /&gt;
|param1text = The {{wikipedia|International Civil Aviation Organization airport code|ICAO code|noicon=1}} of an airport to retrieve information about.&lt;br /&gt;
|param2 = type&lt;br /&gt;
|param2text = When this argument is used, the function will return the closest airport of a certain type. Can be one of &amp;quot;heliport,&amp;quot; &amp;quot;seaport,&amp;quot; or &amp;quot;airport&amp;quot; (default).&lt;br /&gt;
: {{inote|Running this function without any parameters is equivalent to this:&lt;br /&gt;
: &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
airportinfo(&amp;quot;airport&amp;quot;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
|param3 = lat ''and'' lon&lt;br /&gt;
|param3text = When these parameters are used, the function will return information on the nearest airport, heliport or seaplane base (depending on the '''type''' parameter) to those coordinates.&lt;br /&gt;
|example1 = var info = airportinfo();&lt;br /&gt;
print(&amp;quot;Nearest airport: &amp;quot;, info.name, &amp;quot; (&amp;quot;, info.id, &amp;quot;)&amp;quot;); # prints the name and ICAO code of the nearest airport&lt;br /&gt;
|example2 = var info = airportinfo(&amp;quot;heliport&amp;quot;);&lt;br /&gt;
print(&amp;quot;Elevation of the nearest heliport: &amp;quot;, math.round(info.elevation * M2FT), &amp;quot; ft&amp;quot;); # prints the elevation and name of the nearest heliport&lt;br /&gt;
|example3 = var info = airportinfo(&amp;quot;KSQL&amp;quot;);&lt;br /&gt;
print(&amp;quot;-- Runways of &amp;quot;, info.name, &amp;quot; (&amp;quot;, info.id, &amp;quot;): --&amp;quot;);&lt;br /&gt;
foreach(var rwy; keys(info.runways)) {&lt;br /&gt;
    print(rwy); # prints the runways of KSQL&lt;br /&gt;
}&lt;br /&gt;
|example4 = var info = airportinfo(37.81909385, -122.4722484);&lt;br /&gt;
print(&amp;quot;Coordinates of the nearest airport: &amp;quot;, info.lat, &amp;quot;, &amp;quot;, info.lon); # print the name and ICAO of the nearest airport to the Golden Gate Bridge&lt;br /&gt;
|example5 = var info = airportinfo(37.81909385, -122.4722484, &amp;quot;seaport&amp;quot;);&lt;br /&gt;
print(&amp;quot;Nearest seaplane base: &amp;quot;, info.name, &amp;quot; (&amp;quot;, info.id, &amp;quot;)&amp;quot;); # print the name and ID of the nearest seaplane base to the Golden Gate Bridge&lt;br /&gt;
|example6text = This example prints the all information from an &amp;lt;code&amp;gt;airportinfo()&amp;lt;/code&amp;gt; call.&lt;br /&gt;
|example6 = var info = airportinfo(&amp;quot;KSFO&amp;quot;);&lt;br /&gt;
print(info.name);&lt;br /&gt;
print(info.id);&lt;br /&gt;
print(info.lat);&lt;br /&gt;
print(info.lon);&lt;br /&gt;
print(info.has_metar);&lt;br /&gt;
print(info.elevation);&lt;br /&gt;
foreach(var rwy; keys(info.runways)){&lt;br /&gt;
    print(&amp;quot;-- &amp;quot;, rwy, &amp;quot; --&amp;quot;);&lt;br /&gt;
    print(info.runways[rwy].lat);&lt;br /&gt;
    print(info.runways[rwy].lon);&lt;br /&gt;
    print(info.runways[rwy].length);&lt;br /&gt;
    print(info.runways[rwy].width);&lt;br /&gt;
    print(info.runways[rwy].heading);&lt;br /&gt;
    print(info.runways[rwy].stopway);&lt;br /&gt;
    print(info.runways[rwy].threshold);&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===airwaysRoute() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = airwaysRoute(start, end[, type]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1933|t=Source}}&lt;br /&gt;
|text = {{see also|Nasal Flightplan}}&lt;br /&gt;
This function returns a vector containing waypoints between two given waypoints. The returned waypoints are ghosts, but can be accessed in the same way as a Nasal hash. See [[Nasal Flightplan]] for more information.&lt;br /&gt;
|param1 = start&lt;br /&gt;
|param1text = Start waypoint, in the form of a waypoint ghost, such as that provided by {{func link|flightplan()}}.&lt;br /&gt;
|param2 = end&lt;br /&gt;
|param2text = Same as above.&lt;br /&gt;
|param3 = type&lt;br /&gt;
|param3text = Instructs the function to compute a high level route (when set to &amp;quot;highlevel&amp;quot;), or a low level route (when set to &amp;quot;lowlevel&amp;quot;). Defaults to &amp;quot;highlevel.&amp;quot;&lt;br /&gt;
|example1text = In the [[route manager]] dialog, add two waypoints to the flightplan, ideally ones that are far apart (tip: use the [[Map]] for this). Then run this code in your Nasal Console.&lt;br /&gt;
|example1 = var fp = flightplan();&lt;br /&gt;
var start = fp.getWP(0);&lt;br /&gt;
var end = fp.getWP(fp.getPlanSize() - 1);&lt;br /&gt;
var rt = airwaysRoute(start, end);&lt;br /&gt;
foreach(var wp; rt){&lt;br /&gt;
    print(wp.wp_name); # print the waypoints in the computed route&lt;br /&gt;
}&lt;br /&gt;
|example2text = Exactly the same as above, but computes a low level path.&lt;br /&gt;
|example2 = var fp = flightplan();&lt;br /&gt;
var start = fp.getWP(0);&lt;br /&gt;
var end = fp.getWP(fp.getPlanSize() - 1);&lt;br /&gt;
var rt = airwaysRoute(start, end, &amp;quot;lowlevel&amp;quot;);&lt;br /&gt;
foreach(var wp; rt){&lt;br /&gt;
    print(wp.wp_name); # print the waypoints in the computed route&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===airway()===&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = airway(ident [, pos]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=2644|t=Source}}&lt;br /&gt;
|text = {{see also|Nasal Flightplan}}&lt;br /&gt;
This function returns a ghost containing an airway of a specified id.&lt;br /&gt;
|param1 = ident&lt;br /&gt;
|param1text = a Positioned ghost (leg, navaid, airport) and so on, passed to the search function.&lt;br /&gt;
|param2 = pos&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===assert()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = assert(condition[, message]);&lt;br /&gt;
|source = {{fgdata file|Nasal/globals.nas|t=Source}}&lt;br /&gt;
|version = 3.2&lt;br /&gt;
|commit = {{fgdata commit|8b16a7|t=commit}}&lt;br /&gt;
|text = Returns either true if the condition evaluates as true, or aborts with a {{func link|die()}} call, which can be customised.&lt;br /&gt;
|param1 = condition&lt;br /&gt;
|param1text = Condition to evaluate.&lt;br /&gt;
|param2 = message&lt;br /&gt;
|param2text = Optional message that will be used in any {{func link|die()}} call. Defaults to &amp;quot;assertion failed!&amp;quot;&lt;br /&gt;
|example1 = var a = 1;&lt;br /&gt;
var b = 2;&lt;br /&gt;
print(assert(a &amp;lt; b)); # prints &amp;quot;1&amp;quot; (true)&lt;br /&gt;
|example2 = var a = 1;&lt;br /&gt;
var b = 2;&lt;br /&gt;
assert(a &amp;gt; b, 'a is not greater than b'); # aborts with a custom error message&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===carttogeod()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = carttogeod(x, y, z);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=945|t=Source}}&lt;br /&gt;
|text = Converts {{wikipedia|ECEF|Earth-centered, Earth-fixed}} coordinates (x, y and z) to {{wikipedia|geodetic coordinates}} (latitude, longitude, and altitude). A vector is returned containing latitude and longitude, both in degrees, and altitude, which is returned in metres above the equatorial radius of Earth as defined by the {{wikipedia|WGS 84}} (6,378,137 metres).&amp;lt;ref&amp;gt;{{simgear file|simgear/math/sg_geodesy.hxx|l=43}}&amp;lt;/ref&amp;gt;&lt;br /&gt;
|param1 = x&lt;br /&gt;
|param1text = Mandatory x-axis value, in metres.&lt;br /&gt;
|param2 = y&lt;br /&gt;
|param2text = Mandatory y-axis value, in metres.&lt;br /&gt;
|param3 = z&lt;br /&gt;
|param3text = Mandatory z-axis value, in metres.&lt;br /&gt;
|example1 = var (lat, lon, alt) = carttogeod(6378137, 0, 0); # point is the intersection of the prime meridian and equator.&lt;br /&gt;
print(&amp;quot;Latitude: &amp;quot;, lat); # prints lat, lon and alt, which are all zero, see above&lt;br /&gt;
print(&amp;quot;Longitude: &amp;quot;, lon);&lt;br /&gt;
print(&amp;quot;Altitude: &amp;quot;, alt);&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===cmdarg()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|private = _cmdarg()&lt;br /&gt;
|syntax = cmdarg();&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=513|t=Part 1}} {{!}} {{fgdata file|Nasal/globals.nas|t=Part 2}}&lt;br /&gt;
|text = &amp;lt;code&amp;gt;cmdarg()&amp;lt;/code&amp;gt; returns the property root of certain types of XML files. These could be nodes in the [[Property Tree]], or temporary and/or non-public nodes outside the Property tree. &lt;br /&gt;
It is used by Nasal scripts embedded in XML files. It returns a &amp;lt;code&amp;gt;props.Node&amp;lt;/code&amp;gt; object (see {{fgdata file|Nasal/props.nas}}), and you can use all of its methods on the returned value. &amp;lt;code&amp;gt;cmdarg()&amp;lt;/code&amp;gt; should only be used in four types/places of XML files:&lt;br /&gt;
* Bindings: This is needed so that the value of a joystick's axis can be accessed internally.&lt;br /&gt;
* Dialogs: This will return the root of the dialog in the Property Tree. This is useful for dialogs that are created/modified procedurally (e.g. for populating/changing widgets while loading the dialog). &lt;br /&gt;
* Embedded Canvases: The Nasal code behind [[Canvas]] windows [[Howto:Adding a canvas to a GUI dialog|embedded in PUI dialogs]] can use it to accessing the root directory of their Canvas.&lt;br /&gt;
* Animation XML files: If the animation XML file is used in an AI/MP model, &amp;lt;code&amp;gt;cmdarg()&amp;lt;/code&amp;gt; will return the root of the AI model in the &amp;lt;code&amp;gt;/ai/models/&amp;lt;/code&amp;gt; directory. Examples: &amp;lt;code&amp;gt;/ai/models/aircraft[3]/&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/ai/models/multiplayer[1]/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You should not use &amp;lt;code&amp;gt;cmdarg()&amp;lt;/code&amp;gt; in places other than those stated above. Although it won't cause an error, it will return the value of the last legitimate &amp;lt;code&amp;gt;cmdarg()&amp;lt;/code&amp;gt; call. &lt;br /&gt;
&lt;br /&gt;
Also, you should not delay &amp;lt;code&amp;gt;cmdarg()&amp;lt;/code&amp;gt; using {{func link|maketimer()}}, {{func link|settimer()}} or {{func link|setlistener()}}, because it will return an unrelated property.&lt;br /&gt;
|example1 = fgcommand(&amp;quot;dialog-show&amp;quot;, {&amp;quot;dialog-name&amp;quot;: &amp;quot;cmdarg-demo&amp;quot;});&lt;br /&gt;
|example1text = &amp;lt;br&amp;gt;This example demonstrates the usage of &amp;lt;code&amp;gt;cmdarg()&amp;lt;/code&amp;gt; in a binding.  Save the below XML snippet as &amp;lt;tt&amp;gt;[[$FG_ROOT]]/gui/dialogs/cmdarg-demo.xml&amp;lt;/tt&amp;gt;. Then run the Nasal snippet below in your [[Nasal Console]]. Upon clicking {{button|Close}}, a message will be printed sowing the root of the binding in the Property Tree.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;PropertyList&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;name&amp;gt;cmdarg-demo&amp;lt;/name&amp;gt;&lt;br /&gt;
&amp;lt;layout&amp;gt;vbox&amp;lt;/layout&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;text&amp;gt;&lt;br /&gt;
  &amp;lt;label&amp;gt;Click &amp;quot;Close&amp;quot; to activate the demonstration (a message in the console).&amp;lt;/label&amp;gt;&lt;br /&gt;
&amp;lt;/text&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;button&amp;gt;&lt;br /&gt;
  &amp;lt;legend&amp;gt;Close&amp;lt;/legend&amp;gt;&lt;br /&gt;
  &amp;lt;binding&amp;gt;&lt;br /&gt;
    &amp;lt;command&amp;gt;nasal&amp;lt;/command&amp;gt;&lt;br /&gt;
    &amp;lt;script&amp;gt;print(&amp;quot;Button binding root: '&amp;quot; ~ cmdarg().getPath() ~ &amp;quot;'&amp;quot;);&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;/binding&amp;gt;&lt;br /&gt;
  &amp;lt;binding&amp;gt;&lt;br /&gt;
    &amp;lt;command&amp;gt;dialog-close&amp;lt;/command&amp;gt;&lt;br /&gt;
  &amp;lt;/binding&amp;gt;&lt;br /&gt;
&amp;lt;/button&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/PropertyList&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|example2text = This example demonstrates the usage of &amp;lt;code&amp;gt;cmdarg()&amp;lt;/code&amp;gt; in Nasal code within dialogs.  Open &amp;lt;tt&amp;gt;[[$FG_ROOT]]/gui/dialogs/cmdarg-demo.xml&amp;lt;/tt&amp;gt; from the previous example, copy &amp;amp; paste the code below, and save it. Then run the same Nasal snippet as the previous example in your Nasal Console. If you click {{button|Click me!}}, the button's label will change to &amp;quot;I've been changed!&amp;quot;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;PropertyList&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;name&amp;gt;cmdarg-demo&amp;lt;/name&amp;gt;&lt;br /&gt;
&amp;lt;layout&amp;gt;vbox&amp;lt;/layout&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;text&amp;gt;&lt;br /&gt;
  &amp;lt;label&amp;gt;Click &amp;quot;Click me!&amp;quot; to activate the demonstration (the button's label will change).&amp;lt;/label&amp;gt;&lt;br /&gt;
&amp;lt;/text&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;button&amp;gt;&lt;br /&gt;
  &amp;lt;legend&amp;gt;Click me!&amp;lt;/legend&amp;gt;&lt;br /&gt;
  &amp;lt;binding&amp;gt;&lt;br /&gt;
    &amp;lt;command&amp;gt;nasal&amp;lt;/command&amp;gt;&lt;br /&gt;
    &amp;lt;script&amp;gt;change_label();&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;/binding&amp;gt;&lt;br /&gt;
&amp;lt;/button&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;button&amp;gt;&lt;br /&gt;
  &amp;lt;legend&amp;gt;Close&amp;lt;/legend&amp;gt;&lt;br /&gt;
  &amp;lt;binding&amp;gt;&lt;br /&gt;
    &amp;lt;command&amp;gt;dialog-close&amp;lt;/command&amp;gt;&lt;br /&gt;
  &amp;lt;/binding&amp;gt;&lt;br /&gt;
&amp;lt;/button&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nasal&amp;gt;&lt;br /&gt;
  &amp;lt;open&amp;gt;&amp;lt;![CDATA[&lt;br /&gt;
    var dlg_root = cmdarg();&lt;br /&gt;
    var dlg_name = {&amp;quot;dialog-name&amp;quot;: &amp;quot;cmdarg-demo&amp;quot;};&lt;br /&gt;
    var change_label = func {&lt;br /&gt;
        dlg_root.getNode(&amp;quot;button[0]/legend&amp;quot;).setValue(&amp;quot;I've been changed!&amp;quot;);&lt;br /&gt;
        fgcommand(&amp;quot;dialog-close&amp;quot;, dlg_name);&lt;br /&gt;
        fgcommand(&amp;quot;dialog-show&amp;quot;, dlg_name);&lt;br /&gt;
    }&lt;br /&gt;
  ]]&amp;gt;&amp;lt;/open&amp;gt;&lt;br /&gt;
&amp;lt;/nasal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/PropertyList&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|example3text = For an example of &amp;lt;code&amp;gt;cmdarg()&amp;lt;/code&amp;gt; used with Canvas, please see [[Howto:Adding a canvas to a GUI dialog#FGPlot|Howto:Adding a canvas to a GUI dialog]].&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===courseAndDistance()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = courseAndDistance(to);&lt;br /&gt;
courseAndDistance(from, to);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1668|t=Source}}&lt;br /&gt;
|text = Returns a vector containing the course from one point to another and the distance between them in nautical miles. The course is the initial bearing (see [http://www.movable-type.co.uk/scripts/latlong.html#bearing here]), and is in the range 0–360. Both arguments can be one of:&lt;br /&gt;
:* An &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;runway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;taxiway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fix&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;waypoint&amp;lt;/code&amp;gt; ghost type&lt;br /&gt;
:* A hash with ''lat'' and ''lon'' members&lt;br /&gt;
:* A geo.Coord object with geodetic coordinates (cartesian coordinates will not be accepted)&lt;br /&gt;
|param1 = from&lt;br /&gt;
|param1text = Optional parameter defining the from where the function should calculate its results. If the function is given one argument ('''to'''), the aircraft's current position will be used. As well as the argument types as defined above, this argument can be two numbers separated with a comma, as if the function is taking three arguments. See example 5 below.&lt;br /&gt;
|param2 = to&lt;br /&gt;
|param2text = Like the first parameter, but defines the second point.&lt;br /&gt;
|example1text = This example demonstrates the usage of the function with the &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt; ghost type.&lt;br /&gt;
|example1 = var from = airportinfo(&amp;quot;KSFO&amp;quot;);&lt;br /&gt;
var to = airportinfo(&amp;quot;KSQL&amp;quot;);&lt;br /&gt;
var (course, dist) = courseAndDistance(from, to);&lt;br /&gt;
print(course); # prints course from KSFO to KSQL&lt;br /&gt;
print(dist); # prints distance in nm from KSFO to KSQL&lt;br /&gt;
|example2text = This example demonstrates the usage of the function with hashes containing ''lat'' and ''lon''.&lt;br /&gt;
|example2 = var from = {lat: 0, lon: 0};&lt;br /&gt;
var to = {lat: 1, lon: 1};&lt;br /&gt;
var (course, dist) = courseAndDistance(from, to);&lt;br /&gt;
print(course);&lt;br /&gt;
print(dist);&lt;br /&gt;
|example3text = This example demonstrates usage of a geo.Coord object.&lt;br /&gt;
|example3 = var from = geo.Coord.new().set_latlon(0, 0);&lt;br /&gt;
var to = geo.Coord.new().set_latlon(1, 1);&lt;br /&gt;
var (course, dist) = courseAndDistance(from, to);&lt;br /&gt;
print(course);&lt;br /&gt;
print(dist);&lt;br /&gt;
|example4text = This example demonstrates usage of differing parameter types.&lt;br /&gt;
|example4 = var from = airportinfo(&amp;quot;KSFO&amp;quot;);&lt;br /&gt;
var to = geo.Coord.new().set_latlon(0, 0);&lt;br /&gt;
var (course, dist) = courseAndDistance(from, to);&lt;br /&gt;
print(course);&lt;br /&gt;
print(dist);&lt;br /&gt;
|example5text = The same as above, but the other way round.&lt;br /&gt;
|example5 = var to = {lat: 1, lon: 1};&lt;br /&gt;
var (course, dist) = courseAndDistance(0, 0, to);&lt;br /&gt;
print(course);&lt;br /&gt;
print(dist);&lt;br /&gt;
|example6text = Usage of just one parameter.&lt;br /&gt;
|example6 = var dest = airportinfo(&amp;quot;KSQL&amp;quot;);&lt;br /&gt;
var (course, dist) = courseAndDistance(dest);&lt;br /&gt;
print(&amp;quot;Turn to heading &amp;quot;, math.round(course), &amp;quot;. You have &amp;quot;, sprintf(&amp;quot;%.2f&amp;quot;, dist), &amp;quot; nm to go&amp;quot;);&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===createFlightplan()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = createFlightplan(path);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=2331|t=Source}}&lt;br /&gt;
|text = Creates an empty flightplan object. It accepts one argument, ''path'' passed an absolute path to a .fgfp / .gpx file, it will populate the flightplan with waypoints from the file.&lt;br /&gt;
|param1 = path&lt;br /&gt;
|param1text = Optional parameter defining the file from which a flightplan will be populated.&lt;br /&gt;
|example1 = &lt;br /&gt;
var path = getprop(&amp;quot;/sim/fg-home&amp;quot;) ~ &amp;quot;/Export/test.fgfp&amp;quot;;&lt;br /&gt;
var flightplan = createFlightplan(path);&lt;br /&gt;
debug.dump(flightplan);&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===createDiscontinuity()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = createDiscontinuity();&lt;br /&gt;
|text = Returns a &amp;lt;code&amp;gt;waypoint&amp;lt;/code&amp;gt; ghost object. A route discontinuity is inserted by an {{abbr|FMS|Flight Management System}} when it is unsure how to connect two waypoints.&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=2045|t=Source}}&lt;br /&gt;
|version = 2016.1&lt;br /&gt;
|commit = {{flightgear commit|caead6|t=commit}}&lt;br /&gt;
}}&lt;br /&gt;
===createViaTo()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = createViaTo(airway, waypoint);&lt;br /&gt;
|text = Returns a &amp;lt;code&amp;gt;waypoint&amp;lt;/code&amp;gt; ghost object. It represents a route &amp;quot;via '''airway''' to '''waypoint'''&amp;quot;.&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=2009|t=Source}}&lt;br /&gt;
|version = 2016.1&lt;br /&gt;
|commit = {{flightgear commit|caead6|t=commit}}&lt;br /&gt;
|param1 = airway&lt;br /&gt;
|param1text = The name of an airway.&lt;br /&gt;
|param2 = waypoint&lt;br /&gt;
|param2text = Must be in the airway and one of:&lt;br /&gt;
* The name of a waypoint.&lt;br /&gt;
* An &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;runway&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;fix&amp;lt;/code&amp;gt; ghost object.&lt;br /&gt;
}}&lt;br /&gt;
===createWP()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = createWP(pos, name[, flag]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1964|t=Source}}&lt;br /&gt;
|text = Creates a new waypoint ghost object.&lt;br /&gt;
|param1 = pos&lt;br /&gt;
|param1text = Dictates the position of the new waypoint. It can be one of the following:&lt;br /&gt;
:* An &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;runway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;taxiway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fix&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;waypoint&amp;lt;/code&amp;gt; ghost type&lt;br /&gt;
:* A hash with ''lat'' and ''lon'' members&lt;br /&gt;
:* A geo.Coord object&lt;br /&gt;
:* Two numbers separated by a comma, as if the function is taking three arguments. See example 4 below.&lt;br /&gt;
|param2 = name&lt;br /&gt;
|param2text = String that will become the name of the new waypoint.&lt;br /&gt;
|param3 = flag&lt;br /&gt;
|param3text = Optional string that will tell FlightGear what type of waypoint it is. Must be one of &amp;quot;sid,&amp;quot; &amp;quot;star,&amp;quot; &amp;quot;approach,&amp;quot; &amp;quot;missed,&amp;quot; or &amp;quot;pseudo.&amp;quot;&lt;br /&gt;
|example1text = Creates a waypoint directly in front and 1 km away and appends it to the flight plan.&lt;br /&gt;
|example1 = var pos = geo.aircraft_position().apply_course_distance(getprop(&amp;quot;/orientation/heading-deg&amp;quot;), 1000);&lt;br /&gt;
var wp = createWP(pos, &amp;quot;NEWWP&amp;quot;);&lt;br /&gt;
var fp = flightplan();&lt;br /&gt;
fp.appendWP(wp);&lt;br /&gt;
|example2 = var pos = geo.aircraft_position().apply_course_distance(getprop(&amp;quot;/orientation/heading-deg&amp;quot;), 1000);&lt;br /&gt;
var wp = createWP({lat: pos.lat(), lon: pos.lon()}, &amp;quot;NEWWP&amp;quot;);&lt;br /&gt;
var fp = flightplan();&lt;br /&gt;
fp.appendWP(wp);&lt;br /&gt;
|example3 = var apt = airportinfo();&lt;br /&gt;
var wp = createWP(apt, &amp;quot;NEWWP&amp;quot;);&lt;br /&gt;
var fp = flightplan();&lt;br /&gt;
fp.appendWP(wp);&lt;br /&gt;
|example4 = var pos = geo.aircraft_position().apply_course_distance(getprop(&amp;quot;/orientation/heading-deg&amp;quot;), 1000);&lt;br /&gt;
var wp = createWP(pos.lat(), pos.lon(), &amp;quot;NEWWP&amp;quot;);&lt;br /&gt;
var fp = flightplan();&lt;br /&gt;
fp.appendWP(wp);&lt;br /&gt;
|example5text = Creates a new waypoint and adds it to the flight plan. Waypoints of the type &amp;quot;pseudo&amp;quot; are then removed from the flight plan, including the new waypoint. The {{func link|print()}} statements show this.&lt;br /&gt;
|example5 = var pos = geo.aircraft_position().apply_course_distance(getprop(&amp;quot;/orientation/heading-deg&amp;quot;), 1000);&lt;br /&gt;
var wp = createWP(pos, &amp;quot;NEWWP&amp;quot;, &amp;quot;pseudo&amp;quot;);&lt;br /&gt;
var fp = flightplan();&lt;br /&gt;
fp.appendWP(wp);&lt;br /&gt;
print(fp.getPlanSize());&lt;br /&gt;
fp.clearWPType(&amp;quot;pseudo&amp;quot;);&lt;br /&gt;
print(fp.getPlanSize());&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===createWPFrom()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = createWPFrom(object[, flag]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1989|t=Source}}&lt;br /&gt;
|text = Creates a new waypoint object from another object.&lt;br /&gt;
|param1 = object&lt;br /&gt;
|param1text = A ghost object. Must be a ghost type that is one of &amp;quot;airport,&amp;quot; &amp;quot;navaid,&amp;quot; &amp;quot;runway,&amp;quot; or &amp;quot;fix.&amp;quot;&lt;br /&gt;
|param2 = flag&lt;br /&gt;
|param2text = Optional string that will tell FlightGear what type of waypoint it is. Must be one of &amp;quot;sid,&amp;quot; &amp;quot;star,&amp;quot; &amp;quot;approach,&amp;quot; &amp;quot;missed,&amp;quot; or &amp;quot;pseudo.&amp;quot;&lt;br /&gt;
|example1text = Creates a new waypoint and appends it to the flight plan.&lt;br /&gt;
|example1 = var apt = airportinfo(&amp;quot;KSFO&amp;quot;);&lt;br /&gt;
var wp = createWPFrom(apt);&lt;br /&gt;
var fp = flightplan();&lt;br /&gt;
fp.appendWP(wp);&lt;br /&gt;
|example2text = Creates a new waypoint and appends it to the flight plan. This way point is then removed; the {{func link|print()}} statements prove this.&lt;br /&gt;
|example2 = var apt = airportinfo(&amp;quot;KSFO&amp;quot;);&lt;br /&gt;
var wp = createWPFrom(apt, &amp;quot;pseudo&amp;quot;);&lt;br /&gt;
print(wp.wp_name);&lt;br /&gt;
var fp = flightplan();&lt;br /&gt;
fp.appendWP(wp);&lt;br /&gt;
print(fp.getPlanSize());&lt;br /&gt;
fp.clearWPType(&amp;quot;pseudo&amp;quot;);&lt;br /&gt;
print(fp.getPlanSize());&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===defined()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = defined(symbol);&lt;br /&gt;
|source = {{fgdata file|Nasal/globals.nas|t=Source}}&lt;br /&gt;
|text = Returns 1 (true) or 0 (false) depending on whether a variable exists.&lt;br /&gt;
|param1 = symbol&lt;br /&gt;
|param1text = A string that will be what the function searches for.&lt;br /&gt;
|example1 = var number = 12;&lt;br /&gt;
var check_exist = func {&lt;br /&gt;
    print(&amp;quot;Variable 'number' &amp;quot;, defined(&amp;quot;number&amp;quot;) == 1 ? &amp;quot;exists&amp;quot; : &amp;quot;does not exist&amp;quot;); # 'number' does exist&lt;br /&gt;
    print(&amp;quot;Variable 'number2' &amp;quot;, defined(&amp;quot;number2&amp;quot;) == 1 ? &amp;quot;exists&amp;quot; : &amp;quot;does not exist&amp;quot;); # 'number2' does not exist&lt;br /&gt;
}&lt;br /&gt;
check_exist();&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===directory()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = directory(path);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=572|t=Source}}&lt;br /&gt;
|text = Returns a vector containing a list of the folders and files in a given file path or &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; if the path doesn't exist. Hidden folders and files are not added to the vector.&lt;br /&gt;
{{inote|The first two elements of the vector will be &amp;lt;code&amp;gt;'.'&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;'..'&amp;lt;/code&amp;gt;. These are for navigating back up the file tree, but have no use in Nasal. They can be safely removed from the vector.}}&lt;br /&gt;
|param1 = path&lt;br /&gt;
|param1text = Absolute file path.&lt;br /&gt;
|example1text = Gets the folders and files in [[$FG_ROOT]], and then removes the extra first two elements (see note above). &lt;br /&gt;
|example1 = var dir = directory(getprop(&amp;quot;/sim/fg-root&amp;quot;)); # get directory&lt;br /&gt;
dir = subvec(dir, 2); # strips off the first two elements&lt;br /&gt;
debug.dump(dir); # dump the vector&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===fgcommand()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = fgcommand(cmd[, args]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=456|t=Part 1}} {{!}} {{fgdata file|Nasal/globals.nas|t=Part 2}}&lt;br /&gt;
|text = Runs an fgcommand. See also {{readme file|commands}} and [[Bindings]] for more information. See {{flightgear file|src/Main/fg_commands.cxx|l=1425}} for the full list of fgcommands. Note that fgcommands generated by {{func link|addcommand()}} can also be run using this function. Also, the full list of fgcommands depends on the version of FlightGear you have. Returns 1 (true) if the fgcommand succeeded or 0 (false) if it failed.&lt;br /&gt;
|param1 = cmd&lt;br /&gt;
|param1text = String that is the name of the command that is to be run.&lt;br /&gt;
|param2 = args&lt;br /&gt;
|param2text = If the fgcommand takes arguments, they are inputted using this argument. Can either be a &amp;lt;code&amp;gt;props.Node&amp;lt;/code&amp;gt; object, or a hash (see examples below).&lt;br /&gt;
|example1 = fgcommand(&amp;quot;null&amp;quot;); # does nothing&lt;br /&gt;
|example2 = var args = props.Node.new({'script': 'print(&amp;quot;Running fgcommand&amp;quot;);'});&lt;br /&gt;
if (fgcommand(&amp;quot;nasal&amp;quot;, args)) { # prints &amp;quot;Running fgcommand&amp;quot; and then one of these print statements&lt;br /&gt;
    print(&amp;quot;Fgcommand succeeded&amp;quot;);&lt;br /&gt;
} else {&lt;br /&gt;
    print(&amp;quot;Fgcommand encountered a problem&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
|example3 = var args = { 'dialog-name': 'about' };&lt;br /&gt;
fgcommand(&amp;quot;dialog-show&amp;quot;, args); # shows the 'about' dialog&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===findAirportsByICAO() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = findAirportsByICAO(search[, type]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1096|t=Source}}&lt;br /&gt;
|text = Returns a vector containing &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt; ghost objects which are (by default) airports whose ICAO code matches the search string. The results are sorted by range from closest to furthest.&lt;br /&gt;
|param1 = search&lt;br /&gt;
|param1text = Search string for the function. Can either be a partial or a full ICAO code.&lt;br /&gt;
:{{icaution|The more matches there are for the given code, the longer the function will take. Passing just one character (e.g., &amp;quot;K&amp;quot;), might make FlightGear hang for a certain amount of time.}}&lt;br /&gt;
|param2 = type&lt;br /&gt;
|param2text = This will narrow the search to airports of a certain type. By default, only airports are searched for. May be one of &amp;quot;airport,&amp;quot; &amp;quot;heliport,&amp;quot; or &amp;quot;seaport.&amp;quot;&lt;br /&gt;
|example1 = var apts = findAirportsByICAO(&amp;quot;KSF&amp;quot;); # finds all airports matching &amp;quot;KSF&amp;quot;&lt;br /&gt;
foreach(var apt; apts){&lt;br /&gt;
    print(apt.name, &amp;quot; (&amp;quot;, apt.id, &amp;quot;)&amp;quot;); # prints them&lt;br /&gt;
}&lt;br /&gt;
|example2 = var apts = findAirportsByICAO(&amp;quot;SP0&amp;quot;, &amp;quot;seaport&amp;quot;); # finds all seaplane bases matching &amp;quot;SP0&amp;quot;&lt;br /&gt;
foreach(var apt; apts){&lt;br /&gt;
    print(apt.name, &amp;quot; (&amp;quot;, apt.id, &amp;quot;)&amp;quot;); # prints them&lt;br /&gt;
}&lt;br /&gt;
|example3 = var apt = findAirportsByICAO(&amp;quot;XBET&amp;quot;); # one way to check if an airport does exist&amp;quot;&lt;br /&gt;
if (size(apt) == 0) {&lt;br /&gt;
    print(&amp;quot;Airport does not exist&amp;quot;); # this one will be printed&lt;br /&gt;
} else {&lt;br /&gt;
    print(&amp;quot;Airport does exist&amp;quot;);&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===findAirportsWithinRange()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = findAirportsWithinRange([pos, ]range[, type]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1066|t=Source}}&lt;br /&gt;
|text = Returns a vector of &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt; ghost object which are (by default) airports that are within a given range of a given position, or the aircraft's current position. The results are sorted by range from closest to furthest.&lt;br /&gt;
|param1 = pos&lt;br /&gt;
|param1text = Optional position to search around. If not given, the aircraft's current position will be used. Can be one of:&lt;br /&gt;
:* An &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;runway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;taxiway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fix&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;waypoint&amp;lt;/code&amp;gt; ghost type&lt;br /&gt;
:* A hash with ''lat'' and ''lon'' members&lt;br /&gt;
:* A geo.Coord object&lt;br /&gt;
:* Two numbers separated by a comma, as if the function is taking three arguments. Example: &amp;lt;code&amp;gt;findAirportsWithinRange(lat, lon, range, type);&amp;lt;/code&amp;gt;.&lt;br /&gt;
|param2 = range&lt;br /&gt;
|param2text = Mandatory number giving the range in nautical miles within which to search for airports/heliports/seaplane bases.only airports are searched for.&lt;br /&gt;
|param3 = type&lt;br /&gt;
|param3text = This will narrow the search to airports of a certain type. By default, only airports are searched for. May be one of &amp;quot;airport,&amp;quot; &amp;quot;heliport,&amp;quot; or &amp;quot;seaport.&amp;quot;&lt;br /&gt;
|example1text = Searches for airports within 10 nm of [[KSFO]].&lt;br /&gt;
|example1 = var pos = airportinfo(&amp;quot;KSFO&amp;quot;);&lt;br /&gt;
var apts = findAirportsWithinRange(pos, 10);&lt;br /&gt;
foreach(var apt; apts){&lt;br /&gt;
    print(apt.name, &amp;quot; (&amp;quot;, apt.id, &amp;quot;)&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
|example2text = Searches for seaplane bases within 15 nm of [[KSFO]].&lt;br /&gt;
|example2 = var pos = airportinfo(&amp;quot;KSFO&amp;quot;);&lt;br /&gt;
var apts = findAirportsWithinRange(pos, 15, &amp;quot;seaport&amp;quot;);&lt;br /&gt;
foreach(var apt; apts){&lt;br /&gt;
    print(apt.name, &amp;quot; (&amp;quot;, apt.id, &amp;quot;)&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
|example3text = Searches for airports within 10 nm of your current position.&lt;br /&gt;
|example3 = var apts = findAirportsWithinRange(10);&lt;br /&gt;
foreach(var apt; apts){&lt;br /&gt;
    print(apt.name, &amp;quot; (&amp;quot;, apt.id, &amp;quot;)&amp;quot;);&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===findFixesByID()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = findFixesByID([pos, ]id);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1627|t=Source}}&lt;br /&gt;
|text = Returns a vector containing &amp;lt;code&amp;gt;fix&amp;lt;/code&amp;gt; ghost objects matching a given ID, sorted by range from a certain position.&lt;br /&gt;
{{inote|Fixes are (usually) also known as waypoints.}}&lt;br /&gt;
|param1 = pos&lt;br /&gt;
|param1text = Optional position to search around. If not given, the aircraft's current position will be used. Can be one of:&lt;br /&gt;
:* An &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;runway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;taxiway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fix&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;waypoint&amp;lt;/code&amp;gt; ghost type&lt;br /&gt;
:* A hash with ''lat'' and ''lon'' members&lt;br /&gt;
:* A geo.Coord object&lt;br /&gt;
:* Two numbers separated by a comma, as if the function is taking three arguments. Example: &amp;lt;code&amp;gt;findFixesByID(lat, lon, id);&amp;lt;/code&amp;gt;.&lt;br /&gt;
|param2 = id&lt;br /&gt;
|param2text = Full or partial ID of the fix to search for.&lt;br /&gt;
:{{inote|1=Inputting a partial ID does not work correctly (see [http://forum.flightgear.org/viewtopic.php?f=30&amp;amp;t=28129 here]). It is best to just input a full ID.}}&lt;br /&gt;
|example1 = var fixes = findFixesByID(&amp;quot;POGIC&amp;quot;);&lt;br /&gt;
foreach(var fix; fixes){&lt;br /&gt;
    print(fix.id, &amp;quot; - lat: &amp;quot;, fix.lat, &amp;quot; {{!}} lon: &amp;quot;, fix.lon); # prints information about POGIC&lt;br /&gt;
}&lt;br /&gt;
|example2 = var fix = findFixesByID(&amp;quot;ZUNAP&amp;quot;);&lt;br /&gt;
fix = fix[0];&lt;br /&gt;
var (course, dist) = courseAndDistance(fix);&lt;br /&gt;
print(&amp;quot;Turn to heading &amp;quot;, math.round(course), &amp;quot;. You have &amp;quot;, sprintf(&amp;quot;%.2f&amp;quot;, dist), &amp;quot; nm to go to reach &amp;quot;, fixes[0].id);&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===findNavaidByFrequencyMHz()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = findNavaidByFrequencyMHz([pos, ]freq[, type]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1547|t=Source}}&lt;br /&gt;
|text = Returns a &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt; ghost object for a navaid matching a given frequency. If there is more than one navaid with that frequency, the nearest station is returned.&lt;br /&gt;
|param1 = pos&lt;br /&gt;
|param1text = Optional position to search around. If not given, the aircraft's current position will be used. Can be one of:&lt;br /&gt;
:* An &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;runway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;taxiway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fix&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;waypoint&amp;lt;/code&amp;gt; ghost type&lt;br /&gt;
:* A hash with ''lat'' and ''lon'' members&lt;br /&gt;
:* A geo.Coord object&lt;br /&gt;
:* Two numbers separated by a comma, as if the function is taking three arguments. Example: &amp;lt;code&amp;gt;findNavaidByFrequency(lat, lon, freq, type);&amp;lt;/code&amp;gt;.&lt;br /&gt;
|param2 = freq&lt;br /&gt;
|param2text = Frequency, in megahertz, of the navaid to search for.&lt;br /&gt;
|param3 = type&lt;br /&gt;
|param3text = This will narrow the search to navaids of a certain type. Defaults to &amp;quot;all.&amp;quot; For the full list of accepted type arguments, see {{flightgear file|src/Navaids/positioned.cxx|l=127}}.&lt;br /&gt;
|example1 = var navaid = findNavaidByFrequencyMHz(11.17);&lt;br /&gt;
print(&amp;quot;ID: &amp;quot;, navaid.id); # prints info about the navaid&lt;br /&gt;
print(&amp;quot;Name: &amp;quot;, navaid.name);&lt;br /&gt;
print(&amp;quot;Latitude: &amp;quot;, navaid.lat);&lt;br /&gt;
print(&amp;quot;Longitude: &amp;quot;, navaid.lon);&lt;br /&gt;
print(&amp;quot;Elevation (AMSL): &amp;quot;, navaid.elevation, &amp;quot; m&amp;quot;);&lt;br /&gt;
print(&amp;quot;Type: &amp;quot;, navaid.type);&lt;br /&gt;
print(&amp;quot;Frequency: &amp;quot;, sprintf(&amp;quot;%.3f&amp;quot;, navaid.frequency / 1000), &amp;quot; Mhz&amp;quot;);&lt;br /&gt;
print(&amp;quot;Range: &amp;quot;, navaid.range_nm, &amp;quot; nm&amp;quot;);&lt;br /&gt;
if(navaid.course) print(&amp;quot;Course: &amp;quot;, navaid.course);&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== findNavaidsByFrequency()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = findNavaidsByFrequency([pos, ]freq[, type]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1572|t=Source}}&lt;br /&gt;
|text = Returns a vector conatining &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt; ghost objects for navaids that match a given frequency, sorted from nearest to furthest.&lt;br /&gt;
|param1 = pos&lt;br /&gt;
|param1text = Optional position to search around. If not given, the aircraft's current position will be used. Can be one of:&lt;br /&gt;
:* An &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;runway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;taxiway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fix&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;waypoint&amp;lt;/code&amp;gt; ghost type&lt;br /&gt;
:* A hash with ''lat'' and ''lon'' members&lt;br /&gt;
:* A geo.Coord object&lt;br /&gt;
:* Two numbers separated by a comma, as if the function is taking three arguments. Example: &amp;lt;code&amp;gt;findNavaidsByFrequency(lat, lon, freq, type);&amp;lt;/code&amp;gt;.&lt;br /&gt;
|param2 = freq&lt;br /&gt;
|param2text = Frequency, in megahertz, of the navaid to search for.&lt;br /&gt;
|param3 = type&lt;br /&gt;
|param3text = This will narrow the search to navaids of a certain type. Defaults to &amp;quot;all.&amp;quot; For the full list of accepted type arguments, see {{flightgear file|src/Navaids/positioned.cxx|l=127}}.&lt;br /&gt;
|example1 = var navaids = findNavaidsByFrequency(10.955);&lt;br /&gt;
foreach(var navaid; navaids){&lt;br /&gt;
    print(&amp;quot;--&amp;quot;);&lt;br /&gt;
    print(&amp;quot;ID: &amp;quot;, navaid.id); # prints info about the navaid&lt;br /&gt;
    print(&amp;quot;Name: &amp;quot;, navaid.name);&lt;br /&gt;
    print(&amp;quot;Latitude: &amp;quot;, navaid.lat);&lt;br /&gt;
    print(&amp;quot;Longitude: &amp;quot;, navaid.lon);&lt;br /&gt;
    print(&amp;quot;Elevation (AMSL): &amp;quot;, navaid.elevation, &amp;quot; m&amp;quot;);&lt;br /&gt;
    print(&amp;quot;Type: &amp;quot;, navaid.type);&lt;br /&gt;
    print(&amp;quot;Frequency: &amp;quot;, sprintf(&amp;quot;%.3f&amp;quot;, navaid.frequency / 1000), &amp;quot; Mhz&amp;quot;);&lt;br /&gt;
    print(&amp;quot;Range: &amp;quot;, navaid.range_nm, &amp;quot; nm&amp;quot;);&lt;br /&gt;
    if(navaid.course) print(&amp;quot;Course: &amp;quot;, navaid.course);&lt;br /&gt;
    print(&amp;quot;--&amp;quot;);&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===findNavaidsByID()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = findNavaidsByID([pos, ]id[, type]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1600|t=Source}}&lt;br /&gt;
|text = Returns a vector containing &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt; ghost objects matching a given ID, sorted by range from a certain position.&lt;br /&gt;
|param1 = pos&lt;br /&gt;
|param1text = Optional position to search around. If not given, the aircraft's current position will be used. Can be one of:&lt;br /&gt;
:* An &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;runway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;taxiway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fix&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;waypoint&amp;lt;/code&amp;gt; ghost type&lt;br /&gt;
:* A hash with ''lat'' and ''lon'' members&lt;br /&gt;
:* A geo.Coord object&lt;br /&gt;
:* Two numbers separated by a comma, as if the function is taking three arguments. Example: &amp;lt;code&amp;gt;findNavaidsByID(lat, lon, id, type);&amp;lt;/code&amp;gt;.&lt;br /&gt;
|param2 = id&lt;br /&gt;
|param2text = Full or partial ID of the fix to search for.&lt;br /&gt;
:{{inote|1=Inputting a partial ID does not work correctly (see [http://forum.flightgear.org/viewtopic.php?f=30&amp;amp;t=28129 here]). It is best to just input a full ID.}}&lt;br /&gt;
|param3 = type&lt;br /&gt;
|param3text = This will narrow the search to navaids of a certain type. Defaults to &amp;quot;all.&amp;quot; For the full list of accepted type arguments, see {{flightgear file|src/Navaids/positioned.cxx|l=127}}.&lt;br /&gt;
|example1 = var navaid = findNavaidsByID(&amp;quot;MXW&amp;quot;);&lt;br /&gt;
navaid = navaid[0];&lt;br /&gt;
print(&amp;quot;ID: &amp;quot;, navaid.id); # prints info about 'MXW' (a VOR station)&lt;br /&gt;
print(&amp;quot;Name: &amp;quot;, navaid.name);&lt;br /&gt;
print(&amp;quot;Latitude: &amp;quot;, navaid.lat);&lt;br /&gt;
print(&amp;quot;Longitude: &amp;quot;, navaid.lon);&lt;br /&gt;
print(&amp;quot;Elevation (AMSL): &amp;quot;, navaid.elevation, &amp;quot; m&amp;quot;);&lt;br /&gt;
print(&amp;quot;Type: &amp;quot;, navaid.type);&lt;br /&gt;
print(&amp;quot;Frequency: &amp;quot;, sprintf(&amp;quot;%.3f&amp;quot;, navaid.frequency / 1000), &amp;quot; Mhz&amp;quot;);&lt;br /&gt;
print(&amp;quot;Range: &amp;quot;, navaid.range_nm, &amp;quot; nm&amp;quot;);&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===findNavaidsWithinRange()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = findNavaidsWithinRange([pos, ]range[, type]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1518|t=Source}}&lt;br /&gt;
|text = Returns a vector of &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt; ghost objects which are within a given range of a given position (by default the aircraft's current position). The results are sorted from closest to furthest.&lt;br /&gt;
|param1 = pos&lt;br /&gt;
|param1text = Optional position to search around. If not given, the aircraft's current position will be used. Can be one of:&lt;br /&gt;
:* An &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;runway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;taxiway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fix&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;waypoint&amp;lt;/code&amp;gt; ghost type&lt;br /&gt;
:* A hash with ''lat'' and ''lon'' members&lt;br /&gt;
:* A geo.Coord object&lt;br /&gt;
:* Two numbers separated by a comma, as if the function is taking three arguments. Example: &amp;lt;code&amp;gt;findNavaidsWithinRange(lat, lon, range, type);&amp;lt;/code&amp;gt;.&lt;br /&gt;
|param2 = range&lt;br /&gt;
|param2text = Mandatory number giving the range in nautical miles within which to search for navaids. Defaults to &amp;quot;all.&amp;quot; For the full list of accepted type arguments, see {{flightgear file|src/Navaids/positioned.cxx|l=127}}.&lt;br /&gt;
|param3 = type&lt;br /&gt;
|param3text = This will narrow the search to navaids of a certain type.&lt;br /&gt;
|example1text = Searches for navaids within 10 nm of [[KSFO]].&lt;br /&gt;
|example1 = var pos = airportinfo(&amp;quot;KSFO&amp;quot;);&lt;br /&gt;
var navs = findNavaidsWithinRange(pos, 10);&lt;br /&gt;
foreach(var nav; navs){&lt;br /&gt;
    print(nav.name, &amp;quot; (ID: &amp;quot;, nav.id, &amp;quot;)&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
|example2text = Searches for navaids within 10 nm of your current position.&lt;br /&gt;
|example2 = var navs = findNavaidsWithinRange(10);&lt;br /&gt;
foreach(var nav; navs){&lt;br /&gt;
    print(nav.name, &amp;quot; (ID: &amp;quot;, nav.id, &amp;quot;)&amp;quot;);&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===finddata()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = finddata(path);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=603|t=Source}}&lt;br /&gt;
|text = Takes a relative path and tries to return an absolute one. It works by appending the relative path to some paths and testing to see if they exist. As of FlightGear v3.7, these paths are the TerraSync directory (tested first) and [[$FG_ROOT]]. &lt;br /&gt;
|param1 = path&lt;br /&gt;
|param1text = A relative path as a string.&lt;br /&gt;
|example1 = var path = finddata(&amp;quot;Aircraft/Generic&amp;quot;);&lt;br /&gt;
print(path); # prints the absolute path to $FG_ROOT/Aircraft/Generic&lt;br /&gt;
|example2 = var path = finddata(&amp;quot;Airports&amp;quot;);&lt;br /&gt;
print(path); # prints the absolute path to &amp;lt;TerraSync dir&amp;gt;/Airports&lt;br /&gt;
|example3 = var path = finddata(&amp;quot;preferences.xml&amp;quot;);&lt;br /&gt;
print(path); # prints the absolute path to $FG_ROOT/preferences.xml&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===flightplan()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = flightplan([path]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1738|t=Source}}&lt;br /&gt;
|text = {{see also|Nasal Flightplan}}&lt;br /&gt;
Returns a flight plan object, either one for the current flight plan, or one loaded from a given path.&lt;br /&gt;
|param1 = path&lt;br /&gt;
|param1text = Optional path to flight plan XML file.&lt;br /&gt;
|example1text = Gets the active flight plan and gets the ID of the current waypoint. Note that this example requires a flight plan to be set in the [[Route Manager]] first.&lt;br /&gt;
|example1 = var fp = flightplan();&lt;br /&gt;
print(fp.getWP(fp.current).id);&lt;br /&gt;
|example2text = Creates a new flight plan from an XML file and prints the IDs of the waypoints. Note that this example requires a flight plan to have been created and saved as &amp;lt;tt&amp;gt;''[[$FG_HOME]]/fp-demo.xml''&amp;lt;/tt&amp;gt;.&lt;br /&gt;
|example2 = var path = getprop('/sim/fg-home') ~ '/fp-demo.xml';&lt;br /&gt;
var fp = flightplan(path);&lt;br /&gt;
for(var i = 0; i &amp;lt; fp.getPlanSize(); i += 1){&lt;br /&gt;
    print(fp.getWP(i).id);&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===geodinfo()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = geodinfo(lat, lon[, max_alt]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=981|t=Source}}&lt;br /&gt;
|text = Returns a vector containing two entries or &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; if no information could be obtained because the terrain tile wasn't loaded. The first entry in the vector is the elevation (in meters) for the given point, and the second is a hash with information about the assigned material (as defined in &amp;lt;tt&amp;gt;''[[$FG_ROOT]]/Materials''&amp;lt;/tt&amp;gt;), or &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; if there was no material information available (for example, because there is an untextured building at that location). The structure of the hash is as follows (see also {{readme file|materials}}):&lt;br /&gt;
* '''light_coverage:''' The coverage of a single point of light in m&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;.&lt;br /&gt;
* '''bumpiness:''' Normalized bumpiness factor for the material.&lt;br /&gt;
* '''load_resistance:''' The amount of pressure in N/m&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt; the material can withstand without deformation.&lt;br /&gt;
* '''solid:''' 1 (true) or false (0) depending on whether the material is solid or not.&lt;br /&gt;
* '''names:''' Vector of scenery types (usually generated by [[TerraGear]]) that will use this material. &lt;br /&gt;
* '''friction_factor:''' Normalized friction factor of the material.&lt;br /&gt;
* '''rolling_friction:''' The rolling friction coefficient of the material.&lt;br /&gt;
&lt;br /&gt;
Note that this function is a ''very'' CPU-intensive operation, particularly in FlightGear v2.4 and earlier. It is advised to use this function as little as possible.&lt;br /&gt;
|param1 = lat&lt;br /&gt;
|param1text = Latitude, inputted as a number.&lt;br /&gt;
|param2 = lon&lt;br /&gt;
|param2text = Longitude, inputted as a number.&lt;br /&gt;
|param3 = max_alt&lt;br /&gt;
|param3text = The altitude, in metres, from which the function will begin searching for the height of the terrain. Defaults to 10,000 metres. If the terrain is higher than this argument specifies, &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; will be returned.&lt;br /&gt;
|example1text = Dumps information about ground underneath the aircraft.&lt;br /&gt;
|example1 = var pos = geo.aircraft_position();&lt;br /&gt;
var info = geodinfo(pos.lat(), pos.lon());&lt;br /&gt;
debug.dump(info);&lt;br /&gt;
|example2text = Prints whether the ground underneath the aircraft is solid or is water.&lt;br /&gt;
|example2 = var pos = geo.aircraft_position();&lt;br /&gt;
var info = geodinfo(pos.lat(), pos.lon());&lt;br /&gt;
if (info != nil and info[1] != nil) {&lt;br /&gt;
    print(&amp;quot;The ground underneath the aircraft is &amp;quot;, info[1].solid == 1 ? &amp;quot;solid.&amp;quot; : &amp;quot;water.&amp;quot;);&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== geodtocart() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = geodtocart(lat, lon, alt);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=962|t=Source}}&lt;br /&gt;
|text = Converts {{wikipedia|geodetic coordinates}} (latitude, longitude, and altitude) to {{wikipedia|ECEF|Earth-centered, Earth-fixed}} coordinates (x, y and z). A vector is returned containing x, y, and z in metres. The equatorial radius of earth used is that defined by the {{wikipedia|WGS 84}} (6,378,137 metres). All argument are mandatory.&lt;br /&gt;
|param1 = lat&lt;br /&gt;
|param1text = Latitude, in degrees.&lt;br /&gt;
|param2 = lon&lt;br /&gt;
|param2text = Longitude, in degrees.&lt;br /&gt;
|param3 = alt&lt;br /&gt;
|param3text = Altitude, in metres.&lt;br /&gt;
|example1 = var (x, y, z) = geodtocart(0, 0, 0); # point is the intersection of the prime meridian and equator.&lt;br /&gt;
print(&amp;quot;x: &amp;quot;, x); # prints &amp;quot;x: 6378137&amp;quot;&lt;br /&gt;
print(&amp;quot;y: &amp;quot;, y); # prints &amp;quot;y: 0&amp;quot;&lt;br /&gt;
print(&amp;quot;z: &amp;quot;, z); # prints &amp;quot;y: 0&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== get_cart_ground_intersection()===&lt;br /&gt;
Introduced in 2017.2.1, see [[Terrain Detection]].&lt;br /&gt;
&lt;br /&gt;
===getprop()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = &amp;lt;nowiki&amp;gt;getprop(path[, path[, ...]]);&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=345|t=Source}}&lt;br /&gt;
|text = Returns the value of a node in the [[Property Tree]] or &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; if the node does not exist or the value is not a number (NaN).&lt;br /&gt;
|param1 = path&lt;br /&gt;
|param1text = There needs to be at least one argument, but there is no limit to the maximum amount of arguments that can be given. The arguments will be concatenated together to form a property tree path. The arguments must be strings, but in FlightGear v3.2 onwards, there is also support (added by {{flightgear commit|34ed79}}) for numeric arguments as indices. See example 2 below.&lt;br /&gt;
|example1 = print(&amp;quot;You have FlightGear v&amp;quot;, getprop(&amp;quot;/sim/version/flightgear&amp;quot;)); # prints FlightGear version&lt;br /&gt;
|example2text = Note that the example below will only work in FlightGear 3.2 and above.&lt;br /&gt;
|example2 = for(var i = 0; i &amp;lt; 8; i += 1){&lt;br /&gt;
    print(&amp;quot;View #&amp;quot;, i + 1, &amp;quot; is named &amp;quot;, getprop(&amp;quot;/sim/view&amp;quot;, i, &amp;quot;name&amp;quot;));&lt;br /&gt;
}&lt;br /&gt;
|example3text = Same as above, but is supported by all versions of FlightGear.&lt;br /&gt;
|example3 = for(var i = 0; i &amp;lt; 8; i += 1){&lt;br /&gt;
    print(&amp;quot;View #&amp;quot;, i + 1, &amp;quot; is named &amp;quot;, getprop(&amp;quot;/sim/view[&amp;quot; ~ i ~ &amp;quot;]/name&amp;quot;));&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
==== See also====&lt;br /&gt;
{{note| If you have to read/write the same property multiple times (e.g. in an update loop), it is more efficient to use a node object: &lt;br /&gt;
To get a Node rather than its value, use &amp;lt;code&amp;gt;props.globals.getNode()&amp;lt;/code&amp;gt; - see [[Nasal_library/props]]. }}&lt;br /&gt;
&lt;br /&gt;
===greatCircleMove()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = greatCircleMove([pos, ]course, dist);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1681|t=Source}}&lt;br /&gt;
|text = Calculates a new set of geodetic coordinates using inputs of course and distance, either from the aircraft's current position (by default) or from another set of coordinates. Returns a hash containing two members, ''lat'' and ''lon'' (latitude and longitude respectively).&lt;br /&gt;
|param1 = pos&lt;br /&gt;
|param1text = Optional position to calculate from. If not given, the aircraft's current position will be used. Can be one of:&lt;br /&gt;
:* An &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;runway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;taxiway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fix&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;waypoint&amp;lt;/code&amp;gt; ghost object.&lt;br /&gt;
:* A hash with ''lat'' and ''lon'' members&lt;br /&gt;
:* A &amp;lt;code&amp;gt;geo.Coord&amp;lt;/code&amp;gt; object&lt;br /&gt;
:* A lat/lon pair, that is, a pair of numbers (latitude followed by longitude) separated by a comma: &amp;lt;code&amp;gt;greatCircleMove(lat,lon, ...)&amp;lt;/code&amp;gt;.&lt;br /&gt;
|param2 = course&lt;br /&gt;
|param2text = Course to new set of coordinates, in degrees (in the range 0–360).&lt;br /&gt;
|param3 = dist&lt;br /&gt;
|param3text = Distance in nautical miles to the new set of coordinates.&lt;br /&gt;
|example1 = var pos = greatCircleMove(0,0, 0, 1);&lt;br /&gt;
debug.dump(pos); # print hash with coordinates&lt;br /&gt;
|example2 = var fix = findFixesByID(&amp;quot;POGIC&amp;quot;);&lt;br /&gt;
fix = fix[0];&lt;br /&gt;
var pos = greatCircleMove(fix, 45, 10);&lt;br /&gt;
debug.dump(pos); # print hash with coordinates&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===interpolate()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|private = _interpolate()&lt;br /&gt;
|syntax = &amp;lt;nowiki&amp;gt;interpolate(prop, value1, time1[, value2, time2[, ...]]);&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=522|t=Part 1}} {{!}} {{fgdata file|Nasal/globals.nas|t=Part 2}}&lt;br /&gt;
|text = Linearly interpolates a node in the property tree to a given value in a specified time. The value/time pairs will be run one after the other in the order that they are passed to the function. Note that the interpolation will continue even when the simulation is paused.&lt;br /&gt;
|param1 = prop&lt;br /&gt;
|param1text = String or &amp;lt;code&amp;gt;props.Node&amp;lt;/code&amp;gt; object that indicates a node in the property tree to be used.&lt;br /&gt;
|param2 = value''n''&lt;br /&gt;
|param2text = Target value to change the property to in the set amount of time. This should be a number.&lt;br /&gt;
|param3 = time''n''&lt;br /&gt;
|param3text = Time in seconds, that will be taken for the interpolation.&lt;br /&gt;
|example1text = Paste the code below into the Nasal Console and execute. Then, open the Property Browser and look for the property. Finally, run the code again, and watch the value of the property change.&lt;br /&gt;
|example1 = setprop(&amp;quot;/test&amp;quot;, 0); # (re-)set property&lt;br /&gt;
interpolate(&amp;quot;/test&amp;quot;,&lt;br /&gt;
    50, 5, # interpolate to 50 in 5 seconds&lt;br /&gt;
    10, 2, # interpolate to 10 in 2 seconds&lt;br /&gt;
    0, 5); # interpolate to 0 in 5 seconds&lt;br /&gt;
|example2 = # Apply the left brake at 20% per second&lt;br /&gt;
var prop = &amp;quot;controls/gear/brake-left&amp;quot;;&lt;br /&gt;
var dist = 1 - getprop(prop);&lt;br /&gt;
if (dist == 1) {&lt;br /&gt;
    interpolate(prop, 1, dist / 0.2);&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===isa()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = isa(object, class);&lt;br /&gt;
|source = {{fgdata file|Nasal/globals.nas|t=Source}}&lt;br /&gt;
|text = Checks if an object is an instance of, or inherits from, a second object (or class), returning 1 (true) if it is and 0 (false) if otherwise.&lt;br /&gt;
|param1 = object&lt;br /&gt;
|param1text = Object to check.&lt;br /&gt;
|param2 = class&lt;br /&gt;
|param2text = Class/object to check that '''object''' inherits from or is an instance of.&lt;br /&gt;
|example1 = var coord = geo.Coord.new();&lt;br /&gt;
if(isa(coord, geo.Coord)){&lt;br /&gt;
    print(&amp;quot;Variable 'coord' is an instance of class 'geo.Coord'&amp;quot;); # this one will be printed&lt;br /&gt;
} else {&lt;br /&gt;
    print(&amp;quot;Variable 'coord' is not an instance of class 'geo.Coord'&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
|example2 = var coord = geo.Coord.new();&lt;br /&gt;
if(isa(coord, props.Node)){&lt;br /&gt;
    print(&amp;quot;Variable 'coord' is an instance of class 'props.Node'&amp;quot;);&lt;br /&gt;
} else {&lt;br /&gt;
    print(&amp;quot;Variable 'coord' is not an instance of class 'props.Node'&amp;quot;); # this one will be printed&lt;br /&gt;
}&lt;br /&gt;
|example3text = The example below demonstrates checking of inheritance.&lt;br /&gt;
|example3 = var Const = {&lt;br /&gt;
    constant: 2,&lt;br /&gt;
    getConst: func {&lt;br /&gt;
        return me.constant;&lt;br /&gt;
    }&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
var Add = {&lt;br /&gt;
    new: func {&lt;br /&gt;
        return { parents: [Add, Const] };&lt;br /&gt;
    },&lt;br /&gt;
&lt;br /&gt;
    addToConst: func(a){&lt;br /&gt;
        return a * me.getConst();&lt;br /&gt;
    }&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
var m = Add.new();&lt;br /&gt;
print(m.addToConst(4));&lt;br /&gt;
&lt;br /&gt;
if(isa(m, Add)) print(&amp;quot;Variable 'm' is an instance of class 'Add'&amp;quot;); # will be printed&lt;br /&gt;
if(isa(m, Const)) print(&amp;quot;Variable 'm' is an instance of class 'Const'&amp;quot;); # will also be printed&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===logprint()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = &amp;lt;nowiki&amp;gt;logprint(priority[, msg[, msg[, ...]]]);&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=431|t=Source}}&lt;br /&gt;
|text = Concatenates a message and logs it with a given priority level. Unlike {{func link|print()}} and {{func link|printlog()}}, message outputted by this function will be logged in your &amp;lt;code&amp;gt;[[Commonly used debugging tools#fgfs.log|fgfs.log]]&amp;lt;/code&amp;gt; file as coming from the Nasal file itself rather than from {{flightgear file|src/Scripting/NasalSys.cxx}}.&lt;br /&gt;
|param1 = priority&lt;br /&gt;
|param1text = Number specifying the priority level of the outputted message:&lt;br /&gt;
:{{{!}} class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Number !! Debug type&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} 1 {{!!}} Bulk&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} 2 {{!!}} Debug&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} 3 {{!!}} Info&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} 4 {{!!}} Warn&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} 5 {{!!}} Alert&lt;br /&gt;
{{!}}}&lt;br /&gt;
|param2 = msg&lt;br /&gt;
|param2text = The message. There is no limit to the arguments you give give. They will be concatenated together before logging.&lt;br /&gt;
|example1 = # logs the value of pi to three decimal places with log level 3&lt;br /&gt;
logprint(3, &amp;quot;pi = &amp;quot;, sprintf(&amp;quot;%.3f&amp;quot;, math.pi));&lt;br /&gt;
|example2 = logprint(5, &amp;quot;Alert! This is an important message!&amp;quot;);&lt;br /&gt;
}}&lt;br /&gt;
{{note| &lt;br /&gt;
The following constants have been added to the development branch of FlightGear (&amp;quot;next&amp;quot;) and will be releases with FG 2020.1 so you won't have to remember the numbers anymore:&lt;br /&gt;
&lt;br /&gt;
LOG_BULK, LOG_WARN, LOG_DEBUG, LOG_INFO, LOG_ALERT, DEV_WARN, DEV_ALERT }}&lt;br /&gt;
&lt;br /&gt;
===magvar() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = magvar([pos]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1642|t=Source}}&lt;br /&gt;
|text = Returns the {{wikipedia|magnetic variation}} at a given set of coordinates. The table below gives the magnetic model used depending on the version of FlightGear.&lt;br /&gt;
{{{!}} class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! FlightGear versions !! Model !! Reference date&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} 3.6 and above {{!!}} {{wikipedia|World Magnetic Model}} (WMM) 2015 {{!!}} 1 January 2015&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} 0.9.11-pre1 to 3.4 {{!!}} WMM 2005 {{!!}} 1 January 2005&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} 0.7.3 to 0.9.10 {{!!}} WMM 2000 {{!!}} 1 January 2000&lt;br /&gt;
{{!}}}&lt;br /&gt;
|param1 = pos&lt;br /&gt;
|param1text = Optional position to calculate from. If not given, the aircraft's current position will be used. Can be one of:&lt;br /&gt;
:* An &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;runway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;taxiway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fix&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;waypoint&amp;lt;/code&amp;gt; ghost object.&lt;br /&gt;
:* A hash with ''lat'' and ''lon'' members&lt;br /&gt;
:* A &amp;lt;code&amp;gt;geo.Coord&amp;lt;/code&amp;gt; object&lt;br /&gt;
:* A lat/lon pair, that is, a pair of numbers (latitude followed by longitude) separated by a comma: &amp;lt;code&amp;gt;magvar(lat,lon)&amp;lt;/code&amp;gt;.&lt;br /&gt;
|example1 = print(magvar(0, 0)); # prints the magnetic variation at 0, 0&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===maketimer() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = maketimer(interval[, self], function);&lt;br /&gt;
|source = ''Implemented using the {{API Link|flightgear|class|TimerObj}} class.''&amp;lt;br&amp;gt;{{flightgear file|src/Scripting/NasalSys.cxx|l=90|t=Part 1}} {{!}} {{flightgear file|src/Scripting/NasalSys.cxx|l=533|t=Part 2}}&lt;br /&gt;
|version = 2.12&lt;br /&gt;
|commit = {{flightgear commit|ab939f|t=commit}}&lt;br /&gt;
|text = Returns a timer object containing the following methods and members:&lt;br /&gt;
* '''start()''': Starts the timer.&lt;br /&gt;
* '''stop()''': Stops the timer.&lt;br /&gt;
* '''restart(interval)''': Restarts the timer with the given interval.&lt;br /&gt;
* '''singleShot''': Bool showing whether the timer is only to be run once, or continuously until told to stop. Can be both set and read from (see examples).&lt;br /&gt;
* '''isRunning''': Read-only bool telling whether the timer is currently running.&lt;br /&gt;
* '''simulatedTime''': (FG 2017.1+; {{flightgear commit|0af316|t=commit}}) Bool telling whether the timer is using simulated time (which accounts for pause, etc.). Defaults to false (use real time). Can be both read and set. This cannot be changed while the timer is running.&lt;br /&gt;
Unlike {{func link|settimer()}}, which it replaces, &amp;lt;code&amp;gt;maketimer()&amp;lt;/code&amp;gt; provides more control over the timer. In addition, it can help reduce memory usage.&lt;br /&gt;
|param1 = interval&lt;br /&gt;
|param1text = Interval in seconds for the timer.&lt;br /&gt;
|param2 = self&lt;br /&gt;
|param2text = Optional parameter specifying what any &amp;lt;code&amp;gt;'''me'''&amp;lt;/code&amp;gt; references in the function being called will refer to.&lt;br /&gt;
|param3 = function&lt;br /&gt;
|param3text = Function to be called at the given interval.&lt;br /&gt;
|example1 = var timer = maketimer(1, func(){&lt;br /&gt;
    print(&amp;quot;Hello, World!&amp;quot;); # print &amp;quot;Hello, World!&amp;quot; once every second (call timer.stop() to stop it)&lt;br /&gt;
});&lt;br /&gt;
timer.start();&lt;br /&gt;
|example2 = var timer = maketimer(1, math, func(){&lt;br /&gt;
    print(me.math); # 'me' reference is the 'math' namespace&lt;br /&gt;
});&lt;br /&gt;
timer.singleShot = 1; # timer will only be run once&lt;br /&gt;
timer.start();&lt;br /&gt;
|example3 = var timer = maketimer(1, func(){&lt;br /&gt;
    print(&amp;quot;Hello, World!&amp;quot;); # print &amp;quot;Hello, World!&amp;quot; once every second (call timer.stop() to stop it)&lt;br /&gt;
});&lt;br /&gt;
timer.start();&lt;br /&gt;
print(timer.isRunning); # prints 1&lt;br /&gt;
|example4text = In the example below, &amp;quot;Hello, World!&amp;quot; will be printed after one second the first time, and after two seconds thereafter.&lt;br /&gt;
|example4 = var update = func(){&lt;br /&gt;
    print(&amp;quot;Hello, World!&amp;quot;);&lt;br /&gt;
    timer.restart(2); # restarts the timer with a two second interval&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var timer = maketimer(1, update);&lt;br /&gt;
timer.singleShot = 1;&lt;br /&gt;
timer.start();&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===maketimestamp()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = maketimestamp()&lt;br /&gt;
|source = ''Implemented using the {{API Link|flightgear|class|TimeStampObj}} class.''&amp;lt;br&amp;gt;{{flightgear file|src/Scripting/NasalSys.cxx|l=214|t=Part 1}} {{!}} {{flightgear file|src/Scripting/NasalSys.cxx|l=589|t=Part 2}}&lt;br /&gt;
|version = 2019.2&lt;br /&gt;
|commit = {{flightgear commit|7db06300|t=commit}}&lt;br /&gt;
|text = Returns a time stamp object to allow high resolution timing of Nasal operations. When created the timer will automatically be stamped. The object has the following methods:&lt;br /&gt;
* '''stamp()''': Resets the timing operation. Call this first.&lt;br /&gt;
* '''elapsedMSec()''': returns number of milliseconds elapsed since stamp() called. Resolution may vary depending on platform but is usually at least millisecond accuracy.&lt;br /&gt;
* '''elapsedUSec()''': returns number of microseconds elapsed since stamp() called. Resolution may vary depending on platform but is usually at least millisecond accuracy.&lt;br /&gt;
|&lt;br /&gt;
|example1text = In the example below the number of milliseconds elapsed will be printed.&lt;br /&gt;
|example1 = var timestamp = maketimestamp();&lt;br /&gt;
timestamp.stamp();&lt;br /&gt;
print(timestamp.elapsedMSec(), &amp;quot;ms elapsed&amp;quot;);&lt;br /&gt;
print(timestamp.elapsedMSec(), &amp;quot;ms elapsed&amp;quot;);&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===md5()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = md5(string);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=758|t=Source}}&lt;br /&gt;
|version = 3.2&lt;br /&gt;
|commit = {{flightgear commit|cfbf9e|t=commit}}&lt;br /&gt;
|text = Returns a the {{wikipedia|MD5}} hash (as a string) of the inputted string.&lt;br /&gt;
|param1 = string&lt;br /&gt;
|param1text = String the generate the hash of. Mandatory.&lt;br /&gt;
|example1text = The below code should output &amp;lt;code&amp;gt;65a8e27d8879283831b664bd8b7f0ad4&amp;lt;/code&amp;gt;.&lt;br /&gt;
|example1 = print(md5(&amp;quot;Hello, World!&amp;quot;));&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===navinfo()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = navinfo(lat, lon, type, id);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1453|t=Source}}&lt;br /&gt;
|text = Returns vector &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt; ghost objects matching the given '''type''' and '''id''' or &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; on error.&lt;br /&gt;
|param1 = lat ''and'' lon&lt;br /&gt;
|param1text = If given, the returned navaids will be put into order of ascending distance from the location.&lt;br /&gt;
|param2 = type&lt;br /&gt;
|param2text = Narrows the search to the given type. Must be one of &amp;quot;any,&amp;quot; &amp;quot;fix,&amp;quot; &amp;quot;vor,&amp;quot; &amp;quot;ndb,&amp;quot; &amp;quot;ils,&amp;quot; &amp;quot;dme,&amp;quot; or &amp;quot;tacan.&amp;quot; Defaults to the equivalent of &amp;quot;any.&amp;quot;&lt;br /&gt;
|param3 = id&lt;br /&gt;
|param3text = ID to search for. Note that, although all the parameters are technically optional, this parameter must be given, otherwise an empty vector will be returned.&lt;br /&gt;
|example1 = navinfo(&amp;quot;vor&amp;quot;); # returns all VORs&lt;br /&gt;
|example2 = navinfo(&amp;quot;HAM&amp;quot;); # return all navaids whose names start with &amp;quot;HAM&amp;quot;&lt;br /&gt;
|example3 = navinfo(&amp;quot;vor&amp;quot;, &amp;quot;HAM&amp;quot;); # return all VORs whose names start with &amp;quot;HAM&amp;quot;&lt;br /&gt;
|example4 = navinfo(34,48,&amp;quot;vor&amp;quot;,&amp;quot;HAM&amp;quot;); # return all VORs whose names start with &amp;quot;HAM&amp;quot; and sorted by distance relative to 34°, 48°&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===parse_markdown()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = parse_markdown(markdown);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=745|t=Part 1}} {{!}} {{simgear file|simgear/misc/SimpleMarkdown.cxx|t=Part 2}} &lt;br /&gt;
|version = 3.2&lt;br /&gt;
|text = Parses a string containing {{wikipedia|Markdown}} and returns the result as a string. As of FlightGear 2016.1, it is just a simple parser, and does the following:&lt;br /&gt;
* It strips whitespace from the beginning of the string.&lt;br /&gt;
* It supports [http://daringfireball.net/projects/markdown/syntax#p paragraphs and line breaks].&lt;br /&gt;
* It collapses whitespace.&lt;br /&gt;
* It converts unordered [http://daringfireball.net/projects/markdown/syntax#list lists] to use a bullet character (&amp;amp;bull;). Note that the bullet character is implemented in hexadecimal UTF-8 character bytes (&amp;lt;code&amp;gt;E2 80 A2&amp;lt;/code&amp;gt;), as so may not work properly when the being displayed in an encoding other than UTF-8.&lt;br /&gt;
|param1 = markdown&lt;br /&gt;
|param1text = String containing Markdown to be parsed.&lt;br /&gt;
|example1text = &lt;br /&gt;
Save the below code as &amp;lt;tt&amp;gt;''[[$FG_ROOT]]/gui/dialogs/test.xml''&amp;lt;/tt&amp;gt;, then run the Nasal code below it to open the dialog. To change the markdown to be parsed, simply change the code in the highlighted section, save it, and reload the GUI (&amp;lt;tt&amp;gt;Debug &amp;gt; Reload GUI&amp;lt;/tt&amp;gt;).&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot; highlight=&amp;quot;41-50&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;PropertyList&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;name&amp;gt;test&amp;lt;/name&amp;gt;&lt;br /&gt;
&amp;lt;layout&amp;gt;vbox&amp;lt;/layout&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;group&amp;gt;&lt;br /&gt;
    &amp;lt;layout&amp;gt;hbox&amp;lt;/layout&amp;gt;&lt;br /&gt;
    &lt;br /&gt;
    &amp;lt;empty&amp;gt;&lt;br /&gt;
        &amp;lt;stretch&amp;gt;true&amp;lt;/stretch&amp;gt;&lt;br /&gt;
    &amp;lt;/empty&amp;gt;&lt;br /&gt;
    &lt;br /&gt;
    &amp;lt;text&amp;gt;&lt;br /&gt;
        &amp;lt;label&amp;gt;parse_markdown() test dialog&amp;lt;/label&amp;gt;&lt;br /&gt;
    &amp;lt;/text&amp;gt;&lt;br /&gt;
    &lt;br /&gt;
    &amp;lt;empty&amp;gt;&lt;br /&gt;
        &amp;lt;stretch&amp;gt;true&amp;lt;/stretch&amp;gt;&lt;br /&gt;
    &amp;lt;/empty&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    &amp;lt;button&amp;gt;&lt;br /&gt;
        &amp;lt;legend&amp;gt;&amp;lt;/legend&amp;gt;&lt;br /&gt;
        &amp;lt;pref-width&amp;gt;16&amp;lt;/pref-width&amp;gt;&lt;br /&gt;
        &amp;lt;pref-height&amp;gt;16&amp;lt;/pref-height&amp;gt;&lt;br /&gt;
        &amp;lt;binding&amp;gt;&lt;br /&gt;
            &amp;lt;command&amp;gt;dialog-close&amp;lt;/command&amp;gt;&lt;br /&gt;
        &amp;lt;/binding&amp;gt;&lt;br /&gt;
    &amp;lt;/button&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/group&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;canvas&amp;gt;&lt;br /&gt;
    &amp;lt;name&amp;gt;Canvas plot&amp;lt;/name&amp;gt;&lt;br /&gt;
    &amp;lt;stretch&amp;gt;true&amp;lt;/stretch&amp;gt;&lt;br /&gt;
    &amp;lt;pref-width&amp;gt;400&amp;lt;/pref-width&amp;gt;&lt;br /&gt;
    &amp;lt;pref-height&amp;gt;300&amp;lt;/pref-height&amp;gt;&lt;br /&gt;
    &amp;lt;nasal&amp;gt;&lt;br /&gt;
        &amp;lt;load&amp;gt;&amp;lt;![CDATA[&lt;br /&gt;
var text = 'Items:&lt;br /&gt;
* apples&lt;br /&gt;
* oranges&lt;br /&gt;
* pears&lt;br /&gt;
&lt;br /&gt;
Some text.&lt;br /&gt;
Some more items:&lt;br /&gt;
* apples&lt;br /&gt;
* oranges&lt;br /&gt;
* pears';&lt;br /&gt;
&lt;br /&gt;
var parsed = parse_markdown(text);&lt;br /&gt;
&lt;br /&gt;
var root_canvas = canvas.get(cmdarg());&lt;br /&gt;
root_canvas.setColorBackground(255, 255, 255);&lt;br /&gt;
var root = root_canvas.createGroup();&lt;br /&gt;
&lt;br /&gt;
var text_dis = root.createChild(&amp;quot;text&amp;quot;)&lt;br /&gt;
    .setText(parsed)&lt;br /&gt;
    .setTranslation(5, 5)&lt;br /&gt;
    .setFont(&amp;quot;LiberationFonts\LiberationSans-Regular.ttf&amp;quot;)&lt;br /&gt;
    .setFontSize(15)&lt;br /&gt;
    .setColor(0, 0, 0)&lt;br /&gt;
    .setDrawMode(canvas.Text.TEXT)&lt;br /&gt;
    .setAlignment(&amp;quot;left-top&amp;quot;);&lt;br /&gt;
        ]]&amp;gt;&amp;lt;/load&amp;gt;&lt;br /&gt;
    &amp;lt;/nasal&amp;gt;&lt;br /&gt;
&amp;lt;/canvas&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/PropertyList&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|example1 = fgcommand(&amp;quot;dialog-show&amp;quot;, {&amp;quot;dialog-name&amp;quot;: &amp;quot;test&amp;quot;});&lt;br /&gt;
|example2text = The example below parses Markdown and outputs it in a HTML document. The parsed text is placed in &amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot; inline&amp;gt;&amp;lt;pre&amp;gt;&amp;lt;/pre&amp;gt;&amp;lt;/syntaxhighlight&amp;gt; tags. To change the Markdown to be parsed, simply edit the variable &amp;lt;tt&amp;gt;markdown&amp;lt;/tt&amp;gt; at the top of the code.&lt;br /&gt;
|example2 = &amp;lt;nowiki&amp;gt;var markdown = 'Items:&lt;br /&gt;
* apples&lt;br /&gt;
* oranges&lt;br /&gt;
* pears&lt;br /&gt;
&lt;br /&gt;
Some text.&lt;br /&gt;
Some more items:&lt;br /&gt;
* apples&lt;br /&gt;
* oranges&lt;br /&gt;
* pears';&lt;br /&gt;
&lt;br /&gt;
var parsed = parse_markdown(markdown);&lt;br /&gt;
&lt;br /&gt;
debug.dump(parsed);&lt;br /&gt;
&lt;br /&gt;
var path = string.normpath(getprop(&amp;quot;/sim/fg-home&amp;quot;) ~ '/Export/parse_markdown()-test.html');&lt;br /&gt;
&lt;br /&gt;
var file = io.open(path, &amp;quot;w&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
var html = &amp;quot;&amp;lt;!DOCTYPE html&amp;gt;\n\n&amp;lt;html&amp;gt;\n\n&amp;lt;head&amp;gt;\n\t&amp;lt;meta charset=\&amp;quot;UTF-8\&amp;quot;&amp;gt;\n\t&amp;lt;title&amp;gt;parse_markdown() test generated by Nasal&amp;lt;/title&amp;gt;\n&amp;lt;/head&amp;gt;\n\n&amp;lt;body&amp;gt;\n\t&amp;lt;pre&amp;gt;&amp;quot; ~ parsed ~ &amp;quot;&amp;lt;/pre&amp;gt;\n&amp;lt;/body&amp;gt;\n\n&amp;lt;/html&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
io.write(file, html);&lt;br /&gt;
io.close(file);&lt;br /&gt;
print(&amp;quot;Done, file ready for viewing (&amp;quot; ~ path ~ &amp;quot;)&amp;quot;);&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===parsexml()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = &amp;lt;nowiki&amp;gt;parsexml(path[, start[, end[, data[, pro_ins]]]]);&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=710|t=Source}}&lt;br /&gt;
|text = This function is an interface into the built-in [http://expat.sourceforge.net/ Expat XML parser]. The absolute path to the file is returned as string, or &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; is returned on error.&lt;br /&gt;
|param1 = path&lt;br /&gt;
|param1text = Mandatory absolute path to the XML file to be parsed.&lt;br /&gt;
|param2 = start&lt;br /&gt;
|param2text = Optional callback function that will be called for every starting tag. The function should take two argument: the tag name and a hash containing attributes.&lt;br /&gt;
|param3 = end&lt;br /&gt;
|param3text = Optional callback function that will be called for every ending tag. The function should take one argument: the tag name.&lt;br /&gt;
|param4 = data&lt;br /&gt;
|param4text = Optional callback function that will be called for every piece of data within a set of tags. The function should take one argument: the data as a string.&lt;br /&gt;
|param5 = pro_ins&lt;br /&gt;
|param5text = Optional callback function that will be called for every {{wikipedia|Processing Instruction|processing instruction}}. The function should take two argument: the target and the data string.&lt;br /&gt;
|example1text = Save the below XML code in &amp;lt;tt&amp;gt;''[[$FG_HOME]]/Export/demo.xml''&amp;lt;/tt&amp;gt;. Then, execute the Nasal code below in the Nasal Console. The XML will be parsed and each bit of the code will be printed.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;?xml-stylesheet type=&amp;quot;text/xsl&amp;quot; href=&amp;quot;style.xsl&amp;quot;?&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;foo&amp;gt;&lt;br /&gt;
  &amp;lt;blah type=&amp;quot;string&amp;quot;&amp;gt;&amp;lt;![CDATA[ &amp;lt;sender&amp;gt;John Smith&amp;lt;/sender&amp;gt; ]]&amp;gt;&amp;lt;/blah&amp;gt;&lt;br /&gt;
  &amp;lt;blah2 type=&amp;quot;string&amp;quot;&amp;gt;Orange &amp;amp;amp; lemons&amp;lt;/blah2&amp;gt;&lt;br /&gt;
&amp;lt;/foo&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|example1 = var start = func(name, attr){&lt;br /&gt;
    print(&amp;quot;Starting tag: '&amp;quot;, name, &amp;quot;'&amp;quot;);&lt;br /&gt;
    foreach(var a; keys(attr)){&lt;br /&gt;
        print(&amp;quot;\twith attribute &amp;quot;, a, '=&amp;quot;', attr[a], '&amp;quot;');&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var end = func(name){&lt;br /&gt;
    print(&amp;quot;Ending tag: '&amp;quot;, name, &amp;quot;'&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var data = func(data){&lt;br /&gt;
    print(&amp;quot;Data = '&amp;quot;, data, &amp;quot;'&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var pro_instr = func(target, data){&lt;br /&gt;
    print(&amp;quot;Processing instruction: target = '&amp;quot;, target, &amp;quot;', data = '&amp;quot;, data, &amp;quot;'&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
parsexml(getprop(&amp;quot;/sim/fg-home&amp;quot;) ~ '/Export/demo.xml', start, end, data, pro_instr);&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===print()===&lt;br /&gt;
{{Note|As of 07/2020, we are in the process of slowly deprecating and removing Nasal print() (in fgdata) where possible in favor of log [[#logprint()]] which takes a priority level.&lt;br /&gt;
&lt;br /&gt;
This is part of the goal that we achieve zero output at log-level at alert, and everything shown at ‘warn; is really a warning, not just information.&amp;lt;ref&amp;gt;https://sourceforge.net/p/flightgear/mailman/message/37042224/&amp;lt;/ref&amp;gt;}}&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = &amp;lt;nowiki&amp;gt;print(data[, data[, ...]]);&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=415|t=Source}}&lt;br /&gt;
|text = Concatenates its arguments and then prints it to the terminal and the [[Commonly used debugging tools#fgfs.log|log]]. Note that a newline is automatically added.&lt;br /&gt;
|param1 = data&lt;br /&gt;
|param1text = Data to print. Only strings and numbers can be printed; other data types will not be. There many be any number of arguments; they will just be concatenated together.&lt;br /&gt;
|example1 = print(&amp;quot;Just&amp;quot;, &amp;quot; a &amp;quot;, &amp;quot;test&amp;quot;); # prints &amp;quot;Just a test&amp;quot;&lt;br /&gt;
|example2 = print(&amp;quot;pi = &amp;quot;, math.pi); # prints &amp;quot;pi = 3.141592...&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== printf() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = &amp;lt;nowiki&amp;gt;printf(format[, arg[, arg, [...]]]);&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|source = {{fgdata file|Nasal/globals.nas|t=Source}}&lt;br /&gt;
|text = Creates and prints a formatted string. For a description of its arguments, see {{func link|sprintf()}} (it is, in fact, implemented using &amp;lt;code&amp;gt;sprintf()&amp;lt;/code&amp;gt;).&lt;br /&gt;
|example1 = printf(&amp;quot;In hexadecimal, 100000 = %X&amp;quot;, 186A0); # prints &amp;quot;In hexadecimal, 100000 = 186A0&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===printlog()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = &amp;lt;nowiki&amp;gt;printlog(level, data[, data[, ...]]);&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|source = {{fgdata file|Nasal/globals.nas|t=Source}}&lt;br /&gt;
|text = Prints the given message with the given log level. If the log level is higher or equal to &amp;lt;code&amp;gt;/sim/logging/priority&amp;lt;/code&amp;gt;, it is printed.&lt;br /&gt;
|param1 = level&lt;br /&gt;
|param1text = Mandatory log level as a string. Must be one of &amp;quot;none,&amp;quot; &amp;quot;bulk,&amp;quot; &amp;quot;debug,&amp;quot; &amp;quot;info,&amp;quot; &amp;quot;warn,&amp;quot; or &amp;quot;alert.&amp;quot; Note that &amp;quot;none&amp;quot; will mean that the message will never be printed.&lt;br /&gt;
|param2 = data&lt;br /&gt;
|param2text = Data to be printed. Only strings and numbers will be printed; others will not be. There may be any number of arguments; they will just be concatenated together.&lt;br /&gt;
|example1 = printlog(&amp;quot;alert&amp;quot;, &amp;quot;This is an alert&amp;quot;); # message will be printed&lt;br /&gt;
|example2 = printlog(&amp;quot;info&amp;quot;, &amp;quot;Just informing you about something&amp;quot;); # message will be printed only if log level is set to &amp;quot;info&amp;quot; or less&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===rand()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = rand();&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=554|t=Source}}&lt;br /&gt;
|text = Returns a random floating point number between 0 (inclusive) and 1 (exclusive). It takes no arguments.&lt;br /&gt;
|example1 = print(rand()); # prints random number&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===registerFlightPlanDelegate()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = registerFlightPlanDelegate(init_func);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1879|t=Source}}&lt;br /&gt;
|text = Registers a flight plan delegate. See &amp;lt;tt&amp;gt;''{{fgdata file|Nasal/route_manager.nas|t=$FG_ROOT/Nasal/route_manager.nas}}''&amp;lt;/tt&amp;gt; for examples.&lt;br /&gt;
|param1 = init_func&lt;br /&gt;
|param1text = Initialization function which will be called during FlightGear's startup.&lt;br /&gt;
}}&lt;br /&gt;
===removecommand()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = removecommand(cmd);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=668|t=Source}}&lt;br /&gt;
|text = Removes the given fgcommand. Returns &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt;.&lt;br /&gt;
{{caution|This will remove '''any''' [[fgcommands|fgcommand]], even those implemented in C++, so use with caution!}}&lt;br /&gt;
|param1 = cmd&lt;br /&gt;
|param1text = String specifying the name of the command to remove.&lt;br /&gt;
|example1 = addcommand(&amp;quot;hello&amp;quot;, func(){&lt;br /&gt;
    print(&amp;quot;Hello&amp;quot;);&lt;br /&gt;
});&lt;br /&gt;
fgcommand(&amp;quot;hello&amp;quot;); # &amp;quot;Hello&amp;quot; will be printed&lt;br /&gt;
removecommand(&amp;quot;hello&amp;quot;); # removes it&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===removelistener()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = removelistener(id);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=1384|t=Part 1}} {{!}} {{flightgear file|src/Scripting/NasalSys.cxx|l=506|t=Part 2}}&lt;br /&gt;
|text = Removes and deactivates the given listener and returns the number of listeners left or &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; on error.&lt;br /&gt;
{{note|It is good practice to remove listeners when they are not required anymore. This prevents the listeners reducing FlightGear's run performance.}}&lt;br /&gt;
|param1 = id&lt;br /&gt;
|param1text = ID of listener as returned by {{func link|setlistener()}}.&lt;br /&gt;
|example1 = var ls = setlistener(&amp;quot;/sim/test&amp;quot;, func(){&lt;br /&gt;
    print(&amp;quot;Property '/sim/test' has been changed&amp;quot;);&lt;br /&gt;
});&lt;br /&gt;
setprop(&amp;quot;/sim/test&amp;quot;, &amp;quot;blah&amp;quot;); # trigger listener&lt;br /&gt;
var rem = removelistener(ls); # remove listener&lt;br /&gt;
print(&amp;quot;There are &amp;quot;, rem, &amp;quot; listeners remaining&amp;quot;);&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===resolvepath()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = resolvepath(path);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=604|t=Source}}&lt;br /&gt;
|text = Takes a relative path as a string and uses [[SimGear]]'s path-resolving framework to return an absolute path as a string. If the path could not be resolved, an empty string is returned. See [[Resolving Paths]] for a detailed description of the algorithm. This function can also be used to check if a file exists.&lt;br /&gt;
|param1 = path&lt;br /&gt;
|param1text = Relative path to be completed.&lt;br /&gt;
|example1 = print(resolvepath(&amp;quot;Nasal/globals.nas&amp;quot;)); # prints the equivalent of $FG_ROOT/Nasal/globals.nas&lt;br /&gt;
|example2 = print(resolvepath(&amp;quot;blah&amp;quot;)); # prints nothing; could not be resolved&lt;br /&gt;
|example3 = var file_path = resolvepath(&amp;quot;Aircraft/SenecaII/some-file&amp;quot;);&lt;br /&gt;
if (file_path != &amp;quot;&amp;quot;){&lt;br /&gt;
    gui.popupTip(&amp;quot;some-file found&amp;quot;, 2);&lt;br /&gt;
} else {&lt;br /&gt;
    gui.popupTip(&amp;quot;some-file not found&amp;quot;, 2);&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===setlistener()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = &amp;lt;nowiki&amp;gt;setlistener(node, code[, init[, type]]);&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|private = _setlistener()&lt;br /&gt;
|source = {{flightgear file|src/Scripting/Nasal|l=499|t=Part 1}} {{!}} {{flightgear file|src/Scripting/Nasal|l=1350|t=Part 2}}&amp;lt;br&amp;gt;''Listener implemented using the {{API Link|flightgear|class|FGNasalListener}} class.''&lt;br /&gt;
|text = Creates a listener which will be triggered when the given property is changed (depending on the '''type'''). A unique integer ID is returned; this can later be used as the argument to {{func link|removelistener()}}.&lt;br /&gt;
{{note|Listeners are known to be a source of resource leaks. To avoid this, please take measures such as:&lt;br /&gt;
* Using {{func link|removelistener()}} when they are not needed any more.&lt;br /&gt;
* Using a single initialization listener.&lt;br /&gt;
* Avoiding tying listeners to properties that are rapidly updated (e.g., many times per frame).&lt;br /&gt;
}}&lt;br /&gt;
|param1 = node&lt;br /&gt;
|param1text = Mandatory string or &amp;lt;code&amp;gt;props.Node&amp;lt;/code&amp;gt; object pointing to a property in the [[Property Tree]].&lt;br /&gt;
|param2 = code&lt;br /&gt;
|param2text = Mandatory callback function to execute when the listener is triggered. The function can take up to four arguments in the following order:&lt;br /&gt;
* '''changed''': a &amp;lt;code&amp;gt;props.Node&amp;lt;/code&amp;gt; object pointing to the changed node.&lt;br /&gt;
* '''listen''': a &amp;lt;code&amp;gt;props.Node&amp;lt;/code&amp;gt; object pointing to the listened-to node. Note that this argument maybe different depending on the '''type'''.&lt;br /&gt;
* '''mode''': an integer telling how the listener was triggered. 0 means that the value was changed. 1 means that a child property was added. -1 means that a child property was removed.&lt;br /&gt;
* '''is_child''': boolean telling whether '''changed''' is a child property of the listened-to '''node''' or not. 1 (true) if it is, 0 (false) otherwise.&lt;br /&gt;
|param3 = init&lt;br /&gt;
|param3text = If set to 1 (true), the listener will additionally be triggered when it is created. This argument is optional and defaults to 0 (false).&lt;br /&gt;
|param4 = type&lt;br /&gt;
|param4text = Integer specifying the listener's behavior. 0 means that the listener will only trigger when the property is changed. 1 means that the trigger will always be triggered when the property is written to. 2 will mean that the listener will be triggered even if child properties are modified. This argument is optional and defaults to 1.&lt;br /&gt;
|example1 = var prop = props.globals.initNode(&amp;quot;/sim/test&amp;quot;, &amp;quot;&amp;quot;, &amp;quot;STRING&amp;quot;); # create property&lt;br /&gt;
var id = setlistener(&amp;quot;/sim/test&amp;quot;, func(n){ # create listener&lt;br /&gt;
    print(&amp;quot;Value: &amp;quot;, n.getValue());&lt;br /&gt;
});&lt;br /&gt;
setprop(&amp;quot;/sim/test&amp;quot;, &amp;quot;blah&amp;quot;); # trigger listener&lt;br /&gt;
removelistener(id); # remove listener&lt;br /&gt;
prop.remove(); # remove property&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===setprop()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = &amp;lt;nowiki&amp;gt;setprop(path[, path[, ...]], value);&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=385|t=Source}}&lt;br /&gt;
|text = Sets the value of a property in the [[Property Tree]]. If the property does not exist, it will be created. Returns 1 (true) on success or 0 (false) if there was an error.&lt;br /&gt;
{{note|If you want to remove a property, you will have to use one of the &amp;lt;code&amp;gt;props&amp;lt;/code&amp;gt; helpers:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
props.globals.getNode(&amp;quot;foo/bar&amp;quot;).remove(); # take out the complete node&lt;br /&gt;
props.globals.getNode(&amp;quot;foo&amp;quot;).removeChild(&amp;quot;bar&amp;quot;); # take out a certain child node&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
|param1 = path&lt;br /&gt;
|param1text = There needs to be at least one '''path''' argument, but there is no limit to the maximum amount that can be given. They will be concatenated together to form a property tree path. The arguments must be strings, but in FlightGear v3.2 onwards, there also is support (added by {{flightgear commit|34ed79}}) for numeric arguments as indices. See example 2 below.&lt;br /&gt;
|param2 = value&lt;br /&gt;
|param2text = Value to write to the given property. Must be either a string or a number.&lt;br /&gt;
|example1 = setprop(&amp;quot;/sim/demo&amp;quot;, &amp;quot;This is a demo&amp;quot;);&lt;br /&gt;
|example2text = Note that the example below will only work in FlightGear 3.2 and above.&lt;br /&gt;
|example2 = for(var i = 0; i &amp;lt; 3; i += 1){&lt;br /&gt;
    setprop(&amp;quot;/sim/demo&amp;quot;, i, &amp;quot;Demo #&amp;quot; ~ i));&lt;br /&gt;
}&lt;br /&gt;
|example3text = Same as above, but is supported by all versions of FlightGear.&lt;br /&gt;
|example3 = for(var i = 0; i &amp;lt; 3; i += 1){&lt;br /&gt;
    setprop(&amp;quot;/sim/demo[&amp;quot; ~ i ~ &amp;quot;]&amp;quot;, &amp;quot;Demo #&amp;quot; ~ i));&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
====See also====&lt;br /&gt;
{{note| If you have to read/write the same property multiple times (e.g. in an update loop), it is more efficient to use a node object: &lt;br /&gt;
To get a Node rather than its value, use &amp;lt;code&amp;gt;props.globals.getNode()&amp;lt;/code&amp;gt; - see [[Nasal_library/props]]. }}&lt;br /&gt;
&lt;br /&gt;
===settimer() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = settimer(function, delta[, realtime]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=499|t=Part 1}} {{!}} {{flightgear file|src/Scripting/NasalSys.cxx|l=1286|t=Part 2}}&lt;br /&gt;
|text = Runs the given function a specified amount of seconds after the current time. Returns &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt;.&lt;br /&gt;
{{note|Improper use of &amp;lt;code&amp;gt;settimer()&amp;lt;/code&amp;gt; may cause resource leaks. It is also highly recommended that the newer {{func link|maketimer()}} should be used instead of this function.}} &lt;br /&gt;
|param1 = function&lt;br /&gt;
|param1text = Mandatory function that will be called. It may be necessary to enclose code in an anonymous function (see example).&lt;br /&gt;
|param2 = delta&lt;br /&gt;
|param2text = Mandatory amount of time in seconds after which the function will be called.&lt;br /&gt;
|param3 = realtime&lt;br /&gt;
|param3text = If 1 (true), &amp;quot;real time&amp;quot; will be used instead of &amp;quot;simulation time.&amp;quot; Defaults to 0 (false). Note that if &amp;quot;simulation time&amp;quot; is used, the timer will not run while FlightGear is paused.&lt;br /&gt;
|example1 = var myFunc = func(){&lt;br /&gt;
    print(&amp;quot;Hello&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
settimer(myFunc, 2); # runs myFunc after 2 seconds&lt;br /&gt;
|example2 = var sqr = func(a){&lt;br /&gt;
    return a * a;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
settimer(func(){&lt;br /&gt;
    print(sqr(2)); # will print 4 after 2 seconds&lt;br /&gt;
}, 2);&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===srand() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = srand();&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=559|t=Source}}&lt;br /&gt;
|text = Makes the pseudorandom number generator (see {{func link|rand()}}) generate a new {{wikipedia|random seed|noicon=1}} based on time. Returns 0.&lt;br /&gt;
}}&lt;br /&gt;
===systime()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = systime();&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=770|t=Source}}&lt;br /&gt;
|text = Returns the {{wikipedia|Unix time}} (seconds since since 00:00:00 UTC, 1/1/1970) as a floating point number with high resolution. This function is useful for benchmarking purposes (see example 2).&lt;br /&gt;
{{note|1=High resolution timers under Windows can produce inaccurate or fixed sub-millisecond results.&amp;lt;ref&amp;gt;{{cite web|url=http://forum.flightgear.org/viewtopic.php?f=30&amp;amp;t=29259|title=Nasal: systime() ??!?|author=Necolatis|date=Apr 2nd, 2016}}&amp;lt;/ref&amp;gt; This is due to the underlying {{func link|GetSystemTimeAsFileTime()|link=https://msdn.microsoft.com/en-us/library/windows/desktop/ms724397(v=vs.85).aspx}} API call, which depends on hardware availability of suitable high resolution timers. See also [https://msdn.microsoft.com/en-us/library/windows/desktop/dn553408(v=vs.85).aspx Acquiring high-resolution time stamps]}}&lt;br /&gt;
|example1 = print(&amp;quot;Unix time: &amp;quot;, systime()); # prints Unix time&lt;br /&gt;
|example2 = var myFunc = func(){&lt;br /&gt;
    for(var i = 0; i &amp;lt;= 10; i += 1){&lt;br /&gt;
        print(&amp;quot;Interation #&amp;quot;, i);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
var t = systime(); # record time&lt;br /&gt;
myFunc(); # run function&lt;br /&gt;
var t2 = systime(); # record new time&lt;br /&gt;
print(&amp;quot;myFunc() took &amp;quot;, t2 - t, &amp;quot; seconds&amp;quot;); # print result&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===thisfunc()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = thisfunc();&lt;br /&gt;
|source = {{fgdata file|Nasal/globals.nas|t=Source}}&lt;br /&gt;
|text = Returns the function from which this function is called. This allows a function to reliably and safely call itself from within a closure.&lt;br /&gt;
|example1 = var stringify_vec = func(input){&lt;br /&gt;
    if (typeof(input) == &amp;quot;scalar&amp;quot;){&lt;br /&gt;
        return sprintf(&amp;quot;%s&amp;quot;, input);&lt;br /&gt;
    } elsif (typeof(input) == &amp;quot;vector&amp;quot;) {&lt;br /&gt;
        if (size(input) == 0) return &amp;quot;[]&amp;quot;;&lt;br /&gt;
        var this = thisfunc();&lt;br /&gt;
        var buffer = &amp;quot;[&amp;quot;;&lt;br /&gt;
        for(var i = 0; i &amp;lt; size(input); i += 1){&lt;br /&gt;
            buffer ~= this(input[i]);&lt;br /&gt;
            if (i == size(input) - 1) {&lt;br /&gt;
                buffer ~= &amp;quot;]&amp;quot;;&lt;br /&gt;
            } else {&lt;br /&gt;
                buffer ~= &amp;quot;, &amp;quot;;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        return buffer;&lt;br /&gt;
    } else {&lt;br /&gt;
        die(&amp;quot;stringify_vec(): Error! Invalid input. Must be a vector or scalar&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var test_vec = [&amp;quot;a&amp;quot;, &amp;quot;b&amp;quot;, &amp;quot;c&amp;quot;, 1, 2, 3];&lt;br /&gt;
debug.dump(stringify_vec(test_vec)); # prints &amp;quot;[a, b, c, 1, 2, 3]&amp;quot;&lt;br /&gt;
test_vec = [];&lt;br /&gt;
debug.dump(stringify_vec(test_vec)); # prints &amp;quot;[]&amp;quot;&lt;br /&gt;
test_vec = {};&lt;br /&gt;
debug.dump(stringify_vec(test_vec)); # will throw an error&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===tileIndex() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = tileIndex();&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1720|t=Source}}&lt;br /&gt;
|text = Returns the index of the tile at the aircraft's current position as a string. This corresponds to the name of the STG file of the tile. For example, at [[KSFO]], this would be &amp;lt;code&amp;gt;942050&amp;lt;/code&amp;gt;, corresponding to &amp;lt;tt&amp;gt;''[[$FG_SCENERY]]/Terrain/w130n30/w123n3/942050.stg''&amp;lt;/tt&amp;gt;.&lt;br /&gt;
|example1 = print(tileIndex()); # print index&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== tilePath()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = tilePath();&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1712|t=Source}}&lt;br /&gt;
|text = Returns the base path of the tile at the aircraft's current position as a string. For example, at KSFO, this would be &amp;lt;code&amp;gt;w130n30/w123n3&amp;lt;/code&amp;gt;, corresponding to &amp;lt;tt&amp;gt;''[[$FG_SCENERY]]/Terrain/w130n30/w123n3''&amp;lt;/tt&amp;gt;.&lt;br /&gt;
|example1 = print(tilePath()); # print path&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== values()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = values(hash);&lt;br /&gt;
|source = {{fgdata file|Nasal/globals.nas|t=Source}}&lt;br /&gt;
|text = Returns a vector containing the values of the given hash.&lt;br /&gt;
|param1 = hash&lt;br /&gt;
|param1text = Mandatory hash to get the values of.&lt;br /&gt;
|example1 = var hash = {&lt;br /&gt;
    &amp;quot;a&amp;quot;: 1,&lt;br /&gt;
    &amp;quot;b&amp;quot;: 2,&lt;br /&gt;
    &amp;quot;c&amp;quot;: 3&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
foreach(var val; values(hash)){&lt;br /&gt;
    print(val);&lt;br /&gt;
}&lt;br /&gt;
|example2text = The below example does exactly the same thing as the above example, but does not use &amp;lt;code&amp;gt;values()&amp;lt;/code&amp;gt;:&lt;br /&gt;
|example2 = var hash = {&lt;br /&gt;
    &amp;quot;a&amp;quot;: 1,&lt;br /&gt;
    &amp;quot;b&amp;quot;: 2,&lt;br /&gt;
    &amp;quot;c&amp;quot;: 3&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
foreach(var key; keys(hash)){&lt;br /&gt;
    print(hash[key]);&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
==Extension functions new in FG 2020.1 ==&lt;br /&gt;
The following functions have been added to the nasal core library and will be released with FlightGear version 2020.1. &lt;br /&gt;
Before the release they are available in the development branch &amp;quot;next&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
===isfunc()===&lt;br /&gt;
Returns 1 if type or argument is a function, otherwise 0.&lt;br /&gt;
&lt;br /&gt;
===isghost()===&lt;br /&gt;
Returns 1 if type or argument is a ghost, otherwise 0.&lt;br /&gt;
&lt;br /&gt;
=== ishash()===&lt;br /&gt;
Returns 1 if type or argument is a hash, otherwise 0.&lt;br /&gt;
&lt;br /&gt;
===isint()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = isint(x);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|t=Source}}&lt;br /&gt;
|text = Returns 1 if argument has a numeric value and x == floor(x), e.g. integer.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===isnum()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = isnum(x);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|t=Source}}&lt;br /&gt;
|text = Returns 1 if typeof(x) is &amp;quot;scalar&amp;quot; and x has a numeric value otherwise 0. &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===isscalar()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = isscalar(x);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|t=Source}}&lt;br /&gt;
|text = Returns 1 if type or argument is a scalar (string or numeric), otherwise (vector, hash, func, ...) it returns 0. This is useful to check if a variable can be converted to string e.g. when useing the string concat operator &amp;quot;~&amp;quot;. &lt;br /&gt;
|example1 = var a = &amp;quot;foo&amp;quot;; &lt;br /&gt;
var b=42;&lt;br /&gt;
if (isscalar(a) and isscalar(b)) print(a~b);&lt;br /&gt;
if (isstr(a)) print(&amp;quot;a is a string&amp;quot;);&lt;br /&gt;
if (isint(b)) print(&amp;quot;b is an integer&amp;quot;);&lt;br /&gt;
# if (isscalar(a))... is equivalent to if (typeof(a) == &amp;quot;scalar&amp;quot;)...&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
===isstr()===&lt;br /&gt;
Returns 1 if type or argument is a string, otherwise 0. &lt;br /&gt;
&lt;br /&gt;
===isvec()===&lt;br /&gt;
Returns 1 if type or argument is a vector, otherwise 0.&lt;br /&gt;
&lt;br /&gt;
===vecindex()===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = vecindex(vector, value);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|t=Source}}&lt;br /&gt;
|text = Returns the index of value or nil, if value is not found in vector.&lt;br /&gt;
|example1=&lt;br /&gt;
var myvector = [&amp;quot;apple&amp;quot;, &amp;quot;bananna&amp;quot;, &amp;quot;coconut&amp;quot;];&lt;br /&gt;
# to check if a vector contains a certain value compare vecindex to nil&lt;br /&gt;
if (vecindex(myvector, &amp;quot;apple&amp;quot;) != nil) {&lt;br /&gt;
    print(&amp;quot;found apple&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
# WARNING: this will not work as desired because index is 0&lt;br /&gt;
if (vecindex(myvector, &amp;quot;apple&amp;quot;)) {&lt;br /&gt;
    print(&amp;quot;found apple&amp;quot;);&lt;br /&gt;
&amp;lt;nowiki&amp;gt;}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
==Variables==&lt;br /&gt;
Various global constants (technically variables) are provided for use in converting between different units. They are all found in {{fgdata file|Nasal/globals.nas|text=$FG_ROOT/Nasal/globals.nas}}.&lt;br /&gt;
&lt;br /&gt;
===D2R===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var radians = degrees * D2R;&lt;br /&gt;
|text = Converts an angle from degrees to radians when multiplied by the angle in degrees. Equal to &amp;lt;code&amp;gt;π / 180&amp;lt;/code&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
===FPS2KT===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var knots = feet_per_second * FPS2KT;&lt;br /&gt;
|text = Converts a velocity from feet per second to knots when multiplied by the velocity in feet per second. Approximately equal to 0.5925.&lt;br /&gt;
}}&lt;br /&gt;
===FT2M===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var metres = feet * FT2M;&lt;br /&gt;
|text = Converts a length from feet to metres when multiplied by the length in feet. Equal to 0.3048.&lt;br /&gt;
}}&lt;br /&gt;
===GAL2L ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var litres = gallons * GAL2L;&lt;br /&gt;
|text = Converts a volume from US liquid gallons to litres when multiplied by the volume in gallons. Approximately equal to 3.7854.&lt;br /&gt;
}}&lt;br /&gt;
===IN2M===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var metres = inches * IN2M;&lt;br /&gt;
|text = Converts a length from inches to metres when multiplied by the length in inches. Equal to 0.0254.&lt;br /&gt;
}}&lt;br /&gt;
===KG2LB===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var pounds = kilograms * KG2LB;&lt;br /&gt;
|text = Converts a mass from kilograms to pounds when multiplied by the mass in kilograms. Approximately equal to 2.2046.&lt;br /&gt;
}}&lt;br /&gt;
===KT2FPS===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var feet_per_second = knots * KT2FPS;&lt;br /&gt;
|text = Converts a velocity from knots to feet per second when multiplied by the velocity in knots. Approximately equal to 1.6878.&lt;br /&gt;
}}&lt;br /&gt;
===KT2MPS===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var metres_per_second = knots * KT2MPS;&lt;br /&gt;
|text = Converts a velocity from knots to metres per second when multiplied by the velocity in knots. Approximately equal to 0.5144.&lt;br /&gt;
}}&lt;br /&gt;
===L2GAL===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var gallons = litres * L2GAL;&lt;br /&gt;
|text = Converts a volume from litres to US liquid gallons when multiplied by the volume in litres. Approximately equal to 0.2642.&lt;br /&gt;
}}&lt;br /&gt;
===LB2KG===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var kilograms = pounds * LB2KG;&lt;br /&gt;
|text = Converts a mass from pounds to kilograms when multiplied by the mass in pounds. Approximately equal to 0.4536.&lt;br /&gt;
}}&lt;br /&gt;
===M2FT ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var feet = metres * M2FT;&lt;br /&gt;
|text = Converts a length from metres to feet when multiplied by the length in metres. Approximately equal to 3.2808.&lt;br /&gt;
}}&lt;br /&gt;
===M2IN===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var inches = metres * M2IN;&lt;br /&gt;
|text = Converts a length from metres to inches when multiplied by the length in metres. Approximately equal to 39.3701.&lt;br /&gt;
}}&lt;br /&gt;
===M2NM ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var nautical_miles = metres * M2NM;&lt;br /&gt;
|text = Converts a length from metres to nautical miles when multiplied by the length in metres. Approximately equal to 0.00054.&lt;br /&gt;
}}&lt;br /&gt;
===MPS2KT ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var knots = metres_per_second * MPS2KT;&lt;br /&gt;
|text = Converts a velocity from metres per second to knots when multiplied by the velocity in metres per second. Approximately equal to 1.9438.&lt;br /&gt;
}}&lt;br /&gt;
===NM2M ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var metres = nautical_miles * NM2M;&lt;br /&gt;
|text = Converts a length from nautical miles to metres when multiplied by the length in nautical miles. Equal to 1,852.&lt;br /&gt;
}}&lt;br /&gt;
===R2D===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var degrees = radians * R2D;&lt;br /&gt;
|text = Converts an angle from radians to degrees when multiplied by the angle in radians. Equal to &amp;lt;code&amp;gt;180 / π&amp;lt;/code&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Appendix}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Nasal namespaces}}&lt;/div&gt;</summary>
		<author><name>Clm76</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Nasal_library&amp;diff=118209</id>
		<title>Nasal library</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Nasal_library&amp;diff=118209"/>
		<updated>2019-05-10T07:10:00Z</updated>

		<summary type="html">&lt;p&gt;Clm76: /* sort() */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Nasal Navigation|nocat=1}}&lt;br /&gt;
This page documents the global '''library functions and variables''' of FlightGear's built-in scripting language, [[Nasal]]. This includes ''[[#Core library functions|core library functions]]'', which were included in Nasal before its integration into FlightGear, the ''[[#Extension functions|extension functions]]'', which have been subsequently added, and are specifically designed for FlightGear, and the ''[[#variables|global variables]]'', which are conversion variables, added with extension functions, for converting between units. The relevant folders in [[Git]] are:&lt;br /&gt;
* {{flightgear file|src/Scripting}}&lt;br /&gt;
* {{simgear file|simgear/nasal}}&lt;br /&gt;
&lt;br /&gt;
All these functions and variables are in the global namespace, that is, they are directly accessible (e.g., one can call &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; inline&amp;gt;magvar()&amp;lt;/syntaxhighlight&amp;gt; instead of &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; inline&amp;gt;namespace.magvar()&amp;lt;/syntaxhighlight&amp;gt;). However, if a namespace must be used, &amp;lt;code&amp;gt;globals&amp;lt;/code&amp;gt; is the correct namespace, but using it is not recommended. For a more complete explanation, see [[Nasal Namespaces in-depth]].&lt;br /&gt;
&lt;br /&gt;
{{tip|Copy &amp;amp; paste the examples into your [[Nasal Console]] and execute them to see what they do.|width=70%}}&lt;br /&gt;
&lt;br /&gt;
== Core library functions ==&lt;br /&gt;
This is the list of the basic '''core library functions.''' Most of these functions were part of the original Nasal library (before its integration in to FlightGear), while some have been added or changed over time.  See also:&lt;br /&gt;
* http://plausible.org/nasal/lib.html ([http://web.archive.org/web/20101010094553/http://plausible.org/nasal/lib.html archive])&lt;br /&gt;
* {{simgear file|simgear/nasal/lib.c}} ([http://sourceforge.net/p/flightgear/simgear/ci/next/log/?path=/simgear/nasal/lib.c history])&lt;br /&gt;
&lt;br /&gt;
=== append() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = append(vector, element[, element[, ...]]);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=42|t=Source}}&lt;br /&gt;
|text = This function appends, or adds, the given element(s) to the end of the vector given in the first argument.  Returns the vector operated on.&lt;br /&gt;
|param1 = vector&lt;br /&gt;
|param1text = The vector to which the arguments will be appended.&lt;br /&gt;
|param2 = element&lt;br /&gt;
|param2text = An element to be added to the vector.&lt;br /&gt;
|example1 = &lt;br /&gt;
var vector = [1, 2, 3]; # Initialize the vector&lt;br /&gt;
append(vector, 4); # Append the number 4 to the end of the vector&lt;br /&gt;
debug.dump(vector); # Print the contents of the vector&lt;br /&gt;
|example2 = &lt;br /&gt;
var vector = [1, 2, 3]; # Initialize the vector&lt;br /&gt;
append(vector, 4, 5, 6); # Append the numbers 4, 5, and 6 to the end of the vector&lt;br /&gt;
debug.dump(vector); # Print the contents of the vector&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== bind() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = bind(function, locals[, outer_scope]);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=502|t=Source}}&lt;br /&gt;
|text = This creates a new function object. A function in Nasal is three things: the actual code, a hash/namespace of local variables available to the function namespace, and the closure object of that namespace. These correspond to the three arguments respectively.&lt;br /&gt;
|param1 = function&lt;br /&gt;
|param1text = Function to evaluate.&lt;br /&gt;
|param2 = locals&lt;br /&gt;
|param2text = Hash containing values that will become the namespace (first closure) for the function.&lt;br /&gt;
|param3 = outer_scope&lt;br /&gt;
|param3text = Optional function which is bound to the next closure. This can be bound to yet another, making a linked list.&lt;br /&gt;
|example1 = # This is a namespace/hash with a single member, named &amp;quot;key,&amp;quot; which is initialized to 12 &lt;br /&gt;
var Namespace = {&lt;br /&gt;
    key: 12&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
# This is different namespace/hash containing a function&lt;br /&gt;
# dividing a variable &amp;quot;key&amp;quot; (which is unavailable/nil in this namespace) by 2&lt;br /&gt;
var AnotherNamespace = {&lt;br /&gt;
    ret: func {&lt;br /&gt;
        key /= 2;&lt;br /&gt;
    }&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
# To see that key is not available, try to call AnotherNamespace.ret() first&lt;br /&gt;
call(AnotherNamespace.ret, [], nil, nil, var errors = []);&lt;br /&gt;
if(size(errors)){&lt;br /&gt;
    print(&amp;quot;Key could not be divided/resolved!&amp;quot;);&lt;br /&gt;
    debug.printerror(errors);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# Associate the AnotherNamespace.ret() function with the first namespace&lt;br /&gt;
# so that &amp;quot;key&amp;quot; is now available&lt;br /&gt;
var function = bind(AnotherNamespace.ret, Namespace);&lt;br /&gt;
&lt;br /&gt;
# Invoke the new function&lt;br /&gt;
function();&lt;br /&gt;
&lt;br /&gt;
# Print out the value of Namespace.key&lt;br /&gt;
# It was changed to 12 from 6 by AnotherNamespace.ret()&lt;br /&gt;
print(Namespace.key);&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== call() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = call(func[, args[, me[, locals[, error]]]);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=247|t=Source}}&lt;br /&gt;
|text = Calls the given function with the given arguments and returns the result.  This function is very useful as it allows much more control over function calls and catches any errors or {{func link|die()}} calls that would normally trigger run-time errors cancelling execution of the script otherwise. &lt;br /&gt;
|param1 = func&lt;br /&gt;
|param1text = Function to execute.&lt;br /&gt;
|param2 = args&lt;br /&gt;
|param2text = Vector containing arguments to give to the called function.&lt;br /&gt;
|param3 = me&lt;br /&gt;
|param3text = &amp;lt;code&amp;gt;'''me'''&amp;lt;/code&amp;gt; reference for the function call (i.e., for method calls). If given, this will override any &amp;lt;code&amp;gt;'''me'''&amp;lt;/code&amp;gt; value existing in the namespace (locals argument).&lt;br /&gt;
|param4 = locals&lt;br /&gt;
|param4text = A hash with key/value pairs that will be available to the called function, typically used as the namespace for the function to be called.&lt;br /&gt;
|param5 = error&lt;br /&gt;
|param5text = A vector to append errors to.  If the called function generates an error, the error, place, and line will be written to this.  These errors can be printed using {{func link|printerror()|debug}}.&lt;br /&gt;
|example1 =&lt;br /&gt;
# prints &amp;quot;Called from call()&amp;quot;&lt;br /&gt;
call(func {&lt;br /&gt;
    print(&amp;quot;Called from call()&amp;quot;);&lt;br /&gt;
});&lt;br /&gt;
|example2 =&lt;br /&gt;
# prints &amp;quot;a = 1 : b = 2&lt;br /&gt;
call(func(a, b){&lt;br /&gt;
        print(&amp;quot;a = &amp;quot;, a, &amp;quot; : b = &amp;quot;, b);&lt;br /&gt;
    },&lt;br /&gt;
    [1, 2]&lt;br /&gt;
);&lt;br /&gt;
|example3 =&lt;br /&gt;
var Hash = {&lt;br /&gt;
    new: func {&lt;br /&gt;
        var m = { parents: [Hash] };&lt;br /&gt;
&lt;br /&gt;
        m.el1 = &amp;quot;string1&amp;quot;;&lt;br /&gt;
        m.el2 = &amp;quot;string2&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
        return m;&lt;br /&gt;
    }&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
# prints &amp;quot;me.el1 = string1&amp;quot;, then &amp;quot;me.el2 = string2&amp;quot; on the next line&lt;br /&gt;
call(func(a, b){        &lt;br /&gt;
        print(&amp;quot;me.el&amp;quot;, a, &amp;quot; = &amp;quot;, me[&amp;quot;el&amp;quot; ~ a]);      &lt;br /&gt;
        print(&amp;quot;me.el&amp;quot;, b, &amp;quot; = &amp;quot;, me[&amp;quot;el&amp;quot; ~ b]);&lt;br /&gt;
    },&lt;br /&gt;
    [1, 2],&lt;br /&gt;
    Hash.new()&lt;br /&gt;
);&lt;br /&gt;
|example4 =&lt;br /&gt;
# prints the value of math.pi&lt;br /&gt;
call(func {&lt;br /&gt;
        print(pi);&lt;br /&gt;
    }, nil, nil, &lt;br /&gt;
    math&lt;br /&gt;
);&lt;br /&gt;
|example5 =&lt;br /&gt;
call(func {&lt;br /&gt;
        print(math.ip); # math.ip doesn't exist&lt;br /&gt;
    }, nil, nil, nil,&lt;br /&gt;
    var errs = []&lt;br /&gt;
);&lt;br /&gt;
debug.printerror(errs); # The error is caught and printed using debug.printerror()&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== caller() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = caller([level]);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=404|t=Source}}&lt;br /&gt;
|text = Returns a vector containing a record from the current call stack.  The level numbering starts from the currently executing function (level 0).  Level 1 (the default) is the caller of the current function, and so on.  The result is a four-element vector containing a hash of local variables, the function object, the source, and the line number. &lt;br /&gt;
|param1 = level&lt;br /&gt;
|param1text = Optional integer specifying the stack level to return a result from.  Defaults to 1 (i.e. the caller of the currently executing function).&lt;br /&gt;
|example1 =&lt;br /&gt;
var myFunction = func(a, b){&lt;br /&gt;
    debug.dump(caller(0)[0]); # prints a hash of local variables, including arguments a and b&lt;br /&gt;
    return 2 * 2;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;2 x 2 = &amp;quot;, myFunction(2, 2));&lt;br /&gt;
|example2 =&lt;br /&gt;
var get_arg_value = func(){&lt;br /&gt;
    print(&amp;quot;Argument to myFunc = &amp;quot;, caller(1)[0]['a']); # print the value of myFunc's single argument, using caller()&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
var myFunc = func(a){&lt;br /&gt;
    get_arg_value();&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
myFunc(3);&lt;br /&gt;
|example3text = This is a real example taken from {{fgdata file|Nasal/canvas/MapStructure.nas}}.  Function &amp;lt;code&amp;gt;r()&amp;lt;/code&amp;gt; (above the TODOs) returns a hash with the key/value pairs as per its arguments. For example, something like this is returned: &amp;lt;code&amp;gt;{ name: &amp;quot;&amp;lt;name&amp;gt;&amp;quot;, vis: 1, zindex: nil }&amp;lt;/code&amp;gt;.&lt;br /&gt;
|example3 =&lt;br /&gt;
var MapStructure_selfTest = func() {&lt;br /&gt;
	var temp = {};&lt;br /&gt;
	temp.dlg = canvas.Window.new([600,400],&amp;quot;dialog&amp;quot;);&lt;br /&gt;
	temp.canvas = temp.dlg.createCanvas().setColorBackground(1,1,1,0.5);&lt;br /&gt;
	temp.root = temp.canvas.createGroup();&lt;br /&gt;
	var TestMap = temp.root.createChild(&amp;quot;map&amp;quot;);&lt;br /&gt;
	TestMap.setController(&amp;quot;Aircraft position&amp;quot;);&lt;br /&gt;
	TestMap.setRange(25); # TODO: implement zooming/panning via mouse/wheel here, for lack of buttons :-/&lt;br /&gt;
	TestMap.setTranslation(&lt;br /&gt;
		temp.canvas.get(&amp;quot;view[0]&amp;quot;)/2,&lt;br /&gt;
		temp.canvas.get(&amp;quot;view[1]&amp;quot;)/2&lt;br /&gt;
	);&lt;br /&gt;
	var r = func(name,vis=1,zindex=nil) return caller(0)[0];&lt;br /&gt;
	# TODO: we'll need some z-indexing here, right now it's just random&lt;br /&gt;
	# TODO: use foreach/keys to show all layers in this case by traversing SymbolLayer.registry direclty ?&lt;br /&gt;
	# maybe encode implicit z-indexing for each lcontroller ctor call ? - i.e. preferred above/below order ?&lt;br /&gt;
	foreach(var type; [r('TFC',0),r('APT'),r('DME'),r('VOR'),r('NDB'),r('FIX',0),r('RTE'),r('WPT'),r('FLT'),r('WXR'),r('APS'), ] ) &lt;br /&gt;
		TestMap.addLayer(factory: canvas.SymbolLayer, type_arg: type.name,&lt;br /&gt;
					visible: type.vis, priority: type.zindex,&lt;br /&gt;
		);&lt;br /&gt;
}; # MapStructure_selfTest&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== chr() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = chr(code);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=175|t=Source}}&lt;br /&gt;
|text = Returns a character as per the single argument. Extended ASCII is supported (see http://www.asciitable.com/ for a list of supported characters), although this may vary between different systems.  For a list of the most commonly used characters, see the {{wikipedia|ASCII#ASCII printable code chart|ASCII printable code chart}} ('''Dec''' column). The following table lists supported control characters, along with their equivalent control characters in Nasal strings.  {{Note|In Nasal, only strings enclosed with double-quotes (&amp;lt;code&amp;gt;&amp;quot;string&amp;quot;&amp;lt;/code&amp;gt;) supports control chracters.  Strings in single quotes (&amp;lt;code&amp;gt;'string'&amp;lt;/code&amp;gt;) do not.}}&lt;br /&gt;
{{{!}} class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Code !! Name !! Equivalent to&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} 10 {{!!}} {{Wikipedia|Newline}} {{!!}} &amp;lt;code&amp;gt;\n&amp;lt;/code&amp;gt;&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} 9 {{!!}} {{Wikipedia|Tab key#Tab characters|Horizontal tab}} {{!!}} &amp;lt;code&amp;gt;\t&amp;lt;/code&amp;gt;&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} 13 {{!!}} {{Wikipedia|Carriage return}} {{!!}} &amp;lt;code&amp;gt;\r&amp;lt;/code&amp;gt;&lt;br /&gt;
{{!}}}&lt;br /&gt;
|param1 = code&lt;br /&gt;
|param1text = Integer character code for the desired glyph.&lt;br /&gt;
|example1 = print(&amp;quot;Code 65 = &amp;quot;, chr(65)); # prints &amp;quot;Code 65 = A&amp;quot;&lt;br /&gt;
|example2text = This example displays all of the characters in a list, in the format &amp;lt;code&amp;gt;Code '''n''' = &amp;gt;'''char'''&amp;lt;&amp;lt;/code&amp;gt;, '''n''' being the index, and '''char''' being the character.&lt;br /&gt;
|example2 =&lt;br /&gt;
for(var i = 0; i &amp;lt;= 255; i += 1){&lt;br /&gt;
    print(&amp;quot;Code &amp;quot;, i, &amp;quot; = &amp;gt;&amp;quot;, chr(i), &amp;quot;&amp;lt;&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== closure() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = closure(func[, level]);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=421|t=Source}}&lt;br /&gt;
|text = Returns the hash table containing the lexical namespace of the given function. The level numbering start with level 0 being the namespace of '''func'''. &lt;br /&gt;
|param1 = func&lt;br /&gt;
|param1text = Function to evaluate.&lt;br /&gt;
|param2 = level&lt;br /&gt;
|param2text = Optional integer specifying the scope level.  Defaults to 0 (the namespace of '''func''').&lt;br /&gt;
|example1 =&lt;br /&gt;
var get_math_e = func {&lt;br /&gt;
    return e; # return the value of math.e&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var myFunction = bind(get_math_e, math); # bind get_math_e to the math namespace, so that math.e is immediately available to get_math_e&lt;br /&gt;
debug.dump(closure(myFunction)); # print the namespace of get_math_e&lt;br /&gt;
&lt;br /&gt;
print(myFunction());&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== cmp() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = cmp(a, b);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=112|t=Source}}&lt;br /&gt;
|text = Compares two strings, returning -1 if '''a''' is less than '''b''', 0 if they are identical and 1 if '''a''' is greater than '''b'''. &lt;br /&gt;
|param1 = a&lt;br /&gt;
|param1text = First string argument for comparison.&lt;br /&gt;
|param2 = b&lt;br /&gt;
|param2text = Second string argument for comparison.&lt;br /&gt;
|example1 = print(cmp(&amp;quot;1&amp;quot;, &amp;quot;two&amp;quot;)); # prints -1&lt;br /&gt;
|example2 = print(cmp(&amp;quot;string&amp;quot;, &amp;quot;string&amp;quot;)); # prints 0&lt;br /&gt;
|example3 = print(cmp(&amp;quot;one&amp;quot;, &amp;quot;2&amp;quot;)); # prints 1&lt;br /&gt;
|example4 = print(cmp(&amp;quot;string1&amp;quot;, &amp;quot;string2&amp;quot;)); # prints -1&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== compile() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = compile(code[, filename]);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=220|t=Source}}&lt;br /&gt;
|text = Compiles the specified code string and returns a function object bound to the current lexical context.  If there is an error, the function dies, with the argument to {{func link|die()}} being '''filename'''.&lt;br /&gt;
|param1 = code&lt;br /&gt;
|param1text = String containing Nasal code to be compiled.&lt;br /&gt;
|param2 = filename&lt;br /&gt;
|param2text = Optional string used for error messages/logging. Defaults to &amp;lt;code&amp;gt;&amp;lt;compile&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
|example1 = &lt;br /&gt;
var myCode = 'print(&amp;quot;hello&amp;quot;);';&lt;br /&gt;
var helloFunc = compile(myCode, &amp;quot;myCode&amp;quot;);&lt;br /&gt;
helloFunc();&lt;br /&gt;
|example2text = &amp;lt;code&amp;gt;compile&amp;lt;/code&amp;gt; is very convenient to support Nasal loaded from other files.  For instance, [[PropertyList XML files]] (such as GUI dialogs) may contain embedded Nasal sections that need to be parsed, processed and compiled.  For an example of how to do this, save the below XML code as &amp;lt;tt&amp;gt;''[[$FG_ROOT]]/gui/dialogs/test.xml''&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?xml version=&amp;quot;1.0&amp;quot;?&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;PropertyList&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nasal&amp;gt;&amp;lt;![CDATA[&lt;br /&gt;
print(&amp;quot;You have FlightGear v&amp;quot;, getprop(&amp;quot;/sim/version/flightgear&amp;quot;));&lt;br /&gt;
]]&amp;gt;&amp;lt;/nasal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/PropertyList&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Now, start FlightGear and execute this code in the [[Nasal Console]].&lt;br /&gt;
|example2 =&lt;br /&gt;
# Build the path&lt;br /&gt;
var FGRoot = getprop(&amp;quot;/sim/fg-root&amp;quot;);&lt;br /&gt;
var filename = &amp;quot;/gui/dialogs/test.xml&amp;quot;;&lt;br /&gt;
var path = FGRoot ~ filename;&lt;br /&gt;
&lt;br /&gt;
var blob = io.read_properties(path);&lt;br /&gt;
var script = blob.getValues().nasal; # Get the nasal string&lt;br /&gt;
&lt;br /&gt;
# Compile the script.  We're passing the filename here for better runtime diagnostics &lt;br /&gt;
var code = call(func {&lt;br /&gt;
    compile(script, filename);&lt;br /&gt;
}, nil, nil, var compilation_errors = []);&lt;br /&gt;
&lt;br /&gt;
if(size(compilation_errors)){&lt;br /&gt;
    die(&amp;quot;Error compiling code in: &amp;quot; ~ filename);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# Invoke the compiled script, equivalent to code(); &lt;br /&gt;
# We're using call() here to detect errors:&lt;br /&gt;
call(code, [], nil, nil, var runtime_errors = []);&lt;br /&gt;
&lt;br /&gt;
if(size(runtime_errors)){&lt;br /&gt;
    die(&amp;quot;Error calling code compiled loaded from: &amp;quot; ~ filename);&lt;br /&gt;
}&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== contains() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = contains(hash, key);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=184|t=Source}}&lt;br /&gt;
|text = Returns 1 (True) if the hash contains the specified key, or 0 (False) if not.&lt;br /&gt;
|param1 = hash&lt;br /&gt;
|param1text = The hash to search in.&lt;br /&gt;
|param2 = key&lt;br /&gt;
|param2text = The scalar to be searched for, contained as a key in the hash.&lt;br /&gt;
|example1 =&lt;br /&gt;
# Initialize a hash&lt;br /&gt;
var hash = {&lt;br /&gt;
    element: &amp;quot;value&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
print(contains(hash, &amp;quot;element&amp;quot;) ? &amp;quot;Yes&amp;quot; : &amp;quot;No&amp;quot;); # This will print &amp;quot;Yes&amp;quot;&lt;br /&gt;
|example2 =&lt;br /&gt;
# Initialize a hash&lt;br /&gt;
var hash = {&lt;br /&gt;
    element: &amp;quot;value&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
print(contains(hash, &amp;quot;element2&amp;quot;) ? &amp;quot;Yes&amp;quot; : &amp;quot;No&amp;quot;); # This will print &amp;quot;No&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== delete() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = delete(hash, key);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=83|t=Source}}&lt;br /&gt;
|text = Deletes the key from the hash if it exists. Operationally, this is NOT identical to setting the hash value specified by the key to &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; as the key will stay in the hash (at least for a while). This variant potentially frees storage by deleting the reference to the key and by shrinking the hash.  Returns the hash that has been operated on.&lt;br /&gt;
|param1 = hash&lt;br /&gt;
|param1text = The hash from which to delete the key.&lt;br /&gt;
|param2 = key&lt;br /&gt;
|param2text = The scalar to be deleted, contained as a key in the hash.&lt;br /&gt;
|example1 =&lt;br /&gt;
# Initialize the hash&lt;br /&gt;
var hash = {&lt;br /&gt;
    element1: &amp;quot;value1&amp;quot;,&lt;br /&gt;
    element2: &amp;quot;value2&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
delete(hash, &amp;quot;element1&amp;quot;); # Delete element1&lt;br /&gt;
debug.dump(hash); # prints the hash, which is now minus element1&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== die() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = die(error);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=288|t=Source}}&lt;br /&gt;
|text = Terminates execution and unwinds the stack.  The place and the line will be added to the '''error'''.  This invokes the same internal exception handler used for internal runtime errors. Use this to signal fatal errors, or to implement exception handling. The error thrown (including internal runtime errors) can be caught with {{func link|call()}}.&lt;br /&gt;
|param1 = error&lt;br /&gt;
|param1text = String describing the error.&lt;br /&gt;
:{{inote|This parameter is technically optional, but it is highly recommended to use it.}}&lt;br /&gt;
|example1 = &lt;br /&gt;
print(&amp;quot;Will print&amp;quot;);&lt;br /&gt;
die(&amp;quot;Don't go any further!&amp;quot;); &lt;br /&gt;
print(&amp;quot;Won't print&amp;quot;); # Will not be printed because die() stops the process&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== find() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = find(needle, haystack);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=450|t=Source}}&lt;br /&gt;
|text = Finds and returns the index of the first occurrence of the string '''needle''' in the string '''haystack''', or -1 if no such occurrence was found.&lt;br /&gt;
|param1 = needle&lt;br /&gt;
|param1text = String to search for.&lt;br /&gt;
|param2 = haystack&lt;br /&gt;
|param2text = String to search in.&lt;br /&gt;
|example1 = print(find(&amp;quot;c&amp;quot;, &amp;quot;abcdef&amp;quot;)); # prints 2&lt;br /&gt;
|example2 = print(find(&amp;quot;x&amp;quot;, &amp;quot;abcdef&amp;quot;)); # prints -1&lt;br /&gt;
|example3 = print(find(&amp;quot;cd&amp;quot;, &amp;quot;abcdef&amp;quot;)); # prints 2&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== ghosttype() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = ghosttype(ghost);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=207|t=Source}}&lt;br /&gt;
|text = Returns a string containing either a descriptive name of a ghost (a raw C/C++ object), or a unique id (the pointer to the C/C++ &amp;lt;code&amp;gt;naGhostType&amp;lt;/code&amp;gt; instance) if no name has been set.  Ghost is an acronym that stands for '''G'''arbage-collected '''H'''andle to '''O'''ut'''S'''ide '''T'''hingy.&lt;br /&gt;
|param1 = ghost&lt;br /&gt;
|param1text = Ghost to return a description for.&lt;br /&gt;
|example1 = print(ghosttype(airportinfo())); # prints &amp;quot;airport&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== id() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = id(object);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=570|t=Source}}&lt;br /&gt;
|text = Returns a string containing information on the type and ID of the object provided in the single argument.  The information is returned in the form of &amp;lt;code&amp;gt;'''&amp;lt;type&amp;gt;''':'''&amp;lt;id&amp;gt;'''&amp;lt;/code&amp;gt;, where '''&amp;lt;type&amp;gt;''' is the type of object, and '''&amp;lt;id&amp;gt;''' is the ID.&lt;br /&gt;
|param1 = object&lt;br /&gt;
|param1text = Can be either of a string, a vector, a hash, a code, a function, or a ghost.&lt;br /&gt;
|example1 = print(id(&amp;quot;A&amp;quot;)); # prints &amp;quot;str:000000001624A590&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== int() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = int(number);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=90|t=Source}}&lt;br /&gt;
|text = Returns the integer part of the numeric value of the single argument, or &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; if none exists.&lt;br /&gt;
|param1 = number&lt;br /&gt;
|param1text = Number or string with just a number in it to return an integer from.&lt;br /&gt;
|example1 = print(int(23)); # prints &amp;quot;23&amp;quot;&lt;br /&gt;
|example2 = print(int(23.123)); # prints &amp;quot;23&amp;quot;&lt;br /&gt;
|example3 = debug.dump(int(&amp;quot;string&amp;quot;)); # prints &amp;quot;nil&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== keys() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = keys(hash);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=33|t=Source}}&lt;br /&gt;
|text = Returns a vector containing the list of keys found in the single hash argument. &lt;br /&gt;
|param1 = hash&lt;br /&gt;
|param1text = The hash to return the keys from.&lt;br /&gt;
|example1 = &lt;br /&gt;
# Initialize a hash&lt;br /&gt;
var hash = {&lt;br /&gt;
    element1: &amp;quot;value&amp;quot;,&lt;br /&gt;
    element2: &amp;quot;value&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
debug.dump(keys(hash)); # print the vector&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== left() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = left(string, length);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=149|t=Source}}&lt;br /&gt;
|version = 2.12&lt;br /&gt;
|commit = {{simgear commit|bd7163|t=commit}}&lt;br /&gt;
|text = Returns a substring of '''string''', starting from the left.&lt;br /&gt;
|param1 = string&lt;br /&gt;
|param1text = String to return part of.&lt;br /&gt;
|param2 = length&lt;br /&gt;
|param2text = Integer specifying the length of the substring to return.&lt;br /&gt;
|example1 = print(left(&amp;quot;string&amp;quot;, 2)); # prints &amp;quot;st&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== num() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = num(number);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=102|t=Source}}&lt;br /&gt;
|text = Returns the numerical value of the single string argument, or &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; if none exists. &lt;br /&gt;
|param1 = number&lt;br /&gt;
|param1text = String with just a number in it to return a number from.&lt;br /&gt;
|example1 = print(num(&amp;quot;23&amp;quot;)); # prints &amp;quot;23&amp;quot;&lt;br /&gt;
|example2 = print(num(&amp;quot;23.123&amp;quot;)); # prints &amp;quot;23.123&amp;quot;&lt;br /&gt;
|example3 = debug.dump(num(&amp;quot;string&amp;quot;)); # prints &amp;quot;nil&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== pop() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = pop(vector);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=50|t=Source}}&lt;br /&gt;
|text = Removes and returns the last element of the single vector argument, or &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; if the vector is empty. &lt;br /&gt;
|param1 = vector&lt;br /&gt;
|param1text = Vector to remove an element from.&lt;br /&gt;
|example1 = &lt;br /&gt;
var vector = [1, 2, 3];&lt;br /&gt;
pop(vector);&lt;br /&gt;
debug.dump(vector); # prints &amp;quot;[1, 2]&amp;quot;&lt;br /&gt;
|example2 = &lt;br /&gt;
var vector = [1, 2, 3];&lt;br /&gt;
debug.dump(pop(vector)); # prints &amp;quot;3&amp;quot;&lt;br /&gt;
|example3 = &lt;br /&gt;
var vector = [];&lt;br /&gt;
debug.dump(pop(vector)); # prints &amp;quot;nil&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== right() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = right(string, length);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=161|t=Source}}&lt;br /&gt;
|version = 2.12&lt;br /&gt;
|commit = {{simgear commit|bd7163|t=commit}}&lt;br /&gt;
|text = Returns a substring of '''string''', starting from the right.&lt;br /&gt;
|param1 = string&lt;br /&gt;
|param1text = String to return part of.&lt;br /&gt;
|param2 = length&lt;br /&gt;
|param2text = Integer specifying the length of the substring to return.&lt;br /&gt;
|example1 = print(right(&amp;quot;string&amp;quot;, 2)); # prints &amp;quot;ng&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== setsize() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = setsize(vector, size);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=56|t=Source}}&lt;br /&gt;
|text = Sets the size of a vector. The first argument specifies a vector, the second a number representing the desired size of that vector. If the vector is currently larger than the specified size, it is truncated. If it is smaller, it is padded with &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; entries. Returns the vector operated upon. &lt;br /&gt;
|param1 = vector&lt;br /&gt;
|param1text = The vector to be operated on.&lt;br /&gt;
|param2 = size&lt;br /&gt;
|param2text = The desired size of the vector in number of entries.&lt;br /&gt;
|example1 = &lt;br /&gt;
var vector = [1, 2, 3]; # Initialize a vector&lt;br /&gt;
setsize(vector, 4);&lt;br /&gt;
debug.dump(vector); # print the vector&lt;br /&gt;
|example2 = &lt;br /&gt;
var vector = [1, 2, 3]; # Initialize a vector&lt;br /&gt;
setsize(vector, 2);&lt;br /&gt;
debug.dump(vector); # print the vector&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== size() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = size(object);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=23|t=Source}}&lt;br /&gt;
|text = Returns the size of the single argument. For strings, this is the length in bytes. For vectors, this is the number of elements. For hashes, it is the number of key/value pairs. If the argument is &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; or a number, this error will be thrown: &amp;lt;code&amp;gt;object has no size()&amp;lt;/code&amp;gt;.&lt;br /&gt;
|param1 = object&lt;br /&gt;
|param1text = Object to find the size of.  Must be a string, a vector or a hash.&lt;br /&gt;
|example1 = &lt;br /&gt;
var string = &amp;quot;string&amp;quot;;&lt;br /&gt;
print(size(string)); # prints &amp;quot;6&amp;quot;&lt;br /&gt;
|example2 =&lt;br /&gt;
var vector = [1, 2, 3];&lt;br /&gt;
print(size(vector)); # prints &amp;quot;3&amp;quot;&lt;br /&gt;
|example3 =&lt;br /&gt;
var hash = {&lt;br /&gt;
    element1: &amp;quot;value1&amp;quot;,&lt;br /&gt;
    element2: &amp;quot;value2&amp;quot;,&lt;br /&gt;
    element3: &amp;quot;value3&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
print(size(hash)); # prints &amp;quot;3&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== sort() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = sort(vector, function);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=542|t=Source}}&lt;br /&gt;
|text = Returns a vector containing the elements in the input '''vector''' sorted in according to the rule given by '''function'''. Implemented with the ANSI C {{func link|qsort()|link=http://www.cplusplus.com/reference/cstdlib/qsort/}}, &amp;lt;code&amp;gt;sort()&amp;lt;/code&amp;gt; is stable.  This means that if the rules in the first example are used, equal elements in the output vector will appear in the same relative order as they do in the input.  It is run in a loop, so '''function''' is run several times.&lt;br /&gt;
|param1 = vector&lt;br /&gt;
|param1text = Input vector to sort.&lt;br /&gt;
|param2 = function&lt;br /&gt;
|param2text = Function according to which the elements will be sorted by.  It should take two arguments and should return one of 1, 0, or -1.&lt;br /&gt;
{{{!}} class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Return value !! Meaning&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} less than 0 {{!!}} first argument should go before second argument&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} 0 {{!!}} first argument equals second argument&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} greater than 0 {{!!}} first argument should go after second argument&lt;br /&gt;
{{!}}}&lt;br /&gt;
&lt;br /&gt;
|example1text = This example sorts elements from smallest to greatest.&lt;br /&gt;
|example1 = &lt;br /&gt;
var sort_rules = func(a, b){&lt;br /&gt;
    if(a &amp;lt; b){&lt;br /&gt;
        return -1; # A should before b in the returned vector&lt;br /&gt;
    }elsif(a == b){&lt;br /&gt;
        return 0; # A is equivalent to b &lt;br /&gt;
    }else{&lt;br /&gt;
        return 1; # A should after b in the returned vector&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
debug.dump(sort([3, 2, 5, 6, 4, 1], sort_rules)); # prints &amp;quot;[1, 2, 3, 4, 5, 6]&amp;quot;&lt;br /&gt;
|example2text = This example sorts elements from greatest to smallest.&lt;br /&gt;
|example2 = &lt;br /&gt;
# Outputs the elements in reverse order (greatest to smallest)&lt;br /&gt;
var sort_rules = func(a, b){&lt;br /&gt;
    if(a &amp;lt; b){&lt;br /&gt;
        return 1; # -1 in the above example&lt;br /&gt;
    }elsif(a == b){&lt;br /&gt;
        return 0;&lt;br /&gt;
    }else{&lt;br /&gt;
        return -1; # 1 in the above example&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
debug.dump(sort([3, 2, 5, 6, 4, 1], sort_rules)); # prints &amp;quot;[6, 5, 4, 3, 2, 1]&amp;quot;&lt;br /&gt;
|example3text = This example sorts a vector of strings (runways for example) from smallest to greatest.&lt;br /&gt;
|example3 = &lt;br /&gt;
var runways = [&amp;quot;09R&amp;quot;,&amp;quot;27R&amp;quot;,&amp;quot;26L&amp;quot;,&amp;quot;09L&amp;quot;,&amp;quot;15&amp;quot;];&lt;br /&gt;
var rwy = sort(runways,func(a,b) cmp(a,b));&lt;br /&gt;
debug.dump(rwy); # prints ['09L','09R','15','26L','27R']&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== split() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = split(delimiter, string);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=460|t=Source}}&lt;br /&gt;
|text = Splits the input string into a vector of substrings bounded by occurrences of the delimiter substring.&lt;br /&gt;
|param1 = delimiter&lt;br /&gt;
|param1text = String that will split the substrings in the returned vector.&lt;br /&gt;
|param2 = string&lt;br /&gt;
|param2text = String to split up.&lt;br /&gt;
|example1 = debug.dump(split(&amp;quot;cd&amp;quot;, &amp;quot;abcdef&amp;quot;)); # prints &amp;quot;['ab', 'ef']&amp;quot;&lt;br /&gt;
|example2 = debug.dump(split(&amp;quot;.&amp;quot;, &amp;quot;3.2.0&amp;quot;)); # prints &amp;quot;[3, 2, 0]&amp;quot;&lt;br /&gt;
|example3 = debug.dump(split(&amp;quot;/&amp;quot;, &amp;quot;path/to/file&amp;quot;)); # prints &amp;quot;['path', 'to', 'file']&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== sprintf() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = sprintf(format[, arg[, arg, [...]]]);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=355|t=Source}}&lt;br /&gt;
|text = Creates and returns a string formatted using ANSI C {{func link|vsnprintf()|link=http://en.cppreference.com/w/c/io/vfprintf}} &amp;lt;ref&amp;gt;&lt;br /&gt;
{{Cite web&lt;br /&gt;
|url = http://sourceforge.net/p/flightgear/simgear/ci/next/tree/simgear/nasal/lib.c#l308&lt;br /&gt;
|title = fgdata/simgear/simgear/nasal/lib.c, line 308&lt;br /&gt;
|accessdate = October 2015&lt;br /&gt;
}}&lt;br /&gt;
&amp;lt;/ref&amp;gt;.  Below is a table of supported format specifiers.&lt;br /&gt;
{{{!}} class=&amp;quot;wikitable&amp;quot; width=&amp;quot;75%&amp;quot;&lt;br /&gt;
{{!}}+ %[flags][width][.precision]specifier&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; {{!}} Flags&lt;br /&gt;
{{!-}}&lt;br /&gt;
! Flag !! Output&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;+&amp;lt;/code&amp;gt; {{!!}} Forces to precede the result with a plus or minus sign ('''+''' or '''-''') even for positive numbers. By default, only negative numbers are preceded with a '''-''' sign.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} ''space'' {{!!}} Prefixes non-signed numbers with a space.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;-&amp;lt;/code&amp;gt; {{!!}} Left-align the output of this placeholder (the default is to right-align the output) when the width option is specified.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt; {{!!}} Use 0 instead of spaces to pad a field when the width option is specified.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;#&amp;lt;/code&amp;gt; {{!!}} Used with &amp;lt;code&amp;gt;o&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;X&amp;lt;/code&amp;gt; specifiers the value is preceded with &amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;0x&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;0X&amp;lt;/tt&amp;gt; respectively for values different than zero. Used with &amp;lt;code&amp;gt;e&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;E&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;f&amp;lt;/code&amp;gt;, it forces the written output to contain a decimal point even if no digits would follow. By default, if no digits follow, no decimal point is written. Used with &amp;lt;code&amp;gt;g&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;G&amp;lt;/code&amp;gt; the result is the same as with &amp;lt;code&amp;gt;e&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;E&amp;lt;/code&amp;gt; but trailing zeros are not removed.&lt;br /&gt;
{{!-}}&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; {{!}} Width&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} colspan=&amp;quot;2&amp;quot; {{!}} Integer specifying the minimum number of characters to be returned. This includes the decimal point and decimal fraction.&lt;br /&gt;
{{!-}}&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; {{!}} Precision&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} colspan=&amp;quot;2&amp;quot; {{!}} Integer preceded by a dot specifying the number of decimal places to be written.&lt;br /&gt;
{{!-}}&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; {{!}} Specifiers&lt;br /&gt;
{{!-}}&lt;br /&gt;
! Specifier !! Output&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;d&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;i&amp;lt;/code&amp;gt; {{!!}} Signed decimal number.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;s&amp;lt;/code&amp;gt; {{!!}} A string&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;%&amp;lt;/code&amp;gt; {{!!}} Percent (%) character.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;c&amp;lt;/code&amp;gt; {{!!}} A single character assigned to a character code, the code given in an integer argument.  See http://www.asciitable.com/ for a list of supported characters and their codes.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;o&amp;lt;/code&amp;gt; {{!!}} Unsigned integer as an octal number.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;u&amp;lt;/code&amp;gt; {{!!}} Unsigned decimal integer.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;X&amp;lt;/code&amp;gt; {{!!}} Unsigned integer as a hexadecimal number.  If &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt; is used, any letters in the number are lowercase, while &amp;lt;code&amp;gt;X&amp;lt;/code&amp;gt; gives uppercase.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;e&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;E&amp;lt;/code&amp;gt; {{!!}} Double value in scientific notation (i.e., ''[-]ddd.ddd'''e'''[+/-]ddd''), with an exponent being denoted by &amp;lt;tt&amp;gt;e&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;E&amp;lt;/tt&amp;gt; depending on whether an upper or lowercase is used respectively.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;f&amp;lt;/code&amp;gt; {{!!}} Floating-point number, in fixed decimal notation, by default with 6 decimal places.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;F&amp;lt;/code&amp;gt; {{!!}} Appears to be available&amp;lt;ref&amp;gt;&lt;br /&gt;
{{Cite web&lt;br /&gt;
|url = http://sourceforge.net/p/flightgear/simgear/ci/next/tree/simgear/nasal/lib.c#l389&lt;br /&gt;
|title = fgdata/simgear/simgear/nasal/lib.c, line 389&lt;br /&gt;
|accessdate = October 2015&lt;br /&gt;
}}&lt;br /&gt;
&amp;lt;/ref&amp;gt;, but doesn't work.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;g&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;G&amp;lt;/code&amp;gt; {{!!}} Double in either normal or exponential notation, whichever is more appropriate for its magnitude. &amp;lt;code&amp;gt;g&amp;lt;/code&amp;gt; uses lower-case letters, &amp;lt;code&amp;gt;G&amp;lt;/code&amp;gt; uses upper-case letters. This type differs slightly from fixed-point notation in that insignificant zeroes to the right of the decimal point are not included.  Also, the decimal point is not included on whole numbers.&lt;br /&gt;
{{!}}}&lt;br /&gt;
&lt;br /&gt;
|param1 = format&lt;br /&gt;
|param1text = String specifying the format.  Can be used with or without a format specifiers.  See below for examples.&lt;br /&gt;
|param2 = arg&lt;br /&gt;
|param2text = Argument specifying a value to replace a format placeholder (such as &amp;lt;code&amp;gt;%d&amp;lt;/code&amp;gt;) in the format string.  Not required if there are no format specifiers.&lt;br /&gt;
&lt;br /&gt;
|example1 = print(sprintf(&amp;quot;%i&amp;quot;, 54)); # prints &amp;quot;54&amp;quot;&lt;br /&gt;
|example2 = print(sprintf(&amp;quot;Pi = %+.10f&amp;quot;, math.pi)); # prints &amp;quot;Pi = +3.1415926536&amp;quot;&lt;br /&gt;
|example3 = &lt;br /&gt;
print(sprintf(&amp;quot;%6d&amp;quot;, 23)); # prints &amp;quot;    23&amp;quot;&lt;br /&gt;
print(sprintf(&amp;quot;%06d&amp;quot;, 23)); # prints &amp;quot;000023&amp;quot;&lt;br /&gt;
|example4 =&lt;br /&gt;
var FGVer = getprop(&amp;quot;/sim/version/flightgear&amp;quot;);&lt;br /&gt;
print(sprintf(&amp;quot;You have FlightGear v%s&amp;quot;, FGVer)); # prints &amp;quot;You have FlightGear v&amp;lt;your version&amp;gt;&amp;quot;&lt;br /&gt;
|example5 = &lt;br /&gt;
print(sprintf(&amp;quot;Hexadecimal 100000 = %X&amp;quot;, 100000)); # prints &amp;quot;Hexadecimal 100000 = 186A0&amp;quot;&lt;br /&gt;
print(sprintf(&amp;quot;Hexadecimal 100000 = %x&amp;quot;, 100000)); # prints &amp;quot;Hexadecimal 100000 = 186a0&amp;quot;&lt;br /&gt;
|example6 = print(sprintf(&amp;quot;Code 65 is %c&amp;quot;, 65)); # prints &amp;quot;Code 65 is A&amp;quot;&lt;br /&gt;
|example7 = &lt;br /&gt;
print(sprintf(&amp;quot;%e&amp;quot;, 54)); # prints &amp;quot;5.400000e+001&amp;quot;&lt;br /&gt;
print(sprintf(&amp;quot;%E&amp;quot;, 54)); # prints &amp;quot;5.400000E+001&amp;quot;&lt;br /&gt;
|example8 = print(sprintf(&amp;quot;%o&amp;quot;, 54)); # prints &amp;quot;66&amp;quot;&lt;br /&gt;
|example9 = print(sprintf(&amp;quot;50%% of 100 is %i&amp;quot;, 100 / 2)); # prints &amp;quot;50% of 100 is 50&amp;quot;&lt;br /&gt;
|example10 =&lt;br /&gt;
print(sprintf(&amp;quot;%.2f&amp;quot;, 1.4));   #prints &amp;quot;1.40&amp;quot;&lt;br /&gt;
print(sprintf(&amp;quot;%.1f&amp;quot;, 1.4));   #prints &amp;quot;1.4&amp;quot;&lt;br /&gt;
print(sprintf(&amp;quot;% 4.1f&amp;quot;, 1.4)); #prints &amp;quot; 1.4&amp;quot;&lt;br /&gt;
print(sprintf(&amp;quot;%04.1f&amp;quot;, 1.4)); #prints &amp;quot;01.4&amp;quot;&lt;br /&gt;
print(sprintf(&amp;quot;% 6.1f&amp;quot;, 1.4)); #prints &amp;quot;   1.4&amp;quot;&lt;br /&gt;
print(sprintf(&amp;quot;%06.1f&amp;quot;, 1.4)); #prints &amp;quot;0001.4&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== streq() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = streq(a, b);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=107|t=Source}}&lt;br /&gt;
|text = Tests the string values of the two arguments for equality. This function is needed because the &amp;lt;code&amp;gt;'''=='''&amp;lt;/code&amp;gt; operator (see [[Nasal_Operators#Equality|Nasal Operators]]) tests for numeric equality first.  If either or both the arguments are not strings, 0 (False) will be returned.  Returns either 0 (False) or 1 (True).  {{Note|This function is rarely required in typical code.}}&lt;br /&gt;
|param1 = a&lt;br /&gt;
|param1text = First argument for testing equality.&lt;br /&gt;
|param2 = b&lt;br /&gt;
|param2text = Second argument for testing equality.&lt;br /&gt;
|example1 = print(streq(&amp;quot;0&amp;quot;, &amp;quot;0&amp;quot;)); # prints &amp;quot;1&amp;quot; (True)&lt;br /&gt;
|example2 = &lt;br /&gt;
print(0 == 0.0); # prints &amp;quot;1&amp;quot; (True)&lt;br /&gt;
print(streq(&amp;quot;0&amp;quot;, &amp;quot;0.0&amp;quot;)); # prints &amp;quot;0&amp;quot; (False)&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== substr() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = substr(string, start [, length]);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=129|t=Source}}&lt;br /&gt;
|text = Similar the {{func link|subvec()}}, but operates on strings. Computes and returns a substring. The first argument specifies a string, the second is the index of the start of a substring, the optional third argument specifies a length (the default is to return the rest of the string from the start).&lt;br /&gt;
|param1 = string&lt;br /&gt;
|param1text = String to return a substring from.&lt;br /&gt;
|param2 = start&lt;br /&gt;
|param2text = Integer specifying the start of a substring. Negative values specify a position from the end of the string.&lt;br /&gt;
|param3 = length&lt;br /&gt;
|param3text = Optional argument specifying the length of the substring. Defaults to the end of the string.&lt;br /&gt;
|example1 = print(substr(&amp;quot;abcde&amp;quot;, 1, 3)); # prints &amp;quot;bcd&amp;quot;&lt;br /&gt;
|example2 = print(substr(&amp;quot;abcde&amp;quot;, 1)); # prints &amp;quot;bcde&amp;quot;&lt;br /&gt;
|example3 = print(substr(&amp;quot;abcde&amp;quot;, 2, 1)); # prints &amp;quot;c&amp;quot;&lt;br /&gt;
|example4 = print(substr(&amp;quot;abcde&amp;quot;, -2)); # prints &amp;quot;de&amp;quot;&lt;br /&gt;
|example5 = print(substr(&amp;quot;abcde&amp;quot;, -3, 2)); # prints &amp;quot;cd&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== subvec() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = subvec(vector, start[, length]);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=63|t=Source}}&lt;br /&gt;
|text = Returns a sub-range of a vector. The first argument specifies a vector, the second a starting index, and the optional third argument indicates a length (the default is to the end of the vector). &lt;br /&gt;
|param1 = vector&lt;br /&gt;
|param1text = The vector to take the sub-vector from.&lt;br /&gt;
|param2 = start&lt;br /&gt;
|param2text = The starting point of the sub-vector within the given vector.&lt;br /&gt;
|param3 = length&lt;br /&gt;
|param3text = Optional argument specifying the length of the sub-vector, from the starting point.&lt;br /&gt;
'''Notes:'''&lt;br /&gt;
* Omitting the ''vector'' and ''start'' arguments is not an error (possibly it should be) but the return value is ''nil''.&lt;br /&gt;
* A negative ''start'' argument ''is'' an error. This seems wrong. Perhaps the language designer could comment.&lt;br /&gt;
* A value of ''start'' greater than ''size(vector)'' causes an error. A value equal to ''size(vector)'' returns an empty vector.&lt;br /&gt;
* If the value of ''length'' is greater than ''size(vector) - start'' then it is ignored. That is, all elements from ''start'' to the end of ''vector'' are returned. If ''length'' is zero then an empty vector is returned. A negative value of ''length'' causes an error.&lt;br /&gt;
|example1 = &lt;br /&gt;
var vector = [1, 2, 3];&lt;br /&gt;
debug.dump(subvec(vector, 0)); # prints &amp;quot;[1, 2, 3]&amp;quot;&lt;br /&gt;
|example2 = &lt;br /&gt;
var vector = [1, 2, 3];&lt;br /&gt;
debug.dump(subvec(vector, 1)); # prints &amp;quot;[2, 3]&amp;quot;&lt;br /&gt;
|example3 = &lt;br /&gt;
var vector = [1, 2, 3];&lt;br /&gt;
debug.dump(subvec(vector, 1, 1)); # prints &amp;quot;[2]&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== typeof() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = typeof(object);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/lib.c|l=193|t=Source}}&lt;br /&gt;
|text = Returns a string indicating the whether the object is &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt;, a scalar (number or string), a vector, a hash, a function, or a ghost.&lt;br /&gt;
|param1 = object&lt;br /&gt;
|param1text = Object to return the type of.&lt;br /&gt;
|example1 = &lt;br /&gt;
var object = nil;&lt;br /&gt;
print(typeof(object)); # prints &amp;quot;nil&amp;quot;&lt;br /&gt;
|example2 = &lt;br /&gt;
var object = &amp;quot;Hello world!&amp;quot;;&lt;br /&gt;
print(typeof(object)); # prints &amp;quot;scalar&amp;quot;&lt;br /&gt;
|example3 = &lt;br /&gt;
var object = math.pi;&lt;br /&gt;
print(typeof(object)); # prints &amp;quot;scalar&amp;quot;&lt;br /&gt;
|example4 = &lt;br /&gt;
var object = [1, 2, 3];&lt;br /&gt;
print(typeof(object)); # prints &amp;quot;vector&amp;quot;&lt;br /&gt;
|example5 = &lt;br /&gt;
var object = {};&lt;br /&gt;
print(typeof(object)); # prints &amp;quot;hash&amp;quot;&lt;br /&gt;
|example6 = &lt;br /&gt;
var object = func {};&lt;br /&gt;
print(typeof(object)); # prints &amp;quot;func&amp;quot;&lt;br /&gt;
|example7 =&lt;br /&gt;
var object = airportinfo();&lt;br /&gt;
print(typeof(object)); # prints &amp;quot;ghost&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!-- == Extension modules ==&lt;br /&gt;
=== thread ===&lt;br /&gt;
{{WIP}}&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = thread.newthread(func);&lt;br /&gt;
|source = {{simgear file|simgear/nasal/threadlib.c|l=101|t=Source}}&lt;br /&gt;
|text = &lt;br /&gt;
|example1text = start a new worker thread&lt;br /&gt;
|example1 = thread.newthread( func() {} );&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = thread.newlock();&lt;br /&gt;
|source = {{simgear file|simgear/nasal/threadlib.c|l=101|t=Source}}&lt;br /&gt;
|text = &lt;br /&gt;
|example1text = create a new lock&lt;br /&gt;
|example1 = var lock = thread.newlock()&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = thread.lock();&lt;br /&gt;
|source = {{simgear file|simgear/nasal/threadlib.c|l=101|t=Source}}&lt;br /&gt;
|text = &lt;br /&gt;
|example1text = lock a lock&lt;br /&gt;
|example1 = var lock = thread.newlock()&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = thread.unlock();&lt;br /&gt;
|source = {{simgear file|simgear/nasal/threadlib.c|l=101|t=Source}}&lt;br /&gt;
|text = &lt;br /&gt;
|example1text = unlock a lock&lt;br /&gt;
|example1 = var lock = thread.unlock()&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = thread.newsem();&lt;br /&gt;
|source = {{simgear file|simgear/nasal/threadlib.c|l=101|t=Source}}&lt;br /&gt;
|text = &lt;br /&gt;
|example1text = create a new {{Wikipedia|semaphore}}&lt;br /&gt;
|example1 = var semaphore = thread.newsem()&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = thread.semdown();&lt;br /&gt;
|source = {{simgear file|simgear/nasal/threadlib.c|l=101|t=Source}}&lt;br /&gt;
|text = &lt;br /&gt;
|example1text = semaphore down&lt;br /&gt;
|example1 = thread.semdown(semaphore)&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = thread.semup();&lt;br /&gt;
|source = {{simgear file|simgear/nasal/threadlib.c|l=101|t=Source}}&lt;br /&gt;
|text = &lt;br /&gt;
|example1text = semaphore up&lt;br /&gt;
|example1 = thread.semup(semaphore)&lt;br /&gt;
}} --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Extension functions ==&lt;br /&gt;
The '''extension functions''' are global functions that have been added to Nasal since its integration into FlightGear. Unlike the core library functions, they are generally specifically designed to interact directly with FlightGear. Extension functions come from three source files:&lt;br /&gt;
* {{flightgear file|src/Scripting/NasalPositioned.cxx}}&lt;br /&gt;
* {{flightgear file|src/Scripting/NasalSys.cxx}}&lt;br /&gt;
* {{fgdata file|Nasal/globals.nas}}&lt;br /&gt;
&lt;br /&gt;
=== abort() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = abort();&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=565|t=Source}}&lt;br /&gt;
|text = This function is a wrapper for the C++ {{func link|abort()|link=http://www.cplusplus.com/reference/cstdlib/abort/}} function. It simply aborts FlightGear with an error, which varies depending on the operating system. This function should not really be used; instead, please use the &amp;quot;exit&amp;quot; [[Fgcommands|fgcommand]], which will exit FlightGear more gracefully (see example below).&lt;br /&gt;
|example1text = This example will immediately stop FlightGear with an error, such as &amp;quot;FlightGear has stopped working.&amp;quot;&lt;br /&gt;
|example1 = abort();&lt;br /&gt;
|example2text = For exiting FlightGear in a better way, please use the following code:&lt;br /&gt;
|example2 = fgcommand(&amp;quot;exit&amp;quot;);&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== abs() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = abs(number);&lt;br /&gt;
|source = {{fgdata file|Nasal/globals.nas|t=Source}}&lt;br /&gt;
|text = This simple function returns the {{wikipedia|absolute value|noicon=1}} of the provided number.&lt;br /&gt;
|param1 = number&lt;br /&gt;
|param1text = This argument is required and should be a number.&lt;br /&gt;
|example1 = print(abs(1)); # prints &amp;quot;1&amp;quot;&lt;br /&gt;
|example2 = print(abs(-1)); # prints &amp;quot;1&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== aircraftToCart() ===&lt;br /&gt;
This new function in FG 2017.2.1 takes coordinates in aircraft structural coordinate system, and translate them into geocentric coordinates.&lt;br /&gt;
Example for (5,6,7):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
var pos = aircraftToCart({x: -5, y: 6, z: -7});&lt;br /&gt;
var coord = geo.Coord.new();&lt;br /&gt;
coord.set_xyz(pos.x, pos.y, pos.z);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Notice: x and z is inverted sign on purpose.&lt;br /&gt;
if you want lat. lon, alt from that, just call: (degrees and meters)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
coord.lat()&lt;br /&gt;
coord.lon()&lt;br /&gt;
coord.alt()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== addcommand() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = addcommand(name, code);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=659|t=Source}}&lt;br /&gt;
|version = 2.12&lt;br /&gt;
|commit = {{flightgear commit|7b663c|t=commit}}&lt;br /&gt;
|text = {{see also|Howto:Add new fgcommands to FlightGear}}&lt;br /&gt;
&lt;br /&gt;
This function enables the addition of a new custom [[fgcommands|fgcommand]] to FlightGear from within Nasal. An fgcommand created using this method can be used in exactly the same way as the built-in fgcommands. Also, an fgcommand created via this method will always return True or 1, like all other fgcommands.&lt;br /&gt;
|param1 = name&lt;br /&gt;
|param1text = This will become the name of the new fgcommand. Must be a string.&lt;br /&gt;
|param2 = code&lt;br /&gt;
|param2text = The code that will be executed when the fgcommand is run. Must be a function.&lt;br /&gt;
|example1text = This example adds a new fgcommand and then runs it. Although it executes a simple {{func link|print()}} statement, any valid Nasal code can be used.&lt;br /&gt;
|example1 = addcommand(&amp;quot;myFGCmd&amp;quot;, func(node) {&lt;br /&gt;
    print(&amp;quot;fgcommand 'myFGCmd' has been run.&amp;quot;);&lt;br /&gt;
    props.dump( node );&lt;br /&gt;
});&lt;br /&gt;
fgcommand(&amp;quot;myFGCmd&amp;quot;, props.Node.new({foo:1, bar:2}) );&lt;br /&gt;
|example2text = This example demonstrates how parameters are defined in a new fgcommand.&lt;br /&gt;
|example2 = addcommand(&amp;quot;myFGCmd&amp;quot;, func(node){&lt;br /&gt;
    print(node.getNode(&amp;quot;number&amp;quot;).getValue()); # prints the value of &amp;quot;number,&amp;quot; which is 12&lt;br /&gt;
});&lt;br /&gt;
fgcommand(&amp;quot;myFGCmd&amp;quot;, props.Node.new({&amp;quot;number&amp;quot;: 12}));&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== airportinfo() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = airportinfo();&lt;br /&gt;
airportinfo(type);&lt;br /&gt;
airportinfo(id);&lt;br /&gt;
airportinfo(lat, lon[, type]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1024|t=Source}}&lt;br /&gt;
|text = Function for retrieval of airport, heliport, or seaplane base information. It returns a Nasal ghost; however, its structure is like that of a Nasal hash. The following information is returned:&lt;br /&gt;
* '''parents''': A vector containing a hash of various functions to access information about the runway. See {{flightgear file|src/Scripting/NasalPositioned.cxx|l=2659}} for full list.&lt;br /&gt;
* '''lon''': Longitude of the location.&lt;br /&gt;
* '''lat''': Latitude of the location.&lt;br /&gt;
* '''has_metar''': True or false depending whether the airport has a [[METAR]] code defined for it.&lt;br /&gt;
* '''elevation''': Elevation of the location in metres.&lt;br /&gt;
* '''id''': ICAO code of the airport (or ID of the seaplane base/heliport).&lt;br /&gt;
* '''name''': Name of the airport/heliport/seaplane base.&lt;br /&gt;
* '''runways'''&lt;br /&gt;
** '''&amp;lt;runway name&amp;gt;'''&lt;br /&gt;
*** '''id''': Name of runway.&lt;br /&gt;
*** '''lat''': Latitude of the runway.&lt;br /&gt;
*** '''lon''': Longitude of the runway.&lt;br /&gt;
*** '''heading''': Heading of the runway.&lt;br /&gt;
*** '''length''': Length of the runway in metres.&lt;br /&gt;
*** '''width''': Width of the runway in metres.&lt;br /&gt;
*** '''surface''': Runway surface type.&lt;br /&gt;
*** '''threshold''': Length of the runway's {{wikipedia|displaced threshold}} in metres. Will return 0 if there is none.&lt;br /&gt;
*** '''stopway''': Length of the runway's stopway (the area before the threshold) in metres. Will return 0 if there is none.&lt;br /&gt;
*** '''reciprocal''': &amp;lt;code&amp;gt;runway&amp;lt;/code&amp;gt; ghost of the reciprocal runway.&lt;br /&gt;
*** '''ils_frequency_mhz''': ILS frequency in megahertz.&lt;br /&gt;
*** '''ils''': &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt; ghost of the ILS transmitter.&lt;br /&gt;
* '''helipads'''&lt;br /&gt;
** '''&amp;lt;helipad name&amp;gt;'''&lt;br /&gt;
*** '''id''': Name of helipad.&lt;br /&gt;
*** '''lat''': Latitude of the helipad.&lt;br /&gt;
*** '''lon''': Longitude of the helipad.&lt;br /&gt;
*** '''heading''': Heading of the helipad.&lt;br /&gt;
*** '''length''': Length of the helipad in metres.&lt;br /&gt;
*** '''width''': Width of the helipad in metres.&lt;br /&gt;
*** '''surface''': Helipad surface type.&lt;br /&gt;
* '''taxiways'''&lt;br /&gt;
** '''&amp;lt;taxiway name&amp;gt;'''&lt;br /&gt;
*** '''id''': Name of taxiway.&lt;br /&gt;
*** '''lat''': Latitude of the taxiway.&lt;br /&gt;
*** '''lon''': Longitude of the taxiway.&lt;br /&gt;
*** '''heading''': Heading of the taxiway.&lt;br /&gt;
*** '''length''': Length of the taxiway in metres.&lt;br /&gt;
*** '''width''': Width of the taxiway in metres.&lt;br /&gt;
*** '''surface''': Taxiway surface type.&lt;br /&gt;
&lt;br /&gt;
Information is extracted in the same way as accessing members of a Nasal hash. For example:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
# prints to lengths of the runways of the nearest airport in feet and metres&lt;br /&gt;
var info = airportinfo();&lt;br /&gt;
print(&amp;quot;-- Lengths of the runways at &amp;quot;, info.name, &amp;quot; (&amp;quot;, info.id, &amp;quot;) --&amp;quot;);&lt;br /&gt;
foreach(var rwy; keys(info.runways)){&lt;br /&gt;
    print(rwy, &amp;quot;: &amp;quot;, math.round(info.runways[rwy].length * M2FT), &amp;quot; ft (&amp;quot;, info.runways[rwy].length, &amp;quot; m)&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that searches for locations that are a long way away (e.g., the nearest seaplane base to the middle of the Sahara) may cause FlightGear to pause for an amount of time.&lt;br /&gt;
|param1 = id&lt;br /&gt;
|param1text = The {{wikipedia|International Civil Aviation Organization airport code|ICAO code|noicon=1}} of an airport to retrieve information about.&lt;br /&gt;
|param2 = type&lt;br /&gt;
|param2text = When this argument is used, the function will return the closest airport of a certain type. Can be one of &amp;quot;heliport,&amp;quot; &amp;quot;seaport,&amp;quot; or &amp;quot;airport&amp;quot; (default).&lt;br /&gt;
: {{inote|Running this function without any parameters is equivalent to this:&lt;br /&gt;
: &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
airportinfo(&amp;quot;airport&amp;quot;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
|param3 = lat ''and'' lon&lt;br /&gt;
|param3text = When these parameters are used, the function will return information on the nearest airport, heliport or seaplane base (depending on the '''type''' parameter) to those coordinates.&lt;br /&gt;
|example1 = var info = airportinfo();&lt;br /&gt;
print(&amp;quot;Nearest airport: &amp;quot;, info.name, &amp;quot; (&amp;quot;, info.id, &amp;quot;)&amp;quot;); # prints the name and ICAO code of the nearest airport&lt;br /&gt;
|example2 = var info = airportinfo(&amp;quot;heliport&amp;quot;);&lt;br /&gt;
print(&amp;quot;Elevation of the nearest heliport: &amp;quot;, math.round(info.elevation * M2FT), &amp;quot; ft&amp;quot;); # prints the elevation and name of the nearest heliport&lt;br /&gt;
|example3 = var info = airportinfo(&amp;quot;KSQL&amp;quot;);&lt;br /&gt;
print(&amp;quot;-- Runways of &amp;quot;, info.name, &amp;quot; (&amp;quot;, info.id, &amp;quot;): --&amp;quot;);&lt;br /&gt;
foreach(var rwy; keys(info.runways)) {&lt;br /&gt;
    print(rwy); # prints the runways of KSQL&lt;br /&gt;
}&lt;br /&gt;
|example4 = var info = airportinfo(37.81909385, -122.4722484);&lt;br /&gt;
print(&amp;quot;Coordinates of the nearest airport: &amp;quot;, info.lat, &amp;quot;, &amp;quot;, info.lon); # print the name and ICAO of the nearest airport to the Golden Gate Bridge&lt;br /&gt;
|example5 = var info = airportinfo(37.81909385, -122.4722484, &amp;quot;seaport&amp;quot;);&lt;br /&gt;
print(&amp;quot;Nearest seaplane base: &amp;quot;, info.name, &amp;quot; (&amp;quot;, info.id, &amp;quot;)&amp;quot;); # print the name and ID of the nearest seaplane base to the Golden Gate Bridge&lt;br /&gt;
|example6text = This example prints the all information from an &amp;lt;code&amp;gt;airportinfo()&amp;lt;/code&amp;gt; call.&lt;br /&gt;
|example6 = var info = airportinfo(&amp;quot;KSFO&amp;quot;);&lt;br /&gt;
print(info.name);&lt;br /&gt;
print(info.id);&lt;br /&gt;
print(info.lat);&lt;br /&gt;
print(info.lon);&lt;br /&gt;
print(info.has_metar);&lt;br /&gt;
print(info.elevation);&lt;br /&gt;
foreach(var rwy; keys(info.runways)){&lt;br /&gt;
    print(&amp;quot;-- &amp;quot;, rwy, &amp;quot; --&amp;quot;);&lt;br /&gt;
    print(info.runways[rwy].lat);&lt;br /&gt;
    print(info.runways[rwy].lon);&lt;br /&gt;
    print(info.runways[rwy].length);&lt;br /&gt;
    print(info.runways[rwy].width);&lt;br /&gt;
    print(info.runways[rwy].heading);&lt;br /&gt;
    print(info.runways[rwy].stopway);&lt;br /&gt;
    print(info.runways[rwy].threshold);&lt;br /&gt;
}&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== airwaysRoute() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = airwaysRoute(start, end[, type]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1933|t=Source}}&lt;br /&gt;
|text = {{see also|Nasal Flightplan}}&lt;br /&gt;
This function returns a vector containing waypoints between two given waypoints. The returned waypoints are ghosts, but can be accessed in the same way as a Nasal hash. See [[Nasal Flightplan]] for more information.&lt;br /&gt;
|param1 = start&lt;br /&gt;
|param1text = Start waypoint, in the form of a waypoint ghost, such as that provided by {{func link|flightplan()}}.&lt;br /&gt;
|param2 = end&lt;br /&gt;
|param2text = Same as above.&lt;br /&gt;
|param3 = type&lt;br /&gt;
|param3text = Instructs the function to compute a high level route (when set to &amp;quot;highlevel&amp;quot;), or a low level route (when set to &amp;quot;lowlevel&amp;quot;). Defaults to &amp;quot;highlevel.&amp;quot;&lt;br /&gt;
|example1text = In the [[route manager]] dialog, add two waypoints to the flightplan, ideally ones that are far apart (tip: use the [[Map]] for this). Then run this code in your Nasal Console.&lt;br /&gt;
|example1 = var fp = flightplan();&lt;br /&gt;
var start = fp.getWP(0);&lt;br /&gt;
var end = fp.getWP(fp.getPlanSize() - 1);&lt;br /&gt;
var rt = airwaysRoute(start, end);&lt;br /&gt;
foreach(var wp; rt){&lt;br /&gt;
    print(wp.wp_name); # print the waypoints in the computed route&lt;br /&gt;
}&lt;br /&gt;
|example2text = Exactly the same as above, but computes a low level path.&lt;br /&gt;
|example2 = var fp = flightplan();&lt;br /&gt;
var start = fp.getWP(0);&lt;br /&gt;
var end = fp.getWP(fp.getPlanSize() - 1);&lt;br /&gt;
var rt = airwaysRoute(start, end, &amp;quot;lowlevel&amp;quot;);&lt;br /&gt;
foreach(var wp; rt){&lt;br /&gt;
    print(wp.wp_name); # print the waypoints in the computed route&lt;br /&gt;
}&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== assert() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = assert(condition[, message]);&lt;br /&gt;
|source = {{fgdata file|Nasal/globals.nas|t=Source}}&lt;br /&gt;
|version = 3.2&lt;br /&gt;
|commit = {{fgdata commit|8b16a7|t=commit}}&lt;br /&gt;
|text = Returns either true if the condition evaluates as true, or aborts with a {{func link|die()}} call, which can be customised.&lt;br /&gt;
|param1 = condition&lt;br /&gt;
|param1text = Condition to evaluate.&lt;br /&gt;
|param2 = message&lt;br /&gt;
|param2text = Optional message that will be used in any {{func link|die()}} call. Defaults to &amp;quot;assertion failed!&amp;quot;&lt;br /&gt;
|example1 = var a = 1;&lt;br /&gt;
var b = 2;&lt;br /&gt;
print(assert(a &amp;lt; b)); # prints &amp;quot;1&amp;quot; (true)&lt;br /&gt;
|example2 = var a = 1;&lt;br /&gt;
var b = 2;&lt;br /&gt;
assert(a &amp;gt; b, 'a is not greater than b'); # aborts with a custom error message&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== carttogeod() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = carttogeod(x, y, z);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=945|t=Source}}&lt;br /&gt;
|text = Converts {{wikipedia|ECEF|Earth-centered, Earth-fixed}} coordinates (x, y and z) to {{wikipedia|geodetic coordinates}} (latitude, longitude, and altitude). A vector is returned containing latitude and longitude, both in degrees, and altitude, which is returned in metres above the equatorial radius of Earth as defined by the {{wikipedia|WGS 84}} (6,378,137 metres).&amp;lt;ref&amp;gt;{{simgear file|simgear/math/sg_geodesy.hxx|l=43}}&amp;lt;/ref&amp;gt;&lt;br /&gt;
|param1 = x&lt;br /&gt;
|param1text = Mandatory x-axis value, in metres.&lt;br /&gt;
|param2 = y&lt;br /&gt;
|param2text = Mandatory y-axis value, in metres.&lt;br /&gt;
|param3 = z&lt;br /&gt;
|param3text = Mandatory z-axis value, in metres.&lt;br /&gt;
|example1 = var (lat, lon, alt) = carttogeod(6378137, 0, 0); # point is the intersection of the prime meridian and equator.&lt;br /&gt;
print(&amp;quot;Latitude: &amp;quot;, lat); # prints lat, lon and alt, which are all zero, see above&lt;br /&gt;
print(&amp;quot;Longitude: &amp;quot;, lon);&lt;br /&gt;
print(&amp;quot;Altitude: &amp;quot;, alt);&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== cmdarg() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|private = _cmdarg()&lt;br /&gt;
|syntax = cmdarg();&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=513|t=Part 1}} {{!}} {{fgdata file|Nasal/globals.nas|t=Part 2}}&lt;br /&gt;
|text = &amp;lt;code&amp;gt;cmdarg()&amp;lt;/code&amp;gt; returns the property root of certain types of XML files. These could be nodes in the [[Property Tree]], or temporary and/or non-public nodes outside the Property tree. &lt;br /&gt;
It is used by Nasal scripts embedded in XML files. It returns a &amp;lt;code&amp;gt;props.Node&amp;lt;/code&amp;gt; object (see {{fgdata file|Nasal/props.nas}}), and you can use all of its methods on the returned value. &amp;lt;code&amp;gt;cmdarg()&amp;lt;/code&amp;gt; should only be used in four types/places of XML files:&lt;br /&gt;
* Bindings: This is needed so that the value of a joystick's axis can be accessed internally.&lt;br /&gt;
* Dialogs: This will return the root of the dialog in the Property Tree. This is useful for dialogs that are created/modified procedurally (e.g. for populating/changing widgets while loading the dialog). &lt;br /&gt;
* Embedded Canvases: The Nasal code behind [[Canvas]] windows [[Howto:Adding a canvas to a GUI dialog|embedded in PUI dialogs]] can use it to accessing the root directory of their Canvas.&lt;br /&gt;
* Animation XML files: If the animation XML file is used in an AI/MP model, &amp;lt;code&amp;gt;cmdarg()&amp;lt;/code&amp;gt; will return the root of the AI model in the &amp;lt;code&amp;gt;/ai/models/&amp;lt;/code&amp;gt; directory. Examples: &amp;lt;code&amp;gt;/ai/models/aircraft[3]/&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/ai/models/multiplayer[1]/&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You should not use &amp;lt;code&amp;gt;cmdarg()&amp;lt;/code&amp;gt; in places other than those stated above. Although it won't cause an error, it will return the value of the last legitimate &amp;lt;code&amp;gt;cmdarg()&amp;lt;/code&amp;gt; call. &lt;br /&gt;
&lt;br /&gt;
Also, you should not delay &amp;lt;code&amp;gt;cmdarg()&amp;lt;/code&amp;gt; using {{func link|maketimer()}}, {{func link|settimer()}} or {{func link|setlistener()}}, because it will return an unrelated property.&lt;br /&gt;
|example1 = fgcommand(&amp;quot;dialog-show&amp;quot;, {&amp;quot;dialog-name&amp;quot;: &amp;quot;cmdarg-demo&amp;quot;});&lt;br /&gt;
|example1text = &amp;lt;br&amp;gt;This example demonstrates the usage of &amp;lt;code&amp;gt;cmdarg()&amp;lt;/code&amp;gt; in a binding.  Save the below XML snippet as &amp;lt;tt&amp;gt;[[$FG_ROOT]]/gui/dialogs/cmdarg-demo.xml&amp;lt;/tt&amp;gt;. Then run the Nasal snippet below in your [[Nasal Console]]. Upon clicking {{button|Close}}, a message will be printed sowing the root of the binding in the Property Tree.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;PropertyList&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;name&amp;gt;cmdarg-demo&amp;lt;/name&amp;gt;&lt;br /&gt;
&amp;lt;layout&amp;gt;vbox&amp;lt;/layout&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;text&amp;gt;&lt;br /&gt;
  &amp;lt;label&amp;gt;Click &amp;quot;Close&amp;quot; to activate the demonstration (a message in the console).&amp;lt;/label&amp;gt;&lt;br /&gt;
&amp;lt;/text&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;button&amp;gt;&lt;br /&gt;
  &amp;lt;legend&amp;gt;Close&amp;lt;/legend&amp;gt;&lt;br /&gt;
  &amp;lt;binding&amp;gt;&lt;br /&gt;
    &amp;lt;command&amp;gt;nasal&amp;lt;/command&amp;gt;&lt;br /&gt;
    &amp;lt;script&amp;gt;print(&amp;quot;Button binding root: '&amp;quot; ~ cmdarg().getPath() ~ &amp;quot;'&amp;quot;);&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;/binding&amp;gt;&lt;br /&gt;
  &amp;lt;binding&amp;gt;&lt;br /&gt;
    &amp;lt;command&amp;gt;dialog-close&amp;lt;/command&amp;gt;&lt;br /&gt;
  &amp;lt;/binding&amp;gt;&lt;br /&gt;
&amp;lt;/button&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/PropertyList&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|example2text = This example demonstrates the usage of &amp;lt;code&amp;gt;cmdarg()&amp;lt;/code&amp;gt; in Nasal code within dialogs.  Open &amp;lt;tt&amp;gt;[[$FG_ROOT]]/gui/dialogs/cmdarg-demo.xml&amp;lt;/tt&amp;gt; from the previous example, copy &amp;amp; paste the code below, and save it. Then run the same Nasal snippet as the previous example in your Nasal Console. If you click {{button|Click me!}}, the button's label will change to &amp;quot;I've been changed!&amp;quot;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;PropertyList&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;name&amp;gt;cmdarg-demo&amp;lt;/name&amp;gt;&lt;br /&gt;
&amp;lt;layout&amp;gt;vbox&amp;lt;/layout&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;text&amp;gt;&lt;br /&gt;
  &amp;lt;label&amp;gt;Click &amp;quot;Click me!&amp;quot; to activate the demonstration (the button's label will change).&amp;lt;/label&amp;gt;&lt;br /&gt;
&amp;lt;/text&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;button&amp;gt;&lt;br /&gt;
  &amp;lt;legend&amp;gt;Click me!&amp;lt;/legend&amp;gt;&lt;br /&gt;
  &amp;lt;binding&amp;gt;&lt;br /&gt;
    &amp;lt;command&amp;gt;nasal&amp;lt;/command&amp;gt;&lt;br /&gt;
    &amp;lt;script&amp;gt;change_label();&amp;lt;/script&amp;gt;&lt;br /&gt;
  &amp;lt;/binding&amp;gt;&lt;br /&gt;
&amp;lt;/button&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;button&amp;gt;&lt;br /&gt;
  &amp;lt;legend&amp;gt;Close&amp;lt;/legend&amp;gt;&lt;br /&gt;
  &amp;lt;binding&amp;gt;&lt;br /&gt;
    &amp;lt;command&amp;gt;dialog-close&amp;lt;/command&amp;gt;&lt;br /&gt;
  &amp;lt;/binding&amp;gt;&lt;br /&gt;
&amp;lt;/button&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nasal&amp;gt;&lt;br /&gt;
  &amp;lt;open&amp;gt;&amp;lt;![CDATA[&lt;br /&gt;
    var dlg_root = cmdarg();&lt;br /&gt;
    var dlg_name = {&amp;quot;dialog-name&amp;quot;: &amp;quot;cmdarg-demo&amp;quot;};&lt;br /&gt;
    var change_label = func {&lt;br /&gt;
        dlg_root.getNode(&amp;quot;button[0]/legend&amp;quot;).setValue(&amp;quot;I've been changed!&amp;quot;);&lt;br /&gt;
        fgcommand(&amp;quot;dialog-close&amp;quot;, dlg_name);&lt;br /&gt;
        fgcommand(&amp;quot;dialog-show&amp;quot;, dlg_name);&lt;br /&gt;
    }&lt;br /&gt;
  ]]&amp;gt;&amp;lt;/open&amp;gt;&lt;br /&gt;
&amp;lt;/nasal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/PropertyList&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|example3text = For an example of &amp;lt;code&amp;gt;cmdarg()&amp;lt;/code&amp;gt; used with Canvas, please see [[Howto:Adding a canvas to a GUI dialog#FGPlot|Howto:Adding a canvas to a GUI dialog]].&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== courseAndDistance() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = courseAndDistance(to);&lt;br /&gt;
courseAndDistance(from, to);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1668|t=Source}}&lt;br /&gt;
|text = Returns a vector containing the course from one point to another and the distance between them in nautical miles. The course is the initial bearing (see [http://www.movable-type.co.uk/scripts/latlong.html#bearing here]), and is in the range 0–360. Both arguments can be one of:&lt;br /&gt;
:* An &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;runway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;taxiway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fix&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;waypoint&amp;lt;/code&amp;gt; ghost type&lt;br /&gt;
:* A hash with ''lat'' and ''lon'' members&lt;br /&gt;
:* A geo.Coord object&lt;br /&gt;
|param1 = from&lt;br /&gt;
|param1text = Optional parameter defining the from where the function should calculate its results. If the function is given one argument ('''to'''), the aircraft's current position will be used. As well as the argument types as defined above, this argument can be two numbers separated with a comma, as if the function is taking three arguments. See example 5 below.&lt;br /&gt;
|param2 = to&lt;br /&gt;
|param2text = Like the first parameter, but defines the second point.&lt;br /&gt;
|example1text = This example demonstrates the usage of the function with the &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt; ghost type.&lt;br /&gt;
|example1 = var from = airportinfo(&amp;quot;KSFO&amp;quot;);&lt;br /&gt;
var to = airportinfo(&amp;quot;KSQL&amp;quot;);&lt;br /&gt;
var (course, dist) = courseAndDistance(from, to);&lt;br /&gt;
print(course); # prints course from KSFO to KSQL&lt;br /&gt;
print(dist); # prints distance in nm from KSFO to KSQL&lt;br /&gt;
|example2text = This example demonstrates the usage of the function with hashes containing ''lat'' and ''lon''.&lt;br /&gt;
|example2 = var from = {lat: 0, lon: 0};&lt;br /&gt;
var to = {lat: 1, lon: 1};&lt;br /&gt;
var (course, dist) = courseAndDistance(from, to);&lt;br /&gt;
print(course);&lt;br /&gt;
print(dist);&lt;br /&gt;
|example3text = This example demonstrates usage of a geo.Coord object.&lt;br /&gt;
|example3 = var from = geo.Coord.new().set_latlon(0, 0);&lt;br /&gt;
var to = geo.Coord.new().set_latlon(1, 1);&lt;br /&gt;
var (course, dist) = courseAndDistance(from, to);&lt;br /&gt;
print(course);&lt;br /&gt;
print(dist);&lt;br /&gt;
|example4text = This example demonstrates usage of differing parameter types.&lt;br /&gt;
|example4 = var from = airportinfo(&amp;quot;KSFO&amp;quot;);&lt;br /&gt;
var to = geo.Coord.new().set_latlon(0, 0);&lt;br /&gt;
var (course, dist) = courseAndDistance(from, to);&lt;br /&gt;
print(course);&lt;br /&gt;
print(dist);&lt;br /&gt;
|example5text = The same as above, but the other way round.&lt;br /&gt;
|example5 = var to = {lat: 1, lon: 1};&lt;br /&gt;
var (course, dist) = courseAndDistance(0, 0, to);&lt;br /&gt;
print(course);&lt;br /&gt;
print(dist);&lt;br /&gt;
|example6text = Usage of just one parameter.&lt;br /&gt;
|example6 = var dest = airportinfo(&amp;quot;KSQL&amp;quot;);&lt;br /&gt;
var (course, dist) = courseAndDistance(dest);&lt;br /&gt;
print(&amp;quot;Turn to heading &amp;quot;, math.round(course), &amp;quot;. You have &amp;quot;, sprintf(&amp;quot;%.2f&amp;quot;, dist), &amp;quot; nm to go&amp;quot;);&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== createDiscontinuity() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = createDiscontinuity();&lt;br /&gt;
|text = Returns a &amp;lt;code&amp;gt;waypoint&amp;lt;/code&amp;gt; ghost object. A route discontinuity is inserted by an {{abbr|FMS|Flight Management System}} when it is unsure how to connect two waypoints.&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=2045|t=Source}}&lt;br /&gt;
|version = 2016.1&lt;br /&gt;
|commit = {{flightgear commit|caead6|t=commit}}&lt;br /&gt;
}}&lt;br /&gt;
=== createViaTo() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = createViaTo(airway, waypoint);&lt;br /&gt;
|text = Returns a &amp;lt;code&amp;gt;waypoint&amp;lt;/code&amp;gt; ghost object. It represents a route &amp;quot;via '''airway''' to '''waypoint'''&amp;quot;.&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=2009|t=Source}}&lt;br /&gt;
|version = 2016.1&lt;br /&gt;
|commit = {{flightgear commit|caead6|t=commit}}&lt;br /&gt;
|param1 = airway&lt;br /&gt;
|param1text = The name of an airway.&lt;br /&gt;
|param2 = waypoint&lt;br /&gt;
|param2text = Must be in the airway and one of:&lt;br /&gt;
* The name of a waypoint.&lt;br /&gt;
* An &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;runway&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;fix&amp;lt;/code&amp;gt; ghost object.&lt;br /&gt;
}}&lt;br /&gt;
=== createWP() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = createWP(pos, name[, flag]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1964|t=Source}}&lt;br /&gt;
|text = Creates a new waypoint ghost object.&lt;br /&gt;
|param1 = pos&lt;br /&gt;
|param1text = Dictates the position of the new waypoint. It can be one of the following:&lt;br /&gt;
:* An &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;runway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;taxiway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fix&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;waypoint&amp;lt;/code&amp;gt; ghost type&lt;br /&gt;
:* A hash with ''lat'' and ''lon'' members&lt;br /&gt;
:* A geo.Coord object&lt;br /&gt;
:* Two numbers separated by a comma, as if the function is taking three arguments. See example 4 below.&lt;br /&gt;
|param2 = name&lt;br /&gt;
|param2text = String that will become the name of the new waypoint.&lt;br /&gt;
|param3 = flag&lt;br /&gt;
|param3text = Optional string that will tell FlightGear what type of waypoint it is. Must be one of &amp;quot;sid,&amp;quot; &amp;quot;star,&amp;quot; &amp;quot;approach,&amp;quot; &amp;quot;missed,&amp;quot; or &amp;quot;pseudo.&amp;quot;&lt;br /&gt;
|example1text = Creates a waypoint directly in front and 1 km away and appends it to the flight plan.&lt;br /&gt;
|example1 = var pos = geo.aircraft_position().apply_course_distance(getprop(&amp;quot;/orientation/heading-deg&amp;quot;), 1000);&lt;br /&gt;
var wp = createWP(pos, &amp;quot;NEWWP&amp;quot;);&lt;br /&gt;
var fp = flightplan();&lt;br /&gt;
fp.appendWP(wp);&lt;br /&gt;
|example2 = var pos = geo.aircraft_position().apply_course_distance(getprop(&amp;quot;/orientation/heading-deg&amp;quot;), 1000);&lt;br /&gt;
var wp = createWP({lat: pos.lat(), lon: pos.lon()}, &amp;quot;NEWWP&amp;quot;);&lt;br /&gt;
var fp = flightplan();&lt;br /&gt;
fp.appendWP(wp);&lt;br /&gt;
|example3 = var apt = airportinfo();&lt;br /&gt;
var wp = createWP(apt, &amp;quot;NEWWP&amp;quot;);&lt;br /&gt;
var fp = flightplan();&lt;br /&gt;
fp.appendWP(wp);&lt;br /&gt;
|example4 = var pos = geo.aircraft_position().apply_course_distance(getprop(&amp;quot;/orientation/heading-deg&amp;quot;), 1000);&lt;br /&gt;
var wp = createWP(pos.lat(), pos.lon(), &amp;quot;NEWWP&amp;quot;);&lt;br /&gt;
var fp = flightplan();&lt;br /&gt;
fp.appendWP(wp);&lt;br /&gt;
|example5text = Creates a new waypoint and adds it to the flight plan. Waypoints of the type &amp;quot;pseudo&amp;quot; are then removed from the flight plan, including the new waypoint. The {{func link|print()}} statements show this.&lt;br /&gt;
|example5 = var pos = geo.aircraft_position().apply_course_distance(getprop(&amp;quot;/orientation/heading-deg&amp;quot;), 1000);&lt;br /&gt;
var wp = createWP(pos, &amp;quot;NEWWP&amp;quot;, &amp;quot;pseudo&amp;quot;);&lt;br /&gt;
var fp = flightplan();&lt;br /&gt;
fp.appendWP(wp);&lt;br /&gt;
print(fp.getPlanSize());&lt;br /&gt;
fp.clearWPType(&amp;quot;pseudo&amp;quot;);&lt;br /&gt;
print(fp.getPlanSize());&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== createWPFrom() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = createWPFrom(object[, flag]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1989|t=Source}}&lt;br /&gt;
|text = Creates a new waypoint object from another object.&lt;br /&gt;
|param1 = object&lt;br /&gt;
|param1text = A ghost object. Must be a ghost type that is one of &amp;quot;airport,&amp;quot; &amp;quot;navaid,&amp;quot; &amp;quot;runway,&amp;quot; or &amp;quot;fix.&amp;quot;&lt;br /&gt;
|param2 = flag&lt;br /&gt;
|param2text = Optional string that will tell FlightGear what type of waypoint it is. Must be one of &amp;quot;sid,&amp;quot; &amp;quot;star,&amp;quot; &amp;quot;approach,&amp;quot; &amp;quot;missed,&amp;quot; or &amp;quot;pseudo.&amp;quot;&lt;br /&gt;
|example1text = Creates a new waypoint and appends it to the flight plan.&lt;br /&gt;
|example1 = var apt = airportinfo(&amp;quot;KSFO&amp;quot;);&lt;br /&gt;
var wp = createWPFrom(apt);&lt;br /&gt;
var fp = flightplan();&lt;br /&gt;
fp.appendWP(wp);&lt;br /&gt;
|example2text = Creates a new waypoint and appends it to the flight plan. This way point is then removed; the {{func link|print()}} statements prove this.&lt;br /&gt;
|example2 = var apt = airportinfo(&amp;quot;KSFO&amp;quot;);&lt;br /&gt;
var wp = createWPFrom(apt, &amp;quot;pseudo&amp;quot;);&lt;br /&gt;
print(wp.wp_name);&lt;br /&gt;
var fp = flightplan();&lt;br /&gt;
fp.appendWP(wp);&lt;br /&gt;
print(fp.getPlanSize());&lt;br /&gt;
fp.clearWPType(&amp;quot;pseudo&amp;quot;);&lt;br /&gt;
print(fp.getPlanSize());&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== defined() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = defined(symbol);&lt;br /&gt;
|source = {{fgdata file|Nasal/globals.nas|t=Source}}&lt;br /&gt;
|text = Returns 1 (true) or 0 (false) depending on whether a variable exists.&lt;br /&gt;
|param1 = symbol&lt;br /&gt;
|param1text = A string that will be what the function searches for.&lt;br /&gt;
|example1 = var number = 12;&lt;br /&gt;
var check_exist = func {&lt;br /&gt;
    print(&amp;quot;Variable 'number' &amp;quot;, defined(&amp;quot;number&amp;quot;) == 1 ? &amp;quot;exists&amp;quot; : &amp;quot;does not exist&amp;quot;); # 'number' does exist&lt;br /&gt;
    print(&amp;quot;Variable 'number2' &amp;quot;, defined(&amp;quot;number2&amp;quot;) == 1 ? &amp;quot;exists&amp;quot; : &amp;quot;does not exist&amp;quot;); # 'number2' does not exist&lt;br /&gt;
}&lt;br /&gt;
check_exist();&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== directory() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = directory(path);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=572|t=Source}}&lt;br /&gt;
|text = Returns a vector containing a list of the folders and files in a given file path or &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; if the path doesn't exist. Hidden folders and files are not added to the vector.&lt;br /&gt;
{{inote|The first two elements of the vector will be &amp;lt;code&amp;gt;'.'&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;'..'&amp;lt;/code&amp;gt;. These are for navigating back up the file tree, but have no use in Nasal. They can be safely removed from the vector.}}&lt;br /&gt;
|param1 = path&lt;br /&gt;
|param1text = Absolute file path.&lt;br /&gt;
|example1text = Gets the folders and files in [[$FG_ROOT]], and then removes the extra first two elements (see note above). &lt;br /&gt;
|example1 = var dir = directory(getprop(&amp;quot;/sim/fg-root&amp;quot;)); # get directory&lt;br /&gt;
dir = subvec(dir, 2); # strips off the first two elements&lt;br /&gt;
debug.dump(dir); # dump the vector&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== fgcommand() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = fgcommand(cmd[, args]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=456|t=Part 1}} {{!}} {{fgdata file|Nasal/globals.nas|t=Part 2}}&lt;br /&gt;
|text = Runs an fgcommand. See also {{readme file|commands}} and [[Bindings]] for more information. See {{flightgear file|src/Main/fg_commands.cxx|l=1425}} for the full list of fgcommands. Note that fgcommands generated by {{func link|addcommand()}} can also be run using this function. Also, the full list of fgcommands depends on the version of FlightGear you have. Returns 1 (true) if the fgcommand succeeded or 0 (false) if it failed.&lt;br /&gt;
|param1 = cmd&lt;br /&gt;
|param1text = String that is the name of the command that is to be run.&lt;br /&gt;
|param2 = args&lt;br /&gt;
|param2text = If the fgcommand takes arguments, they are inputted using this argument. Can either be a &amp;lt;code&amp;gt;props.Node&amp;lt;/code&amp;gt; object, or a hash (see examples below).&lt;br /&gt;
|example1 = fgcommand(&amp;quot;null&amp;quot;); # does nothing&lt;br /&gt;
|example2 = var args = props.Node.new({'script': 'print(&amp;quot;Running fgcommand&amp;quot;);'});&lt;br /&gt;
if (fgcommand(&amp;quot;nasal&amp;quot;, args)) { # prints &amp;quot;Running fgcommand&amp;quot; and then one of these print statements&lt;br /&gt;
    print(&amp;quot;Fgcommand succeeded&amp;quot;);&lt;br /&gt;
} else {&lt;br /&gt;
    print(&amp;quot;Fgcommand encountered a problem&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
|example3 = var args = { 'dialog-name': 'about' };&lt;br /&gt;
fgcommand(&amp;quot;dialog-show&amp;quot;, args); # shows the 'about' dialog&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== findAirportsByICAO() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = findAirportsByICAO(search[, type]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1096|t=Source}}&lt;br /&gt;
|text = Returns a vector containing &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt; ghost objects which are (by default) airports whose ICAO code matches the search string. The results are sorted by range from closest to furthest.&lt;br /&gt;
|param1 = search&lt;br /&gt;
|param1text = Search string for the function. Can either be a partial or a full ICAO code.&lt;br /&gt;
:{{icaution|The more matches there are for the given code, the longer the function will take. Passing just one character (e.g., &amp;quot;K&amp;quot;), might make FlightGear hang for a certain amount of time.}}&lt;br /&gt;
|param2 = type&lt;br /&gt;
|param2text = This will narrow the search to airports of a certain type. By default, only airports are searched for. May be one of &amp;quot;airport,&amp;quot; &amp;quot;heliport,&amp;quot; or &amp;quot;seaport.&amp;quot;&lt;br /&gt;
|example1 = var apts = findAirportsByICAO(&amp;quot;KSF&amp;quot;); # finds all airports matching &amp;quot;KSF&amp;quot;&lt;br /&gt;
foreach(var apt; apts){&lt;br /&gt;
    print(apt.name, &amp;quot; (&amp;quot;, apt.id, &amp;quot;)&amp;quot;); # prints them&lt;br /&gt;
}&lt;br /&gt;
|example2 = var apts = findAirportsByICAO(&amp;quot;SP0&amp;quot;, &amp;quot;seaport&amp;quot;); # finds all seaplane bases matching &amp;quot;SP0&amp;quot;&lt;br /&gt;
foreach(var apt; apts){&lt;br /&gt;
    print(apt.name, &amp;quot; (&amp;quot;, apt.id, &amp;quot;)&amp;quot;); # prints them&lt;br /&gt;
}&lt;br /&gt;
|example3 = var apt = findAirportsByICAO(&amp;quot;XBET&amp;quot;); # one way to check if an airport does exist&amp;quot;&lt;br /&gt;
if (size(apt) == 0) {&lt;br /&gt;
    print(&amp;quot;Airport does not exist&amp;quot;); # this one will be printed&lt;br /&gt;
} else {&lt;br /&gt;
    print(&amp;quot;Airport does exist&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== findAirportsWithinRange() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = findAirportsWithinRange([pos, ]range[, type]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1066|t=Source}}&lt;br /&gt;
|text = Returns a vector of &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt; ghost object which are (by default) airports that are within a given range of a given position, or the aircraft's current position. The results are sorted by range from closest to furthest.&lt;br /&gt;
|param1 = pos&lt;br /&gt;
|param1text = Optional position to search around. If not given, the aircraft's current position will be used. Can be one of:&lt;br /&gt;
:* An &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;runway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;taxiway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fix&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;waypoint&amp;lt;/code&amp;gt; ghost type&lt;br /&gt;
:* A hash with ''lat'' and ''lon'' members&lt;br /&gt;
:* A geo.Coord object&lt;br /&gt;
:* Two numbers separated by a comma, as if the function is taking three arguments. Example: &amp;lt;code&amp;gt;findAirportsWithinRange(lat, lon, range, type);&amp;lt;/code&amp;gt;.&lt;br /&gt;
|param2 = range&lt;br /&gt;
|param2text = Mandatory number giving the range in nautical miles within which to search for airports/heliports/seaplane bases.only airports are searched for.&lt;br /&gt;
|param3 = type&lt;br /&gt;
|param3text = This will narrow the search to airports of a certain type. By default, only airports are searched for. May be one of &amp;quot;airport,&amp;quot; &amp;quot;heliport,&amp;quot; or &amp;quot;seaport.&amp;quot;&lt;br /&gt;
|example1text = Searches for airports within 10 nm of [[KSFO]].&lt;br /&gt;
|example1 = var pos = airportinfo(&amp;quot;KSFO&amp;quot;);&lt;br /&gt;
var apts = findAirportsWithinRange(pos, 10);&lt;br /&gt;
foreach(var apt; apts){&lt;br /&gt;
    print(apt.name, &amp;quot; (&amp;quot;, apt.id, &amp;quot;)&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
|example2text = Searches for seaplane bases within 15 nm of [[KSFO]].&lt;br /&gt;
|example2 = var pos = airportinfo(&amp;quot;KSFO&amp;quot;);&lt;br /&gt;
var apts = findAirportsWithinRange(pos, 15, &amp;quot;seaport&amp;quot;);&lt;br /&gt;
foreach(var apt; apts){&lt;br /&gt;
    print(apt.name, &amp;quot; (&amp;quot;, apt.id, &amp;quot;)&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
|example3text = Searches for airports within 10 nm of your current position.&lt;br /&gt;
|example3 = var apts = findAirportsWithinRange(10);&lt;br /&gt;
foreach(var apt; apts){&lt;br /&gt;
    print(apt.name, &amp;quot; (&amp;quot;, apt.id, &amp;quot;)&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== findFixesByID() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = findFixesByID([pos, ]id);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1627|t=Source}}&lt;br /&gt;
|text = Returns a vector containing &amp;lt;code&amp;gt;fix&amp;lt;/code&amp;gt; ghost objects matching a given ID, sorted by range from a certain position.&lt;br /&gt;
{{inote|Fixes are (usually) also known as waypoints.}}&lt;br /&gt;
|param1 = pos&lt;br /&gt;
|param1text = Optional position to search around. If not given, the aircraft's current position will be used. Can be one of:&lt;br /&gt;
:* An &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;runway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;taxiway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fix&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;waypoint&amp;lt;/code&amp;gt; ghost type&lt;br /&gt;
:* A hash with ''lat'' and ''lon'' members&lt;br /&gt;
:* A geo.Coord object&lt;br /&gt;
:* Two numbers separated by a comma, as if the function is taking three arguments. Example: &amp;lt;code&amp;gt;findFixesByID(lat, lon, id);&amp;lt;/code&amp;gt;.&lt;br /&gt;
|param2 = id&lt;br /&gt;
|param2text = Full or partial ID of the fix to search for.&lt;br /&gt;
:{{inote|1=Inputting a partial ID does not work correctly (see [http://forum.flightgear.org/viewtopic.php?f=30&amp;amp;t=28129 here]). It is best to just input a full ID.}}&lt;br /&gt;
|example1 = var fixes = findFixesByID(&amp;quot;POGIC&amp;quot;);&lt;br /&gt;
foreach(var fix; fixes){&lt;br /&gt;
    print(fix.id, &amp;quot; - lat: &amp;quot;, fix.lat, &amp;quot; {{!}} lon: &amp;quot;, fix.lon); # prints information about POGIC&lt;br /&gt;
}&lt;br /&gt;
|example2 = var fix = findFixesByID(&amp;quot;ZUNAP&amp;quot;);&lt;br /&gt;
fix = fix[0];&lt;br /&gt;
var (course, dist) = courseAndDistance(fix);&lt;br /&gt;
print(&amp;quot;Turn to heading &amp;quot;, math.round(course), &amp;quot;. You have &amp;quot;, sprintf(&amp;quot;%.2f&amp;quot;, dist), &amp;quot; nm to go to reach &amp;quot;, fixes[0].id);&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== findNavaidByFrequency() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = findNavaidByFrequency([pos, ]freq[, type]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1547|t=Source}}&lt;br /&gt;
|text = Returns a &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt; ghost object for a navaid matching a given frequency. If there is more than one navaid with that frequency, the nearest station is returned.&lt;br /&gt;
|param1 = pos&lt;br /&gt;
|param1text = Optional position to search around. If not given, the aircraft's current position will be used. Can be one of:&lt;br /&gt;
:* An &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;runway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;taxiway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fix&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;waypoint&amp;lt;/code&amp;gt; ghost type&lt;br /&gt;
:* A hash with ''lat'' and ''lon'' members&lt;br /&gt;
:* A geo.Coord object&lt;br /&gt;
:* Two numbers separated by a comma, as if the function is taking three arguments. Example: &amp;lt;code&amp;gt;findNavaidByFrequency(lat, lon, freq, type);&amp;lt;/code&amp;gt;.&lt;br /&gt;
|param2 = freq&lt;br /&gt;
|param2text = Frequency, in megahertz, of the navaid to search for.&lt;br /&gt;
|param3 = type&lt;br /&gt;
|param3text = This will narrow the search to navaids of a certain type. Defaults to &amp;quot;all.&amp;quot; For the full list of accepted type arguments, see {{flightgear file|src/Navaids/positioned.cxx|l=127}}.&lt;br /&gt;
|example1 = var navaid = findNavaidByFrequency(11.17);&lt;br /&gt;
print(&amp;quot;ID: &amp;quot;, navaid.id); # prints info about the navaid&lt;br /&gt;
print(&amp;quot;Name: &amp;quot;, navaid.name);&lt;br /&gt;
print(&amp;quot;Latitude: &amp;quot;, navaid.lat);&lt;br /&gt;
print(&amp;quot;Longitude: &amp;quot;, navaid.lon);&lt;br /&gt;
print(&amp;quot;Elevation (AMSL): &amp;quot;, navaid.elevation, &amp;quot; m&amp;quot;);&lt;br /&gt;
print(&amp;quot;Type: &amp;quot;, navaid.type);&lt;br /&gt;
print(&amp;quot;Frequency: &amp;quot;, sprintf(&amp;quot;%.3f&amp;quot;, navaid.frequency / 1000), &amp;quot; Mhz&amp;quot;);&lt;br /&gt;
print(&amp;quot;Range: &amp;quot;, navaid.range_nm, &amp;quot; nm&amp;quot;);&lt;br /&gt;
if(navaid.course) print(&amp;quot;Course: &amp;quot;, navaid.course);&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== findNavaidsByFrequency() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = findNavaidsByFrequency([pos, ]freq[, type]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1572|t=Source}}&lt;br /&gt;
|text = Returns a vector conatining &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt; ghost objects for navaids that match a given frequency, sorted from nearest to furthest.&lt;br /&gt;
|param1 = pos&lt;br /&gt;
|param1text = Optional position to search around. If not given, the aircraft's current position will be used. Can be one of:&lt;br /&gt;
:* An &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;runway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;taxiway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fix&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;waypoint&amp;lt;/code&amp;gt; ghost type&lt;br /&gt;
:* A hash with ''lat'' and ''lon'' members&lt;br /&gt;
:* A geo.Coord object&lt;br /&gt;
:* Two numbers separated by a comma, as if the function is taking three arguments. Example: &amp;lt;code&amp;gt;findNavaidsByFrequency(lat, lon, freq, type);&amp;lt;/code&amp;gt;.&lt;br /&gt;
|param2 = freq&lt;br /&gt;
|param2text = Frequency, in megahertz, of the navaid to search for.&lt;br /&gt;
|param3 = type&lt;br /&gt;
|param3text = This will narrow the search to navaids of a certain type. Defaults to &amp;quot;all.&amp;quot; For the full list of accepted type arguments, see {{flightgear file|src/Navaids/positioned.cxx|l=127}}.&lt;br /&gt;
|example1 = var navaids = findNavaidsByFrequency(10.955);&lt;br /&gt;
foreach(var navaid; navaids){&lt;br /&gt;
    print(&amp;quot;--&amp;quot;);&lt;br /&gt;
    print(&amp;quot;ID: &amp;quot;, navaid.id); # prints info about the navaid&lt;br /&gt;
    print(&amp;quot;Name: &amp;quot;, navaid.name);&lt;br /&gt;
    print(&amp;quot;Latitude: &amp;quot;, navaid.lat);&lt;br /&gt;
    print(&amp;quot;Longitude: &amp;quot;, navaid.lon);&lt;br /&gt;
    print(&amp;quot;Elevation (AMSL): &amp;quot;, navaid.elevation, &amp;quot; m&amp;quot;);&lt;br /&gt;
    print(&amp;quot;Type: &amp;quot;, navaid.type);&lt;br /&gt;
    print(&amp;quot;Frequency: &amp;quot;, sprintf(&amp;quot;%.3f&amp;quot;, navaid.frequency / 1000), &amp;quot; Mhz&amp;quot;);&lt;br /&gt;
    print(&amp;quot;Range: &amp;quot;, navaid.range_nm, &amp;quot; nm&amp;quot;);&lt;br /&gt;
    if(navaid.course) print(&amp;quot;Course: &amp;quot;, navaid.course);&lt;br /&gt;
    print(&amp;quot;--&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== findNavaidsByID() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = findNavaidsByID([pos, ]id[, type]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1600|t=Source}}&lt;br /&gt;
|text = Returns a vector containing &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt; ghost objects matching a given ID, sorted by range from a certain position.&lt;br /&gt;
|param1 = pos&lt;br /&gt;
|param1text = Optional position to search around. If not given, the aircraft's current position will be used. Can be one of:&lt;br /&gt;
:* An &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;runway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;taxiway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fix&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;waypoint&amp;lt;/code&amp;gt; ghost type&lt;br /&gt;
:* A hash with ''lat'' and ''lon'' members&lt;br /&gt;
:* A geo.Coord object&lt;br /&gt;
:* Two numbers separated by a comma, as if the function is taking three arguments. Example: &amp;lt;code&amp;gt;findNavaidsByID(lat, lon, id, type);&amp;lt;/code&amp;gt;.&lt;br /&gt;
|param2 = id&lt;br /&gt;
|param2text = Full or partial ID of the fix to search for.&lt;br /&gt;
:{{inote|1=Inputting a partial ID does not work correctly (see [http://forum.flightgear.org/viewtopic.php?f=30&amp;amp;t=28129 here]). It is best to just input a full ID.}}&lt;br /&gt;
|param3 = type&lt;br /&gt;
|param3text = This will narrow the search to navaids of a certain type. Defaults to &amp;quot;all.&amp;quot; For the full list of accepted type arguments, see {{flightgear file|src/Navaids/positioned.cxx|l=127}}.&lt;br /&gt;
|example1 = var navaid = findNavaidsByID(&amp;quot;MXW&amp;quot;);&lt;br /&gt;
navaid = navaid[0];&lt;br /&gt;
print(&amp;quot;ID: &amp;quot;, navaid.id); # prints info about 'MXW' (a VOR station)&lt;br /&gt;
print(&amp;quot;Name: &amp;quot;, navaid.name);&lt;br /&gt;
print(&amp;quot;Latitude: &amp;quot;, navaid.lat);&lt;br /&gt;
print(&amp;quot;Longitude: &amp;quot;, navaid.lon);&lt;br /&gt;
print(&amp;quot;Elevation (AMSL): &amp;quot;, navaid.elevation, &amp;quot; m&amp;quot;);&lt;br /&gt;
print(&amp;quot;Type: &amp;quot;, navaid.type);&lt;br /&gt;
print(&amp;quot;Frequency: &amp;quot;, sprintf(&amp;quot;%.3f&amp;quot;, navaid.frequency / 1000), &amp;quot; Mhz&amp;quot;);&lt;br /&gt;
print(&amp;quot;Range: &amp;quot;, navaid.range_nm, &amp;quot; nm&amp;quot;);&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== findNavaidsWithinRange() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = findNavaidsWithinRange([pos, ]range[, type]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1518|t=Source}}&lt;br /&gt;
|text = Returns a vector of &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt; ghost objects which are within a given range of a given position (by default the aircraft's current position). The results are sorted from closest to furthest.&lt;br /&gt;
|param1 = pos&lt;br /&gt;
|param1text = Optional position to search around. If not given, the aircraft's current position will be used. Can be one of:&lt;br /&gt;
:* An &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;runway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;taxiway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fix&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;waypoint&amp;lt;/code&amp;gt; ghost type&lt;br /&gt;
:* A hash with ''lat'' and ''lon'' members&lt;br /&gt;
:* A geo.Coord object&lt;br /&gt;
:* Two numbers separated by a comma, as if the function is taking three arguments. Example: &amp;lt;code&amp;gt;findNavaidsWithinRange(lat, lon, range, type);&amp;lt;/code&amp;gt;.&lt;br /&gt;
|param2 = range&lt;br /&gt;
|param2text = Mandatory number giving the range in nautical miles within which to search for navaids. Defaults to &amp;quot;all.&amp;quot; For the full list of accepted type arguments, see {{flightgear file|src/Navaids/positioned.cxx|l=127}}.&lt;br /&gt;
|param3 = type&lt;br /&gt;
|param3text = This will narrow the search to navaids of a certain type.&lt;br /&gt;
|example1text = Searches for navaids within 10 nm of [[KSFO]].&lt;br /&gt;
|example1 = var pos = airportinfo(&amp;quot;KSFO&amp;quot;);&lt;br /&gt;
var navs = findNavaidsWithinRange(pos, 10);&lt;br /&gt;
foreach(var nav; navs){&lt;br /&gt;
    print(nav.name, &amp;quot; (ID: &amp;quot;, nav.id, &amp;quot;)&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
|example2text = Searches for navaids within 10 nm of your current position.&lt;br /&gt;
|example2 = var navs = findNavaidsWithinRange(10);&lt;br /&gt;
foreach(var nav; navs){&lt;br /&gt;
    print(nav.name, &amp;quot; (ID: &amp;quot;, nav.id, &amp;quot;)&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== finddata() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = finddata(path);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=603|t=Source}}&lt;br /&gt;
|text = Takes a relative path and tries to return an absolute one. It works by appending the relative path to some paths and testing to see if they exist. As of FlightGear v3.7, these paths are the TerraSync directory (tested first) and [[$FG_ROOT]]. &lt;br /&gt;
|param1 = path&lt;br /&gt;
|param1text = A relative path as a string.&lt;br /&gt;
|example1 = var path = finddata(&amp;quot;Aircraft/Generic&amp;quot;);&lt;br /&gt;
print(path); # prints the absolute path to $FG_ROOT/Aircraft/Generic&lt;br /&gt;
|example2 = var path = finddata(&amp;quot;Airports&amp;quot;);&lt;br /&gt;
print(path); # prints the absolute path to &amp;lt;TerraSync dir&amp;gt;/Airports&lt;br /&gt;
|example3 = var path = finddata(&amp;quot;preferences.xml&amp;quot;);&lt;br /&gt;
print(path); # prints the absolute path to $FG_ROOT/preferences.xml&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== flightplan() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = flightplan([path]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1738|t=Source}}&lt;br /&gt;
|text = {{see also|Nasal Flightplan}}&lt;br /&gt;
Returns a flight plan object, either one for the current flight plan, or one loaded from a given path.&lt;br /&gt;
|param1 = path&lt;br /&gt;
|param1text = Optional path to flight plan XML file.&lt;br /&gt;
|example1text = Gets the active flight plan and gets the ID of the current waypoint. Note that this example requires a flight plan to be set in the [[Route Manager]] first.&lt;br /&gt;
|example1 = var fp = flightplan();&lt;br /&gt;
print(fp.getWP(fp.current).id);&lt;br /&gt;
|example2text = Creates a new flight plan from an XML file and prints the IDs of the waypoints. Note that this example requires a flight plan to have been created and saved as &amp;lt;tt&amp;gt;''[[$FG_HOME]]/fp-demo.xml''&amp;lt;/tt&amp;gt;.&lt;br /&gt;
|example2 = var path = getprop('/sim/fg-home') ~ '/fp-demo.xml';&lt;br /&gt;
var fp = flightplan(path);&lt;br /&gt;
for(var i = 0; i &amp;lt; fp.getPlanSize(); i += 1){&lt;br /&gt;
    print(fp.getWP(i).id);&lt;br /&gt;
}&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== geodinfo() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = geodinfo(lat, lon[, max_alt]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=981|t=Source}}&lt;br /&gt;
|text = Returns a vector containing two entries or &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; if no information could be obtained because the terrain tile wasn't loaded. The first entry in the vector is the elevation (in meters) for the given point, and the second is a hash with information about the assigned material (as defined in &amp;lt;tt&amp;gt;''[[$FG_ROOT]]/Materials''&amp;lt;/tt&amp;gt;), or &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; if there was no material information available (for example, because there is an untextured building at that location). The structure of the hash is as follows (see also {{readme file|materials}}):&lt;br /&gt;
* '''light_coverage:''' The coverage of a single point of light in m&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;.&lt;br /&gt;
* '''bumpiness:''' Normalized bumpiness factor for the material.&lt;br /&gt;
* '''load_resistance:''' The amount of pressure in N/m&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt; the material can withstand without deformation.&lt;br /&gt;
* '''solid:''' 1 (true) or false (0) depending on whether the material is solid or not.&lt;br /&gt;
* '''names:''' Vector of scenery types (usually generated by [[TerraGear]]) that will use this material. &lt;br /&gt;
* '''friction_factor:''' Normalized friction factor of the material.&lt;br /&gt;
* '''rolling_friction:''' The rolling friction coefficient of the material.&lt;br /&gt;
&lt;br /&gt;
Note that this function is a ''very'' CPU-intensive operation, particularly in FlightGear v2.4 and earlier. It is advised to use this function as little as possible.&lt;br /&gt;
|param1 = lat&lt;br /&gt;
|param1text = Latitude, inputted as a number.&lt;br /&gt;
|param2 = lon&lt;br /&gt;
|param2text = Longitude, inputted as a number.&lt;br /&gt;
|param3 = max_alt&lt;br /&gt;
|param3text = The altitude, in metres, from which the function will begin searching for the height of the terrain. Defaults to 10,000 metres. If the terrain is higher than this argument specifies, &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; will be returned.&lt;br /&gt;
|example1text = Dumps information about ground underneath the aircraft.&lt;br /&gt;
|example1 = var pos = geo.aircraft_position();&lt;br /&gt;
var info = geodinfo(pos.lat(), pos.lon());&lt;br /&gt;
debug.dump(info);&lt;br /&gt;
|example2text = Prints whether the ground underneath the aircraft is solid or is water.&lt;br /&gt;
|example2 = var pos = geo.aircraft_position();&lt;br /&gt;
var info = geodinfo(pos.lat(), pos.lon());&lt;br /&gt;
if (info != nil and info[1] != nil) {&lt;br /&gt;
    print(&amp;quot;The ground underneath the aircraft is &amp;quot;, info[1].solid == 1 ? &amp;quot;solid.&amp;quot; : &amp;quot;water.&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== geodtocart() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = geodtocart(lat, lon, alt);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=962|t=Source}}&lt;br /&gt;
|text = Converts {{wikipedia|geodetic coordinates}} (latitude, longitude, and altitude) to {{wikipedia|ECEF|Earth-centered, Earth-fixed}} coordinates (x, y and z). A vector is returned containing x, y, and z in metres. The equatorial radius of earth used is that defined by the {{wikipedia|WGS 84}} (6,378,137 metres). All argument are mandatory.&lt;br /&gt;
|param1 = lat&lt;br /&gt;
|param1text = Latitude, in degrees.&lt;br /&gt;
|param2 = lon&lt;br /&gt;
|param2text = Longitude, in degrees.&lt;br /&gt;
|param3 = alt&lt;br /&gt;
|param3text = Altitude, in metres.&lt;br /&gt;
|example1 = var (x, y, z) = geodtocart(0, 0, 0); # point is the intersection of the prime meridian and equator.&lt;br /&gt;
print(&amp;quot;x: &amp;quot;, x); # prints &amp;quot;x: 6378137&amp;quot;&lt;br /&gt;
print(&amp;quot;y: &amp;quot;, y); # prints &amp;quot;y: 0&amp;quot;&lt;br /&gt;
print(&amp;quot;z: &amp;quot;, z); # prints &amp;quot;y: 0&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== get_cart_ground_intersection() ===&lt;br /&gt;
Introduced in 2017.2.1, see [[Terrain Detection]].&lt;br /&gt;
&lt;br /&gt;
=== getprop() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = getprop(path[, path[, ...]]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=345|t=Source}}&lt;br /&gt;
|text = Returns the value of a node in the [[Property Tree]] or &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; if the node does not exist or the value is not a number (NaN).&lt;br /&gt;
|param1 = path&lt;br /&gt;
|param1text = There needs to be at least one argument, but there is no limit to the maximum amount of arguments that can be given. The arguments will be concatenated together to form a property tree path. The arguments must be strings, but in FlightGear v3.2 onwards, there is also support (added by {{flightgear commit|34ed79}}) for numeric arguments as indices. See example 2 below.&lt;br /&gt;
|example1 = print(&amp;quot;You have FlightGear v&amp;quot;, getprop(&amp;quot;/sim/version/flightgear&amp;quot;)); # prints FlightGear version&lt;br /&gt;
|example2text = Note that the example below will only work in FlightGear 3.2 and above.&lt;br /&gt;
|example2 = for(var i = 0; i &amp;lt; 8; i += 1){&lt;br /&gt;
    print(&amp;quot;View #&amp;quot;, i + 1, &amp;quot; is named &amp;quot;, getprop(&amp;quot;/sim/view&amp;quot;, i, &amp;quot;name&amp;quot;));&lt;br /&gt;
}&lt;br /&gt;
|example3text = Same as above, but is supported by all versions of FlightGear.&lt;br /&gt;
|example3 = for(var i = 0; i &amp;lt; 8; i += 1){&lt;br /&gt;
    print(&amp;quot;View #&amp;quot;, i + 1, &amp;quot; is named &amp;quot;, getprop(&amp;quot;/sim/view[&amp;quot; ~ i ~ &amp;quot;]/name&amp;quot;));&lt;br /&gt;
}&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== greatCircleMove() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = greatCircleMove([pos, ]course, dist);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1681|t=Source}}&lt;br /&gt;
|text = Calculates a new set of geodetic coordinates using inputs of course and distance, either from the aircraft's current position (by default) or from another set of coordinates. Returns a hash containing two members, ''lat'' and ''lon'' (latitude and longitude respectively).&lt;br /&gt;
|param1 = pos&lt;br /&gt;
|param1text = Optional position to calculate from. If not given, the aircraft's current position will be used. Can be one of:&lt;br /&gt;
:* An &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;runway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;taxiway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fix&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;waypoint&amp;lt;/code&amp;gt; ghost object.&lt;br /&gt;
:* A hash with ''lat'' and ''lon'' members&lt;br /&gt;
:* A &amp;lt;code&amp;gt;geo.Coord&amp;lt;/code&amp;gt; object&lt;br /&gt;
:* A lat/lon pair, that is, a pair of numbers (latitude followed by longitude) separated by a comma: &amp;lt;code&amp;gt;greatCircleMove(lat,lon, ...)&amp;lt;/code&amp;gt;.&lt;br /&gt;
|param2 = course&lt;br /&gt;
|param2text = Course to new set of coordinates, in degrees (in the range 0–360).&lt;br /&gt;
|param3 = dist&lt;br /&gt;
|param3text = Distance in nautical miles to the new set of coordinates.&lt;br /&gt;
|example1 = var pos = greatCircleMove(0,0, 0, 1);&lt;br /&gt;
debug.dump(pos); # print hash with coordinates&lt;br /&gt;
|example2 = var fix = findFixesByID(&amp;quot;POGIC&amp;quot;);&lt;br /&gt;
fix = fix[0];&lt;br /&gt;
var pos = greatCircleMove(fix, 45, 10);&lt;br /&gt;
debug.dump(pos); # print hash with coordinates&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== interpolate() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|private = _interpolate()&lt;br /&gt;
|syntax = interpolate(prop, value1, time1[, value2, time2[, ...]]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=522|t=Part 1}} {{!}} {{fgdata file|Nasal/globals.nas|t=Part 2}}&lt;br /&gt;
|text = Linearly interpolates a node in the property tree to a given value in a specified time. The value/time pairs will be run one after the other in the order that they are passed to the function. Note that the interpolation will continue even when the simulation is paused.&lt;br /&gt;
|param1 = prop&lt;br /&gt;
|param1text = String or &amp;lt;code&amp;gt;props.Node&amp;lt;/code&amp;gt; object that indicates a node in the property tree to be used.&lt;br /&gt;
|param2 = value''n''&lt;br /&gt;
|param2text = Target value to change the property to in the set amount of time. This should be a number.&lt;br /&gt;
|param3 = time''n''&lt;br /&gt;
|param3text = Time in seconds, that will be taken for the interpolation.&lt;br /&gt;
|example1text = Paste the code below into the Nasal Console and execute. Then, open the Property Browser and look for the property. Finally, run the code again, and watch the value of the property change.&lt;br /&gt;
|example1 = setprop(&amp;quot;/test&amp;quot;, 0); # (re-)set property&lt;br /&gt;
interpolate(&amp;quot;/test&amp;quot;,&lt;br /&gt;
    50, 5, # interpolate to 50 in 5 seconds&lt;br /&gt;
    10, 2, # interpolate to 10 in 2 seconds&lt;br /&gt;
    0, 5); # interpolate to 0 in 5 seconds&lt;br /&gt;
|example2 = # Apply the left brake at 20% per second&lt;br /&gt;
var prop = &amp;quot;controls/gear/brake-left&amp;quot;;&lt;br /&gt;
var dist = 1 - getprop(prop);&lt;br /&gt;
if (dist == 1) {&lt;br /&gt;
    interpolate(prop, 1, dist / 0.2);&lt;br /&gt;
}&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== isa() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = isa(object, class);&lt;br /&gt;
|source = {{fgdata file|Nasal/globals.nas|t=Source}}&lt;br /&gt;
|text = Checks if an object is an instance of, or inherits from, a second object (or class), returning 1 (true) if it is and 0 (false) if otherwise.&lt;br /&gt;
|param1 = object&lt;br /&gt;
|param1text = Object to check.&lt;br /&gt;
|param2 = class&lt;br /&gt;
|param2text = Class/object to check that '''object''' inherits from or is an instance of.&lt;br /&gt;
|example1 = var coord = geo.Coord.new();&lt;br /&gt;
if(isa(coord, geo.Coord)){&lt;br /&gt;
    print(&amp;quot;Variable 'coord' is an instance of class 'geo.Coord'&amp;quot;); # this one will be printed&lt;br /&gt;
} else {&lt;br /&gt;
    print(&amp;quot;Variable 'coord' is not an instance of class 'geo.Coord'&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
|example2 = var coord = geo.Coord.new();&lt;br /&gt;
if(isa(coord, props.Node)){&lt;br /&gt;
    print(&amp;quot;Variable 'coord' is an instance of class 'props.Node'&amp;quot;);&lt;br /&gt;
} else {&lt;br /&gt;
    print(&amp;quot;Variable 'coord' is not an instance of class 'props.Node'&amp;quot;); # this one will be printed&lt;br /&gt;
}&lt;br /&gt;
|example3text = The example below demonstrates checking of inheritance.&lt;br /&gt;
|example3 = var Const = {&lt;br /&gt;
    constant: 2,&lt;br /&gt;
    getConst: func {&lt;br /&gt;
        return me.constant;&lt;br /&gt;
    }&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
var Add = {&lt;br /&gt;
    new: func {&lt;br /&gt;
        return { parents: [Add, Const] };&lt;br /&gt;
    },&lt;br /&gt;
&lt;br /&gt;
    addToConst: func(a){&lt;br /&gt;
        return a * me.getConst();&lt;br /&gt;
    }&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
var m = Add.new();&lt;br /&gt;
print(m.addToConst(4));&lt;br /&gt;
&lt;br /&gt;
if(isa(m, Add)) print(&amp;quot;Variable 'm' is an instance of class 'Add'&amp;quot;); # will be printed&lt;br /&gt;
if(isa(m, Const)) print(&amp;quot;Variable 'm' is an instance of class 'Const'&amp;quot;); # will also be printed&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== logprint() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = logprint(priority[, msg[, msg[, ...]]]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=431|t=Source}}&lt;br /&gt;
|text = Concatenates a message and logs it with a given priority level. Unlike {{func link|print()}} and {{func link|printlog()}}, message outputted by this function will be logged in your &amp;lt;code&amp;gt;[[Commonly used debugging tools#fgfs.log|fgfs.log]]&amp;lt;/code&amp;gt; file as coming from the Nasal file itself rather than from {{flightgear file|src/Scripting/NasalSys.cxx}}.&lt;br /&gt;
|param1 = priority&lt;br /&gt;
|param1text = Number specifying the priority level of the outputted message:&lt;br /&gt;
:{{{!}} class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Number !! Debug type&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} 1 {{!!}} Bulk&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} 2 {{!!}} Debug&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} 3 {{!!}} Info&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} 4 {{!!}} Warn&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} 5 {{!!}} Alert&lt;br /&gt;
{{!}}}&lt;br /&gt;
|param2 = msg&lt;br /&gt;
|param2text = The message. There is no limit to the arguments you give give. They will be concatenated together before logging.&lt;br /&gt;
|example1 = # logs the value of pi to three decimal places with log level 3&lt;br /&gt;
logprint(3, &amp;quot;pi = &amp;quot;, sprintf(&amp;quot;%.3f&amp;quot;, math.pi));&lt;br /&gt;
|example2 = logprint(5, &amp;quot;Alert! This is an important message!&amp;quot;);&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== magvar() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = magvar([pos]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1642|t=Source}}&lt;br /&gt;
|text = Returns the {{wikipedia|magnetic variation}} at a given set of coordinates. The table below gives the magnetic model used depending on the version of FlightGear.&lt;br /&gt;
{{{!}} class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! FlightGear versions !! Model !! Reference date&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} 3.6 and above {{!!}} {{wikipedia|World Magnetic Model}} (WMM) 2015 {{!!}} 1 January 2015&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} 0.9.11-pre1 to 3.4 {{!!}} WMM 2005 {{!!}} 1 January 2005&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} 0.7.3 to 0.9.10 {{!!}} WMM 2000 {{!!}} 1 January 2000&lt;br /&gt;
{{!}}}&lt;br /&gt;
|param1 = pos&lt;br /&gt;
|param1text = Optional position to calculate from. If not given, the aircraft's current position will be used. Can be one of:&lt;br /&gt;
:* An &amp;lt;code&amp;gt;airport&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;runway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;taxiway&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fix&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;waypoint&amp;lt;/code&amp;gt; ghost object.&lt;br /&gt;
:* A hash with ''lat'' and ''lon'' members&lt;br /&gt;
:* A &amp;lt;code&amp;gt;geo.Coord&amp;lt;/code&amp;gt; object&lt;br /&gt;
:* A lat/lon pair, that is, a pair of numbers (latitude followed by longitude) separated by a comma: &amp;lt;code&amp;gt;magvar(lat,lon)&amp;lt;/code&amp;gt;.&lt;br /&gt;
|example1 = print(magvar(0, 0)); # prints the magnetic variation at 0, 0&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== maketimer() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = maketimer(interval[, self], function);&lt;br /&gt;
|source = ''Implemented using the {{API Link|flightgear|class|TimerObj}} class.''&amp;lt;br&amp;gt;{{flightgear file|src/Scripting/NasalSys.cxx|l=90|t=Part 1}} {{!}} {{flightgear file|src/Scripting/NasalSys.cxx|l=533|t=Part 2}}&lt;br /&gt;
|version = 2.12&lt;br /&gt;
|commit = {{flightgear commit|ab939f|t=commit}}&lt;br /&gt;
|text = Returns a timer object containing the following methods and members:&lt;br /&gt;
* '''start()''': Starts the timer.&lt;br /&gt;
* '''stop()''': Stops the timer.&lt;br /&gt;
* '''restart(interval)''': Restarts the timer with the given interval.&lt;br /&gt;
* '''singleShot''': Bool showing whether the timer is only to be run once, or continuously until told to stop. Can be both set and read from (see examples).&lt;br /&gt;
* '''isRunning''': Read-only bool telling whether the timer is currently running.&lt;br /&gt;
* '''simulatedTime''': (FG 2017.1+; {{flightgear commit|0af316|t=commit}}) Bool telling whether the timer is using simulated time (which accounts for pause, etc.). Defaults to false (use real time). Can be both read and set. This cannot be changed while the timer is running.&lt;br /&gt;
Unlike {{func link|settimer()}}, which it replaces, &amp;lt;code&amp;gt;maketimer()&amp;lt;/code&amp;gt; provides more control over the timer. In addition, it can help reduce memory usage.&lt;br /&gt;
|param1 = interval&lt;br /&gt;
|param1text = Interval in seconds for the timer.&lt;br /&gt;
|param2 = self&lt;br /&gt;
|param2text = Optional parameter specifying what any &amp;lt;code&amp;gt;'''me'''&amp;lt;/code&amp;gt; references in the function being called will refer to.&lt;br /&gt;
|param3 = function&lt;br /&gt;
|param3text = Function to be called at the given interval.&lt;br /&gt;
|example1 = var timer = maketimer(1, func(){&lt;br /&gt;
    print(&amp;quot;Hello, World!&amp;quot;); # print &amp;quot;Hello, World!&amp;quot; once every second (call timer.stop() to stop it)&lt;br /&gt;
});&lt;br /&gt;
timer.start();&lt;br /&gt;
|example2 = var timer = maketimer(1, math, func(){&lt;br /&gt;
    print(me.math); # 'me' reference is the 'math' namespace&lt;br /&gt;
});&lt;br /&gt;
timer.singleShot = 1; # timer will only be run once&lt;br /&gt;
timer.start();&lt;br /&gt;
|example3 = var timer = maketimer(1, func(){&lt;br /&gt;
    print(&amp;quot;Hello, World!&amp;quot;); # print &amp;quot;Hello, World!&amp;quot; once every second (call timer.stop() to stop it)&lt;br /&gt;
});&lt;br /&gt;
timer.start();&lt;br /&gt;
print(timer.isRunning); # prints 1&lt;br /&gt;
|example4text = In the example below, &amp;quot;Hello, World!&amp;quot; will be printed after one second the first time, and after two seconds thereafter.&lt;br /&gt;
|example4 = var update = func(){&lt;br /&gt;
    print(&amp;quot;Hello, World!&amp;quot;);&lt;br /&gt;
    timer.restart(2); # restarts the timer with a two second interval&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var timer = maketimer(1, update);&lt;br /&gt;
timer.singleShot = 1;&lt;br /&gt;
timer.start();&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== maketimestamp() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = maketimestamp()&lt;br /&gt;
|source = ''Implemented using the {{API Link|flightgear|class|TimeStampObj}} class.''&amp;lt;br&amp;gt;{{flightgear file|src/Scripting/NasalSys.cxx|l=214|t=Part 1}} {{!}} {{flightgear file|src/Scripting/NasalSys.cxx|l=589|t=Part 2}}&lt;br /&gt;
|version = 2019.2&lt;br /&gt;
|commit = {{flightgear commit|7db06300|t=commit}}&lt;br /&gt;
|text = Returns a time stamp object to allow high resolution timing of Nasal operations. When created the timer will automatically be stamped. The object has the following methods:&lt;br /&gt;
* '''stamp()''': Resets the timing operation. Call this first.&lt;br /&gt;
* '''elapsedMSec()''': returns number of milliseconds elapsed since stamp() called. Resolution may vary depending on platform but is usually at least millisecond accuracy.&lt;br /&gt;
* '''elapsedUSec()''': returns number of microseconds elapsed since stamp() called. Resolution may vary depending on platform but is usually at least millisecond accuracy.&lt;br /&gt;
|&lt;br /&gt;
|example1text = In the example below the number of milliseconds elapsed will be printed.&lt;br /&gt;
|example1 = var timestamp = maketimestamp();&lt;br /&gt;
timestamp.stamp();&lt;br /&gt;
print(timestamp.elapsedMSec(), &amp;quot;ms elapsed&amp;quot;);&lt;br /&gt;
print(timestamp.elapsedMSec(), &amp;quot;ms elapsed&amp;quot;);&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== md5() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = md5(string);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=758|t=Source}}&lt;br /&gt;
|version = 3.2&lt;br /&gt;
|commit = {{flightgear commit|cfbf9e|t=commit}}&lt;br /&gt;
|text = Returns a the {{wikipedia|MD5}} hash (as a string) of the inputted string.&lt;br /&gt;
|param1 = string&lt;br /&gt;
|param1text = String the generate the hash of. Mandatory.&lt;br /&gt;
|example1text = The below code should output &amp;lt;code&amp;gt;65a8e27d8879283831b664bd8b7f0ad4&amp;lt;/code&amp;gt;.&lt;br /&gt;
|example1 = print(md5(&amp;quot;Hello, World!&amp;quot;));&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== navinfo() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = navinfo(lat, lon, type, id);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1453|t=Source}}&lt;br /&gt;
|text = Returns vector &amp;lt;code&amp;gt;navaid&amp;lt;/code&amp;gt; ghost objects matching the given '''type''' and '''id''' or &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; on error.&lt;br /&gt;
|param1 = lat ''and'' lon&lt;br /&gt;
|param1text = If given, the returned navaids will be put into order of ascending distance from the location.&lt;br /&gt;
|param2 = type&lt;br /&gt;
|param2text = Narrows the search to the given type. Must be one of &amp;quot;any,&amp;quot; &amp;quot;fix,&amp;quot; &amp;quot;vor,&amp;quot; &amp;quot;ndb,&amp;quot; &amp;quot;ils,&amp;quot; &amp;quot;dme,&amp;quot; or &amp;quot;tacan.&amp;quot; Defaults to the equivalent of &amp;quot;any.&amp;quot;&lt;br /&gt;
|param3 = id&lt;br /&gt;
|param3text = ID to search for. Note that, although all the parameters are technically optional, this parameter must be given, otherwise an empty vector will be returned.&lt;br /&gt;
|example1 = navinfo(&amp;quot;vor&amp;quot;); # returns all VORs&lt;br /&gt;
|example2 = navinfo(&amp;quot;HAM&amp;quot;); # return all navaids whose names start with &amp;quot;HAM&amp;quot;&lt;br /&gt;
|example3 = navinfo(&amp;quot;vor&amp;quot;, &amp;quot;HAM&amp;quot;); # return all VORs whose names start with &amp;quot;HAM&amp;quot;&lt;br /&gt;
|example4 = navinfo(34,48,&amp;quot;vor&amp;quot;,&amp;quot;HAM&amp;quot;); # return all VORs whose names start with &amp;quot;HAM&amp;quot; and sorted by distance relative to 34°, 48°&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== parse_markdown() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = parse_markdown(markdown);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=745|t=Part 1}} {{!}} {{simgear file|simgear/misc/SimpleMarkdown.cxx|t=Part 2}} &lt;br /&gt;
|version = 3.2&lt;br /&gt;
|text = Parses a string containing {{wikipedia|Markdown}} and returns the result as a string. As of FlightGear 2016.1, it is just a simple parser, and does the following:&lt;br /&gt;
* It strips whitespace from the beginning of the string.&lt;br /&gt;
* It supports [http://daringfireball.net/projects/markdown/syntax#p paragraphs and line breaks].&lt;br /&gt;
* It collapses whitespace.&lt;br /&gt;
* It converts unordered [http://daringfireball.net/projects/markdown/syntax#list lists] to use a bullet character (&amp;amp;bull;). Note that the bullet character is implemented in hexadecimal UTF-8 character bytes (&amp;lt;code&amp;gt;E2 80 A2&amp;lt;/code&amp;gt;), as so may not work properly when the being displayed in an encoding other than UTF-8.&lt;br /&gt;
|param1 = markdown&lt;br /&gt;
|param1text = String containing Markdown to be parsed.&lt;br /&gt;
|example1text = &lt;br /&gt;
Save the below code as &amp;lt;tt&amp;gt;''[[$FG_ROOT]]/gui/dialogs/test.xml''&amp;lt;/tt&amp;gt;, then run the Nasal code below it to open the dialog. To change the markdown to be parsed, simply change the code in the highlighted section, save it, and reload the GUI (&amp;lt;tt&amp;gt;Debug &amp;gt; Reload GUI&amp;lt;/tt&amp;gt;).&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot; highlight=&amp;quot;41-50&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;PropertyList&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;name&amp;gt;test&amp;lt;/name&amp;gt;&lt;br /&gt;
&amp;lt;layout&amp;gt;vbox&amp;lt;/layout&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;group&amp;gt;&lt;br /&gt;
    &amp;lt;layout&amp;gt;hbox&amp;lt;/layout&amp;gt;&lt;br /&gt;
    &lt;br /&gt;
    &amp;lt;empty&amp;gt;&lt;br /&gt;
        &amp;lt;stretch&amp;gt;true&amp;lt;/stretch&amp;gt;&lt;br /&gt;
    &amp;lt;/empty&amp;gt;&lt;br /&gt;
    &lt;br /&gt;
    &amp;lt;text&amp;gt;&lt;br /&gt;
        &amp;lt;label&amp;gt;parse_markdown() test dialog&amp;lt;/label&amp;gt;&lt;br /&gt;
    &amp;lt;/text&amp;gt;&lt;br /&gt;
    &lt;br /&gt;
    &amp;lt;empty&amp;gt;&lt;br /&gt;
        &amp;lt;stretch&amp;gt;true&amp;lt;/stretch&amp;gt;&lt;br /&gt;
    &amp;lt;/empty&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    &amp;lt;button&amp;gt;&lt;br /&gt;
        &amp;lt;legend&amp;gt;&amp;lt;/legend&amp;gt;&lt;br /&gt;
        &amp;lt;pref-width&amp;gt;16&amp;lt;/pref-width&amp;gt;&lt;br /&gt;
        &amp;lt;pref-height&amp;gt;16&amp;lt;/pref-height&amp;gt;&lt;br /&gt;
        &amp;lt;binding&amp;gt;&lt;br /&gt;
            &amp;lt;command&amp;gt;dialog-close&amp;lt;/command&amp;gt;&lt;br /&gt;
        &amp;lt;/binding&amp;gt;&lt;br /&gt;
    &amp;lt;/button&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/group&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;canvas&amp;gt;&lt;br /&gt;
    &amp;lt;name&amp;gt;Canvas plot&amp;lt;/name&amp;gt;&lt;br /&gt;
    &amp;lt;stretch&amp;gt;true&amp;lt;/stretch&amp;gt;&lt;br /&gt;
    &amp;lt;pref-width&amp;gt;400&amp;lt;/pref-width&amp;gt;&lt;br /&gt;
    &amp;lt;pref-height&amp;gt;300&amp;lt;/pref-height&amp;gt;&lt;br /&gt;
    &amp;lt;nasal&amp;gt;&lt;br /&gt;
        &amp;lt;load&amp;gt;&amp;lt;![CDATA[&lt;br /&gt;
var text = 'Items:&lt;br /&gt;
* apples&lt;br /&gt;
* oranges&lt;br /&gt;
* pears&lt;br /&gt;
&lt;br /&gt;
Some text.&lt;br /&gt;
Some more items:&lt;br /&gt;
* apples&lt;br /&gt;
* oranges&lt;br /&gt;
* pears';&lt;br /&gt;
&lt;br /&gt;
var parsed = parse_markdown(text);&lt;br /&gt;
&lt;br /&gt;
var root_canvas = canvas.get(cmdarg());&lt;br /&gt;
root_canvas.setColorBackground(255, 255, 255);&lt;br /&gt;
var root = root_canvas.createGroup();&lt;br /&gt;
&lt;br /&gt;
var text_dis = root.createChild(&amp;quot;text&amp;quot;)&lt;br /&gt;
    .setText(parsed)&lt;br /&gt;
    .setTranslation(5, 5)&lt;br /&gt;
    .setFont(&amp;quot;LiberationFonts\LiberationSans-Regular.ttf&amp;quot;)&lt;br /&gt;
    .setFontSize(15)&lt;br /&gt;
    .setColor(0, 0, 0)&lt;br /&gt;
    .setDrawMode(canvas.Text.TEXT)&lt;br /&gt;
    .setAlignment(&amp;quot;left-top&amp;quot;);&lt;br /&gt;
        ]]&amp;gt;&amp;lt;/load&amp;gt;&lt;br /&gt;
    &amp;lt;/nasal&amp;gt;&lt;br /&gt;
&amp;lt;/canvas&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/PropertyList&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|example1 = fgcommand(&amp;quot;dialog-show&amp;quot;, {&amp;quot;dialog-name&amp;quot;: &amp;quot;test&amp;quot;});&lt;br /&gt;
|example2text = The example below parses Markdown and outputs it in a HTML document. The parsed text is placed in &amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot; inline&amp;gt;&amp;lt;pre&amp;gt;&amp;lt;/pre&amp;gt;&amp;lt;/syntaxhighlight&amp;gt; tags. To change the Markdown to be parsed, simply edit the variable &amp;lt;tt&amp;gt;markdown&amp;lt;/tt&amp;gt; at the top of the code.&lt;br /&gt;
|example2 = &amp;lt;nowiki&amp;gt;var markdown = 'Items:&lt;br /&gt;
* apples&lt;br /&gt;
* oranges&lt;br /&gt;
* pears&lt;br /&gt;
&lt;br /&gt;
Some text.&lt;br /&gt;
Some more items:&lt;br /&gt;
* apples&lt;br /&gt;
* oranges&lt;br /&gt;
* pears';&lt;br /&gt;
&lt;br /&gt;
var parsed = parse_markdown(markdown);&lt;br /&gt;
&lt;br /&gt;
debug.dump(parsed);&lt;br /&gt;
&lt;br /&gt;
var path = string.normpath(getprop(&amp;quot;/sim/fg-home&amp;quot;) ~ '/Export/parse_markdown()-test.html');&lt;br /&gt;
&lt;br /&gt;
var file = io.open(path, &amp;quot;w&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
var html = &amp;quot;&amp;lt;!DOCTYPE html&amp;gt;\n\n&amp;lt;html&amp;gt;\n\n&amp;lt;head&amp;gt;\n\t&amp;lt;meta charset=\&amp;quot;UTF-8\&amp;quot;&amp;gt;\n\t&amp;lt;title&amp;gt;parse_markdown() test generated by Nasal&amp;lt;/title&amp;gt;\n&amp;lt;/head&amp;gt;\n\n&amp;lt;body&amp;gt;\n\t&amp;lt;pre&amp;gt;&amp;quot; ~ parsed ~ &amp;quot;&amp;lt;/pre&amp;gt;\n&amp;lt;/body&amp;gt;\n\n&amp;lt;/html&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
io.write(file, html);&lt;br /&gt;
io.close(file);&lt;br /&gt;
print(&amp;quot;Done, file ready for viewing (&amp;quot; ~ path ~ &amp;quot;)&amp;quot;);&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== parsexml() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = parsexml(path[, start[, end[, data[, pro_ins]]]]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=710|t=Source}}&lt;br /&gt;
|text = This function is an interface into the built-in [http://expat.sourceforge.net/ Expat XML parser]. The absolute path to the file is returned as string, or &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; is returned on error.&lt;br /&gt;
|param1 = path&lt;br /&gt;
|param1text = Mandatory absolute path to the XML file to be parsed.&lt;br /&gt;
|param2 = start&lt;br /&gt;
|param2text = Optional callback function that will be called for every starting tag. The function should take two argument: the tag name and a hash containing attributes.&lt;br /&gt;
|param3 = end&lt;br /&gt;
|param3text = Optional callback function that will be called for every ending tag. The function should take one argument: the tag name.&lt;br /&gt;
|param4 = data&lt;br /&gt;
|param4text = Optional callback function that will be called for every piece of data within a set of tags. The function should take one argument: the data as a string.&lt;br /&gt;
|param5 = pro_ins&lt;br /&gt;
|param5text = Optional callback function that will be called for every {{wikipedia|Processing Instruction|processing instruction}}. The function should take two argument: the target and the data string.&lt;br /&gt;
|example1text = Save the below XML code in &amp;lt;tt&amp;gt;''[[$FG_HOME]]/Export/demo.xml''&amp;lt;/tt&amp;gt;. Then, execute the Nasal code below in the Nasal Console. The XML will be parsed and each bit of the code will be printed.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;?xml-stylesheet type=&amp;quot;text/xsl&amp;quot; href=&amp;quot;style.xsl&amp;quot;?&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;foo&amp;gt;&lt;br /&gt;
  &amp;lt;blah type=&amp;quot;string&amp;quot;&amp;gt;&amp;lt;![CDATA[ &amp;lt;sender&amp;gt;John Smith&amp;lt;/sender&amp;gt; ]]&amp;gt;&amp;lt;/blah&amp;gt;&lt;br /&gt;
  &amp;lt;blah2 type=&amp;quot;string&amp;quot;&amp;gt;Orange &amp;amp;amp; lemons&amp;lt;/blah2&amp;gt;&lt;br /&gt;
&amp;lt;/foo&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|example1 = var start = func(name, attr){&lt;br /&gt;
    print(&amp;quot;Starting tag: '&amp;quot;, name, &amp;quot;'&amp;quot;);&lt;br /&gt;
    foreach(var a; keys(attr)){&lt;br /&gt;
        print(&amp;quot;\twith attribute &amp;quot;, a, '=&amp;quot;', attr[a], '&amp;quot;');&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var end = func(name){&lt;br /&gt;
    print(&amp;quot;Ending tag: '&amp;quot;, name, &amp;quot;'&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var data = func(data){&lt;br /&gt;
    print(&amp;quot;Data = '&amp;quot;, data, &amp;quot;'&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var pro_instr = func(target, data){&lt;br /&gt;
    print(&amp;quot;Processing instruction: target = '&amp;quot;, target, &amp;quot;', data = '&amp;quot;, data, &amp;quot;'&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
parsexml(getprop(&amp;quot;/sim/fg-home&amp;quot;) ~ '/Export/demo.xml', start, end, data, pro_instr);&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== print() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = print(data[, data[, ...]]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=415|t=Source}}&lt;br /&gt;
|text = Concatenates its arguments and then prints it to the terminal and the [[Commonly used debugging tools#fgfs.log|log]]. Note that a newline is automatically added.&lt;br /&gt;
|param1 = data&lt;br /&gt;
|param1text = Data to print. Only strings and numbers can be printed; other data types will not be. There many be any number of arguments; they will just be concatenated together.&lt;br /&gt;
|example1 = print(&amp;quot;Just&amp;quot;, &amp;quot; a &amp;quot;, &amp;quot;test&amp;quot;); # prints &amp;quot;Just a test&amp;quot;&lt;br /&gt;
|example2 = print(&amp;quot;pi = &amp;quot;, math.pi); # prints &amp;quot;pi = 3.141592...&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== printf() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = printf(format[, arg[, arg, [...]]]);&lt;br /&gt;
|source = {{fgdata file|Nasal/globals.nas|t=Source}}&lt;br /&gt;
|text = Creates and prints a formatted string. For a description of its arguments, see {{func link|sprintf()}} (it is, in fact, implemented using &amp;lt;code&amp;gt;sprintf()&amp;lt;/code&amp;gt;).&lt;br /&gt;
|example1 = printf(&amp;quot;In hexadecimal, 100000 = %X&amp;quot;, 186A0); # prints &amp;quot;In hexadecimal, 100000 = 186A0&amp;quot;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== printlog() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = printlog(level, data[, data[, ...]]);&lt;br /&gt;
|source = {{fgdata file|Nasal/globals.nas|t=Source}}&lt;br /&gt;
|text = Prints the given message with the given log level. If the log level is higher or equal to &amp;lt;code&amp;gt;/sim/logging/priority&amp;lt;/code&amp;gt;, it is printed.&lt;br /&gt;
|param1 = level&lt;br /&gt;
|param1text = Mandatory log level as a string. Must be one of &amp;quot;none,&amp;quot; &amp;quot;bulk,&amp;quot; &amp;quot;debug,&amp;quot; &amp;quot;info,&amp;quot; &amp;quot;warn,&amp;quot; or &amp;quot;alert.&amp;quot; Note that &amp;quot;none&amp;quot; will mean that the message will never be printed.&lt;br /&gt;
|param2 = data&lt;br /&gt;
|param2text = Data to be printed. Only strings and numbers will be printed; others will not be. There may be any number of arguments; they will just be concatenated together.&lt;br /&gt;
|example1 = printlog(&amp;quot;alert&amp;quot;, &amp;quot;This is an alert&amp;quot;); # message will be printed&lt;br /&gt;
|example2 = printlog(&amp;quot;info&amp;quot;, &amp;quot;Just informing you about something&amp;quot;); # message will be printed only if log level is set to &amp;quot;info&amp;quot; or less&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== rand() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = rand();&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=554|t=Source}}&lt;br /&gt;
|text = Returns a random floating point number between 0 (inclusive) and 1 (exclusive). It takes no arguments.&lt;br /&gt;
|example1 = print(rand()); # prints random number&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== registerFlightPlanDelegate() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = registerFlightPlanDelegate(init_func);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1879|t=Source}}&lt;br /&gt;
|text = Registers a flight plan delegate. See &amp;lt;tt&amp;gt;''{{fgdata file|Nasal/route_manager.nas|t=$FG_ROOT/Nasal/route_manager.nas}}''&amp;lt;/tt&amp;gt; for examples.&lt;br /&gt;
|param1 = init_func&lt;br /&gt;
|param1text = Initialization function which will be called during FlightGear's startup.&lt;br /&gt;
}}&lt;br /&gt;
=== removecommand() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = removecommand(cmd);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=668|t=Source}}&lt;br /&gt;
|text = Removes the given fgcommand. Returns &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt;.&lt;br /&gt;
{{caution|This will remove '''any''' [[fgcommands|fgcommand]], even those implemented in C++, so use with caution!}}&lt;br /&gt;
|param1 = cmd&lt;br /&gt;
|param1text = String specifying the name of the command to remove.&lt;br /&gt;
|example1 = addcommand(&amp;quot;hello&amp;quot;, func(){&lt;br /&gt;
    print(&amp;quot;Hello&amp;quot;);&lt;br /&gt;
});&lt;br /&gt;
fgcommand(&amp;quot;hello&amp;quot;); # &amp;quot;Hello&amp;quot; will be printed&lt;br /&gt;
removecommand(&amp;quot;hello&amp;quot;); # removes it&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== removelistener() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = removelistener(id);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=1384|t=Part 1}} {{!}} {{flightgear file|src/Scripting/NasalSys.cxx|l=506|t=Part 2}}&lt;br /&gt;
|text = Removes and deactivates the given listener and returns the number of listeners left or &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; on error.&lt;br /&gt;
{{note|It is good practice to remove listeners when they are not required anymore. This prevents the listeners reducing FlightGear's run performance.}}&lt;br /&gt;
|param1 = id&lt;br /&gt;
|param1text = ID of listener as returned by {{func link|setlistener()}}.&lt;br /&gt;
|example1 = var ls = setlistener(&amp;quot;/sim/test&amp;quot;, func(){&lt;br /&gt;
    print(&amp;quot;Property '/sim/test' has been changed&amp;quot;);&lt;br /&gt;
});&lt;br /&gt;
setprop(&amp;quot;/sim/test&amp;quot;, &amp;quot;blah&amp;quot;); # trigger listener&lt;br /&gt;
var rem = removelistener(ls); # remove listener&lt;br /&gt;
print(&amp;quot;There are &amp;quot;, rem, &amp;quot; listeners remaining&amp;quot;);&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== resolvepath() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = resolvepath(path);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=604|t=Source}}&lt;br /&gt;
|text = Takes a relative path as a string and uses [[SimGear]]'s path-resolving framework to return an absolute path as a string. If the path could not be resolved, an empty string is returned. See [[Resolving Paths]] for a detailed description of the algorithm. This function can also be used to check if a file exists.&lt;br /&gt;
|param1 = path&lt;br /&gt;
|param1text = Relative path to be completed.&lt;br /&gt;
|example1 = print(resolvepath(&amp;quot;Nasal/globals.nas&amp;quot;)); # prints the equivalent of $FG_ROOT/Nasal/globals.nas&lt;br /&gt;
|example2 = print(resolvepath(&amp;quot;blah&amp;quot;)); # prints nothing; could not be resolved&lt;br /&gt;
|example3 = var file_path = resolvepath(&amp;quot;Aircraft/SenecaII/some-file&amp;quot;);&lt;br /&gt;
if (file_path != &amp;quot;&amp;quot;){&lt;br /&gt;
    gui.popupTip(&amp;quot;some-file found&amp;quot;, 2);&lt;br /&gt;
} else {&lt;br /&gt;
    gui.popupTip(&amp;quot;some-file not found&amp;quot;, 2);&lt;br /&gt;
}&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== setlistener() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = setlistener(node, code[, init[, type]]);&lt;br /&gt;
|private = _setlistener()&lt;br /&gt;
|source = {{flightgear file|src/Scripting/Nasal|l=499|t=Part 1}} {{!}} {{flightgear file|src/Scripting/Nasal|l=1350|t=Part 2}}&amp;lt;br&amp;gt;''Listener implemented using the {{API Link|flightgear|class|FGNasalListener}} class.''&lt;br /&gt;
|text = Creates a listener which will be triggered when the given property is changed (depending on the '''type'''). A unique integer ID is returned; this can later be used as the argument to {{func link|removelistener()}}.&lt;br /&gt;
{{note|Listeners are known to be a source of resource leaks. To avoid this, please take measures such as:&lt;br /&gt;
* Using {{func link|removelistener()}} when they are not needed any more.&lt;br /&gt;
* Using a single initialization listener.&lt;br /&gt;
* Avoiding tying listeners to properties that are rapidly updated (e.g., many times per frame).&lt;br /&gt;
}}&lt;br /&gt;
|param1 = node&lt;br /&gt;
|param1text = Mandatory string or &amp;lt;code&amp;gt;props.Node&amp;lt;/code&amp;gt; object pointing to a property in the [[Property Tree]].&lt;br /&gt;
|param2 = code&lt;br /&gt;
|param2text = Mandatory callback function to execute when the listener is triggered. The function can take up to four arguments in the following order:&lt;br /&gt;
* '''changed''': a &amp;lt;code&amp;gt;props.Node&amp;lt;/code&amp;gt; object pointing to the changed node.&lt;br /&gt;
* '''listen''': a &amp;lt;code&amp;gt;props.Node&amp;lt;/code&amp;gt; object pointing to the listened-to node. Note that this argument maybe different depending on the '''type'''.&lt;br /&gt;
* '''mode''': an integer telling how the listener was triggered. 0 means that the value was changed. 1 means that a child property was added. -1 means that a child property was removed.&lt;br /&gt;
* '''is_child''': boolean telling whether '''changed''' is a child property of the listened-to '''node''' or not. 1 (true) if it is, 0 (false) otherwise.&lt;br /&gt;
|param3 = init&lt;br /&gt;
|param3text = If set to 1 (true), the listener will additionally be triggered when it is created. This argument is optional and defaults to 0 (false).&lt;br /&gt;
|param4 = type&lt;br /&gt;
|param4text = Integer specifying the listener's behavior. 0 means that the listener will only trigger when the property is changed. 1 means that the trigger will always be triggered when the property is written to. 2 will mean that the listener will be triggered even if child properties are modified. This argument is optional and defaults to 1.&lt;br /&gt;
|example1 = var prop = props.globals.initNode(&amp;quot;/sim/test&amp;quot;, &amp;quot;&amp;quot;, &amp;quot;STRING&amp;quot;); # create property&lt;br /&gt;
var id = setlistener(&amp;quot;/sim/test&amp;quot;, func(n){ # create listener&lt;br /&gt;
    print(&amp;quot;Value: &amp;quot;, n.getValue());&lt;br /&gt;
});&lt;br /&gt;
setprop(&amp;quot;/sim/test&amp;quot;, &amp;quot;blah&amp;quot;); # trigger listener&lt;br /&gt;
removelistener(id); # remove listener&lt;br /&gt;
prop.remove(); # remove property&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== setprop() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = setprop(path[, path[, ...]], value);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=385|t=Source}}&lt;br /&gt;
|text = Sets the value of a property in the [[Property Tree]]. If the property does not exist, it will be created. Returns 1 (true) on success or 0 (false) if there was an error.&lt;br /&gt;
{{note|If you want to remove a property, you will have to use one of the &amp;lt;code&amp;gt;props&amp;lt;/code&amp;gt; helpers:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
props.globals.getNode(&amp;quot;foo/bar&amp;quot;).remove(); # take out the complete node&lt;br /&gt;
props.globals.getNode(&amp;quot;foo&amp;quot;).removeChild(&amp;quot;bar&amp;quot;); # take out a certain child node&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
|param1 = path&lt;br /&gt;
|param1text = There needs to be at least one '''path''' argument, but there is no limit to the maximum amount that can be given. They will be concatenated together to form a property tree path. The arguments must be strings, but in FlightGear v3.2 onwards, there also is support (added by {{flightgear commit|34ed79}}) for numeric arguments as indices. See example 2 below.&lt;br /&gt;
|param2 = value&lt;br /&gt;
|param2text = Value to write to the given property. Must be either a string or a number.&lt;br /&gt;
|example1 = setprop(&amp;quot;/sim/demo&amp;quot;, &amp;quot;This is a demo&amp;quot;);&lt;br /&gt;
|example2text = Note that the example below will only work in FlightGear 3.2 and above.&lt;br /&gt;
|example2 = for(var i = 0; i &amp;lt; 3; i += 1){&lt;br /&gt;
    setprop(&amp;quot;/sim/demo&amp;quot;, i, &amp;quot;Demo #&amp;quot; ~ i));&lt;br /&gt;
}&lt;br /&gt;
|example3text = Same as above, but is supported by all versions of FlightGear.&lt;br /&gt;
|example3 = for(var i = 0; i &amp;lt; 3; i += 1){&lt;br /&gt;
    setprop(&amp;quot;/sim/demo[&amp;quot; ~ i ~ &amp;quot;]&amp;quot;, &amp;quot;Demo #&amp;quot; ~ i));&lt;br /&gt;
}&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== settimer() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = settimer(function, delta[, realtime]);&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=499|t=Part 1}} {{!}} {{flightgear file|src/Scripting/NasalSys.cxx|l=1286|t=Part 2}}&lt;br /&gt;
|text = Runs the given function a specified amount of seconds after the current time. Returns &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt;.&lt;br /&gt;
{{note|Improper use of &amp;lt;code&amp;gt;settimer()&amp;lt;/code&amp;gt; may cause resource leaks. It is also highly recommended that the newer {{func link|maketimer()}} should be used instead of this function.}} &lt;br /&gt;
|param1 = function&lt;br /&gt;
|param1text = Mandatory function that will be called. It may be necessary to enclose code in an anonymous function (see example).&lt;br /&gt;
|param2 = delta&lt;br /&gt;
|param2text = Mandatory amount of time in seconds after which the function will be called.&lt;br /&gt;
|param3 = realtime&lt;br /&gt;
|param3text = If 1 (true), &amp;quot;real time&amp;quot; will be used instead of &amp;quot;simulation time.&amp;quot; Defaults to 0 (false). Note that if &amp;quot;simulation time&amp;quot; is used, the timer will not run while FlightGear is paused.&lt;br /&gt;
|example1 = var myFunc = func(){&lt;br /&gt;
    print(&amp;quot;Hello&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
settimer(myFunc, 2); # runs myFunc after 2 seconds&lt;br /&gt;
|example2 = var sqr = func(a){&lt;br /&gt;
    return a * a;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
settimer(func(){&lt;br /&gt;
    print(sqr(2)); # will print 4 after 2 seconds&lt;br /&gt;
}, 2);&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== srand() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = srand();&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=559|t=Source}}&lt;br /&gt;
|text = Makes the pseudorandom number generator (see {{func link|rand()}}) generate a new {{wikipedia|random seed|noicon=1}} based on time. Returns 0.&lt;br /&gt;
}}&lt;br /&gt;
=== systime() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = systime();&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalSys.cxx|l=770|t=Source}}&lt;br /&gt;
|text = Returns the {{wikipedia|Unix time}} (seconds since since 00:00:00 UTC, 1/1/1970) as a floating point number with high resolution. This function is useful for benchmarking purposes (see example 2).&lt;br /&gt;
{{note|1=High resolution timers under Windows can produce inaccurate or fixed sub-millisecond results.&amp;lt;ref&amp;gt;{{cite web|url=http://forum.flightgear.org/viewtopic.php?f=30&amp;amp;t=29259|title=Nasal: systime() ??!?|author=Necolatis|date=Apr 2nd, 2016}}&amp;lt;/ref&amp;gt; This is due to the underlying {{func link|GetSystemTimeAsFileTime()|link=https://msdn.microsoft.com/en-us/library/windows/desktop/ms724397(v=vs.85).aspx}} API call, which depends on hardware availability of suitable high resolution timers. See also [https://msdn.microsoft.com/en-us/library/windows/desktop/dn553408(v=vs.85).aspx Acquiring high-resolution time stamps]}}&lt;br /&gt;
|example1 = print(&amp;quot;Unix time: &amp;quot;, systime()); # prints Unix time&lt;br /&gt;
|example2 = var myFunc = func(){&lt;br /&gt;
    for(var i = 0; i &amp;lt;= 10; i += 1){&lt;br /&gt;
        print(&amp;quot;Interation #&amp;quot;, i);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
var t = systime(); # record time&lt;br /&gt;
myFunc(); # run function&lt;br /&gt;
var t2 = systime(); # record new time&lt;br /&gt;
print(&amp;quot;myFunc() took &amp;quot;, t2 - t, &amp;quot; seconds&amp;quot;); # print result&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== thisfunc() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = thisfunc();&lt;br /&gt;
|source = {{fgdata file|Nasal/globals.nas|t=Source}}&lt;br /&gt;
|text = Returns the function from which this function is called. This allows a function to reliably and safely call itself from within a closure.&lt;br /&gt;
|example1 = var stringify_vec = func(input){&lt;br /&gt;
    if (typeof(input) == &amp;quot;scalar&amp;quot;){&lt;br /&gt;
        return sprintf(&amp;quot;%s&amp;quot;, input);&lt;br /&gt;
    } elsif (typeof(input) == &amp;quot;vector&amp;quot;) {&lt;br /&gt;
        if (size(input) == 0) return &amp;quot;[]&amp;quot;;&lt;br /&gt;
        var this = thisfunc();&lt;br /&gt;
        var buffer = &amp;quot;[&amp;quot;;&lt;br /&gt;
        for(var i = 0; i &amp;lt; size(input); i += 1){&lt;br /&gt;
            buffer ~= this(input[i]);&lt;br /&gt;
            if (i == size(input) - 1) {&lt;br /&gt;
                buffer ~= &amp;quot;]&amp;quot;;&lt;br /&gt;
            } else {&lt;br /&gt;
                buffer ~= &amp;quot;, &amp;quot;;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        return buffer;&lt;br /&gt;
    } else {&lt;br /&gt;
        die(&amp;quot;stringify_vec(): Error! Invalid input. Must be a vector or scalar&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var test_vec = [&amp;quot;a&amp;quot;, &amp;quot;b&amp;quot;, &amp;quot;c&amp;quot;, 1, 2, 3];&lt;br /&gt;
debug.dump(stringify_vec(test_vec)); # prints &amp;quot;[a, b, c, 1, 2, 3]&amp;quot;&lt;br /&gt;
test_vec = [];&lt;br /&gt;
debug.dump(stringify_vec(test_vec)); # prints &amp;quot;[]&amp;quot;&lt;br /&gt;
test_vec = {};&lt;br /&gt;
debug.dump(stringify_vec(test_vec)); # will throw an error&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== tileIndex() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = tileIndex();&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1720|t=Source}}&lt;br /&gt;
|text = Returns the index of the tile at the aircraft's current position as a string. This corresponds to the name of the STG file of the tile. For example, at [[KSFO]], this would be &amp;lt;code&amp;gt;942050&amp;lt;/code&amp;gt;, corresponding to &amp;lt;tt&amp;gt;''[[$FG_SCENERY]]/Terrain/w130n30/w123n3/942050.stg''&amp;lt;/tt&amp;gt;.&lt;br /&gt;
|example1 = print(tileIndex()); # print index&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== tilePath() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = tilePath();&lt;br /&gt;
|source = {{flightgear file|src/Scripting/NasalPositioned.cxx|l=1712|t=Source}}&lt;br /&gt;
|text = Returns the base path of the tile at the aircraft's current position as a string. For example, at KSFO, this would be &amp;lt;code&amp;gt;w130n30/w123n3&amp;lt;/code&amp;gt;, corresponding to &amp;lt;tt&amp;gt;''[[$FG_SCENERY]]/Terrain/w130n30/w123n3''&amp;lt;/tt&amp;gt;.&lt;br /&gt;
|example1 = print(tilePath()); # print path&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== values() ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = values(hash);&lt;br /&gt;
|source = {{fgdata file|Nasal/globals.nas|t=Source}}&lt;br /&gt;
|text = Returns a vector containing the values of the given hash.&lt;br /&gt;
|param1 = hash&lt;br /&gt;
|param1text = Mandatory hash to get the values of.&lt;br /&gt;
|example1 = var hash = {&lt;br /&gt;
    &amp;quot;a&amp;quot;: 1,&lt;br /&gt;
    &amp;quot;b&amp;quot;: 2,&lt;br /&gt;
    &amp;quot;c&amp;quot;: 3&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
foreach(var val; values(hash)){&lt;br /&gt;
    print(val);&lt;br /&gt;
}&lt;br /&gt;
|example2text = The below example does exactly the same thing as the above example, but does not use &amp;lt;code&amp;gt;values()&amp;lt;/code&amp;gt;:&lt;br /&gt;
|example2 = var hash = {&lt;br /&gt;
    &amp;quot;a&amp;quot;: 1,&lt;br /&gt;
    &amp;quot;b&amp;quot;: 2,&lt;br /&gt;
    &amp;quot;c&amp;quot;: 3&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
foreach(var key; keys(hash)){&lt;br /&gt;
    print(hash[key]);&lt;br /&gt;
}&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Variables ==&lt;br /&gt;
Various global constants (technically variables) are provided for use in converting between different units. They are all found in {{fgdata file|Nasal/globals.nas|text=$FG_ROOT/Nasal/globals.nas}}.&lt;br /&gt;
&lt;br /&gt;
=== D2R ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var radians = degrees * D2R;&lt;br /&gt;
|text = Converts an angle from degrees to radians when multiplied by the angle in degrees. Equal to &amp;lt;code&amp;gt;π / 180&amp;lt;/code&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
=== FPS2KT ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var knots = feet_per_second * FPS2KT;&lt;br /&gt;
|text = Converts a velocity from feet per second to knots when multiplied by the velocity in feet per second. Approximately equal to 0.5925.&lt;br /&gt;
}}&lt;br /&gt;
=== FT2M ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var metres = feet * FT2M;&lt;br /&gt;
|text = Converts a length from feet to metres when multiplied by the length in feet. Equal to 0.3048.&lt;br /&gt;
}}&lt;br /&gt;
=== GAL2L ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var litres = gallons * GAL2L;&lt;br /&gt;
|text = Converts a volume from US liquid gallons to litres when multiplied by the volume in gallons. Approximately equal to 3.7854.&lt;br /&gt;
}}&lt;br /&gt;
=== IN2M ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var metres = inches * IN2M;&lt;br /&gt;
|text = Converts a length from inches to metres when multiplied by the length in inches. Equal to 0.0254.&lt;br /&gt;
}}&lt;br /&gt;
=== KG2LB ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var pounds = kilograms * KG2LB;&lt;br /&gt;
|text = Converts a mass from kilograms to pounds when multiplied by the mass in kilograms. Approximately equal to 2.2046.&lt;br /&gt;
}}&lt;br /&gt;
=== KT2FPS ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var feet_per_second = knots * KT2FPS;&lt;br /&gt;
|text = Converts a velocity from knots to feet per second when multiplied by the velocity in knots. Approximately equal to 1.6878.&lt;br /&gt;
}}&lt;br /&gt;
=== KT2MPS ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var metres_per_second = knots * KT2MPS;&lt;br /&gt;
|text = Converts a velocity from knots to metres per second when multiplied by the velocity in knots. Approximately equal to 0.5144.&lt;br /&gt;
}}&lt;br /&gt;
=== L2GAL ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var gallons = litres * L2GAL;&lt;br /&gt;
|text = Converts a volume from litres to US liquid gallons when multiplied by the volume in litres. Approximately equal to 0.2642.&lt;br /&gt;
}}&lt;br /&gt;
=== LB2KG ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var kilograms = pounds * LB2KG;&lt;br /&gt;
|text = Converts a mass from pounds to kilograms when multiplied by the mass in pounds. Approximately equal to 0.4536.&lt;br /&gt;
}}&lt;br /&gt;
=== M2FT ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var feet = metres * M2FT;&lt;br /&gt;
|text = Converts a length from metres to feet when multiplied by the length in metres. Approximately equal to 3.2808.&lt;br /&gt;
}}&lt;br /&gt;
=== M2IN ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var inches = metres * M2IN;&lt;br /&gt;
|text = Converts a length from metres to inches when multiplied by the length in metres. Approximately equal to 39.3701.&lt;br /&gt;
}}&lt;br /&gt;
=== M2NM ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var nautical_miles = metres * M2NM;&lt;br /&gt;
|text = Converts a length from metres to nautical miles when multiplied by the length in metres. Approximately equal to 0.00054.&lt;br /&gt;
}}&lt;br /&gt;
=== MPS2KT ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var knots = metres_per_second * MPS2KT;&lt;br /&gt;
|text = Converts a velocity from metres per second to knots when multiplied by the velocity in metres per second. Approximately equal to 1.9438.&lt;br /&gt;
}}&lt;br /&gt;
=== NM2M ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var metres = nautical_miles * NM2M;&lt;br /&gt;
|text = Converts a length from nautical miles to metres when multiplied by the length in nautical miles. Equal to 1,852.&lt;br /&gt;
}}&lt;br /&gt;
=== R2D ===&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|syntax = var degrees = radians * R2D;&lt;br /&gt;
|text = Converts an angle from radians to degrees when multiplied by the angle in radians. Equal to &amp;lt;code&amp;gt;180 / π&amp;lt;/code&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Appendix}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Nasal namespaces}}&lt;/div&gt;</summary>
		<author><name>Clm76</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=FG1000&amp;diff=116247</id>
		<title>FG1000</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=FG1000&amp;diff=116247"/>
		<updated>2018-09-29T16:17:35Z</updated>

		<summary type="html">&lt;p&gt;Clm76: /* Load and start the FG1000 system */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
[[File:Mirrored MFD.png|thumb|FG1000 MFD being displayed (incorrectly!) in a DA-40, and mirrored to a GUI window at 50% scale (02/2018)]]&lt;br /&gt;
[[File:FG1000 SVG UI.jpg|thumb|Stuart pushed an SVG UI for the FG1000, replacing the PUI version. (01/2018)]]&lt;br /&gt;
&lt;br /&gt;
{{Infobox subsystem&lt;br /&gt;
|name         = FG1000&lt;br /&gt;
|started      = 07/2017 &lt;br /&gt;
|description  = G1000-based MFD emulation&lt;br /&gt;
|status       = Under active development as of 07/2017&lt;br /&gt;
|developers   = {{Usr|Stuart}}, Richard (via [[Emesary]]), Michat (UI graphics), www2 (3D artwork) &amp;lt;ref&amp;gt;https://forum.flightgear.org/viewtopic.php?f=71&amp;amp;t=32764&amp;amp;start=60#p327229&amp;lt;/ref&amp;gt;&lt;br /&gt;
|changelog = https://sourceforge.net/p/flightgear/fgdata/ci/next/log/?path=/Aircraft/Instruments-3d/FG1000&lt;br /&gt;
|folders = https://sourceforge.net/p/flightgear/fgdata/ci/next/tree/Aircraft/Instruments-3d/FG1000/&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Current Status ==&lt;br /&gt;
&lt;br /&gt;
06/04/2018:&lt;br /&gt;
* PFD is largely complete. Transponder, NRST, ALT units and alternative units still to implement.&lt;br /&gt;
* MFD is functional, and now includes display and editing of the current flightplan, along with moving map, engine information.&lt;br /&gt;
* MFD and PFD can be displayed as a GUI window under Debug menu or added to an aircraft with the GDU-1045 model as part of a 3D cockpit.  &lt;br /&gt;
&lt;br /&gt;
There's still a huge number of pages to write, and the more complex pages around route planning will likely be particularly challenging, but it should be far faster now these blocks are in place. .  If anyone is interested in creating a specific page, please also get in touch with Stuart.  The various ''Nearest... and ...Information'' pages should be quite easy to do based on the pages that have been written already.  See the Design Notes for details.&lt;br /&gt;
&lt;br /&gt;
Next steps:&lt;br /&gt;
* Writing the remaining (large) number of pages&lt;br /&gt;
* Styling the pages - making the iconography match Garmin exactly.&lt;br /&gt;
&lt;br /&gt;
=== Gallery ===&lt;br /&gt;
&amp;lt;gallery mode=packed widths=230px heights=230px&amp;gt;&lt;br /&gt;
PFD 20180406.png|FG1000 PFD showing inset map, current wind data, HSI with GPS bearing, active flightplan list&lt;br /&gt;
MFD 20180406.png|FG1000 MFD with Cessna 182T Engine Information System (EIS), and Active Flightplan&lt;br /&gt;
FG1000_Checklist_MFD_page.png|FG1000 MFD with Checklist page displayed&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== A Cheat's Guide to Using the FG1000 ==&lt;br /&gt;
&lt;br /&gt;
For those without the time or interest to read the G1000 User Guide, here's a very quick run-down on navigating through the MFD:&lt;br /&gt;
&lt;br /&gt;
General:&lt;br /&gt;
* Click on the buttons, though some are not implemented yet.&lt;br /&gt;
* Use the mouse wheel to scroll the knobs (NAV, HDG, ALT, COM, CRS/BARO, RANGE, FMS).  For knobs with both and inner and an outer (NAV, COM, CRS/BARO, FMS), scrolling when pressing the Shift key changes the outer knob.&lt;br /&gt;
* Use the outer FMS knob (shift-mouse wheel on the FMS knob on the bottom right) to navigate through the page groups.&lt;br /&gt;
* Use the inner FMS knob (mouse wheel on the FMS knob on the bottom right) to navigate through the pages within the group.  The currently selected page is shown in blue. If you stop moving the cursor, the page will be loaded in 0.5s.&lt;br /&gt;
* Within a page, click on the FMS knob to activate the cursor (CRSR).  You you should see some element highlighted on the page, typically flashing.  You can navigate around the page using the outer and inner FMS knobs.  The Outer FMS knob (shift-mouse wheel) typical moves the cursor between elements.  The Inner FMS knob typically changes the selected item.&lt;br /&gt;
* Use the RANGE knob to zoom map views in/out.&lt;br /&gt;
&lt;br /&gt;
Implemented function:&lt;br /&gt;
&lt;br /&gt;
PFD:&lt;br /&gt;
* Artificial horizon, compass rose, altimeter and airspeed indicator&lt;br /&gt;
* HSI that can display NAV1, NAV2, GPS deviation (press CDI)&lt;br /&gt;
* Wind information (press PFD-&amp;gt;WIND then select one of the options for display)&lt;br /&gt;
* Bearings to NAV1, NAV2, GPS (press PFD then BRG1/BRG2 to cycle through the options)&lt;br /&gt;
* Inset Map (Press INSET softkey)&lt;br /&gt;
* Active Flight Plan (press FPL when there is an active flightplan to display)&lt;br /&gt;
* DirectTo GPS (press DTO)&lt;br /&gt;
&lt;br /&gt;
MFD:&lt;br /&gt;
* There is a two channel NAV/COM with an active and standby frequency and automatic ID of NAV frequencies at the top of the UI.  They all integrate with the standard properties, so should work automatically with most aircraft.&lt;br /&gt;
* If you have a frequency highlighted in any of the pages (e.g. WPT - AIRPORT INFORMATION) press the ENT key to load it into the standby NAV/COM frequency as appropriate.&lt;br /&gt;
* To set up a GPS Direct To route:&lt;br /&gt;
** Press the DTO button (D with an arrow through it on the right side of the panel, below the word &amp;quot;PAN&amp;quot;).  This will bring up a Direct To window, allowing you to enter the ID of an airport, Fix etc. to navigate to.&lt;br /&gt;
** You can enter the ID in one of two ways:&lt;br /&gt;
*** Scroll the FMS knob backwards to bring up a sub-menu of waypoints categories (FPL, NRST, RECENT, USER, AIRWAY).  Use the inner FMS knob to scroll between the different categories, and the outer FMS knob to select a specific item.  &lt;br /&gt;
*** Scroll the FMS knob forwards to enter an ID manually using the FMS knob.  Use the outer FMS knob to move to the next poisition in the ID.  &lt;br /&gt;
** Once you've got an ID, press ENT which will load details into the window and highlight the ACTIVATE button.&lt;br /&gt;
** Press ENT once more to load the Direct-to into the GPS and activate it.&lt;br /&gt;
* To view airport information, select the WPT - AIRPORT INFORMATION page.  Use the outer FMS knob to move between the AIRPORT, RUNWAYS and FREQUENCY boxes, and the inner FMS to edit the airport ID, scroll through available runways, or highlight a frequency.&lt;br /&gt;
* To edit the current flightplan, press the FPL page, then press the CRSR and enter waypoints for DTO above.&lt;br /&gt;
&lt;br /&gt;
== Aircraft Installation ==&lt;br /&gt;
&lt;br /&gt;
Adding the FG1000 to an aircraft is intended to be fairly straightforward.&lt;br /&gt;
&lt;br /&gt;
=== Add the GDU to the cockpit ===&lt;br /&gt;
&lt;br /&gt;
Place one or more of the display (GDU) units in your model.  The XML files&lt;br /&gt;
are in GDU104X directory and are named  &amp;lt;model&amp;gt;.&amp;lt;device-number&amp;gt;.xml, e.g.&lt;br /&gt;
&lt;br /&gt;
GDU-1045.1.xml  GDU-1045.2.xml  GDU-1045.3.xml  GDU-1045.4.xml&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;device-number&amp;gt; is referenced later to identify which displays to used&lt;br /&gt;
for each PFD, MFD etc.  You must only used one of each &amp;lt;device-number&amp;gt; value,&lt;br /&gt;
though you can mix and match the GDU models.&lt;br /&gt;
&lt;br /&gt;
The different GDU models are as follows:&lt;br /&gt;
&lt;br /&gt;
* GDU-1040 - No autopilot controls&lt;br /&gt;
* GDU-1044B - autopilot controls without VNAV&lt;br /&gt;
* GDU-1045 - autopilot controls with VNAV&lt;br /&gt;
&lt;br /&gt;
=== Load the Interfaces to provide data ===&lt;br /&gt;
&lt;br /&gt;
This is used to provide airdata, navdata, NAV/COM settings, engine information &lt;br /&gt;
to the GDUs.  There is a&lt;br /&gt;
generic interface (GenericInterfaceController) that uses the standard FG route&lt;br /&gt;
manager, GPS, navigation data and NAV/COM settings, and engine data for a single&lt;br /&gt;
piston engine.&lt;br /&gt;
&lt;br /&gt;
You need to load it as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
var nasal_dir = getprop(&amp;quot;/sim/fg-root&amp;quot;) ~ &amp;quot;/Aircraft/Instruments-3d/FG1000/Nasal/&amp;quot;;&lt;br /&gt;
io.load_nasal(nasal_dir ~ 'Interfaces/GenericInterfaceController.nas', &amp;quot;fg1000&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
var interfaceController = fg1000.GenericInterfaceController.getOrCreateInstance();&lt;br /&gt;
interfaceController.start();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You may want to create your own version depending on what properties you are&lt;br /&gt;
using for the NAV/COM, and your engine configuration.&lt;br /&gt;
&lt;br /&gt;
Note that you should only start the interface after you've started the FG1000&lt;br /&gt;
as they will publish information at start of day that the displays need.&lt;br /&gt;
&lt;br /&gt;
=== Create an EIS Display ===&lt;br /&gt;
&lt;br /&gt;
This is the Engine Information System and displays on the left side of the MFD.&lt;br /&gt;
&lt;br /&gt;
There is a generic EIS provided&lt;br /&gt;
(see FG1000/Nasal/EIS, and FG1000/MFDPages/EIS.svg) that is intended to match&lt;br /&gt;
a Cessna 172.  If you need something different, you should create your own&lt;br /&gt;
EIS class and SVG file.&lt;br /&gt;
&lt;br /&gt;
Note that you need to pass data to/from the engine using Emesary. See&lt;br /&gt;
Interfaces/GenericEISPublisher.nas for a reference implementation.&lt;br /&gt;
&lt;br /&gt;
=== Load and start the FG1000 system ===&lt;br /&gt;
&lt;br /&gt;
See FG1000/Nasal/FG1000.nas for full details,&lt;br /&gt;
including how to pass in the EIS. A simple FG1000 using the generic EIS, and&lt;br /&gt;
displaying PFD on device-number 1 and an MFD on device-number 2 is as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
var nasal_dir = getprop(&amp;quot;/sim/fg-root&amp;quot;) ~ &amp;quot;/Aircraft/Instruments-3d/FG1000/Nasal/&amp;quot;;&lt;br /&gt;
io.load_nasal(nasal_dir ~ 'FG1000.nas', &amp;quot;fg1000&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
# Create the FG1000&lt;br /&gt;
var fg1000system = fg1000.FG1000.getOrCreateInstance();&lt;br /&gt;
&lt;br /&gt;
# Create a PFD as device 1, MFD as device 2&lt;br /&gt;
fg1000system.addPFD(1);&lt;br /&gt;
fg1000system.addMFD(2);&lt;br /&gt;
&lt;br /&gt;
# Display the devices&lt;br /&gt;
fg1000system.display(1);&lt;br /&gt;
fg1000system.display(2);&lt;br /&gt;
&lt;br /&gt;
fg1000system.show();&lt;br /&gt;
&lt;br /&gt;
#  Display a GUI version of device 1 at 50% scale.&lt;br /&gt;
#fg1000system.displayGUI(1, 0.5);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Page Status ==&lt;br /&gt;
&lt;br /&gt;
This is the current status of the individual pages.  Full progress bars indicate that they are functionally complete, though the iconography is not correct.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable sortable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Page !! Reference !! Status !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| {{FG1000 file|name=NavigationMap|label=Navigation Map}} ||  || {{progressbar|100|20}} || See Layers below&lt;br /&gt;
|-&lt;br /&gt;
| {{FG1000 file|name=TrafficMap|label=Traffic Map}} ||  || {{progressbar|100|20}} || Zoom controls unclear&lt;br /&gt;
|-&lt;br /&gt;
| {{FG1000 file|name=Stormscope|label=Storm Scope}} ||  || {{progressbar|0|20}} || &lt;br /&gt;
|-&lt;br /&gt;
| {{FG1000 file|name=WeatherDataLink|label=Weather Data Link}} ||  || {{progressbar|0|20}} || &lt;br /&gt;
|-&lt;br /&gt;
| {{FG1000 file|name=TAWSB|label=TAWS-B}}||  || {{progressbar|0|20}} || &lt;br /&gt;
|-&lt;br /&gt;
| {{FG1000 file|name=AirportInformation|label=Airport Information}} ||  || {{progressbar|100|20}} || Needs additional page of AOPA information, which we don't have&lt;br /&gt;
|-&lt;br /&gt;
| {{FG1000 file|name=IntersectionInfo|label=Intersection Information}} ||  || {{progressbar|100|20}} || &lt;br /&gt;
|-&lt;br /&gt;
| {{FG1000 file|name=NDBInfo|label=NDB Information}} ||  || {{progressbar|100|20}} || &lt;br /&gt;
|-&lt;br /&gt;
| {{FG1000 file|name=VORInfo|label=VOR Information}} ||  || {{progressbar|100|20}} || &lt;br /&gt;
|-&lt;br /&gt;
| User WPT Information ||  || {{progressbar|0|20}} || &lt;br /&gt;
|-&lt;br /&gt;
| Trip Planning ||  || {{progressbar|0|20}} || &lt;br /&gt;
|-&lt;br /&gt;
| Utility ||  || {{progressbar|0|20}} || &lt;br /&gt;
|-&lt;br /&gt;
| GPS Status ||  || {{progressbar|0|20}} || &lt;br /&gt;
|-&lt;br /&gt;
| XM Radio ||  || {{progressbar|0|20}} || &lt;br /&gt;
|-&lt;br /&gt;
| System Status ||  || {{progressbar|0|20}} || &lt;br /&gt;
|-&lt;br /&gt;
| {{FG1000 file|name=ActiveFlightPlanNarrow|label=Active Flight Plan}} ||  || {{progressbar|100|20}} || Viewing and editing of the active flight plan implemented.  VNAV and the &amp;quot;wide view&amp;quot; not yet implemented&lt;br /&gt;
|-&lt;br /&gt;
| Flight Plan Catalog ||  || {{progressbar|0|20}} || &lt;br /&gt;
|-&lt;br /&gt;
| Stored Flight Plan ||  || {{progressbar|0|20}} || &lt;br /&gt;
|-&lt;br /&gt;
| {{FG1000 file|name=Checklist|label=Checklist}} ||  || {{progressbar|100|20}} || &lt;br /&gt;
|-&lt;br /&gt;
| {{FG1000 file|name=NearestAirports|label=Nearest Airports}} ||  || {{progressbar|100|20}} || &lt;br /&gt;
|-&lt;br /&gt;
| {{FG1000 file|name=NearestIntersections|label=Nearest Intersections}} ||  || {{progressbar|100|20}} || &lt;br /&gt;
|-&lt;br /&gt;
| {{FG1000 file|name=NearestNDB|label=Nearest NDB}} ||  || {{progressbar|100|20}} || &lt;br /&gt;
|-&lt;br /&gt;
| {{FG1000 file|name=NearestNDB|label=Nearest VOR}} ||  || {{progressbar|100|20}} || &lt;br /&gt;
|-&lt;br /&gt;
| Nearest User Waypoints ||  || {{progressbar|0|20}} || &lt;br /&gt;
|-&lt;br /&gt;
| Nearest Frequencies ||  || {{progressbar|0|20}} || &lt;br /&gt;
|-&lt;br /&gt;
| Nearest Airspaces ||  || {{progressbar|0|20}}|| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This is the current status of the major functional elements&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Area !! Status !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| Navigation - FMS || {{progressbar|100|20}} || &lt;br /&gt;
|-&lt;br /&gt;
| Softkeys || {{progressbar|100|20}} || &lt;br /&gt;
|-&lt;br /&gt;
| MENU key || {{progressbar|0|20}} || &lt;br /&gt;
|-&lt;br /&gt;
| NAV/COM  || {{progressbar|90|20}} || Volume controls to be added&lt;br /&gt;
|-&lt;br /&gt;
| GPE Header || {{progressbar|0|20}} ||&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Motivation ==&lt;br /&gt;
{{Main article|Canvas_News#Moving_map.2FRNAV_discussion}}&lt;br /&gt;
&lt;br /&gt;
The enormous variety in current glass flight decks means we really need to think of a new way of defining glass cockpit layouts.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://sourceforge.net/p/flightgear/mailman/message/15602744/ &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: [Flightgear-devel] Glass cockpit  and external gauges. &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Robin van Steenbergen &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Oct 2nd, 2007 &lt;br /&gt;
  |added  =  Oct 2nd, 2007 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The airspace system is in the process of changing drastically [...]  this isn't just a matter of throwing up a canvas showing some GPS waypoints and a magenta line. Modern navigators are astoundingly-complex devices — probably an order of magnitude more lines of code than FlightGear itself — and even their basic flight planning algorithms and databases (e.g. fly-by waypoints vs fly-over waypoints, open vs closed approach procedures, transitions into RNAV approaches, etc.) are far beyond the scope of anything we've tried, and we'd also need an up-to-date database far more complex than the ones we have now. Once you get to the extra features, like FIS-B weather or TIS-B traffic info over ADS-B, or TAWS (terrain alerting), we're probably in way over our heads trying to emulate even the simplest general-aviation IFR GPS.&lt;br /&gt;
&lt;br /&gt;
This may help folks understand what the G1000 is all about: http://static.garmincdn.com/pumac/190-00498-07_0A_Web.pdf &lt;br /&gt;
Writing a G1000 isn't that hard. Writing a '''feature complete''' G1000 is a ton of work. &amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://sourceforge.net/p/flightgear/mailman/message/35925783/ &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: [Flightgear-devel] RFD: FlightGear and the changing state of&lt;br /&gt;
 air navigation &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; geneb &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 3rd, 2017 &lt;br /&gt;
  |added  =  Jul 3rd, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Depending on how we deal with this challenge, the question is whether that means that the usefulness of FlightGear will also gradually taper off. &amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://sourceforge.net/p/flightgear/mailman/message/35924395/ &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; [Flightgear-devel] RFD: FlightGear and the changing state of air&lt;br /&gt;
 navigation &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; David Megginson &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 3rd, 2017 &lt;br /&gt;
  |added  =  Jul 3rd, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Instead of just making one-off tweaks like the consumer sims did, we (as a team) emulated entire systems like the vacuum, pitot-static, and electrical systems, so that failures would be realistic. In the RNAV age, we need to do the same thing; it's just that it's a bigger job. FlightGear will still be great for people who want to practice the mechanical parts of flying (e.g. crosswind wheel landings in a Cub), but will slip further and further behind for people who want to use it for real IFR practice.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://sourceforge.net/p/flightgear/mailman/message/35927088/ &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: [Flightgear-devel] RFD: FlightGear and the changing state of&lt;br /&gt;
 air navigation &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; David Megginson &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 4th, 2017 &lt;br /&gt;
  |added  =  Jul 4th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Background ==&lt;br /&gt;
{{Main article|Complex Canvas Avionics}}&lt;br /&gt;
&lt;br /&gt;
== Challenges ==&lt;br /&gt;
=== Performance ===&lt;br /&gt;
{{See also|Canvas_Development#Property_I.2FO_observations|Canvas_MapStructure#Performance}}&lt;br /&gt;
Nasal as such is fast, and compared with the cost of rendering the terrain, rendering a canvas display is fast on the GPU (you can test by rendering but not updating a canvas display), and unless you're doing something very inefficient, calculating a display but not writing it to the property tree is fast (you can test this by disabling the write commands). It's the property I/O which you need to structure well, and then canvas will run fast. And of course the complexity of your underlying simulation costs - if you run a thermal model underneath to get real temperature reading, it costs more than inventing the numbers. But that has nothing to do with canvas. &amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://sourceforge.net/p/flightgear/mailman/message/35926343/ &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: [Flightgear-devel] RFD: FlightGear and the changing state of&lt;br /&gt;
 air navigation &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Thorsten Renk &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 4th, 2017 &lt;br /&gt;
  |added  =  Jul 4th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
it's a lot of work to code all these displays which someone has to do, but there's no reason to assume it'd be hopeless performance-wise.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://sourceforge.net/p/flightgear/mailman/message/35926412/ &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: [Flightgear-devel] RFD: FlightGear and the changing state of&lt;br /&gt;
 air navigation &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Thorsten Renk &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 4th, 2017 &lt;br /&gt;
  |added  =  Jul 4th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
we need to be realistic here: The G1000 is a fairly significant piece of computer hardware that we're going to emulate. It's not going to be &amp;quot;free&amp;quot; particularly for those on older hardware that's already struggling. However, hopefully we can offload a chunk of the logic (route management, autopilot/flight director) to the core, and do things like offline generation of terrain maps to minimize the impact.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://sourceforge.net/p/flightgear/mailman/message/35926745/ &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: [Flightgear-devel] RFD: FlightGear and the changing state of&lt;br /&gt;
 air navigation &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Stuart Buchanan &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 4th, 2017 &lt;br /&gt;
  |added  =  Jul 4th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Route Manager ===&lt;br /&gt;
that's exactly the kind of device James hopes the new code can support. He's read the G1000 pilot's manual, and  *thinks* that nearly all the functions can be provided by the current GPS code It would be great if people could look over the current GPS features and indicate any pieces you think might be missing - additional commands, additional search features, extra data, or anything really. &amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://sourceforge.net/p/flightgear/mailman/message/23814733/ &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: [Flightgear-devel] ZKV500 GPS instrument; (New?) GPS code bug &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; James Turner &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Oct 22nd, 2009 &lt;br /&gt;
  |added  =  Oct 22nd, 2009 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== tile servers ===&lt;br /&gt;
Please keep in mind that most tile servers discourage bulk downloads. For the whole planet,oomlevel 8 is approx. 67k files, at zoomlevel 9 you have 265k, level 10 has a little over one million tiles. And one would probably want to go up to level 15 (roughly 1e9 files)&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://sourceforge.net/p/flightgear/mailman/message/36057678/ &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: [Flightgear-devel] Canvas MapLayer for OpenStreetMap, OpenAIP,&lt;br /&gt;
 VFR Sectionals &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Torsten Dreyer &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Sep 29th, 2017 &lt;br /&gt;
  |added  =  Sep 29th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Offline mode ===&lt;br /&gt;
would it be possible to provide an offline tool to pre-fetch a region, like TerraMaster, or terrasync.py? Call me old-fashioned or paranoid, but I don't want FG to go online and do stuff automatically, I prefer downloading what I need manually so that I know what gets onto my harddisk. Also - think of all the people with poor home internet which might have larger bandwidth only in a library elsewhere.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://sourceforge.net/p/flightgear/mailman/message/36057499/ &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: [Flightgear-devel] Canvas MapLayer for OpenStreetMap, OpenAIP,&lt;br /&gt;
 VFR Sectionals &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Thorsten Renk &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Sep 29th, 2017 &lt;br /&gt;
  |added  =  Sep 29th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
that would certainly be possible - the code retrieving the data is pretty trivial and porting the Nasal code to python or some other script should be straightforward.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://sourceforge.net/p/flightgear/mailman/message/36057657/ &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: [Flightgear-devel] Canvas MapLayer for OpenStreetMap, OpenAIP,&lt;br /&gt;
 VFR Sectionals &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Stuart Buchanan &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Sep 29th, 2017 &lt;br /&gt;
  |added  =  Sep 29th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Navdata ===&lt;br /&gt;
we need some better data sources, especially with direct routing replacing airways in many areas, and having airspace data would help the map displays, but the current API is specifically designed to support G430, G1000 and similar class devices. Adding RNAV approaches is eminently doable, see the the RNavController base class which the GPS/FMS layer uses to allow new waypoint / route segments to define how they are flown and drive CDIs &amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://sourceforge.net/p/flightgear/mailman/message/35927598/ &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: [Flightgear-devel] RFD: FlightGear and the changing state of&lt;br /&gt;
 air navigation &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; James Turner &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 4th, 2017 &lt;br /&gt;
  |added  =  Jul 4th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Getting the algorithms right, and keeping the data up to date every 7 weeks (we have only a tiny sliver of that data currently), is going to be much more of a challenge. I expect that it's probably an order of magnitude more complicated than our current flight dynamics (etc.), so we'd have to grow our contributor base, and find funding to pay at least Curt (and maybe a couple of others) full time to manage the project. Note that that's what has happened for other complex FOSS projects, like office suites and browsers, which generally end up working through a foundation, rather than our current relaxed circle-of-friends arrangement. The reason it's so much harder now is that the software *is* the product for GPS navigators, FMSs, etc. (unlike with older avionics, where the hardware was the main thing). That means that emulating even a simple unit like the GTN 650 or GNS 430W is almost difficult as building one, so we're trying to keep up with armies of full-time software developers at Garmin, Avidyne, etc.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://sourceforge.net/p/flightgear/mailman/message/35927243/ &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: [Flightgear-devel] RFD: FlightGear and the changing state of&lt;br /&gt;
 air navigation &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; David Megginson &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 4th, 2017 &lt;br /&gt;
  |added  =  Jul 4th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
According to the Route Manager wiki page, we already have support for SID/STAR data provided from Navigraph, which is released on the AIRAC cycle, and costs a pretty small amount (9 EUR for a single download of FMS data, or 90 EUR per year) Given the relatively low cost, I don't think that we as an organiation want to get into trying to digitize data ourselves just to make it open-source or public domain. Particularly given the low cost of a download. We might want to digitize the STAR/SIDs for the default airport with each release so there are some approaches available for those who don't want to purchase the data.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://sourceforge.net/p/flightgear/mailman/message/35927426/ &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: [Flightgear-devel] RFD: FlightGear and the changing state of&lt;br /&gt;
 air navigation &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Stuart Buchanan &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 4th, 2017 &lt;br /&gt;
  |added  =  Jul 4th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
everyone agrees we would like to add airspace data - what we’re missing is a suitable data source with the correct license. Torsten found a raster-based source but for wider use in the sim we really need vector data, so we can render it ourselves directly. &amp;lt;ref&amp;gt;{{cite web  |url    =  https://sourceforge.net/p/flightgear/mailman/message/36178857/   |title  =  &amp;lt;nowiki&amp;gt; Re: [Flightgear-devel] [flightgear/simgear] Nasal regex patch &amp;lt;/nowiki&amp;gt;   |author =  &amp;lt;nowiki&amp;gt; James Turner &amp;lt;/nowiki&amp;gt;   |date   =  Jan 5th, 2018   |added  =  Jan 5th, 2018   |script_version = 0.36   }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For airspace data we could use [https://openflightmaps.org/ https://openflightmaps.org/] . It provides community created navigation data for many countries in (currently only) Europe with a free license.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=326105#p326105 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Canvas G1000 &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; TheTom &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jan 9th, 2018 &lt;br /&gt;
  |added  =  Jan 9th, 2018 &lt;br /&gt;
  |script_version = 0.36 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
we definitely want vector data.  Stuart is hoping that we might convince the source of the raster data we're using at present to provide it as raw vector data, but we haven't had any luck so far.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=326201&amp;amp;sid=137f64d32b770e50a3c4061be42ce28a#p326201 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Canvas G1000 &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; stuart &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jan 10th, 2018 &lt;br /&gt;
  |added  =  Jan 10th, 2018 &lt;br /&gt;
  |script_version = 0.36 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Airport Data (US - only )==&lt;br /&gt;
At least for the US, all required information is available in the form of regularly updated UDFF files:&lt;br /&gt;
* https://www.ngs.noaa.gov/AERO/aero.html&lt;br /&gt;
* https://www.ngs.noaa.gov/AERO/uddf/WESTERN-PACIFIC/CALIFORNIA/SFO__03B.F77&lt;br /&gt;
* https://www.ngs.noaa.gov/AERO/annof_7.pdf&lt;br /&gt;
&lt;br /&gt;
It would seem relatively straightforward to provide a scripted parser to create the corresponding charts procedurally.&lt;br /&gt;
As a matter of fact, this could even be done in a background thread or using a separate Python script, i.e. if we wanted to ship pre-created imagery as part of fgdata.&lt;br /&gt;
&lt;br /&gt;
However, if the built-in Canvas system is extended to provide these charts, [[Phi]] could also be entirely self-contained by rendering the same  airport charts without having to depend on external data sources.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Layers  ==&lt;br /&gt;
&lt;br /&gt;
{{See also|Canvas_Sandbox#CanvasShapefile}}&lt;br /&gt;
&lt;br /&gt;
Following is the list of layers displayed by the G1000 system (see page 153 of the 190-00498-07 Rev. A), and mapping to the equivalent MapStructure Layer.&lt;br /&gt;
Many of these would also be good to have for other avionics/GUI dialogs, including the NavDisplay framework, which is currently re-implementing this functionality separately, i.e. not yet using MapStructure.&lt;br /&gt;
&lt;br /&gt;
Following is the list of layers displayed by the G1000 system, based on the Garmin G1000 Integrated Flight Deck Pilot's Guide for the Cessna Nav III [http://static.garmincdn.com/pumac/190-00498-07_0A_Web.pdf Garmin Site] [https://web.archive.org/web/20170708032442/http://static.garmincdn.com/pumac/190-00498-07_0A_Web.pdf Wayback machine], page 153, and the mapping to the equivalent MapStructure Layer.&lt;br /&gt;
&lt;br /&gt;
Richard mentioned that if he were to implement approach plates in the EFB he'd probably use raster images provided via http and provide an http service within FG to do this, or to allow the EFB to use any other external web service. Other content for the EFB could be also provided as SVG via http.&amp;lt;ref&amp;gt;{{cite web  |url    =  https://forum.flightgear.org/viewtopic.php?p=259194#p259194   |title  =  &amp;lt;nowiki&amp;gt; Re:  &amp;lt;/nowiki&amp;gt;   |author =  &amp;lt;nowiki&amp;gt; Richard &amp;lt;/nowiki&amp;gt;   |date   =  Sep 29th, 2015   |added  =  Sep 29th, 2015   |script_version = 0.36   }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Layer !! [[Canvas MapStructure Layers|MapStructure Layer]] !! Status !! Page || Notes&lt;br /&gt;
|-&lt;br /&gt;
| Flight Plan Route Lines || {{MapStructure_File|name=RTE|type=lcontroller}} || Requires styling || {{PDF Link|page=190}} ||&lt;br /&gt;
|-&lt;br /&gt;
| Flight Plan Route Waypoints || {{MapStructure_File|name=WPT|type=lcontroller}}  || Requires styling || {{PDF Link|page=190}} || &lt;br /&gt;
|-&lt;br /&gt;
| Rivers/Lakes || {{MapStructure_File|name=VFRChart|type=lcontroller}}   ||   || {{PDF Link|page=148}} || Currently using downloaded raster from web.  Perhaps generate similarly to Atlas&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=203495#p203495 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Atlas still in use ? &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Torsten &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Mar 17th, 2014 &lt;br /&gt;
  |added  =  Mar 17th, 2014 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;, or could be vector data from scenery. {{AtlasVsCanvasCameras}}&lt;br /&gt;
|-&lt;br /&gt;
| Topography Data || {{MapStructure_File|name=VFRChart|type=lcontroller}} || Synthetic  || {{PDF Link|page=145}} || Height-map at chart-resolution.  Perhaps generate similarly to Atlas? {{AtlasVsCanvasCameras}}&lt;br /&gt;
|-&lt;br /&gt;
| International Borders ||   ||   ||  {{PDF Link|page=148}} || Vector data from scenery?&lt;br /&gt;
|-&lt;br /&gt;
| Track Vector ||   ||   || {{PDF Link|page=156}} || Forward looking display of track. Look-ahead time selectable. &lt;br /&gt;
|-&lt;br /&gt;
| Navigation Range Ring ||   ||   || {{PDF Link|page=159}} || Straightforward extension of {{MapStructure_File|name=APS|type=lcontroller}}.&lt;br /&gt;
|-&lt;br /&gt;
| Fuel Range Ring ||   ||   || 159 || Straightforward extension of {{MapStructure_File|name=APS|type=lcontroller}}.&lt;br /&gt;
|-&lt;br /&gt;
| Terrain Data ||   ||   || 364 || Should be straightforward, with exception of obstacles.  [[Spoken GCA|Profile view]] also required.&lt;br /&gt;
|-&lt;br /&gt;
| Traffic || {{MapStructure_File|name=TFC|type=lcontroller}} ||   || 394,423 || Various options, each with different iconography and data displayed. &lt;br /&gt;
|-&lt;br /&gt;
| Airways || {{MapStructure_File|name=VFRChart|type=lcontroller}} ||   ||  154 || Needs to be replaced with vector data&lt;br /&gt;
|-&lt;br /&gt;
| NEXRAD ||   ||   || 282 || Heaps of weather data with complex symbology.&lt;br /&gt;
|-&lt;br /&gt;
| XM Lightning Data || {{MapStructure_File|name=WXR|type=lcontroller}} || Change to symbol for lightning? || 294 || Optional  &lt;br /&gt;
|-&lt;br /&gt;
| Airports || {{MapStructure_File|name=APT|type=lcontroller}}, {{MapStructure_File|name=RWY|type=lcontroller}} || Require re-style based on size  ||  149, 163 ||&lt;br /&gt;
|-&lt;br /&gt;
| Runway Labels || {{MapStructure_File|name=RWY|type=lcontroller}} ||   || 148 || Needs to be added to {{MapStructure_File|name=RWY|type=symbol}}.  Should be straightforward&lt;br /&gt;
|-&lt;br /&gt;
| Restricted ||  {{MapStructure_File|name=OpenAIP|type=lcontroller}}  ||   ||  182 || {{NeedVectorData}}&lt;br /&gt;
|-&lt;br /&gt;
| MOA (Military) ||  {{MapStructure_File|name=OpenAIP|type=lcontroller}}  ||   ||  182 || {{NeedVectorData}}&lt;br /&gt;
|-&lt;br /&gt;
| User Waypoints ||   ||   ||  &lt;br /&gt;
|-&lt;br /&gt;
| Latitude/Longitude Grid || {{MapStructure_File|name=GRID|type=lcontroller}} ||   ||  &lt;br /&gt;
|-&lt;br /&gt;
| NAVAIDs || {{MapStructure_File|name=APT|type=lcontroller}}, {{MapStructure_File|name=VOR|type=lcontroller}}, {{MapStructure_File|name=FIX|type=lcontroller}}, {{MapStructure_File|name=NDB|type=lcontroller}} || APT, requires styling  ||  162 || &lt;br /&gt;
|-&lt;br /&gt;
| Class B Airspaces/TMA || {{MapStructure_File|name=OpenAIP|type=lcontroller}}  ||   || 182 || {{NeedVectorData}}&lt;br /&gt;
|-&lt;br /&gt;
| Class C Airspaces/TCA || {{MapStructure_File|name=OpenAIP|type=lcontroller}} ||   ||  182 || {{NeedVectorData}}&lt;br /&gt;
|-&lt;br /&gt;
| Class D Airspaces || {{MapStructure_File|name=OpenAIP|type=lcontroller}}  ||   ||  182 || {{NeedVectorData}}&lt;br /&gt;
|-&lt;br /&gt;
| Other Airspaces/ADIZ || {{MapStructure_File|name=OpenAIP|type=lcontroller}}  ||   ||  182 || {{NeedVectorData}}&lt;br /&gt;
|-&lt;br /&gt;
| TFRs ||   ||   || 310 || &lt;br /&gt;
|-&lt;br /&gt;
| Obstacles || {{MapStructure_File|name=OpenAIP|type=lcontroller}}  ||   ||  182 || {{NeedVectorData}}&lt;br /&gt;
|-&lt;br /&gt;
| Land/Country Text ||   ||   || 148 || Currently using raster data from web. Replace with {{MapStructure_File|name=POI|type=lcontroller}}? &lt;br /&gt;
|-&lt;br /&gt;
| Cities  || {{MapStructure_File|name=VFRChart|type=lcontroller}} ||   ||  148 || Currently using raster data from web.  Perhaps generate similarly to Atlas, or could be vector data from scenery. {{AtlasVsCanvasCameras}}&lt;br /&gt;
|-&lt;br /&gt;
| Roads || {{MapStructure_File|name=VFRChart|type=lcontroller}}  ||   ||  148 || Currently using raster data from web.  Requires vector data from scenery&lt;br /&gt;
|-&lt;br /&gt;
| Railroads || {{MapStructure_File|name=VFRChart|type=lcontroller}}  ||   ||  148 || Currently using raster data from web.  Requires vector data from scenery&lt;br /&gt;
|-&lt;br /&gt;
| State/Province Boundaries || {{MapStructure_File|name=VFRChart|type=lcontroller}}  ||   ||  148 || Currently using raster data from web.  Requires vector data from scenery&lt;br /&gt;
|-&lt;br /&gt;
| River/Lake Names || {{MapStructure_File|name=VFRChart|type=lcontroller}}  ||   ||  148 || Currently using raster data from web.  Replace with {{MapStructure_File|name=POI|type=lcontroller}}?&lt;br /&gt;
|-&lt;br /&gt;
| Selected Altitude Intercept Arc ||   ||   ||  161 || Simple extention to APS&lt;br /&gt;
|-&lt;br /&gt;
| SafeTaxi (Optional) || {{MapStructure_File|name=RWY|type=lcontroller}}, {{MapStructure_File|name=TAXI|type=lcontroller}} ||   || 493  || We don't currently have the data to display taxiway identifier or holding points&lt;br /&gt;
|-&lt;br /&gt;
| ChartView (Optional) || ||   || 503  || [[Canvas_Sandbox#CanvasPDF|Rendering of PDFs.]]  Might be possible to integrate with NaviGraph chart data?&lt;br /&gt;
|-&lt;br /&gt;
| FliteChart (Optional) || ||   || 521  || [[Canvas_Sandbox#CanvasPDF|Rendering of PDFs.]]  Might be possible to integrate with NaviGraph chart data?&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Existing work ==&lt;br /&gt;
{{See also|Complex Canvas Avionics}}&lt;br /&gt;
&lt;br /&gt;
=== Avionics / Cockpits ===&lt;br /&gt;
* [[User:Zakharov/zkv1000 installation guide|zkv1000]] see [https://forum.flightgear.org/viewtopic.php?f=14&amp;amp;t=32056]&lt;br /&gt;
* [[Extra500]] / Avidyne Entegra R9000&lt;br /&gt;
&lt;br /&gt;
===  Nasal / Canvas ===&lt;br /&gt;
* [[Canvas MFD Framework]]&lt;br /&gt;
* MapStructure&lt;br /&gt;
* MapStructure Layers&lt;br /&gt;
* [[Howto:Creating a Canvas GUI Widget]]&lt;br /&gt;
* [[Emesary]]&lt;br /&gt;
&lt;br /&gt;
=== C++ ===&lt;br /&gt;
* [[Atlas]] map creation&lt;br /&gt;
* [[Howto:Canvas View Camera Element|Synthetic Vision]] element for rendering viewmgr views to a Canvas &lt;br /&gt;
* [[Canvas_Sandbox#CanvasPDF|Canvas PDF]] element - for rendering PDF files (think EFB functionality)&lt;br /&gt;
&lt;br /&gt;
== Possible Work ==&lt;br /&gt;
&lt;br /&gt;
=== Architectural TODO List ===&lt;br /&gt;
This is a short list of architectural improvements I've noted during development that need to be revisited:&lt;br /&gt;
* Use NavMap for MFD Navigation Map, DirectTo, Nearest, Information pages.&lt;br /&gt;
* Separate out the HSI, tapes from the PFD and make them generic MFD UI Elements&lt;br /&gt;
* Improve the performance of the maps, perhaps using a cache for queries, definitely by more aggressive clipping of the SlippyMap layers based on map size.&lt;br /&gt;
&lt;br /&gt;
The layers table shown above mentions a few limitations and ideas, such as:&lt;br /&gt;
=== Nasal/Canvas ===&lt;br /&gt;
* &amp;lt;image&amp;gt; tag support for svg.nas (patch: [http://wiki.flightgear.org/Howto:Extend_the_Canvas_SVG_module])&lt;br /&gt;
* MapStructure: [[MapStructure_Optimizations#Allocating_selectable_ranges_into_groups|Allocating selectable ranges into Canvas groups]]&lt;br /&gt;
&lt;br /&gt;
=== C++ ===&lt;br /&gt;
* fix up [[Canvas_MapStructure#Performance|projection issues]], and adopt Gijs' [[Canvas_Development#Projections|projection handling code]] for the hard-coded PUI [[Map]], to get rid of the standard Sanson-Flamsteed projection &lt;br /&gt;
* adding a profile view, probably in conjunction with terrain sampling&lt;br /&gt;
* using atlas code to generate heightmaps/topography&lt;br /&gt;
* adding ESRI shapefile support to the Canvas system&lt;br /&gt;
* adding PDF rendering support the Canvas system&lt;br /&gt;
&lt;br /&gt;
== Design ==&lt;br /&gt;
&lt;br /&gt;
The broad structure of the Nasal files in Aircaft/Instruments-3d/FG1000/Nasal is as follows:&lt;br /&gt;
&lt;br /&gt;
* MFD.nas - top level MFD device, loads the other Nasal files (likely to be moved elsewhere later) and pages.&lt;br /&gt;
* PageGroupControllers.nas - controller for the Page Group display in the bottom right of the MFD, controlled by the FMS knob, and which allows selection between different page groups and individual pages.&lt;br /&gt;
* [PageName]/[PageName].nas - MFD page, inheriting from the MFDPage.nas.  Creates any required MapStructure and hierarchy of softkeys.&lt;br /&gt;
* [PageName]/[PageName]Controller.nas - Controller for the MFD page and the MapStructure layers, inheriting from MFDPageController.nas&lt;br /&gt;
* [PageName]/[PageName]Style.nas - Style controls for the MapStructure layers for the given Page.&lt;br /&gt;
* [PageName]/[PageName]Options.nas - Options for the MapStructure layers for the given Page.&lt;br /&gt;
&lt;br /&gt;
Key design notes:&lt;br /&gt;
* Most of the underlying Canvas [[MapStructure]] layers are now written (though they require styling, and/or would benefit from replacement with vector data). &lt;br /&gt;
* [[Emesary]] IPC framework is used to link between the MFD and underlying simulation state. This should make it easy to run the MFD on a separate FG instance, and provides a good demarkation between the individual aircraft systems and the FG1000 itself. &lt;br /&gt;
* Various underlying UI classes are used to handle highlighting, selection and scrolling of fields.  See [[Canvas MFD Framework]] for details of the elements now supported.&lt;br /&gt;
&lt;br /&gt;
== Resources ==&lt;br /&gt;
&lt;br /&gt;
{{#ev:youtube|I0y6p0ct1m0}}&lt;br /&gt;
&lt;br /&gt;
== Related ==&lt;br /&gt;
=== Commits ===&lt;br /&gt;
This section is intended to help keep track of related fgdata commits, so that if/when the project should stall, people can more easily look up related changes.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* {{fgdata commit|da252c8a18fe2ce88066c051f6df588639377792|text=Initial Direct To page}}(01/2018)&lt;br /&gt;
* {{fgdata commit|0c4544c1e1788f3396330c86016b66df8643fd3b|text=Add Config Store and MFD Header display.}}(01/2018)&lt;br /&gt;
* {{fgdata commit|9bd10f8273a6e64b12b9a034ca4952d87090f921|text=SVG MFD UI from miChat}}(01/2018)&lt;br /&gt;
* {{fgdata commit|f6f5efe9f442ff058d9d425d85337139b2a9bf1f|text=Use Emesary interface rather than airportinfo()}}(01/2018)&lt;br /&gt;
* {{fgdata commit|d306e7ba100840f5c3909123a383b19f7e7d7540|text=Set standby COM frequency from Airport pages}}(01/2018)&lt;br /&gt;
* {{fgdata commit|cbb281759cd3f2b830bffdd12ba72d456996a13a|text=NAV/COM Radio support}}(01/2018)&lt;br /&gt;
* {{fgdata commit|a25c66850a93f0ae7a6f7ad8769680c1fba99887|text=Add full set of hardkeys to fg1000 PUI dialog.}}(01/2018)&lt;br /&gt;
* {{fgdata commit|ab8774a3e0aa3909ad284a65194f5d80dc097c6e|text=extend MFDPageController to handle Emesary register}}(01/2018)&lt;br /&gt;
* {{fgdata commit|73424c1791242e053efcdac37a7dd3b66226387b|text=Update NAV/COMM frequencies from properties}}(01/2018)&lt;br /&gt;
* {{fgdata commit|ad77dc2f9cd2a1e1ebd5efd4226cbfbf3a522008|text=Modify FG1000 EIS to use Emesary}}(01/2018)&lt;br /&gt;
* {{fgdata commit|9eb91171b4689b29910db06757bc47767ff59d2c|text=FG1000 Nearest Airports page}}(01/2018)&lt;br /&gt;
* {{fgdata commit|475fd505859b7841e29de725d4c1c39db7ad4ee5|text=PFD UI Elements and NearestAirports page}} (12/2017)&lt;br /&gt;
* {{fgdata commit|d0203549e833b32b719697b3a2c2a63e16fcef16|text=Add AirportInfo and template pages for FG1000}} (12/2017)&lt;br /&gt;
* {{fgdata commit|3d31775ff36bf3aa08a929992b70f32f2a525509|text=Initial commit of FG1000 MFD}} (11/2017)&lt;br /&gt;
&lt;br /&gt;
MapStructure related:&lt;br /&gt;
&lt;br /&gt;
* {{fgdata commit|4bd57492f454a05f725e6fe5a80dc246cb5e5e0f|text=GRID layer}} (10/2017)&lt;br /&gt;
* {{fgdata commit|d84c527ca747dd6bbd9634507f0664ff3631bdef|text=RWY, TAXI, TWR, PARKING}} (10/2017)&lt;br /&gt;
* {{fgdata commit|e38ec03c609b882664d0496932709aa623e486cb|text=STAMEN}} (10/2017)&lt;br /&gt;
* {{fgdata commit|2fbc1dc491da867e0fd4f8fc810c5cddd80ced1f|text=VFRChart}} (09/2017)&lt;br /&gt;
* {{fgdata commit|85b7665c19ebaae02d746fef53f25f8d8974eb13|text=OSM and OpenAIP}} (09/2017)&lt;br /&gt;
&lt;br /&gt;
=== References ===&lt;br /&gt;
{{Appendix}}&lt;br /&gt;
&lt;br /&gt;
[[Category:Canvas]]&lt;/div&gt;</summary>
		<author><name>Clm76</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=FG1000&amp;diff=116246</id>
		<title>FG1000</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=FG1000&amp;diff=116246"/>
		<updated>2018-09-29T16:16:37Z</updated>

		<summary type="html">&lt;p&gt;Clm76: /* Add the GDU to the cockpit */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
[[File:Mirrored MFD.png|thumb|FG1000 MFD being displayed (incorrectly!) in a DA-40, and mirrored to a GUI window at 50% scale (02/2018)]]&lt;br /&gt;
[[File:FG1000 SVG UI.jpg|thumb|Stuart pushed an SVG UI for the FG1000, replacing the PUI version. (01/2018)]]&lt;br /&gt;
&lt;br /&gt;
{{Infobox subsystem&lt;br /&gt;
|name         = FG1000&lt;br /&gt;
|started      = 07/2017 &lt;br /&gt;
|description  = G1000-based MFD emulation&lt;br /&gt;
|status       = Under active development as of 07/2017&lt;br /&gt;
|developers   = {{Usr|Stuart}}, Richard (via [[Emesary]]), Michat (UI graphics), www2 (3D artwork) &amp;lt;ref&amp;gt;https://forum.flightgear.org/viewtopic.php?f=71&amp;amp;t=32764&amp;amp;start=60#p327229&amp;lt;/ref&amp;gt;&lt;br /&gt;
|changelog = https://sourceforge.net/p/flightgear/fgdata/ci/next/log/?path=/Aircraft/Instruments-3d/FG1000&lt;br /&gt;
|folders = https://sourceforge.net/p/flightgear/fgdata/ci/next/tree/Aircraft/Instruments-3d/FG1000/&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Current Status ==&lt;br /&gt;
&lt;br /&gt;
06/04/2018:&lt;br /&gt;
* PFD is largely complete. Transponder, NRST, ALT units and alternative units still to implement.&lt;br /&gt;
* MFD is functional, and now includes display and editing of the current flightplan, along with moving map, engine information.&lt;br /&gt;
* MFD and PFD can be displayed as a GUI window under Debug menu or added to an aircraft with the GDU-1045 model as part of a 3D cockpit.  &lt;br /&gt;
&lt;br /&gt;
There's still a huge number of pages to write, and the more complex pages around route planning will likely be particularly challenging, but it should be far faster now these blocks are in place. .  If anyone is interested in creating a specific page, please also get in touch with Stuart.  The various ''Nearest... and ...Information'' pages should be quite easy to do based on the pages that have been written already.  See the Design Notes for details.&lt;br /&gt;
&lt;br /&gt;
Next steps:&lt;br /&gt;
* Writing the remaining (large) number of pages&lt;br /&gt;
* Styling the pages - making the iconography match Garmin exactly.&lt;br /&gt;
&lt;br /&gt;
=== Gallery ===&lt;br /&gt;
&amp;lt;gallery mode=packed widths=230px heights=230px&amp;gt;&lt;br /&gt;
PFD 20180406.png|FG1000 PFD showing inset map, current wind data, HSI with GPS bearing, active flightplan list&lt;br /&gt;
MFD 20180406.png|FG1000 MFD with Cessna 182T Engine Information System (EIS), and Active Flightplan&lt;br /&gt;
FG1000_Checklist_MFD_page.png|FG1000 MFD with Checklist page displayed&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== A Cheat's Guide to Using the FG1000 ==&lt;br /&gt;
&lt;br /&gt;
For those without the time or interest to read the G1000 User Guide, here's a very quick run-down on navigating through the MFD:&lt;br /&gt;
&lt;br /&gt;
General:&lt;br /&gt;
* Click on the buttons, though some are not implemented yet.&lt;br /&gt;
* Use the mouse wheel to scroll the knobs (NAV, HDG, ALT, COM, CRS/BARO, RANGE, FMS).  For knobs with both and inner and an outer (NAV, COM, CRS/BARO, FMS), scrolling when pressing the Shift key changes the outer knob.&lt;br /&gt;
* Use the outer FMS knob (shift-mouse wheel on the FMS knob on the bottom right) to navigate through the page groups.&lt;br /&gt;
* Use the inner FMS knob (mouse wheel on the FMS knob on the bottom right) to navigate through the pages within the group.  The currently selected page is shown in blue. If you stop moving the cursor, the page will be loaded in 0.5s.&lt;br /&gt;
* Within a page, click on the FMS knob to activate the cursor (CRSR).  You you should see some element highlighted on the page, typically flashing.  You can navigate around the page using the outer and inner FMS knobs.  The Outer FMS knob (shift-mouse wheel) typical moves the cursor between elements.  The Inner FMS knob typically changes the selected item.&lt;br /&gt;
* Use the RANGE knob to zoom map views in/out.&lt;br /&gt;
&lt;br /&gt;
Implemented function:&lt;br /&gt;
&lt;br /&gt;
PFD:&lt;br /&gt;
* Artificial horizon, compass rose, altimeter and airspeed indicator&lt;br /&gt;
* HSI that can display NAV1, NAV2, GPS deviation (press CDI)&lt;br /&gt;
* Wind information (press PFD-&amp;gt;WIND then select one of the options for display)&lt;br /&gt;
* Bearings to NAV1, NAV2, GPS (press PFD then BRG1/BRG2 to cycle through the options)&lt;br /&gt;
* Inset Map (Press INSET softkey)&lt;br /&gt;
* Active Flight Plan (press FPL when there is an active flightplan to display)&lt;br /&gt;
* DirectTo GPS (press DTO)&lt;br /&gt;
&lt;br /&gt;
MFD:&lt;br /&gt;
* There is a two channel NAV/COM with an active and standby frequency and automatic ID of NAV frequencies at the top of the UI.  They all integrate with the standard properties, so should work automatically with most aircraft.&lt;br /&gt;
* If you have a frequency highlighted in any of the pages (e.g. WPT - AIRPORT INFORMATION) press the ENT key to load it into the standby NAV/COM frequency as appropriate.&lt;br /&gt;
* To set up a GPS Direct To route:&lt;br /&gt;
** Press the DTO button (D with an arrow through it on the right side of the panel, below the word &amp;quot;PAN&amp;quot;).  This will bring up a Direct To window, allowing you to enter the ID of an airport, Fix etc. to navigate to.&lt;br /&gt;
** You can enter the ID in one of two ways:&lt;br /&gt;
*** Scroll the FMS knob backwards to bring up a sub-menu of waypoints categories (FPL, NRST, RECENT, USER, AIRWAY).  Use the inner FMS knob to scroll between the different categories, and the outer FMS knob to select a specific item.  &lt;br /&gt;
*** Scroll the FMS knob forwards to enter an ID manually using the FMS knob.  Use the outer FMS knob to move to the next poisition in the ID.  &lt;br /&gt;
** Once you've got an ID, press ENT which will load details into the window and highlight the ACTIVATE button.&lt;br /&gt;
** Press ENT once more to load the Direct-to into the GPS and activate it.&lt;br /&gt;
* To view airport information, select the WPT - AIRPORT INFORMATION page.  Use the outer FMS knob to move between the AIRPORT, RUNWAYS and FREQUENCY boxes, and the inner FMS to edit the airport ID, scroll through available runways, or highlight a frequency.&lt;br /&gt;
* To edit the current flightplan, press the FPL page, then press the CRSR and enter waypoints for DTO above.&lt;br /&gt;
&lt;br /&gt;
== Aircraft Installation ==&lt;br /&gt;
&lt;br /&gt;
Adding the FG1000 to an aircraft is intended to be fairly straightforward.&lt;br /&gt;
&lt;br /&gt;
=== Add the GDU to the cockpit ===&lt;br /&gt;
&lt;br /&gt;
Place one or more of the display (GDU) units in your model.  The XML files&lt;br /&gt;
are in GDU104X directory and are named  &amp;lt;model&amp;gt;.&amp;lt;device-number&amp;gt;.xml, e.g.&lt;br /&gt;
&lt;br /&gt;
GDU-1045.1.xml  GDU-1045.2.xml  GDU-1045.3.xml  GDU-1045.4.xml&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;device-number&amp;gt; is referenced later to identify which displays to used&lt;br /&gt;
for each PFD, MFD etc.  You must only used one of each &amp;lt;device-number&amp;gt; value,&lt;br /&gt;
though you can mix and match the GDU models.&lt;br /&gt;
&lt;br /&gt;
The different GDU models are as follows:&lt;br /&gt;
&lt;br /&gt;
* GDU-1040 - No autopilot controls&lt;br /&gt;
* GDU-1044B - autopilot controls without VNAV&lt;br /&gt;
* GDU-1045 - autopilot controls with VNAV&lt;br /&gt;
&lt;br /&gt;
=== Load the Interfaces to provide data ===&lt;br /&gt;
&lt;br /&gt;
This is used to provide airdata, navdata, NAV/COM settings, engine information &lt;br /&gt;
to the GDUs.  There is a&lt;br /&gt;
generic interface (GenericInterfaceController) that uses the standard FG route&lt;br /&gt;
manager, GPS, navigation data and NAV/COM settings, and engine data for a single&lt;br /&gt;
piston engine.&lt;br /&gt;
&lt;br /&gt;
You need to load it as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
var nasal_dir = getprop(&amp;quot;/sim/fg-root&amp;quot;) ~ &amp;quot;/Aircraft/Instruments-3d/FG1000/Nasal/&amp;quot;;&lt;br /&gt;
io.load_nasal(nasal_dir ~ 'Interfaces/GenericInterfaceController.nas', &amp;quot;fg1000&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
var interfaceController = fg1000.GenericInterfaceController.getOrCreateInstance();&lt;br /&gt;
interfaceController.start();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You may want to create your own version depending on what properties you are&lt;br /&gt;
using for the NAV/COM, and your engine configuration.&lt;br /&gt;
&lt;br /&gt;
Note that you should only start the interface after you've started the FG1000&lt;br /&gt;
as they will publish information at start of day that the displays need.&lt;br /&gt;
&lt;br /&gt;
=== Create an EIS Display ===&lt;br /&gt;
&lt;br /&gt;
This is the Engine Information System and displays on the left side of the MFD.&lt;br /&gt;
&lt;br /&gt;
There is a generic EIS provided&lt;br /&gt;
(see FG1000/Nasal/EIS, and FG1000/MFDPages/EIS.svg) that is intended to match&lt;br /&gt;
a Cessna 172.  If you need something different, you should create your own&lt;br /&gt;
EIS class and SVG file.&lt;br /&gt;
&lt;br /&gt;
Note that you need to pass data to/from the engine using Emesary. See&lt;br /&gt;
Interfaces/GenericEISPublisher.nas for a reference implementation.&lt;br /&gt;
&lt;br /&gt;
=== Load and start the FG1000 system ===&lt;br /&gt;
&lt;br /&gt;
See FG1000/Nasal/FG1000.nas for full details,&lt;br /&gt;
including how to pass in the EIS. A simple FG1000 using the generic EIS, and&lt;br /&gt;
displaying PFD on device-number 1 and an MFD on device-number 2 is as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
var nasal_dir = getprop(&amp;quot;/sim/fg-root&amp;quot;) ~ &amp;quot;/Aircraft/Instruments-3d/FG1000/Nasal/&amp;quot;;&lt;br /&gt;
io.load_nasal(nasal_dir ~ 'FG1000.nas', &amp;quot;fg1000&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
# Create the FG1000&lt;br /&gt;
var fg1000system = fg1000.FG1000.getOrCreateInstance();&lt;br /&gt;
&lt;br /&gt;
# Create a PFD as device 1, MFD as device 2&lt;br /&gt;
fg1000system.addPFD(1);&lt;br /&gt;
fg1000system.addMFD(2);&lt;br /&gt;
&lt;br /&gt;
# Display the devices&lt;br /&gt;
fg1000system.display(1);&lt;br /&gt;
fg1000system.display(2);&lt;br /&gt;
&lt;br /&gt;
#  Display a GUI version of device 1 at 50% scale.&lt;br /&gt;
#fg1000system.displayGUI(1, 0.5);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Page Status ==&lt;br /&gt;
&lt;br /&gt;
This is the current status of the individual pages.  Full progress bars indicate that they are functionally complete, though the iconography is not correct.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable sortable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Page !! Reference !! Status !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| {{FG1000 file|name=NavigationMap|label=Navigation Map}} ||  || {{progressbar|100|20}} || See Layers below&lt;br /&gt;
|-&lt;br /&gt;
| {{FG1000 file|name=TrafficMap|label=Traffic Map}} ||  || {{progressbar|100|20}} || Zoom controls unclear&lt;br /&gt;
|-&lt;br /&gt;
| {{FG1000 file|name=Stormscope|label=Storm Scope}} ||  || {{progressbar|0|20}} || &lt;br /&gt;
|-&lt;br /&gt;
| {{FG1000 file|name=WeatherDataLink|label=Weather Data Link}} ||  || {{progressbar|0|20}} || &lt;br /&gt;
|-&lt;br /&gt;
| {{FG1000 file|name=TAWSB|label=TAWS-B}}||  || {{progressbar|0|20}} || &lt;br /&gt;
|-&lt;br /&gt;
| {{FG1000 file|name=AirportInformation|label=Airport Information}} ||  || {{progressbar|100|20}} || Needs additional page of AOPA information, which we don't have&lt;br /&gt;
|-&lt;br /&gt;
| {{FG1000 file|name=IntersectionInfo|label=Intersection Information}} ||  || {{progressbar|100|20}} || &lt;br /&gt;
|-&lt;br /&gt;
| {{FG1000 file|name=NDBInfo|label=NDB Information}} ||  || {{progressbar|100|20}} || &lt;br /&gt;
|-&lt;br /&gt;
| {{FG1000 file|name=VORInfo|label=VOR Information}} ||  || {{progressbar|100|20}} || &lt;br /&gt;
|-&lt;br /&gt;
| User WPT Information ||  || {{progressbar|0|20}} || &lt;br /&gt;
|-&lt;br /&gt;
| Trip Planning ||  || {{progressbar|0|20}} || &lt;br /&gt;
|-&lt;br /&gt;
| Utility ||  || {{progressbar|0|20}} || &lt;br /&gt;
|-&lt;br /&gt;
| GPS Status ||  || {{progressbar|0|20}} || &lt;br /&gt;
|-&lt;br /&gt;
| XM Radio ||  || {{progressbar|0|20}} || &lt;br /&gt;
|-&lt;br /&gt;
| System Status ||  || {{progressbar|0|20}} || &lt;br /&gt;
|-&lt;br /&gt;
| {{FG1000 file|name=ActiveFlightPlanNarrow|label=Active Flight Plan}} ||  || {{progressbar|100|20}} || Viewing and editing of the active flight plan implemented.  VNAV and the &amp;quot;wide view&amp;quot; not yet implemented&lt;br /&gt;
|-&lt;br /&gt;
| Flight Plan Catalog ||  || {{progressbar|0|20}} || &lt;br /&gt;
|-&lt;br /&gt;
| Stored Flight Plan ||  || {{progressbar|0|20}} || &lt;br /&gt;
|-&lt;br /&gt;
| {{FG1000 file|name=Checklist|label=Checklist}} ||  || {{progressbar|100|20}} || &lt;br /&gt;
|-&lt;br /&gt;
| {{FG1000 file|name=NearestAirports|label=Nearest Airports}} ||  || {{progressbar|100|20}} || &lt;br /&gt;
|-&lt;br /&gt;
| {{FG1000 file|name=NearestIntersections|label=Nearest Intersections}} ||  || {{progressbar|100|20}} || &lt;br /&gt;
|-&lt;br /&gt;
| {{FG1000 file|name=NearestNDB|label=Nearest NDB}} ||  || {{progressbar|100|20}} || &lt;br /&gt;
|-&lt;br /&gt;
| {{FG1000 file|name=NearestNDB|label=Nearest VOR}} ||  || {{progressbar|100|20}} || &lt;br /&gt;
|-&lt;br /&gt;
| Nearest User Waypoints ||  || {{progressbar|0|20}} || &lt;br /&gt;
|-&lt;br /&gt;
| Nearest Frequencies ||  || {{progressbar|0|20}} || &lt;br /&gt;
|-&lt;br /&gt;
| Nearest Airspaces ||  || {{progressbar|0|20}}|| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This is the current status of the major functional elements&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Area !! Status !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| Navigation - FMS || {{progressbar|100|20}} || &lt;br /&gt;
|-&lt;br /&gt;
| Softkeys || {{progressbar|100|20}} || &lt;br /&gt;
|-&lt;br /&gt;
| MENU key || {{progressbar|0|20}} || &lt;br /&gt;
|-&lt;br /&gt;
| NAV/COM  || {{progressbar|90|20}} || Volume controls to be added&lt;br /&gt;
|-&lt;br /&gt;
| GPE Header || {{progressbar|0|20}} ||&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Motivation ==&lt;br /&gt;
{{Main article|Canvas_News#Moving_map.2FRNAV_discussion}}&lt;br /&gt;
&lt;br /&gt;
The enormous variety in current glass flight decks means we really need to think of a new way of defining glass cockpit layouts.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://sourceforge.net/p/flightgear/mailman/message/15602744/ &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: [Flightgear-devel] Glass cockpit  and external gauges. &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Robin van Steenbergen &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Oct 2nd, 2007 &lt;br /&gt;
  |added  =  Oct 2nd, 2007 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The airspace system is in the process of changing drastically [...]  this isn't just a matter of throwing up a canvas showing some GPS waypoints and a magenta line. Modern navigators are astoundingly-complex devices — probably an order of magnitude more lines of code than FlightGear itself — and even their basic flight planning algorithms and databases (e.g. fly-by waypoints vs fly-over waypoints, open vs closed approach procedures, transitions into RNAV approaches, etc.) are far beyond the scope of anything we've tried, and we'd also need an up-to-date database far more complex than the ones we have now. Once you get to the extra features, like FIS-B weather or TIS-B traffic info over ADS-B, or TAWS (terrain alerting), we're probably in way over our heads trying to emulate even the simplest general-aviation IFR GPS.&lt;br /&gt;
&lt;br /&gt;
This may help folks understand what the G1000 is all about: http://static.garmincdn.com/pumac/190-00498-07_0A_Web.pdf &lt;br /&gt;
Writing a G1000 isn't that hard. Writing a '''feature complete''' G1000 is a ton of work. &amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://sourceforge.net/p/flightgear/mailman/message/35925783/ &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: [Flightgear-devel] RFD: FlightGear and the changing state of&lt;br /&gt;
 air navigation &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; geneb &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 3rd, 2017 &lt;br /&gt;
  |added  =  Jul 3rd, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Depending on how we deal with this challenge, the question is whether that means that the usefulness of FlightGear will also gradually taper off. &amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://sourceforge.net/p/flightgear/mailman/message/35924395/ &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; [Flightgear-devel] RFD: FlightGear and the changing state of air&lt;br /&gt;
 navigation &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; David Megginson &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 3rd, 2017 &lt;br /&gt;
  |added  =  Jul 3rd, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Instead of just making one-off tweaks like the consumer sims did, we (as a team) emulated entire systems like the vacuum, pitot-static, and electrical systems, so that failures would be realistic. In the RNAV age, we need to do the same thing; it's just that it's a bigger job. FlightGear will still be great for people who want to practice the mechanical parts of flying (e.g. crosswind wheel landings in a Cub), but will slip further and further behind for people who want to use it for real IFR practice.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://sourceforge.net/p/flightgear/mailman/message/35927088/ &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: [Flightgear-devel] RFD: FlightGear and the changing state of&lt;br /&gt;
 air navigation &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; David Megginson &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 4th, 2017 &lt;br /&gt;
  |added  =  Jul 4th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Background ==&lt;br /&gt;
{{Main article|Complex Canvas Avionics}}&lt;br /&gt;
&lt;br /&gt;
== Challenges ==&lt;br /&gt;
=== Performance ===&lt;br /&gt;
{{See also|Canvas_Development#Property_I.2FO_observations|Canvas_MapStructure#Performance}}&lt;br /&gt;
Nasal as such is fast, and compared with the cost of rendering the terrain, rendering a canvas display is fast on the GPU (you can test by rendering but not updating a canvas display), and unless you're doing something very inefficient, calculating a display but not writing it to the property tree is fast (you can test this by disabling the write commands). It's the property I/O which you need to structure well, and then canvas will run fast. And of course the complexity of your underlying simulation costs - if you run a thermal model underneath to get real temperature reading, it costs more than inventing the numbers. But that has nothing to do with canvas. &amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://sourceforge.net/p/flightgear/mailman/message/35926343/ &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: [Flightgear-devel] RFD: FlightGear and the changing state of&lt;br /&gt;
 air navigation &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Thorsten Renk &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 4th, 2017 &lt;br /&gt;
  |added  =  Jul 4th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
it's a lot of work to code all these displays which someone has to do, but there's no reason to assume it'd be hopeless performance-wise.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://sourceforge.net/p/flightgear/mailman/message/35926412/ &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: [Flightgear-devel] RFD: FlightGear and the changing state of&lt;br /&gt;
 air navigation &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Thorsten Renk &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 4th, 2017 &lt;br /&gt;
  |added  =  Jul 4th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
we need to be realistic here: The G1000 is a fairly significant piece of computer hardware that we're going to emulate. It's not going to be &amp;quot;free&amp;quot; particularly for those on older hardware that's already struggling. However, hopefully we can offload a chunk of the logic (route management, autopilot/flight director) to the core, and do things like offline generation of terrain maps to minimize the impact.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://sourceforge.net/p/flightgear/mailman/message/35926745/ &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: [Flightgear-devel] RFD: FlightGear and the changing state of&lt;br /&gt;
 air navigation &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Stuart Buchanan &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 4th, 2017 &lt;br /&gt;
  |added  =  Jul 4th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Route Manager ===&lt;br /&gt;
that's exactly the kind of device James hopes the new code can support. He's read the G1000 pilot's manual, and  *thinks* that nearly all the functions can be provided by the current GPS code It would be great if people could look over the current GPS features and indicate any pieces you think might be missing - additional commands, additional search features, extra data, or anything really. &amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://sourceforge.net/p/flightgear/mailman/message/23814733/ &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: [Flightgear-devel] ZKV500 GPS instrument; (New?) GPS code bug &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; James Turner &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Oct 22nd, 2009 &lt;br /&gt;
  |added  =  Oct 22nd, 2009 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== tile servers ===&lt;br /&gt;
Please keep in mind that most tile servers discourage bulk downloads. For the whole planet,oomlevel 8 is approx. 67k files, at zoomlevel 9 you have 265k, level 10 has a little over one million tiles. And one would probably want to go up to level 15 (roughly 1e9 files)&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://sourceforge.net/p/flightgear/mailman/message/36057678/ &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: [Flightgear-devel] Canvas MapLayer for OpenStreetMap, OpenAIP,&lt;br /&gt;
 VFR Sectionals &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Torsten Dreyer &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Sep 29th, 2017 &lt;br /&gt;
  |added  =  Sep 29th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Offline mode ===&lt;br /&gt;
would it be possible to provide an offline tool to pre-fetch a region, like TerraMaster, or terrasync.py? Call me old-fashioned or paranoid, but I don't want FG to go online and do stuff automatically, I prefer downloading what I need manually so that I know what gets onto my harddisk. Also - think of all the people with poor home internet which might have larger bandwidth only in a library elsewhere.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://sourceforge.net/p/flightgear/mailman/message/36057499/ &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: [Flightgear-devel] Canvas MapLayer for OpenStreetMap, OpenAIP,&lt;br /&gt;
 VFR Sectionals &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Thorsten Renk &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Sep 29th, 2017 &lt;br /&gt;
  |added  =  Sep 29th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
that would certainly be possible - the code retrieving the data is pretty trivial and porting the Nasal code to python or some other script should be straightforward.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://sourceforge.net/p/flightgear/mailman/message/36057657/ &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: [Flightgear-devel] Canvas MapLayer for OpenStreetMap, OpenAIP,&lt;br /&gt;
 VFR Sectionals &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Stuart Buchanan &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Sep 29th, 2017 &lt;br /&gt;
  |added  =  Sep 29th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Navdata ===&lt;br /&gt;
we need some better data sources, especially with direct routing replacing airways in many areas, and having airspace data would help the map displays, but the current API is specifically designed to support G430, G1000 and similar class devices. Adding RNAV approaches is eminently doable, see the the RNavController base class which the GPS/FMS layer uses to allow new waypoint / route segments to define how they are flown and drive CDIs &amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://sourceforge.net/p/flightgear/mailman/message/35927598/ &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: [Flightgear-devel] RFD: FlightGear and the changing state of&lt;br /&gt;
 air navigation &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; James Turner &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 4th, 2017 &lt;br /&gt;
  |added  =  Jul 4th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Getting the algorithms right, and keeping the data up to date every 7 weeks (we have only a tiny sliver of that data currently), is going to be much more of a challenge. I expect that it's probably an order of magnitude more complicated than our current flight dynamics (etc.), so we'd have to grow our contributor base, and find funding to pay at least Curt (and maybe a couple of others) full time to manage the project. Note that that's what has happened for other complex FOSS projects, like office suites and browsers, which generally end up working through a foundation, rather than our current relaxed circle-of-friends arrangement. The reason it's so much harder now is that the software *is* the product for GPS navigators, FMSs, etc. (unlike with older avionics, where the hardware was the main thing). That means that emulating even a simple unit like the GTN 650 or GNS 430W is almost difficult as building one, so we're trying to keep up with armies of full-time software developers at Garmin, Avidyne, etc.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://sourceforge.net/p/flightgear/mailman/message/35927243/ &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: [Flightgear-devel] RFD: FlightGear and the changing state of&lt;br /&gt;
 air navigation &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; David Megginson &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 4th, 2017 &lt;br /&gt;
  |added  =  Jul 4th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
According to the Route Manager wiki page, we already have support for SID/STAR data provided from Navigraph, which is released on the AIRAC cycle, and costs a pretty small amount (9 EUR for a single download of FMS data, or 90 EUR per year) Given the relatively low cost, I don't think that we as an organiation want to get into trying to digitize data ourselves just to make it open-source or public domain. Particularly given the low cost of a download. We might want to digitize the STAR/SIDs for the default airport with each release so there are some approaches available for those who don't want to purchase the data.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://sourceforge.net/p/flightgear/mailman/message/35927426/ &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: [Flightgear-devel] RFD: FlightGear and the changing state of&lt;br /&gt;
 air navigation &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Stuart Buchanan &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jul 4th, 2017 &lt;br /&gt;
  |added  =  Jul 4th, 2017 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
everyone agrees we would like to add airspace data - what we’re missing is a suitable data source with the correct license. Torsten found a raster-based source but for wider use in the sim we really need vector data, so we can render it ourselves directly. &amp;lt;ref&amp;gt;{{cite web  |url    =  https://sourceforge.net/p/flightgear/mailman/message/36178857/   |title  =  &amp;lt;nowiki&amp;gt; Re: [Flightgear-devel] [flightgear/simgear] Nasal regex patch &amp;lt;/nowiki&amp;gt;   |author =  &amp;lt;nowiki&amp;gt; James Turner &amp;lt;/nowiki&amp;gt;   |date   =  Jan 5th, 2018   |added  =  Jan 5th, 2018   |script_version = 0.36   }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For airspace data we could use [https://openflightmaps.org/ https://openflightmaps.org/] . It provides community created navigation data for many countries in (currently only) Europe with a free license.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=326105#p326105 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Canvas G1000 &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; TheTom &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jan 9th, 2018 &lt;br /&gt;
  |added  =  Jan 9th, 2018 &lt;br /&gt;
  |script_version = 0.36 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
we definitely want vector data.  Stuart is hoping that we might convince the source of the raster data we're using at present to provide it as raw vector data, but we haven't had any luck so far.&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=326201&amp;amp;sid=137f64d32b770e50a3c4061be42ce28a#p326201 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Canvas G1000 &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; stuart &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Jan 10th, 2018 &lt;br /&gt;
  |added  =  Jan 10th, 2018 &lt;br /&gt;
  |script_version = 0.36 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Airport Data (US - only )==&lt;br /&gt;
At least for the US, all required information is available in the form of regularly updated UDFF files:&lt;br /&gt;
* https://www.ngs.noaa.gov/AERO/aero.html&lt;br /&gt;
* https://www.ngs.noaa.gov/AERO/uddf/WESTERN-PACIFIC/CALIFORNIA/SFO__03B.F77&lt;br /&gt;
* https://www.ngs.noaa.gov/AERO/annof_7.pdf&lt;br /&gt;
&lt;br /&gt;
It would seem relatively straightforward to provide a scripted parser to create the corresponding charts procedurally.&lt;br /&gt;
As a matter of fact, this could even be done in a background thread or using a separate Python script, i.e. if we wanted to ship pre-created imagery as part of fgdata.&lt;br /&gt;
&lt;br /&gt;
However, if the built-in Canvas system is extended to provide these charts, [[Phi]] could also be entirely self-contained by rendering the same  airport charts without having to depend on external data sources.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Layers  ==&lt;br /&gt;
&lt;br /&gt;
{{See also|Canvas_Sandbox#CanvasShapefile}}&lt;br /&gt;
&lt;br /&gt;
Following is the list of layers displayed by the G1000 system (see page 153 of the 190-00498-07 Rev. A), and mapping to the equivalent MapStructure Layer.&lt;br /&gt;
Many of these would also be good to have for other avionics/GUI dialogs, including the NavDisplay framework, which is currently re-implementing this functionality separately, i.e. not yet using MapStructure.&lt;br /&gt;
&lt;br /&gt;
Following is the list of layers displayed by the G1000 system, based on the Garmin G1000 Integrated Flight Deck Pilot's Guide for the Cessna Nav III [http://static.garmincdn.com/pumac/190-00498-07_0A_Web.pdf Garmin Site] [https://web.archive.org/web/20170708032442/http://static.garmincdn.com/pumac/190-00498-07_0A_Web.pdf Wayback machine], page 153, and the mapping to the equivalent MapStructure Layer.&lt;br /&gt;
&lt;br /&gt;
Richard mentioned that if he were to implement approach plates in the EFB he'd probably use raster images provided via http and provide an http service within FG to do this, or to allow the EFB to use any other external web service. Other content for the EFB could be also provided as SVG via http.&amp;lt;ref&amp;gt;{{cite web  |url    =  https://forum.flightgear.org/viewtopic.php?p=259194#p259194   |title  =  &amp;lt;nowiki&amp;gt; Re:  &amp;lt;/nowiki&amp;gt;   |author =  &amp;lt;nowiki&amp;gt; Richard &amp;lt;/nowiki&amp;gt;   |date   =  Sep 29th, 2015   |added  =  Sep 29th, 2015   |script_version = 0.36   }}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Layer !! [[Canvas MapStructure Layers|MapStructure Layer]] !! Status !! Page || Notes&lt;br /&gt;
|-&lt;br /&gt;
| Flight Plan Route Lines || {{MapStructure_File|name=RTE|type=lcontroller}} || Requires styling || {{PDF Link|page=190}} ||&lt;br /&gt;
|-&lt;br /&gt;
| Flight Plan Route Waypoints || {{MapStructure_File|name=WPT|type=lcontroller}}  || Requires styling || {{PDF Link|page=190}} || &lt;br /&gt;
|-&lt;br /&gt;
| Rivers/Lakes || {{MapStructure_File|name=VFRChart|type=lcontroller}}   ||   || {{PDF Link|page=148}} || Currently using downloaded raster from web.  Perhaps generate similarly to Atlas&amp;lt;ref&amp;gt;{{cite web&lt;br /&gt;
  |url    =  https://forum.flightgear.org/viewtopic.php?p=203495#p203495 &lt;br /&gt;
  |title  =  &amp;lt;nowiki&amp;gt; Re: Atlas still in use ? &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |author =  &amp;lt;nowiki&amp;gt; Torsten &amp;lt;/nowiki&amp;gt; &lt;br /&gt;
  |date   =  Mar 17th, 2014 &lt;br /&gt;
  |added  =  Mar 17th, 2014 &lt;br /&gt;
  |script_version = 0.40 &lt;br /&gt;
  }}&amp;lt;/ref&amp;gt;, or could be vector data from scenery. {{AtlasVsCanvasCameras}}&lt;br /&gt;
|-&lt;br /&gt;
| Topography Data || {{MapStructure_File|name=VFRChart|type=lcontroller}} || Synthetic  || {{PDF Link|page=145}} || Height-map at chart-resolution.  Perhaps generate similarly to Atlas? {{AtlasVsCanvasCameras}}&lt;br /&gt;
|-&lt;br /&gt;
| International Borders ||   ||   ||  {{PDF Link|page=148}} || Vector data from scenery?&lt;br /&gt;
|-&lt;br /&gt;
| Track Vector ||   ||   || {{PDF Link|page=156}} || Forward looking display of track. Look-ahead time selectable. &lt;br /&gt;
|-&lt;br /&gt;
| Navigation Range Ring ||   ||   || {{PDF Link|page=159}} || Straightforward extension of {{MapStructure_File|name=APS|type=lcontroller}}.&lt;br /&gt;
|-&lt;br /&gt;
| Fuel Range Ring ||   ||   || 159 || Straightforward extension of {{MapStructure_File|name=APS|type=lcontroller}}.&lt;br /&gt;
|-&lt;br /&gt;
| Terrain Data ||   ||   || 364 || Should be straightforward, with exception of obstacles.  [[Spoken GCA|Profile view]] also required.&lt;br /&gt;
|-&lt;br /&gt;
| Traffic || {{MapStructure_File|name=TFC|type=lcontroller}} ||   || 394,423 || Various options, each with different iconography and data displayed. &lt;br /&gt;
|-&lt;br /&gt;
| Airways || {{MapStructure_File|name=VFRChart|type=lcontroller}} ||   ||  154 || Needs to be replaced with vector data&lt;br /&gt;
|-&lt;br /&gt;
| NEXRAD ||   ||   || 282 || Heaps of weather data with complex symbology.&lt;br /&gt;
|-&lt;br /&gt;
| XM Lightning Data || {{MapStructure_File|name=WXR|type=lcontroller}} || Change to symbol for lightning? || 294 || Optional  &lt;br /&gt;
|-&lt;br /&gt;
| Airports || {{MapStructure_File|name=APT|type=lcontroller}}, {{MapStructure_File|name=RWY|type=lcontroller}} || Require re-style based on size  ||  149, 163 ||&lt;br /&gt;
|-&lt;br /&gt;
| Runway Labels || {{MapStructure_File|name=RWY|type=lcontroller}} ||   || 148 || Needs to be added to {{MapStructure_File|name=RWY|type=symbol}}.  Should be straightforward&lt;br /&gt;
|-&lt;br /&gt;
| Restricted ||  {{MapStructure_File|name=OpenAIP|type=lcontroller}}  ||   ||  182 || {{NeedVectorData}}&lt;br /&gt;
|-&lt;br /&gt;
| MOA (Military) ||  {{MapStructure_File|name=OpenAIP|type=lcontroller}}  ||   ||  182 || {{NeedVectorData}}&lt;br /&gt;
|-&lt;br /&gt;
| User Waypoints ||   ||   ||  &lt;br /&gt;
|-&lt;br /&gt;
| Latitude/Longitude Grid || {{MapStructure_File|name=GRID|type=lcontroller}} ||   ||  &lt;br /&gt;
|-&lt;br /&gt;
| NAVAIDs || {{MapStructure_File|name=APT|type=lcontroller}}, {{MapStructure_File|name=VOR|type=lcontroller}}, {{MapStructure_File|name=FIX|type=lcontroller}}, {{MapStructure_File|name=NDB|type=lcontroller}} || APT, requires styling  ||  162 || &lt;br /&gt;
|-&lt;br /&gt;
| Class B Airspaces/TMA || {{MapStructure_File|name=OpenAIP|type=lcontroller}}  ||   || 182 || {{NeedVectorData}}&lt;br /&gt;
|-&lt;br /&gt;
| Class C Airspaces/TCA || {{MapStructure_File|name=OpenAIP|type=lcontroller}} ||   ||  182 || {{NeedVectorData}}&lt;br /&gt;
|-&lt;br /&gt;
| Class D Airspaces || {{MapStructure_File|name=OpenAIP|type=lcontroller}}  ||   ||  182 || {{NeedVectorData}}&lt;br /&gt;
|-&lt;br /&gt;
| Other Airspaces/ADIZ || {{MapStructure_File|name=OpenAIP|type=lcontroller}}  ||   ||  182 || {{NeedVectorData}}&lt;br /&gt;
|-&lt;br /&gt;
| TFRs ||   ||   || 310 || &lt;br /&gt;
|-&lt;br /&gt;
| Obstacles || {{MapStructure_File|name=OpenAIP|type=lcontroller}}  ||   ||  182 || {{NeedVectorData}}&lt;br /&gt;
|-&lt;br /&gt;
| Land/Country Text ||   ||   || 148 || Currently using raster data from web. Replace with {{MapStructure_File|name=POI|type=lcontroller}}? &lt;br /&gt;
|-&lt;br /&gt;
| Cities  || {{MapStructure_File|name=VFRChart|type=lcontroller}} ||   ||  148 || Currently using raster data from web.  Perhaps generate similarly to Atlas, or could be vector data from scenery. {{AtlasVsCanvasCameras}}&lt;br /&gt;
|-&lt;br /&gt;
| Roads || {{MapStructure_File|name=VFRChart|type=lcontroller}}  ||   ||  148 || Currently using raster data from web.  Requires vector data from scenery&lt;br /&gt;
|-&lt;br /&gt;
| Railroads || {{MapStructure_File|name=VFRChart|type=lcontroller}}  ||   ||  148 || Currently using raster data from web.  Requires vector data from scenery&lt;br /&gt;
|-&lt;br /&gt;
| State/Province Boundaries || {{MapStructure_File|name=VFRChart|type=lcontroller}}  ||   ||  148 || Currently using raster data from web.  Requires vector data from scenery&lt;br /&gt;
|-&lt;br /&gt;
| River/Lake Names || {{MapStructure_File|name=VFRChart|type=lcontroller}}  ||   ||  148 || Currently using raster data from web.  Replace with {{MapStructure_File|name=POI|type=lcontroller}}?&lt;br /&gt;
|-&lt;br /&gt;
| Selected Altitude Intercept Arc ||   ||   ||  161 || Simple extention to APS&lt;br /&gt;
|-&lt;br /&gt;
| SafeTaxi (Optional) || {{MapStructure_File|name=RWY|type=lcontroller}}, {{MapStructure_File|name=TAXI|type=lcontroller}} ||   || 493  || We don't currently have the data to display taxiway identifier or holding points&lt;br /&gt;
|-&lt;br /&gt;
| ChartView (Optional) || ||   || 503  || [[Canvas_Sandbox#CanvasPDF|Rendering of PDFs.]]  Might be possible to integrate with NaviGraph chart data?&lt;br /&gt;
|-&lt;br /&gt;
| FliteChart (Optional) || ||   || 521  || [[Canvas_Sandbox#CanvasPDF|Rendering of PDFs.]]  Might be possible to integrate with NaviGraph chart data?&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Existing work ==&lt;br /&gt;
{{See also|Complex Canvas Avionics}}&lt;br /&gt;
&lt;br /&gt;
=== Avionics / Cockpits ===&lt;br /&gt;
* [[User:Zakharov/zkv1000 installation guide|zkv1000]] see [https://forum.flightgear.org/viewtopic.php?f=14&amp;amp;t=32056]&lt;br /&gt;
* [[Extra500]] / Avidyne Entegra R9000&lt;br /&gt;
&lt;br /&gt;
===  Nasal / Canvas ===&lt;br /&gt;
* [[Canvas MFD Framework]]&lt;br /&gt;
* MapStructure&lt;br /&gt;
* MapStructure Layers&lt;br /&gt;
* [[Howto:Creating a Canvas GUI Widget]]&lt;br /&gt;
* [[Emesary]]&lt;br /&gt;
&lt;br /&gt;
=== C++ ===&lt;br /&gt;
* [[Atlas]] map creation&lt;br /&gt;
* [[Howto:Canvas View Camera Element|Synthetic Vision]] element for rendering viewmgr views to a Canvas &lt;br /&gt;
* [[Canvas_Sandbox#CanvasPDF|Canvas PDF]] element - for rendering PDF files (think EFB functionality)&lt;br /&gt;
&lt;br /&gt;
== Possible Work ==&lt;br /&gt;
&lt;br /&gt;
=== Architectural TODO List ===&lt;br /&gt;
This is a short list of architectural improvements I've noted during development that need to be revisited:&lt;br /&gt;
* Use NavMap for MFD Navigation Map, DirectTo, Nearest, Information pages.&lt;br /&gt;
* Separate out the HSI, tapes from the PFD and make them generic MFD UI Elements&lt;br /&gt;
* Improve the performance of the maps, perhaps using a cache for queries, definitely by more aggressive clipping of the SlippyMap layers based on map size.&lt;br /&gt;
&lt;br /&gt;
The layers table shown above mentions a few limitations and ideas, such as:&lt;br /&gt;
=== Nasal/Canvas ===&lt;br /&gt;
* &amp;lt;image&amp;gt; tag support for svg.nas (patch: [http://wiki.flightgear.org/Howto:Extend_the_Canvas_SVG_module])&lt;br /&gt;
* MapStructure: [[MapStructure_Optimizations#Allocating_selectable_ranges_into_groups|Allocating selectable ranges into Canvas groups]]&lt;br /&gt;
&lt;br /&gt;
=== C++ ===&lt;br /&gt;
* fix up [[Canvas_MapStructure#Performance|projection issues]], and adopt Gijs' [[Canvas_Development#Projections|projection handling code]] for the hard-coded PUI [[Map]], to get rid of the standard Sanson-Flamsteed projection &lt;br /&gt;
* adding a profile view, probably in conjunction with terrain sampling&lt;br /&gt;
* using atlas code to generate heightmaps/topography&lt;br /&gt;
* adding ESRI shapefile support to the Canvas system&lt;br /&gt;
* adding PDF rendering support the Canvas system&lt;br /&gt;
&lt;br /&gt;
== Design ==&lt;br /&gt;
&lt;br /&gt;
The broad structure of the Nasal files in Aircaft/Instruments-3d/FG1000/Nasal is as follows:&lt;br /&gt;
&lt;br /&gt;
* MFD.nas - top level MFD device, loads the other Nasal files (likely to be moved elsewhere later) and pages.&lt;br /&gt;
* PageGroupControllers.nas - controller for the Page Group display in the bottom right of the MFD, controlled by the FMS knob, and which allows selection between different page groups and individual pages.&lt;br /&gt;
* [PageName]/[PageName].nas - MFD page, inheriting from the MFDPage.nas.  Creates any required MapStructure and hierarchy of softkeys.&lt;br /&gt;
* [PageName]/[PageName]Controller.nas - Controller for the MFD page and the MapStructure layers, inheriting from MFDPageController.nas&lt;br /&gt;
* [PageName]/[PageName]Style.nas - Style controls for the MapStructure layers for the given Page.&lt;br /&gt;
* [PageName]/[PageName]Options.nas - Options for the MapStructure layers for the given Page.&lt;br /&gt;
&lt;br /&gt;
Key design notes:&lt;br /&gt;
* Most of the underlying Canvas [[MapStructure]] layers are now written (though they require styling, and/or would benefit from replacement with vector data). &lt;br /&gt;
* [[Emesary]] IPC framework is used to link between the MFD and underlying simulation state. This should make it easy to run the MFD on a separate FG instance, and provides a good demarkation between the individual aircraft systems and the FG1000 itself. &lt;br /&gt;
* Various underlying UI classes are used to handle highlighting, selection and scrolling of fields.  See [[Canvas MFD Framework]] for details of the elements now supported.&lt;br /&gt;
&lt;br /&gt;
== Resources ==&lt;br /&gt;
&lt;br /&gt;
{{#ev:youtube|I0y6p0ct1m0}}&lt;br /&gt;
&lt;br /&gt;
== Related ==&lt;br /&gt;
=== Commits ===&lt;br /&gt;
This section is intended to help keep track of related fgdata commits, so that if/when the project should stall, people can more easily look up related changes.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* {{fgdata commit|da252c8a18fe2ce88066c051f6df588639377792|text=Initial Direct To page}}(01/2018)&lt;br /&gt;
* {{fgdata commit|0c4544c1e1788f3396330c86016b66df8643fd3b|text=Add Config Store and MFD Header display.}}(01/2018)&lt;br /&gt;
* {{fgdata commit|9bd10f8273a6e64b12b9a034ca4952d87090f921|text=SVG MFD UI from miChat}}(01/2018)&lt;br /&gt;
* {{fgdata commit|f6f5efe9f442ff058d9d425d85337139b2a9bf1f|text=Use Emesary interface rather than airportinfo()}}(01/2018)&lt;br /&gt;
* {{fgdata commit|d306e7ba100840f5c3909123a383b19f7e7d7540|text=Set standby COM frequency from Airport pages}}(01/2018)&lt;br /&gt;
* {{fgdata commit|cbb281759cd3f2b830bffdd12ba72d456996a13a|text=NAV/COM Radio support}}(01/2018)&lt;br /&gt;
* {{fgdata commit|a25c66850a93f0ae7a6f7ad8769680c1fba99887|text=Add full set of hardkeys to fg1000 PUI dialog.}}(01/2018)&lt;br /&gt;
* {{fgdata commit|ab8774a3e0aa3909ad284a65194f5d80dc097c6e|text=extend MFDPageController to handle Emesary register}}(01/2018)&lt;br /&gt;
* {{fgdata commit|73424c1791242e053efcdac37a7dd3b66226387b|text=Update NAV/COMM frequencies from properties}}(01/2018)&lt;br /&gt;
* {{fgdata commit|ad77dc2f9cd2a1e1ebd5efd4226cbfbf3a522008|text=Modify FG1000 EIS to use Emesary}}(01/2018)&lt;br /&gt;
* {{fgdata commit|9eb91171b4689b29910db06757bc47767ff59d2c|text=FG1000 Nearest Airports page}}(01/2018)&lt;br /&gt;
* {{fgdata commit|475fd505859b7841e29de725d4c1c39db7ad4ee5|text=PFD UI Elements and NearestAirports page}} (12/2017)&lt;br /&gt;
* {{fgdata commit|d0203549e833b32b719697b3a2c2a63e16fcef16|text=Add AirportInfo and template pages for FG1000}} (12/2017)&lt;br /&gt;
* {{fgdata commit|3d31775ff36bf3aa08a929992b70f32f2a525509|text=Initial commit of FG1000 MFD}} (11/2017)&lt;br /&gt;
&lt;br /&gt;
MapStructure related:&lt;br /&gt;
&lt;br /&gt;
* {{fgdata commit|4bd57492f454a05f725e6fe5a80dc246cb5e5e0f|text=GRID layer}} (10/2017)&lt;br /&gt;
* {{fgdata commit|d84c527ca747dd6bbd9634507f0664ff3631bdef|text=RWY, TAXI, TWR, PARKING}} (10/2017)&lt;br /&gt;
* {{fgdata commit|e38ec03c609b882664d0496932709aa623e486cb|text=STAMEN}} (10/2017)&lt;br /&gt;
* {{fgdata commit|2fbc1dc491da867e0fd4f8fc810c5cddd80ced1f|text=VFRChart}} (09/2017)&lt;br /&gt;
* {{fgdata commit|85b7665c19ebaae02d746fef53f25f8d8974eb13|text=OSM and OpenAIP}} (09/2017)&lt;br /&gt;
&lt;br /&gt;
=== References ===&lt;br /&gt;
{{Appendix}}&lt;br /&gt;
&lt;br /&gt;
[[Category:Canvas]]&lt;/div&gt;</summary>
		<author><name>Clm76</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Nasal_Flightplan&amp;diff=112636</id>
		<title>Nasal Flightplan</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Nasal_Flightplan&amp;diff=112636"/>
		<updated>2017-12-03T07:17:43Z</updated>

		<summary type="html">&lt;p&gt;Clm76: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Systems Modeling Disclaimer}}&lt;br /&gt;
{{Autoflight Navigation}}&lt;br /&gt;
{{Nasal Navigation}}&lt;br /&gt;
&lt;br /&gt;
Because FlightGear's [[route manager]] flight plan system is exposed to [[Nasal]], '''Nasal can be used to interact with flight plans'''. Obviously, this is very useful, especially for aircraft like airliners, which have complex route managers systems. This article shows how this can be done.&lt;br /&gt;
&lt;br /&gt;
== Background ==&lt;br /&gt;
The Nasal functions relating to the route manager system include:&lt;br /&gt;
* {{func link|flightplan()}} - return the active plan.&lt;br /&gt;
* {{func link|airwaysRoute()}}&lt;br /&gt;
* {{func link|createFlightplan()}} - creates new empty plan. A path can be supplied to load a plan from xml.&lt;br /&gt;
&lt;br /&gt;
Flight plans are based on waypoints, which, in C++, inherit from the {{API Link|flightgear|class|FGPositioned}} class.&lt;br /&gt;
&lt;br /&gt;
== Waypoint hashes ==&lt;br /&gt;
The following are members of a waypoint ghost, as generated by, for example, {{func link|airwaysRoute()}}:&lt;br /&gt;
&lt;br /&gt;
; wp_name : Name of the waypoint, returned as string.&lt;br /&gt;
; wp_type : Waypoint type, returned as string. One of &amp;quot;basic,&amp;quot; &amp;quot;navaid,&amp;quot; &amp;quot;offset-navaid,&amp;quot; &amp;quot;runway,&amp;quot; &amp;quot;hdgToAlt,&amp;quot; &amp;quot;dmeIntercept,&amp;quot; &amp;quot;radialIntercept,&amp;quot; or &amp;quot;vectors.&amp;quot;&lt;br /&gt;
; wp_role: Role of waypoint.&lt;br /&gt;
; wp_lat ''or'' lat : Latitude of waypoint.&lt;br /&gt;
; wp_lon ''or'' lon : Longitude of waypoint.&lt;br /&gt;
; wp_parent_name : Name of waypoint's parent.&lt;br /&gt;
; wp_parent : Waypoint's parent.&lt;br /&gt;
; fly_type : How to waypoint should be flown over or reacted to. One of &amp;quot;Hold,&amp;quot; &amp;quot;flyOver,&amp;quot; or &amp;quot;flyBy.&amp;quot;&lt;br /&gt;
; heading_course : Heading of runway.&lt;br /&gt;
&lt;br /&gt;
== Flightplan methods and variables ==&lt;br /&gt;
Notice that instead of passing a leg as parameter, a waypoint can be passed.&lt;br /&gt;
; getWP(index) : Returns the leg for specified index.&lt;br /&gt;
; currentWP() : Return current active leg.&lt;br /&gt;
; nextWP() : Make next waypoint active.&lt;br /&gt;
; getPlanSize() : Returns number of waypoints&lt;br /&gt;
; appendWP(leg) : Add a leg to the end of the flightplan.&lt;br /&gt;
; insertWP(leg, index) : Pass a leg object and its position.&lt;br /&gt;
; deleteWP(index) : Deletes the waypoint at specified index.&lt;br /&gt;
; insertWPAfter() : &lt;br /&gt;
; insertWaypoints(vector, index) : Pass a vector of waypoint objects, and the position to insert.&lt;br /&gt;
; cleanPlan() : Clears all waypoints except destination and departure.&lt;br /&gt;
; clearWPType(type) : Supply a type string, it will clear all waypoints of the type.&lt;br /&gt;
; clone() : Return a copy of the flightplan.&lt;br /&gt;
; pathGeod() : &lt;br /&gt;
; finish() : Finish the plan. (a call to delegate will be made)&lt;br /&gt;
; indexOfWP(wp) : Returns the index of the passed waypoint/leg.&lt;br /&gt;
; destination : airport object as destination.&lt;br /&gt;
; destination_runway : rwy object as destination and its airport implicit.&lt;br /&gt;
; departure : airport object as departure.&lt;br /&gt;
; departure_runway : rwy object as departure and its airport implicit.&lt;br /&gt;
; id : optional name of plan.&lt;br /&gt;
; sid : procedure object for SID.&lt;br /&gt;
; star : procedure object for STAR.&lt;br /&gt;
; sid_trans :&lt;br /&gt;
; star_trans :&lt;br /&gt;
; approach : procedure object for approach.&lt;br /&gt;
; current : Index of current waypoint.&lt;br /&gt;
; aircraftCategory : ICAO aircraft category.&lt;br /&gt;
; followLegTrackToFix : Specific used by some procedures. It controls whether the system will fly on an intersection course (and hence, different track) to the end of the leg, or make a corrective S-turn to get back on the leg track immediately it becomes possible.&lt;br /&gt;
; activate() : This will make the flight plan the default (the one used in route-manager).&lt;br /&gt;
; save(path) : Save a plan to xml. Will return true or false for success.&lt;br /&gt;
&lt;br /&gt;
== Leg methods and variables ==&lt;br /&gt;
A leg is a wrapper for a waypoint, so it will have all waypoints variables and methods plus what is listed below.&lt;br /&gt;
Notice, that a leg you have gotten from a plan, but deleted in the plan, will be invalid and should not be used or modified.&lt;br /&gt;
A leg you have gotten from a plan, when modified, will modify the leg inside the plan you got it from.&lt;br /&gt;
; setSpeed(speed, type) : Sets the speed restriction. Setting nil type, will crash FG.&lt;br /&gt;
; setAltitude(altitude, type) : Sets the altitude restriction. Setting nil type, will crash FG.&lt;br /&gt;
; path() :&lt;br /&gt;
; courseAndDistanceFrom() :&lt;br /&gt;
; parents :&lt;br /&gt;
; index :&lt;br /&gt;
; alt_cstr : Read-only. Altitude restriction.&lt;br /&gt;
; alt_cstr_type : Read-only. Altitude restriction type. Can be &amp;quot;at&amp;quot;, &amp;quot;above&amp;quot;, &amp;quot;below&amp;quot;, &amp;quot;mach&amp;quot;, &amp;quot;computed&amp;quot;, &amp;quot;computed-mach&amp;quot;, &amp;quot;delete&amp;quot;.&lt;br /&gt;
; speed_cstr : Read-only. Speed restriction.&lt;br /&gt;
; speed_cstr_type : Read-only. Speed restriction. Can be &amp;quot;at&amp;quot;, &amp;quot;above&amp;quot;, &amp;quot;below&amp;quot;, &amp;quot;mach&amp;quot;, &amp;quot;computed&amp;quot;, &amp;quot;computed-mach&amp;quot;, &amp;quot;delete&amp;quot;.&lt;br /&gt;
; leg_distance :&lt;br /&gt;
; leg_bearing :&lt;br /&gt;
; distance_along_route :&lt;br /&gt;
&lt;br /&gt;
== Procedure methods and variables ==&lt;br /&gt;
; route(runway) : Returns a vector of waypoints.&lt;br /&gt;
; transition() : Only works on STAR and SID.&lt;br /&gt;
&lt;br /&gt;
== Examples ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
# get the active flight plan (the one being used by the route manager)&lt;br /&gt;
var fp = flightplan();&lt;br /&gt;
&lt;br /&gt;
# or create one an XML file&lt;br /&gt;
fp = flightplan('/path/to/xml');&lt;br /&gt;
&lt;br /&gt;
# save the active flight plan&lt;br /&gt;
fgcommand(&amp;quot;save-flightplan&amp;quot;, props.Node.new({&amp;quot;path&amp;quot;: 'path/to/xml'}));&lt;br /&gt;
&lt;br /&gt;
# duplicate a flight-plan&lt;br /&gt;
var secondary = fp.clone();&lt;br /&gt;
&lt;br /&gt;
var dest = airportinfo('KSFO');&lt;br /&gt;
var rwy = dest.runway('19L');&lt;br /&gt;
# the the arrival runway (and airport, automatically)&lt;br /&gt;
fp.destination_runway = rwy;&lt;br /&gt;
&lt;br /&gt;
# or if no runway is known/specified&lt;br /&gt;
fp.destination = dest;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Building procedures and transitions. As mentioned above there's a couple of different ways to handle this, the examples below assume the C++ code automatically deletes and re-inserts waypoints for procedures, but that's only one possible design.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
var apt = airportinfo('KSFO');&lt;br /&gt;
# example for SIDs, STARs are the same, approaches too&lt;br /&gt;
var allSids = apt.sids();&lt;br /&gt;
&lt;br /&gt;
# SIDs for a specific runway - note these return SID IDs as string, for compatibility with existing code&lt;br /&gt;
var rwySids = apt.sids('28L');&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Inserting and deleting waypoints (possibly in batches)&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
var fp = flightplan();&lt;br /&gt;
# waypoint created from lat, lon, or a navaid, or anything else?&lt;br /&gt;
var pos = geo.aircraft_position().apply_course_distance(getprop(&amp;quot;/orientation/heading-deg&amp;quot;), 100000);&lt;br /&gt;
var wp = createWP(pos.lat(), pos.lon(), &amp;quot;EPICA&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
# manually insert a waypoint at the end of the plan&lt;br /&gt;
fp.insertWP(wp, fp.getPlanSize());&lt;br /&gt;
&lt;br /&gt;
# manually insert a waypoint at a defined position (n) into the plan&lt;br /&gt;
fp.insertWP(wp, n);&lt;br /&gt;
&lt;br /&gt;
# route along airways, and insert a whole bunch of waypoints&lt;br /&gt;
# this is needed for the route-manager dialog, maybe not for a&lt;br /&gt;
# real FMS interface....&lt;br /&gt;
var segment = [&amp;lt;waypoint object&amp;gt;, &amp;lt;waypoint object&amp;gt;, &amp;lt;waypoint object&amp;gt;];&lt;br /&gt;
# segment is a vector of waypoints now&lt;br /&gt;
fp.insertWaypoints(segment, fp.getPlanSize());&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== aircraft.history() (3.2+) ==&lt;br /&gt;
&lt;br /&gt;
Function to expose flight history as aircraft.history()&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
var hist = aircraft.history();&lt;br /&gt;
&lt;br /&gt;
# get history of aircraft position/orientation collapsing&lt;br /&gt;
# nodes with a distance smaller than the given minimum&lt;br /&gt;
# edge length&lt;br /&gt;
debug.dump( hist.pathForHistory(&amp;lt;minimum-edge-length-meter&amp;gt;) );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Related content ==&lt;br /&gt;
=== Wiki articles ===&lt;br /&gt;
* [[Nasal library#Extension functions|Nasal library § Extension functions]]&lt;br /&gt;
* [[Navdata cache#Accessing via Nasal|Navdata cache § Accessing via Nasal]]&lt;br /&gt;
* [[Route Manager]]&lt;br /&gt;
* [[Route Manager internals]]&lt;br /&gt;
&lt;br /&gt;
=== Documentation ===&lt;br /&gt;
* [http://api-docs.freeflightsim.org/flightgear/NasalPositioned_8cxx_source.html#l00585 flightplanGhostSetMember] — ''Members of a flight plan object that can be set.''&lt;br /&gt;
* [http://api-docs.freeflightsim.org/flightgear/NasalPositioned_8cxx_source.html#l00557 flightplanGhostGetMember] — ''Members of flight plan object that can be read.''&lt;br /&gt;
* [http://api-docs.freeflightsim.org/flightgear/NasalPositioned_8cxx_source.html#l02485 initNasalPositioned] — ''Contains a list of functions that can be called on airport, flight plan, waypoint, procedure, or flight plan leg objects.''&lt;br /&gt;
* [http://api-docs.freeflightsim.org/flightgear/route__mgr_8cxx_source.html#l00220 FGRouteMgr::FGRouteMgr] — ''List of [[fgcommands]] relating to the route manager.''&lt;br /&gt;
&lt;br /&gt;
=== Misc ===&lt;br /&gt;
* https://sourceforge.net/p/flightgear/mailman/message/34688624/&lt;br /&gt;
* https://sourceforge.net/p/flightgear/mailman/message/35164289/&lt;br /&gt;
* https://sourceforge.net/p/flightgear/mailman/flightgear-devel/thread/BB67C9F3-028B-40E5-94E4-246CD1E0B2CC%40mac.com/#msg23708827&lt;br /&gt;
* https://sourceforge.net/p/flightgear/mailman/message/33699574/&lt;br /&gt;
* https://sourceforge.net/p/flightgear/mailman/message/21010010/&lt;br /&gt;
* https://sourceforge.net/p/flightgear/mailman/message/23678747/&lt;br /&gt;
&lt;br /&gt;
=== Search Queries ===&lt;br /&gt;
&lt;br /&gt;
=== Commits ===&lt;br /&gt;
&lt;br /&gt;
=== Threads ===&lt;br /&gt;
{{Appendix}}&lt;br /&gt;
&lt;br /&gt;
[[Category:Nasal]]&lt;/div&gt;</summary>
		<author><name>Clm76</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Nasal_Flightplan&amp;diff=106974</id>
		<title>Nasal Flightplan</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Nasal_Flightplan&amp;diff=106974"/>
		<updated>2017-02-18T15:41:44Z</updated>

		<summary type="html">&lt;p&gt;Clm76: There was an error in fp.insertWP. Added how to insert a waypoint at a defined place into the plan.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Systems Modeling Disclaimer}}&lt;br /&gt;
{{Autoflight Navigation}}&lt;br /&gt;
{{Nasal Navigation}}&lt;br /&gt;
&lt;br /&gt;
Because FlightGear's [[route manager]] flight plan system is exposed to [[Nasal]], '''Nasal can be used to interact with flight plans'''. Obviously, this is very useful, especially for aircraft like airliners, which have complex route managers systems. This article shows how this can be done.&lt;br /&gt;
&lt;br /&gt;
== Background ==&lt;br /&gt;
The Nasal functions relating to the route manager system include:&lt;br /&gt;
* {{func link|flightplan()}}&lt;br /&gt;
* {{func link|airwaysRoute()}}&lt;br /&gt;
&lt;br /&gt;
Flight plans are based on waypoints, which, in C++, inherit from the {{API Link|flightgear|class|FGPositioned}} class.&lt;br /&gt;
&lt;br /&gt;
== Waypoint hashes ==&lt;br /&gt;
The following are members of a waypoint ghost, as generated by, for example, {{func link|airwaysRoute()}}:&lt;br /&gt;
&lt;br /&gt;
; wp_name : Name of the waypoint, returned as string.&lt;br /&gt;
; wp_type : Waypoint type, returned as string. One of &amp;quot;basic,&amp;quot; &amp;quot;navaid,&amp;quot; &amp;quot;offset-navaid,&amp;quot; &amp;quot;runway,&amp;quot; &amp;quot;hdgToAlt,&amp;quot; &amp;quot;dmeIntercept,&amp;quot; &amp;quot;radialIntercept,&amp;quot; or &amp;quot;vectors.&amp;quot;&lt;br /&gt;
; wp_role: Role of waypoint.&lt;br /&gt;
; wp_lat ''or'' lat : Latitude of waypoint.&lt;br /&gt;
; wp_lon ''or'' lon : Longitude of waypoint.&lt;br /&gt;
; wp_parent_name : Name of waypoint's parent.&lt;br /&gt;
; wp_parent : Waypoint's parent.&lt;br /&gt;
; fly_type : How to waypoint should be flown over or reacted to. One of &amp;quot;Hold,&amp;quot; &amp;quot;flyOver,&amp;quot; or &amp;quot;flyBy.&amp;quot;&lt;br /&gt;
; heading_course : Heading of runway.&lt;br /&gt;
&lt;br /&gt;
== Examples ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
# get the active flight plan (the one being used by the route manager)&lt;br /&gt;
var fp = flightplan();&lt;br /&gt;
&lt;br /&gt;
# or create one an XML file&lt;br /&gt;
fp = flightplan('/path/to/xml');&lt;br /&gt;
&lt;br /&gt;
# save the active flight plan&lt;br /&gt;
fgcommand(&amp;quot;save-flightplan&amp;quot;, props.Node.new({&amp;quot;path&amp;quot;: 'path/to/xml'}));&lt;br /&gt;
&lt;br /&gt;
# duplicate a flight-plan&lt;br /&gt;
var secondary = fp.clone();&lt;br /&gt;
&lt;br /&gt;
var dest = airportinfo('KSFO');&lt;br /&gt;
var rwy = dest.runway('19L');&lt;br /&gt;
# the the arrival runway (and airport, automatically)&lt;br /&gt;
fp.destination_runway(rwy)&lt;br /&gt;
&lt;br /&gt;
# or if no runway is known/specified&lt;br /&gt;
fp.destination(dest)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Building procedures and transitions. As mentioned above there's a couple of different ways to handle this, the examples below assume the C++ code automatically deletes and re-inserts waypoints for procedures, but that's only one possible design.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
var apt = airportinfo('KSFO');&lt;br /&gt;
# example for SIDs, STARs are the same, approaches too&lt;br /&gt;
var allSids = apt.sids();&lt;br /&gt;
&lt;br /&gt;
# SIDs for a specific runway - note these return SID IDs as string, for compatibility with existing code&lt;br /&gt;
var rwySids = apt.sids('28L');&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Inserting and deleting waypoints (possibly in batches)&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
var fp = flightplan();&lt;br /&gt;
# waypoint created from lat, lon, or a navaid, or anything else?&lt;br /&gt;
var pos = geo.aircraft_position().apply_course_distance(getprop(&amp;quot;/orientation/heading-deg&amp;quot;), 100000);&lt;br /&gt;
var wp = createWP(pos.lat(), pos.lon(), &amp;quot;EPICA&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
# manually insert a waypoint at the end of the plan&lt;br /&gt;
fp.insertWP(wp, fp.getPlanSize());&lt;br /&gt;
&lt;br /&gt;
# manually insert a waypoint at a defined position (n) into the plan&lt;br /&gt;
fp.insertWP(wp, n);&lt;br /&gt;
&lt;br /&gt;
# route along airways, and insert a whole bunch of waypoints&lt;br /&gt;
# this is needed for the route-manager dialog, maybe not for a&lt;br /&gt;
# real FMS interface....&lt;br /&gt;
var segment = [&amp;lt;waypoint object&amp;gt;, &amp;lt;waypoint object&amp;gt;, &amp;lt;waypoint object&amp;gt;];&lt;br /&gt;
# segment is a vector of waypoints now&lt;br /&gt;
fp.insertWaypoints(segment, fp.getPlanSize());&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== aircraft.history() (3.2+) ==&lt;br /&gt;
&lt;br /&gt;
Function to expose flight history as aircraft.history()&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
var hist = aircraft.history();&lt;br /&gt;
&lt;br /&gt;
# get history of aircraft position/orientation collapsing&lt;br /&gt;
# nodes with a distance smaller than the given minimum&lt;br /&gt;
# edge length&lt;br /&gt;
debug.dump( hist.pathForHistory(&amp;lt;minimum-edge-length-meter&amp;gt;) );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Related content ==&lt;br /&gt;
=== Wiki articles ===&lt;br /&gt;
* [[Nasal library#Extension functions|Nasal library § Extension functions]]&lt;br /&gt;
* [[Navdata cache#Accessing via Nasal|Navdata cache § Accessing via Nasal]]&lt;br /&gt;
&lt;br /&gt;
=== Documentation ===&lt;br /&gt;
* [http://api-docs.freeflightsim.org/flightgear/NasalPositioned_8cxx_source.html#l00585 flightplanGhostSetMember] — ''Members of a flight plan object that can be set.''&lt;br /&gt;
* [http://api-docs.freeflightsim.org/flightgear/NasalPositioned_8cxx_source.html#l00557 flightplanGhostGetMember] — ''Members of flight plan object that can be read.''&lt;br /&gt;
* [http://api-docs.freeflightsim.org/flightgear/NasalPositioned_8cxx_source.html#l02485 initNasalPositioned] — ''Contains a list of functions that can be called on airport, flight plan, waypoint, procedure, or flight plan leg objects.''&lt;br /&gt;
* [http://api-docs.freeflightsim.org/flightgear/route__mgr_8cxx_source.html#l00220 FGRouteMgr::FGRouteMgr] — ''List of [[fgcommands]] relating to the route manager.''&lt;br /&gt;
&lt;br /&gt;
[[Category:Nasal]]&lt;/div&gt;</summary>
		<author><name>Clm76</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Canvas_snippets&amp;diff=105438</id>
		<title>Canvas snippets</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Canvas_snippets&amp;diff=105438"/>
		<updated>2016-10-29T07:41:00Z</updated>

		<summary type="html">&lt;p&gt;Clm76: /* Customizing MapStructure Styling */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Stub}}&lt;br /&gt;
{{-}}&lt;br /&gt;
This article is meant to be a collection of self-contained code snippets for doing Nasal/Canvas coding without having to spend too much time working through APIs, documentation and existing code - the idea is to provide snippets that contain comments and explicit assumptions, so that these snippets can be easily adapted by people without necessarily having to be very familiar with Nasal/Canvas coding. In the long term, we're hoping to grow a library of useful code snippets to cover most use-cases, while also covering more complex scenarios, e.g. using Canvas-specific helper frameworks like [[Canvas MapStructure]], the [[NavDisplay]] or the recent CDU work.&lt;br /&gt;
&lt;br /&gt;
Canvas itself is designed to be front-end agnostic, meaning that it doesn't matter where a Canvas is used/displayed - this is accomplished by so called &amp;quot;placements&amp;quot;, which determine where a Canvas texture shown. Thus, the back-end logic can remain the same usually, so that GUI dialogs may show avionics, but also so that avionics may show GUI widgets, which also applies to HUDs, liveries and even placements within the FlightGear scenery.&lt;br /&gt;
&lt;br /&gt;
We encourage people to use the snippets listed below to get started with Nasal/Canvas coding, but also to provide feedback on extending/improving this article to make it even more accessible and useful. Equally, if you're aware of any Canvas-related efforts that may contain useful code snippets, please do feel free to add those snippets here. Even people who don't have any interest in coding itself are encouraged to get involved by helping test the snippets listed below, for which you only need to the [[Nasal Console]], and while you're at it, please also help provide/update screen shots for each code snippet.&lt;br /&gt;
&lt;br /&gt;
Contributions added to this article should ideally satisfy some requirements:&lt;br /&gt;
* be entirely self-contained&lt;br /&gt;
* contain good/clear comments&lt;br /&gt;
* have pointers/references to related code/use-cases&lt;br /&gt;
* make assumptions explicit&lt;br /&gt;
* contain screen shots for each example&lt;br /&gt;
* avoid overloaded symbol names to ensure that examples can be merged and adapted easily&lt;br /&gt;
* dependencies (svg files, images/textures etc) should always be chosen such that snippets always work using $FG_ROOT&lt;br /&gt;
* hard-coded assumptions (e.g. texture dimensions in terms of width/height etc) should be encapsulated using variables&lt;br /&gt;
 &lt;br /&gt;
{{Canvas Navigation}}&lt;br /&gt;
&lt;br /&gt;
== Creating a standalone Canvas ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[Special:UploadWizard|Upload requested]] || &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
var (width, height) = (512,512);&lt;br /&gt;
# Create a standalone Canvas (not attached to any GUI dialog/aircraft etc) &lt;br /&gt;
var myCanvas = canvas.new({&lt;br /&gt;
  &amp;quot;name&amp;quot;: &amp;quot;Livery Test&amp;quot;,   # The name is optional but allow for easier identification&lt;br /&gt;
  &amp;quot;size&amp;quot;: [width, height], # Size of the underlying texture (should be a power of 2, required) [Resolution]&lt;br /&gt;
  &amp;quot;view&amp;quot;: [width, height],  # Virtual resolution (Defines the coordinate system of the canvas [Dimensions]&lt;br /&gt;
                        # which will be stretched the size of the texture, required)&lt;br /&gt;
  &amp;quot;mipmapping&amp;quot;: 1       # Enable mipmapping (optional)&lt;br /&gt;
});&lt;br /&gt;
&lt;br /&gt;
# set background color&lt;br /&gt;
myCanvas.set(&amp;quot;background&amp;quot;, canvas.style.getColor(&amp;quot;bg_color&amp;quot;));&lt;br /&gt;
&lt;br /&gt;
# creating the top-level/root group which will contain all other elements/group&lt;br /&gt;
var root = myCanvas.createGroup();&lt;br /&gt;
&lt;br /&gt;
# OPTIONAL: Create a Canvas dialog window to hold the canvas and show that it's working&lt;br /&gt;
# the Canvas is now standalone, i.e. continues to live once the dialog is closed!&lt;br /&gt;
var window = canvas.Window.new([width,height],&amp;quot;dialog&amp;quot;);&lt;br /&gt;
window.setCanvas(myCanvas);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Accessing the Canvas Desktop ==&lt;br /&gt;
Sometimes you may want to render to the main screen, without creating a separate Canvas window - this can be accomplished by using the Canvas desktop, note that the following example is fully self-contained, i.e. does not require any code to be added to work:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[Special:UploadWizard|Upload requested]] || &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
var myNode = canvas.getDesktop().createChild(&amp;quot;text&amp;quot;)&lt;br /&gt;
      .setText(&amp;quot;Hello Canvas Desktop&amp;quot;)&lt;br /&gt;
      .setFontSize(25, 1.0)          # font size (in texels) and font aspect ratio&lt;br /&gt;
      .setColor(1,0,0,1)             # red, fully opaque&lt;br /&gt;
      .setAlignment(&amp;quot;center-center&amp;quot;) # how the text is aligned to where you place it&lt;br /&gt;
      .setTranslation(160, 80);     # where to place the text&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Creating Tooltips ==&lt;br /&gt;
== Creating Popups ==&lt;br /&gt;
== Creating a Canvas GUI Window ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Snippets-canvas-dialog.png|left|thumb|This is what the [[Nasal]]/[[Canvas]] snippet will look like once you pasted it into the [[Nasal Console]] and click &amp;quot;Execute&amp;quot;.]] || {{Note|This example uses so called method chaining, if you're not familiar with the concept, please see: [[Object_Oriented_Programming_with_Nasal#More_on_methods:_Chaining|Method Chaining]].}}&lt;br /&gt;
&lt;br /&gt;
{{Canvas Snippets Boilerplate}}&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Adding Raster Images ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code &lt;br /&gt;
|-&lt;br /&gt;
| [[File:Canvas Snippets-raster image.png|thumb|Canvas snippet demonstrating how to load a raster image]] ||&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# path is relative to $FG_ROOT (base package)&lt;br /&gt;
var path = &amp;quot;Textures/Splash1.png&amp;quot;;&lt;br /&gt;
# create an image child for the texture&lt;br /&gt;
var child = root.createChild(&amp;quot;image&amp;quot;)&lt;br /&gt;
    .setFile(path)&lt;br /&gt;
    .setTranslation(100, 10)&lt;br /&gt;
    .setSize(130, 130);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Adding Raster Images downloaded on demand ==&lt;br /&gt;
&lt;br /&gt;
The '''path''' could also just as well be a URL, i.e. a raster image retrieved via http - for example, the following snippet is entirely self-contained and can be pasted into the [[Nasal Console]] and directly executed &amp;quot;as is&amp;quot;: &lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Canvas-raster-images-via-url.png|thumb|screen shot demonstrating how Nasal and Canvas can be used to display raster images downloaded on demand]] ||&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# create a new window, dimensions are 400 x 200, using the dialog decoration (i.e. titlebar)&lt;br /&gt;
var window = canvas.Window.new([400,200],&amp;quot;dialog&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
# adding a canvas to the new window and setting up background colors/transparency&lt;br /&gt;
var myCanvas = window.createCanvas().set(&amp;quot;background&amp;quot;, canvas.style.getColor(&amp;quot;bg_color&amp;quot;));&lt;br /&gt;
&lt;br /&gt;
# Using specific css colors would also be possible:&lt;br /&gt;
# myCanvas.set(&amp;quot;background&amp;quot;, &amp;quot;#ffaac0&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
# creating the top-level/root group which will contain all other elements/group&lt;br /&gt;
var root = myCanvas.createGroup();&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
# path now a URL&lt;br /&gt;
var url = &amp;quot;http://www.worldwidetelescope.org/docs/Images/MapOfEarth.jpg&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
# create an image child for the texture&lt;br /&gt;
var child=root.createChild(&amp;quot;image&amp;quot;)&lt;br /&gt;
    .setFile( url ) &lt;br /&gt;
    .setTranslation(45,22) # centered, in relation to dialog coordinates&lt;br /&gt;
    .setSize(310,155); # image dimensions&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Clipping ==&lt;br /&gt;
{{Note|{{FGCquote&lt;br /&gt;
|1= Scaling or any other type of transformation or changing the coordinates of individual points is definitely more efficient than clipping (which requires to change the OpenGL clip planes for every rendered object with a different clipping rectangle).&lt;br /&gt;
|2= {{cite web&lt;br /&gt;
  | url    = http://forum.flightgear.org/viewtopic.php?p=277062#p277062&lt;br /&gt;
  | title  = &amp;lt;nowiki&amp;gt;Re: Space Shuttle&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  | author = &amp;lt;nowiki&amp;gt;TheTom&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  | date   = Feb 21st, 2016&lt;br /&gt;
  | added   = Feb 21st, 2016&lt;br /&gt;
  | script_version = 0.25&lt;br /&gt;
  }}&lt;br /&gt;
}}&lt;br /&gt;
}}&lt;br /&gt;
I would suggest to refer to api.nas and look for &amp;quot;clip&amp;quot; and/or &amp;quot;rect&amp;quot; - IIRC, you need to set up a clipping rectangle by setting some kind of &amp;quot;clip&amp;quot; property and setting it to a rect value in the form of rect(...)&lt;br /&gt;
{{FGCquote&lt;br /&gt;
|1= For details, see the clipping example at: [[Canvas Nasal API#set 2]] and  [[Canvas Element#clip.28.29]]&lt;br /&gt;
|2= {{cite web&lt;br /&gt;
  | url    = http://forum.flightgear.org/viewtopic.php?p=276965#p276965&lt;br /&gt;
  | title  = &amp;lt;nowiki&amp;gt;Re: Space Shuttle&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  | author = &amp;lt;nowiki&amp;gt;Hooray&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  | date   = Feb 21st, 2016&lt;br /&gt;
  | added   = Feb 21st, 2016&lt;br /&gt;
  | script_version = 0.25&lt;br /&gt;
  }}&lt;br /&gt;
}}&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[File:MapStructure_dialog_with_clipping_and_event_handling_applied.png|thumb]] ||&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# create a new window, dimensions are 400 x 200, using the dialog decoration (i.e. titlebar)&lt;br /&gt;
var window = canvas.Window.new([400,200],&amp;quot;dialog&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
# adding a canvas to the new window and setting up background colors/transparency&lt;br /&gt;
var myCanvas = window.createCanvas().set(&amp;quot;background&amp;quot;, canvas.style.getColor(&amp;quot;bg_color&amp;quot;));&lt;br /&gt;
&lt;br /&gt;
# Using specific css colors would also be possible:&lt;br /&gt;
# myCanvas.set(&amp;quot;background&amp;quot;, &amp;quot;#ffaac0&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
# creating the top-level/root group which will contain all other elements/group&lt;br /&gt;
var root = myCanvas.createGroup();&lt;br /&gt;
&lt;br /&gt;
# create an image child for the texture&lt;br /&gt;
var child=root.createChild(&amp;quot;.........&amp;quot;)&lt;br /&gt;
    .setFile( url ) &lt;br /&gt;
    .setTranslation(45,22) # centered, in relation to dialog coordinates&lt;br /&gt;
    .setSize(310,155); # image dimensions&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Adding OpenVG Paths ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Openvg-via-canvas.png|thumb|screen shot showing a simple Canvas GUI dialog demonstrating how to use OpenVG-path drawing via Nasal and Canvas]] &lt;br /&gt;
| {{Note|This assumes that you already have a top-level root group set up, and named it '''root'''.}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
var graph = root.createChild(&amp;quot;group&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
var x_axis = graph.createChild(&amp;quot;path&amp;quot;, &amp;quot;x-axis&amp;quot;)&lt;br /&gt;
.moveTo(10, height/2)&lt;br /&gt;
.lineTo(width-10, height/2)&lt;br /&gt;
.setColor(1,0,0)&lt;br /&gt;
.setStrokeLineWidth(3);&lt;br /&gt;
&lt;br /&gt;
var y_axis = graph.createChild(&amp;quot;path&amp;quot;, &amp;quot;y-axis&amp;quot;)&lt;br /&gt;
.moveTo(10, 10)&lt;br /&gt;
.lineTo(10, height-10)&lt;br /&gt;
.setColor(0,0,1)&lt;br /&gt;
.setStrokeLineWidth(2);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Canvas-openvg-quadTo.png|thumb|screen shot showing a simple Canvas GUI dialog demonstrating how to use OpenVG-path drawing via Nasal and Canvas (Canvas/OpenVG quadTo API for drawing curves)]] &lt;br /&gt;
| {{Note|This assumes that you are appending this to the snippet shown above.}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
var points = [	60,height-20, &lt;br /&gt;
		230,height-100,&lt;br /&gt;
		];&lt;br /&gt;
&lt;br /&gt;
var track = graph.createChild(&amp;quot;path&amp;quot;, &amp;quot;track&amp;quot;)&lt;br /&gt;
.moveTo(10, height/2)&lt;br /&gt;
.quadTo( points )&lt;br /&gt;
.setColor(0,1,0)&lt;br /&gt;
.setStrokeLineWidth(4);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Canvas-openvg-cubicTo.png|thumb|screen shot showing a simple Canvas GUI dialog demonstrating how to use OpenVG-path drawing via Nasal and Canvas (Canvas/OpenVG cubicTo API for drawing curves)]] &lt;br /&gt;
| {{Note|This assumes that you are appending this to the snippet shown above.}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
var points = [	60,height-20, &lt;br /&gt;
		120,height-120,&lt;br /&gt;
		230,height-100,&lt;br /&gt;
		];&lt;br /&gt;
&lt;br /&gt;
var track = graph.createChild(&amp;quot;path&amp;quot;, &amp;quot;track&amp;quot;)&lt;br /&gt;
.moveTo(10, height/2)&lt;br /&gt;
.cubicTo( points )&lt;br /&gt;
.setColor(0,1,0)&lt;br /&gt;
.setStrokeLineWidth(4);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Adding Vector Images ==&lt;br /&gt;
&lt;br /&gt;
[[Category:Canvas SVG]]&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Canvas-svg-support.png|thumb|screen shot demonstrating how the scripted Nasal-based SVG parser can be used to dynamically turn SVG files into OpenVG instructions understood by Canvas]] &lt;br /&gt;
| {{Note|This assumes that you already have a top-level root group set up, and named it '''root'''.}}&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
# change the background color &lt;br /&gt;
myCanvas.set(&amp;quot;background&amp;quot;, &amp;quot;#ffaac0&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
# creating the top-level/root group which will contain all other elements/group&lt;br /&gt;
var root = myCanvas.createGroup();&lt;br /&gt;
&lt;br /&gt;
var filename = &amp;quot;/Nasal/canvas/map/Images/boeingAirplane.svg&amp;quot;;&lt;br /&gt;
var svg_symbol = root.createChild('group');&lt;br /&gt;
canvas.parsesvg(svg_symbol, filename);&lt;br /&gt;
&lt;br /&gt;
svg_symbol.setTranslation(width/2,height/2);&lt;br /&gt;
&lt;br /&gt;
#svg_symbol.setScale(0.2);&lt;br /&gt;
#svg_symbol.setRotation(radians)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Using the SymbolCache ==&lt;br /&gt;
Whenever a symbol may need to be shown/instanced multiple times (possibly using different styling), it makes sense to use caching - otherwise, identical symbols would be treated as separate OpenVG groups, all of which would need to be rasterized/rendedered separately (i.e. 100 identical symbols would be updated/rendered one by one). &lt;br /&gt;
&lt;br /&gt;
Typically, a map may display multiple instances of an otherwise identical symbol (think VOR, NDB, DME etc) - equally, a multiplayer map may showing multiple aircraft symbols at the same time. In these cases, it makes sense to use the SymbolCache framework, which will render symbols into a separate Canvas texture and provide a texture map that can be treated as a lookup map, which even supports styling for otherwise identical symbols. To learn more, please refer to [[Canvas MapStructure#The_SymbolCache|SymbolCache]]&lt;br /&gt;
... &lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Canvas-SymbolCache-Instancing.png|thumb|Screen shot showing a Canvas based GUI dialog that is using the SymbolCache for instancing multiple symbols (including support for styling)]] &lt;br /&gt;
| &amp;lt;!--{{Caution|This is currently untested code}}--&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
##&lt;br /&gt;
# this is the function that draws a symbol using OpenVG paths&lt;br /&gt;
# it accepts a group to draw to and returns the rendered group &lt;br /&gt;
# to the caller&lt;br /&gt;
var drawVOR = func(group) {&lt;br /&gt;
    return group.createChild(&amp;quot;path&amp;quot;)&lt;br /&gt;
        .moveTo(-15,0)&lt;br /&gt;
        .lineTo(-7.5,12.5)&lt;br /&gt;
        .lineTo(7.5,12.5)&lt;br /&gt;
        .lineTo(15,0)&lt;br /&gt;
        .lineTo(7.5,-12.5)&lt;br /&gt;
        .lineTo(-7.5,-12.5)&lt;br /&gt;
        .close()&lt;br /&gt;
        .setStrokeLineWidth(line_width) # style-able&lt;br /&gt;
        .setColor(color); # style-able&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
var style = { # styling related attributes (as per the draw* function above)&lt;br /&gt;
    line_width: 3,&lt;br /&gt;
    scale_factor: 1,&lt;br /&gt;
    color: [1,0,0],&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
# create a new cache entry for the styled symbol&lt;br /&gt;
var myCachedSymbol = canvas.StyleableCacheable.new(&lt;br /&gt;
    name:'myVOR', draw_func: drawVOR,&lt;br /&gt;
    cache: canvas.SymbolCache32x32, # the cache to be used&lt;br /&gt;
    draw_mode: canvas.SymbolCache.DRAW_CENTERED,&lt;br /&gt;
    relevant_keys: ['line_width', 'color'], # styling related attributes&lt;br /&gt;
);&lt;br /&gt;
&lt;br /&gt;
var target = root.createChild('group');&lt;br /&gt;
&lt;br /&gt;
var x=0;&lt;br /&gt;
var y=height/2;&lt;br /&gt;
&lt;br /&gt;
var xoffset=50;&lt;br /&gt;
&lt;br /&gt;
# render 5 instanced symbols using the style specified above&lt;br /&gt;
for (var i=0;i&amp;lt;5;i+=1) {&lt;br /&gt;
# look up the raster image for the symbol&lt;br /&gt;
# render it using the passed style and adjust scaling&lt;br /&gt;
var instanced = myCachedSymbol.render(target, style)&lt;br /&gt;
  .setScale(style.scale_factor)&lt;br /&gt;
  .setTranslation(x+=xoffset,y);&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The example shown above uses a fixed symbol/icon cache that is set up while booting Flightgear - sometimes, we may need cache for different purposes. So, let's assume, we need a new/custom cache with a different resolution for each entry in the cache (e.g. 256x256), we can easily accomplish that by setting up a new cache like this: &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
var mySymbolCache256x256 = canvas.SymbolCache.new(1024,256);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Adding Text Elements ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[File:CanvasText-Hello-World.png|thumb|Screen shot showing the CanvasText example contributed by Necolatis]]&lt;br /&gt;
| {{Note|This assumes that you already have a top-level root group set up, and named it '''root'''.}}&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
var myText = root.createChild(&amp;quot;text&amp;quot;)&lt;br /&gt;
      .setText(&amp;quot;Hello world!&amp;quot;)&lt;br /&gt;
      .setFontSize(20, 0.9)          # font size (in texels) and font aspect ratio&lt;br /&gt;
      .setColor(1,0,0,1)             # red, fully opaque&lt;br /&gt;
      .setAlignment(&amp;quot;center-center&amp;quot;) # how the text is aligned to where you place it&lt;br /&gt;
      .setTranslation(160, 80);     # where to place the text&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Adding GUI Labels ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Canvas-Layout-Label-example-by-Necolatis.png|thumb|Canvas demo: Layouts and Labels (by Necolatis)]]&lt;br /&gt;
| {{Note|This assumes that you already have a top-level root group set up, and named it '''root''', just change this variable if you want it to be rendered elsewhere. It also assumes you have a Layout item setup and called '''myLayoutItem'''.}}&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
# create a new layout&lt;br /&gt;
var myLayout = canvas.HBoxLayout.new();&lt;br /&gt;
# assign it to the Canvas&lt;br /&gt;
myCanvas.setLayout(myLayout);&lt;br /&gt;
&lt;br /&gt;
var label = canvas.gui.widgets.Label.new(root, canvas.style, {wordWrap: 0}); # wordwrap: 0 will disable wordwrapping, to enable it use 1 instead&lt;br /&gt;
label.setText(&amp;quot;Hello World!&amp;quot;);&lt;br /&gt;
myLayout.addItem(label);&lt;br /&gt;
&lt;br /&gt;
var label2 = canvas.gui.widgets.Label.new(root, canvas.style, {wordWrap: 0}); # wordwrap: 0 will disable wordwrapping, to enable it use 1 instead&lt;br /&gt;
label2.setText(&amp;quot;Hello FlightGear&amp;quot;);&lt;br /&gt;
myLayout.addItem(label2);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Adding GUI Buttons (Layouts)==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Canvas-demo-layouts-and-buttons-by-Necolatis.png|thumb|Canvas snippet: buttons and layouts (by Necolatis)]] &lt;br /&gt;
|&lt;br /&gt;
&lt;br /&gt;
{{Note|This assumes that you already have a top-level root group set up, and named it '''root''', just change this variable if you want it to be rendered elsewhere. It also assumes you have a Layout item setup and called '''myLayoutItem'''.}}&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
# create a new layout&lt;br /&gt;
var myLayout = canvas.HBoxLayout.new();&lt;br /&gt;
# assign it to the Canvas&lt;br /&gt;
myCanvas.setLayout(myLayout);&lt;br /&gt;
&lt;br /&gt;
# click button&lt;br /&gt;
&lt;br /&gt;
var button = canvas.gui.widgets.Button.new(root, canvas.style, {})&lt;br /&gt;
	.setText(&amp;quot;Click on me&amp;quot;)&lt;br /&gt;
	.setFixedSize(75, 25);&lt;br /&gt;
&lt;br /&gt;
button.listen(&amp;quot;clicked&amp;quot;, func {&lt;br /&gt;
        # add code here to react on click on button.&lt;br /&gt;
print(&amp;quot;Button clicked !&amp;quot;);&lt;br /&gt;
});&lt;br /&gt;
&lt;br /&gt;
myLayout.addItem(button);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Canvas-toggle-button-snippet-by-Necolatis.png|thumb|Canvas toggle button demo by Necolatis]]&lt;br /&gt;
| &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
# create a new layout&lt;br /&gt;
var myLayout = canvas.HBoxLayout.new();&lt;br /&gt;
# assign it to the Canvas&lt;br /&gt;
myCanvas.setLayout(myLayout);&lt;br /&gt;
&lt;br /&gt;
var button = canvas.gui.widgets.Button.new(root, canvas.style, {})&lt;br /&gt;
        .setText(&amp;quot;Toggle me&amp;quot;)&lt;br /&gt;
        .setCheckable(1) # this indicates that is should be a toggle button&lt;br /&gt;
        .setChecked(0) # depressed by default&lt;br /&gt;
        .setFixedSize(75, 25);&lt;br /&gt;
&lt;br /&gt;
button.listen(&amp;quot;toggled&amp;quot;, func (e) {&lt;br /&gt;
        if( e.detail.checked ) {&lt;br /&gt;
            # add code here to react on button being depressed.&lt;br /&gt;
        } else {&lt;br /&gt;
            # add code here to react on button not being depressed.&lt;br /&gt;
        }&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
myLayout.addItem(button);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Canvas Input Dialog ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Snippets-canvas-input-dialog.png|thumb|Canvas input dialog]]&lt;br /&gt;
||&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
# create a new InputDialog with a title, label, and a callback&lt;br /&gt;
canvas.InputDialog.getText(&amp;quot;Input Dialog Title&amp;quot;, &amp;quot;Please enter some text&amp;quot;, func(btn,value) {&lt;br /&gt;
    if (value) gui.popupTip(&amp;quot;You entered: &amp;quot;~value);&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Canvas ScrollArea ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Canvas-snippets-scrollArea-demo.png|thumb|Screen shot showing a Canvas ScrollArea populated with different splash screens, loaded from $FG_ROOT/Textures]]&lt;br /&gt;
||&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
var vbox = canvas.VBoxLayout.new();&lt;br /&gt;
myCanvas.setLayout(vbox);&lt;br /&gt;
&lt;br /&gt;
 var scroll = canvas.gui.widgets.ScrollArea.new(root, canvas.style, {size: [96, 128]}).move(20, 100);&lt;br /&gt;
 vbox.addItem(scroll, 1);&lt;br /&gt;
&lt;br /&gt;
var scrollContent =&lt;br /&gt;
      scroll.getContent()&lt;br /&gt;
            .set(&amp;quot;font&amp;quot;, &amp;quot;LiberationFonts/LiberationSans-Bold.ttf&amp;quot;)&lt;br /&gt;
            .set(&amp;quot;character-size&amp;quot;, 16)&lt;br /&gt;
            .set(&amp;quot;alignment&amp;quot;, &amp;quot;left-center&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
var list = canvas.VBoxLayout.new();&lt;br /&gt;
scroll.setLayout(list);&lt;br /&gt;
&lt;br /&gt;
for (var i=1;i&amp;lt;=5;i+=1) {&lt;br /&gt;
var label = canvas.gui.widgets.Label.new(scrollContent, canvas.style, {wordWrap: 0}); &lt;br /&gt;
label.setImage(&amp;quot;Textures/Splash&amp;quot;~i~&amp;quot;.png&amp;quot;);&lt;br /&gt;
label.setFixedSize(256,256);&lt;br /&gt;
list.addItem(label);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Using Layouts ==&lt;br /&gt;
&lt;br /&gt;
== Using Styling ==&lt;br /&gt;
== Adding a HUD ==&lt;br /&gt;
== Adding a 2D Instrument ==&lt;br /&gt;
== Adding a 2D Panel ==&lt;br /&gt;
== Adding a PFD ==&lt;br /&gt;
&lt;br /&gt;
== Adding a Failure Mgmt Widget ==&lt;br /&gt;
{{Note|This kind of widget will typically be useful for dialogs requiring a method for managing aircraft specific system failures (e.g. an instructor console).}}&lt;br /&gt;
&lt;br /&gt;
== Adding a MapStructure map to a Canvas  ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code &lt;br /&gt;
|-&lt;br /&gt;
| [[File:Snippets-canvas-mapstructure-dialog.png|thumb|[[MapStructure]] layers shown in a Canvas GUI dialog]]&lt;br /&gt;
| {{Note|This assumes that you already have a top-level root group set up, and named it '''root''', just change this variable if you want it to be rendered elsewhere. Equally, this snippet assumes that your canvas is named '''myCanvas'''.}}&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
var TestMap = root.createChild(&amp;quot;map&amp;quot;);&lt;br /&gt;
TestMap.setController(&amp;quot;Aircraft position&amp;quot;);&lt;br /&gt;
TestMap.setRange(25); &lt;br /&gt;
 &lt;br /&gt;
TestMap.setTranslation(    myCanvas.get(&amp;quot;view[0]&amp;quot;)/2,&lt;br /&gt;
                           myCanvas.get(&amp;quot;view[1]&amp;quot;)/2&lt;br /&gt;
                        );&lt;br /&gt;
var r = func(name,vis=1,zindex=nil) return caller(0)[0];&lt;br /&gt;
&lt;br /&gt;
foreach(var type; [r('APT'), r('VOR') ] )&lt;br /&gt;
 TestMap.addLayer(factory: canvas.SymbolLayer, type_arg: type.name, visible: type.vis, priority: type.zindex,);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Customizing MapStructure Styling  ==&lt;br /&gt;
{{See also|Canvas MapStructure#Styling}}&lt;br /&gt;
{{WIP}}&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code &lt;br /&gt;
|-&lt;br /&gt;
| [[File:Layers style change.png|thumb|[[MapStructure]] layers shown in a Canvas GUI dialog. Active VOR and radial in red color, inactive VOR in green color]]&lt;br /&gt;
| {{Note|This assumes that you already have a top-level root group set up, and named it '''root''', just change this variable if you want it to be rendered elsewhere. Equally, this snippet assumes that your canvas is named '''myCanvas'''.}}&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
var TestMap = root.createChild(&amp;quot;map&amp;quot;);&lt;br /&gt;
TestMap.setController(&amp;quot;Aircraft position&amp;quot;);&lt;br /&gt;
TestMap.setRange(25); &lt;br /&gt;
 &lt;br /&gt;
TestMap.setTranslation(    myCanvas.get(&amp;quot;view[0]&amp;quot;)/2,&lt;br /&gt;
                           myCanvas.get(&amp;quot;view[1]&amp;quot;)/2&lt;br /&gt;
                        );&lt;br /&gt;
var r = func(name,vis=1,zindex=nil) return caller(0)[0];&lt;br /&gt;
var type = r('APT');&lt;br /&gt;
var style_apt = {&lt;br /&gt;
    scale_factor:0.5, # 50 %&lt;br /&gt;
    color_default:[0,1,0.9], # rgb&lt;br /&gt;
    line_width:4, # thickness&lt;br /&gt;
    label_font_color:[0,1,0.9], # rgb&lt;br /&gt;
    label_font_size:30 # font size&lt;br /&gt;
};&lt;br /&gt;
TestMap.addLayer(factory: canvas.SymbolLayer, type_arg: type.name, visible: type.vis, priority: type.zindex,style:style_apt);&lt;br /&gt;
&lt;br /&gt;
var type = r('VOR');&lt;br /&gt;
var style_vor = {&lt;br /&gt;
    scale_factor:0.6,&lt;br /&gt;
    active_color:[1,0,0],&lt;br /&gt;
    inactive_color:[0,1,0],&lt;br /&gt;
    line_width:4&lt;br /&gt;
};&lt;br /&gt;
TestMap.addLayer(factory: canvas.SymbolLayer, type_arg: type.name, visible: type.vis, priority: type.zindex,style:style_vor);&lt;br /&gt;
&lt;br /&gt;
### See ...fgdata/Nasal/canvas/map/APT.symbol   ...fgdata/Nasal/canvas/map/VOR.style ... for the style variables that can be configured.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Using MapStructure and Overlays ==&lt;br /&gt;
{{FGCquote&lt;br /&gt;
  |Support for overlays using geo-referenced raster images isn't too well-developed currently, so would need to work with a few hard-coded assumptions (e.g. being specific to a certain raster image and map layout) - but maybe TheTom can provide a few more ideas on how to proceed from here - otherwise, the Map is normally not aware of any non-Map items, i.e. as long as the overlay is added &amp;quot;outside&amp;quot; the Map, it isn't even aware of the raster image - and when it is added as a map element, it would probably be rotated. So there's still some work needed here. But generally, this should work well enough even in its current form (the screen shot is purely based on the Canvas Snippets article, i.e. it just displays a Canvas GUI dialog, adds the downloaded raster image and then adds the MapStructure APS layer - without the Map being aware of the overlay/geo-referencing that is needed currently).&lt;br /&gt;
  |{{cite web |url=http://forum.flightgear.org/viewtopic.php?p=238316#p238316&lt;br /&gt;
     |title=&amp;lt;nowiki&amp;gt;Re: Using Canvas for visualizing orbital flights (cont'd PM)&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     |author=&amp;lt;nowiki&amp;gt;Hooray&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     |date=&amp;lt;nowiki&amp;gt;Fri Apr 10&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
   }}&lt;br /&gt;
}}&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code &lt;br /&gt;
|-&lt;br /&gt;
| [[File:Canvas-overlay-with-mapstructure.png|thumb|Using the [[MapStructure]] framework in conjunction with raster images as overlays]]&lt;br /&gt;
| {{Note|This assumes that you already have a top-level root group set up, and named it '''root''', just change this variable if you want it to be rendered elsewhere. Equally, this snippet assumes that your canvas is named '''myCanvas'''.}}&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== A simple tile map ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code &lt;br /&gt;
|-&lt;br /&gt;
| [[File:Canvas - Tile map demo.png|thumb|A simple, canvas based tile map which is centered around the aircraft.]]&lt;br /&gt;
| &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
var (width,height) = (768,512);&lt;br /&gt;
var tile_size = 256;&lt;br /&gt;
&lt;br /&gt;
var window = canvas.Window.new([width, height],&amp;quot;dialog&amp;quot;)&lt;br /&gt;
                   .set('title', &amp;quot;Tile map demo&amp;quot;);&lt;br /&gt;
var g = window.getCanvas(1)&lt;br /&gt;
              .createGroup();&lt;br /&gt;
&lt;br /&gt;
# Simple user interface (Buttons for zoom and label for displaying it)&lt;br /&gt;
var zoom = 10;&lt;br /&gt;
var type = &amp;quot;map&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
var ui_root = window.getCanvas().createGroup();&lt;br /&gt;
var vbox = canvas.VBoxLayout.new();&lt;br /&gt;
window.setLayout(vbox);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
var button_in = canvas.gui.widgets.Button.new(ui_root, canvas.style, {})&lt;br /&gt;
                                  .setText(&amp;quot;+&amp;quot;)&lt;br /&gt;
                                  .listen(&amp;quot;clicked&amp;quot;, func changeZoom(1));&lt;br /&gt;
var button_out = canvas.gui.widgets.Button.new(ui_root, canvas.style, {})&lt;br /&gt;
                                  .setText(&amp;quot;-&amp;quot;)&lt;br /&gt;
                                  .listen(&amp;quot;clicked&amp;quot;, func changeZoom(-1));&lt;br /&gt;
button_in.setSizeHint([32, 32]);&lt;br /&gt;
button_out.setSizeHint([32, 32]);&lt;br /&gt;
&lt;br /&gt;
var label_zoom = canvas.gui.widgets.Label.new(ui_root, canvas.style, {});&lt;br /&gt;
&lt;br /&gt;
var button_box = canvas.HBoxLayout.new();&lt;br /&gt;
button_box.addItem(button_in);&lt;br /&gt;
button_box.addItem(label_zoom);&lt;br /&gt;
button_box.addItem(button_out);&lt;br /&gt;
button_box.addStretch(1);&lt;br /&gt;
&lt;br /&gt;
vbox.addItem(button_box);&lt;br /&gt;
vbox.addStretch(1);&lt;br /&gt;
&lt;br /&gt;
var changeZoom = func(d)&lt;br /&gt;
{&lt;br /&gt;
  zoom = math.max(2, math.min(19, zoom + d));&lt;br /&gt;
  label_zoom.setText(&amp;quot;Zoom &amp;quot; ~ zoom);&lt;br /&gt;
  updateTiles();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# http://polymaps.org/docs/&lt;br /&gt;
# https://github.com/simplegeo/polymaps&lt;br /&gt;
# https://github.com/Leaflet/Leaflet&lt;br /&gt;
&lt;br /&gt;
var maps_base = getprop(&amp;quot;/sim/fg-home&amp;quot;) ~ '/cache/maps';&lt;br /&gt;
&lt;br /&gt;
# http://otile1.mqcdn.com/tiles/1.0.0/map&lt;br /&gt;
# http://otile1.mqcdn.com/tiles/1.0.0/sat&lt;br /&gt;
# (also see http://wiki.openstreetmap.org/wiki/Tile_usage_policy)&lt;br /&gt;
var makeUrl =&lt;br /&gt;
  string.compileTemplate('http://otile1.mqcdn.com/tiles/1.0.0/{type}/{z}/{x}/{y}.jpg');&lt;br /&gt;
var makePath =&lt;br /&gt;
  string.compileTemplate(maps_base ~ '/osm-{type}/{z}/{x}/{y}.jpg');&lt;br /&gt;
var num_tiles = [4, 3];&lt;br /&gt;
&lt;br /&gt;
var center_tile_offset = [&lt;br /&gt;
  (num_tiles[0] - 1) / 2,&lt;br /&gt;
  (num_tiles[1] - 1) / 2&lt;br /&gt;
];&lt;br /&gt;
&lt;br /&gt;
# simple aircraft icon at current position/center of the map&lt;br /&gt;
g.createChild(&amp;quot;path&amp;quot;)&lt;br /&gt;
 .moveTo( tile_size * center_tile_offset[0] - 10,&lt;br /&gt;
          tile_size * center_tile_offset[1] )&lt;br /&gt;
 .horiz(20)&lt;br /&gt;
 .move(-10,-10)&lt;br /&gt;
 .vert(20)&lt;br /&gt;
 .set(&amp;quot;stroke&amp;quot;, &amp;quot;red&amp;quot;)&lt;br /&gt;
 .set(&amp;quot;stroke-width&amp;quot;, 2)&lt;br /&gt;
 .set(&amp;quot;z-index&amp;quot;, 1);&lt;br /&gt;
&lt;br /&gt;
##&lt;br /&gt;
# initialize the map by setting up&lt;br /&gt;
# a grid of raster images  &lt;br /&gt;
&lt;br /&gt;
var tiles = setsize([], num_tiles[0]);&lt;br /&gt;
for(var x = 0; x &amp;lt; num_tiles[0]; x += 1)&lt;br /&gt;
{&lt;br /&gt;
  tiles[x] = setsize([], num_tiles[1]);&lt;br /&gt;
  for(var y = 0; y &amp;lt; num_tiles[1]; y += 1)&lt;br /&gt;
    tiles[x][y] = g.createChild(&amp;quot;image&amp;quot;, &amp;quot;map-tile&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var last_tile = [-1,-1];&lt;br /&gt;
var last_type = type;&lt;br /&gt;
&lt;br /&gt;
##&lt;br /&gt;
# this is the callback that will be regularly called by the timer&lt;br /&gt;
# to update the map&lt;br /&gt;
var updateTiles = func()&lt;br /&gt;
{&lt;br /&gt;
  # get current position&lt;br /&gt;
  var lat = getprop('/position/latitude-deg');&lt;br /&gt;
  var lon = getprop('/position/longitude-deg');&lt;br /&gt;
&lt;br /&gt;
  var n = math.pow(2, zoom);&lt;br /&gt;
  var offset = [&lt;br /&gt;
    n * ((lon + 180) / 360) - center_tile_offset[0],&lt;br /&gt;
    (1 - math.ln(math.tan(lat * math.pi/180) + 1 / math.cos(lat * math.pi/180)) / math.pi) / 2 * n - center_tile_offset[1]&lt;br /&gt;
  ];&lt;br /&gt;
  var tile_index = [int(offset[0]), int(offset[1])];&lt;br /&gt;
&lt;br /&gt;
  var ox = tile_index[0] - offset[0];&lt;br /&gt;
  var oy = tile_index[1] - offset[1];&lt;br /&gt;
&lt;br /&gt;
  for(var x = 0; x &amp;lt; num_tiles[0]; x += 1)&lt;br /&gt;
    for(var y = 0; y &amp;lt; num_tiles[1]; y += 1)&lt;br /&gt;
      tiles[x][y].setTranslation(int((ox + x) * tile_size + 0.5), int((oy + y) * tile_size + 0.5));&lt;br /&gt;
&lt;br /&gt;
  if(    tile_index[0] != last_tile[0]&lt;br /&gt;
      or tile_index[1] != last_tile[1]&lt;br /&gt;
      or type != last_type )&lt;br /&gt;
  {&lt;br /&gt;
    for(var x = 0; x &amp;lt; num_tiles[0]; x += 1)&lt;br /&gt;
      for(var y = 0; y &amp;lt; num_tiles[1]; y += 1)&lt;br /&gt;
      {&lt;br /&gt;
        var pos = {&lt;br /&gt;
          z: zoom,&lt;br /&gt;
          x: int(offset[0] + x),&lt;br /&gt;
          y: int(offset[1] + y),&lt;br /&gt;
          type: type&lt;br /&gt;
        };&lt;br /&gt;
&lt;br /&gt;
        (func {&lt;br /&gt;
        var img_path = makePath(pos);&lt;br /&gt;
        var tile = tiles[x][y];&lt;br /&gt;
&lt;br /&gt;
        if( io.stat(img_path) == nil )&lt;br /&gt;
        { # image not found, save in $FG_HOME&lt;br /&gt;
          var img_url = makeUrl(pos);&lt;br /&gt;
          print('requesting ' ~ img_url);&lt;br /&gt;
          http.save(img_url, img_path)&lt;br /&gt;
              .done(func {print('received image ' ~ img_path); tile.set(&amp;quot;src&amp;quot;, img_path);})&lt;br /&gt;
              .fail(func (r) print('Failed to get image ' ~ img_path ~ ' ' ~ r.status ~ ': ' ~ r.reason));&lt;br /&gt;
        }&lt;br /&gt;
        else # cached image found, reusing&lt;br /&gt;
        {&lt;br /&gt;
          print('loading ' ~ img_path);&lt;br /&gt;
          tile.set(&amp;quot;src&amp;quot;, img_path)&lt;br /&gt;
        }&lt;br /&gt;
        })();&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
    last_tile = tile_index;&lt;br /&gt;
    last_type = type;&lt;br /&gt;
  }&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
##&lt;br /&gt;
# set up a timer that will invoke updateTiles at 2-second intervals&lt;br /&gt;
var update_timer = maketimer(2, updateTiles);&lt;br /&gt;
# actually start the timer&lt;br /&gt;
update_timer.start();&lt;br /&gt;
&lt;br /&gt;
##&lt;br /&gt;
# set up default zoom level&lt;br /&gt;
changeZoom(0);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
###&lt;br /&gt;
# The following lines were recently added and have not yet been tested&lt;br /&gt;
# (if in doubt, remove them)&lt;br /&gt;
window.del = func()&lt;br /&gt;
{&lt;br /&gt;
  print(&amp;quot;Cleaning up window:&amp;quot;,title,&amp;quot;\n&amp;quot;);&lt;br /&gt;
  update_timer.stop();&lt;br /&gt;
# explanation for the call() technique at: http://wiki.flightgear.org/Object_oriented_programming_in_Nasal#Making_safer_base-class_calls&lt;br /&gt;
  call(canvas.Window.del, [], me);&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Adding a NavDisplay ==&lt;br /&gt;
&lt;br /&gt;
== Adding a CDU ==&lt;br /&gt;
&lt;br /&gt;
== Adding a Multi-Function Display (MFD) ==&lt;/div&gt;</summary>
		<author><name>Clm76</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Canvas_snippets&amp;diff=105437</id>
		<title>Canvas snippets</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Canvas_snippets&amp;diff=105437"/>
		<updated>2016-10-29T07:37:33Z</updated>

		<summary type="html">&lt;p&gt;Clm76: /* Customizing MapStructure Styling */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Stub}}&lt;br /&gt;
{{-}}&lt;br /&gt;
This article is meant to be a collection of self-contained code snippets for doing Nasal/Canvas coding without having to spend too much time working through APIs, documentation and existing code - the idea is to provide snippets that contain comments and explicit assumptions, so that these snippets can be easily adapted by people without necessarily having to be very familiar with Nasal/Canvas coding. In the long term, we're hoping to grow a library of useful code snippets to cover most use-cases, while also covering more complex scenarios, e.g. using Canvas-specific helper frameworks like [[Canvas MapStructure]], the [[NavDisplay]] or the recent CDU work.&lt;br /&gt;
&lt;br /&gt;
Canvas itself is designed to be front-end agnostic, meaning that it doesn't matter where a Canvas is used/displayed - this is accomplished by so called &amp;quot;placements&amp;quot;, which determine where a Canvas texture shown. Thus, the back-end logic can remain the same usually, so that GUI dialogs may show avionics, but also so that avionics may show GUI widgets, which also applies to HUDs, liveries and even placements within the FlightGear scenery.&lt;br /&gt;
&lt;br /&gt;
We encourage people to use the snippets listed below to get started with Nasal/Canvas coding, but also to provide feedback on extending/improving this article to make it even more accessible and useful. Equally, if you're aware of any Canvas-related efforts that may contain useful code snippets, please do feel free to add those snippets here. Even people who don't have any interest in coding itself are encouraged to get involved by helping test the snippets listed below, for which you only need to the [[Nasal Console]], and while you're at it, please also help provide/update screen shots for each code snippet.&lt;br /&gt;
&lt;br /&gt;
Contributions added to this article should ideally satisfy some requirements:&lt;br /&gt;
* be entirely self-contained&lt;br /&gt;
* contain good/clear comments&lt;br /&gt;
* have pointers/references to related code/use-cases&lt;br /&gt;
* make assumptions explicit&lt;br /&gt;
* contain screen shots for each example&lt;br /&gt;
* avoid overloaded symbol names to ensure that examples can be merged and adapted easily&lt;br /&gt;
* dependencies (svg files, images/textures etc) should always be chosen such that snippets always work using $FG_ROOT&lt;br /&gt;
* hard-coded assumptions (e.g. texture dimensions in terms of width/height etc) should be encapsulated using variables&lt;br /&gt;
 &lt;br /&gt;
{{Canvas Navigation}}&lt;br /&gt;
&lt;br /&gt;
== Creating a standalone Canvas ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[Special:UploadWizard|Upload requested]] || &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
var (width, height) = (512,512);&lt;br /&gt;
# Create a standalone Canvas (not attached to any GUI dialog/aircraft etc) &lt;br /&gt;
var myCanvas = canvas.new({&lt;br /&gt;
  &amp;quot;name&amp;quot;: &amp;quot;Livery Test&amp;quot;,   # The name is optional but allow for easier identification&lt;br /&gt;
  &amp;quot;size&amp;quot;: [width, height], # Size of the underlying texture (should be a power of 2, required) [Resolution]&lt;br /&gt;
  &amp;quot;view&amp;quot;: [width, height],  # Virtual resolution (Defines the coordinate system of the canvas [Dimensions]&lt;br /&gt;
                        # which will be stretched the size of the texture, required)&lt;br /&gt;
  &amp;quot;mipmapping&amp;quot;: 1       # Enable mipmapping (optional)&lt;br /&gt;
});&lt;br /&gt;
&lt;br /&gt;
# set background color&lt;br /&gt;
myCanvas.set(&amp;quot;background&amp;quot;, canvas.style.getColor(&amp;quot;bg_color&amp;quot;));&lt;br /&gt;
&lt;br /&gt;
# creating the top-level/root group which will contain all other elements/group&lt;br /&gt;
var root = myCanvas.createGroup();&lt;br /&gt;
&lt;br /&gt;
# OPTIONAL: Create a Canvas dialog window to hold the canvas and show that it's working&lt;br /&gt;
# the Canvas is now standalone, i.e. continues to live once the dialog is closed!&lt;br /&gt;
var window = canvas.Window.new([width,height],&amp;quot;dialog&amp;quot;);&lt;br /&gt;
window.setCanvas(myCanvas);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Accessing the Canvas Desktop ==&lt;br /&gt;
Sometimes you may want to render to the main screen, without creating a separate Canvas window - this can be accomplished by using the Canvas desktop, note that the following example is fully self-contained, i.e. does not require any code to be added to work:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[Special:UploadWizard|Upload requested]] || &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
var myNode = canvas.getDesktop().createChild(&amp;quot;text&amp;quot;)&lt;br /&gt;
      .setText(&amp;quot;Hello Canvas Desktop&amp;quot;)&lt;br /&gt;
      .setFontSize(25, 1.0)          # font size (in texels) and font aspect ratio&lt;br /&gt;
      .setColor(1,0,0,1)             # red, fully opaque&lt;br /&gt;
      .setAlignment(&amp;quot;center-center&amp;quot;) # how the text is aligned to where you place it&lt;br /&gt;
      .setTranslation(160, 80);     # where to place the text&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Creating Tooltips ==&lt;br /&gt;
== Creating Popups ==&lt;br /&gt;
== Creating a Canvas GUI Window ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Snippets-canvas-dialog.png|left|thumb|This is what the [[Nasal]]/[[Canvas]] snippet will look like once you pasted it into the [[Nasal Console]] and click &amp;quot;Execute&amp;quot;.]] || {{Note|This example uses so called method chaining, if you're not familiar with the concept, please see: [[Object_Oriented_Programming_with_Nasal#More_on_methods:_Chaining|Method Chaining]].}}&lt;br /&gt;
&lt;br /&gt;
{{Canvas Snippets Boilerplate}}&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Adding Raster Images ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code &lt;br /&gt;
|-&lt;br /&gt;
| [[File:Canvas Snippets-raster image.png|thumb|Canvas snippet demonstrating how to load a raster image]] ||&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# path is relative to $FG_ROOT (base package)&lt;br /&gt;
var path = &amp;quot;Textures/Splash1.png&amp;quot;;&lt;br /&gt;
# create an image child for the texture&lt;br /&gt;
var child = root.createChild(&amp;quot;image&amp;quot;)&lt;br /&gt;
    .setFile(path)&lt;br /&gt;
    .setTranslation(100, 10)&lt;br /&gt;
    .setSize(130, 130);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Adding Raster Images downloaded on demand ==&lt;br /&gt;
&lt;br /&gt;
The '''path''' could also just as well be a URL, i.e. a raster image retrieved via http - for example, the following snippet is entirely self-contained and can be pasted into the [[Nasal Console]] and directly executed &amp;quot;as is&amp;quot;: &lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Canvas-raster-images-via-url.png|thumb|screen shot demonstrating how Nasal and Canvas can be used to display raster images downloaded on demand]] ||&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# create a new window, dimensions are 400 x 200, using the dialog decoration (i.e. titlebar)&lt;br /&gt;
var window = canvas.Window.new([400,200],&amp;quot;dialog&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
# adding a canvas to the new window and setting up background colors/transparency&lt;br /&gt;
var myCanvas = window.createCanvas().set(&amp;quot;background&amp;quot;, canvas.style.getColor(&amp;quot;bg_color&amp;quot;));&lt;br /&gt;
&lt;br /&gt;
# Using specific css colors would also be possible:&lt;br /&gt;
# myCanvas.set(&amp;quot;background&amp;quot;, &amp;quot;#ffaac0&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
# creating the top-level/root group which will contain all other elements/group&lt;br /&gt;
var root = myCanvas.createGroup();&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
# path now a URL&lt;br /&gt;
var url = &amp;quot;http://www.worldwidetelescope.org/docs/Images/MapOfEarth.jpg&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
# create an image child for the texture&lt;br /&gt;
var child=root.createChild(&amp;quot;image&amp;quot;)&lt;br /&gt;
    .setFile( url ) &lt;br /&gt;
    .setTranslation(45,22) # centered, in relation to dialog coordinates&lt;br /&gt;
    .setSize(310,155); # image dimensions&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Clipping ==&lt;br /&gt;
{{Note|{{FGCquote&lt;br /&gt;
|1= Scaling or any other type of transformation or changing the coordinates of individual points is definitely more efficient than clipping (which requires to change the OpenGL clip planes for every rendered object with a different clipping rectangle).&lt;br /&gt;
|2= {{cite web&lt;br /&gt;
  | url    = http://forum.flightgear.org/viewtopic.php?p=277062#p277062&lt;br /&gt;
  | title  = &amp;lt;nowiki&amp;gt;Re: Space Shuttle&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  | author = &amp;lt;nowiki&amp;gt;TheTom&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  | date   = Feb 21st, 2016&lt;br /&gt;
  | added   = Feb 21st, 2016&lt;br /&gt;
  | script_version = 0.25&lt;br /&gt;
  }}&lt;br /&gt;
}}&lt;br /&gt;
}}&lt;br /&gt;
I would suggest to refer to api.nas and look for &amp;quot;clip&amp;quot; and/or &amp;quot;rect&amp;quot; - IIRC, you need to set up a clipping rectangle by setting some kind of &amp;quot;clip&amp;quot; property and setting it to a rect value in the form of rect(...)&lt;br /&gt;
{{FGCquote&lt;br /&gt;
|1= For details, see the clipping example at: [[Canvas Nasal API#set 2]] and  [[Canvas Element#clip.28.29]]&lt;br /&gt;
|2= {{cite web&lt;br /&gt;
  | url    = http://forum.flightgear.org/viewtopic.php?p=276965#p276965&lt;br /&gt;
  | title  = &amp;lt;nowiki&amp;gt;Re: Space Shuttle&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  | author = &amp;lt;nowiki&amp;gt;Hooray&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  | date   = Feb 21st, 2016&lt;br /&gt;
  | added   = Feb 21st, 2016&lt;br /&gt;
  | script_version = 0.25&lt;br /&gt;
  }}&lt;br /&gt;
}}&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[File:MapStructure_dialog_with_clipping_and_event_handling_applied.png|thumb]] ||&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# create a new window, dimensions are 400 x 200, using the dialog decoration (i.e. titlebar)&lt;br /&gt;
var window = canvas.Window.new([400,200],&amp;quot;dialog&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
# adding a canvas to the new window and setting up background colors/transparency&lt;br /&gt;
var myCanvas = window.createCanvas().set(&amp;quot;background&amp;quot;, canvas.style.getColor(&amp;quot;bg_color&amp;quot;));&lt;br /&gt;
&lt;br /&gt;
# Using specific css colors would also be possible:&lt;br /&gt;
# myCanvas.set(&amp;quot;background&amp;quot;, &amp;quot;#ffaac0&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
# creating the top-level/root group which will contain all other elements/group&lt;br /&gt;
var root = myCanvas.createGroup();&lt;br /&gt;
&lt;br /&gt;
# create an image child for the texture&lt;br /&gt;
var child=root.createChild(&amp;quot;.........&amp;quot;)&lt;br /&gt;
    .setFile( url ) &lt;br /&gt;
    .setTranslation(45,22) # centered, in relation to dialog coordinates&lt;br /&gt;
    .setSize(310,155); # image dimensions&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Adding OpenVG Paths ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Openvg-via-canvas.png|thumb|screen shot showing a simple Canvas GUI dialog demonstrating how to use OpenVG-path drawing via Nasal and Canvas]] &lt;br /&gt;
| {{Note|This assumes that you already have a top-level root group set up, and named it '''root'''.}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
var graph = root.createChild(&amp;quot;group&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
var x_axis = graph.createChild(&amp;quot;path&amp;quot;, &amp;quot;x-axis&amp;quot;)&lt;br /&gt;
.moveTo(10, height/2)&lt;br /&gt;
.lineTo(width-10, height/2)&lt;br /&gt;
.setColor(1,0,0)&lt;br /&gt;
.setStrokeLineWidth(3);&lt;br /&gt;
&lt;br /&gt;
var y_axis = graph.createChild(&amp;quot;path&amp;quot;, &amp;quot;y-axis&amp;quot;)&lt;br /&gt;
.moveTo(10, 10)&lt;br /&gt;
.lineTo(10, height-10)&lt;br /&gt;
.setColor(0,0,1)&lt;br /&gt;
.setStrokeLineWidth(2);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Canvas-openvg-quadTo.png|thumb|screen shot showing a simple Canvas GUI dialog demonstrating how to use OpenVG-path drawing via Nasal and Canvas (Canvas/OpenVG quadTo API for drawing curves)]] &lt;br /&gt;
| {{Note|This assumes that you are appending this to the snippet shown above.}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
var points = [	60,height-20, &lt;br /&gt;
		230,height-100,&lt;br /&gt;
		];&lt;br /&gt;
&lt;br /&gt;
var track = graph.createChild(&amp;quot;path&amp;quot;, &amp;quot;track&amp;quot;)&lt;br /&gt;
.moveTo(10, height/2)&lt;br /&gt;
.quadTo( points )&lt;br /&gt;
.setColor(0,1,0)&lt;br /&gt;
.setStrokeLineWidth(4);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Canvas-openvg-cubicTo.png|thumb|screen shot showing a simple Canvas GUI dialog demonstrating how to use OpenVG-path drawing via Nasal and Canvas (Canvas/OpenVG cubicTo API for drawing curves)]] &lt;br /&gt;
| {{Note|This assumes that you are appending this to the snippet shown above.}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
var points = [	60,height-20, &lt;br /&gt;
		120,height-120,&lt;br /&gt;
		230,height-100,&lt;br /&gt;
		];&lt;br /&gt;
&lt;br /&gt;
var track = graph.createChild(&amp;quot;path&amp;quot;, &amp;quot;track&amp;quot;)&lt;br /&gt;
.moveTo(10, height/2)&lt;br /&gt;
.cubicTo( points )&lt;br /&gt;
.setColor(0,1,0)&lt;br /&gt;
.setStrokeLineWidth(4);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Adding Vector Images ==&lt;br /&gt;
&lt;br /&gt;
[[Category:Canvas SVG]]&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Canvas-svg-support.png|thumb|screen shot demonstrating how the scripted Nasal-based SVG parser can be used to dynamically turn SVG files into OpenVG instructions understood by Canvas]] &lt;br /&gt;
| {{Note|This assumes that you already have a top-level root group set up, and named it '''root'''.}}&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
# change the background color &lt;br /&gt;
myCanvas.set(&amp;quot;background&amp;quot;, &amp;quot;#ffaac0&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
# creating the top-level/root group which will contain all other elements/group&lt;br /&gt;
var root = myCanvas.createGroup();&lt;br /&gt;
&lt;br /&gt;
var filename = &amp;quot;/Nasal/canvas/map/Images/boeingAirplane.svg&amp;quot;;&lt;br /&gt;
var svg_symbol = root.createChild('group');&lt;br /&gt;
canvas.parsesvg(svg_symbol, filename);&lt;br /&gt;
&lt;br /&gt;
svg_symbol.setTranslation(width/2,height/2);&lt;br /&gt;
&lt;br /&gt;
#svg_symbol.setScale(0.2);&lt;br /&gt;
#svg_symbol.setRotation(radians)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Using the SymbolCache ==&lt;br /&gt;
Whenever a symbol may need to be shown/instanced multiple times (possibly using different styling), it makes sense to use caching - otherwise, identical symbols would be treated as separate OpenVG groups, all of which would need to be rasterized/rendedered separately (i.e. 100 identical symbols would be updated/rendered one by one). &lt;br /&gt;
&lt;br /&gt;
Typically, a map may display multiple instances of an otherwise identical symbol (think VOR, NDB, DME etc) - equally, a multiplayer map may showing multiple aircraft symbols at the same time. In these cases, it makes sense to use the SymbolCache framework, which will render symbols into a separate Canvas texture and provide a texture map that can be treated as a lookup map, which even supports styling for otherwise identical symbols. To learn more, please refer to [[Canvas MapStructure#The_SymbolCache|SymbolCache]]&lt;br /&gt;
... &lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Canvas-SymbolCache-Instancing.png|thumb|Screen shot showing a Canvas based GUI dialog that is using the SymbolCache for instancing multiple symbols (including support for styling)]] &lt;br /&gt;
| &amp;lt;!--{{Caution|This is currently untested code}}--&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
##&lt;br /&gt;
# this is the function that draws a symbol using OpenVG paths&lt;br /&gt;
# it accepts a group to draw to and returns the rendered group &lt;br /&gt;
# to the caller&lt;br /&gt;
var drawVOR = func(group) {&lt;br /&gt;
    return group.createChild(&amp;quot;path&amp;quot;)&lt;br /&gt;
        .moveTo(-15,0)&lt;br /&gt;
        .lineTo(-7.5,12.5)&lt;br /&gt;
        .lineTo(7.5,12.5)&lt;br /&gt;
        .lineTo(15,0)&lt;br /&gt;
        .lineTo(7.5,-12.5)&lt;br /&gt;
        .lineTo(-7.5,-12.5)&lt;br /&gt;
        .close()&lt;br /&gt;
        .setStrokeLineWidth(line_width) # style-able&lt;br /&gt;
        .setColor(color); # style-able&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
var style = { # styling related attributes (as per the draw* function above)&lt;br /&gt;
    line_width: 3,&lt;br /&gt;
    scale_factor: 1,&lt;br /&gt;
    color: [1,0,0],&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
# create a new cache entry for the styled symbol&lt;br /&gt;
var myCachedSymbol = canvas.StyleableCacheable.new(&lt;br /&gt;
    name:'myVOR', draw_func: drawVOR,&lt;br /&gt;
    cache: canvas.SymbolCache32x32, # the cache to be used&lt;br /&gt;
    draw_mode: canvas.SymbolCache.DRAW_CENTERED,&lt;br /&gt;
    relevant_keys: ['line_width', 'color'], # styling related attributes&lt;br /&gt;
);&lt;br /&gt;
&lt;br /&gt;
var target = root.createChild('group');&lt;br /&gt;
&lt;br /&gt;
var x=0;&lt;br /&gt;
var y=height/2;&lt;br /&gt;
&lt;br /&gt;
var xoffset=50;&lt;br /&gt;
&lt;br /&gt;
# render 5 instanced symbols using the style specified above&lt;br /&gt;
for (var i=0;i&amp;lt;5;i+=1) {&lt;br /&gt;
# look up the raster image for the symbol&lt;br /&gt;
# render it using the passed style and adjust scaling&lt;br /&gt;
var instanced = myCachedSymbol.render(target, style)&lt;br /&gt;
  .setScale(style.scale_factor)&lt;br /&gt;
  .setTranslation(x+=xoffset,y);&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The example shown above uses a fixed symbol/icon cache that is set up while booting Flightgear - sometimes, we may need cache for different purposes. So, let's assume, we need a new/custom cache with a different resolution for each entry in the cache (e.g. 256x256), we can easily accomplish that by setting up a new cache like this: &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
var mySymbolCache256x256 = canvas.SymbolCache.new(1024,256);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Adding Text Elements ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[File:CanvasText-Hello-World.png|thumb|Screen shot showing the CanvasText example contributed by Necolatis]]&lt;br /&gt;
| {{Note|This assumes that you already have a top-level root group set up, and named it '''root'''.}}&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
var myText = root.createChild(&amp;quot;text&amp;quot;)&lt;br /&gt;
      .setText(&amp;quot;Hello world!&amp;quot;)&lt;br /&gt;
      .setFontSize(20, 0.9)          # font size (in texels) and font aspect ratio&lt;br /&gt;
      .setColor(1,0,0,1)             # red, fully opaque&lt;br /&gt;
      .setAlignment(&amp;quot;center-center&amp;quot;) # how the text is aligned to where you place it&lt;br /&gt;
      .setTranslation(160, 80);     # where to place the text&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Adding GUI Labels ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Canvas-Layout-Label-example-by-Necolatis.png|thumb|Canvas demo: Layouts and Labels (by Necolatis)]]&lt;br /&gt;
| {{Note|This assumes that you already have a top-level root group set up, and named it '''root''', just change this variable if you want it to be rendered elsewhere. It also assumes you have a Layout item setup and called '''myLayoutItem'''.}}&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
# create a new layout&lt;br /&gt;
var myLayout = canvas.HBoxLayout.new();&lt;br /&gt;
# assign it to the Canvas&lt;br /&gt;
myCanvas.setLayout(myLayout);&lt;br /&gt;
&lt;br /&gt;
var label = canvas.gui.widgets.Label.new(root, canvas.style, {wordWrap: 0}); # wordwrap: 0 will disable wordwrapping, to enable it use 1 instead&lt;br /&gt;
label.setText(&amp;quot;Hello World!&amp;quot;);&lt;br /&gt;
myLayout.addItem(label);&lt;br /&gt;
&lt;br /&gt;
var label2 = canvas.gui.widgets.Label.new(root, canvas.style, {wordWrap: 0}); # wordwrap: 0 will disable wordwrapping, to enable it use 1 instead&lt;br /&gt;
label2.setText(&amp;quot;Hello FlightGear&amp;quot;);&lt;br /&gt;
myLayout.addItem(label2);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Adding GUI Buttons (Layouts)==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Canvas-demo-layouts-and-buttons-by-Necolatis.png|thumb|Canvas snippet: buttons and layouts (by Necolatis)]] &lt;br /&gt;
|&lt;br /&gt;
&lt;br /&gt;
{{Note|This assumes that you already have a top-level root group set up, and named it '''root''', just change this variable if you want it to be rendered elsewhere. It also assumes you have a Layout item setup and called '''myLayoutItem'''.}}&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
# create a new layout&lt;br /&gt;
var myLayout = canvas.HBoxLayout.new();&lt;br /&gt;
# assign it to the Canvas&lt;br /&gt;
myCanvas.setLayout(myLayout);&lt;br /&gt;
&lt;br /&gt;
# click button&lt;br /&gt;
&lt;br /&gt;
var button = canvas.gui.widgets.Button.new(root, canvas.style, {})&lt;br /&gt;
	.setText(&amp;quot;Click on me&amp;quot;)&lt;br /&gt;
	.setFixedSize(75, 25);&lt;br /&gt;
&lt;br /&gt;
button.listen(&amp;quot;clicked&amp;quot;, func {&lt;br /&gt;
        # add code here to react on click on button.&lt;br /&gt;
print(&amp;quot;Button clicked !&amp;quot;);&lt;br /&gt;
});&lt;br /&gt;
&lt;br /&gt;
myLayout.addItem(button);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Canvas-toggle-button-snippet-by-Necolatis.png|thumb|Canvas toggle button demo by Necolatis]]&lt;br /&gt;
| &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
# create a new layout&lt;br /&gt;
var myLayout = canvas.HBoxLayout.new();&lt;br /&gt;
# assign it to the Canvas&lt;br /&gt;
myCanvas.setLayout(myLayout);&lt;br /&gt;
&lt;br /&gt;
var button = canvas.gui.widgets.Button.new(root, canvas.style, {})&lt;br /&gt;
        .setText(&amp;quot;Toggle me&amp;quot;)&lt;br /&gt;
        .setCheckable(1) # this indicates that is should be a toggle button&lt;br /&gt;
        .setChecked(0) # depressed by default&lt;br /&gt;
        .setFixedSize(75, 25);&lt;br /&gt;
&lt;br /&gt;
button.listen(&amp;quot;toggled&amp;quot;, func (e) {&lt;br /&gt;
        if( e.detail.checked ) {&lt;br /&gt;
            # add code here to react on button being depressed.&lt;br /&gt;
        } else {&lt;br /&gt;
            # add code here to react on button not being depressed.&lt;br /&gt;
        }&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
myLayout.addItem(button);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Canvas Input Dialog ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Snippets-canvas-input-dialog.png|thumb|Canvas input dialog]]&lt;br /&gt;
||&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
# create a new InputDialog with a title, label, and a callback&lt;br /&gt;
canvas.InputDialog.getText(&amp;quot;Input Dialog Title&amp;quot;, &amp;quot;Please enter some text&amp;quot;, func(btn,value) {&lt;br /&gt;
    if (value) gui.popupTip(&amp;quot;You entered: &amp;quot;~value);&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Canvas ScrollArea ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Canvas-snippets-scrollArea-demo.png|thumb|Screen shot showing a Canvas ScrollArea populated with different splash screens, loaded from $FG_ROOT/Textures]]&lt;br /&gt;
||&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
var vbox = canvas.VBoxLayout.new();&lt;br /&gt;
myCanvas.setLayout(vbox);&lt;br /&gt;
&lt;br /&gt;
 var scroll = canvas.gui.widgets.ScrollArea.new(root, canvas.style, {size: [96, 128]}).move(20, 100);&lt;br /&gt;
 vbox.addItem(scroll, 1);&lt;br /&gt;
&lt;br /&gt;
var scrollContent =&lt;br /&gt;
      scroll.getContent()&lt;br /&gt;
            .set(&amp;quot;font&amp;quot;, &amp;quot;LiberationFonts/LiberationSans-Bold.ttf&amp;quot;)&lt;br /&gt;
            .set(&amp;quot;character-size&amp;quot;, 16)&lt;br /&gt;
            .set(&amp;quot;alignment&amp;quot;, &amp;quot;left-center&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
var list = canvas.VBoxLayout.new();&lt;br /&gt;
scroll.setLayout(list);&lt;br /&gt;
&lt;br /&gt;
for (var i=1;i&amp;lt;=5;i+=1) {&lt;br /&gt;
var label = canvas.gui.widgets.Label.new(scrollContent, canvas.style, {wordWrap: 0}); &lt;br /&gt;
label.setImage(&amp;quot;Textures/Splash&amp;quot;~i~&amp;quot;.png&amp;quot;);&lt;br /&gt;
label.setFixedSize(256,256);&lt;br /&gt;
list.addItem(label);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Using Layouts ==&lt;br /&gt;
&lt;br /&gt;
== Using Styling ==&lt;br /&gt;
== Adding a HUD ==&lt;br /&gt;
== Adding a 2D Instrument ==&lt;br /&gt;
== Adding a 2D Panel ==&lt;br /&gt;
== Adding a PFD ==&lt;br /&gt;
&lt;br /&gt;
== Adding a Failure Mgmt Widget ==&lt;br /&gt;
{{Note|This kind of widget will typically be useful for dialogs requiring a method for managing aircraft specific system failures (e.g. an instructor console).}}&lt;br /&gt;
&lt;br /&gt;
== Adding a MapStructure map to a Canvas  ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code &lt;br /&gt;
|-&lt;br /&gt;
| [[File:Snippets-canvas-mapstructure-dialog.png|thumb|[[MapStructure]] layers shown in a Canvas GUI dialog]]&lt;br /&gt;
| {{Note|This assumes that you already have a top-level root group set up, and named it '''root''', just change this variable if you want it to be rendered elsewhere. Equally, this snippet assumes that your canvas is named '''myCanvas'''.}}&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
var TestMap = root.createChild(&amp;quot;map&amp;quot;);&lt;br /&gt;
TestMap.setController(&amp;quot;Aircraft position&amp;quot;);&lt;br /&gt;
TestMap.setRange(25); &lt;br /&gt;
 &lt;br /&gt;
TestMap.setTranslation(    myCanvas.get(&amp;quot;view[0]&amp;quot;)/2,&lt;br /&gt;
                           myCanvas.get(&amp;quot;view[1]&amp;quot;)/2&lt;br /&gt;
                        );&lt;br /&gt;
var r = func(name,vis=1,zindex=nil) return caller(0)[0];&lt;br /&gt;
&lt;br /&gt;
foreach(var type; [r('APT'), r('VOR') ] )&lt;br /&gt;
 TestMap.addLayer(factory: canvas.SymbolLayer, type_arg: type.name, visible: type.vis, priority: type.zindex,);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Customizing MapStructure Styling  ==&lt;br /&gt;
{{See also|Canvas MapStructure#Styling}}&lt;br /&gt;
{{WIP}}&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code &lt;br /&gt;
|-&lt;br /&gt;
| [[File:Layers style change.png|thumb|[[MapStructure]] layers shown in a Canvas GUI dialog. Active VOR and radial in red color, inactive VOR in green color]]&lt;br /&gt;
| {{Note|This assumes that you already have a top-level root group set up, and named it '''root''', just change this variable if you want it to be rendered elsewhere. Equally, this snippet assumes that your canvas is named '''myCanvas'''.}}&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
var TestMap = root.createChild(&amp;quot;map&amp;quot;);&lt;br /&gt;
TestMap.setController(&amp;quot;Aircraft position&amp;quot;);&lt;br /&gt;
TestMap.setRange(25); &lt;br /&gt;
 &lt;br /&gt;
TestMap.setTranslation(    myCanvas.get(&amp;quot;view[0]&amp;quot;)/2,&lt;br /&gt;
                           myCanvas.get(&amp;quot;view[1]&amp;quot;)/2&lt;br /&gt;
                        );&lt;br /&gt;
var r = func(name,vis=1,zindex=nil) return caller(0)[0];&lt;br /&gt;
var type = r('APT');&lt;br /&gt;
var style_apt = {&lt;br /&gt;
    scale_factor:0.5, # 50 %&lt;br /&gt;
    color_default:[0,1,0.9], # rgb&lt;br /&gt;
    line_width:4, # thickness&lt;br /&gt;
    label_font_color:[0,1,0.9], # rgb&lt;br /&gt;
    label_font_size:30 # font size&lt;br /&gt;
};&lt;br /&gt;
TestMap.addLayer(factory: canvas.SymbolLayer, type_arg: type.name, visible: type.vis, priority: type.zindex,style:style_apt);&lt;br /&gt;
&lt;br /&gt;
var type = r('VOR');&lt;br /&gt;
var style_vor = {&lt;br /&gt;
    scale_factor:0.6,&lt;br /&gt;
    active_color:[1,0,0],&lt;br /&gt;
    inactive_color:[0,1,0],&lt;br /&gt;
    line_width:4,&lt;br /&gt;
};&lt;br /&gt;
TestMap.addLayer(factory: canvas.SymbolLayer, type_arg: type.name, visible: type.vis, priority: type.zindex,style:style_vor);&lt;br /&gt;
&lt;br /&gt;
### See ...fgdata/Nasal/canvas/map/APT.symbol   ...fgdata/Nasal/canvas/map/VOR.style ... for the style variables that can be configured.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Using MapStructure and Overlays ==&lt;br /&gt;
{{FGCquote&lt;br /&gt;
  |Support for overlays using geo-referenced raster images isn't too well-developed currently, so would need to work with a few hard-coded assumptions (e.g. being specific to a certain raster image and map layout) - but maybe TheTom can provide a few more ideas on how to proceed from here - otherwise, the Map is normally not aware of any non-Map items, i.e. as long as the overlay is added &amp;quot;outside&amp;quot; the Map, it isn't even aware of the raster image - and when it is added as a map element, it would probably be rotated. So there's still some work needed here. But generally, this should work well enough even in its current form (the screen shot is purely based on the Canvas Snippets article, i.e. it just displays a Canvas GUI dialog, adds the downloaded raster image and then adds the MapStructure APS layer - without the Map being aware of the overlay/geo-referencing that is needed currently).&lt;br /&gt;
  |{{cite web |url=http://forum.flightgear.org/viewtopic.php?p=238316#p238316&lt;br /&gt;
     |title=&amp;lt;nowiki&amp;gt;Re: Using Canvas for visualizing orbital flights (cont'd PM)&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     |author=&amp;lt;nowiki&amp;gt;Hooray&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     |date=&amp;lt;nowiki&amp;gt;Fri Apr 10&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
   }}&lt;br /&gt;
}}&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code &lt;br /&gt;
|-&lt;br /&gt;
| [[File:Canvas-overlay-with-mapstructure.png|thumb|Using the [[MapStructure]] framework in conjunction with raster images as overlays]]&lt;br /&gt;
| {{Note|This assumes that you already have a top-level root group set up, and named it '''root''', just change this variable if you want it to be rendered elsewhere. Equally, this snippet assumes that your canvas is named '''myCanvas'''.}}&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== A simple tile map ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code &lt;br /&gt;
|-&lt;br /&gt;
| [[File:Canvas - Tile map demo.png|thumb|A simple, canvas based tile map which is centered around the aircraft.]]&lt;br /&gt;
| &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
var (width,height) = (768,512);&lt;br /&gt;
var tile_size = 256;&lt;br /&gt;
&lt;br /&gt;
var window = canvas.Window.new([width, height],&amp;quot;dialog&amp;quot;)&lt;br /&gt;
                   .set('title', &amp;quot;Tile map demo&amp;quot;);&lt;br /&gt;
var g = window.getCanvas(1)&lt;br /&gt;
              .createGroup();&lt;br /&gt;
&lt;br /&gt;
# Simple user interface (Buttons for zoom and label for displaying it)&lt;br /&gt;
var zoom = 10;&lt;br /&gt;
var type = &amp;quot;map&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
var ui_root = window.getCanvas().createGroup();&lt;br /&gt;
var vbox = canvas.VBoxLayout.new();&lt;br /&gt;
window.setLayout(vbox);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
var button_in = canvas.gui.widgets.Button.new(ui_root, canvas.style, {})&lt;br /&gt;
                                  .setText(&amp;quot;+&amp;quot;)&lt;br /&gt;
                                  .listen(&amp;quot;clicked&amp;quot;, func changeZoom(1));&lt;br /&gt;
var button_out = canvas.gui.widgets.Button.new(ui_root, canvas.style, {})&lt;br /&gt;
                                  .setText(&amp;quot;-&amp;quot;)&lt;br /&gt;
                                  .listen(&amp;quot;clicked&amp;quot;, func changeZoom(-1));&lt;br /&gt;
button_in.setSizeHint([32, 32]);&lt;br /&gt;
button_out.setSizeHint([32, 32]);&lt;br /&gt;
&lt;br /&gt;
var label_zoom = canvas.gui.widgets.Label.new(ui_root, canvas.style, {});&lt;br /&gt;
&lt;br /&gt;
var button_box = canvas.HBoxLayout.new();&lt;br /&gt;
button_box.addItem(button_in);&lt;br /&gt;
button_box.addItem(label_zoom);&lt;br /&gt;
button_box.addItem(button_out);&lt;br /&gt;
button_box.addStretch(1);&lt;br /&gt;
&lt;br /&gt;
vbox.addItem(button_box);&lt;br /&gt;
vbox.addStretch(1);&lt;br /&gt;
&lt;br /&gt;
var changeZoom = func(d)&lt;br /&gt;
{&lt;br /&gt;
  zoom = math.max(2, math.min(19, zoom + d));&lt;br /&gt;
  label_zoom.setText(&amp;quot;Zoom &amp;quot; ~ zoom);&lt;br /&gt;
  updateTiles();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# http://polymaps.org/docs/&lt;br /&gt;
# https://github.com/simplegeo/polymaps&lt;br /&gt;
# https://github.com/Leaflet/Leaflet&lt;br /&gt;
&lt;br /&gt;
var maps_base = getprop(&amp;quot;/sim/fg-home&amp;quot;) ~ '/cache/maps';&lt;br /&gt;
&lt;br /&gt;
# http://otile1.mqcdn.com/tiles/1.0.0/map&lt;br /&gt;
# http://otile1.mqcdn.com/tiles/1.0.0/sat&lt;br /&gt;
# (also see http://wiki.openstreetmap.org/wiki/Tile_usage_policy)&lt;br /&gt;
var makeUrl =&lt;br /&gt;
  string.compileTemplate('http://otile1.mqcdn.com/tiles/1.0.0/{type}/{z}/{x}/{y}.jpg');&lt;br /&gt;
var makePath =&lt;br /&gt;
  string.compileTemplate(maps_base ~ '/osm-{type}/{z}/{x}/{y}.jpg');&lt;br /&gt;
var num_tiles = [4, 3];&lt;br /&gt;
&lt;br /&gt;
var center_tile_offset = [&lt;br /&gt;
  (num_tiles[0] - 1) / 2,&lt;br /&gt;
  (num_tiles[1] - 1) / 2&lt;br /&gt;
];&lt;br /&gt;
&lt;br /&gt;
# simple aircraft icon at current position/center of the map&lt;br /&gt;
g.createChild(&amp;quot;path&amp;quot;)&lt;br /&gt;
 .moveTo( tile_size * center_tile_offset[0] - 10,&lt;br /&gt;
          tile_size * center_tile_offset[1] )&lt;br /&gt;
 .horiz(20)&lt;br /&gt;
 .move(-10,-10)&lt;br /&gt;
 .vert(20)&lt;br /&gt;
 .set(&amp;quot;stroke&amp;quot;, &amp;quot;red&amp;quot;)&lt;br /&gt;
 .set(&amp;quot;stroke-width&amp;quot;, 2)&lt;br /&gt;
 .set(&amp;quot;z-index&amp;quot;, 1);&lt;br /&gt;
&lt;br /&gt;
##&lt;br /&gt;
# initialize the map by setting up&lt;br /&gt;
# a grid of raster images  &lt;br /&gt;
&lt;br /&gt;
var tiles = setsize([], num_tiles[0]);&lt;br /&gt;
for(var x = 0; x &amp;lt; num_tiles[0]; x += 1)&lt;br /&gt;
{&lt;br /&gt;
  tiles[x] = setsize([], num_tiles[1]);&lt;br /&gt;
  for(var y = 0; y &amp;lt; num_tiles[1]; y += 1)&lt;br /&gt;
    tiles[x][y] = g.createChild(&amp;quot;image&amp;quot;, &amp;quot;map-tile&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var last_tile = [-1,-1];&lt;br /&gt;
var last_type = type;&lt;br /&gt;
&lt;br /&gt;
##&lt;br /&gt;
# this is the callback that will be regularly called by the timer&lt;br /&gt;
# to update the map&lt;br /&gt;
var updateTiles = func()&lt;br /&gt;
{&lt;br /&gt;
  # get current position&lt;br /&gt;
  var lat = getprop('/position/latitude-deg');&lt;br /&gt;
  var lon = getprop('/position/longitude-deg');&lt;br /&gt;
&lt;br /&gt;
  var n = math.pow(2, zoom);&lt;br /&gt;
  var offset = [&lt;br /&gt;
    n * ((lon + 180) / 360) - center_tile_offset[0],&lt;br /&gt;
    (1 - math.ln(math.tan(lat * math.pi/180) + 1 / math.cos(lat * math.pi/180)) / math.pi) / 2 * n - center_tile_offset[1]&lt;br /&gt;
  ];&lt;br /&gt;
  var tile_index = [int(offset[0]), int(offset[1])];&lt;br /&gt;
&lt;br /&gt;
  var ox = tile_index[0] - offset[0];&lt;br /&gt;
  var oy = tile_index[1] - offset[1];&lt;br /&gt;
&lt;br /&gt;
  for(var x = 0; x &amp;lt; num_tiles[0]; x += 1)&lt;br /&gt;
    for(var y = 0; y &amp;lt; num_tiles[1]; y += 1)&lt;br /&gt;
      tiles[x][y].setTranslation(int((ox + x) * tile_size + 0.5), int((oy + y) * tile_size + 0.5));&lt;br /&gt;
&lt;br /&gt;
  if(    tile_index[0] != last_tile[0]&lt;br /&gt;
      or tile_index[1] != last_tile[1]&lt;br /&gt;
      or type != last_type )&lt;br /&gt;
  {&lt;br /&gt;
    for(var x = 0; x &amp;lt; num_tiles[0]; x += 1)&lt;br /&gt;
      for(var y = 0; y &amp;lt; num_tiles[1]; y += 1)&lt;br /&gt;
      {&lt;br /&gt;
        var pos = {&lt;br /&gt;
          z: zoom,&lt;br /&gt;
          x: int(offset[0] + x),&lt;br /&gt;
          y: int(offset[1] + y),&lt;br /&gt;
          type: type&lt;br /&gt;
        };&lt;br /&gt;
&lt;br /&gt;
        (func {&lt;br /&gt;
        var img_path = makePath(pos);&lt;br /&gt;
        var tile = tiles[x][y];&lt;br /&gt;
&lt;br /&gt;
        if( io.stat(img_path) == nil )&lt;br /&gt;
        { # image not found, save in $FG_HOME&lt;br /&gt;
          var img_url = makeUrl(pos);&lt;br /&gt;
          print('requesting ' ~ img_url);&lt;br /&gt;
          http.save(img_url, img_path)&lt;br /&gt;
              .done(func {print('received image ' ~ img_path); tile.set(&amp;quot;src&amp;quot;, img_path);})&lt;br /&gt;
              .fail(func (r) print('Failed to get image ' ~ img_path ~ ' ' ~ r.status ~ ': ' ~ r.reason));&lt;br /&gt;
        }&lt;br /&gt;
        else # cached image found, reusing&lt;br /&gt;
        {&lt;br /&gt;
          print('loading ' ~ img_path);&lt;br /&gt;
          tile.set(&amp;quot;src&amp;quot;, img_path)&lt;br /&gt;
        }&lt;br /&gt;
        })();&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
    last_tile = tile_index;&lt;br /&gt;
    last_type = type;&lt;br /&gt;
  }&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
##&lt;br /&gt;
# set up a timer that will invoke updateTiles at 2-second intervals&lt;br /&gt;
var update_timer = maketimer(2, updateTiles);&lt;br /&gt;
# actually start the timer&lt;br /&gt;
update_timer.start();&lt;br /&gt;
&lt;br /&gt;
##&lt;br /&gt;
# set up default zoom level&lt;br /&gt;
changeZoom(0);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
###&lt;br /&gt;
# The following lines were recently added and have not yet been tested&lt;br /&gt;
# (if in doubt, remove them)&lt;br /&gt;
window.del = func()&lt;br /&gt;
{&lt;br /&gt;
  print(&amp;quot;Cleaning up window:&amp;quot;,title,&amp;quot;\n&amp;quot;);&lt;br /&gt;
  update_timer.stop();&lt;br /&gt;
# explanation for the call() technique at: http://wiki.flightgear.org/Object_oriented_programming_in_Nasal#Making_safer_base-class_calls&lt;br /&gt;
  call(canvas.Window.del, [], me);&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Adding a NavDisplay ==&lt;br /&gt;
&lt;br /&gt;
== Adding a CDU ==&lt;br /&gt;
&lt;br /&gt;
== Adding a Multi-Function Display (MFD) ==&lt;/div&gt;</summary>
		<author><name>Clm76</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Canvas_snippets&amp;diff=105436</id>
		<title>Canvas snippets</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Canvas_snippets&amp;diff=105436"/>
		<updated>2016-10-29T07:35:12Z</updated>

		<summary type="html">&lt;p&gt;Clm76: /* Customizing MapStructure Styling */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Stub}}&lt;br /&gt;
{{-}}&lt;br /&gt;
This article is meant to be a collection of self-contained code snippets for doing Nasal/Canvas coding without having to spend too much time working through APIs, documentation and existing code - the idea is to provide snippets that contain comments and explicit assumptions, so that these snippets can be easily adapted by people without necessarily having to be very familiar with Nasal/Canvas coding. In the long term, we're hoping to grow a library of useful code snippets to cover most use-cases, while also covering more complex scenarios, e.g. using Canvas-specific helper frameworks like [[Canvas MapStructure]], the [[NavDisplay]] or the recent CDU work.&lt;br /&gt;
&lt;br /&gt;
Canvas itself is designed to be front-end agnostic, meaning that it doesn't matter where a Canvas is used/displayed - this is accomplished by so called &amp;quot;placements&amp;quot;, which determine where a Canvas texture shown. Thus, the back-end logic can remain the same usually, so that GUI dialogs may show avionics, but also so that avionics may show GUI widgets, which also applies to HUDs, liveries and even placements within the FlightGear scenery.&lt;br /&gt;
&lt;br /&gt;
We encourage people to use the snippets listed below to get started with Nasal/Canvas coding, but also to provide feedback on extending/improving this article to make it even more accessible and useful. Equally, if you're aware of any Canvas-related efforts that may contain useful code snippets, please do feel free to add those snippets here. Even people who don't have any interest in coding itself are encouraged to get involved by helping test the snippets listed below, for which you only need to the [[Nasal Console]], and while you're at it, please also help provide/update screen shots for each code snippet.&lt;br /&gt;
&lt;br /&gt;
Contributions added to this article should ideally satisfy some requirements:&lt;br /&gt;
* be entirely self-contained&lt;br /&gt;
* contain good/clear comments&lt;br /&gt;
* have pointers/references to related code/use-cases&lt;br /&gt;
* make assumptions explicit&lt;br /&gt;
* contain screen shots for each example&lt;br /&gt;
* avoid overloaded symbol names to ensure that examples can be merged and adapted easily&lt;br /&gt;
* dependencies (svg files, images/textures etc) should always be chosen such that snippets always work using $FG_ROOT&lt;br /&gt;
* hard-coded assumptions (e.g. texture dimensions in terms of width/height etc) should be encapsulated using variables&lt;br /&gt;
 &lt;br /&gt;
{{Canvas Navigation}}&lt;br /&gt;
&lt;br /&gt;
== Creating a standalone Canvas ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[Special:UploadWizard|Upload requested]] || &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
var (width, height) = (512,512);&lt;br /&gt;
# Create a standalone Canvas (not attached to any GUI dialog/aircraft etc) &lt;br /&gt;
var myCanvas = canvas.new({&lt;br /&gt;
  &amp;quot;name&amp;quot;: &amp;quot;Livery Test&amp;quot;,   # The name is optional but allow for easier identification&lt;br /&gt;
  &amp;quot;size&amp;quot;: [width, height], # Size of the underlying texture (should be a power of 2, required) [Resolution]&lt;br /&gt;
  &amp;quot;view&amp;quot;: [width, height],  # Virtual resolution (Defines the coordinate system of the canvas [Dimensions]&lt;br /&gt;
                        # which will be stretched the size of the texture, required)&lt;br /&gt;
  &amp;quot;mipmapping&amp;quot;: 1       # Enable mipmapping (optional)&lt;br /&gt;
});&lt;br /&gt;
&lt;br /&gt;
# set background color&lt;br /&gt;
myCanvas.set(&amp;quot;background&amp;quot;, canvas.style.getColor(&amp;quot;bg_color&amp;quot;));&lt;br /&gt;
&lt;br /&gt;
# creating the top-level/root group which will contain all other elements/group&lt;br /&gt;
var root = myCanvas.createGroup();&lt;br /&gt;
&lt;br /&gt;
# OPTIONAL: Create a Canvas dialog window to hold the canvas and show that it's working&lt;br /&gt;
# the Canvas is now standalone, i.e. continues to live once the dialog is closed!&lt;br /&gt;
var window = canvas.Window.new([width,height],&amp;quot;dialog&amp;quot;);&lt;br /&gt;
window.setCanvas(myCanvas);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Accessing the Canvas Desktop ==&lt;br /&gt;
Sometimes you may want to render to the main screen, without creating a separate Canvas window - this can be accomplished by using the Canvas desktop, note that the following example is fully self-contained, i.e. does not require any code to be added to work:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[Special:UploadWizard|Upload requested]] || &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
var myNode = canvas.getDesktop().createChild(&amp;quot;text&amp;quot;)&lt;br /&gt;
      .setText(&amp;quot;Hello Canvas Desktop&amp;quot;)&lt;br /&gt;
      .setFontSize(25, 1.0)          # font size (in texels) and font aspect ratio&lt;br /&gt;
      .setColor(1,0,0,1)             # red, fully opaque&lt;br /&gt;
      .setAlignment(&amp;quot;center-center&amp;quot;) # how the text is aligned to where you place it&lt;br /&gt;
      .setTranslation(160, 80);     # where to place the text&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Creating Tooltips ==&lt;br /&gt;
== Creating Popups ==&lt;br /&gt;
== Creating a Canvas GUI Window ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Snippets-canvas-dialog.png|left|thumb|This is what the [[Nasal]]/[[Canvas]] snippet will look like once you pasted it into the [[Nasal Console]] and click &amp;quot;Execute&amp;quot;.]] || {{Note|This example uses so called method chaining, if you're not familiar with the concept, please see: [[Object_Oriented_Programming_with_Nasal#More_on_methods:_Chaining|Method Chaining]].}}&lt;br /&gt;
&lt;br /&gt;
{{Canvas Snippets Boilerplate}}&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Adding Raster Images ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code &lt;br /&gt;
|-&lt;br /&gt;
| [[File:Canvas Snippets-raster image.png|thumb|Canvas snippet demonstrating how to load a raster image]] ||&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# path is relative to $FG_ROOT (base package)&lt;br /&gt;
var path = &amp;quot;Textures/Splash1.png&amp;quot;;&lt;br /&gt;
# create an image child for the texture&lt;br /&gt;
var child = root.createChild(&amp;quot;image&amp;quot;)&lt;br /&gt;
    .setFile(path)&lt;br /&gt;
    .setTranslation(100, 10)&lt;br /&gt;
    .setSize(130, 130);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Adding Raster Images downloaded on demand ==&lt;br /&gt;
&lt;br /&gt;
The '''path''' could also just as well be a URL, i.e. a raster image retrieved via http - for example, the following snippet is entirely self-contained and can be pasted into the [[Nasal Console]] and directly executed &amp;quot;as is&amp;quot;: &lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Canvas-raster-images-via-url.png|thumb|screen shot demonstrating how Nasal and Canvas can be used to display raster images downloaded on demand]] ||&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# create a new window, dimensions are 400 x 200, using the dialog decoration (i.e. titlebar)&lt;br /&gt;
var window = canvas.Window.new([400,200],&amp;quot;dialog&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
# adding a canvas to the new window and setting up background colors/transparency&lt;br /&gt;
var myCanvas = window.createCanvas().set(&amp;quot;background&amp;quot;, canvas.style.getColor(&amp;quot;bg_color&amp;quot;));&lt;br /&gt;
&lt;br /&gt;
# Using specific css colors would also be possible:&lt;br /&gt;
# myCanvas.set(&amp;quot;background&amp;quot;, &amp;quot;#ffaac0&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
# creating the top-level/root group which will contain all other elements/group&lt;br /&gt;
var root = myCanvas.createGroup();&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
# path now a URL&lt;br /&gt;
var url = &amp;quot;http://www.worldwidetelescope.org/docs/Images/MapOfEarth.jpg&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
# create an image child for the texture&lt;br /&gt;
var child=root.createChild(&amp;quot;image&amp;quot;)&lt;br /&gt;
    .setFile( url ) &lt;br /&gt;
    .setTranslation(45,22) # centered, in relation to dialog coordinates&lt;br /&gt;
    .setSize(310,155); # image dimensions&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Clipping ==&lt;br /&gt;
{{Note|{{FGCquote&lt;br /&gt;
|1= Scaling or any other type of transformation or changing the coordinates of individual points is definitely more efficient than clipping (which requires to change the OpenGL clip planes for every rendered object with a different clipping rectangle).&lt;br /&gt;
|2= {{cite web&lt;br /&gt;
  | url    = http://forum.flightgear.org/viewtopic.php?p=277062#p277062&lt;br /&gt;
  | title  = &amp;lt;nowiki&amp;gt;Re: Space Shuttle&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  | author = &amp;lt;nowiki&amp;gt;TheTom&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  | date   = Feb 21st, 2016&lt;br /&gt;
  | added   = Feb 21st, 2016&lt;br /&gt;
  | script_version = 0.25&lt;br /&gt;
  }}&lt;br /&gt;
}}&lt;br /&gt;
}}&lt;br /&gt;
I would suggest to refer to api.nas and look for &amp;quot;clip&amp;quot; and/or &amp;quot;rect&amp;quot; - IIRC, you need to set up a clipping rectangle by setting some kind of &amp;quot;clip&amp;quot; property and setting it to a rect value in the form of rect(...)&lt;br /&gt;
{{FGCquote&lt;br /&gt;
|1= For details, see the clipping example at: [[Canvas Nasal API#set 2]] and  [[Canvas Element#clip.28.29]]&lt;br /&gt;
|2= {{cite web&lt;br /&gt;
  | url    = http://forum.flightgear.org/viewtopic.php?p=276965#p276965&lt;br /&gt;
  | title  = &amp;lt;nowiki&amp;gt;Re: Space Shuttle&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  | author = &amp;lt;nowiki&amp;gt;Hooray&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  | date   = Feb 21st, 2016&lt;br /&gt;
  | added   = Feb 21st, 2016&lt;br /&gt;
  | script_version = 0.25&lt;br /&gt;
  }}&lt;br /&gt;
}}&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[File:MapStructure_dialog_with_clipping_and_event_handling_applied.png|thumb]] ||&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# create a new window, dimensions are 400 x 200, using the dialog decoration (i.e. titlebar)&lt;br /&gt;
var window = canvas.Window.new([400,200],&amp;quot;dialog&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
# adding a canvas to the new window and setting up background colors/transparency&lt;br /&gt;
var myCanvas = window.createCanvas().set(&amp;quot;background&amp;quot;, canvas.style.getColor(&amp;quot;bg_color&amp;quot;));&lt;br /&gt;
&lt;br /&gt;
# Using specific css colors would also be possible:&lt;br /&gt;
# myCanvas.set(&amp;quot;background&amp;quot;, &amp;quot;#ffaac0&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
# creating the top-level/root group which will contain all other elements/group&lt;br /&gt;
var root = myCanvas.createGroup();&lt;br /&gt;
&lt;br /&gt;
# create an image child for the texture&lt;br /&gt;
var child=root.createChild(&amp;quot;.........&amp;quot;)&lt;br /&gt;
    .setFile( url ) &lt;br /&gt;
    .setTranslation(45,22) # centered, in relation to dialog coordinates&lt;br /&gt;
    .setSize(310,155); # image dimensions&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Adding OpenVG Paths ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Openvg-via-canvas.png|thumb|screen shot showing a simple Canvas GUI dialog demonstrating how to use OpenVG-path drawing via Nasal and Canvas]] &lt;br /&gt;
| {{Note|This assumes that you already have a top-level root group set up, and named it '''root'''.}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
var graph = root.createChild(&amp;quot;group&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
var x_axis = graph.createChild(&amp;quot;path&amp;quot;, &amp;quot;x-axis&amp;quot;)&lt;br /&gt;
.moveTo(10, height/2)&lt;br /&gt;
.lineTo(width-10, height/2)&lt;br /&gt;
.setColor(1,0,0)&lt;br /&gt;
.setStrokeLineWidth(3);&lt;br /&gt;
&lt;br /&gt;
var y_axis = graph.createChild(&amp;quot;path&amp;quot;, &amp;quot;y-axis&amp;quot;)&lt;br /&gt;
.moveTo(10, 10)&lt;br /&gt;
.lineTo(10, height-10)&lt;br /&gt;
.setColor(0,0,1)&lt;br /&gt;
.setStrokeLineWidth(2);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Canvas-openvg-quadTo.png|thumb|screen shot showing a simple Canvas GUI dialog demonstrating how to use OpenVG-path drawing via Nasal and Canvas (Canvas/OpenVG quadTo API for drawing curves)]] &lt;br /&gt;
| {{Note|This assumes that you are appending this to the snippet shown above.}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
var points = [	60,height-20, &lt;br /&gt;
		230,height-100,&lt;br /&gt;
		];&lt;br /&gt;
&lt;br /&gt;
var track = graph.createChild(&amp;quot;path&amp;quot;, &amp;quot;track&amp;quot;)&lt;br /&gt;
.moveTo(10, height/2)&lt;br /&gt;
.quadTo( points )&lt;br /&gt;
.setColor(0,1,0)&lt;br /&gt;
.setStrokeLineWidth(4);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Canvas-openvg-cubicTo.png|thumb|screen shot showing a simple Canvas GUI dialog demonstrating how to use OpenVG-path drawing via Nasal and Canvas (Canvas/OpenVG cubicTo API for drawing curves)]] &lt;br /&gt;
| {{Note|This assumes that you are appending this to the snippet shown above.}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
var points = [	60,height-20, &lt;br /&gt;
		120,height-120,&lt;br /&gt;
		230,height-100,&lt;br /&gt;
		];&lt;br /&gt;
&lt;br /&gt;
var track = graph.createChild(&amp;quot;path&amp;quot;, &amp;quot;track&amp;quot;)&lt;br /&gt;
.moveTo(10, height/2)&lt;br /&gt;
.cubicTo( points )&lt;br /&gt;
.setColor(0,1,0)&lt;br /&gt;
.setStrokeLineWidth(4);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Adding Vector Images ==&lt;br /&gt;
&lt;br /&gt;
[[Category:Canvas SVG]]&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Canvas-svg-support.png|thumb|screen shot demonstrating how the scripted Nasal-based SVG parser can be used to dynamically turn SVG files into OpenVG instructions understood by Canvas]] &lt;br /&gt;
| {{Note|This assumes that you already have a top-level root group set up, and named it '''root'''.}}&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
# change the background color &lt;br /&gt;
myCanvas.set(&amp;quot;background&amp;quot;, &amp;quot;#ffaac0&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
# creating the top-level/root group which will contain all other elements/group&lt;br /&gt;
var root = myCanvas.createGroup();&lt;br /&gt;
&lt;br /&gt;
var filename = &amp;quot;/Nasal/canvas/map/Images/boeingAirplane.svg&amp;quot;;&lt;br /&gt;
var svg_symbol = root.createChild('group');&lt;br /&gt;
canvas.parsesvg(svg_symbol, filename);&lt;br /&gt;
&lt;br /&gt;
svg_symbol.setTranslation(width/2,height/2);&lt;br /&gt;
&lt;br /&gt;
#svg_symbol.setScale(0.2);&lt;br /&gt;
#svg_symbol.setRotation(radians)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Using the SymbolCache ==&lt;br /&gt;
Whenever a symbol may need to be shown/instanced multiple times (possibly using different styling), it makes sense to use caching - otherwise, identical symbols would be treated as separate OpenVG groups, all of which would need to be rasterized/rendedered separately (i.e. 100 identical symbols would be updated/rendered one by one). &lt;br /&gt;
&lt;br /&gt;
Typically, a map may display multiple instances of an otherwise identical symbol (think VOR, NDB, DME etc) - equally, a multiplayer map may showing multiple aircraft symbols at the same time. In these cases, it makes sense to use the SymbolCache framework, which will render symbols into a separate Canvas texture and provide a texture map that can be treated as a lookup map, which even supports styling for otherwise identical symbols. To learn more, please refer to [[Canvas MapStructure#The_SymbolCache|SymbolCache]]&lt;br /&gt;
... &lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Canvas-SymbolCache-Instancing.png|thumb|Screen shot showing a Canvas based GUI dialog that is using the SymbolCache for instancing multiple symbols (including support for styling)]] &lt;br /&gt;
| &amp;lt;!--{{Caution|This is currently untested code}}--&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
##&lt;br /&gt;
# this is the function that draws a symbol using OpenVG paths&lt;br /&gt;
# it accepts a group to draw to and returns the rendered group &lt;br /&gt;
# to the caller&lt;br /&gt;
var drawVOR = func(group) {&lt;br /&gt;
    return group.createChild(&amp;quot;path&amp;quot;)&lt;br /&gt;
        .moveTo(-15,0)&lt;br /&gt;
        .lineTo(-7.5,12.5)&lt;br /&gt;
        .lineTo(7.5,12.5)&lt;br /&gt;
        .lineTo(15,0)&lt;br /&gt;
        .lineTo(7.5,-12.5)&lt;br /&gt;
        .lineTo(-7.5,-12.5)&lt;br /&gt;
        .close()&lt;br /&gt;
        .setStrokeLineWidth(line_width) # style-able&lt;br /&gt;
        .setColor(color); # style-able&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
var style = { # styling related attributes (as per the draw* function above)&lt;br /&gt;
    line_width: 3,&lt;br /&gt;
    scale_factor: 1,&lt;br /&gt;
    color: [1,0,0],&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
# create a new cache entry for the styled symbol&lt;br /&gt;
var myCachedSymbol = canvas.StyleableCacheable.new(&lt;br /&gt;
    name:'myVOR', draw_func: drawVOR,&lt;br /&gt;
    cache: canvas.SymbolCache32x32, # the cache to be used&lt;br /&gt;
    draw_mode: canvas.SymbolCache.DRAW_CENTERED,&lt;br /&gt;
    relevant_keys: ['line_width', 'color'], # styling related attributes&lt;br /&gt;
);&lt;br /&gt;
&lt;br /&gt;
var target = root.createChild('group');&lt;br /&gt;
&lt;br /&gt;
var x=0;&lt;br /&gt;
var y=height/2;&lt;br /&gt;
&lt;br /&gt;
var xoffset=50;&lt;br /&gt;
&lt;br /&gt;
# render 5 instanced symbols using the style specified above&lt;br /&gt;
for (var i=0;i&amp;lt;5;i+=1) {&lt;br /&gt;
# look up the raster image for the symbol&lt;br /&gt;
# render it using the passed style and adjust scaling&lt;br /&gt;
var instanced = myCachedSymbol.render(target, style)&lt;br /&gt;
  .setScale(style.scale_factor)&lt;br /&gt;
  .setTranslation(x+=xoffset,y);&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The example shown above uses a fixed symbol/icon cache that is set up while booting Flightgear - sometimes, we may need cache for different purposes. So, let's assume, we need a new/custom cache with a different resolution for each entry in the cache (e.g. 256x256), we can easily accomplish that by setting up a new cache like this: &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
var mySymbolCache256x256 = canvas.SymbolCache.new(1024,256);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Adding Text Elements ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[File:CanvasText-Hello-World.png|thumb|Screen shot showing the CanvasText example contributed by Necolatis]]&lt;br /&gt;
| {{Note|This assumes that you already have a top-level root group set up, and named it '''root'''.}}&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
var myText = root.createChild(&amp;quot;text&amp;quot;)&lt;br /&gt;
      .setText(&amp;quot;Hello world!&amp;quot;)&lt;br /&gt;
      .setFontSize(20, 0.9)          # font size (in texels) and font aspect ratio&lt;br /&gt;
      .setColor(1,0,0,1)             # red, fully opaque&lt;br /&gt;
      .setAlignment(&amp;quot;center-center&amp;quot;) # how the text is aligned to where you place it&lt;br /&gt;
      .setTranslation(160, 80);     # where to place the text&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Adding GUI Labels ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Canvas-Layout-Label-example-by-Necolatis.png|thumb|Canvas demo: Layouts and Labels (by Necolatis)]]&lt;br /&gt;
| {{Note|This assumes that you already have a top-level root group set up, and named it '''root''', just change this variable if you want it to be rendered elsewhere. It also assumes you have a Layout item setup and called '''myLayoutItem'''.}}&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
# create a new layout&lt;br /&gt;
var myLayout = canvas.HBoxLayout.new();&lt;br /&gt;
# assign it to the Canvas&lt;br /&gt;
myCanvas.setLayout(myLayout);&lt;br /&gt;
&lt;br /&gt;
var label = canvas.gui.widgets.Label.new(root, canvas.style, {wordWrap: 0}); # wordwrap: 0 will disable wordwrapping, to enable it use 1 instead&lt;br /&gt;
label.setText(&amp;quot;Hello World!&amp;quot;);&lt;br /&gt;
myLayout.addItem(label);&lt;br /&gt;
&lt;br /&gt;
var label2 = canvas.gui.widgets.Label.new(root, canvas.style, {wordWrap: 0}); # wordwrap: 0 will disable wordwrapping, to enable it use 1 instead&lt;br /&gt;
label2.setText(&amp;quot;Hello FlightGear&amp;quot;);&lt;br /&gt;
myLayout.addItem(label2);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Adding GUI Buttons (Layouts)==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Canvas-demo-layouts-and-buttons-by-Necolatis.png|thumb|Canvas snippet: buttons and layouts (by Necolatis)]] &lt;br /&gt;
|&lt;br /&gt;
&lt;br /&gt;
{{Note|This assumes that you already have a top-level root group set up, and named it '''root''', just change this variable if you want it to be rendered elsewhere. It also assumes you have a Layout item setup and called '''myLayoutItem'''.}}&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
# create a new layout&lt;br /&gt;
var myLayout = canvas.HBoxLayout.new();&lt;br /&gt;
# assign it to the Canvas&lt;br /&gt;
myCanvas.setLayout(myLayout);&lt;br /&gt;
&lt;br /&gt;
# click button&lt;br /&gt;
&lt;br /&gt;
var button = canvas.gui.widgets.Button.new(root, canvas.style, {})&lt;br /&gt;
	.setText(&amp;quot;Click on me&amp;quot;)&lt;br /&gt;
	.setFixedSize(75, 25);&lt;br /&gt;
&lt;br /&gt;
button.listen(&amp;quot;clicked&amp;quot;, func {&lt;br /&gt;
        # add code here to react on click on button.&lt;br /&gt;
print(&amp;quot;Button clicked !&amp;quot;);&lt;br /&gt;
});&lt;br /&gt;
&lt;br /&gt;
myLayout.addItem(button);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Canvas-toggle-button-snippet-by-Necolatis.png|thumb|Canvas toggle button demo by Necolatis]]&lt;br /&gt;
| &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
# create a new layout&lt;br /&gt;
var myLayout = canvas.HBoxLayout.new();&lt;br /&gt;
# assign it to the Canvas&lt;br /&gt;
myCanvas.setLayout(myLayout);&lt;br /&gt;
&lt;br /&gt;
var button = canvas.gui.widgets.Button.new(root, canvas.style, {})&lt;br /&gt;
        .setText(&amp;quot;Toggle me&amp;quot;)&lt;br /&gt;
        .setCheckable(1) # this indicates that is should be a toggle button&lt;br /&gt;
        .setChecked(0) # depressed by default&lt;br /&gt;
        .setFixedSize(75, 25);&lt;br /&gt;
&lt;br /&gt;
button.listen(&amp;quot;toggled&amp;quot;, func (e) {&lt;br /&gt;
        if( e.detail.checked ) {&lt;br /&gt;
            # add code here to react on button being depressed.&lt;br /&gt;
        } else {&lt;br /&gt;
            # add code here to react on button not being depressed.&lt;br /&gt;
        }&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
myLayout.addItem(button);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Canvas Input Dialog ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Snippets-canvas-input-dialog.png|thumb|Canvas input dialog]]&lt;br /&gt;
||&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
# create a new InputDialog with a title, label, and a callback&lt;br /&gt;
canvas.InputDialog.getText(&amp;quot;Input Dialog Title&amp;quot;, &amp;quot;Please enter some text&amp;quot;, func(btn,value) {&lt;br /&gt;
    if (value) gui.popupTip(&amp;quot;You entered: &amp;quot;~value);&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Canvas ScrollArea ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Canvas-snippets-scrollArea-demo.png|thumb|Screen shot showing a Canvas ScrollArea populated with different splash screens, loaded from $FG_ROOT/Textures]]&lt;br /&gt;
||&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
var vbox = canvas.VBoxLayout.new();&lt;br /&gt;
myCanvas.setLayout(vbox);&lt;br /&gt;
&lt;br /&gt;
 var scroll = canvas.gui.widgets.ScrollArea.new(root, canvas.style, {size: [96, 128]}).move(20, 100);&lt;br /&gt;
 vbox.addItem(scroll, 1);&lt;br /&gt;
&lt;br /&gt;
var scrollContent =&lt;br /&gt;
      scroll.getContent()&lt;br /&gt;
            .set(&amp;quot;font&amp;quot;, &amp;quot;LiberationFonts/LiberationSans-Bold.ttf&amp;quot;)&lt;br /&gt;
            .set(&amp;quot;character-size&amp;quot;, 16)&lt;br /&gt;
            .set(&amp;quot;alignment&amp;quot;, &amp;quot;left-center&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
var list = canvas.VBoxLayout.new();&lt;br /&gt;
scroll.setLayout(list);&lt;br /&gt;
&lt;br /&gt;
for (var i=1;i&amp;lt;=5;i+=1) {&lt;br /&gt;
var label = canvas.gui.widgets.Label.new(scrollContent, canvas.style, {wordWrap: 0}); &lt;br /&gt;
label.setImage(&amp;quot;Textures/Splash&amp;quot;~i~&amp;quot;.png&amp;quot;);&lt;br /&gt;
label.setFixedSize(256,256);&lt;br /&gt;
list.addItem(label);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Using Layouts ==&lt;br /&gt;
&lt;br /&gt;
== Using Styling ==&lt;br /&gt;
== Adding a HUD ==&lt;br /&gt;
== Adding a 2D Instrument ==&lt;br /&gt;
== Adding a 2D Panel ==&lt;br /&gt;
== Adding a PFD ==&lt;br /&gt;
&lt;br /&gt;
== Adding a Failure Mgmt Widget ==&lt;br /&gt;
{{Note|This kind of widget will typically be useful for dialogs requiring a method for managing aircraft specific system failures (e.g. an instructor console).}}&lt;br /&gt;
&lt;br /&gt;
== Adding a MapStructure map to a Canvas  ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code &lt;br /&gt;
|-&lt;br /&gt;
| [[File:Snippets-canvas-mapstructure-dialog.png|thumb|[[MapStructure]] layers shown in a Canvas GUI dialog]]&lt;br /&gt;
| {{Note|This assumes that you already have a top-level root group set up, and named it '''root''', just change this variable if you want it to be rendered elsewhere. Equally, this snippet assumes that your canvas is named '''myCanvas'''.}}&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
var TestMap = root.createChild(&amp;quot;map&amp;quot;);&lt;br /&gt;
TestMap.setController(&amp;quot;Aircraft position&amp;quot;);&lt;br /&gt;
TestMap.setRange(25); &lt;br /&gt;
 &lt;br /&gt;
TestMap.setTranslation(    myCanvas.get(&amp;quot;view[0]&amp;quot;)/2,&lt;br /&gt;
                           myCanvas.get(&amp;quot;view[1]&amp;quot;)/2&lt;br /&gt;
                        );&lt;br /&gt;
var r = func(name,vis=1,zindex=nil) return caller(0)[0];&lt;br /&gt;
&lt;br /&gt;
foreach(var type; [r('APT'), r('VOR') ] )&lt;br /&gt;
 TestMap.addLayer(factory: canvas.SymbolLayer, type_arg: type.name, visible: type.vis, priority: type.zindex,);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Customizing MapStructure Styling  ==&lt;br /&gt;
{{See also|Canvas MapStructure#Styling}}&lt;br /&gt;
{{WIP}}&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code &lt;br /&gt;
|-&lt;br /&gt;
| [[File:Layers style change.png|thumb|[[MapStructure]] layers shown in a Canvas GUI dialog]]&lt;br /&gt;
| {{Note|This assumes that you already have a top-level root group set up, and named it '''root''', just change this variable if you want it to be rendered elsewhere. Equally, this snippet assumes that your canvas is named '''myCanvas'''.}}&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
var TestMap = root.createChild(&amp;quot;map&amp;quot;);&lt;br /&gt;
TestMap.setController(&amp;quot;Aircraft position&amp;quot;);&lt;br /&gt;
TestMap.setRange(25); &lt;br /&gt;
 &lt;br /&gt;
TestMap.setTranslation(    myCanvas.get(&amp;quot;view[0]&amp;quot;)/2,&lt;br /&gt;
                           myCanvas.get(&amp;quot;view[1]&amp;quot;)/2&lt;br /&gt;
                        );&lt;br /&gt;
var r = func(name,vis=1,zindex=nil) return caller(0)[0];&lt;br /&gt;
var type = r('APT');&lt;br /&gt;
var style_apt = {&lt;br /&gt;
    scale_factor:0.5, # 50 %&lt;br /&gt;
    color_default:[0,1,0.9], # rgb&lt;br /&gt;
    line_width:4, # thickness&lt;br /&gt;
    label_font_color:[0,1,0.9], # rgb&lt;br /&gt;
    label_font_size:30 # font size&lt;br /&gt;
};&lt;br /&gt;
TestMap.addLayer(factory: canvas.SymbolLayer, type_arg: type.name, visible: type.vis, priority: type.zindex,style:style_apt);&lt;br /&gt;
&lt;br /&gt;
var type = r('VOR');&lt;br /&gt;
var style_vor = {&lt;br /&gt;
    scale_factor:0.6,&lt;br /&gt;
    active_color:[1,0,0],&lt;br /&gt;
    inactive_color:[0,1,0],&lt;br /&gt;
    line_width:4,&lt;br /&gt;
};&lt;br /&gt;
TestMap.addLayer(factory: canvas.SymbolLayer, type_arg: type.name, visible: type.vis, priority: type.zindex,style:style_vor);&lt;br /&gt;
&lt;br /&gt;
### See ...fgdata/Nasal/canvas/map/APT.symbol   ...fgdata/Nasal/canvas/map/VOR.style ... for the style variables that can be configured.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Using MapStructure and Overlays ==&lt;br /&gt;
{{FGCquote&lt;br /&gt;
  |Support for overlays using geo-referenced raster images isn't too well-developed currently, so would need to work with a few hard-coded assumptions (e.g. being specific to a certain raster image and map layout) - but maybe TheTom can provide a few more ideas on how to proceed from here - otherwise, the Map is normally not aware of any non-Map items, i.e. as long as the overlay is added &amp;quot;outside&amp;quot; the Map, it isn't even aware of the raster image - and when it is added as a map element, it would probably be rotated. So there's still some work needed here. But generally, this should work well enough even in its current form (the screen shot is purely based on the Canvas Snippets article, i.e. it just displays a Canvas GUI dialog, adds the downloaded raster image and then adds the MapStructure APS layer - without the Map being aware of the overlay/geo-referencing that is needed currently).&lt;br /&gt;
  |{{cite web |url=http://forum.flightgear.org/viewtopic.php?p=238316#p238316&lt;br /&gt;
     |title=&amp;lt;nowiki&amp;gt;Re: Using Canvas for visualizing orbital flights (cont'd PM)&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     |author=&amp;lt;nowiki&amp;gt;Hooray&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     |date=&amp;lt;nowiki&amp;gt;Fri Apr 10&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
   }}&lt;br /&gt;
}}&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code &lt;br /&gt;
|-&lt;br /&gt;
| [[File:Canvas-overlay-with-mapstructure.png|thumb|Using the [[MapStructure]] framework in conjunction with raster images as overlays]]&lt;br /&gt;
| {{Note|This assumes that you already have a top-level root group set up, and named it '''root''', just change this variable if you want it to be rendered elsewhere. Equally, this snippet assumes that your canvas is named '''myCanvas'''.}}&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== A simple tile map ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code &lt;br /&gt;
|-&lt;br /&gt;
| [[File:Canvas - Tile map demo.png|thumb|A simple, canvas based tile map which is centered around the aircraft.]]&lt;br /&gt;
| &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
var (width,height) = (768,512);&lt;br /&gt;
var tile_size = 256;&lt;br /&gt;
&lt;br /&gt;
var window = canvas.Window.new([width, height],&amp;quot;dialog&amp;quot;)&lt;br /&gt;
                   .set('title', &amp;quot;Tile map demo&amp;quot;);&lt;br /&gt;
var g = window.getCanvas(1)&lt;br /&gt;
              .createGroup();&lt;br /&gt;
&lt;br /&gt;
# Simple user interface (Buttons for zoom and label for displaying it)&lt;br /&gt;
var zoom = 10;&lt;br /&gt;
var type = &amp;quot;map&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
var ui_root = window.getCanvas().createGroup();&lt;br /&gt;
var vbox = canvas.VBoxLayout.new();&lt;br /&gt;
window.setLayout(vbox);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
var button_in = canvas.gui.widgets.Button.new(ui_root, canvas.style, {})&lt;br /&gt;
                                  .setText(&amp;quot;+&amp;quot;)&lt;br /&gt;
                                  .listen(&amp;quot;clicked&amp;quot;, func changeZoom(1));&lt;br /&gt;
var button_out = canvas.gui.widgets.Button.new(ui_root, canvas.style, {})&lt;br /&gt;
                                  .setText(&amp;quot;-&amp;quot;)&lt;br /&gt;
                                  .listen(&amp;quot;clicked&amp;quot;, func changeZoom(-1));&lt;br /&gt;
button_in.setSizeHint([32, 32]);&lt;br /&gt;
button_out.setSizeHint([32, 32]);&lt;br /&gt;
&lt;br /&gt;
var label_zoom = canvas.gui.widgets.Label.new(ui_root, canvas.style, {});&lt;br /&gt;
&lt;br /&gt;
var button_box = canvas.HBoxLayout.new();&lt;br /&gt;
button_box.addItem(button_in);&lt;br /&gt;
button_box.addItem(label_zoom);&lt;br /&gt;
button_box.addItem(button_out);&lt;br /&gt;
button_box.addStretch(1);&lt;br /&gt;
&lt;br /&gt;
vbox.addItem(button_box);&lt;br /&gt;
vbox.addStretch(1);&lt;br /&gt;
&lt;br /&gt;
var changeZoom = func(d)&lt;br /&gt;
{&lt;br /&gt;
  zoom = math.max(2, math.min(19, zoom + d));&lt;br /&gt;
  label_zoom.setText(&amp;quot;Zoom &amp;quot; ~ zoom);&lt;br /&gt;
  updateTiles();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# http://polymaps.org/docs/&lt;br /&gt;
# https://github.com/simplegeo/polymaps&lt;br /&gt;
# https://github.com/Leaflet/Leaflet&lt;br /&gt;
&lt;br /&gt;
var maps_base = getprop(&amp;quot;/sim/fg-home&amp;quot;) ~ '/cache/maps';&lt;br /&gt;
&lt;br /&gt;
# http://otile1.mqcdn.com/tiles/1.0.0/map&lt;br /&gt;
# http://otile1.mqcdn.com/tiles/1.0.0/sat&lt;br /&gt;
# (also see http://wiki.openstreetmap.org/wiki/Tile_usage_policy)&lt;br /&gt;
var makeUrl =&lt;br /&gt;
  string.compileTemplate('http://otile1.mqcdn.com/tiles/1.0.0/{type}/{z}/{x}/{y}.jpg');&lt;br /&gt;
var makePath =&lt;br /&gt;
  string.compileTemplate(maps_base ~ '/osm-{type}/{z}/{x}/{y}.jpg');&lt;br /&gt;
var num_tiles = [4, 3];&lt;br /&gt;
&lt;br /&gt;
var center_tile_offset = [&lt;br /&gt;
  (num_tiles[0] - 1) / 2,&lt;br /&gt;
  (num_tiles[1] - 1) / 2&lt;br /&gt;
];&lt;br /&gt;
&lt;br /&gt;
# simple aircraft icon at current position/center of the map&lt;br /&gt;
g.createChild(&amp;quot;path&amp;quot;)&lt;br /&gt;
 .moveTo( tile_size * center_tile_offset[0] - 10,&lt;br /&gt;
          tile_size * center_tile_offset[1] )&lt;br /&gt;
 .horiz(20)&lt;br /&gt;
 .move(-10,-10)&lt;br /&gt;
 .vert(20)&lt;br /&gt;
 .set(&amp;quot;stroke&amp;quot;, &amp;quot;red&amp;quot;)&lt;br /&gt;
 .set(&amp;quot;stroke-width&amp;quot;, 2)&lt;br /&gt;
 .set(&amp;quot;z-index&amp;quot;, 1);&lt;br /&gt;
&lt;br /&gt;
##&lt;br /&gt;
# initialize the map by setting up&lt;br /&gt;
# a grid of raster images  &lt;br /&gt;
&lt;br /&gt;
var tiles = setsize([], num_tiles[0]);&lt;br /&gt;
for(var x = 0; x &amp;lt; num_tiles[0]; x += 1)&lt;br /&gt;
{&lt;br /&gt;
  tiles[x] = setsize([], num_tiles[1]);&lt;br /&gt;
  for(var y = 0; y &amp;lt; num_tiles[1]; y += 1)&lt;br /&gt;
    tiles[x][y] = g.createChild(&amp;quot;image&amp;quot;, &amp;quot;map-tile&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var last_tile = [-1,-1];&lt;br /&gt;
var last_type = type;&lt;br /&gt;
&lt;br /&gt;
##&lt;br /&gt;
# this is the callback that will be regularly called by the timer&lt;br /&gt;
# to update the map&lt;br /&gt;
var updateTiles = func()&lt;br /&gt;
{&lt;br /&gt;
  # get current position&lt;br /&gt;
  var lat = getprop('/position/latitude-deg');&lt;br /&gt;
  var lon = getprop('/position/longitude-deg');&lt;br /&gt;
&lt;br /&gt;
  var n = math.pow(2, zoom);&lt;br /&gt;
  var offset = [&lt;br /&gt;
    n * ((lon + 180) / 360) - center_tile_offset[0],&lt;br /&gt;
    (1 - math.ln(math.tan(lat * math.pi/180) + 1 / math.cos(lat * math.pi/180)) / math.pi) / 2 * n - center_tile_offset[1]&lt;br /&gt;
  ];&lt;br /&gt;
  var tile_index = [int(offset[0]), int(offset[1])];&lt;br /&gt;
&lt;br /&gt;
  var ox = tile_index[0] - offset[0];&lt;br /&gt;
  var oy = tile_index[1] - offset[1];&lt;br /&gt;
&lt;br /&gt;
  for(var x = 0; x &amp;lt; num_tiles[0]; x += 1)&lt;br /&gt;
    for(var y = 0; y &amp;lt; num_tiles[1]; y += 1)&lt;br /&gt;
      tiles[x][y].setTranslation(int((ox + x) * tile_size + 0.5), int((oy + y) * tile_size + 0.5));&lt;br /&gt;
&lt;br /&gt;
  if(    tile_index[0] != last_tile[0]&lt;br /&gt;
      or tile_index[1] != last_tile[1]&lt;br /&gt;
      or type != last_type )&lt;br /&gt;
  {&lt;br /&gt;
    for(var x = 0; x &amp;lt; num_tiles[0]; x += 1)&lt;br /&gt;
      for(var y = 0; y &amp;lt; num_tiles[1]; y += 1)&lt;br /&gt;
      {&lt;br /&gt;
        var pos = {&lt;br /&gt;
          z: zoom,&lt;br /&gt;
          x: int(offset[0] + x),&lt;br /&gt;
          y: int(offset[1] + y),&lt;br /&gt;
          type: type&lt;br /&gt;
        };&lt;br /&gt;
&lt;br /&gt;
        (func {&lt;br /&gt;
        var img_path = makePath(pos);&lt;br /&gt;
        var tile = tiles[x][y];&lt;br /&gt;
&lt;br /&gt;
        if( io.stat(img_path) == nil )&lt;br /&gt;
        { # image not found, save in $FG_HOME&lt;br /&gt;
          var img_url = makeUrl(pos);&lt;br /&gt;
          print('requesting ' ~ img_url);&lt;br /&gt;
          http.save(img_url, img_path)&lt;br /&gt;
              .done(func {print('received image ' ~ img_path); tile.set(&amp;quot;src&amp;quot;, img_path);})&lt;br /&gt;
              .fail(func (r) print('Failed to get image ' ~ img_path ~ ' ' ~ r.status ~ ': ' ~ r.reason));&lt;br /&gt;
        }&lt;br /&gt;
        else # cached image found, reusing&lt;br /&gt;
        {&lt;br /&gt;
          print('loading ' ~ img_path);&lt;br /&gt;
          tile.set(&amp;quot;src&amp;quot;, img_path)&lt;br /&gt;
        }&lt;br /&gt;
        })();&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
    last_tile = tile_index;&lt;br /&gt;
    last_type = type;&lt;br /&gt;
  }&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
##&lt;br /&gt;
# set up a timer that will invoke updateTiles at 2-second intervals&lt;br /&gt;
var update_timer = maketimer(2, updateTiles);&lt;br /&gt;
# actually start the timer&lt;br /&gt;
update_timer.start();&lt;br /&gt;
&lt;br /&gt;
##&lt;br /&gt;
# set up default zoom level&lt;br /&gt;
changeZoom(0);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
###&lt;br /&gt;
# The following lines were recently added and have not yet been tested&lt;br /&gt;
# (if in doubt, remove them)&lt;br /&gt;
window.del = func()&lt;br /&gt;
{&lt;br /&gt;
  print(&amp;quot;Cleaning up window:&amp;quot;,title,&amp;quot;\n&amp;quot;);&lt;br /&gt;
  update_timer.stop();&lt;br /&gt;
# explanation for the call() technique at: http://wiki.flightgear.org/Object_oriented_programming_in_Nasal#Making_safer_base-class_calls&lt;br /&gt;
  call(canvas.Window.del, [], me);&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Adding a NavDisplay ==&lt;br /&gt;
&lt;br /&gt;
== Adding a CDU ==&lt;br /&gt;
&lt;br /&gt;
== Adding a Multi-Function Display (MFD) ==&lt;/div&gt;</summary>
		<author><name>Clm76</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=File:Layers_style_change.png&amp;diff=105435</id>
		<title>File:Layers style change.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=File:Layers_style_change.png&amp;diff=105435"/>
		<updated>2016-10-29T07:31:04Z</updated>

		<summary type="html">&lt;p&gt;Clm76: User created page with UploadWizard&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=={{int:filedesc}}==&lt;br /&gt;
{{Information&lt;br /&gt;
|description={{en|1=MapStructure layers shown in a canvas GUI dialog}}&lt;br /&gt;
|date=2016-10-29&lt;br /&gt;
|source={{own}}&lt;br /&gt;
|author=[[User:Clm76|Clm76]]&lt;br /&gt;
|permission=&lt;br /&gt;
|other versions=&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=={{int:license-header}}==&lt;br /&gt;
{{self|cc-by-sa-4.0}}&lt;/div&gt;</summary>
		<author><name>Clm76</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Canvas_snippets&amp;diff=105427</id>
		<title>Canvas snippets</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Canvas_snippets&amp;diff=105427"/>
		<updated>2016-10-28T07:41:34Z</updated>

		<summary type="html">&lt;p&gt;Clm76: /* Customizing MapStructure Styling */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Stub}}&lt;br /&gt;
{{-}}&lt;br /&gt;
This article is meant to be a collection of self-contained code snippets for doing Nasal/Canvas coding without having to spend too much time working through APIs, documentation and existing code - the idea is to provide snippets that contain comments and explicit assumptions, so that these snippets can be easily adapted by people without necessarily having to be very familiar with Nasal/Canvas coding. In the long term, we're hoping to grow a library of useful code snippets to cover most use-cases, while also covering more complex scenarios, e.g. using Canvas-specific helper frameworks like [[Canvas MapStructure]], the [[NavDisplay]] or the recent CDU work.&lt;br /&gt;
&lt;br /&gt;
Canvas itself is designed to be front-end agnostic, meaning that it doesn't matter where a Canvas is used/displayed - this is accomplished by so called &amp;quot;placements&amp;quot;, which determine where a Canvas texture shown. Thus, the back-end logic can remain the same usually, so that GUI dialogs may show avionics, but also so that avionics may show GUI widgets, which also applies to HUDs, liveries and even placements within the FlightGear scenery.&lt;br /&gt;
&lt;br /&gt;
We encourage people to use the snippets listed below to get started with Nasal/Canvas coding, but also to provide feedback on extending/improving this article to make it even more accessible and useful. Equally, if you're aware of any Canvas-related efforts that may contain useful code snippets, please do feel free to add those snippets here. Even people who don't have any interest in coding itself are encouraged to get involved by helping test the snippets listed below, for which you only need to the [[Nasal Console]], and while you're at it, please also help provide/update screen shots for each code snippet.&lt;br /&gt;
&lt;br /&gt;
Contributions added to this article should ideally satisfy some requirements:&lt;br /&gt;
* be entirely self-contained&lt;br /&gt;
* contain good/clear comments&lt;br /&gt;
* have pointers/references to related code/use-cases&lt;br /&gt;
* make assumptions explicit&lt;br /&gt;
* contain screen shots for each example&lt;br /&gt;
* avoid overloaded symbol names to ensure that examples can be merged and adapted easily&lt;br /&gt;
* dependencies (svg files, images/textures etc) should always be chosen such that snippets always work using $FG_ROOT&lt;br /&gt;
* hard-coded assumptions (e.g. texture dimensions in terms of width/height etc) should be encapsulated using variables&lt;br /&gt;
 &lt;br /&gt;
{{Canvas Navigation}}&lt;br /&gt;
&lt;br /&gt;
== Creating a standalone Canvas ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[Special:UploadWizard|Upload requested]] || &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
var (width, height) = (512,512);&lt;br /&gt;
# Create a standalone Canvas (not attached to any GUI dialog/aircraft etc) &lt;br /&gt;
var myCanvas = canvas.new({&lt;br /&gt;
  &amp;quot;name&amp;quot;: &amp;quot;Livery Test&amp;quot;,   # The name is optional but allow for easier identification&lt;br /&gt;
  &amp;quot;size&amp;quot;: [width, height], # Size of the underlying texture (should be a power of 2, required) [Resolution]&lt;br /&gt;
  &amp;quot;view&amp;quot;: [width, height],  # Virtual resolution (Defines the coordinate system of the canvas [Dimensions]&lt;br /&gt;
                        # which will be stretched the size of the texture, required)&lt;br /&gt;
  &amp;quot;mipmapping&amp;quot;: 1       # Enable mipmapping (optional)&lt;br /&gt;
});&lt;br /&gt;
&lt;br /&gt;
# set background color&lt;br /&gt;
myCanvas.set(&amp;quot;background&amp;quot;, canvas.style.getColor(&amp;quot;bg_color&amp;quot;));&lt;br /&gt;
&lt;br /&gt;
# creating the top-level/root group which will contain all other elements/group&lt;br /&gt;
var root = myCanvas.createGroup();&lt;br /&gt;
&lt;br /&gt;
# OPTIONAL: Create a Canvas dialog window to hold the canvas and show that it's working&lt;br /&gt;
# the Canvas is now standalone, i.e. continues to live once the dialog is closed!&lt;br /&gt;
var window = canvas.Window.new([width,height],&amp;quot;dialog&amp;quot;);&lt;br /&gt;
window.setCanvas(myCanvas);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Accessing the Canvas Desktop ==&lt;br /&gt;
Sometimes you may want to render to the main screen, without creating a separate Canvas window - this can be accomplished by using the Canvas desktop, note that the following example is fully self-contained, i.e. does not require any code to be added to work:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[Special:UploadWizard|Upload requested]] || &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
var myNode = canvas.getDesktop().createChild(&amp;quot;text&amp;quot;)&lt;br /&gt;
      .setText(&amp;quot;Hello Canvas Desktop&amp;quot;)&lt;br /&gt;
      .setFontSize(25, 1.0)          # font size (in texels) and font aspect ratio&lt;br /&gt;
      .setColor(1,0,0,1)             # red, fully opaque&lt;br /&gt;
      .setAlignment(&amp;quot;center-center&amp;quot;) # how the text is aligned to where you place it&lt;br /&gt;
      .setTranslation(160, 80);     # where to place the text&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Creating Tooltips ==&lt;br /&gt;
== Creating Popups ==&lt;br /&gt;
== Creating a Canvas GUI Window ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Snippets-canvas-dialog.png|left|thumb|This is what the [[Nasal]]/[[Canvas]] snippet will look like once you pasted it into the [[Nasal Console]] and click &amp;quot;Execute&amp;quot;.]] || {{Note|This example uses so called method chaining, if you're not familiar with the concept, please see: [[Object_Oriented_Programming_with_Nasal#More_on_methods:_Chaining|Method Chaining]].}}&lt;br /&gt;
&lt;br /&gt;
{{Canvas Snippets Boilerplate}}&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Adding Raster Images ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code &lt;br /&gt;
|-&lt;br /&gt;
| [[File:Canvas Snippets-raster image.png|thumb|Canvas snippet demonstrating how to load a raster image]] ||&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# path is relative to $FG_ROOT (base package)&lt;br /&gt;
var path = &amp;quot;Textures/Splash1.png&amp;quot;;&lt;br /&gt;
# create an image child for the texture&lt;br /&gt;
var child = root.createChild(&amp;quot;image&amp;quot;)&lt;br /&gt;
    .setFile(path)&lt;br /&gt;
    .setTranslation(100, 10)&lt;br /&gt;
    .setSize(130, 130);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Adding Raster Images downloaded on demand ==&lt;br /&gt;
&lt;br /&gt;
The '''path''' could also just as well be a URL, i.e. a raster image retrieved via http - for example, the following snippet is entirely self-contained and can be pasted into the [[Nasal Console]] and directly executed &amp;quot;as is&amp;quot;: &lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Canvas-raster-images-via-url.png|thumb|screen shot demonstrating how Nasal and Canvas can be used to display raster images downloaded on demand]] ||&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# create a new window, dimensions are 400 x 200, using the dialog decoration (i.e. titlebar)&lt;br /&gt;
var window = canvas.Window.new([400,200],&amp;quot;dialog&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
# adding a canvas to the new window and setting up background colors/transparency&lt;br /&gt;
var myCanvas = window.createCanvas().set(&amp;quot;background&amp;quot;, canvas.style.getColor(&amp;quot;bg_color&amp;quot;));&lt;br /&gt;
&lt;br /&gt;
# Using specific css colors would also be possible:&lt;br /&gt;
# myCanvas.set(&amp;quot;background&amp;quot;, &amp;quot;#ffaac0&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
# creating the top-level/root group which will contain all other elements/group&lt;br /&gt;
var root = myCanvas.createGroup();&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
# path now a URL&lt;br /&gt;
var url = &amp;quot;http://www.worldwidetelescope.org/docs/Images/MapOfEarth.jpg&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
# create an image child for the texture&lt;br /&gt;
var child=root.createChild(&amp;quot;image&amp;quot;)&lt;br /&gt;
    .setFile( url ) &lt;br /&gt;
    .setTranslation(45,22) # centered, in relation to dialog coordinates&lt;br /&gt;
    .setSize(310,155); # image dimensions&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Clipping ==&lt;br /&gt;
{{Note|{{FGCquote&lt;br /&gt;
|1= Scaling or any other type of transformation or changing the coordinates of individual points is definitely more efficient than clipping (which requires to change the OpenGL clip planes for every rendered object with a different clipping rectangle).&lt;br /&gt;
|2= {{cite web&lt;br /&gt;
  | url    = http://forum.flightgear.org/viewtopic.php?p=277062#p277062&lt;br /&gt;
  | title  = &amp;lt;nowiki&amp;gt;Re: Space Shuttle&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  | author = &amp;lt;nowiki&amp;gt;TheTom&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  | date   = Feb 21st, 2016&lt;br /&gt;
  | added   = Feb 21st, 2016&lt;br /&gt;
  | script_version = 0.25&lt;br /&gt;
  }}&lt;br /&gt;
}}&lt;br /&gt;
}}&lt;br /&gt;
I would suggest to refer to api.nas and look for &amp;quot;clip&amp;quot; and/or &amp;quot;rect&amp;quot; - IIRC, you need to set up a clipping rectangle by setting some kind of &amp;quot;clip&amp;quot; property and setting it to a rect value in the form of rect(...)&lt;br /&gt;
{{FGCquote&lt;br /&gt;
|1= For details, see the clipping example at: [[Canvas Nasal API#set 2]] and  [[Canvas Element#clip.28.29]]&lt;br /&gt;
|2= {{cite web&lt;br /&gt;
  | url    = http://forum.flightgear.org/viewtopic.php?p=276965#p276965&lt;br /&gt;
  | title  = &amp;lt;nowiki&amp;gt;Re: Space Shuttle&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  | author = &amp;lt;nowiki&amp;gt;Hooray&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  | date   = Feb 21st, 2016&lt;br /&gt;
  | added   = Feb 21st, 2016&lt;br /&gt;
  | script_version = 0.25&lt;br /&gt;
  }}&lt;br /&gt;
}}&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[File:MapStructure_dialog_with_clipping_and_event_handling_applied.png|thumb]] ||&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# create a new window, dimensions are 400 x 200, using the dialog decoration (i.e. titlebar)&lt;br /&gt;
var window = canvas.Window.new([400,200],&amp;quot;dialog&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
# adding a canvas to the new window and setting up background colors/transparency&lt;br /&gt;
var myCanvas = window.createCanvas().set(&amp;quot;background&amp;quot;, canvas.style.getColor(&amp;quot;bg_color&amp;quot;));&lt;br /&gt;
&lt;br /&gt;
# Using specific css colors would also be possible:&lt;br /&gt;
# myCanvas.set(&amp;quot;background&amp;quot;, &amp;quot;#ffaac0&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
# creating the top-level/root group which will contain all other elements/group&lt;br /&gt;
var root = myCanvas.createGroup();&lt;br /&gt;
&lt;br /&gt;
# create an image child for the texture&lt;br /&gt;
var child=root.createChild(&amp;quot;.........&amp;quot;)&lt;br /&gt;
    .setFile( url ) &lt;br /&gt;
    .setTranslation(45,22) # centered, in relation to dialog coordinates&lt;br /&gt;
    .setSize(310,155); # image dimensions&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Adding OpenVG Paths ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Openvg-via-canvas.png|thumb|screen shot showing a simple Canvas GUI dialog demonstrating how to use OpenVG-path drawing via Nasal and Canvas]] &lt;br /&gt;
| {{Note|This assumes that you already have a top-level root group set up, and named it '''root'''.}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
var graph = root.createChild(&amp;quot;group&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
var x_axis = graph.createChild(&amp;quot;path&amp;quot;, &amp;quot;x-axis&amp;quot;)&lt;br /&gt;
.moveTo(10, height/2)&lt;br /&gt;
.lineTo(width-10, height/2)&lt;br /&gt;
.setColor(1,0,0)&lt;br /&gt;
.setStrokeLineWidth(3);&lt;br /&gt;
&lt;br /&gt;
var y_axis = graph.createChild(&amp;quot;path&amp;quot;, &amp;quot;y-axis&amp;quot;)&lt;br /&gt;
.moveTo(10, 10)&lt;br /&gt;
.lineTo(10, height-10)&lt;br /&gt;
.setColor(0,0,1)&lt;br /&gt;
.setStrokeLineWidth(2);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Canvas-openvg-quadTo.png|thumb|screen shot showing a simple Canvas GUI dialog demonstrating how to use OpenVG-path drawing via Nasal and Canvas (Canvas/OpenVG quadTo API for drawing curves)]] &lt;br /&gt;
| {{Note|This assumes that you are appending this to the snippet shown above.}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
var points = [	60,height-20, &lt;br /&gt;
		230,height-100,&lt;br /&gt;
		];&lt;br /&gt;
&lt;br /&gt;
var track = graph.createChild(&amp;quot;path&amp;quot;, &amp;quot;track&amp;quot;)&lt;br /&gt;
.moveTo(10, height/2)&lt;br /&gt;
.quadTo( points )&lt;br /&gt;
.setColor(0,1,0)&lt;br /&gt;
.setStrokeLineWidth(4);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Canvas-openvg-cubicTo.png|thumb|screen shot showing a simple Canvas GUI dialog demonstrating how to use OpenVG-path drawing via Nasal and Canvas (Canvas/OpenVG cubicTo API for drawing curves)]] &lt;br /&gt;
| {{Note|This assumes that you are appending this to the snippet shown above.}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
var points = [	60,height-20, &lt;br /&gt;
		120,height-120,&lt;br /&gt;
		230,height-100,&lt;br /&gt;
		];&lt;br /&gt;
&lt;br /&gt;
var track = graph.createChild(&amp;quot;path&amp;quot;, &amp;quot;track&amp;quot;)&lt;br /&gt;
.moveTo(10, height/2)&lt;br /&gt;
.cubicTo( points )&lt;br /&gt;
.setColor(0,1,0)&lt;br /&gt;
.setStrokeLineWidth(4);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Adding Vector Images ==&lt;br /&gt;
&lt;br /&gt;
[[Category:Canvas SVG]]&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Canvas-svg-support.png|thumb|screen shot demonstrating how the scripted Nasal-based SVG parser can be used to dynamically turn SVG files into OpenVG instructions understood by Canvas]] &lt;br /&gt;
| {{Note|This assumes that you already have a top-level root group set up, and named it '''root'''.}}&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
# change the background color &lt;br /&gt;
myCanvas.set(&amp;quot;background&amp;quot;, &amp;quot;#ffaac0&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
# creating the top-level/root group which will contain all other elements/group&lt;br /&gt;
var root = myCanvas.createGroup();&lt;br /&gt;
&lt;br /&gt;
var filename = &amp;quot;/Nasal/canvas/map/Images/boeingAirplane.svg&amp;quot;;&lt;br /&gt;
var svg_symbol = root.createChild('group');&lt;br /&gt;
canvas.parsesvg(svg_symbol, filename);&lt;br /&gt;
&lt;br /&gt;
svg_symbol.setTranslation(width/2,height/2);&lt;br /&gt;
&lt;br /&gt;
#svg_symbol.setScale(0.2);&lt;br /&gt;
#svg_symbol.setRotation(radians)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Using the SymbolCache ==&lt;br /&gt;
Whenever a symbol may need to be shown/instanced multiple times (possibly using different styling), it makes sense to use caching - otherwise, identical symbols would be treated as separate OpenVG groups, all of which would need to be rasterized/rendedered separately (i.e. 100 identical symbols would be updated/rendered one by one). &lt;br /&gt;
&lt;br /&gt;
Typically, a map may display multiple instances of an otherwise identical symbol (think VOR, NDB, DME etc) - equally, a multiplayer map may showing multiple aircraft symbols at the same time. In these cases, it makes sense to use the SymbolCache framework, which will render symbols into a separate Canvas texture and provide a texture map that can be treated as a lookup map, which even supports styling for otherwise identical symbols. To learn more, please refer to [[Canvas MapStructure#The_SymbolCache|SymbolCache]]&lt;br /&gt;
... &lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Canvas-SymbolCache-Instancing.png|thumb|Screen shot showing a Canvas based GUI dialog that is using the SymbolCache for instancing multiple symbols (including support for styling)]] &lt;br /&gt;
| &amp;lt;!--{{Caution|This is currently untested code}}--&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
##&lt;br /&gt;
# this is the function that draws a symbol using OpenVG paths&lt;br /&gt;
# it accepts a group to draw to and returns the rendered group &lt;br /&gt;
# to the caller&lt;br /&gt;
var drawVOR = func(group) {&lt;br /&gt;
    return group.createChild(&amp;quot;path&amp;quot;)&lt;br /&gt;
        .moveTo(-15,0)&lt;br /&gt;
        .lineTo(-7.5,12.5)&lt;br /&gt;
        .lineTo(7.5,12.5)&lt;br /&gt;
        .lineTo(15,0)&lt;br /&gt;
        .lineTo(7.5,-12.5)&lt;br /&gt;
        .lineTo(-7.5,-12.5)&lt;br /&gt;
        .close()&lt;br /&gt;
        .setStrokeLineWidth(line_width) # style-able&lt;br /&gt;
        .setColor(color); # style-able&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
var style = { # styling related attributes (as per the draw* function above)&lt;br /&gt;
    line_width: 3,&lt;br /&gt;
    scale_factor: 1,&lt;br /&gt;
    color: [1,0,0],&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
# create a new cache entry for the styled symbol&lt;br /&gt;
var myCachedSymbol = canvas.StyleableCacheable.new(&lt;br /&gt;
    name:'myVOR', draw_func: drawVOR,&lt;br /&gt;
    cache: canvas.SymbolCache32x32, # the cache to be used&lt;br /&gt;
    draw_mode: canvas.SymbolCache.DRAW_CENTERED,&lt;br /&gt;
    relevant_keys: ['line_width', 'color'], # styling related attributes&lt;br /&gt;
);&lt;br /&gt;
&lt;br /&gt;
var target = root.createChild('group');&lt;br /&gt;
&lt;br /&gt;
var x=0;&lt;br /&gt;
var y=height/2;&lt;br /&gt;
&lt;br /&gt;
var xoffset=50;&lt;br /&gt;
&lt;br /&gt;
# render 5 instanced symbols using the style specified above&lt;br /&gt;
for (var i=0;i&amp;lt;5;i+=1) {&lt;br /&gt;
# look up the raster image for the symbol&lt;br /&gt;
# render it using the passed style and adjust scaling&lt;br /&gt;
var instanced = myCachedSymbol.render(target, style)&lt;br /&gt;
  .setScale(style.scale_factor)&lt;br /&gt;
  .setTranslation(x+=xoffset,y);&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The example shown above uses a fixed symbol/icon cache that is set up while booting Flightgear - sometimes, we may need cache for different purposes. So, let's assume, we need a new/custom cache with a different resolution for each entry in the cache (e.g. 256x256), we can easily accomplish that by setting up a new cache like this: &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
var mySymbolCache256x256 = canvas.SymbolCache.new(1024,256);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Adding Text Elements ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[File:CanvasText-Hello-World.png|thumb|Screen shot showing the CanvasText example contributed by Necolatis]]&lt;br /&gt;
| {{Note|This assumes that you already have a top-level root group set up, and named it '''root'''.}}&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
var myText = root.createChild(&amp;quot;text&amp;quot;)&lt;br /&gt;
      .setText(&amp;quot;Hello world!&amp;quot;)&lt;br /&gt;
      .setFontSize(20, 0.9)          # font size (in texels) and font aspect ratio&lt;br /&gt;
      .setColor(1,0,0,1)             # red, fully opaque&lt;br /&gt;
      .setAlignment(&amp;quot;center-center&amp;quot;) # how the text is aligned to where you place it&lt;br /&gt;
      .setTranslation(160, 80);     # where to place the text&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Adding GUI Labels ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Canvas-Layout-Label-example-by-Necolatis.png|thumb|Canvas demo: Layouts and Labels (by Necolatis)]]&lt;br /&gt;
| {{Note|This assumes that you already have a top-level root group set up, and named it '''root''', just change this variable if you want it to be rendered elsewhere. It also assumes you have a Layout item setup and called '''myLayoutItem'''.}}&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
# create a new layout&lt;br /&gt;
var myLayout = canvas.HBoxLayout.new();&lt;br /&gt;
# assign it to the Canvas&lt;br /&gt;
myCanvas.setLayout(myLayout);&lt;br /&gt;
&lt;br /&gt;
var label = canvas.gui.widgets.Label.new(root, canvas.style, {wordWrap: 0}); # wordwrap: 0 will disable wordwrapping, to enable it use 1 instead&lt;br /&gt;
label.setText(&amp;quot;Hello World!&amp;quot;);&lt;br /&gt;
myLayout.addItem(label);&lt;br /&gt;
&lt;br /&gt;
var label2 = canvas.gui.widgets.Label.new(root, canvas.style, {wordWrap: 0}); # wordwrap: 0 will disable wordwrapping, to enable it use 1 instead&lt;br /&gt;
label2.setText(&amp;quot;Hello FlightGear&amp;quot;);&lt;br /&gt;
myLayout.addItem(label2);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Adding GUI Buttons (Layouts)==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Canvas-demo-layouts-and-buttons-by-Necolatis.png|thumb|Canvas snippet: buttons and layouts (by Necolatis)]] &lt;br /&gt;
|&lt;br /&gt;
&lt;br /&gt;
{{Note|This assumes that you already have a top-level root group set up, and named it '''root''', just change this variable if you want it to be rendered elsewhere. It also assumes you have a Layout item setup and called '''myLayoutItem'''.}}&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
# create a new layout&lt;br /&gt;
var myLayout = canvas.HBoxLayout.new();&lt;br /&gt;
# assign it to the Canvas&lt;br /&gt;
myCanvas.setLayout(myLayout);&lt;br /&gt;
&lt;br /&gt;
# click button&lt;br /&gt;
&lt;br /&gt;
var button = canvas.gui.widgets.Button.new(root, canvas.style, {})&lt;br /&gt;
	.setText(&amp;quot;Click on me&amp;quot;)&lt;br /&gt;
	.setFixedSize(75, 25);&lt;br /&gt;
&lt;br /&gt;
button.listen(&amp;quot;clicked&amp;quot;, func {&lt;br /&gt;
        # add code here to react on click on button.&lt;br /&gt;
print(&amp;quot;Button clicked !&amp;quot;);&lt;br /&gt;
});&lt;br /&gt;
&lt;br /&gt;
myLayout.addItem(button);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Canvas-toggle-button-snippet-by-Necolatis.png|thumb|Canvas toggle button demo by Necolatis]]&lt;br /&gt;
| &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
# create a new layout&lt;br /&gt;
var myLayout = canvas.HBoxLayout.new();&lt;br /&gt;
# assign it to the Canvas&lt;br /&gt;
myCanvas.setLayout(myLayout);&lt;br /&gt;
&lt;br /&gt;
var button = canvas.gui.widgets.Button.new(root, canvas.style, {})&lt;br /&gt;
        .setText(&amp;quot;Toggle me&amp;quot;)&lt;br /&gt;
        .setCheckable(1) # this indicates that is should be a toggle button&lt;br /&gt;
        .setChecked(0) # depressed by default&lt;br /&gt;
        .setFixedSize(75, 25);&lt;br /&gt;
&lt;br /&gt;
button.listen(&amp;quot;toggled&amp;quot;, func (e) {&lt;br /&gt;
        if( e.detail.checked ) {&lt;br /&gt;
            # add code here to react on button being depressed.&lt;br /&gt;
        } else {&lt;br /&gt;
            # add code here to react on button not being depressed.&lt;br /&gt;
        }&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
myLayout.addItem(button);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Canvas Input Dialog ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Snippets-canvas-input-dialog.png|thumb|Canvas input dialog]]&lt;br /&gt;
||&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
# create a new InputDialog with a title, label, and a callback&lt;br /&gt;
canvas.InputDialog.getText(&amp;quot;Input Dialog Title&amp;quot;, &amp;quot;Please enter some text&amp;quot;, func(btn,value) {&lt;br /&gt;
    if (value) gui.popupTip(&amp;quot;You entered: &amp;quot;~value);&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Canvas ScrollArea ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Canvas-snippets-scrollArea-demo.png|thumb|Screen shot showing a Canvas ScrollArea populated with different splash screens, loaded from $FG_ROOT/Textures]]&lt;br /&gt;
||&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
var vbox = canvas.VBoxLayout.new();&lt;br /&gt;
myCanvas.setLayout(vbox);&lt;br /&gt;
&lt;br /&gt;
 var scroll = canvas.gui.widgets.ScrollArea.new(root, canvas.style, {size: [96, 128]}).move(20, 100);&lt;br /&gt;
 vbox.addItem(scroll, 1);&lt;br /&gt;
&lt;br /&gt;
var scrollContent =&lt;br /&gt;
      scroll.getContent()&lt;br /&gt;
            .set(&amp;quot;font&amp;quot;, &amp;quot;LiberationFonts/LiberationSans-Bold.ttf&amp;quot;)&lt;br /&gt;
            .set(&amp;quot;character-size&amp;quot;, 16)&lt;br /&gt;
            .set(&amp;quot;alignment&amp;quot;, &amp;quot;left-center&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
var list = canvas.VBoxLayout.new();&lt;br /&gt;
scroll.setLayout(list);&lt;br /&gt;
&lt;br /&gt;
for (var i=1;i&amp;lt;=5;i+=1) {&lt;br /&gt;
var label = canvas.gui.widgets.Label.new(scrollContent, canvas.style, {wordWrap: 0}); &lt;br /&gt;
label.setImage(&amp;quot;Textures/Splash&amp;quot;~i~&amp;quot;.png&amp;quot;);&lt;br /&gt;
label.setFixedSize(256,256);&lt;br /&gt;
list.addItem(label);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Using Layouts ==&lt;br /&gt;
&lt;br /&gt;
== Using Styling ==&lt;br /&gt;
== Adding a HUD ==&lt;br /&gt;
== Adding a 2D Instrument ==&lt;br /&gt;
== Adding a 2D Panel ==&lt;br /&gt;
== Adding a PFD ==&lt;br /&gt;
&lt;br /&gt;
== Adding a Failure Mgmt Widget ==&lt;br /&gt;
{{Note|This kind of widget will typically be useful for dialogs requiring a method for managing aircraft specific system failures (e.g. an instructor console).}}&lt;br /&gt;
&lt;br /&gt;
== Adding a MapStructure map to a Canvas  ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code &lt;br /&gt;
|-&lt;br /&gt;
| [[File:Snippets-canvas-mapstructure-dialog.png|thumb|[[MapStructure]] layers shown in a Canvas GUI dialog]]&lt;br /&gt;
| {{Note|This assumes that you already have a top-level root group set up, and named it '''root''', just change this variable if you want it to be rendered elsewhere. Equally, this snippet assumes that your canvas is named '''myCanvas'''.}}&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
var TestMap = root.createChild(&amp;quot;map&amp;quot;);&lt;br /&gt;
TestMap.setController(&amp;quot;Aircraft position&amp;quot;);&lt;br /&gt;
TestMap.setRange(25); &lt;br /&gt;
 &lt;br /&gt;
TestMap.setTranslation(    myCanvas.get(&amp;quot;view[0]&amp;quot;)/2,&lt;br /&gt;
                           myCanvas.get(&amp;quot;view[1]&amp;quot;)/2&lt;br /&gt;
                        );&lt;br /&gt;
var r = func(name,vis=1,zindex=nil) return caller(0)[0];&lt;br /&gt;
&lt;br /&gt;
foreach(var type; [r('APT'), r('VOR') ] )&lt;br /&gt;
 TestMap.addLayer(factory: canvas.SymbolLayer, type_arg: type.name, visible: type.vis, priority: type.zindex,);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Customizing MapStructure Styling  ==&lt;br /&gt;
{{See also|Canvas MapStructure#Styling}}&lt;br /&gt;
{{WIP}}&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code &lt;br /&gt;
|-&lt;br /&gt;
| [[File:Snippets-canvas-mapstructure-dialog.png|thumb|[[MapStructure]] layers shown in a Canvas GUI dialog]]&lt;br /&gt;
| {{Note|This assumes that you already have a top-level root group set up, and named it '''root''', just change this variable if you want it to be rendered elsewhere. Equally, this snippet assumes that your canvas is named '''myCanvas'''.}}&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
var TestMap = root.createChild(&amp;quot;map&amp;quot;);&lt;br /&gt;
TestMap.setController(&amp;quot;Aircraft position&amp;quot;);&lt;br /&gt;
TestMap.setRange(25); &lt;br /&gt;
 &lt;br /&gt;
TestMap.setTranslation(    myCanvas.get(&amp;quot;view[0]&amp;quot;)/2,&lt;br /&gt;
                           myCanvas.get(&amp;quot;view[1]&amp;quot;)/2&lt;br /&gt;
                        );&lt;br /&gt;
var r = func(name,vis=1,zindex=nil) return caller(0)[0];&lt;br /&gt;
var type = r('APT');&lt;br /&gt;
var style_apt = {&lt;br /&gt;
    scale_factor:0.5,&lt;br /&gt;
    color_default:[0,1,0.9],&lt;br /&gt;
    line_width:4,&lt;br /&gt;
    label_font_color:[0,1,0.9],&lt;br /&gt;
    label_font_size:30&lt;br /&gt;
};&lt;br /&gt;
TestMap.addLayer(factory: canvas.SymbolLayer, type_arg: type.name, visible: type.vis, priority: type.zindex,style:style_apt);&lt;br /&gt;
&lt;br /&gt;
var type = r('VOR');&lt;br /&gt;
var style_vor = {&lt;br /&gt;
    scale_factor:0.6,&lt;br /&gt;
    active_color:[1,0,0],&lt;br /&gt;
    inactive_color:[0,1,0],&lt;br /&gt;
    line_width:4,&lt;br /&gt;
};&lt;br /&gt;
TestMap.addLayer(factory: canvas.SymbolLayer, type_arg: type.name, visible: type.vis, priority: type.zindex,style:style_vor);&lt;br /&gt;
&lt;br /&gt;
### See ...fgdata/Nasal/canvas/map/APT.symbol   ...fgdata/Nasal/canvas/map/VOR.style ... for the style variables that can be configured.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Using MapStructure and Overlays ==&lt;br /&gt;
{{FGCquote&lt;br /&gt;
  |Support for overlays using geo-referenced raster images isn't too well-developed currently, so would need to work with a few hard-coded assumptions (e.g. being specific to a certain raster image and map layout) - but maybe TheTom can provide a few more ideas on how to proceed from here - otherwise, the Map is normally not aware of any non-Map items, i.e. as long as the overlay is added &amp;quot;outside&amp;quot; the Map, it isn't even aware of the raster image - and when it is added as a map element, it would probably be rotated. So there's still some work needed here. But generally, this should work well enough even in its current form (the screen shot is purely based on the Canvas Snippets article, i.e. it just displays a Canvas GUI dialog, adds the downloaded raster image and then adds the MapStructure APS layer - without the Map being aware of the overlay/geo-referencing that is needed currently).&lt;br /&gt;
  |{{cite web |url=http://forum.flightgear.org/viewtopic.php?p=238316#p238316&lt;br /&gt;
     |title=&amp;lt;nowiki&amp;gt;Re: Using Canvas for visualizing orbital flights (cont'd PM)&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     |author=&amp;lt;nowiki&amp;gt;Hooray&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     |date=&amp;lt;nowiki&amp;gt;Fri Apr 10&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
   }}&lt;br /&gt;
}}&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code &lt;br /&gt;
|-&lt;br /&gt;
| [[File:Canvas-overlay-with-mapstructure.png|thumb|Using the [[MapStructure]] framework in conjunction with raster images as overlays]]&lt;br /&gt;
| {{Note|This assumes that you already have a top-level root group set up, and named it '''root''', just change this variable if you want it to be rendered elsewhere. Equally, this snippet assumes that your canvas is named '''myCanvas'''.}}&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== A simple tile map ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code &lt;br /&gt;
|-&lt;br /&gt;
| [[File:Canvas - Tile map demo.png|thumb|A simple, canvas based tile map which is centered around the aircraft.]]&lt;br /&gt;
| &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
var (width,height) = (768,512);&lt;br /&gt;
var tile_size = 256;&lt;br /&gt;
&lt;br /&gt;
var window = canvas.Window.new([width, height],&amp;quot;dialog&amp;quot;)&lt;br /&gt;
                   .set('title', &amp;quot;Tile map demo&amp;quot;);&lt;br /&gt;
var g = window.getCanvas(1)&lt;br /&gt;
              .createGroup();&lt;br /&gt;
&lt;br /&gt;
# Simple user interface (Buttons for zoom and label for displaying it)&lt;br /&gt;
var zoom = 10;&lt;br /&gt;
var type = &amp;quot;map&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
var ui_root = window.getCanvas().createGroup();&lt;br /&gt;
var vbox = canvas.VBoxLayout.new();&lt;br /&gt;
window.setLayout(vbox);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
var button_in = canvas.gui.widgets.Button.new(ui_root, canvas.style, {})&lt;br /&gt;
                                  .setText(&amp;quot;+&amp;quot;)&lt;br /&gt;
                                  .listen(&amp;quot;clicked&amp;quot;, func changeZoom(1));&lt;br /&gt;
var button_out = canvas.gui.widgets.Button.new(ui_root, canvas.style, {})&lt;br /&gt;
                                  .setText(&amp;quot;-&amp;quot;)&lt;br /&gt;
                                  .listen(&amp;quot;clicked&amp;quot;, func changeZoom(-1));&lt;br /&gt;
button_in.setSizeHint([32, 32]);&lt;br /&gt;
button_out.setSizeHint([32, 32]);&lt;br /&gt;
&lt;br /&gt;
var label_zoom = canvas.gui.widgets.Label.new(ui_root, canvas.style, {});&lt;br /&gt;
&lt;br /&gt;
var button_box = canvas.HBoxLayout.new();&lt;br /&gt;
button_box.addItem(button_in);&lt;br /&gt;
button_box.addItem(label_zoom);&lt;br /&gt;
button_box.addItem(button_out);&lt;br /&gt;
button_box.addStretch(1);&lt;br /&gt;
&lt;br /&gt;
vbox.addItem(button_box);&lt;br /&gt;
vbox.addStretch(1);&lt;br /&gt;
&lt;br /&gt;
var changeZoom = func(d)&lt;br /&gt;
{&lt;br /&gt;
  zoom = math.max(2, math.min(19, zoom + d));&lt;br /&gt;
  label_zoom.setText(&amp;quot;Zoom &amp;quot; ~ zoom);&lt;br /&gt;
  updateTiles();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# http://polymaps.org/docs/&lt;br /&gt;
# https://github.com/simplegeo/polymaps&lt;br /&gt;
# https://github.com/Leaflet/Leaflet&lt;br /&gt;
&lt;br /&gt;
var maps_base = getprop(&amp;quot;/sim/fg-home&amp;quot;) ~ '/cache/maps';&lt;br /&gt;
&lt;br /&gt;
# http://otile1.mqcdn.com/tiles/1.0.0/map&lt;br /&gt;
# http://otile1.mqcdn.com/tiles/1.0.0/sat&lt;br /&gt;
# (also see http://wiki.openstreetmap.org/wiki/Tile_usage_policy)&lt;br /&gt;
var makeUrl =&lt;br /&gt;
  string.compileTemplate('http://otile1.mqcdn.com/tiles/1.0.0/{type}/{z}/{x}/{y}.jpg');&lt;br /&gt;
var makePath =&lt;br /&gt;
  string.compileTemplate(maps_base ~ '/osm-{type}/{z}/{x}/{y}.jpg');&lt;br /&gt;
var num_tiles = [4, 3];&lt;br /&gt;
&lt;br /&gt;
var center_tile_offset = [&lt;br /&gt;
  (num_tiles[0] - 1) / 2,&lt;br /&gt;
  (num_tiles[1] - 1) / 2&lt;br /&gt;
];&lt;br /&gt;
&lt;br /&gt;
# simple aircraft icon at current position/center of the map&lt;br /&gt;
g.createChild(&amp;quot;path&amp;quot;)&lt;br /&gt;
 .moveTo( tile_size * center_tile_offset[0] - 10,&lt;br /&gt;
          tile_size * center_tile_offset[1] )&lt;br /&gt;
 .horiz(20)&lt;br /&gt;
 .move(-10,-10)&lt;br /&gt;
 .vert(20)&lt;br /&gt;
 .set(&amp;quot;stroke&amp;quot;, &amp;quot;red&amp;quot;)&lt;br /&gt;
 .set(&amp;quot;stroke-width&amp;quot;, 2)&lt;br /&gt;
 .set(&amp;quot;z-index&amp;quot;, 1);&lt;br /&gt;
&lt;br /&gt;
##&lt;br /&gt;
# initialize the map by setting up&lt;br /&gt;
# a grid of raster images  &lt;br /&gt;
&lt;br /&gt;
var tiles = setsize([], num_tiles[0]);&lt;br /&gt;
for(var x = 0; x &amp;lt; num_tiles[0]; x += 1)&lt;br /&gt;
{&lt;br /&gt;
  tiles[x] = setsize([], num_tiles[1]);&lt;br /&gt;
  for(var y = 0; y &amp;lt; num_tiles[1]; y += 1)&lt;br /&gt;
    tiles[x][y] = g.createChild(&amp;quot;image&amp;quot;, &amp;quot;map-tile&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var last_tile = [-1,-1];&lt;br /&gt;
var last_type = type;&lt;br /&gt;
&lt;br /&gt;
##&lt;br /&gt;
# this is the callback that will be regularly called by the timer&lt;br /&gt;
# to update the map&lt;br /&gt;
var updateTiles = func()&lt;br /&gt;
{&lt;br /&gt;
  # get current position&lt;br /&gt;
  var lat = getprop('/position/latitude-deg');&lt;br /&gt;
  var lon = getprop('/position/longitude-deg');&lt;br /&gt;
&lt;br /&gt;
  var n = math.pow(2, zoom);&lt;br /&gt;
  var offset = [&lt;br /&gt;
    n * ((lon + 180) / 360) - center_tile_offset[0],&lt;br /&gt;
    (1 - math.ln(math.tan(lat * math.pi/180) + 1 / math.cos(lat * math.pi/180)) / math.pi) / 2 * n - center_tile_offset[1]&lt;br /&gt;
  ];&lt;br /&gt;
  var tile_index = [int(offset[0]), int(offset[1])];&lt;br /&gt;
&lt;br /&gt;
  var ox = tile_index[0] - offset[0];&lt;br /&gt;
  var oy = tile_index[1] - offset[1];&lt;br /&gt;
&lt;br /&gt;
  for(var x = 0; x &amp;lt; num_tiles[0]; x += 1)&lt;br /&gt;
    for(var y = 0; y &amp;lt; num_tiles[1]; y += 1)&lt;br /&gt;
      tiles[x][y].setTranslation(int((ox + x) * tile_size + 0.5), int((oy + y) * tile_size + 0.5));&lt;br /&gt;
&lt;br /&gt;
  if(    tile_index[0] != last_tile[0]&lt;br /&gt;
      or tile_index[1] != last_tile[1]&lt;br /&gt;
      or type != last_type )&lt;br /&gt;
  {&lt;br /&gt;
    for(var x = 0; x &amp;lt; num_tiles[0]; x += 1)&lt;br /&gt;
      for(var y = 0; y &amp;lt; num_tiles[1]; y += 1)&lt;br /&gt;
      {&lt;br /&gt;
        var pos = {&lt;br /&gt;
          z: zoom,&lt;br /&gt;
          x: int(offset[0] + x),&lt;br /&gt;
          y: int(offset[1] + y),&lt;br /&gt;
          type: type&lt;br /&gt;
        };&lt;br /&gt;
&lt;br /&gt;
        (func {&lt;br /&gt;
        var img_path = makePath(pos);&lt;br /&gt;
        var tile = tiles[x][y];&lt;br /&gt;
&lt;br /&gt;
        if( io.stat(img_path) == nil )&lt;br /&gt;
        { # image not found, save in $FG_HOME&lt;br /&gt;
          var img_url = makeUrl(pos);&lt;br /&gt;
          print('requesting ' ~ img_url);&lt;br /&gt;
          http.save(img_url, img_path)&lt;br /&gt;
              .done(func {print('received image ' ~ img_path); tile.set(&amp;quot;src&amp;quot;, img_path);})&lt;br /&gt;
              .fail(func (r) print('Failed to get image ' ~ img_path ~ ' ' ~ r.status ~ ': ' ~ r.reason));&lt;br /&gt;
        }&lt;br /&gt;
        else # cached image found, reusing&lt;br /&gt;
        {&lt;br /&gt;
          print('loading ' ~ img_path);&lt;br /&gt;
          tile.set(&amp;quot;src&amp;quot;, img_path)&lt;br /&gt;
        }&lt;br /&gt;
        })();&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
    last_tile = tile_index;&lt;br /&gt;
    last_type = type;&lt;br /&gt;
  }&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
##&lt;br /&gt;
# set up a timer that will invoke updateTiles at 2-second intervals&lt;br /&gt;
var update_timer = maketimer(2, updateTiles);&lt;br /&gt;
# actually start the timer&lt;br /&gt;
update_timer.start();&lt;br /&gt;
&lt;br /&gt;
##&lt;br /&gt;
# set up default zoom level&lt;br /&gt;
changeZoom(0);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
###&lt;br /&gt;
# The following lines were recently added and have not yet been tested&lt;br /&gt;
# (if in doubt, remove them)&lt;br /&gt;
window.del = func()&lt;br /&gt;
{&lt;br /&gt;
  print(&amp;quot;Cleaning up window:&amp;quot;,title,&amp;quot;\n&amp;quot;);&lt;br /&gt;
  update_timer.stop();&lt;br /&gt;
# explanation for the call() technique at: http://wiki.flightgear.org/Object_oriented_programming_in_Nasal#Making_safer_base-class_calls&lt;br /&gt;
  call(canvas.Window.del, [], me);&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Adding a NavDisplay ==&lt;br /&gt;
&lt;br /&gt;
== Adding a CDU ==&lt;br /&gt;
&lt;br /&gt;
== Adding a Multi-Function Display (MFD) ==&lt;/div&gt;</summary>
		<author><name>Clm76</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Canvas_snippets&amp;diff=105420</id>
		<title>Canvas snippets</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Canvas_snippets&amp;diff=105420"/>
		<updated>2016-10-27T19:48:02Z</updated>

		<summary type="html">&lt;p&gt;Clm76: /* Customizing MapStructure Styling */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Stub}}&lt;br /&gt;
{{-}}&lt;br /&gt;
This article is meant to be a collection of self-contained code snippets for doing Nasal/Canvas coding without having to spend too much time working through APIs, documentation and existing code - the idea is to provide snippets that contain comments and explicit assumptions, so that these snippets can be easily adapted by people without necessarily having to be very familiar with Nasal/Canvas coding. In the long term, we're hoping to grow a library of useful code snippets to cover most use-cases, while also covering more complex scenarios, e.g. using Canvas-specific helper frameworks like [[Canvas MapStructure]], the [[NavDisplay]] or the recent CDU work.&lt;br /&gt;
&lt;br /&gt;
Canvas itself is designed to be front-end agnostic, meaning that it doesn't matter where a Canvas is used/displayed - this is accomplished by so called &amp;quot;placements&amp;quot;, which determine where a Canvas texture shown. Thus, the back-end logic can remain the same usually, so that GUI dialogs may show avionics, but also so that avionics may show GUI widgets, which also applies to HUDs, liveries and even placements within the FlightGear scenery.&lt;br /&gt;
&lt;br /&gt;
We encourage people to use the snippets listed below to get started with Nasal/Canvas coding, but also to provide feedback on extending/improving this article to make it even more accessible and useful. Equally, if you're aware of any Canvas-related efforts that may contain useful code snippets, please do feel free to add those snippets here. Even people who don't have any interest in coding itself are encouraged to get involved by helping test the snippets listed below, for which you only need to the [[Nasal Console]], and while you're at it, please also help provide/update screen shots for each code snippet.&lt;br /&gt;
&lt;br /&gt;
Contributions added to this article should ideally satisfy some requirements:&lt;br /&gt;
* be entirely self-contained&lt;br /&gt;
* contain good/clear comments&lt;br /&gt;
* have pointers/references to related code/use-cases&lt;br /&gt;
* make assumptions explicit&lt;br /&gt;
* contain screen shots for each example&lt;br /&gt;
* avoid overloaded symbol names to ensure that examples can be merged and adapted easily&lt;br /&gt;
* dependencies (svg files, images/textures etc) should always be chosen such that snippets always work using $FG_ROOT&lt;br /&gt;
* hard-coded assumptions (e.g. texture dimensions in terms of width/height etc) should be encapsulated using variables&lt;br /&gt;
 &lt;br /&gt;
{{Canvas Navigation}}&lt;br /&gt;
&lt;br /&gt;
== Creating a standalone Canvas ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[Special:UploadWizard|Upload requested]] || &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
var (width, height) = (512,512);&lt;br /&gt;
# Create a standalone Canvas (not attached to any GUI dialog/aircraft etc) &lt;br /&gt;
var myCanvas = canvas.new({&lt;br /&gt;
  &amp;quot;name&amp;quot;: &amp;quot;Livery Test&amp;quot;,   # The name is optional but allow for easier identification&lt;br /&gt;
  &amp;quot;size&amp;quot;: [width, height], # Size of the underlying texture (should be a power of 2, required) [Resolution]&lt;br /&gt;
  &amp;quot;view&amp;quot;: [width, height],  # Virtual resolution (Defines the coordinate system of the canvas [Dimensions]&lt;br /&gt;
                        # which will be stretched the size of the texture, required)&lt;br /&gt;
  &amp;quot;mipmapping&amp;quot;: 1       # Enable mipmapping (optional)&lt;br /&gt;
});&lt;br /&gt;
&lt;br /&gt;
# set background color&lt;br /&gt;
myCanvas.set(&amp;quot;background&amp;quot;, canvas.style.getColor(&amp;quot;bg_color&amp;quot;));&lt;br /&gt;
&lt;br /&gt;
# creating the top-level/root group which will contain all other elements/group&lt;br /&gt;
var root = myCanvas.createGroup();&lt;br /&gt;
&lt;br /&gt;
# OPTIONAL: Create a Canvas dialog window to hold the canvas and show that it's working&lt;br /&gt;
# the Canvas is now standalone, i.e. continues to live once the dialog is closed!&lt;br /&gt;
var window = canvas.Window.new([width,height],&amp;quot;dialog&amp;quot;);&lt;br /&gt;
window.setCanvas(myCanvas);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Accessing the Canvas Desktop ==&lt;br /&gt;
Sometimes you may want to render to the main screen, without creating a separate Canvas window - this can be accomplished by using the Canvas desktop, note that the following example is fully self-contained, i.e. does not require any code to be added to work:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[Special:UploadWizard|Upload requested]] || &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
var myNode = canvas.getDesktop().createChild(&amp;quot;text&amp;quot;)&lt;br /&gt;
      .setText(&amp;quot;Hello Canvas Desktop&amp;quot;)&lt;br /&gt;
      .setFontSize(25, 1.0)          # font size (in texels) and font aspect ratio&lt;br /&gt;
      .setColor(1,0,0,1)             # red, fully opaque&lt;br /&gt;
      .setAlignment(&amp;quot;center-center&amp;quot;) # how the text is aligned to where you place it&lt;br /&gt;
      .setTranslation(160, 80);     # where to place the text&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Creating Tooltips ==&lt;br /&gt;
== Creating Popups ==&lt;br /&gt;
== Creating a Canvas GUI Window ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Snippets-canvas-dialog.png|left|thumb|This is what the [[Nasal]]/[[Canvas]] snippet will look like once you pasted it into the [[Nasal Console]] and click &amp;quot;Execute&amp;quot;.]] || {{Note|This example uses so called method chaining, if you're not familiar with the concept, please see: [[Object_Oriented_Programming_with_Nasal#More_on_methods:_Chaining|Method Chaining]].}}&lt;br /&gt;
&lt;br /&gt;
{{Canvas Snippets Boilerplate}}&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Adding Raster Images ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code &lt;br /&gt;
|-&lt;br /&gt;
| [[File:Canvas Snippets-raster image.png|thumb|Canvas snippet demonstrating how to load a raster image]] ||&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# path is relative to $FG_ROOT (base package)&lt;br /&gt;
var path = &amp;quot;Textures/Splash1.png&amp;quot;;&lt;br /&gt;
# create an image child for the texture&lt;br /&gt;
var child = root.createChild(&amp;quot;image&amp;quot;)&lt;br /&gt;
    .setFile(path)&lt;br /&gt;
    .setTranslation(100, 10)&lt;br /&gt;
    .setSize(130, 130);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Adding Raster Images downloaded on demand ==&lt;br /&gt;
&lt;br /&gt;
The '''path''' could also just as well be a URL, i.e. a raster image retrieved via http - for example, the following snippet is entirely self-contained and can be pasted into the [[Nasal Console]] and directly executed &amp;quot;as is&amp;quot;: &lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Canvas-raster-images-via-url.png|thumb|screen shot demonstrating how Nasal and Canvas can be used to display raster images downloaded on demand]] ||&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# create a new window, dimensions are 400 x 200, using the dialog decoration (i.e. titlebar)&lt;br /&gt;
var window = canvas.Window.new([400,200],&amp;quot;dialog&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
# adding a canvas to the new window and setting up background colors/transparency&lt;br /&gt;
var myCanvas = window.createCanvas().set(&amp;quot;background&amp;quot;, canvas.style.getColor(&amp;quot;bg_color&amp;quot;));&lt;br /&gt;
&lt;br /&gt;
# Using specific css colors would also be possible:&lt;br /&gt;
# myCanvas.set(&amp;quot;background&amp;quot;, &amp;quot;#ffaac0&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
# creating the top-level/root group which will contain all other elements/group&lt;br /&gt;
var root = myCanvas.createGroup();&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
# path now a URL&lt;br /&gt;
var url = &amp;quot;http://www.worldwidetelescope.org/docs/Images/MapOfEarth.jpg&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
# create an image child for the texture&lt;br /&gt;
var child=root.createChild(&amp;quot;image&amp;quot;)&lt;br /&gt;
    .setFile( url ) &lt;br /&gt;
    .setTranslation(45,22) # centered, in relation to dialog coordinates&lt;br /&gt;
    .setSize(310,155); # image dimensions&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Clipping ==&lt;br /&gt;
{{Note|{{FGCquote&lt;br /&gt;
|1= Scaling or any other type of transformation or changing the coordinates of individual points is definitely more efficient than clipping (which requires to change the OpenGL clip planes for every rendered object with a different clipping rectangle).&lt;br /&gt;
|2= {{cite web&lt;br /&gt;
  | url    = http://forum.flightgear.org/viewtopic.php?p=277062#p277062&lt;br /&gt;
  | title  = &amp;lt;nowiki&amp;gt;Re: Space Shuttle&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  | author = &amp;lt;nowiki&amp;gt;TheTom&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  | date   = Feb 21st, 2016&lt;br /&gt;
  | added   = Feb 21st, 2016&lt;br /&gt;
  | script_version = 0.25&lt;br /&gt;
  }}&lt;br /&gt;
}}&lt;br /&gt;
}}&lt;br /&gt;
I would suggest to refer to api.nas and look for &amp;quot;clip&amp;quot; and/or &amp;quot;rect&amp;quot; - IIRC, you need to set up a clipping rectangle by setting some kind of &amp;quot;clip&amp;quot; property and setting it to a rect value in the form of rect(...)&lt;br /&gt;
{{FGCquote&lt;br /&gt;
|1= For details, see the clipping example at: [[Canvas Nasal API#set 2]] and  [[Canvas Element#clip.28.29]]&lt;br /&gt;
|2= {{cite web&lt;br /&gt;
  | url    = http://forum.flightgear.org/viewtopic.php?p=276965#p276965&lt;br /&gt;
  | title  = &amp;lt;nowiki&amp;gt;Re: Space Shuttle&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  | author = &amp;lt;nowiki&amp;gt;Hooray&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  | date   = Feb 21st, 2016&lt;br /&gt;
  | added   = Feb 21st, 2016&lt;br /&gt;
  | script_version = 0.25&lt;br /&gt;
  }}&lt;br /&gt;
}}&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[File:MapStructure_dialog_with_clipping_and_event_handling_applied.png|thumb]] ||&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# create a new window, dimensions are 400 x 200, using the dialog decoration (i.e. titlebar)&lt;br /&gt;
var window = canvas.Window.new([400,200],&amp;quot;dialog&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
# adding a canvas to the new window and setting up background colors/transparency&lt;br /&gt;
var myCanvas = window.createCanvas().set(&amp;quot;background&amp;quot;, canvas.style.getColor(&amp;quot;bg_color&amp;quot;));&lt;br /&gt;
&lt;br /&gt;
# Using specific css colors would also be possible:&lt;br /&gt;
# myCanvas.set(&amp;quot;background&amp;quot;, &amp;quot;#ffaac0&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
# creating the top-level/root group which will contain all other elements/group&lt;br /&gt;
var root = myCanvas.createGroup();&lt;br /&gt;
&lt;br /&gt;
# create an image child for the texture&lt;br /&gt;
var child=root.createChild(&amp;quot;.........&amp;quot;)&lt;br /&gt;
    .setFile( url ) &lt;br /&gt;
    .setTranslation(45,22) # centered, in relation to dialog coordinates&lt;br /&gt;
    .setSize(310,155); # image dimensions&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Adding OpenVG Paths ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Openvg-via-canvas.png|thumb|screen shot showing a simple Canvas GUI dialog demonstrating how to use OpenVG-path drawing via Nasal and Canvas]] &lt;br /&gt;
| {{Note|This assumes that you already have a top-level root group set up, and named it '''root'''.}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
var graph = root.createChild(&amp;quot;group&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
var x_axis = graph.createChild(&amp;quot;path&amp;quot;, &amp;quot;x-axis&amp;quot;)&lt;br /&gt;
.moveTo(10, height/2)&lt;br /&gt;
.lineTo(width-10, height/2)&lt;br /&gt;
.setColor(1,0,0)&lt;br /&gt;
.setStrokeLineWidth(3);&lt;br /&gt;
&lt;br /&gt;
var y_axis = graph.createChild(&amp;quot;path&amp;quot;, &amp;quot;y-axis&amp;quot;)&lt;br /&gt;
.moveTo(10, 10)&lt;br /&gt;
.lineTo(10, height-10)&lt;br /&gt;
.setColor(0,0,1)&lt;br /&gt;
.setStrokeLineWidth(2);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Canvas-openvg-quadTo.png|thumb|screen shot showing a simple Canvas GUI dialog demonstrating how to use OpenVG-path drawing via Nasal and Canvas (Canvas/OpenVG quadTo API for drawing curves)]] &lt;br /&gt;
| {{Note|This assumes that you are appending this to the snippet shown above.}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
var points = [	60,height-20, &lt;br /&gt;
		230,height-100,&lt;br /&gt;
		];&lt;br /&gt;
&lt;br /&gt;
var track = graph.createChild(&amp;quot;path&amp;quot;, &amp;quot;track&amp;quot;)&lt;br /&gt;
.moveTo(10, height/2)&lt;br /&gt;
.quadTo( points )&lt;br /&gt;
.setColor(0,1,0)&lt;br /&gt;
.setStrokeLineWidth(4);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Canvas-openvg-cubicTo.png|thumb|screen shot showing a simple Canvas GUI dialog demonstrating how to use OpenVG-path drawing via Nasal and Canvas (Canvas/OpenVG cubicTo API for drawing curves)]] &lt;br /&gt;
| {{Note|This assumes that you are appending this to the snippet shown above.}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
var points = [	60,height-20, &lt;br /&gt;
		120,height-120,&lt;br /&gt;
		230,height-100,&lt;br /&gt;
		];&lt;br /&gt;
&lt;br /&gt;
var track = graph.createChild(&amp;quot;path&amp;quot;, &amp;quot;track&amp;quot;)&lt;br /&gt;
.moveTo(10, height/2)&lt;br /&gt;
.cubicTo( points )&lt;br /&gt;
.setColor(0,1,0)&lt;br /&gt;
.setStrokeLineWidth(4);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Adding Vector Images ==&lt;br /&gt;
&lt;br /&gt;
[[Category:Canvas SVG]]&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Canvas-svg-support.png|thumb|screen shot demonstrating how the scripted Nasal-based SVG parser can be used to dynamically turn SVG files into OpenVG instructions understood by Canvas]] &lt;br /&gt;
| {{Note|This assumes that you already have a top-level root group set up, and named it '''root'''.}}&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
# change the background color &lt;br /&gt;
myCanvas.set(&amp;quot;background&amp;quot;, &amp;quot;#ffaac0&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
# creating the top-level/root group which will contain all other elements/group&lt;br /&gt;
var root = myCanvas.createGroup();&lt;br /&gt;
&lt;br /&gt;
var filename = &amp;quot;/Nasal/canvas/map/Images/boeingAirplane.svg&amp;quot;;&lt;br /&gt;
var svg_symbol = root.createChild('group');&lt;br /&gt;
canvas.parsesvg(svg_symbol, filename);&lt;br /&gt;
&lt;br /&gt;
svg_symbol.setTranslation(width/2,height/2);&lt;br /&gt;
&lt;br /&gt;
#svg_symbol.setScale(0.2);&lt;br /&gt;
#svg_symbol.setRotation(radians)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Using the SymbolCache ==&lt;br /&gt;
Whenever a symbol may need to be shown/instanced multiple times (possibly using different styling), it makes sense to use caching - otherwise, identical symbols would be treated as separate OpenVG groups, all of which would need to be rasterized/rendedered separately (i.e. 100 identical symbols would be updated/rendered one by one). &lt;br /&gt;
&lt;br /&gt;
Typically, a map may display multiple instances of an otherwise identical symbol (think VOR, NDB, DME etc) - equally, a multiplayer map may showing multiple aircraft symbols at the same time. In these cases, it makes sense to use the SymbolCache framework, which will render symbols into a separate Canvas texture and provide a texture map that can be treated as a lookup map, which even supports styling for otherwise identical symbols. To learn more, please refer to [[Canvas MapStructure#The_SymbolCache|SymbolCache]]&lt;br /&gt;
... &lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Canvas-SymbolCache-Instancing.png|thumb|Screen shot showing a Canvas based GUI dialog that is using the SymbolCache for instancing multiple symbols (including support for styling)]] &lt;br /&gt;
| &amp;lt;!--{{Caution|This is currently untested code}}--&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
##&lt;br /&gt;
# this is the function that draws a symbol using OpenVG paths&lt;br /&gt;
# it accepts a group to draw to and returns the rendered group &lt;br /&gt;
# to the caller&lt;br /&gt;
var drawVOR = func(group) {&lt;br /&gt;
    return group.createChild(&amp;quot;path&amp;quot;)&lt;br /&gt;
        .moveTo(-15,0)&lt;br /&gt;
        .lineTo(-7.5,12.5)&lt;br /&gt;
        .lineTo(7.5,12.5)&lt;br /&gt;
        .lineTo(15,0)&lt;br /&gt;
        .lineTo(7.5,-12.5)&lt;br /&gt;
        .lineTo(-7.5,-12.5)&lt;br /&gt;
        .close()&lt;br /&gt;
        .setStrokeLineWidth(line_width) # style-able&lt;br /&gt;
        .setColor(color); # style-able&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
var style = { # styling related attributes (as per the draw* function above)&lt;br /&gt;
    line_width: 3,&lt;br /&gt;
    scale_factor: 1,&lt;br /&gt;
    color: [1,0,0],&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
# create a new cache entry for the styled symbol&lt;br /&gt;
var myCachedSymbol = canvas.StyleableCacheable.new(&lt;br /&gt;
    name:'myVOR', draw_func: drawVOR,&lt;br /&gt;
    cache: canvas.SymbolCache32x32, # the cache to be used&lt;br /&gt;
    draw_mode: canvas.SymbolCache.DRAW_CENTERED,&lt;br /&gt;
    relevant_keys: ['line_width', 'color'], # styling related attributes&lt;br /&gt;
);&lt;br /&gt;
&lt;br /&gt;
var target = root.createChild('group');&lt;br /&gt;
&lt;br /&gt;
var x=0;&lt;br /&gt;
var y=height/2;&lt;br /&gt;
&lt;br /&gt;
var xoffset=50;&lt;br /&gt;
&lt;br /&gt;
# render 5 instanced symbols using the style specified above&lt;br /&gt;
for (var i=0;i&amp;lt;5;i+=1) {&lt;br /&gt;
# look up the raster image for the symbol&lt;br /&gt;
# render it using the passed style and adjust scaling&lt;br /&gt;
var instanced = myCachedSymbol.render(target, style)&lt;br /&gt;
  .setScale(style.scale_factor)&lt;br /&gt;
  .setTranslation(x+=xoffset,y);&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The example shown above uses a fixed symbol/icon cache that is set up while booting Flightgear - sometimes, we may need cache for different purposes. So, let's assume, we need a new/custom cache with a different resolution for each entry in the cache (e.g. 256x256), we can easily accomplish that by setting up a new cache like this: &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
var mySymbolCache256x256 = canvas.SymbolCache.new(1024,256);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Adding Text Elements ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[File:CanvasText-Hello-World.png|thumb|Screen shot showing the CanvasText example contributed by Necolatis]]&lt;br /&gt;
| {{Note|This assumes that you already have a top-level root group set up, and named it '''root'''.}}&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
var myText = root.createChild(&amp;quot;text&amp;quot;)&lt;br /&gt;
      .setText(&amp;quot;Hello world!&amp;quot;)&lt;br /&gt;
      .setFontSize(20, 0.9)          # font size (in texels) and font aspect ratio&lt;br /&gt;
      .setColor(1,0,0,1)             # red, fully opaque&lt;br /&gt;
      .setAlignment(&amp;quot;center-center&amp;quot;) # how the text is aligned to where you place it&lt;br /&gt;
      .setTranslation(160, 80);     # where to place the text&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Adding GUI Labels ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Canvas-Layout-Label-example-by-Necolatis.png|thumb|Canvas demo: Layouts and Labels (by Necolatis)]]&lt;br /&gt;
| {{Note|This assumes that you already have a top-level root group set up, and named it '''root''', just change this variable if you want it to be rendered elsewhere. It also assumes you have a Layout item setup and called '''myLayoutItem'''.}}&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
# create a new layout&lt;br /&gt;
var myLayout = canvas.HBoxLayout.new();&lt;br /&gt;
# assign it to the Canvas&lt;br /&gt;
myCanvas.setLayout(myLayout);&lt;br /&gt;
&lt;br /&gt;
var label = canvas.gui.widgets.Label.new(root, canvas.style, {wordWrap: 0}); # wordwrap: 0 will disable wordwrapping, to enable it use 1 instead&lt;br /&gt;
label.setText(&amp;quot;Hello World!&amp;quot;);&lt;br /&gt;
myLayout.addItem(label);&lt;br /&gt;
&lt;br /&gt;
var label2 = canvas.gui.widgets.Label.new(root, canvas.style, {wordWrap: 0}); # wordwrap: 0 will disable wordwrapping, to enable it use 1 instead&lt;br /&gt;
label2.setText(&amp;quot;Hello FlightGear&amp;quot;);&lt;br /&gt;
myLayout.addItem(label2);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Adding GUI Buttons (Layouts)==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Canvas-demo-layouts-and-buttons-by-Necolatis.png|thumb|Canvas snippet: buttons and layouts (by Necolatis)]] &lt;br /&gt;
|&lt;br /&gt;
&lt;br /&gt;
{{Note|This assumes that you already have a top-level root group set up, and named it '''root''', just change this variable if you want it to be rendered elsewhere. It also assumes you have a Layout item setup and called '''myLayoutItem'''.}}&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
# create a new layout&lt;br /&gt;
var myLayout = canvas.HBoxLayout.new();&lt;br /&gt;
# assign it to the Canvas&lt;br /&gt;
myCanvas.setLayout(myLayout);&lt;br /&gt;
&lt;br /&gt;
# click button&lt;br /&gt;
&lt;br /&gt;
var button = canvas.gui.widgets.Button.new(root, canvas.style, {})&lt;br /&gt;
	.setText(&amp;quot;Click on me&amp;quot;)&lt;br /&gt;
	.setFixedSize(75, 25);&lt;br /&gt;
&lt;br /&gt;
button.listen(&amp;quot;clicked&amp;quot;, func {&lt;br /&gt;
        # add code here to react on click on button.&lt;br /&gt;
print(&amp;quot;Button clicked !&amp;quot;);&lt;br /&gt;
});&lt;br /&gt;
&lt;br /&gt;
myLayout.addItem(button);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Canvas-toggle-button-snippet-by-Necolatis.png|thumb|Canvas toggle button demo by Necolatis]]&lt;br /&gt;
| &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
# create a new layout&lt;br /&gt;
var myLayout = canvas.HBoxLayout.new();&lt;br /&gt;
# assign it to the Canvas&lt;br /&gt;
myCanvas.setLayout(myLayout);&lt;br /&gt;
&lt;br /&gt;
var button = canvas.gui.widgets.Button.new(root, canvas.style, {})&lt;br /&gt;
        .setText(&amp;quot;Toggle me&amp;quot;)&lt;br /&gt;
        .setCheckable(1) # this indicates that is should be a toggle button&lt;br /&gt;
        .setChecked(0) # depressed by default&lt;br /&gt;
        .setFixedSize(75, 25);&lt;br /&gt;
&lt;br /&gt;
button.listen(&amp;quot;toggled&amp;quot;, func (e) {&lt;br /&gt;
        if( e.detail.checked ) {&lt;br /&gt;
            # add code here to react on button being depressed.&lt;br /&gt;
        } else {&lt;br /&gt;
            # add code here to react on button not being depressed.&lt;br /&gt;
        }&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
myLayout.addItem(button);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Canvas Input Dialog ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Snippets-canvas-input-dialog.png|thumb|Canvas input dialog]]&lt;br /&gt;
||&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
# create a new InputDialog with a title, label, and a callback&lt;br /&gt;
canvas.InputDialog.getText(&amp;quot;Input Dialog Title&amp;quot;, &amp;quot;Please enter some text&amp;quot;, func(btn,value) {&lt;br /&gt;
    if (value) gui.popupTip(&amp;quot;You entered: &amp;quot;~value);&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Canvas ScrollArea ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Canvas-snippets-scrollArea-demo.png|thumb|Screen shot showing a Canvas ScrollArea populated with different splash screens, loaded from $FG_ROOT/Textures]]&lt;br /&gt;
||&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
var vbox = canvas.VBoxLayout.new();&lt;br /&gt;
myCanvas.setLayout(vbox);&lt;br /&gt;
&lt;br /&gt;
 var scroll = canvas.gui.widgets.ScrollArea.new(root, canvas.style, {size: [96, 128]}).move(20, 100);&lt;br /&gt;
 vbox.addItem(scroll, 1);&lt;br /&gt;
&lt;br /&gt;
var scrollContent =&lt;br /&gt;
      scroll.getContent()&lt;br /&gt;
            .set(&amp;quot;font&amp;quot;, &amp;quot;LiberationFonts/LiberationSans-Bold.ttf&amp;quot;)&lt;br /&gt;
            .set(&amp;quot;character-size&amp;quot;, 16)&lt;br /&gt;
            .set(&amp;quot;alignment&amp;quot;, &amp;quot;left-center&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
var list = canvas.VBoxLayout.new();&lt;br /&gt;
scroll.setLayout(list);&lt;br /&gt;
&lt;br /&gt;
for (var i=1;i&amp;lt;=5;i+=1) {&lt;br /&gt;
var label = canvas.gui.widgets.Label.new(scrollContent, canvas.style, {wordWrap: 0}); &lt;br /&gt;
label.setImage(&amp;quot;Textures/Splash&amp;quot;~i~&amp;quot;.png&amp;quot;);&lt;br /&gt;
label.setFixedSize(256,256);&lt;br /&gt;
list.addItem(label);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Using Layouts ==&lt;br /&gt;
&lt;br /&gt;
== Using Styling ==&lt;br /&gt;
== Adding a HUD ==&lt;br /&gt;
== Adding a 2D Instrument ==&lt;br /&gt;
== Adding a 2D Panel ==&lt;br /&gt;
== Adding a PFD ==&lt;br /&gt;
&lt;br /&gt;
== Adding a Failure Mgmt Widget ==&lt;br /&gt;
{{Note|This kind of widget will typically be useful for dialogs requiring a method for managing aircraft specific system failures (e.g. an instructor console).}}&lt;br /&gt;
&lt;br /&gt;
== Adding a MapStructure map to a Canvas  ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code &lt;br /&gt;
|-&lt;br /&gt;
| [[File:Snippets-canvas-mapstructure-dialog.png|thumb|[[MapStructure]] layers shown in a Canvas GUI dialog]]&lt;br /&gt;
| {{Note|This assumes that you already have a top-level root group set up, and named it '''root''', just change this variable if you want it to be rendered elsewhere. Equally, this snippet assumes that your canvas is named '''myCanvas'''.}}&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
var TestMap = root.createChild(&amp;quot;map&amp;quot;);&lt;br /&gt;
TestMap.setController(&amp;quot;Aircraft position&amp;quot;);&lt;br /&gt;
TestMap.setRange(25); &lt;br /&gt;
 &lt;br /&gt;
TestMap.setTranslation(    myCanvas.get(&amp;quot;view[0]&amp;quot;)/2,&lt;br /&gt;
                           myCanvas.get(&amp;quot;view[1]&amp;quot;)/2&lt;br /&gt;
                        );&lt;br /&gt;
var r = func(name,vis=1,zindex=nil) return caller(0)[0];&lt;br /&gt;
&lt;br /&gt;
foreach(var type; [r('APT'), r('VOR') ] )&lt;br /&gt;
 TestMap.addLayer(factory: canvas.SymbolLayer, type_arg: type.name, visible: type.vis, priority: type.zindex,);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Customizing MapStructure Styling  ==&lt;br /&gt;
{{WIP}}&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code &lt;br /&gt;
|-&lt;br /&gt;
| [[File:Snippets-canvas-mapstructure-dialog.png|thumb|[[MapStructure]] layers shown in a Canvas GUI dialog]]&lt;br /&gt;
| {{Note|This assumes that you already have a top-level root group set up, and named it '''root''', just change this variable if you want it to be rendered elsewhere. Equally, this snippet assumes that your canvas is named '''myCanvas'''.}}&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
var TestMap = root.createChild(&amp;quot;map&amp;quot;);&lt;br /&gt;
TestMap.setController(&amp;quot;Aircraft position&amp;quot;);&lt;br /&gt;
TestMap.setRange(25); &lt;br /&gt;
 &lt;br /&gt;
TestMap.setTranslation(    myCanvas.get(&amp;quot;view[0]&amp;quot;)/2,&lt;br /&gt;
                           myCanvas.get(&amp;quot;view[1]&amp;quot;)/2&lt;br /&gt;
                        );&lt;br /&gt;
var r = func(name,vis=1,zindex=nil) return caller(0)[0];&lt;br /&gt;
var type = r('APT');&lt;br /&gt;
var style_apt = {&lt;br /&gt;
    scale_factor:0.5,&lt;br /&gt;
    color_default:[0,1,0.9],&lt;br /&gt;
    line_width:4,&lt;br /&gt;
    label_font_color:[0,1,0.9],&lt;br /&gt;
    label_font_size:30&lt;br /&gt;
};&lt;br /&gt;
TestMap.addLayer(factory: canvas.SymbolLayer, type_arg: type.name, visible: type.vis, priority: type.zindex,style:style_apt);&lt;br /&gt;
&lt;br /&gt;
var type = r('VOR');&lt;br /&gt;
var style_vor = {&lt;br /&gt;
    scale_factor:0.5,&lt;br /&gt;
    color:[0.1,0.9,0],&lt;br /&gt;
    line_width:4,&lt;br /&gt;
    text_color:[0.1,0.9,0]&lt;br /&gt;
};&lt;br /&gt;
TestMap.addLayer(factory: canvas.SymbolLayer, type_arg: type.name, visible: type.vis, priority: type.zindex,style:style_vor);&lt;br /&gt;
&lt;br /&gt;
### See ...fgdata/Nasal/canvas/map/APT.symbol   ...fgdata/Nasal/canvas/map/VOR.style ... for the style variables that can be configured.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Using MapStructure and Overlays ==&lt;br /&gt;
{{FGCquote&lt;br /&gt;
  |Support for overlays using geo-referenced raster images isn't too well-developed currently, so would need to work with a few hard-coded assumptions (e.g. being specific to a certain raster image and map layout) - but maybe TheTom can provide a few more ideas on how to proceed from here - otherwise, the Map is normally not aware of any non-Map items, i.e. as long as the overlay is added &amp;quot;outside&amp;quot; the Map, it isn't even aware of the raster image - and when it is added as a map element, it would probably be rotated. So there's still some work needed here. But generally, this should work well enough even in its current form (the screen shot is purely based on the Canvas Snippets article, i.e. it just displays a Canvas GUI dialog, adds the downloaded raster image and then adds the MapStructure APS layer - without the Map being aware of the overlay/geo-referencing that is needed currently).&lt;br /&gt;
  |{{cite web |url=http://forum.flightgear.org/viewtopic.php?p=238316#p238316&lt;br /&gt;
     |title=&amp;lt;nowiki&amp;gt;Re: Using Canvas for visualizing orbital flights (cont'd PM)&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     |author=&amp;lt;nowiki&amp;gt;Hooray&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     |date=&amp;lt;nowiki&amp;gt;Fri Apr 10&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
   }}&lt;br /&gt;
}}&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code &lt;br /&gt;
|-&lt;br /&gt;
| [[File:Canvas-overlay-with-mapstructure.png|thumb|Using the [[MapStructure]] framework in conjunction with raster images as overlays]]&lt;br /&gt;
| {{Note|This assumes that you already have a top-level root group set up, and named it '''root''', just change this variable if you want it to be rendered elsewhere. Equally, this snippet assumes that your canvas is named '''myCanvas'''.}}&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== A simple tile map ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code &lt;br /&gt;
|-&lt;br /&gt;
| [[File:Canvas - Tile map demo.png|thumb|A simple, canvas based tile map which is centered around the aircraft.]]&lt;br /&gt;
| &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
var (width,height) = (768,512);&lt;br /&gt;
var tile_size = 256;&lt;br /&gt;
&lt;br /&gt;
var window = canvas.Window.new([width, height],&amp;quot;dialog&amp;quot;)&lt;br /&gt;
                   .set('title', &amp;quot;Tile map demo&amp;quot;);&lt;br /&gt;
var g = window.getCanvas(1)&lt;br /&gt;
              .createGroup();&lt;br /&gt;
&lt;br /&gt;
# Simple user interface (Buttons for zoom and label for displaying it)&lt;br /&gt;
var zoom = 10;&lt;br /&gt;
var type = &amp;quot;map&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
var ui_root = window.getCanvas().createGroup();&lt;br /&gt;
var vbox = canvas.VBoxLayout.new();&lt;br /&gt;
window.setLayout(vbox);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
var button_in = canvas.gui.widgets.Button.new(ui_root, canvas.style, {})&lt;br /&gt;
                                  .setText(&amp;quot;+&amp;quot;)&lt;br /&gt;
                                  .listen(&amp;quot;clicked&amp;quot;, func changeZoom(1));&lt;br /&gt;
var button_out = canvas.gui.widgets.Button.new(ui_root, canvas.style, {})&lt;br /&gt;
                                  .setText(&amp;quot;-&amp;quot;)&lt;br /&gt;
                                  .listen(&amp;quot;clicked&amp;quot;, func changeZoom(-1));&lt;br /&gt;
button_in.setSizeHint([32, 32]);&lt;br /&gt;
button_out.setSizeHint([32, 32]);&lt;br /&gt;
&lt;br /&gt;
var label_zoom = canvas.gui.widgets.Label.new(ui_root, canvas.style, {});&lt;br /&gt;
&lt;br /&gt;
var button_box = canvas.HBoxLayout.new();&lt;br /&gt;
button_box.addItem(button_in);&lt;br /&gt;
button_box.addItem(label_zoom);&lt;br /&gt;
button_box.addItem(button_out);&lt;br /&gt;
button_box.addStretch(1);&lt;br /&gt;
&lt;br /&gt;
vbox.addItem(button_box);&lt;br /&gt;
vbox.addStretch(1);&lt;br /&gt;
&lt;br /&gt;
var changeZoom = func(d)&lt;br /&gt;
{&lt;br /&gt;
  zoom = math.max(2, math.min(19, zoom + d));&lt;br /&gt;
  label_zoom.setText(&amp;quot;Zoom &amp;quot; ~ zoom);&lt;br /&gt;
  updateTiles();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# http://polymaps.org/docs/&lt;br /&gt;
# https://github.com/simplegeo/polymaps&lt;br /&gt;
# https://github.com/Leaflet/Leaflet&lt;br /&gt;
&lt;br /&gt;
var maps_base = getprop(&amp;quot;/sim/fg-home&amp;quot;) ~ '/cache/maps';&lt;br /&gt;
&lt;br /&gt;
# http://otile1.mqcdn.com/tiles/1.0.0/map&lt;br /&gt;
# http://otile1.mqcdn.com/tiles/1.0.0/sat&lt;br /&gt;
# (also see http://wiki.openstreetmap.org/wiki/Tile_usage_policy)&lt;br /&gt;
var makeUrl =&lt;br /&gt;
  string.compileTemplate('http://otile1.mqcdn.com/tiles/1.0.0/{type}/{z}/{x}/{y}.jpg');&lt;br /&gt;
var makePath =&lt;br /&gt;
  string.compileTemplate(maps_base ~ '/osm-{type}/{z}/{x}/{y}.jpg');&lt;br /&gt;
var num_tiles = [4, 3];&lt;br /&gt;
&lt;br /&gt;
var center_tile_offset = [&lt;br /&gt;
  (num_tiles[0] - 1) / 2,&lt;br /&gt;
  (num_tiles[1] - 1) / 2&lt;br /&gt;
];&lt;br /&gt;
&lt;br /&gt;
# simple aircraft icon at current position/center of the map&lt;br /&gt;
g.createChild(&amp;quot;path&amp;quot;)&lt;br /&gt;
 .moveTo( tile_size * center_tile_offset[0] - 10,&lt;br /&gt;
          tile_size * center_tile_offset[1] )&lt;br /&gt;
 .horiz(20)&lt;br /&gt;
 .move(-10,-10)&lt;br /&gt;
 .vert(20)&lt;br /&gt;
 .set(&amp;quot;stroke&amp;quot;, &amp;quot;red&amp;quot;)&lt;br /&gt;
 .set(&amp;quot;stroke-width&amp;quot;, 2)&lt;br /&gt;
 .set(&amp;quot;z-index&amp;quot;, 1);&lt;br /&gt;
&lt;br /&gt;
##&lt;br /&gt;
# initialize the map by setting up&lt;br /&gt;
# a grid of raster images  &lt;br /&gt;
&lt;br /&gt;
var tiles = setsize([], num_tiles[0]);&lt;br /&gt;
for(var x = 0; x &amp;lt; num_tiles[0]; x += 1)&lt;br /&gt;
{&lt;br /&gt;
  tiles[x] = setsize([], num_tiles[1]);&lt;br /&gt;
  for(var y = 0; y &amp;lt; num_tiles[1]; y += 1)&lt;br /&gt;
    tiles[x][y] = g.createChild(&amp;quot;image&amp;quot;, &amp;quot;map-tile&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var last_tile = [-1,-1];&lt;br /&gt;
var last_type = type;&lt;br /&gt;
&lt;br /&gt;
##&lt;br /&gt;
# this is the callback that will be regularly called by the timer&lt;br /&gt;
# to update the map&lt;br /&gt;
var updateTiles = func()&lt;br /&gt;
{&lt;br /&gt;
  # get current position&lt;br /&gt;
  var lat = getprop('/position/latitude-deg');&lt;br /&gt;
  var lon = getprop('/position/longitude-deg');&lt;br /&gt;
&lt;br /&gt;
  var n = math.pow(2, zoom);&lt;br /&gt;
  var offset = [&lt;br /&gt;
    n * ((lon + 180) / 360) - center_tile_offset[0],&lt;br /&gt;
    (1 - math.ln(math.tan(lat * math.pi/180) + 1 / math.cos(lat * math.pi/180)) / math.pi) / 2 * n - center_tile_offset[1]&lt;br /&gt;
  ];&lt;br /&gt;
  var tile_index = [int(offset[0]), int(offset[1])];&lt;br /&gt;
&lt;br /&gt;
  var ox = tile_index[0] - offset[0];&lt;br /&gt;
  var oy = tile_index[1] - offset[1];&lt;br /&gt;
&lt;br /&gt;
  for(var x = 0; x &amp;lt; num_tiles[0]; x += 1)&lt;br /&gt;
    for(var y = 0; y &amp;lt; num_tiles[1]; y += 1)&lt;br /&gt;
      tiles[x][y].setTranslation(int((ox + x) * tile_size + 0.5), int((oy + y) * tile_size + 0.5));&lt;br /&gt;
&lt;br /&gt;
  if(    tile_index[0] != last_tile[0]&lt;br /&gt;
      or tile_index[1] != last_tile[1]&lt;br /&gt;
      or type != last_type )&lt;br /&gt;
  {&lt;br /&gt;
    for(var x = 0; x &amp;lt; num_tiles[0]; x += 1)&lt;br /&gt;
      for(var y = 0; y &amp;lt; num_tiles[1]; y += 1)&lt;br /&gt;
      {&lt;br /&gt;
        var pos = {&lt;br /&gt;
          z: zoom,&lt;br /&gt;
          x: int(offset[0] + x),&lt;br /&gt;
          y: int(offset[1] + y),&lt;br /&gt;
          type: type&lt;br /&gt;
        };&lt;br /&gt;
&lt;br /&gt;
        (func {&lt;br /&gt;
        var img_path = makePath(pos);&lt;br /&gt;
        var tile = tiles[x][y];&lt;br /&gt;
&lt;br /&gt;
        if( io.stat(img_path) == nil )&lt;br /&gt;
        { # image not found, save in $FG_HOME&lt;br /&gt;
          var img_url = makeUrl(pos);&lt;br /&gt;
          print('requesting ' ~ img_url);&lt;br /&gt;
          http.save(img_url, img_path)&lt;br /&gt;
              .done(func {print('received image ' ~ img_path); tile.set(&amp;quot;src&amp;quot;, img_path);})&lt;br /&gt;
              .fail(func (r) print('Failed to get image ' ~ img_path ~ ' ' ~ r.status ~ ': ' ~ r.reason));&lt;br /&gt;
        }&lt;br /&gt;
        else # cached image found, reusing&lt;br /&gt;
        {&lt;br /&gt;
          print('loading ' ~ img_path);&lt;br /&gt;
          tile.set(&amp;quot;src&amp;quot;, img_path)&lt;br /&gt;
        }&lt;br /&gt;
        })();&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
    last_tile = tile_index;&lt;br /&gt;
    last_type = type;&lt;br /&gt;
  }&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
##&lt;br /&gt;
# set up a timer that will invoke updateTiles at 2-second intervals&lt;br /&gt;
var update_timer = maketimer(2, updateTiles);&lt;br /&gt;
# actually start the timer&lt;br /&gt;
update_timer.start();&lt;br /&gt;
&lt;br /&gt;
##&lt;br /&gt;
# set up default zoom level&lt;br /&gt;
changeZoom(0);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
###&lt;br /&gt;
# The following lines were recently added and have not yet been tested&lt;br /&gt;
# (if in doubt, remove them)&lt;br /&gt;
window.del = func()&lt;br /&gt;
{&lt;br /&gt;
  print(&amp;quot;Cleaning up window:&amp;quot;,title,&amp;quot;\n&amp;quot;);&lt;br /&gt;
  update_timer.stop();&lt;br /&gt;
# explanation for the call() technique at: http://wiki.flightgear.org/Object_oriented_programming_in_Nasal#Making_safer_base-class_calls&lt;br /&gt;
  call(canvas.Window.del, [], me);&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Adding a NavDisplay ==&lt;br /&gt;
&lt;br /&gt;
== Adding a CDU ==&lt;br /&gt;
&lt;br /&gt;
== Adding a Multi-Function Display (MFD) ==&lt;/div&gt;</summary>
		<author><name>Clm76</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Canvas_snippets&amp;diff=105419</id>
		<title>Canvas snippets</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Canvas_snippets&amp;diff=105419"/>
		<updated>2016-10-27T19:44:44Z</updated>

		<summary type="html">&lt;p&gt;Clm76: /* Customizing MapStructure Styling */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Stub}}&lt;br /&gt;
{{-}}&lt;br /&gt;
This article is meant to be a collection of self-contained code snippets for doing Nasal/Canvas coding without having to spend too much time working through APIs, documentation and existing code - the idea is to provide snippets that contain comments and explicit assumptions, so that these snippets can be easily adapted by people without necessarily having to be very familiar with Nasal/Canvas coding. In the long term, we're hoping to grow a library of useful code snippets to cover most use-cases, while also covering more complex scenarios, e.g. using Canvas-specific helper frameworks like [[Canvas MapStructure]], the [[NavDisplay]] or the recent CDU work.&lt;br /&gt;
&lt;br /&gt;
Canvas itself is designed to be front-end agnostic, meaning that it doesn't matter where a Canvas is used/displayed - this is accomplished by so called &amp;quot;placements&amp;quot;, which determine where a Canvas texture shown. Thus, the back-end logic can remain the same usually, so that GUI dialogs may show avionics, but also so that avionics may show GUI widgets, which also applies to HUDs, liveries and even placements within the FlightGear scenery.&lt;br /&gt;
&lt;br /&gt;
We encourage people to use the snippets listed below to get started with Nasal/Canvas coding, but also to provide feedback on extending/improving this article to make it even more accessible and useful. Equally, if you're aware of any Canvas-related efforts that may contain useful code snippets, please do feel free to add those snippets here. Even people who don't have any interest in coding itself are encouraged to get involved by helping test the snippets listed below, for which you only need to the [[Nasal Console]], and while you're at it, please also help provide/update screen shots for each code snippet.&lt;br /&gt;
&lt;br /&gt;
Contributions added to this article should ideally satisfy some requirements:&lt;br /&gt;
* be entirely self-contained&lt;br /&gt;
* contain good/clear comments&lt;br /&gt;
* have pointers/references to related code/use-cases&lt;br /&gt;
* make assumptions explicit&lt;br /&gt;
* contain screen shots for each example&lt;br /&gt;
* avoid overloaded symbol names to ensure that examples can be merged and adapted easily&lt;br /&gt;
* dependencies (svg files, images/textures etc) should always be chosen such that snippets always work using $FG_ROOT&lt;br /&gt;
* hard-coded assumptions (e.g. texture dimensions in terms of width/height etc) should be encapsulated using variables&lt;br /&gt;
 &lt;br /&gt;
{{Canvas Navigation}}&lt;br /&gt;
&lt;br /&gt;
== Creating a standalone Canvas ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[Special:UploadWizard|Upload requested]] || &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
var (width, height) = (512,512);&lt;br /&gt;
# Create a standalone Canvas (not attached to any GUI dialog/aircraft etc) &lt;br /&gt;
var myCanvas = canvas.new({&lt;br /&gt;
  &amp;quot;name&amp;quot;: &amp;quot;Livery Test&amp;quot;,   # The name is optional but allow for easier identification&lt;br /&gt;
  &amp;quot;size&amp;quot;: [width, height], # Size of the underlying texture (should be a power of 2, required) [Resolution]&lt;br /&gt;
  &amp;quot;view&amp;quot;: [width, height],  # Virtual resolution (Defines the coordinate system of the canvas [Dimensions]&lt;br /&gt;
                        # which will be stretched the size of the texture, required)&lt;br /&gt;
  &amp;quot;mipmapping&amp;quot;: 1       # Enable mipmapping (optional)&lt;br /&gt;
});&lt;br /&gt;
&lt;br /&gt;
# set background color&lt;br /&gt;
myCanvas.set(&amp;quot;background&amp;quot;, canvas.style.getColor(&amp;quot;bg_color&amp;quot;));&lt;br /&gt;
&lt;br /&gt;
# creating the top-level/root group which will contain all other elements/group&lt;br /&gt;
var root = myCanvas.createGroup();&lt;br /&gt;
&lt;br /&gt;
# OPTIONAL: Create a Canvas dialog window to hold the canvas and show that it's working&lt;br /&gt;
# the Canvas is now standalone, i.e. continues to live once the dialog is closed!&lt;br /&gt;
var window = canvas.Window.new([width,height],&amp;quot;dialog&amp;quot;);&lt;br /&gt;
window.setCanvas(myCanvas);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Accessing the Canvas Desktop ==&lt;br /&gt;
Sometimes you may want to render to the main screen, without creating a separate Canvas window - this can be accomplished by using the Canvas desktop, note that the following example is fully self-contained, i.e. does not require any code to be added to work:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[Special:UploadWizard|Upload requested]] || &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
var myNode = canvas.getDesktop().createChild(&amp;quot;text&amp;quot;)&lt;br /&gt;
      .setText(&amp;quot;Hello Canvas Desktop&amp;quot;)&lt;br /&gt;
      .setFontSize(25, 1.0)          # font size (in texels) and font aspect ratio&lt;br /&gt;
      .setColor(1,0,0,1)             # red, fully opaque&lt;br /&gt;
      .setAlignment(&amp;quot;center-center&amp;quot;) # how the text is aligned to where you place it&lt;br /&gt;
      .setTranslation(160, 80);     # where to place the text&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Creating Tooltips ==&lt;br /&gt;
== Creating Popups ==&lt;br /&gt;
== Creating a Canvas GUI Window ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Snippets-canvas-dialog.png|left|thumb|This is what the [[Nasal]]/[[Canvas]] snippet will look like once you pasted it into the [[Nasal Console]] and click &amp;quot;Execute&amp;quot;.]] || {{Note|This example uses so called method chaining, if you're not familiar with the concept, please see: [[Object_Oriented_Programming_with_Nasal#More_on_methods:_Chaining|Method Chaining]].}}&lt;br /&gt;
&lt;br /&gt;
{{Canvas Snippets Boilerplate}}&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Adding Raster Images ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code &lt;br /&gt;
|-&lt;br /&gt;
| [[File:Canvas Snippets-raster image.png|thumb|Canvas snippet demonstrating how to load a raster image]] ||&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# path is relative to $FG_ROOT (base package)&lt;br /&gt;
var path = &amp;quot;Textures/Splash1.png&amp;quot;;&lt;br /&gt;
# create an image child for the texture&lt;br /&gt;
var child = root.createChild(&amp;quot;image&amp;quot;)&lt;br /&gt;
    .setFile(path)&lt;br /&gt;
    .setTranslation(100, 10)&lt;br /&gt;
    .setSize(130, 130);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Adding Raster Images downloaded on demand ==&lt;br /&gt;
&lt;br /&gt;
The '''path''' could also just as well be a URL, i.e. a raster image retrieved via http - for example, the following snippet is entirely self-contained and can be pasted into the [[Nasal Console]] and directly executed &amp;quot;as is&amp;quot;: &lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Canvas-raster-images-via-url.png|thumb|screen shot demonstrating how Nasal and Canvas can be used to display raster images downloaded on demand]] ||&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# create a new window, dimensions are 400 x 200, using the dialog decoration (i.e. titlebar)&lt;br /&gt;
var window = canvas.Window.new([400,200],&amp;quot;dialog&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
# adding a canvas to the new window and setting up background colors/transparency&lt;br /&gt;
var myCanvas = window.createCanvas().set(&amp;quot;background&amp;quot;, canvas.style.getColor(&amp;quot;bg_color&amp;quot;));&lt;br /&gt;
&lt;br /&gt;
# Using specific css colors would also be possible:&lt;br /&gt;
# myCanvas.set(&amp;quot;background&amp;quot;, &amp;quot;#ffaac0&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
# creating the top-level/root group which will contain all other elements/group&lt;br /&gt;
var root = myCanvas.createGroup();&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
# path now a URL&lt;br /&gt;
var url = &amp;quot;http://www.worldwidetelescope.org/docs/Images/MapOfEarth.jpg&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
# create an image child for the texture&lt;br /&gt;
var child=root.createChild(&amp;quot;image&amp;quot;)&lt;br /&gt;
    .setFile( url ) &lt;br /&gt;
    .setTranslation(45,22) # centered, in relation to dialog coordinates&lt;br /&gt;
    .setSize(310,155); # image dimensions&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Clipping ==&lt;br /&gt;
{{Note|{{FGCquote&lt;br /&gt;
|1= Scaling or any other type of transformation or changing the coordinates of individual points is definitely more efficient than clipping (which requires to change the OpenGL clip planes for every rendered object with a different clipping rectangle).&lt;br /&gt;
|2= {{cite web&lt;br /&gt;
  | url    = http://forum.flightgear.org/viewtopic.php?p=277062#p277062&lt;br /&gt;
  | title  = &amp;lt;nowiki&amp;gt;Re: Space Shuttle&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  | author = &amp;lt;nowiki&amp;gt;TheTom&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  | date   = Feb 21st, 2016&lt;br /&gt;
  | added   = Feb 21st, 2016&lt;br /&gt;
  | script_version = 0.25&lt;br /&gt;
  }}&lt;br /&gt;
}}&lt;br /&gt;
}}&lt;br /&gt;
I would suggest to refer to api.nas and look for &amp;quot;clip&amp;quot; and/or &amp;quot;rect&amp;quot; - IIRC, you need to set up a clipping rectangle by setting some kind of &amp;quot;clip&amp;quot; property and setting it to a rect value in the form of rect(...)&lt;br /&gt;
{{FGCquote&lt;br /&gt;
|1= For details, see the clipping example at: [[Canvas Nasal API#set 2]] and  [[Canvas Element#clip.28.29]]&lt;br /&gt;
|2= {{cite web&lt;br /&gt;
  | url    = http://forum.flightgear.org/viewtopic.php?p=276965#p276965&lt;br /&gt;
  | title  = &amp;lt;nowiki&amp;gt;Re: Space Shuttle&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  | author = &amp;lt;nowiki&amp;gt;Hooray&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  | date   = Feb 21st, 2016&lt;br /&gt;
  | added   = Feb 21st, 2016&lt;br /&gt;
  | script_version = 0.25&lt;br /&gt;
  }}&lt;br /&gt;
}}&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[File:MapStructure_dialog_with_clipping_and_event_handling_applied.png|thumb]] ||&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# create a new window, dimensions are 400 x 200, using the dialog decoration (i.e. titlebar)&lt;br /&gt;
var window = canvas.Window.new([400,200],&amp;quot;dialog&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
# adding a canvas to the new window and setting up background colors/transparency&lt;br /&gt;
var myCanvas = window.createCanvas().set(&amp;quot;background&amp;quot;, canvas.style.getColor(&amp;quot;bg_color&amp;quot;));&lt;br /&gt;
&lt;br /&gt;
# Using specific css colors would also be possible:&lt;br /&gt;
# myCanvas.set(&amp;quot;background&amp;quot;, &amp;quot;#ffaac0&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
# creating the top-level/root group which will contain all other elements/group&lt;br /&gt;
var root = myCanvas.createGroup();&lt;br /&gt;
&lt;br /&gt;
# create an image child for the texture&lt;br /&gt;
var child=root.createChild(&amp;quot;.........&amp;quot;)&lt;br /&gt;
    .setFile( url ) &lt;br /&gt;
    .setTranslation(45,22) # centered, in relation to dialog coordinates&lt;br /&gt;
    .setSize(310,155); # image dimensions&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Adding OpenVG Paths ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Openvg-via-canvas.png|thumb|screen shot showing a simple Canvas GUI dialog demonstrating how to use OpenVG-path drawing via Nasal and Canvas]] &lt;br /&gt;
| {{Note|This assumes that you already have a top-level root group set up, and named it '''root'''.}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
var graph = root.createChild(&amp;quot;group&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
var x_axis = graph.createChild(&amp;quot;path&amp;quot;, &amp;quot;x-axis&amp;quot;)&lt;br /&gt;
.moveTo(10, height/2)&lt;br /&gt;
.lineTo(width-10, height/2)&lt;br /&gt;
.setColor(1,0,0)&lt;br /&gt;
.setStrokeLineWidth(3);&lt;br /&gt;
&lt;br /&gt;
var y_axis = graph.createChild(&amp;quot;path&amp;quot;, &amp;quot;y-axis&amp;quot;)&lt;br /&gt;
.moveTo(10, 10)&lt;br /&gt;
.lineTo(10, height-10)&lt;br /&gt;
.setColor(0,0,1)&lt;br /&gt;
.setStrokeLineWidth(2);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Canvas-openvg-quadTo.png|thumb|screen shot showing a simple Canvas GUI dialog demonstrating how to use OpenVG-path drawing via Nasal and Canvas (Canvas/OpenVG quadTo API for drawing curves)]] &lt;br /&gt;
| {{Note|This assumes that you are appending this to the snippet shown above.}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
var points = [	60,height-20, &lt;br /&gt;
		230,height-100,&lt;br /&gt;
		];&lt;br /&gt;
&lt;br /&gt;
var track = graph.createChild(&amp;quot;path&amp;quot;, &amp;quot;track&amp;quot;)&lt;br /&gt;
.moveTo(10, height/2)&lt;br /&gt;
.quadTo( points )&lt;br /&gt;
.setColor(0,1,0)&lt;br /&gt;
.setStrokeLineWidth(4);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Canvas-openvg-cubicTo.png|thumb|screen shot showing a simple Canvas GUI dialog demonstrating how to use OpenVG-path drawing via Nasal and Canvas (Canvas/OpenVG cubicTo API for drawing curves)]] &lt;br /&gt;
| {{Note|This assumes that you are appending this to the snippet shown above.}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
var points = [	60,height-20, &lt;br /&gt;
		120,height-120,&lt;br /&gt;
		230,height-100,&lt;br /&gt;
		];&lt;br /&gt;
&lt;br /&gt;
var track = graph.createChild(&amp;quot;path&amp;quot;, &amp;quot;track&amp;quot;)&lt;br /&gt;
.moveTo(10, height/2)&lt;br /&gt;
.cubicTo( points )&lt;br /&gt;
.setColor(0,1,0)&lt;br /&gt;
.setStrokeLineWidth(4);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Adding Vector Images ==&lt;br /&gt;
&lt;br /&gt;
[[Category:Canvas SVG]]&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Canvas-svg-support.png|thumb|screen shot demonstrating how the scripted Nasal-based SVG parser can be used to dynamically turn SVG files into OpenVG instructions understood by Canvas]] &lt;br /&gt;
| {{Note|This assumes that you already have a top-level root group set up, and named it '''root'''.}}&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
# change the background color &lt;br /&gt;
myCanvas.set(&amp;quot;background&amp;quot;, &amp;quot;#ffaac0&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
# creating the top-level/root group which will contain all other elements/group&lt;br /&gt;
var root = myCanvas.createGroup();&lt;br /&gt;
&lt;br /&gt;
var filename = &amp;quot;/Nasal/canvas/map/Images/boeingAirplane.svg&amp;quot;;&lt;br /&gt;
var svg_symbol = root.createChild('group');&lt;br /&gt;
canvas.parsesvg(svg_symbol, filename);&lt;br /&gt;
&lt;br /&gt;
svg_symbol.setTranslation(width/2,height/2);&lt;br /&gt;
&lt;br /&gt;
#svg_symbol.setScale(0.2);&lt;br /&gt;
#svg_symbol.setRotation(radians)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Using the SymbolCache ==&lt;br /&gt;
Whenever a symbol may need to be shown/instanced multiple times (possibly using different styling), it makes sense to use caching - otherwise, identical symbols would be treated as separate OpenVG groups, all of which would need to be rasterized/rendedered separately (i.e. 100 identical symbols would be updated/rendered one by one). &lt;br /&gt;
&lt;br /&gt;
Typically, a map may display multiple instances of an otherwise identical symbol (think VOR, NDB, DME etc) - equally, a multiplayer map may showing multiple aircraft symbols at the same time. In these cases, it makes sense to use the SymbolCache framework, which will render symbols into a separate Canvas texture and provide a texture map that can be treated as a lookup map, which even supports styling for otherwise identical symbols. To learn more, please refer to [[Canvas MapStructure#The_SymbolCache|SymbolCache]]&lt;br /&gt;
... &lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Canvas-SymbolCache-Instancing.png|thumb|Screen shot showing a Canvas based GUI dialog that is using the SymbolCache for instancing multiple symbols (including support for styling)]] &lt;br /&gt;
| &amp;lt;!--{{Caution|This is currently untested code}}--&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
##&lt;br /&gt;
# this is the function that draws a symbol using OpenVG paths&lt;br /&gt;
# it accepts a group to draw to and returns the rendered group &lt;br /&gt;
# to the caller&lt;br /&gt;
var drawVOR = func(group) {&lt;br /&gt;
    return group.createChild(&amp;quot;path&amp;quot;)&lt;br /&gt;
        .moveTo(-15,0)&lt;br /&gt;
        .lineTo(-7.5,12.5)&lt;br /&gt;
        .lineTo(7.5,12.5)&lt;br /&gt;
        .lineTo(15,0)&lt;br /&gt;
        .lineTo(7.5,-12.5)&lt;br /&gt;
        .lineTo(-7.5,-12.5)&lt;br /&gt;
        .close()&lt;br /&gt;
        .setStrokeLineWidth(line_width) # style-able&lt;br /&gt;
        .setColor(color); # style-able&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
var style = { # styling related attributes (as per the draw* function above)&lt;br /&gt;
    line_width: 3,&lt;br /&gt;
    scale_factor: 1,&lt;br /&gt;
    color: [1,0,0],&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
# create a new cache entry for the styled symbol&lt;br /&gt;
var myCachedSymbol = canvas.StyleableCacheable.new(&lt;br /&gt;
    name:'myVOR', draw_func: drawVOR,&lt;br /&gt;
    cache: canvas.SymbolCache32x32, # the cache to be used&lt;br /&gt;
    draw_mode: canvas.SymbolCache.DRAW_CENTERED,&lt;br /&gt;
    relevant_keys: ['line_width', 'color'], # styling related attributes&lt;br /&gt;
);&lt;br /&gt;
&lt;br /&gt;
var target = root.createChild('group');&lt;br /&gt;
&lt;br /&gt;
var x=0;&lt;br /&gt;
var y=height/2;&lt;br /&gt;
&lt;br /&gt;
var xoffset=50;&lt;br /&gt;
&lt;br /&gt;
# render 5 instanced symbols using the style specified above&lt;br /&gt;
for (var i=0;i&amp;lt;5;i+=1) {&lt;br /&gt;
# look up the raster image for the symbol&lt;br /&gt;
# render it using the passed style and adjust scaling&lt;br /&gt;
var instanced = myCachedSymbol.render(target, style)&lt;br /&gt;
  .setScale(style.scale_factor)&lt;br /&gt;
  .setTranslation(x+=xoffset,y);&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The example shown above uses a fixed symbol/icon cache that is set up while booting Flightgear - sometimes, we may need cache for different purposes. So, let's assume, we need a new/custom cache with a different resolution for each entry in the cache (e.g. 256x256), we can easily accomplish that by setting up a new cache like this: &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
var mySymbolCache256x256 = canvas.SymbolCache.new(1024,256);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Adding Text Elements ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[File:CanvasText-Hello-World.png|thumb|Screen shot showing the CanvasText example contributed by Necolatis]]&lt;br /&gt;
| {{Note|This assumes that you already have a top-level root group set up, and named it '''root'''.}}&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
var myText = root.createChild(&amp;quot;text&amp;quot;)&lt;br /&gt;
      .setText(&amp;quot;Hello world!&amp;quot;)&lt;br /&gt;
      .setFontSize(20, 0.9)          # font size (in texels) and font aspect ratio&lt;br /&gt;
      .setColor(1,0,0,1)             # red, fully opaque&lt;br /&gt;
      .setAlignment(&amp;quot;center-center&amp;quot;) # how the text is aligned to where you place it&lt;br /&gt;
      .setTranslation(160, 80);     # where to place the text&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Adding GUI Labels ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Canvas-Layout-Label-example-by-Necolatis.png|thumb|Canvas demo: Layouts and Labels (by Necolatis)]]&lt;br /&gt;
| {{Note|This assumes that you already have a top-level root group set up, and named it '''root''', just change this variable if you want it to be rendered elsewhere. It also assumes you have a Layout item setup and called '''myLayoutItem'''.}}&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
# create a new layout&lt;br /&gt;
var myLayout = canvas.HBoxLayout.new();&lt;br /&gt;
# assign it to the Canvas&lt;br /&gt;
myCanvas.setLayout(myLayout);&lt;br /&gt;
&lt;br /&gt;
var label = canvas.gui.widgets.Label.new(root, canvas.style, {wordWrap: 0}); # wordwrap: 0 will disable wordwrapping, to enable it use 1 instead&lt;br /&gt;
label.setText(&amp;quot;Hello World!&amp;quot;);&lt;br /&gt;
myLayout.addItem(label);&lt;br /&gt;
&lt;br /&gt;
var label2 = canvas.gui.widgets.Label.new(root, canvas.style, {wordWrap: 0}); # wordwrap: 0 will disable wordwrapping, to enable it use 1 instead&lt;br /&gt;
label2.setText(&amp;quot;Hello FlightGear&amp;quot;);&lt;br /&gt;
myLayout.addItem(label2);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Adding GUI Buttons (Layouts)==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Canvas-demo-layouts-and-buttons-by-Necolatis.png|thumb|Canvas snippet: buttons and layouts (by Necolatis)]] &lt;br /&gt;
|&lt;br /&gt;
&lt;br /&gt;
{{Note|This assumes that you already have a top-level root group set up, and named it '''root''', just change this variable if you want it to be rendered elsewhere. It also assumes you have a Layout item setup and called '''myLayoutItem'''.}}&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
# create a new layout&lt;br /&gt;
var myLayout = canvas.HBoxLayout.new();&lt;br /&gt;
# assign it to the Canvas&lt;br /&gt;
myCanvas.setLayout(myLayout);&lt;br /&gt;
&lt;br /&gt;
# click button&lt;br /&gt;
&lt;br /&gt;
var button = canvas.gui.widgets.Button.new(root, canvas.style, {})&lt;br /&gt;
	.setText(&amp;quot;Click on me&amp;quot;)&lt;br /&gt;
	.setFixedSize(75, 25);&lt;br /&gt;
&lt;br /&gt;
button.listen(&amp;quot;clicked&amp;quot;, func {&lt;br /&gt;
        # add code here to react on click on button.&lt;br /&gt;
print(&amp;quot;Button clicked !&amp;quot;);&lt;br /&gt;
});&lt;br /&gt;
&lt;br /&gt;
myLayout.addItem(button);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Canvas-toggle-button-snippet-by-Necolatis.png|thumb|Canvas toggle button demo by Necolatis]]&lt;br /&gt;
| &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
# create a new layout&lt;br /&gt;
var myLayout = canvas.HBoxLayout.new();&lt;br /&gt;
# assign it to the Canvas&lt;br /&gt;
myCanvas.setLayout(myLayout);&lt;br /&gt;
&lt;br /&gt;
var button = canvas.gui.widgets.Button.new(root, canvas.style, {})&lt;br /&gt;
        .setText(&amp;quot;Toggle me&amp;quot;)&lt;br /&gt;
        .setCheckable(1) # this indicates that is should be a toggle button&lt;br /&gt;
        .setChecked(0) # depressed by default&lt;br /&gt;
        .setFixedSize(75, 25);&lt;br /&gt;
&lt;br /&gt;
button.listen(&amp;quot;toggled&amp;quot;, func (e) {&lt;br /&gt;
        if( e.detail.checked ) {&lt;br /&gt;
            # add code here to react on button being depressed.&lt;br /&gt;
        } else {&lt;br /&gt;
            # add code here to react on button not being depressed.&lt;br /&gt;
        }&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
myLayout.addItem(button);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Canvas Input Dialog ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Snippets-canvas-input-dialog.png|thumb|Canvas input dialog]]&lt;br /&gt;
||&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
# create a new InputDialog with a title, label, and a callback&lt;br /&gt;
canvas.InputDialog.getText(&amp;quot;Input Dialog Title&amp;quot;, &amp;quot;Please enter some text&amp;quot;, func(btn,value) {&lt;br /&gt;
    if (value) gui.popupTip(&amp;quot;You entered: &amp;quot;~value);&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Canvas ScrollArea ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Canvas-snippets-scrollArea-demo.png|thumb|Screen shot showing a Canvas ScrollArea populated with different splash screens, loaded from $FG_ROOT/Textures]]&lt;br /&gt;
||&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
var vbox = canvas.VBoxLayout.new();&lt;br /&gt;
myCanvas.setLayout(vbox);&lt;br /&gt;
&lt;br /&gt;
 var scroll = canvas.gui.widgets.ScrollArea.new(root, canvas.style, {size: [96, 128]}).move(20, 100);&lt;br /&gt;
 vbox.addItem(scroll, 1);&lt;br /&gt;
&lt;br /&gt;
var scrollContent =&lt;br /&gt;
      scroll.getContent()&lt;br /&gt;
            .set(&amp;quot;font&amp;quot;, &amp;quot;LiberationFonts/LiberationSans-Bold.ttf&amp;quot;)&lt;br /&gt;
            .set(&amp;quot;character-size&amp;quot;, 16)&lt;br /&gt;
            .set(&amp;quot;alignment&amp;quot;, &amp;quot;left-center&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
var list = canvas.VBoxLayout.new();&lt;br /&gt;
scroll.setLayout(list);&lt;br /&gt;
&lt;br /&gt;
for (var i=1;i&amp;lt;=5;i+=1) {&lt;br /&gt;
var label = canvas.gui.widgets.Label.new(scrollContent, canvas.style, {wordWrap: 0}); &lt;br /&gt;
label.setImage(&amp;quot;Textures/Splash&amp;quot;~i~&amp;quot;.png&amp;quot;);&lt;br /&gt;
label.setFixedSize(256,256);&lt;br /&gt;
list.addItem(label);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Using Layouts ==&lt;br /&gt;
&lt;br /&gt;
== Using Styling ==&lt;br /&gt;
== Adding a HUD ==&lt;br /&gt;
== Adding a 2D Instrument ==&lt;br /&gt;
== Adding a 2D Panel ==&lt;br /&gt;
== Adding a PFD ==&lt;br /&gt;
&lt;br /&gt;
== Adding a Failure Mgmt Widget ==&lt;br /&gt;
{{Note|This kind of widget will typically be useful for dialogs requiring a method for managing aircraft specific system failures (e.g. an instructor console).}}&lt;br /&gt;
&lt;br /&gt;
== Adding a MapStructure map to a Canvas  ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code &lt;br /&gt;
|-&lt;br /&gt;
| [[File:Snippets-canvas-mapstructure-dialog.png|thumb|[[MapStructure]] layers shown in a Canvas GUI dialog]]&lt;br /&gt;
| {{Note|This assumes that you already have a top-level root group set up, and named it '''root''', just change this variable if you want it to be rendered elsewhere. Equally, this snippet assumes that your canvas is named '''myCanvas'''.}}&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
var TestMap = root.createChild(&amp;quot;map&amp;quot;);&lt;br /&gt;
TestMap.setController(&amp;quot;Aircraft position&amp;quot;);&lt;br /&gt;
TestMap.setRange(25); &lt;br /&gt;
 &lt;br /&gt;
TestMap.setTranslation(    myCanvas.get(&amp;quot;view[0]&amp;quot;)/2,&lt;br /&gt;
                           myCanvas.get(&amp;quot;view[1]&amp;quot;)/2&lt;br /&gt;
                        );&lt;br /&gt;
var r = func(name,vis=1,zindex=nil) return caller(0)[0];&lt;br /&gt;
&lt;br /&gt;
foreach(var type; [r('APT'), r('VOR') ] )&lt;br /&gt;
 TestMap.addLayer(factory: canvas.SymbolLayer, type_arg: type.name, visible: type.vis, priority: type.zindex,);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Customizing MapStructure Styling  ==&lt;br /&gt;
{{WIP}}&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code &lt;br /&gt;
|-&lt;br /&gt;
| [[File:Snippets-canvas-mapstructure-dialog.png|thumb|[[MapStructure]] layers shown in a Canvas GUI dialog]]&lt;br /&gt;
| {{Note|This assumes that you already have a top-level root group set up, and named it '''root''', just change this variable if you want it to be rendered elsewhere. Equally, this snippet assumes that your canvas is named '''myCanvas'''.}}&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
var TestMap = root.createChild(&amp;quot;map&amp;quot;);&lt;br /&gt;
TestMap.setController(&amp;quot;Aircraft position&amp;quot;);&lt;br /&gt;
TestMap.setRange(25); &lt;br /&gt;
 &lt;br /&gt;
TestMap.setTranslation(    myCanvas.get(&amp;quot;view[0]&amp;quot;)/2,&lt;br /&gt;
                           myCanvas.get(&amp;quot;view[1]&amp;quot;)/2&lt;br /&gt;
                        );&lt;br /&gt;
var r = func(name,vis=1,zindex=nil) return caller(0)[0];&lt;br /&gt;
var type = r('APT');&lt;br /&gt;
var style_apt = {&lt;br /&gt;
    scale_factor:0.5,&lt;br /&gt;
    color_default:[0,1,0.9],&lt;br /&gt;
    line_width:4,&lt;br /&gt;
    label_font_color:[0,1,0.9],&lt;br /&gt;
    label_font_size:30&lt;br /&gt;
};&lt;br /&gt;
TestMap.addLayer(factory: canvas.SymbolLayer, type_arg: type.name, visible: type.vis, priority: type.zindex,style:style_apt);&lt;br /&gt;
&lt;br /&gt;
var type = r('VOR');&lt;br /&gt;
var style_vor = {&lt;br /&gt;
    scale_factor:0.5,&lt;br /&gt;
    color:[0.1,0.9,0],&lt;br /&gt;
    line_width:4,&lt;br /&gt;
    text_color:[0.1,0.9,0]&lt;br /&gt;
};&lt;br /&gt;
TestMap.addLayer(factory: canvas.SymbolLayer, type_arg: type.name, visible: type.vis, priority: type.zindex,style:style_vor);&lt;br /&gt;
&lt;br /&gt;
### See ...fgdata/Nasal/canvas/map/APT.symbol   ...fgdata/Nasal/canvas/map/VOR.style ... for the style variables that can be configured.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Using MapStructure and Overlays ==&lt;br /&gt;
{{FGCquote&lt;br /&gt;
  |Support for overlays using geo-referenced raster images isn't too well-developed currently, so would need to work with a few hard-coded assumptions (e.g. being specific to a certain raster image and map layout) - but maybe TheTom can provide a few more ideas on how to proceed from here - otherwise, the Map is normally not aware of any non-Map items, i.e. as long as the overlay is added &amp;quot;outside&amp;quot; the Map, it isn't even aware of the raster image - and when it is added as a map element, it would probably be rotated. So there's still some work needed here. But generally, this should work well enough even in its current form (the screen shot is purely based on the Canvas Snippets article, i.e. it just displays a Canvas GUI dialog, adds the downloaded raster image and then adds the MapStructure APS layer - without the Map being aware of the overlay/geo-referencing that is needed currently).&lt;br /&gt;
  |{{cite web |url=http://forum.flightgear.org/viewtopic.php?p=238316#p238316&lt;br /&gt;
     |title=&amp;lt;nowiki&amp;gt;Re: Using Canvas for visualizing orbital flights (cont'd PM)&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     |author=&amp;lt;nowiki&amp;gt;Hooray&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     |date=&amp;lt;nowiki&amp;gt;Fri Apr 10&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
   }}&lt;br /&gt;
}}&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code &lt;br /&gt;
|-&lt;br /&gt;
| [[File:Canvas-overlay-with-mapstructure.png|thumb|Using the [[MapStructure]] framework in conjunction with raster images as overlays]]&lt;br /&gt;
| {{Note|This assumes that you already have a top-level root group set up, and named it '''root''', just change this variable if you want it to be rendered elsewhere. Equally, this snippet assumes that your canvas is named '''myCanvas'''.}}&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== A simple tile map ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code &lt;br /&gt;
|-&lt;br /&gt;
| [[File:Canvas - Tile map demo.png|thumb|A simple, canvas based tile map which is centered around the aircraft.]]&lt;br /&gt;
| &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
var (width,height) = (768,512);&lt;br /&gt;
var tile_size = 256;&lt;br /&gt;
&lt;br /&gt;
var window = canvas.Window.new([width, height],&amp;quot;dialog&amp;quot;)&lt;br /&gt;
                   .set('title', &amp;quot;Tile map demo&amp;quot;);&lt;br /&gt;
var g = window.getCanvas(1)&lt;br /&gt;
              .createGroup();&lt;br /&gt;
&lt;br /&gt;
# Simple user interface (Buttons for zoom and label for displaying it)&lt;br /&gt;
var zoom = 10;&lt;br /&gt;
var type = &amp;quot;map&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
var ui_root = window.getCanvas().createGroup();&lt;br /&gt;
var vbox = canvas.VBoxLayout.new();&lt;br /&gt;
window.setLayout(vbox);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
var button_in = canvas.gui.widgets.Button.new(ui_root, canvas.style, {})&lt;br /&gt;
                                  .setText(&amp;quot;+&amp;quot;)&lt;br /&gt;
                                  .listen(&amp;quot;clicked&amp;quot;, func changeZoom(1));&lt;br /&gt;
var button_out = canvas.gui.widgets.Button.new(ui_root, canvas.style, {})&lt;br /&gt;
                                  .setText(&amp;quot;-&amp;quot;)&lt;br /&gt;
                                  .listen(&amp;quot;clicked&amp;quot;, func changeZoom(-1));&lt;br /&gt;
button_in.setSizeHint([32, 32]);&lt;br /&gt;
button_out.setSizeHint([32, 32]);&lt;br /&gt;
&lt;br /&gt;
var label_zoom = canvas.gui.widgets.Label.new(ui_root, canvas.style, {});&lt;br /&gt;
&lt;br /&gt;
var button_box = canvas.HBoxLayout.new();&lt;br /&gt;
button_box.addItem(button_in);&lt;br /&gt;
button_box.addItem(label_zoom);&lt;br /&gt;
button_box.addItem(button_out);&lt;br /&gt;
button_box.addStretch(1);&lt;br /&gt;
&lt;br /&gt;
vbox.addItem(button_box);&lt;br /&gt;
vbox.addStretch(1);&lt;br /&gt;
&lt;br /&gt;
var changeZoom = func(d)&lt;br /&gt;
{&lt;br /&gt;
  zoom = math.max(2, math.min(19, zoom + d));&lt;br /&gt;
  label_zoom.setText(&amp;quot;Zoom &amp;quot; ~ zoom);&lt;br /&gt;
  updateTiles();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# http://polymaps.org/docs/&lt;br /&gt;
# https://github.com/simplegeo/polymaps&lt;br /&gt;
# https://github.com/Leaflet/Leaflet&lt;br /&gt;
&lt;br /&gt;
var maps_base = getprop(&amp;quot;/sim/fg-home&amp;quot;) ~ '/cache/maps';&lt;br /&gt;
&lt;br /&gt;
# http://otile1.mqcdn.com/tiles/1.0.0/map&lt;br /&gt;
# http://otile1.mqcdn.com/tiles/1.0.0/sat&lt;br /&gt;
# (also see http://wiki.openstreetmap.org/wiki/Tile_usage_policy)&lt;br /&gt;
var makeUrl =&lt;br /&gt;
  string.compileTemplate('http://otile1.mqcdn.com/tiles/1.0.0/{type}/{z}/{x}/{y}.jpg');&lt;br /&gt;
var makePath =&lt;br /&gt;
  string.compileTemplate(maps_base ~ '/osm-{type}/{z}/{x}/{y}.jpg');&lt;br /&gt;
var num_tiles = [4, 3];&lt;br /&gt;
&lt;br /&gt;
var center_tile_offset = [&lt;br /&gt;
  (num_tiles[0] - 1) / 2,&lt;br /&gt;
  (num_tiles[1] - 1) / 2&lt;br /&gt;
];&lt;br /&gt;
&lt;br /&gt;
# simple aircraft icon at current position/center of the map&lt;br /&gt;
g.createChild(&amp;quot;path&amp;quot;)&lt;br /&gt;
 .moveTo( tile_size * center_tile_offset[0] - 10,&lt;br /&gt;
          tile_size * center_tile_offset[1] )&lt;br /&gt;
 .horiz(20)&lt;br /&gt;
 .move(-10,-10)&lt;br /&gt;
 .vert(20)&lt;br /&gt;
 .set(&amp;quot;stroke&amp;quot;, &amp;quot;red&amp;quot;)&lt;br /&gt;
 .set(&amp;quot;stroke-width&amp;quot;, 2)&lt;br /&gt;
 .set(&amp;quot;z-index&amp;quot;, 1);&lt;br /&gt;
&lt;br /&gt;
##&lt;br /&gt;
# initialize the map by setting up&lt;br /&gt;
# a grid of raster images  &lt;br /&gt;
&lt;br /&gt;
var tiles = setsize([], num_tiles[0]);&lt;br /&gt;
for(var x = 0; x &amp;lt; num_tiles[0]; x += 1)&lt;br /&gt;
{&lt;br /&gt;
  tiles[x] = setsize([], num_tiles[1]);&lt;br /&gt;
  for(var y = 0; y &amp;lt; num_tiles[1]; y += 1)&lt;br /&gt;
    tiles[x][y] = g.createChild(&amp;quot;image&amp;quot;, &amp;quot;map-tile&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var last_tile = [-1,-1];&lt;br /&gt;
var last_type = type;&lt;br /&gt;
&lt;br /&gt;
##&lt;br /&gt;
# this is the callback that will be regularly called by the timer&lt;br /&gt;
# to update the map&lt;br /&gt;
var updateTiles = func()&lt;br /&gt;
{&lt;br /&gt;
  # get current position&lt;br /&gt;
  var lat = getprop('/position/latitude-deg');&lt;br /&gt;
  var lon = getprop('/position/longitude-deg');&lt;br /&gt;
&lt;br /&gt;
  var n = math.pow(2, zoom);&lt;br /&gt;
  var offset = [&lt;br /&gt;
    n * ((lon + 180) / 360) - center_tile_offset[0],&lt;br /&gt;
    (1 - math.ln(math.tan(lat * math.pi/180) + 1 / math.cos(lat * math.pi/180)) / math.pi) / 2 * n - center_tile_offset[1]&lt;br /&gt;
  ];&lt;br /&gt;
  var tile_index = [int(offset[0]), int(offset[1])];&lt;br /&gt;
&lt;br /&gt;
  var ox = tile_index[0] - offset[0];&lt;br /&gt;
  var oy = tile_index[1] - offset[1];&lt;br /&gt;
&lt;br /&gt;
  for(var x = 0; x &amp;lt; num_tiles[0]; x += 1)&lt;br /&gt;
    for(var y = 0; y &amp;lt; num_tiles[1]; y += 1)&lt;br /&gt;
      tiles[x][y].setTranslation(int((ox + x) * tile_size + 0.5), int((oy + y) * tile_size + 0.5));&lt;br /&gt;
&lt;br /&gt;
  if(    tile_index[0] != last_tile[0]&lt;br /&gt;
      or tile_index[1] != last_tile[1]&lt;br /&gt;
      or type != last_type )&lt;br /&gt;
  {&lt;br /&gt;
    for(var x = 0; x &amp;lt; num_tiles[0]; x += 1)&lt;br /&gt;
      for(var y = 0; y &amp;lt; num_tiles[1]; y += 1)&lt;br /&gt;
      {&lt;br /&gt;
        var pos = {&lt;br /&gt;
          z: zoom,&lt;br /&gt;
          x: int(offset[0] + x),&lt;br /&gt;
          y: int(offset[1] + y),&lt;br /&gt;
          type: type&lt;br /&gt;
        };&lt;br /&gt;
&lt;br /&gt;
        (func {&lt;br /&gt;
        var img_path = makePath(pos);&lt;br /&gt;
        var tile = tiles[x][y];&lt;br /&gt;
&lt;br /&gt;
        if( io.stat(img_path) == nil )&lt;br /&gt;
        { # image not found, save in $FG_HOME&lt;br /&gt;
          var img_url = makeUrl(pos);&lt;br /&gt;
          print('requesting ' ~ img_url);&lt;br /&gt;
          http.save(img_url, img_path)&lt;br /&gt;
              .done(func {print('received image ' ~ img_path); tile.set(&amp;quot;src&amp;quot;, img_path);})&lt;br /&gt;
              .fail(func (r) print('Failed to get image ' ~ img_path ~ ' ' ~ r.status ~ ': ' ~ r.reason));&lt;br /&gt;
        }&lt;br /&gt;
        else # cached image found, reusing&lt;br /&gt;
        {&lt;br /&gt;
          print('loading ' ~ img_path);&lt;br /&gt;
          tile.set(&amp;quot;src&amp;quot;, img_path)&lt;br /&gt;
        }&lt;br /&gt;
        })();&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
    last_tile = tile_index;&lt;br /&gt;
    last_type = type;&lt;br /&gt;
  }&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
##&lt;br /&gt;
# set up a timer that will invoke updateTiles at 2-second intervals&lt;br /&gt;
var update_timer = maketimer(2, updateTiles);&lt;br /&gt;
# actually start the timer&lt;br /&gt;
update_timer.start();&lt;br /&gt;
&lt;br /&gt;
##&lt;br /&gt;
# set up default zoom level&lt;br /&gt;
changeZoom(0);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
###&lt;br /&gt;
# The following lines were recently added and have not yet been tested&lt;br /&gt;
# (if in doubt, remove them)&lt;br /&gt;
window.del = func()&lt;br /&gt;
{&lt;br /&gt;
  print(&amp;quot;Cleaning up window:&amp;quot;,title,&amp;quot;\n&amp;quot;);&lt;br /&gt;
  update_timer.stop();&lt;br /&gt;
# explanation for the call() technique at: http://wiki.flightgear.org/Object_oriented_programming_in_Nasal#Making_safer_base-class_calls&lt;br /&gt;
  call(canvas.Window.del, [], me);&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Adding a NavDisplay ==&lt;br /&gt;
&lt;br /&gt;
== Adding a CDU ==&lt;br /&gt;
&lt;br /&gt;
== Adding a Multi-Function Display (MFD) ==&lt;/div&gt;</summary>
		<author><name>Clm76</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Canvas_snippets&amp;diff=105418</id>
		<title>Canvas snippets</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Canvas_snippets&amp;diff=105418"/>
		<updated>2016-10-27T19:42:50Z</updated>

		<summary type="html">&lt;p&gt;Clm76: /* Customizing MapStructure Styling */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Stub}}&lt;br /&gt;
{{-}}&lt;br /&gt;
This article is meant to be a collection of self-contained code snippets for doing Nasal/Canvas coding without having to spend too much time working through APIs, documentation and existing code - the idea is to provide snippets that contain comments and explicit assumptions, so that these snippets can be easily adapted by people without necessarily having to be very familiar with Nasal/Canvas coding. In the long term, we're hoping to grow a library of useful code snippets to cover most use-cases, while also covering more complex scenarios, e.g. using Canvas-specific helper frameworks like [[Canvas MapStructure]], the [[NavDisplay]] or the recent CDU work.&lt;br /&gt;
&lt;br /&gt;
Canvas itself is designed to be front-end agnostic, meaning that it doesn't matter where a Canvas is used/displayed - this is accomplished by so called &amp;quot;placements&amp;quot;, which determine where a Canvas texture shown. Thus, the back-end logic can remain the same usually, so that GUI dialogs may show avionics, but also so that avionics may show GUI widgets, which also applies to HUDs, liveries and even placements within the FlightGear scenery.&lt;br /&gt;
&lt;br /&gt;
We encourage people to use the snippets listed below to get started with Nasal/Canvas coding, but also to provide feedback on extending/improving this article to make it even more accessible and useful. Equally, if you're aware of any Canvas-related efforts that may contain useful code snippets, please do feel free to add those snippets here. Even people who don't have any interest in coding itself are encouraged to get involved by helping test the snippets listed below, for which you only need to the [[Nasal Console]], and while you're at it, please also help provide/update screen shots for each code snippet.&lt;br /&gt;
&lt;br /&gt;
Contributions added to this article should ideally satisfy some requirements:&lt;br /&gt;
* be entirely self-contained&lt;br /&gt;
* contain good/clear comments&lt;br /&gt;
* have pointers/references to related code/use-cases&lt;br /&gt;
* make assumptions explicit&lt;br /&gt;
* contain screen shots for each example&lt;br /&gt;
* avoid overloaded symbol names to ensure that examples can be merged and adapted easily&lt;br /&gt;
* dependencies (svg files, images/textures etc) should always be chosen such that snippets always work using $FG_ROOT&lt;br /&gt;
* hard-coded assumptions (e.g. texture dimensions in terms of width/height etc) should be encapsulated using variables&lt;br /&gt;
 &lt;br /&gt;
{{Canvas Navigation}}&lt;br /&gt;
&lt;br /&gt;
== Creating a standalone Canvas ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[Special:UploadWizard|Upload requested]] || &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
var (width, height) = (512,512);&lt;br /&gt;
# Create a standalone Canvas (not attached to any GUI dialog/aircraft etc) &lt;br /&gt;
var myCanvas = canvas.new({&lt;br /&gt;
  &amp;quot;name&amp;quot;: &amp;quot;Livery Test&amp;quot;,   # The name is optional but allow for easier identification&lt;br /&gt;
  &amp;quot;size&amp;quot;: [width, height], # Size of the underlying texture (should be a power of 2, required) [Resolution]&lt;br /&gt;
  &amp;quot;view&amp;quot;: [width, height],  # Virtual resolution (Defines the coordinate system of the canvas [Dimensions]&lt;br /&gt;
                        # which will be stretched the size of the texture, required)&lt;br /&gt;
  &amp;quot;mipmapping&amp;quot;: 1       # Enable mipmapping (optional)&lt;br /&gt;
});&lt;br /&gt;
&lt;br /&gt;
# set background color&lt;br /&gt;
myCanvas.set(&amp;quot;background&amp;quot;, canvas.style.getColor(&amp;quot;bg_color&amp;quot;));&lt;br /&gt;
&lt;br /&gt;
# creating the top-level/root group which will contain all other elements/group&lt;br /&gt;
var root = myCanvas.createGroup();&lt;br /&gt;
&lt;br /&gt;
# OPTIONAL: Create a Canvas dialog window to hold the canvas and show that it's working&lt;br /&gt;
# the Canvas is now standalone, i.e. continues to live once the dialog is closed!&lt;br /&gt;
var window = canvas.Window.new([width,height],&amp;quot;dialog&amp;quot;);&lt;br /&gt;
window.setCanvas(myCanvas);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Accessing the Canvas Desktop ==&lt;br /&gt;
Sometimes you may want to render to the main screen, without creating a separate Canvas window - this can be accomplished by using the Canvas desktop, note that the following example is fully self-contained, i.e. does not require any code to be added to work:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[Special:UploadWizard|Upload requested]] || &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
var myNode = canvas.getDesktop().createChild(&amp;quot;text&amp;quot;)&lt;br /&gt;
      .setText(&amp;quot;Hello Canvas Desktop&amp;quot;)&lt;br /&gt;
      .setFontSize(25, 1.0)          # font size (in texels) and font aspect ratio&lt;br /&gt;
      .setColor(1,0,0,1)             # red, fully opaque&lt;br /&gt;
      .setAlignment(&amp;quot;center-center&amp;quot;) # how the text is aligned to where you place it&lt;br /&gt;
      .setTranslation(160, 80);     # where to place the text&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Creating Tooltips ==&lt;br /&gt;
== Creating Popups ==&lt;br /&gt;
== Creating a Canvas GUI Window ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Snippets-canvas-dialog.png|left|thumb|This is what the [[Nasal]]/[[Canvas]] snippet will look like once you pasted it into the [[Nasal Console]] and click &amp;quot;Execute&amp;quot;.]] || {{Note|This example uses so called method chaining, if you're not familiar with the concept, please see: [[Object_Oriented_Programming_with_Nasal#More_on_methods:_Chaining|Method Chaining]].}}&lt;br /&gt;
&lt;br /&gt;
{{Canvas Snippets Boilerplate}}&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Adding Raster Images ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code &lt;br /&gt;
|-&lt;br /&gt;
| [[File:Canvas Snippets-raster image.png|thumb|Canvas snippet demonstrating how to load a raster image]] ||&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# path is relative to $FG_ROOT (base package)&lt;br /&gt;
var path = &amp;quot;Textures/Splash1.png&amp;quot;;&lt;br /&gt;
# create an image child for the texture&lt;br /&gt;
var child = root.createChild(&amp;quot;image&amp;quot;)&lt;br /&gt;
    .setFile(path)&lt;br /&gt;
    .setTranslation(100, 10)&lt;br /&gt;
    .setSize(130, 130);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Adding Raster Images downloaded on demand ==&lt;br /&gt;
&lt;br /&gt;
The '''path''' could also just as well be a URL, i.e. a raster image retrieved via http - for example, the following snippet is entirely self-contained and can be pasted into the [[Nasal Console]] and directly executed &amp;quot;as is&amp;quot;: &lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Canvas-raster-images-via-url.png|thumb|screen shot demonstrating how Nasal and Canvas can be used to display raster images downloaded on demand]] ||&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# create a new window, dimensions are 400 x 200, using the dialog decoration (i.e. titlebar)&lt;br /&gt;
var window = canvas.Window.new([400,200],&amp;quot;dialog&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
# adding a canvas to the new window and setting up background colors/transparency&lt;br /&gt;
var myCanvas = window.createCanvas().set(&amp;quot;background&amp;quot;, canvas.style.getColor(&amp;quot;bg_color&amp;quot;));&lt;br /&gt;
&lt;br /&gt;
# Using specific css colors would also be possible:&lt;br /&gt;
# myCanvas.set(&amp;quot;background&amp;quot;, &amp;quot;#ffaac0&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
# creating the top-level/root group which will contain all other elements/group&lt;br /&gt;
var root = myCanvas.createGroup();&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
# path now a URL&lt;br /&gt;
var url = &amp;quot;http://www.worldwidetelescope.org/docs/Images/MapOfEarth.jpg&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
# create an image child for the texture&lt;br /&gt;
var child=root.createChild(&amp;quot;image&amp;quot;)&lt;br /&gt;
    .setFile( url ) &lt;br /&gt;
    .setTranslation(45,22) # centered, in relation to dialog coordinates&lt;br /&gt;
    .setSize(310,155); # image dimensions&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Clipping ==&lt;br /&gt;
{{Note|{{FGCquote&lt;br /&gt;
|1= Scaling or any other type of transformation or changing the coordinates of individual points is definitely more efficient than clipping (which requires to change the OpenGL clip planes for every rendered object with a different clipping rectangle).&lt;br /&gt;
|2= {{cite web&lt;br /&gt;
  | url    = http://forum.flightgear.org/viewtopic.php?p=277062#p277062&lt;br /&gt;
  | title  = &amp;lt;nowiki&amp;gt;Re: Space Shuttle&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  | author = &amp;lt;nowiki&amp;gt;TheTom&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  | date   = Feb 21st, 2016&lt;br /&gt;
  | added   = Feb 21st, 2016&lt;br /&gt;
  | script_version = 0.25&lt;br /&gt;
  }}&lt;br /&gt;
}}&lt;br /&gt;
}}&lt;br /&gt;
I would suggest to refer to api.nas and look for &amp;quot;clip&amp;quot; and/or &amp;quot;rect&amp;quot; - IIRC, you need to set up a clipping rectangle by setting some kind of &amp;quot;clip&amp;quot; property and setting it to a rect value in the form of rect(...)&lt;br /&gt;
{{FGCquote&lt;br /&gt;
|1= For details, see the clipping example at: [[Canvas Nasal API#set 2]] and  [[Canvas Element#clip.28.29]]&lt;br /&gt;
|2= {{cite web&lt;br /&gt;
  | url    = http://forum.flightgear.org/viewtopic.php?p=276965#p276965&lt;br /&gt;
  | title  = &amp;lt;nowiki&amp;gt;Re: Space Shuttle&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  | author = &amp;lt;nowiki&amp;gt;Hooray&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  | date   = Feb 21st, 2016&lt;br /&gt;
  | added   = Feb 21st, 2016&lt;br /&gt;
  | script_version = 0.25&lt;br /&gt;
  }}&lt;br /&gt;
}}&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[File:MapStructure_dialog_with_clipping_and_event_handling_applied.png|thumb]] ||&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# create a new window, dimensions are 400 x 200, using the dialog decoration (i.e. titlebar)&lt;br /&gt;
var window = canvas.Window.new([400,200],&amp;quot;dialog&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
# adding a canvas to the new window and setting up background colors/transparency&lt;br /&gt;
var myCanvas = window.createCanvas().set(&amp;quot;background&amp;quot;, canvas.style.getColor(&amp;quot;bg_color&amp;quot;));&lt;br /&gt;
&lt;br /&gt;
# Using specific css colors would also be possible:&lt;br /&gt;
# myCanvas.set(&amp;quot;background&amp;quot;, &amp;quot;#ffaac0&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
# creating the top-level/root group which will contain all other elements/group&lt;br /&gt;
var root = myCanvas.createGroup();&lt;br /&gt;
&lt;br /&gt;
# create an image child for the texture&lt;br /&gt;
var child=root.createChild(&amp;quot;.........&amp;quot;)&lt;br /&gt;
    .setFile( url ) &lt;br /&gt;
    .setTranslation(45,22) # centered, in relation to dialog coordinates&lt;br /&gt;
    .setSize(310,155); # image dimensions&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Adding OpenVG Paths ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Openvg-via-canvas.png|thumb|screen shot showing a simple Canvas GUI dialog demonstrating how to use OpenVG-path drawing via Nasal and Canvas]] &lt;br /&gt;
| {{Note|This assumes that you already have a top-level root group set up, and named it '''root'''.}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
var graph = root.createChild(&amp;quot;group&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
var x_axis = graph.createChild(&amp;quot;path&amp;quot;, &amp;quot;x-axis&amp;quot;)&lt;br /&gt;
.moveTo(10, height/2)&lt;br /&gt;
.lineTo(width-10, height/2)&lt;br /&gt;
.setColor(1,0,0)&lt;br /&gt;
.setStrokeLineWidth(3);&lt;br /&gt;
&lt;br /&gt;
var y_axis = graph.createChild(&amp;quot;path&amp;quot;, &amp;quot;y-axis&amp;quot;)&lt;br /&gt;
.moveTo(10, 10)&lt;br /&gt;
.lineTo(10, height-10)&lt;br /&gt;
.setColor(0,0,1)&lt;br /&gt;
.setStrokeLineWidth(2);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Canvas-openvg-quadTo.png|thumb|screen shot showing a simple Canvas GUI dialog demonstrating how to use OpenVG-path drawing via Nasal and Canvas (Canvas/OpenVG quadTo API for drawing curves)]] &lt;br /&gt;
| {{Note|This assumes that you are appending this to the snippet shown above.}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
var points = [	60,height-20, &lt;br /&gt;
		230,height-100,&lt;br /&gt;
		];&lt;br /&gt;
&lt;br /&gt;
var track = graph.createChild(&amp;quot;path&amp;quot;, &amp;quot;track&amp;quot;)&lt;br /&gt;
.moveTo(10, height/2)&lt;br /&gt;
.quadTo( points )&lt;br /&gt;
.setColor(0,1,0)&lt;br /&gt;
.setStrokeLineWidth(4);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Canvas-openvg-cubicTo.png|thumb|screen shot showing a simple Canvas GUI dialog demonstrating how to use OpenVG-path drawing via Nasal and Canvas (Canvas/OpenVG cubicTo API for drawing curves)]] &lt;br /&gt;
| {{Note|This assumes that you are appending this to the snippet shown above.}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
var points = [	60,height-20, &lt;br /&gt;
		120,height-120,&lt;br /&gt;
		230,height-100,&lt;br /&gt;
		];&lt;br /&gt;
&lt;br /&gt;
var track = graph.createChild(&amp;quot;path&amp;quot;, &amp;quot;track&amp;quot;)&lt;br /&gt;
.moveTo(10, height/2)&lt;br /&gt;
.cubicTo( points )&lt;br /&gt;
.setColor(0,1,0)&lt;br /&gt;
.setStrokeLineWidth(4);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Adding Vector Images ==&lt;br /&gt;
&lt;br /&gt;
[[Category:Canvas SVG]]&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Canvas-svg-support.png|thumb|screen shot demonstrating how the scripted Nasal-based SVG parser can be used to dynamically turn SVG files into OpenVG instructions understood by Canvas]] &lt;br /&gt;
| {{Note|This assumes that you already have a top-level root group set up, and named it '''root'''.}}&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
# change the background color &lt;br /&gt;
myCanvas.set(&amp;quot;background&amp;quot;, &amp;quot;#ffaac0&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
# creating the top-level/root group which will contain all other elements/group&lt;br /&gt;
var root = myCanvas.createGroup();&lt;br /&gt;
&lt;br /&gt;
var filename = &amp;quot;/Nasal/canvas/map/Images/boeingAirplane.svg&amp;quot;;&lt;br /&gt;
var svg_symbol = root.createChild('group');&lt;br /&gt;
canvas.parsesvg(svg_symbol, filename);&lt;br /&gt;
&lt;br /&gt;
svg_symbol.setTranslation(width/2,height/2);&lt;br /&gt;
&lt;br /&gt;
#svg_symbol.setScale(0.2);&lt;br /&gt;
#svg_symbol.setRotation(radians)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Using the SymbolCache ==&lt;br /&gt;
Whenever a symbol may need to be shown/instanced multiple times (possibly using different styling), it makes sense to use caching - otherwise, identical symbols would be treated as separate OpenVG groups, all of which would need to be rasterized/rendedered separately (i.e. 100 identical symbols would be updated/rendered one by one). &lt;br /&gt;
&lt;br /&gt;
Typically, a map may display multiple instances of an otherwise identical symbol (think VOR, NDB, DME etc) - equally, a multiplayer map may showing multiple aircraft symbols at the same time. In these cases, it makes sense to use the SymbolCache framework, which will render symbols into a separate Canvas texture and provide a texture map that can be treated as a lookup map, which even supports styling for otherwise identical symbols. To learn more, please refer to [[Canvas MapStructure#The_SymbolCache|SymbolCache]]&lt;br /&gt;
... &lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Canvas-SymbolCache-Instancing.png|thumb|Screen shot showing a Canvas based GUI dialog that is using the SymbolCache for instancing multiple symbols (including support for styling)]] &lt;br /&gt;
| &amp;lt;!--{{Caution|This is currently untested code}}--&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
##&lt;br /&gt;
# this is the function that draws a symbol using OpenVG paths&lt;br /&gt;
# it accepts a group to draw to and returns the rendered group &lt;br /&gt;
# to the caller&lt;br /&gt;
var drawVOR = func(group) {&lt;br /&gt;
    return group.createChild(&amp;quot;path&amp;quot;)&lt;br /&gt;
        .moveTo(-15,0)&lt;br /&gt;
        .lineTo(-7.5,12.5)&lt;br /&gt;
        .lineTo(7.5,12.5)&lt;br /&gt;
        .lineTo(15,0)&lt;br /&gt;
        .lineTo(7.5,-12.5)&lt;br /&gt;
        .lineTo(-7.5,-12.5)&lt;br /&gt;
        .close()&lt;br /&gt;
        .setStrokeLineWidth(line_width) # style-able&lt;br /&gt;
        .setColor(color); # style-able&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
var style = { # styling related attributes (as per the draw* function above)&lt;br /&gt;
    line_width: 3,&lt;br /&gt;
    scale_factor: 1,&lt;br /&gt;
    color: [1,0,0],&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
# create a new cache entry for the styled symbol&lt;br /&gt;
var myCachedSymbol = canvas.StyleableCacheable.new(&lt;br /&gt;
    name:'myVOR', draw_func: drawVOR,&lt;br /&gt;
    cache: canvas.SymbolCache32x32, # the cache to be used&lt;br /&gt;
    draw_mode: canvas.SymbolCache.DRAW_CENTERED,&lt;br /&gt;
    relevant_keys: ['line_width', 'color'], # styling related attributes&lt;br /&gt;
);&lt;br /&gt;
&lt;br /&gt;
var target = root.createChild('group');&lt;br /&gt;
&lt;br /&gt;
var x=0;&lt;br /&gt;
var y=height/2;&lt;br /&gt;
&lt;br /&gt;
var xoffset=50;&lt;br /&gt;
&lt;br /&gt;
# render 5 instanced symbols using the style specified above&lt;br /&gt;
for (var i=0;i&amp;lt;5;i+=1) {&lt;br /&gt;
# look up the raster image for the symbol&lt;br /&gt;
# render it using the passed style and adjust scaling&lt;br /&gt;
var instanced = myCachedSymbol.render(target, style)&lt;br /&gt;
  .setScale(style.scale_factor)&lt;br /&gt;
  .setTranslation(x+=xoffset,y);&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The example shown above uses a fixed symbol/icon cache that is set up while booting Flightgear - sometimes, we may need cache for different purposes. So, let's assume, we need a new/custom cache with a different resolution for each entry in the cache (e.g. 256x256), we can easily accomplish that by setting up a new cache like this: &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
var mySymbolCache256x256 = canvas.SymbolCache.new(1024,256);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Adding Text Elements ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[File:CanvasText-Hello-World.png|thumb|Screen shot showing the CanvasText example contributed by Necolatis]]&lt;br /&gt;
| {{Note|This assumes that you already have a top-level root group set up, and named it '''root'''.}}&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
var myText = root.createChild(&amp;quot;text&amp;quot;)&lt;br /&gt;
      .setText(&amp;quot;Hello world!&amp;quot;)&lt;br /&gt;
      .setFontSize(20, 0.9)          # font size (in texels) and font aspect ratio&lt;br /&gt;
      .setColor(1,0,0,1)             # red, fully opaque&lt;br /&gt;
      .setAlignment(&amp;quot;center-center&amp;quot;) # how the text is aligned to where you place it&lt;br /&gt;
      .setTranslation(160, 80);     # where to place the text&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Adding GUI Labels ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Canvas-Layout-Label-example-by-Necolatis.png|thumb|Canvas demo: Layouts and Labels (by Necolatis)]]&lt;br /&gt;
| {{Note|This assumes that you already have a top-level root group set up, and named it '''root''', just change this variable if you want it to be rendered elsewhere. It also assumes you have a Layout item setup and called '''myLayoutItem'''.}}&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
# create a new layout&lt;br /&gt;
var myLayout = canvas.HBoxLayout.new();&lt;br /&gt;
# assign it to the Canvas&lt;br /&gt;
myCanvas.setLayout(myLayout);&lt;br /&gt;
&lt;br /&gt;
var label = canvas.gui.widgets.Label.new(root, canvas.style, {wordWrap: 0}); # wordwrap: 0 will disable wordwrapping, to enable it use 1 instead&lt;br /&gt;
label.setText(&amp;quot;Hello World!&amp;quot;);&lt;br /&gt;
myLayout.addItem(label);&lt;br /&gt;
&lt;br /&gt;
var label2 = canvas.gui.widgets.Label.new(root, canvas.style, {wordWrap: 0}); # wordwrap: 0 will disable wordwrapping, to enable it use 1 instead&lt;br /&gt;
label2.setText(&amp;quot;Hello FlightGear&amp;quot;);&lt;br /&gt;
myLayout.addItem(label2);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Adding GUI Buttons (Layouts)==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Canvas-demo-layouts-and-buttons-by-Necolatis.png|thumb|Canvas snippet: buttons and layouts (by Necolatis)]] &lt;br /&gt;
|&lt;br /&gt;
&lt;br /&gt;
{{Note|This assumes that you already have a top-level root group set up, and named it '''root''', just change this variable if you want it to be rendered elsewhere. It also assumes you have a Layout item setup and called '''myLayoutItem'''.}}&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
# create a new layout&lt;br /&gt;
var myLayout = canvas.HBoxLayout.new();&lt;br /&gt;
# assign it to the Canvas&lt;br /&gt;
myCanvas.setLayout(myLayout);&lt;br /&gt;
&lt;br /&gt;
# click button&lt;br /&gt;
&lt;br /&gt;
var button = canvas.gui.widgets.Button.new(root, canvas.style, {})&lt;br /&gt;
	.setText(&amp;quot;Click on me&amp;quot;)&lt;br /&gt;
	.setFixedSize(75, 25);&lt;br /&gt;
&lt;br /&gt;
button.listen(&amp;quot;clicked&amp;quot;, func {&lt;br /&gt;
        # add code here to react on click on button.&lt;br /&gt;
print(&amp;quot;Button clicked !&amp;quot;);&lt;br /&gt;
});&lt;br /&gt;
&lt;br /&gt;
myLayout.addItem(button);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| [[File:Canvas-toggle-button-snippet-by-Necolatis.png|thumb|Canvas toggle button demo by Necolatis]]&lt;br /&gt;
| &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
# create a new layout&lt;br /&gt;
var myLayout = canvas.HBoxLayout.new();&lt;br /&gt;
# assign it to the Canvas&lt;br /&gt;
myCanvas.setLayout(myLayout);&lt;br /&gt;
&lt;br /&gt;
var button = canvas.gui.widgets.Button.new(root, canvas.style, {})&lt;br /&gt;
        .setText(&amp;quot;Toggle me&amp;quot;)&lt;br /&gt;
        .setCheckable(1) # this indicates that is should be a toggle button&lt;br /&gt;
        .setChecked(0) # depressed by default&lt;br /&gt;
        .setFixedSize(75, 25);&lt;br /&gt;
&lt;br /&gt;
button.listen(&amp;quot;toggled&amp;quot;, func (e) {&lt;br /&gt;
        if( e.detail.checked ) {&lt;br /&gt;
            # add code here to react on button being depressed.&lt;br /&gt;
        } else {&lt;br /&gt;
            # add code here to react on button not being depressed.&lt;br /&gt;
        }&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
myLayout.addItem(button);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Canvas Input Dialog ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Snippets-canvas-input-dialog.png|thumb|Canvas input dialog]]&lt;br /&gt;
||&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
# create a new InputDialog with a title, label, and a callback&lt;br /&gt;
canvas.InputDialog.getText(&amp;quot;Input Dialog Title&amp;quot;, &amp;quot;Please enter some text&amp;quot;, func(btn,value) {&lt;br /&gt;
    if (value) gui.popupTip(&amp;quot;You entered: &amp;quot;~value);&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Canvas ScrollArea ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code&lt;br /&gt;
|-&lt;br /&gt;
|[[File:Canvas-snippets-scrollArea-demo.png|thumb|Screen shot showing a Canvas ScrollArea populated with different splash screens, loaded from $FG_ROOT/Textures]]&lt;br /&gt;
||&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
var vbox = canvas.VBoxLayout.new();&lt;br /&gt;
myCanvas.setLayout(vbox);&lt;br /&gt;
&lt;br /&gt;
 var scroll = canvas.gui.widgets.ScrollArea.new(root, canvas.style, {size: [96, 128]}).move(20, 100);&lt;br /&gt;
 vbox.addItem(scroll, 1);&lt;br /&gt;
&lt;br /&gt;
var scrollContent =&lt;br /&gt;
      scroll.getContent()&lt;br /&gt;
            .set(&amp;quot;font&amp;quot;, &amp;quot;LiberationFonts/LiberationSans-Bold.ttf&amp;quot;)&lt;br /&gt;
            .set(&amp;quot;character-size&amp;quot;, 16)&lt;br /&gt;
            .set(&amp;quot;alignment&amp;quot;, &amp;quot;left-center&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
var list = canvas.VBoxLayout.new();&lt;br /&gt;
scroll.setLayout(list);&lt;br /&gt;
&lt;br /&gt;
for (var i=1;i&amp;lt;=5;i+=1) {&lt;br /&gt;
var label = canvas.gui.widgets.Label.new(scrollContent, canvas.style, {wordWrap: 0}); &lt;br /&gt;
label.setImage(&amp;quot;Textures/Splash&amp;quot;~i~&amp;quot;.png&amp;quot;);&lt;br /&gt;
label.setFixedSize(256,256);&lt;br /&gt;
list.addItem(label);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Using Layouts ==&lt;br /&gt;
&lt;br /&gt;
== Using Styling ==&lt;br /&gt;
== Adding a HUD ==&lt;br /&gt;
== Adding a 2D Instrument ==&lt;br /&gt;
== Adding a 2D Panel ==&lt;br /&gt;
== Adding a PFD ==&lt;br /&gt;
&lt;br /&gt;
== Adding a Failure Mgmt Widget ==&lt;br /&gt;
{{Note|This kind of widget will typically be useful for dialogs requiring a method for managing aircraft specific system failures (e.g. an instructor console).}}&lt;br /&gt;
&lt;br /&gt;
== Adding a MapStructure map to a Canvas  ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code &lt;br /&gt;
|-&lt;br /&gt;
| [[File:Snippets-canvas-mapstructure-dialog.png|thumb|[[MapStructure]] layers shown in a Canvas GUI dialog]]&lt;br /&gt;
| {{Note|This assumes that you already have a top-level root group set up, and named it '''root''', just change this variable if you want it to be rendered elsewhere. Equally, this snippet assumes that your canvas is named '''myCanvas'''.}}&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
var TestMap = root.createChild(&amp;quot;map&amp;quot;);&lt;br /&gt;
TestMap.setController(&amp;quot;Aircraft position&amp;quot;);&lt;br /&gt;
TestMap.setRange(25); &lt;br /&gt;
 &lt;br /&gt;
TestMap.setTranslation(    myCanvas.get(&amp;quot;view[0]&amp;quot;)/2,&lt;br /&gt;
                           myCanvas.get(&amp;quot;view[1]&amp;quot;)/2&lt;br /&gt;
                        );&lt;br /&gt;
var r = func(name,vis=1,zindex=nil) return caller(0)[0];&lt;br /&gt;
&lt;br /&gt;
foreach(var type; [r('APT'), r('VOR') ] )&lt;br /&gt;
 TestMap.addLayer(factory: canvas.SymbolLayer, type_arg: type.name, visible: type.vis, priority: type.zindex,);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Customizing MapStructure Styling  ==&lt;br /&gt;
{{WIP}}&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code &lt;br /&gt;
|-&lt;br /&gt;
| [[File:Snippets-canvas-mapstructure-dialog.png|thumb|[[MapStructure]] layers shown in a Canvas GUI dialog]]&lt;br /&gt;
| {{Note|This assumes that you already have a top-level root group set up, and named it '''root''', just change this variable if you want it to be rendered elsewhere. Equally, this snippet assumes that your canvas is named '''myCanvas'''.}}&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
var TestMap = root.createChild(&amp;quot;map&amp;quot;);&lt;br /&gt;
TestMap.setController(&amp;quot;Aircraft position&amp;quot;);&lt;br /&gt;
TestMap.setRange(25); &lt;br /&gt;
 &lt;br /&gt;
TestMap.setTranslation(    myCanvas.get(&amp;quot;view[0]&amp;quot;)/2,&lt;br /&gt;
                           myCanvas.get(&amp;quot;view[1]&amp;quot;)/2&lt;br /&gt;
                        );&lt;br /&gt;
var r = func(name,vis=1,zindex=nil) return caller(0)[0];&lt;br /&gt;
var type = r('APT');&lt;br /&gt;
var style_apt = {&lt;br /&gt;
    scale_factor:0.5,&lt;br /&gt;
    color_default:[0,1,0.9],&lt;br /&gt;
    line_width:4,&lt;br /&gt;
    label_font_color:[0,1,0.9],&lt;br /&gt;
    label_font_size:30&lt;br /&gt;
};&lt;br /&gt;
TestMap.addLayer(factory: canvas.SymbolLayer, type_arg: type.name, visible: type.vis, priority: type.zindex,style:style_apt);&lt;br /&gt;
&lt;br /&gt;
var type = r('VOR');&lt;br /&gt;
var style_vor = {&lt;br /&gt;
    scale_factor:0.5,&lt;br /&gt;
    color:[0.1,0.9,0],&lt;br /&gt;
    line_width:4,&lt;br /&gt;
    text_color:[0.1,0.9,0]&lt;br /&gt;
};&lt;br /&gt;
TestMap.addLayer(factory: canvas.SymbolLayer, type_arg: type.name, visible: type.vis, priority: type.zindex,style:style_vor);&lt;br /&gt;
&lt;br /&gt;
### See ...fgdata/Nasal/canvas/map/APT.symbol   ...fgdata/Nasal/canvas/map/VOR.style ... for the style variables that can be configured.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|&lt;br /&gt;
&lt;br /&gt;
== Using MapStructure and Overlays ==&lt;br /&gt;
{{FGCquote&lt;br /&gt;
  |Support for overlays using geo-referenced raster images isn't too well-developed currently, so would need to work with a few hard-coded assumptions (e.g. being specific to a certain raster image and map layout) - but maybe TheTom can provide a few more ideas on how to proceed from here - otherwise, the Map is normally not aware of any non-Map items, i.e. as long as the overlay is added &amp;quot;outside&amp;quot; the Map, it isn't even aware of the raster image - and when it is added as a map element, it would probably be rotated. So there's still some work needed here. But generally, this should work well enough even in its current form (the screen shot is purely based on the Canvas Snippets article, i.e. it just displays a Canvas GUI dialog, adds the downloaded raster image and then adds the MapStructure APS layer - without the Map being aware of the overlay/geo-referencing that is needed currently).&lt;br /&gt;
  |{{cite web |url=http://forum.flightgear.org/viewtopic.php?p=238316#p238316&lt;br /&gt;
     |title=&amp;lt;nowiki&amp;gt;Re: Using Canvas for visualizing orbital flights (cont'd PM)&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     |author=&amp;lt;nowiki&amp;gt;Hooray&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
     |date=&amp;lt;nowiki&amp;gt;Fri Apr 10&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
   }}&lt;br /&gt;
}}&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code &lt;br /&gt;
|-&lt;br /&gt;
| [[File:Canvas-overlay-with-mapstructure.png|thumb|Using the [[MapStructure]] framework in conjunction with raster images as overlays]]&lt;br /&gt;
| {{Note|This assumes that you already have a top-level root group set up, and named it '''root''', just change this variable if you want it to be rendered elsewhere. Equally, this snippet assumes that your canvas is named '''myCanvas'''.}}&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== A simple tile map ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Screenshot !! Code &lt;br /&gt;
|-&lt;br /&gt;
| [[File:Canvas - Tile map demo.png|thumb|A simple, canvas based tile map which is centered around the aircraft.]]&lt;br /&gt;
| &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
var (width,height) = (768,512);&lt;br /&gt;
var tile_size = 256;&lt;br /&gt;
&lt;br /&gt;
var window = canvas.Window.new([width, height],&amp;quot;dialog&amp;quot;)&lt;br /&gt;
                   .set('title', &amp;quot;Tile map demo&amp;quot;);&lt;br /&gt;
var g = window.getCanvas(1)&lt;br /&gt;
              .createGroup();&lt;br /&gt;
&lt;br /&gt;
# Simple user interface (Buttons for zoom and label for displaying it)&lt;br /&gt;
var zoom = 10;&lt;br /&gt;
var type = &amp;quot;map&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
var ui_root = window.getCanvas().createGroup();&lt;br /&gt;
var vbox = canvas.VBoxLayout.new();&lt;br /&gt;
window.setLayout(vbox);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
var button_in = canvas.gui.widgets.Button.new(ui_root, canvas.style, {})&lt;br /&gt;
                                  .setText(&amp;quot;+&amp;quot;)&lt;br /&gt;
                                  .listen(&amp;quot;clicked&amp;quot;, func changeZoom(1));&lt;br /&gt;
var button_out = canvas.gui.widgets.Button.new(ui_root, canvas.style, {})&lt;br /&gt;
                                  .setText(&amp;quot;-&amp;quot;)&lt;br /&gt;
                                  .listen(&amp;quot;clicked&amp;quot;, func changeZoom(-1));&lt;br /&gt;
button_in.setSizeHint([32, 32]);&lt;br /&gt;
button_out.setSizeHint([32, 32]);&lt;br /&gt;
&lt;br /&gt;
var label_zoom = canvas.gui.widgets.Label.new(ui_root, canvas.style, {});&lt;br /&gt;
&lt;br /&gt;
var button_box = canvas.HBoxLayout.new();&lt;br /&gt;
button_box.addItem(button_in);&lt;br /&gt;
button_box.addItem(label_zoom);&lt;br /&gt;
button_box.addItem(button_out);&lt;br /&gt;
button_box.addStretch(1);&lt;br /&gt;
&lt;br /&gt;
vbox.addItem(button_box);&lt;br /&gt;
vbox.addStretch(1);&lt;br /&gt;
&lt;br /&gt;
var changeZoom = func(d)&lt;br /&gt;
{&lt;br /&gt;
  zoom = math.max(2, math.min(19, zoom + d));&lt;br /&gt;
  label_zoom.setText(&amp;quot;Zoom &amp;quot; ~ zoom);&lt;br /&gt;
  updateTiles();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# http://polymaps.org/docs/&lt;br /&gt;
# https://github.com/simplegeo/polymaps&lt;br /&gt;
# https://github.com/Leaflet/Leaflet&lt;br /&gt;
&lt;br /&gt;
var maps_base = getprop(&amp;quot;/sim/fg-home&amp;quot;) ~ '/cache/maps';&lt;br /&gt;
&lt;br /&gt;
# http://otile1.mqcdn.com/tiles/1.0.0/map&lt;br /&gt;
# http://otile1.mqcdn.com/tiles/1.0.0/sat&lt;br /&gt;
# (also see http://wiki.openstreetmap.org/wiki/Tile_usage_policy)&lt;br /&gt;
var makeUrl =&lt;br /&gt;
  string.compileTemplate('http://otile1.mqcdn.com/tiles/1.0.0/{type}/{z}/{x}/{y}.jpg');&lt;br /&gt;
var makePath =&lt;br /&gt;
  string.compileTemplate(maps_base ~ '/osm-{type}/{z}/{x}/{y}.jpg');&lt;br /&gt;
var num_tiles = [4, 3];&lt;br /&gt;
&lt;br /&gt;
var center_tile_offset = [&lt;br /&gt;
  (num_tiles[0] - 1) / 2,&lt;br /&gt;
  (num_tiles[1] - 1) / 2&lt;br /&gt;
];&lt;br /&gt;
&lt;br /&gt;
# simple aircraft icon at current position/center of the map&lt;br /&gt;
g.createChild(&amp;quot;path&amp;quot;)&lt;br /&gt;
 .moveTo( tile_size * center_tile_offset[0] - 10,&lt;br /&gt;
          tile_size * center_tile_offset[1] )&lt;br /&gt;
 .horiz(20)&lt;br /&gt;
 .move(-10,-10)&lt;br /&gt;
 .vert(20)&lt;br /&gt;
 .set(&amp;quot;stroke&amp;quot;, &amp;quot;red&amp;quot;)&lt;br /&gt;
 .set(&amp;quot;stroke-width&amp;quot;, 2)&lt;br /&gt;
 .set(&amp;quot;z-index&amp;quot;, 1);&lt;br /&gt;
&lt;br /&gt;
##&lt;br /&gt;
# initialize the map by setting up&lt;br /&gt;
# a grid of raster images  &lt;br /&gt;
&lt;br /&gt;
var tiles = setsize([], num_tiles[0]);&lt;br /&gt;
for(var x = 0; x &amp;lt; num_tiles[0]; x += 1)&lt;br /&gt;
{&lt;br /&gt;
  tiles[x] = setsize([], num_tiles[1]);&lt;br /&gt;
  for(var y = 0; y &amp;lt; num_tiles[1]; y += 1)&lt;br /&gt;
    tiles[x][y] = g.createChild(&amp;quot;image&amp;quot;, &amp;quot;map-tile&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var last_tile = [-1,-1];&lt;br /&gt;
var last_type = type;&lt;br /&gt;
&lt;br /&gt;
##&lt;br /&gt;
# this is the callback that will be regularly called by the timer&lt;br /&gt;
# to update the map&lt;br /&gt;
var updateTiles = func()&lt;br /&gt;
{&lt;br /&gt;
  # get current position&lt;br /&gt;
  var lat = getprop('/position/latitude-deg');&lt;br /&gt;
  var lon = getprop('/position/longitude-deg');&lt;br /&gt;
&lt;br /&gt;
  var n = math.pow(2, zoom);&lt;br /&gt;
  var offset = [&lt;br /&gt;
    n * ((lon + 180) / 360) - center_tile_offset[0],&lt;br /&gt;
    (1 - math.ln(math.tan(lat * math.pi/180) + 1 / math.cos(lat * math.pi/180)) / math.pi) / 2 * n - center_tile_offset[1]&lt;br /&gt;
  ];&lt;br /&gt;
  var tile_index = [int(offset[0]), int(offset[1])];&lt;br /&gt;
&lt;br /&gt;
  var ox = tile_index[0] - offset[0];&lt;br /&gt;
  var oy = tile_index[1] - offset[1];&lt;br /&gt;
&lt;br /&gt;
  for(var x = 0; x &amp;lt; num_tiles[0]; x += 1)&lt;br /&gt;
    for(var y = 0; y &amp;lt; num_tiles[1]; y += 1)&lt;br /&gt;
      tiles[x][y].setTranslation(int((ox + x) * tile_size + 0.5), int((oy + y) * tile_size + 0.5));&lt;br /&gt;
&lt;br /&gt;
  if(    tile_index[0] != last_tile[0]&lt;br /&gt;
      or tile_index[1] != last_tile[1]&lt;br /&gt;
      or type != last_type )&lt;br /&gt;
  {&lt;br /&gt;
    for(var x = 0; x &amp;lt; num_tiles[0]; x += 1)&lt;br /&gt;
      for(var y = 0; y &amp;lt; num_tiles[1]; y += 1)&lt;br /&gt;
      {&lt;br /&gt;
        var pos = {&lt;br /&gt;
          z: zoom,&lt;br /&gt;
          x: int(offset[0] + x),&lt;br /&gt;
          y: int(offset[1] + y),&lt;br /&gt;
          type: type&lt;br /&gt;
        };&lt;br /&gt;
&lt;br /&gt;
        (func {&lt;br /&gt;
        var img_path = makePath(pos);&lt;br /&gt;
        var tile = tiles[x][y];&lt;br /&gt;
&lt;br /&gt;
        if( io.stat(img_path) == nil )&lt;br /&gt;
        { # image not found, save in $FG_HOME&lt;br /&gt;
          var img_url = makeUrl(pos);&lt;br /&gt;
          print('requesting ' ~ img_url);&lt;br /&gt;
          http.save(img_url, img_path)&lt;br /&gt;
              .done(func {print('received image ' ~ img_path); tile.set(&amp;quot;src&amp;quot;, img_path);})&lt;br /&gt;
              .fail(func (r) print('Failed to get image ' ~ img_path ~ ' ' ~ r.status ~ ': ' ~ r.reason));&lt;br /&gt;
        }&lt;br /&gt;
        else # cached image found, reusing&lt;br /&gt;
        {&lt;br /&gt;
          print('loading ' ~ img_path);&lt;br /&gt;
          tile.set(&amp;quot;src&amp;quot;, img_path)&lt;br /&gt;
        }&lt;br /&gt;
        })();&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
    last_tile = tile_index;&lt;br /&gt;
    last_type = type;&lt;br /&gt;
  }&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
##&lt;br /&gt;
# set up a timer that will invoke updateTiles at 2-second intervals&lt;br /&gt;
var update_timer = maketimer(2, updateTiles);&lt;br /&gt;
# actually start the timer&lt;br /&gt;
update_timer.start();&lt;br /&gt;
&lt;br /&gt;
##&lt;br /&gt;
# set up default zoom level&lt;br /&gt;
changeZoom(0);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
###&lt;br /&gt;
# The following lines were recently added and have not yet been tested&lt;br /&gt;
# (if in doubt, remove them)&lt;br /&gt;
window.del = func()&lt;br /&gt;
{&lt;br /&gt;
  print(&amp;quot;Cleaning up window:&amp;quot;,title,&amp;quot;\n&amp;quot;);&lt;br /&gt;
  update_timer.stop();&lt;br /&gt;
# explanation for the call() technique at: http://wiki.flightgear.org/Object_oriented_programming_in_Nasal#Making_safer_base-class_calls&lt;br /&gt;
  call(canvas.Window.del, [], me);&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Adding a NavDisplay ==&lt;br /&gt;
&lt;br /&gt;
== Adding a CDU ==&lt;br /&gt;
&lt;br /&gt;
== Adding a Multi-Function Display (MFD) ==&lt;/div&gt;</summary>
		<author><name>Clm76</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Howto:Prototyping_a_new_NavDisplay_Style&amp;diff=104789</id>
		<title>Howto:Prototyping a new NavDisplay Style</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Howto:Prototyping_a_new_NavDisplay_Style&amp;diff=104789"/>
		<updated>2016-10-12T15:53:57Z</updated>

		<summary type="html">&lt;p&gt;Clm76: /* Status */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Stub}}&lt;br /&gt;
[[File:CanvasND development via PUI.png|thumb|Screenshot showing a [[PUI]] dialog with two embedded [[Canvas]] regions for displaying two independent [[NavDisplay]] instances.]]&lt;br /&gt;
== Objective ==&lt;br /&gt;
Demonstrate how a simple PUI/XML dialog can be used to easily prototype new [[NavDisplay]] styles, showing two instances for each pilot, which can be controlled/tested independently.&lt;br /&gt;
&lt;br /&gt;
== Approach ==&lt;br /&gt;
This is a PUI/XML dialog with two embedded CanvasWidget areas that instantiate independent ND instances using a subset of the code commonly found in the aircraft specific ND.nas file, adapted to use the embedded Canvas region for rendering the two NDs and GUI buttons/widgets added to control the whole thing without necessarily requiring a fully developed cockpit.&lt;br /&gt;
&lt;br /&gt;
== Roadmap ==&lt;br /&gt;
* add buttons for controlling each ND instance (range, zoom, centering)&lt;br /&gt;
* use io.include to directly reload the styles stuff (for rapid prototyping)&lt;br /&gt;
* procedurally add buttons for each switch by enhancing the myCockpit_switches hash with legends/labels for the UI use-case (use checkboxes for boolean props)&lt;br /&gt;
* add a dropdown menu to select the position source (main aircraft vs. AI/MP traffic)&lt;br /&gt;
&lt;br /&gt;
== Status ==&lt;br /&gt;
For now this is  heavily based on ideas, and code, borrowed from the original [[MapStructure Debugger]] (code still available at [https://gitorious.org/fg/canvas-hackers-fgdata?p=fg:canvas-hackers-fgdata.git;a=blob_plain;f=gui/dialogs/mapstructure-debugger.xml;hb=42dbf52623c7596c7446ede439235cc0016f99c8])&lt;br /&gt;
&lt;br /&gt;
{{WIP}}&lt;br /&gt;
&lt;br /&gt;
{{Note|For testing purposes, put the following dialog into $FG_ROOT/gui/dialogs/canvas-nd.xml and use the [[Nasal Console]] to run the dialog (or extend the [[Menubar]] accordingly):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;fgcommand(&amp;quot;dialog-show&amp;quot;, props.Node.new({'dialog-name':'canvas-nd'}) );&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;PropertyList&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;name&amp;gt;canvas-nd&amp;lt;/name&amp;gt;&lt;br /&gt;
&amp;lt;modal&amp;gt;false&amp;lt;/modal&amp;gt;&lt;br /&gt;
&amp;lt;layout&amp;gt;vbox&amp;lt;/layout&amp;gt;&lt;br /&gt;
&amp;lt;text&amp;gt;&lt;br /&gt;
  &amp;lt;label&amp;gt;Canvas ND&amp;lt;/label&amp;gt;&lt;br /&gt;
&amp;lt;/text&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nasal&amp;gt;&lt;br /&gt;
  &amp;lt;open&amp;gt;&amp;lt;![CDATA[&lt;br /&gt;
    print(&amp;quot;nasal/opening block&amp;quot;);&lt;br /&gt;
    var show_canvas_id = func(c) {&lt;br /&gt;
        print(&amp;quot;Canvas is:&amp;quot;, c.getPath());&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    # to be used for shutting down each created instances upon closing the dialog (see the close block below)&lt;br /&gt;
    var MFDInstances = [];&lt;br /&gt;
&lt;br /&gt;
    #### &lt;br /&gt;
    ## an adapted version of ND.nas&lt;br /&gt;
    ##&lt;br /&gt;
&lt;br /&gt;
    var setupND = func(property_root, my_canvas) {&lt;br /&gt;
&lt;br /&gt;
        var myCockpit_switches = {&lt;br /&gt;
            # symbolic alias : relative property (as used in bindings), initial value, type&lt;br /&gt;
            'toggle_range':         {path: '/inputs/range-nm', value:40, type:'INT'},&lt;br /&gt;
            'toggle_weather':       {path: '/inputs/wxr', value:0, type:'BOOL'},&lt;br /&gt;
            'toggle_airports':      {path: '/inputs/arpt', value:0, type:'BOOL'},&lt;br /&gt;
            'toggle_stations':      {path: '/inputs/sta', value:0, type:'BOOL'},&lt;br /&gt;
            'toggle_waypoints':     {path: '/inputs/wpt', value:0, type:'BOOL'},&lt;br /&gt;
            'toggle_position':      {path: '/inputs/pos', value:0, type:'BOOL'},&lt;br /&gt;
            'toggle_data':          {path: '/inputs/data',value:0, type:'BOOL'},&lt;br /&gt;
            'toggle_terrain':       {path: '/inputs/terr',value:0, type:'BOOL'},&lt;br /&gt;
            'toggle_traffic':       {path: '/inputs/tfc',value:0, type:'BOOL'},&lt;br /&gt;
            'toggle_centered':      {path: '/inputs/nd-centered',value:0, type:'BOOL'},&lt;br /&gt;
            'toggle_lh_vor_adf':    {path: '/inputs/lh-vor-adf',value:0, type:'INT'},&lt;br /&gt;
            'toggle_rh_vor_adf':    {path: '/inputs/rh-vor-adf',value:0, type:'INT'},&lt;br /&gt;
            'toggle_display_mode':  {path: '/mfd/display-mode', value:'MAP', type:'STRING'},&lt;br /&gt;
            'toggle_display_type':  {path: '/mfd/display-type', value:'LCD', type:'STRING'},&lt;br /&gt;
            'toggle_true_north':    {path: '/mfd/true-north', value:0, type:'BOOL'},&lt;br /&gt;
            'toggle_rangearc':      {path: '/mfd/rangearc', value:0, type:'BOOL'},&lt;br /&gt;
            'toggle_track_heading': {path: '/hdg-trk-selected', value:0, type:'BOOL'},&lt;br /&gt;
            'toggle_hdg_bug_only':  {path: '/hdg-bug-only', value:0, type:'BOOL'},&lt;br /&gt;
            # add new switches here&lt;br /&gt;
        };&lt;br /&gt;
&lt;br /&gt;
        ###&lt;br /&gt;
        # entry point, this will set up an ND instance&lt;br /&gt;
&lt;br /&gt;
        # get a handle to the NavDisplay in canvas namespace (for now), see $FG_ROOT/Nasal/canvas/map/navdisplay.mfd&lt;br /&gt;
        var ND = canvas.NavDisplay;&lt;br /&gt;
&lt;br /&gt;
        ##&lt;br /&gt;
        # set up a  new ND instance, under 'instrumentation/efis' and use the&lt;br /&gt;
        # myCockpit_switches hash to map control properties&lt;br /&gt;
        # FIXME: use cmdarg() here to use a dialog specific path for all switches&lt;br /&gt;
        var myND = ND.new(&amp;quot;instrumentation/efis&amp;quot;, myCockpit_switches, 'Boeing');&lt;br /&gt;
&lt;br /&gt;
        var group = my_canvas.createGroup();&lt;br /&gt;
        myND.newMFD(group, my_canvas);&lt;br /&gt;
        myND.update();&lt;br /&gt;
        # store the instance for later cleanup&lt;br /&gt;
        append(MFDInstances, myND);&lt;br /&gt;
&lt;br /&gt;
        return myND;&lt;br /&gt;
    } # setupND()&lt;br /&gt;
  ]]&amp;gt;&amp;lt;/open&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;close&amp;gt;&amp;lt;![CDATA[&lt;br /&gt;
    print(&amp;quot;nasal/closing block&amp;quot;);&lt;br /&gt;
    foreach(var mfd; MFDInstances) {&lt;br /&gt;
        mfd.del();&lt;br /&gt;
    }&lt;br /&gt;
  ]]&amp;gt;&amp;lt;/close&amp;gt;&lt;br /&gt;
&amp;lt;/nasal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;group&amp;gt;&lt;br /&gt;
  &amp;lt;layout&amp;gt;hbox&amp;lt;/layout&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;canvas&amp;gt;&lt;br /&gt;
    &amp;lt;name&amp;gt;mfd1&amp;lt;/name&amp;gt;&lt;br /&gt;
    &amp;lt;valign&amp;gt;fill&amp;lt;/valign&amp;gt;&lt;br /&gt;
    &amp;lt;halign&amp;gt;fill&amp;lt;/halign&amp;gt;&lt;br /&gt;
    &amp;lt;stretch&amp;gt;true&amp;lt;/stretch&amp;gt;&lt;br /&gt;
    &amp;lt;pref-width&amp;gt;512&amp;lt;/pref-width&amp;gt;&lt;br /&gt;
    &amp;lt;pref-height&amp;gt;512&amp;lt;/pref-height&amp;gt;&lt;br /&gt;
    &amp;lt;view&amp;gt;1024&amp;lt;/view&amp;gt;&lt;br /&gt;
    &amp;lt;view&amp;gt;1024&amp;lt;/view&amp;gt;&lt;br /&gt;
    &amp;lt;nasal&amp;gt;      &lt;br /&gt;
      &amp;lt;!--&lt;br /&gt;
           this is the Nasal/canvas section where you can run your own Nasal code &lt;br /&gt;
           to access the canvas section &lt;br /&gt;
      --&amp;gt;&lt;br /&gt;
      &amp;lt;load&amp;gt;&amp;lt;![CDATA[&lt;br /&gt;
        # you can add your canvas-specific code here&lt;br /&gt;
        var my_canvas = canvas.get(cmdarg()); # this will get a handle to the parent canvas:&lt;br /&gt;
        show_canvas_id(my_canvas);&lt;br /&gt;
        setupND(property_root: cmdarg(), my_canvas: my_canvas);&lt;br /&gt;
      ]]&amp;gt;&amp;lt;/load&amp;gt;&lt;br /&gt;
    &amp;lt;/nasal&amp;gt;&lt;br /&gt;
  &amp;lt;/canvas&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;canvas&amp;gt;&lt;br /&gt;
    &amp;lt;name&amp;gt;mfd2&amp;lt;/name&amp;gt;&lt;br /&gt;
    &amp;lt;valign&amp;gt;fill&amp;lt;/valign&amp;gt;&lt;br /&gt;
    &amp;lt;halign&amp;gt;fill&amp;lt;/halign&amp;gt;&lt;br /&gt;
    &amp;lt;stretch&amp;gt;true&amp;lt;/stretch&amp;gt;&lt;br /&gt;
    &amp;lt;pref-width&amp;gt;512&amp;lt;/pref-width&amp;gt;&lt;br /&gt;
    &amp;lt;pref-height&amp;gt;512&amp;lt;/pref-height&amp;gt;&lt;br /&gt;
    &amp;lt;view&amp;gt;1024&amp;lt;/view&amp;gt;&lt;br /&gt;
    &amp;lt;view&amp;gt;1024&amp;lt;/view&amp;gt;&lt;br /&gt;
    &amp;lt;nasal&amp;gt;      &lt;br /&gt;
      &amp;lt;!--&lt;br /&gt;
           this is the Nasal/canvas section where you can run your own Nasal code &lt;br /&gt;
           to access the canvas section &lt;br /&gt;
      --&amp;gt;&lt;br /&gt;
      &amp;lt;load&amp;gt;&amp;lt;![CDATA[&lt;br /&gt;
        # you can add your canvas-specific code here&lt;br /&gt;
        var my_canvas = canvas.get(cmdarg()); # this will get a handle to the parent canvas:&lt;br /&gt;
        show_canvas_id(my_canvas);&lt;br /&gt;
        setupND(property_root: cmdarg(), my_canvas: my_canvas);&lt;br /&gt;
      ]]&amp;gt;&amp;lt;/load&amp;gt;&lt;br /&gt;
    &amp;lt;/nasal&amp;gt;&lt;br /&gt;
  &amp;lt;/canvas&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/group&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;group&amp;gt;&lt;br /&gt;
  &amp;lt;layout&amp;gt;hbox&amp;lt;/layout&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;button&amp;gt;&lt;br /&gt;
    &amp;lt;legend&amp;gt;Reload&amp;lt;/legend&amp;gt;&lt;br /&gt;
    &amp;lt;default&amp;gt;1&amp;lt;/default&amp;gt;&lt;br /&gt;
    &amp;lt;border&amp;gt;2&amp;lt;/border&amp;gt;&lt;br /&gt;
    &amp;lt;binding&amp;gt;&lt;br /&gt;
      &amp;lt;command&amp;gt;reinit&amp;lt;/command&amp;gt;&lt;br /&gt;
      &amp;lt;subsystem&amp;gt;gui&amp;lt;/subsystem&amp;gt;&lt;br /&gt;
    &amp;lt;/binding&amp;gt;&lt;br /&gt;
  &amp;lt;/button&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;button&amp;gt;&lt;br /&gt;
    &amp;lt;legend&amp;gt;Exit&amp;lt;/legend&amp;gt;&lt;br /&gt;
    &amp;lt;equal&amp;gt;true&amp;lt;/equal&amp;gt;&lt;br /&gt;
    &amp;lt;key&amp;gt;Esc&amp;lt;/key&amp;gt;&lt;br /&gt;
    &amp;lt;binding&amp;gt;&lt;br /&gt;
      &amp;lt;command&amp;gt;dialog-close&amp;lt;/command&amp;gt;&lt;br /&gt;
    &amp;lt;/binding&amp;gt;&lt;br /&gt;
  &amp;lt;/button&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/group&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/PropertyList&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Clm76</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Nasal_library&amp;diff=85151</id>
		<title>Nasal library</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Nasal_library&amp;diff=85151"/>
		<updated>2015-05-30T07:29:20Z</updated>

		<summary type="html">&lt;p&gt;Clm76: /* airportinfo() */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Nasal Navigation}}&lt;br /&gt;
&lt;br /&gt;
This page documents the core library functions of FlightGear's built-in scripting language, [[Nasal]].  The relevant folders in [[Git]] are:&lt;br /&gt;
* {{Repo link|site=s|proj=flightgear/flightgear|type=git|path=src/Scripting}}&lt;br /&gt;
* {{Repo link|site=s|proj=flightgear/simgear|type=git|path=simgear/nasal}}&lt;br /&gt;
&lt;br /&gt;
{{Note|Feel free to copy &amp;amp; paste the examples into your [[Nasal Console]] and execute them to see what they do.}}&lt;br /&gt;
&lt;br /&gt;
== Core library ==&lt;br /&gt;
This is the list of the basic core library functions that were in Nasal before its integration in to FlightGear.  See also:&lt;br /&gt;
* http://plausible.org/nasal/lib.html ([http://web.archive.org/web/20101010094553/http://plausible.org/nasal/lib.html archive])&lt;br /&gt;
* {{Repo link|site=s|proj=flightgear/simgear|type=git|path=simgear/nasal/lib.c|lines=590}} (lines 590-619)&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|name = append&lt;br /&gt;
|source = core&lt;br /&gt;
|line = 42&lt;br /&gt;
|syntax = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;append(vector, element[, element[, ...]]);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|text = This function appends, or adds, the given element(s) to the end of the vector given in the first argument.  Returns the vector operated on.&lt;br /&gt;
|heading = ===&lt;br /&gt;
|param1 = vector&lt;br /&gt;
|param1text = The vector to which the arguments will be appended.&lt;br /&gt;
|param2 = element&lt;br /&gt;
|param2text = An element to be added to the vector.&lt;br /&gt;
|example1 = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
var vector = [1, 2, 3]; # Initialize the vector&lt;br /&gt;
append(vector, 4); # Append the number 4 to the end of the vector&lt;br /&gt;
debug.dump(vector); # Print the contents of the vector&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|example2 = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
var vector = [1, 2, 3]; # Initialize the vector&lt;br /&gt;
append(vector, 4, 5, 6); # Append the numbers 4, 5, and 6 to the end of the vector&lt;br /&gt;
debug.dump(vector); # Print the contents of the vector&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|name = bind&lt;br /&gt;
|source = core&lt;br /&gt;
|line = 502&lt;br /&gt;
|syntax = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;bind(function, locals[, outer_scope]);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|text = This creates a new function object.  A function in Nasal is three things: the actual code, a hash/namespace of local variables available to the function namespace, and the closure object of that namespace.  These correspond to the three arguments respectively.&lt;br /&gt;
|heading = ===&lt;br /&gt;
|param1 = function&lt;br /&gt;
|param1text = Function to evaluate.&lt;br /&gt;
|param2 = locals&lt;br /&gt;
|param2text = Hash containing values that will become the namespace (first closure) for the function.&lt;br /&gt;
|param3 = outer_scope&lt;br /&gt;
|param3text = Optional function which is bound to the next closure.  This can be bound to yet another, making a linked list.&lt;br /&gt;
|example1 = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
# This is a namespace/hash with a single member, named &amp;quot;key&amp;quot;, which is initialized to 12 &lt;br /&gt;
var Namespace = {&lt;br /&gt;
    key: 12&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
# This is different namespace/hash containing a function&lt;br /&gt;
# dividing a variable &amp;quot;key&amp;quot; (which is unavailable/nil in this namespace) by 2&lt;br /&gt;
var AnotherNamespace = {&lt;br /&gt;
    ret: func {&lt;br /&gt;
        key /= 2;&lt;br /&gt;
    }&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
# To see that key is not available, try to call AnotherNamespace.ret() first&lt;br /&gt;
call(AnotherNamespace.ret, [], nil, nil, var errors = []);&lt;br /&gt;
if(size(errors)){&lt;br /&gt;
    print(&amp;quot;Key could not be divided/resolved!&amp;quot;);&lt;br /&gt;
    debug.printerror(errors);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# Associate the AnotherNamespace.ret() function with the first namespace&lt;br /&gt;
# so that &amp;quot;key&amp;quot; is now available&lt;br /&gt;
var function = bind(AnotherNamespace.ret, Namespace);&lt;br /&gt;
&lt;br /&gt;
# Invoke the new function&lt;br /&gt;
function();&lt;br /&gt;
&lt;br /&gt;
# Print out the value of Namespace.key&lt;br /&gt;
# It was changed to 6 from 12 by AnotherNamespace.ret()&lt;br /&gt;
print(Namespace.key);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|name = call&lt;br /&gt;
|source = core&lt;br /&gt;
|line = 247&lt;br /&gt;
|heading = ===&lt;br /&gt;
|syntax = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;call(func[, args[, me[, locals[, error]]]);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|text = Calls the given function with the given arguments and returns the result.  This function is very useful as it allows much more control over function calls and catches any errors or {{Func link|die}} calls that would normally trigger run-time errors cancelling execution of the script otherwise. &lt;br /&gt;
|param1 = func&lt;br /&gt;
|param1text = Function to execute.&lt;br /&gt;
|param2 = args&lt;br /&gt;
|param2text = Vector containing arguments to give to the called function.&lt;br /&gt;
|param3 = me&lt;br /&gt;
|param3text = &amp;lt;code&amp;gt;'''me'''&amp;lt;/code&amp;gt; reference for the function call (i.e., for method calls). If given, this will override any &amp;lt;code&amp;gt;'''me'''&amp;lt;/code&amp;gt; value existing in the namespace (locals argument).&lt;br /&gt;
|param4 = locals&lt;br /&gt;
|param4text = A hash with key/value pairs that will be available to the called function, typically used as the namespace for the function to be called.&lt;br /&gt;
|param5 = error&lt;br /&gt;
|param5text = A vector to append errors to.  If the called function generates an error, the error, place, and line will be written to this.  These errors can be printed using {{Func link|debug.printerror()|ext=https://gitorious.org/fg/fgdata/source/master:Nasal/debug.nas#L398}}.&lt;br /&gt;
|example1 = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
# prints &amp;quot;Called from call()&amp;quot;&lt;br /&gt;
call(func {&lt;br /&gt;
    print(&amp;quot;Called from call()&amp;quot;);&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|example2 = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
# prints &amp;quot;a = 1 : b = 2&lt;br /&gt;
call(func(a, b){&lt;br /&gt;
        print(&amp;quot;a = &amp;quot;, a, &amp;quot; : b = &amp;quot;, b);&lt;br /&gt;
    },&lt;br /&gt;
    [1, 2]&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|example3 = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
var Hash = {&lt;br /&gt;
    new: func {&lt;br /&gt;
        var m = { parents: [Hash] };&lt;br /&gt;
&lt;br /&gt;
        m.el1 = &amp;quot;string1&amp;quot;;&lt;br /&gt;
        m.el2 = &amp;quot;string2&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
        return m;&lt;br /&gt;
    }&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
# prints &amp;quot;me.el1 = string1&amp;quot;, then &amp;quot;me.el2 = string2&amp;quot; on the next line&lt;br /&gt;
call(func(a, b){        &lt;br /&gt;
        print(&amp;quot;me.el&amp;quot;, a, &amp;quot; = &amp;quot;, me[&amp;quot;el&amp;quot; ~ a]);      &lt;br /&gt;
        print(&amp;quot;me.el&amp;quot;, b, &amp;quot; = &amp;quot;, me[&amp;quot;el&amp;quot; ~ b]);&lt;br /&gt;
    },&lt;br /&gt;
    [1, 2],&lt;br /&gt;
    Hash.new()&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|example4 = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
# prints the value of math.pi&lt;br /&gt;
call(func {&lt;br /&gt;
        print(pi);&lt;br /&gt;
    }, nil, nil, &lt;br /&gt;
    math&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|example5 = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
call(func {&lt;br /&gt;
        print(math.ip); # math.ip doesn't exist&lt;br /&gt;
    }, nil, nil, nil,&lt;br /&gt;
    var errs = []&lt;br /&gt;
);&lt;br /&gt;
debug.printerror(errs); # The error is caught and printed using debug.printerror()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|name = caller&lt;br /&gt;
|source = core&lt;br /&gt;
|line = 404&lt;br /&gt;
|heading = ===&lt;br /&gt;
|syntax = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;caller([level]);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|text = Returns a vector containing a record from the current call stack.  The level numbering starts from the currently executing function (level 0).  Level 1 (the default) is the caller of the current function, and so on.  The result is a four-element vector containing a hash of local variables, the function object, the source, and the line number. &lt;br /&gt;
|param1 = level&lt;br /&gt;
|param1text = Optional integer specifying the stack level to return a result from.  Defaults to 1 (the caller of the currently executing function.&lt;br /&gt;
|example1 = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
var myFunction = func(a, b){&lt;br /&gt;
    debug.dump(caller(0)[0]); # prints a hash of local variables, including arguments a and b&lt;br /&gt;
    return 2 * 2;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;2 x 2 = &amp;quot;, myFunction(2, 2));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|example2 = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
var get_arg_value = func(){&lt;br /&gt;
    print(&amp;quot;Argument to myFunc = &amp;quot;, caller(1)[0]['a']); # print the value of myFunc's single argument, using caller()&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
var myFunc = func(a){&lt;br /&gt;
    get_arg_value();&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
myFunc(3);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|example3text = This is a real example taken from &amp;lt;tt&amp;gt;''[[$FG_ROOT]]/Nasal/canvas/MapStructure.nas''&amp;lt;/tt&amp;gt;.  &amp;lt;code&amp;gt;r();&amp;lt;/code&amp;gt; (highlighted) returns a hash with the key/value pairs as per its arguments. For example, something like this is returned: &amp;lt;code&amp;gt;{ name: &amp;quot;&amp;lt;name&amp;gt;&amp;quot;, vis: 1, zindex: nil }&amp;lt;/code&amp;gt;.&lt;br /&gt;
|example3 = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; line start=&amp;quot;66&amp;quot; highlight=&amp;quot;13&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
var MapStructure_selfTest = func() {&lt;br /&gt;
	var temp = {};&lt;br /&gt;
	temp.dlg = canvas.Window.new([600,400],&amp;quot;dialog&amp;quot;);&lt;br /&gt;
	temp.canvas = temp.dlg.createCanvas().setColorBackground(1,1,1,0.5);&lt;br /&gt;
	temp.root = temp.canvas.createGroup();&lt;br /&gt;
	var TestMap = temp.root.createChild(&amp;quot;map&amp;quot;);&lt;br /&gt;
	TestMap.setController(&amp;quot;Aircraft position&amp;quot;);&lt;br /&gt;
	TestMap.setRange(25); # TODO: implement zooming/panning via mouse/wheel here, for lack of buttons :-/&lt;br /&gt;
	TestMap.setTranslation(&lt;br /&gt;
		temp.canvas.get(&amp;quot;view[0]&amp;quot;)/2,&lt;br /&gt;
		temp.canvas.get(&amp;quot;view[1]&amp;quot;)/2&lt;br /&gt;
	);&lt;br /&gt;
	var r = func(name,vis=1,zindex=nil) return caller(0)[0];&lt;br /&gt;
	# TODO: we'll need some z-indexing here, right now it's just random&lt;br /&gt;
	# TODO: use foreach/keys to show all layers in this case by traversing SymbolLayer.registry direclty ?&lt;br /&gt;
	# maybe encode implicit z-indexing for each lcontroller ctor call ? - i.e. preferred above/below order ?&lt;br /&gt;
	foreach(var type; [r('TFC',0),r('APT'),r('DME'),r('VOR'),r('NDB'),r('FIX',0),r('RTE'),r('WPT'),r('FLT'),r('WXR'),r('APS'), ] ) &lt;br /&gt;
		TestMap.addLayer(factory: canvas.SymbolLayer, type_arg: type.name,&lt;br /&gt;
					visible: type.vis, priority: type.zindex,&lt;br /&gt;
		);&lt;br /&gt;
}; # MapStructure_selfTest&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|name = chr&lt;br /&gt;
|source = core&lt;br /&gt;
|line = 175&lt;br /&gt;
|heading = ===&lt;br /&gt;
|syntax = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;chr(code);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|text = Returns a character as per the single argument. Extended ASCII is supported (see http://www.asciitable.com/ for a list of supported characters), although this may vary between different systems.  For a list of the most commonly used characters, see http://en.wikipedia.org/wiki/ASCII#ASCII_printable_code_chart ('''Dec''' column). The following table lists supported control characters, along with their equivalent control characters in Nasal strings.  {{Note|In Nasal, only strings enclosed with double-quotes (&amp;lt;code&amp;gt;&amp;quot;string&amp;quot;&amp;lt;/code&amp;gt;) supports control chracters.  Strings in single quotes (&amp;lt;code&amp;gt;'string'&amp;lt;/code&amp;gt;) do not.}}&lt;br /&gt;
{{{!}} class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Code !! Name !! Equivalent to&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} 10 {{!!}} {{Wikipedia|Newline}} {{!!}} &amp;lt;code&amp;gt;\n&amp;lt;/code&amp;gt;&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} 9 {{!!}} {{Wikipedia|Tab_key#Tab_characters|Horizontal tab}} {{!!}} &amp;lt;code&amp;gt;\t&amp;lt;/code&amp;gt;&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} 13 {{!!}} {{Wikipedia|Carriage return}} {{!!}} &amp;lt;code&amp;gt;\r&amp;lt;/code&amp;gt;&lt;br /&gt;
{{!}}}&lt;br /&gt;
|param1 = code&lt;br /&gt;
|param1text = Integer character code for the desired glyph.&lt;br /&gt;
|example1 = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
print(&amp;quot;Code 65 = &amp;quot;, chr(65)); # prints &amp;quot;Code 65 = A&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|example2text = This example displays all of the characters in a list, in the format &amp;lt;code&amp;gt;Code '''n''' = &amp;gt;'''char'''&amp;lt;&amp;lt;/code&amp;gt;, '''n''' being the index, and '''char''' being the character.&lt;br /&gt;
|example2 = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
for(var i = 0; i &amp;lt;= 255; i += 1){&lt;br /&gt;
    print(&amp;quot;Code &amp;quot;, i, &amp;quot; = &amp;gt;&amp;quot;, chr(i), &amp;quot;&amp;lt;&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|name = closure&lt;br /&gt;
|source = core&lt;br /&gt;
|line = 421&lt;br /&gt;
|heading = ===&lt;br /&gt;
|syntax = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;closure(func[, level]);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|text = Returns the hash table containing the lexical namespace of the given function. The level numbering start with level 0 being the namespace of '''func'''. &lt;br /&gt;
|param1 = func&lt;br /&gt;
|param1text = Function to evaluate.&lt;br /&gt;
|param2 = level&lt;br /&gt;
|param2text = Optional integer specifying the scope level.  Defaults to 0 (the namespace of '''func''').&lt;br /&gt;
|example1 = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
var get_math_e = func {&lt;br /&gt;
    return e; # return the value of math.e&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var myFunction = bind(get_math_e, math); # bind get_math_e to the math namespace, so that math.e is immediately available to get_math_e&lt;br /&gt;
debug.dump(closure(myFunction)); # print the namespace of get_math_e&lt;br /&gt;
&lt;br /&gt;
print(myFunction());&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|name = cmp&lt;br /&gt;
|source = core&lt;br /&gt;
|line = 112&lt;br /&gt;
|heading = ===&lt;br /&gt;
|syntax = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;cmp(a, b);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|text = Compares two strings, returning -1 if '''a''' is less than '''b''', 0 if they are identical and 1 if '''a''' is greater than '''b'''. &lt;br /&gt;
|param1 = a&lt;br /&gt;
|param1text = First string argument for comparison.&lt;br /&gt;
|param2 = b&lt;br /&gt;
|param2text = Second string argument for comparison.&lt;br /&gt;
|example1 = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
print(cmp(&amp;quot;1&amp;quot;, &amp;quot;two&amp;quot;)); # prints -1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|example2 = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
print(cmp(&amp;quot;string&amp;quot;, &amp;quot;string&amp;quot;)); # prints 0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|example3 = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
print(cmp(&amp;quot;one&amp;quot;, &amp;quot;2&amp;quot;)); # prints 1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|example4 = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
print(cmp(&amp;quot;string1&amp;quot;, &amp;quot;string2&amp;quot;)); # prints -1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|name = compile&lt;br /&gt;
|source = core&lt;br /&gt;
|line = 220&lt;br /&gt;
|heading = ===&lt;br /&gt;
|syntax = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;compile(code[, filename]);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|text = Compiles the specified code string and returns a function object bound to the current lexical context.  If there is an error, the function dies, with the argument to {{Func link|die}} being '''filename'''.&lt;br /&gt;
|param1 = code&lt;br /&gt;
|param1text = String containing Nasal code to be compiled.&lt;br /&gt;
|param2 = filename&lt;br /&gt;
|param2text = Optional string used for error messages/logging. Defaults to &amp;lt;code&amp;gt;&amp;lt;compile&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
|example1 = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
var myCode = 'print(&amp;quot;hello&amp;quot;);';&lt;br /&gt;
var helloFunc = compile(myCode, &amp;quot;myCode&amp;quot;);&lt;br /&gt;
helloFunc();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|example2text = &amp;lt;code&amp;gt;compile&amp;lt;/code&amp;gt; is very convenient to support Nasal loaded from other files.  For instance, [[PropertyList XML files]] (such as GUI dialogs) may contain embedded Nasal sections that need to be parsed, processed and compiled.  For an example of how to do this, save the below XML code as &amp;lt;tt&amp;gt;''[[$FG_ROOT]]/gui/dialogs/test.xml''&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&amp;lt;syntaxhighlight land=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?xml version=&amp;quot;1.0&amp;quot;?&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;PropertyList&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nasal&amp;gt;&amp;lt;![CDATA[&lt;br /&gt;
print(&amp;quot;You have FlightGear v&amp;quot;, getprop(&amp;quot;/sim/version/flightgear&amp;quot;));&lt;br /&gt;
]]&amp;gt;&amp;lt;/nasal&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/PropertyList&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Now, start FlightGear and execute this code in the [[Nasal Console]].&lt;br /&gt;
|example2 = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
# Build the path&lt;br /&gt;
var FGRoot = getprop(&amp;quot;/sim/fg-root&amp;quot;);&lt;br /&gt;
var filename = &amp;quot;/gui/dialogs/test.xml&amp;quot;;&lt;br /&gt;
var path = FGRoot ~ filename;&lt;br /&gt;
&lt;br /&gt;
var blob = io.read_properties(path);&lt;br /&gt;
var script = blob.getValues().nasal; # Get the nasal string&lt;br /&gt;
&lt;br /&gt;
# Compile the script.  We're passing the filename here for better runtime diagnostics &lt;br /&gt;
var code = call(func {&lt;br /&gt;
    compile(script, filename);&lt;br /&gt;
}, nil, nil, var compilation_errors = []);&lt;br /&gt;
&lt;br /&gt;
if(size(compilation_errors)){&lt;br /&gt;
    die(&amp;quot;Error compiling code in: &amp;quot; ~ filename);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# Invoke the compiled script, equivalent to code(); &lt;br /&gt;
# We're using call() here to detect errors:&lt;br /&gt;
call(code, [], nil, nil, var runtime_errors = []);&lt;br /&gt;
&lt;br /&gt;
if(size(runtime_errors)){&lt;br /&gt;
    die(&amp;quot;Error calling code compiled loaded from: &amp;quot; ~ filename);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|name = contains&lt;br /&gt;
|source = core&lt;br /&gt;
|line = 184&lt;br /&gt;
|heading = ===&lt;br /&gt;
|syntax = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;contains(hash, key);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|text = Returns 1 (True) if the hash contains the specified key, or 0 (False) if not.&lt;br /&gt;
|param1 = hash&lt;br /&gt;
|param1text = The hash to search in.&lt;br /&gt;
|param2 = key&lt;br /&gt;
|param2text = The scalar to be searched for, contained as a key in the hash.&lt;br /&gt;
|example1 = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
# Initialize a hash&lt;br /&gt;
var hash = {&lt;br /&gt;
    element: &amp;quot;value&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
print(contains(hash, &amp;quot;element&amp;quot;) ? &amp;quot;Yes&amp;quot; : &amp;quot;No&amp;quot;); # This will print &amp;quot;Yes&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|example2 = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
# Initialize a hash&lt;br /&gt;
var hash = {&lt;br /&gt;
    element: &amp;quot;value&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
print(contains(hash, &amp;quot;element2&amp;quot;) ? &amp;quot;Yes&amp;quot; : &amp;quot;No&amp;quot;); # This will print &amp;quot;No&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|name = delete&lt;br /&gt;
|source = core&lt;br /&gt;
|line = 83&lt;br /&gt;
|heading = ===&lt;br /&gt;
|syntax = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;delete(hash, key);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|text = Deletes the key from the hash if it exists. Operationally, this is identical to setting the hash value specified by the key to &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt;, but this variant potentially frees storage by deleting the reference to the key and by shrinking the hash.  Returns the hash that has been operated on.&lt;br /&gt;
|param1 = hash&lt;br /&gt;
|param1text = The hash from which to delete the key.&lt;br /&gt;
|param2 = key&lt;br /&gt;
|param2text = The scalar to be deleted, contained as a key in the hash.&lt;br /&gt;
&lt;br /&gt;
|example1 = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
# Initialize the hash&lt;br /&gt;
var hash = {&lt;br /&gt;
    element1: &amp;quot;value1&amp;quot;,&lt;br /&gt;
    element2: &amp;quot;value2&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
delete(hash, &amp;quot;element1&amp;quot;); # Delete element1&lt;br /&gt;
debug.dump(hash); # prints the hash, which is now minus element1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|name = die&lt;br /&gt;
|source = core&lt;br /&gt;
|line = 288&lt;br /&gt;
|heading = ===&lt;br /&gt;
|syntax = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;die(error);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|text = Terminates execution and unwinds the stack.  The place and the line will be added to the '''error'''.  This invokes the same internal exception handler used for internal runtime errors. Use this to signal fatal errors, or to implement exception handling. The error thrown (including internal runtime errors) can be caught with {{Func link|call}}.&lt;br /&gt;
|param1 = error&lt;br /&gt;
|param1text = String describing the error.&lt;br /&gt;
:{{inote|This parameter is technically optional, but it is highly recommended to use it.}}&lt;br /&gt;
&lt;br /&gt;
|example1 = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
print(&amp;quot;Will print&amp;quot;);&lt;br /&gt;
die(&amp;quot;Don't go any further!&amp;quot;); &lt;br /&gt;
print(&amp;quot;Won't print&amp;quot;); # Will not be printed because die() stops the process&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|name = find&lt;br /&gt;
|source = core&lt;br /&gt;
|line = 450&lt;br /&gt;
|heading = ===&lt;br /&gt;
|syntax = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;find(needle, haystack);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|text = Finds and returns the index of the first occurrence of the string '''needle''' in the string '''haystack''', or -1 if no such occurrence was found.&lt;br /&gt;
|param1 = needle&lt;br /&gt;
|param1text = String to search for.&lt;br /&gt;
|param2 = haystack&lt;br /&gt;
|param2text = String to search in.&lt;br /&gt;
&lt;br /&gt;
|example1 = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
print(find(&amp;quot;c&amp;quot;, &amp;quot;abcdef&amp;quot;)); # prints 2&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|example2 = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
print(find(&amp;quot;x&amp;quot;, &amp;quot;abcdef&amp;quot;)); # prints -1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|example3 = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
print(find(&amp;quot;cd&amp;quot;, &amp;quot;abcdef&amp;quot;)); # prints 2&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|name = ghosttype&lt;br /&gt;
|source = core&lt;br /&gt;
|line = 207&lt;br /&gt;
|heading = ===&lt;br /&gt;
|syntax = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;ghosttype(ghost);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|text = Returns a string containing either a descriptive name of a ghost (a raw C/C++ object), or a unique id (the pointer to the C/C++ &amp;lt;code&amp;gt;naGhostType&amp;lt;/code&amp;gt; instance) if no name has been set.  Ghost is an acronym that stands for '''G'''arbage-collected '''H'''andle to '''O'''ut'''S'''ide '''T'''hingy.&lt;br /&gt;
|param1 = ghost&lt;br /&gt;
|param1text = Ghost to return a description for.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|name = id&lt;br /&gt;
|source = core&lt;br /&gt;
|line = 570&lt;br /&gt;
|heading = ===&lt;br /&gt;
|syntax = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;id(object);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|text = Returns a string containing information on the type and ID of the object provided in the single argument.  The information is returned in the form of &amp;lt;code&amp;gt;'''type''':'''id'''&amp;lt;/code&amp;gt;, where '''type''' is the type of object, and '''id''' is the ID.&lt;br /&gt;
|param1 = object&lt;br /&gt;
|param1text = Can be either of a string, a vector, a hash, a code, a function, or a ghost.&lt;br /&gt;
&lt;br /&gt;
|example1 = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
print(id(&amp;quot;A&amp;quot;)); # prints &amp;quot;str:000000001624A590&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|name = int&lt;br /&gt;
|source = core&lt;br /&gt;
|line = 90&lt;br /&gt;
|heading = ===&lt;br /&gt;
|syntax = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;int(number);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|text = Returns the integer part of the numeric value of the single argument, or &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; if none exists.&lt;br /&gt;
|param1 = number&lt;br /&gt;
|param1text = Number or string with just a number in it to return an integer from.&lt;br /&gt;
&lt;br /&gt;
|example1 = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
print(int(23)); # prints &amp;quot;23&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|example2 = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
print(int(23.123)); # prints &amp;quot;23&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|example3 = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
debug.dump(int(&amp;quot;string&amp;quot;)); # prints &amp;quot;nil&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|name = keys&lt;br /&gt;
|source = core&lt;br /&gt;
|line = 33&lt;br /&gt;
|heading = ===&lt;br /&gt;
|syntax = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;keys(hash);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|text = Returns a vector containing the list of keys found in the single hash argument. &lt;br /&gt;
|param1 = hash&lt;br /&gt;
|param1text = The hash to return the keys from.&lt;br /&gt;
&lt;br /&gt;
|example1 = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
# Initialize a hash&lt;br /&gt;
var hash = {&lt;br /&gt;
    element1: &amp;quot;value&amp;quot;,&lt;br /&gt;
    element2: &amp;quot;value&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
debug.dump(keys(hash)); # print the vector&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|name = left&lt;br /&gt;
|source = core&lt;br /&gt;
|line = 149&lt;br /&gt;
|version = 2.12.0+&lt;br /&gt;
|heading = ===&lt;br /&gt;
|syntax = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;left(string, length);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|text = Returns a substring of '''string''', starting from the left.&lt;br /&gt;
|param1 = string&lt;br /&gt;
|param1text = String to return part of.&lt;br /&gt;
|param2 = length&lt;br /&gt;
|param2text = Integer specifying the length of the substring to return.&lt;br /&gt;
&lt;br /&gt;
|example1 = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
print(left(&amp;quot;string&amp;quot;, 2)); # prints &amp;quot;st&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|name = num&lt;br /&gt;
|source = core&lt;br /&gt;
|line = 102&lt;br /&gt;
|heading = ===&lt;br /&gt;
|syntax = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;num(number);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|text = Returns the numerical value of the single striing argument, or &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; if none exists. &lt;br /&gt;
|param1 = number&lt;br /&gt;
|param1text = String with just a number in it to return a number from.&lt;br /&gt;
&lt;br /&gt;
|example1 = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
print(num(&amp;quot;23&amp;quot;)); # prints &amp;quot;23&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|example2 = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
print(num(&amp;quot;23.123&amp;quot;)); # prints &amp;quot;23.123&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|example3 = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
debug.dump(num(&amp;quot;string&amp;quot;)); # prints &amp;quot;nil&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|name = pop&lt;br /&gt;
|source = core&lt;br /&gt;
|line = 50&lt;br /&gt;
|heading = ===&lt;br /&gt;
|syntax = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;pop(vector);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|text = Removes and returns the last element of the single vector argument, or &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; if the vector is empty. &lt;br /&gt;
|param1 = vector&lt;br /&gt;
|param1text = Vector to remove an element from.&lt;br /&gt;
&lt;br /&gt;
|example1 = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
var vector = [1, 2, 3];&lt;br /&gt;
pop(vector);&lt;br /&gt;
debug.dump(vector); # prints &amp;quot;[1, 2]&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|example2 = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
var vector = [1, 2, 3];&lt;br /&gt;
debug.dump(pop(vector)); # prints &amp;quot;3&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|example3 = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
var vector = [];&lt;br /&gt;
debug.dump(pop(vector)); # prints &amp;quot;nil&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|name = right&lt;br /&gt;
|source = core&lt;br /&gt;
|line = 161&lt;br /&gt;
|version = 2.12.0+&lt;br /&gt;
|heading = ===&lt;br /&gt;
|syntax = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;right(string, length);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|text = Returns a substring of '''string''', starting from the right.&lt;br /&gt;
|param1 = string&lt;br /&gt;
|param1text = String to return part of.&lt;br /&gt;
|param2 = length&lt;br /&gt;
|param2text = Integer specifying the length of the substring to return.&lt;br /&gt;
&lt;br /&gt;
|example1 = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
print(right(&amp;quot;string&amp;quot;, 2)); # prints &amp;quot;ng&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|name = setsize&lt;br /&gt;
|source = core&lt;br /&gt;
|line = 56&lt;br /&gt;
|heading = ===&lt;br /&gt;
|syntax = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;setsize(vector, size);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|text = Sets the size of a vector. The first argument specifies a vector, the second a number representing the desired size of that vector. If the vector is currently larger than the specified size, it is truncated. If it is smaller, it is padded with &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; entries. Returns the vector operated upon. &lt;br /&gt;
|param1 = vector&lt;br /&gt;
|param1text = The vector to be operated on.&lt;br /&gt;
|param2 = size&lt;br /&gt;
|param2text = The desired size of the vector in number of entries.&lt;br /&gt;
&lt;br /&gt;
|example1 = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
var vector = [1, 2, 3]; # Initialize a vector&lt;br /&gt;
setsize(vector, 4);&lt;br /&gt;
debug.dump(vector); # print the vector&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|example2 = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
var vector = [1, 2, 3]; # Initialize a vector&lt;br /&gt;
setsize(vector, 2);&lt;br /&gt;
debug.dump(vector); # print the vector&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|name = size&lt;br /&gt;
|source = core&lt;br /&gt;
|line = 23&lt;br /&gt;
|heading = ===&lt;br /&gt;
|syntax = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;size(object);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|text = Returns the size of the single argument. For strings, this is the length in bytes. For vectors, this is the number of elements. For hashes, it is the number of key/value pairs. If the arguent is &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt; or a number, this error will be thrown: &amp;lt;code&amp;gt;object has no size()&amp;lt;/code&amp;gt;.&lt;br /&gt;
|param1 = object&lt;br /&gt;
|param1text = Object to find the size of.  Must be a string, a vector or a hash.&lt;br /&gt;
&lt;br /&gt;
|example1 = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
var string = &amp;quot;string&amp;quot;;&lt;br /&gt;
print(size(string)); # prints &amp;quot;6&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|example2 = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
var vector = [1, 2, 3];&lt;br /&gt;
print(size(vector)); # prints &amp;quot;3&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|example3 = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
var hash = {&lt;br /&gt;
    element1: &amp;quot;value1&amp;quot;,&lt;br /&gt;
    element2: &amp;quot;value2&amp;quot;,&lt;br /&gt;
    element3: &amp;quot;value3&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
print(size(hash)); # prints &amp;quot;3&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|name = sort&lt;br /&gt;
|source = core&lt;br /&gt;
|line = 542&lt;br /&gt;
|heading = ===&lt;br /&gt;
|syntax = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;sort(vector, function);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|text = Returns a vector containing the elements in the input '''vector''' sorted in according to the rule given by '''function'''. Implemented with the ANSI C {{Func link|qsort()|ext=http://www.cplusplus.com/reference/cstdlib/qsort/}}, &amp;lt;code&amp;gt;sort()&amp;lt;/code&amp;gt; is stable.  This means that if the rules in the first example are used, equal elements in the output vector will appear in the same relative order as they do in the input.  It is run in a loop, so '''function''' is run several times.&lt;br /&gt;
|param1 = vector&lt;br /&gt;
|param1text = Input vector to sort.&lt;br /&gt;
|param2 = function&lt;br /&gt;
|param2text = Function according to which the elements will be sorted by.  It should take two arguments and should return one of 1, 0, or -1.&lt;br /&gt;
{{{!}} class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Return value !! Meaning&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} less than 0 {{!!}} first argument should go before second argument&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} 0 {{!!}} first argument equals second argument&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} greater than 0 {{!!}} first argument should go after second argument&lt;br /&gt;
{{!}}}&lt;br /&gt;
&lt;br /&gt;
|example1text = This example sorts elements from smallest to greatest.&lt;br /&gt;
|example1 = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
var sort_rules = func(a, b){&lt;br /&gt;
    if(a &amp;lt; b){&lt;br /&gt;
        return -1; # A should before b in the returned vector&lt;br /&gt;
    }elsif(a == b){&lt;br /&gt;
        return 0; # A is equivalent to b &lt;br /&gt;
    }else{&lt;br /&gt;
        return 1; # A should after b in the returned vector&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
debug.dump(sort([3, 2, 5, 6, 4, 1], sort_rules)); # prints &amp;quot;[1, 2, 3, 4, 5, 6]&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|example1text = This example sorts elements from greatest to smallest.&lt;br /&gt;
|example2 = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
# Outputs the elements in reverse order (greatest to smallest)&lt;br /&gt;
var sort_rules = func(a, b){&lt;br /&gt;
    if(a &amp;lt; b){&lt;br /&gt;
        return 1; # -1 in the above example&lt;br /&gt;
    }elsif(a == b){&lt;br /&gt;
        return 0;&lt;br /&gt;
    }else{&lt;br /&gt;
        return -1; # 1 in the above example&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
debug.dump(sort([3, 2, 5, 6, 4, 1], sort_rules)); # prints &amp;quot;[6, 5, 4, 3, 2, 1]&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|name = split&lt;br /&gt;
|source = core&lt;br /&gt;
|line = 460&lt;br /&gt;
|heading = ===&lt;br /&gt;
|syntax = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;split(delimiter, string);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|text = Splits the input string into a vector of substrings bounded by occurrences of the delimiter substring.&lt;br /&gt;
|param1 = delimiter&lt;br /&gt;
|param1text = String that will split the substrings in the returned vector.&lt;br /&gt;
|param2 = string&lt;br /&gt;
|param2text = String to split up.&lt;br /&gt;
&lt;br /&gt;
|example1 = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
debug.dump(split(&amp;quot;cd&amp;quot;, &amp;quot;abcdef&amp;quot;)); # prints &amp;quot;['ab', 'ef']&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|example2 = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
debug.dump(split(&amp;quot;.&amp;quot;, &amp;quot;3.2.0&amp;quot;)); # prints &amp;quot;[3, 2, 0]&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|example3 = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
debug.dump(split(&amp;quot;/&amp;quot;, &amp;quot;path/to/file&amp;quot;)); # prints &amp;quot;['path', 'to', 'file']&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|name = sprintf&lt;br /&gt;
|source = core&lt;br /&gt;
|line = 355&lt;br /&gt;
|heading = ===&lt;br /&gt;
|syntax = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;sprintf(format[, arg[, arg, [...]]]);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|text = Creates and returns a string formatted using ANSI C {{Func link|vsnprintf()|ext=http://en.cppreference.com/w/c/io/vfprintf}} &amp;lt;ref&amp;gt;&lt;br /&gt;
{{Cite web&lt;br /&gt;
|url = https://gitorious.org/fg/simgear/source/next:simgear/nasal/lib.c#L308&lt;br /&gt;
|title = fg/simgear/source/next:simgear/nasal/lib.c, line 308&lt;br /&gt;
|accessdate = February 2015&lt;br /&gt;
}}&lt;br /&gt;
&amp;lt;/ref&amp;gt;.  Below is a table of supported format specifiers.&lt;br /&gt;
{{{!}} class=&amp;quot;wikitable&amp;quot; width=&amp;quot;75%&amp;quot;&lt;br /&gt;
{{!}}+ %[flags][width][.precision]specifier&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; {{!}} Flags&lt;br /&gt;
{{!-}}&lt;br /&gt;
! Flag !! Output&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;+&amp;lt;/code&amp;gt; {{!!}} Forces to precede the result with a plus or minus sign ('''+''' or '''-''') even for positive numbers. By default, only negative numbers are preceded with a '''-''' sign.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} ''space'' {{!!}} Prefixes non-signed numbers with a space.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;-&amp;lt;/code&amp;gt; {{!!}} Left-align the output of this placeholder (the default is to right-align the output) when the width option is specified.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt; {{!!}} Use 0 instead of spaces to pad a field when the width option is specified.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;#&amp;lt;/code&amp;gt; {{!!}} Used with &amp;lt;code&amp;gt;o&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;X&amp;lt;/code&amp;gt; specifiers the value is preceded with &amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;0x&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;0X&amp;lt;/tt&amp;gt; respectively for values different than zero. Used with &amp;lt;code&amp;gt;e&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;E&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;f&amp;lt;/code&amp;gt;, it forces the written output to contain a decimal point even if no digits would follow. By default, if no digits follow, no decimal point is written. Used with &amp;lt;code&amp;gt;g&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;G&amp;lt;/code&amp;gt; the result is the same as with &amp;lt;code&amp;gt;e&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;E&amp;lt;/code&amp;gt; but trailing zeros are not removed.&lt;br /&gt;
{{!-}}&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; {{!}} Width&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} colspan=&amp;quot;2&amp;quot; {{!}} Integer specifying the minimum number of characters to be returned.&lt;br /&gt;
{{!-}}&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; {{!}} Precision&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} colspan=&amp;quot;2&amp;quot; {{!}} Integer preceded by a dot specifying the number of decimal places to be written.&lt;br /&gt;
{{!-}}&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; {{!}} Specifiers&lt;br /&gt;
{{!-}}&lt;br /&gt;
! Specifier !! Output&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;d&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;i&amp;lt;/code&amp;gt; {{!!}} Signed decimal number.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;%&amp;lt;/code&amp;gt; {{!!}} Percent (%) character.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;c&amp;lt;/code&amp;gt; {{!!}} A single character assigned to a character code, the code given in an integer argument.  See http://www.asciitable.com/ for a list of supported characters and their codes.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;o&amp;lt;/code&amp;gt; {{!!}} Unsigned integer as an octal number.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;u&amp;lt;/code&amp;gt; {{!!}} Unsigned decimal integer.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;X&amp;lt;/code&amp;gt; {{!!}} Unsigned integer as a hexadecimal number.  If &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt; is used, any letters in the number are lowercase, while &amp;lt;code&amp;gt;X&amp;lt;/code&amp;gt; gives uppercase.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;e&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;E&amp;lt;/code&amp;gt; {{!!}} Double value in scientific notation (i.e., ''[-]ddd.ddd'''e'''[+/-]ddd''), with an exponent being denoted by &amp;lt;tt&amp;gt;e&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;E&amp;lt;/tt&amp;gt; depending on whether an upper or lowercase is used respectively.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;f&amp;lt;/code&amp;gt; {{!!}} Floating-point number, in fixed decimal notation, by default with 6 decimal places.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;F&amp;lt;/code&amp;gt; {{!!}} Appears to be supported &amp;lt;ref&amp;gt;&lt;br /&gt;
{{Cite web&lt;br /&gt;
|url = https://gitorious.org/fg/simgear/source/next:simgear/nasal/lib.c#L389&lt;br /&gt;
|title = fg/simgear/source/next:simgear/nasal/lib.c, line 389&lt;br /&gt;
|accessdate = February 2015&lt;br /&gt;
}}&lt;br /&gt;
&amp;lt;/ref&amp;gt;, but doesn't work.&lt;br /&gt;
{{!-}}&lt;br /&gt;
{{!}} &amp;lt;code&amp;gt;g&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;G&amp;lt;/code&amp;gt; {{!!}} Double in either normal or exponential notation, whichever is more appropriate for its magnitude. &amp;lt;code&amp;gt;g&amp;lt;/code&amp;gt; uses lower-case letters, &amp;lt;code&amp;gt;G&amp;lt;/code&amp;gt; uses upper-case letters. This type differs slightly from fixed-point notation in that insignificant zeroes to the right of the decimal point are not included.  Also, the decimal point is not included on whole numbers.&lt;br /&gt;
{{!}}}&lt;br /&gt;
&lt;br /&gt;
|param1 = format&lt;br /&gt;
|param1text = String specifying the format.  Can be used with or without a format specifiers.  See below for examples.&lt;br /&gt;
|param2 = arg&lt;br /&gt;
|param2text = Argument specifying a value to replace a format placeholder (such as &amp;lt;code&amp;gt;%d&amp;lt;/code&amp;gt;) in the format string.  Not required if there are no format specifiers.&lt;br /&gt;
&lt;br /&gt;
|example1 = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
print(sprintf(&amp;quot;%i&amp;quot;, 54)); # prints &amp;quot;54&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|example2 = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
print(sprintf(&amp;quot;Pi = %+.10f&amp;quot;, math.pi)); # prints &amp;quot;Pi = +3.1415926536&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|example3 = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&lt;br /&gt;
&amp;gt;print(sprintf(&amp;quot;%6d&amp;quot;, 23)); # prints &amp;quot;    23&amp;quot;&lt;br /&gt;
print(sprintf(&amp;quot;%06d&amp;quot;, 23)); # prints &amp;quot;000023&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|example4 = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
var FGVer = getprop(&amp;quot;/sim/version/flightgear&amp;quot;);&lt;br /&gt;
print(sprintf(&amp;quot;You have FlightGear v%s&amp;quot;, FGVer)); # prints &amp;quot;You have FlightGear v&amp;lt;your version&amp;gt;&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|example5 = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
print(sprintf(&amp;quot;Hexadecimal 100000 = %X&amp;quot;, 100000)); # prints &amp;quot;Hexadecimal 100000 = 186A0&amp;quot;&lt;br /&gt;
print(sprintf(&amp;quot;Hexadecimal 100000 = %x&amp;quot;, 100000)); # prints &amp;quot;Hexadecimal 100000 = 186a0&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|example6 = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
print(sprintf(&amp;quot;Code 65 is %c&amp;quot;, 65)); # prints &amp;quot;Code 65 is A&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|example7 = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
print(sprintf(&amp;quot;%e&amp;quot;, 54)); # prints &amp;quot;5.400000e+001&amp;quot;&lt;br /&gt;
print(sprintf(&amp;quot;%E&amp;quot;, 54)); # prints &amp;quot;5.400000E+001&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|example8 = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
print(sprintf(&amp;quot;%o&amp;quot;, 54)); # prints &amp;quot;66&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|example9 = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
print(sprintf(&amp;quot;50%% of 100 is %i&amp;quot;, 100 / 2)); # prints &amp;quot;50% of 100 is 50&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|name = streq&lt;br /&gt;
|source = core&lt;br /&gt;
|line = 107&lt;br /&gt;
|heading = ===&lt;br /&gt;
|syntax = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;streq(a, b);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|text = Tests the string values of the two arguments for equality. This function is needed because the &amp;lt;code&amp;gt;'''=='''&amp;lt;/code&amp;gt; operator (see [[Nasal_Operators#Equality|Nasal Operators]]) tests for numeric equality first.  If either or both the arguments are not strings, 0 (False) will be returned.  Returns either 0 (False) or 1 (True).  {{Note|This function is rarely required in typical code.}}&lt;br /&gt;
|param1 = a&lt;br /&gt;
|param1text = First argument for testing equality.&lt;br /&gt;
|param2 = b&lt;br /&gt;
|param2text = Second argument for testing equality.&lt;br /&gt;
&lt;br /&gt;
|example1 = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
print(streq(&amp;quot;0&amp;quot;, &amp;quot;0&amp;quot;)); # prints &amp;quot;1&amp;quot; (True)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|example2 = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
print(0 == 0.0); # prints &amp;quot;1&amp;quot; (True)&lt;br /&gt;
print(streq(&amp;quot;0&amp;quot;, &amp;quot;0.0&amp;quot;)); # prints &amp;quot;0&amp;quot; (False)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|name = substr&lt;br /&gt;
|source = core&lt;br /&gt;
|line = 129&lt;br /&gt;
|heading = ===&lt;br /&gt;
|syntax = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;substr(string, start [, length]);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|text = Similar the {{func link|subvec}}, but operates on strings.  Computes and returns a substring. The first argument specifies a string, the second is the index of the start of a substring, the optional third argument specifies a length (the default is to return the rest of the string from the start).&lt;br /&gt;
|param1 = string&lt;br /&gt;
|param1text = String to return a substring from.&lt;br /&gt;
|param2 = start&lt;br /&gt;
|param2text = Integer specifying the start of a substring.&lt;br /&gt;
|param3 = length&lt;br /&gt;
|param3text = Optional argument specifying the length of the substring.  Defaults to the end of the string.&lt;br /&gt;
&lt;br /&gt;
|example1 = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
print(substr(&amp;quot;abcde&amp;quot;, 1, 3)); # prints &amp;quot;bcd&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|example2 = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
print(substr(&amp;quot;abcde&amp;quot;, 1)); # prints &amp;quot;bcde&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|example3 = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
print(substr(&amp;quot;abcde&amp;quot;, 2, 1)); # prints &amp;quot;c&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|name = subvec&lt;br /&gt;
|source = core&lt;br /&gt;
|line = 63&lt;br /&gt;
|heading = ===&lt;br /&gt;
|syntax = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;subvec(vector, start[, length]);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|text = Returns a sub-range of a vector. The first argument specifies a vector, the second a starting index, and the optional third argument indicates a length (the default is to the end of the vector). &lt;br /&gt;
|param1 = vector&lt;br /&gt;
|param1text = The vector to take the sub-vector from.&lt;br /&gt;
|param2 = start&lt;br /&gt;
|param2text = The starting point of the sub-vetor within the given vector.&lt;br /&gt;
|param3 = length&lt;br /&gt;
|param3text = Optional argument specifying the length of the sub-vector, from the starting point.&lt;br /&gt;
&lt;br /&gt;
|example1 = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
var vector = [1, 2, 3];&lt;br /&gt;
debug.dump(subvec(vector, 0)); # prints &amp;quot;[1, 2, 3]&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|example2 = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
var vector = [1, 2, 3];&lt;br /&gt;
debug.dump(subvec(vector, 1)); # prints &amp;quot;[2, 3]&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|example3 = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
var vector = [1, 2, 3];&lt;br /&gt;
debug.dump(subvec(vector, 1, 1)); # prints &amp;quot;[2]&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Nasal doc&lt;br /&gt;
|name = typeof&lt;br /&gt;
|source = core&lt;br /&gt;
|line = 193&lt;br /&gt;
|heading = ===&lt;br /&gt;
|syntax = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;typeof(object);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|text = Returns a string indicating the whether the object is &amp;lt;code&amp;gt;'''nil'''&amp;lt;/code&amp;gt;, a scalar (number or string), a vector, a hash, a function, or a ghost.&lt;br /&gt;
|param1 = object&lt;br /&gt;
|param1text = Object to return the type of.&lt;br /&gt;
|example1 = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
var object = nil;&lt;br /&gt;
print(typeof(object)); # prints &amp;quot;nil&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|example2 = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
var object = &amp;quot;Hello world!&amp;quot;;&lt;br /&gt;
print(typeof(object)); # prints &amp;quot;scalar&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|example3 = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
var object = math.pi;&lt;br /&gt;
print(typeof(object)); # prints &amp;quot;scalar&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|example4 = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
var object = [1, 2, 3];&lt;br /&gt;
print(typeof(object)); # prints &amp;quot;vector&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|example5 = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
var object = {};&lt;br /&gt;
print(typeof(object)); # prints &amp;quot;hash&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|example6 = &amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
var object = func {};&lt;br /&gt;
print(typeof(object)); # prints &amp;quot;func&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Extension functions ==&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;tt&amp;gt;cmdarg()&amp;lt;/tt&amp;gt; ===&lt;br /&gt;
cmdarg() is a mechanism to pass arguments to a nasal script (wrapped in properties) instead of &amp;quot;normal&amp;quot; function parameters. Note that cmdarg() should be primarily used in Nasal code embedded in XML files and should be considered depreciated otherwise (see [http://www.mail-archive.com/flightgear-devel@lists.sourceforge.net/msg18361.html]).&lt;br /&gt;
&lt;br /&gt;
cmdarg() will keep working in (joystick) XML-'''bindings''' and on the top-level of embedded Nasal scripts (i.e. dialog and animation XML files).&lt;br /&gt;
&lt;br /&gt;
As such, the cmdarg() function is primarily used for listener callbacks declared in XML markup, cmdarg() returns the listened-to property as props.Node object, so you can use it with all its methods (see [[$FG_ROOT]]/Nasal/props.nas) for example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
print(cmdarg().getPath(), &amp;quot; has been changed to &amp;quot;, cmdarg().getValue())&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The cmdarg() function avoids that you have to type the exact same path twice (once here and once in the setlistener() command) and it makes clear that this is the listened to property. Also, you can use all the nice props.Node methods on cmdarg() directly:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
setlistener(&amp;quot;/gear/launchbar/state&amp;quot;, func {&lt;br /&gt;
    if (cmdarg().getValue() == &amp;quot;Engaged&amp;quot;)&lt;br /&gt;
        setprop(&amp;quot;/sim/messages/copilot&amp;quot;, &amp;quot;Engaged!&amp;quot;);&lt;br /&gt;
}, 1, 0);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Use of cmdarg() outside of XML-bindings won't cause an error, but (still) return the last cmdarg() property. This just won't be the listened-to property anymore, but whatever the last legitimate cmdarg() user set. Most of the time it will be the property root of a joystick binding.&lt;br /&gt;
&lt;br /&gt;
Don't make any assumptions and use cmdarg() only in one of these cases:&lt;br /&gt;
&lt;br /&gt;
* binding: returns root of this binding's property branch. Needed for accessing an axis' value:         cmdarg().getNode(&amp;quot;setting&amp;quot;).getValue()&lt;br /&gt;
&lt;br /&gt;
* dialog xml files: returns root of that file's property branch in memory. This can be used to let embedded Nasal change the dialog (e.g. clear and build lists) before the final layout is decided&lt;br /&gt;
&lt;br /&gt;
* animation xml files: returns root of this model's place in /ai/models/ when used as AI/MP model. Examples:       /ai/models/multiplayer[3], /ai/models/tanker[1], etc.&amp;lt;ref&amp;gt;{{cite web |url=http://www.mail-archive.com/flightgear-devel@lists.sourceforge.net/msg14164.html |title=multiplayer generic properties |date=8 December 2007 |author=Melchior Franz}}&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* AI aircraft XML files&lt;br /&gt;
&lt;br /&gt;
* remotely invoking Nasal code by setting properties using the built-in telnet daemon (RPC)&amp;lt;ref&amp;gt;{{cite web |url=http://www.mail-archive.com/flightgear-devel@lists.sourceforge.net/msg00150.html |title=Calling FG functions via network interface |author=Melchior Franz |date=2 January 2006}}&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;{{cite web |url=http://www.mail-archive.com/flightgear-devel@lists.sourceforge.net/msg00336.html |title=Calling FG functions via network interface |author=Melchior Franz |date=7 January 2006}}&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
'''In all cases, the cmdarg() call must not be delayed until later using settimer() or setlistener(). Because later it'll again return some unrelated property!&lt;br /&gt;
'''&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;tt&amp;gt;fgcommand()&amp;lt;/tt&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Runs an internal &amp;quot;fgcommand&amp;quot;, see [[Bindings]] for a full list of available items (since they are most often used in bindings for input devices). The first argument is the name of the command (e.g. &amp;quot;property-interpolate&amp;quot;) and the second is a props.Node object or its ghost which is the &amp;quot;argument&amp;quot; to the fgcommand. (This ensures that fgcommands are universal since props.Node/SGPropertyNode objects can really be accessed from anywhere in FlightGear.) Each fgcommand returns 1 if it succeeded or 0 if it failed.&lt;br /&gt;
&lt;br /&gt;
The profiling related fgcommands '''profiler-start''' and '''profiler-stop''' are documented at [[Built-in Profiler]].&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;tt&amp;gt;addcommand()&amp;lt;/tt&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Add a fgcommand which can be used like a regular fgcommand (global scope, etc.). First argument is the name, second is the function to run. Note that this fgcommand will always return true! Inside the callback/function one can use either cmdarg() to retrieve the arguments or use the first argument passed to the function (since they will be the same).&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;tt&amp;gt;removecommand()&amp;lt;/tt&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
As you can guess, there's also a removecommand() function which will remove '''any''' command – even those implemented in C++! As such it can be very dangerous and remove core functionality, so use with caution.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;tt&amp;gt;print()&amp;lt;/tt&amp;gt; ===&lt;br /&gt;
Concatenates an arbitrary number of arguments to one string, appends a new-line, and prints it to the terminal. Returns the number of printed characters.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
print(&amp;quot;Just&amp;quot;, &amp;quot; a &amp;quot;, &amp;quot;test&amp;quot;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;tt&amp;gt;logprint()&amp;lt;/tt&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Similar, but the first argument is reserved for number specifying the priority (i.e. matching a sgDebugPriority object: 1 for bulk, 2 for debug, 3 for info, and 4 for warn). Also see &amp;lt;tt&amp;gt;printlog()&amp;lt;/tt&amp;gt; in globals.nas: it does essentially the same but with named levels (&amp;quot;bulk&amp;quot;, etc.). (The latter relies on &amp;lt;tt&amp;gt;print()&amp;lt;/tt&amp;gt;, however, and does not make use of the sophistication of &amp;lt;tt&amp;gt;sglog&amp;lt;/tt&amp;gt; in dealing with source file and line number.)&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;tt&amp;gt;getprop()&amp;lt;/tt&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Returns the node value for a given path, or &amp;lt;tt&amp;gt;nil&amp;lt;/tt&amp;gt; if the node doesn't exist or hasn't been initialized to a value yet. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
getprop(&amp;lt;path&amp;gt; [, &amp;lt;path&amp;gt;, [...]]);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Several arguments will be added together to produce a path, with numeric arguments specifying indexes (as of FlightGear 3.1), so&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
getprop(&amp;quot;canvas/by-index&amp;quot;, &amp;quot;texture&amp;quot;, 1, &amp;quot;name&amp;quot;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
is the same as:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
getprop(&amp;quot;canvas/by-index/texture[1]/name&amp;quot;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
print(&amp;quot;The frame rate is &amp;quot;, getprop(&amp;quot;/sim/frame-rate&amp;quot;), &amp;quot; FPS&amp;quot;);&lt;br /&gt;
for (var i=0; i &amp;lt; 10; i += 1) {&lt;br /&gt;
    print(&amp;quot;View &amp;quot;, i, &amp;quot;'s name is: &amp;quot;, getprop(&amp;quot;/sim/view&amp;quot;, i, &amp;quot;name&amp;quot;));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;tt&amp;gt;setprop()&amp;lt;/tt&amp;gt; ===&lt;br /&gt;
Sets a property value for a given node path string. Returns 1 on success or 0 if the property could not be set (i.e. was read-only).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
setprop(&amp;lt;path&amp;gt; [, &amp;lt;path&amp;gt;, [...]], &amp;lt;value&amp;gt;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
All arguments but the last are concatenated to a path string, like getprop() above. The last value is written to the respective node. If the node isn't writable, then an error message is printed to the console.&lt;br /&gt;
&lt;br /&gt;
Examples:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
setprop(&amp;quot;/sim/current-view/view-number&amp;quot;, 2);&lt;br /&gt;
setprop(&amp;quot;/controls&amp;quot;, &amp;quot;engines/engine[0]&amp;quot;, &amp;quot;reverser&amp;quot;, 1);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Erasing a property from the property tree''': a property that has been created, for example through &amp;lt;tt&amp;gt;setprop()&amp;lt;/tt&amp;gt; has to be erased using the props namespace helper, like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
props.globals.getNode(&amp;quot;foo/bar&amp;quot;).remove(); 		# take out the complete node&lt;br /&gt;
props.globals.getNode(&amp;quot;foo&amp;quot;).removeChild(&amp;quot;bar&amp;quot;); 	# take out a certain child node&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;tt&amp;gt;interpolate()&amp;lt;/tt&amp;gt; ===&lt;br /&gt;
Give the value from a value or a source node to a destination node in given time.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
interpolate(&amp;lt;path&amp;gt;, &amp;lt;value&amp;gt;, &amp;lt;time&amp;gt; [, &amp;lt;value2&amp;gt;, &amp;lt;time2&amp;gt; [, ...]]);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Examples:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
interpolate(&amp;quot;controls/switches/nav-lights-pos&amp;quot;, 1, 0.25); # After 25ms, nav-lights-pos = 1&lt;br /&gt;
interpolate(&amp;quot;controls/gear/brake-left-pos&amp;quot;, getprop(&amp;quot;controls/gear/brake-left&amp;quot;), 1); # After 1s, brake-left-pos = brake-left&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To interpolate with a constant ''speed'', use the formula &amp;lt;code&amp;gt;time = distance/speed&amp;lt;/code&amp;gt;.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
# Apply the brakes at 20%/second&lt;br /&gt;
var p = &amp;quot;controls/gear/brake-left&amp;quot;;&lt;br /&gt;
var current = getprop(p);&lt;br /&gt;
var dist = 1-current;&lt;br /&gt;
if (dist) interpolate(p, 1, dist/.2);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== setlistener() ===&lt;br /&gt;
&lt;br /&gt;
=== settimer() ===&lt;br /&gt;
{{Leaking Nasal Disclaimer&lt;br /&gt;
|oldapi=settimer()&lt;br /&gt;
|newapi=maketimer()&lt;br /&gt;
}}&lt;br /&gt;
Runs a function after a given simulation time (default) or real time in seconds.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
settimer(&amp;lt;function&amp;gt;, &amp;lt;time&amp;gt; [, &amp;lt;realtime=0&amp;gt;]);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The '''first argument''' is a function object (ie, &amp;quot;func { ... }&amp;quot;).  Note that this is different from a function call (ie, &amp;quot;func ( ... )&amp;quot;). If you don't understand what this means, just remember to always enclose the first argument in any call to settimer with the word &amp;quot;func&amp;quot; and braces &amp;quot;{ }&amp;quot;, and it will always work. &lt;br /&gt;
&lt;br /&gt;
The '''second argument''' is a delay time. After this amount of time the function will be executed.&lt;br /&gt;
For instance, if you want to print the words &amp;quot;My result&amp;quot; in five seconds, use this code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
settimer ( func { print ( &amp;quot;My result&amp;quot;); }, 5);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Inside the braces of the func object you can put any valid Nasal code, including a function call.  In fact, if you want to call a function with certain values as arguments, the way to do it is to turn it into a function object by enclosing it with a func{}, for example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
myarg1=&amp;quot;My favorite string&amp;quot;;&lt;br /&gt;
myarg2=432;&lt;br /&gt;
settimer ( func { myfunction ( myarg1, myarg2); }, 25);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The '''third argument''' is optional and defaults to 0, which lets the time argument be interpreted as &amp;quot;seconds simulation time&amp;quot;. In this case the timer doesn't run when FlightGear is paused. For user interaction purposes (measuring key press time, displaying popups, etc.) one usually prefers real time.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
# simulation time example&lt;br /&gt;
var copilot_annoyed = func { setprop(&amp;quot;/sim/messages/copilot&amp;quot;, &amp;quot;Stop it! Immediately!&amp;quot;) }&lt;br /&gt;
settimer(copilot_annoyed, 10);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
# real time example&lt;br /&gt;
var popdown = func ( tipArg ) { &lt;br /&gt;
    fgcommand(&amp;quot;dialog-close&amp;quot;, tipArg); &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
var selfStatusPopupTip = func (label, delay = 10, override = nil) {	&lt;br /&gt;
    var tmpl = props.Node.new({&lt;br /&gt;
            name : &amp;quot;PopTipSelf&amp;quot;, modal : 0, layout : &amp;quot;hbox&amp;quot;,&lt;br /&gt;
            y: 140,&lt;br /&gt;
            text : { label : label, padding : 6 }&lt;br /&gt;
    });&lt;br /&gt;
    if (override != nil) tmpl.setValues(override);&lt;br /&gt;
&lt;br /&gt;
    popdown(tipArgSelf);&lt;br /&gt;
    fgcommand(&amp;quot;dialog-new&amp;quot;, tmpl);&lt;br /&gt;
    fgcommand(&amp;quot;dialog-show&amp;quot;, tipArgSelf);&lt;br /&gt;
&lt;br /&gt;
    currTimerSelf += 1;&lt;br /&gt;
    var thisTimerSelf = currTimerSelf;&lt;br /&gt;
&lt;br /&gt;
    # Final argument 1 is a flag to use &amp;quot;real&amp;quot; time, not simulated time&lt;br /&gt;
    settimer(func { if(currTimerSelf == thisTimerSelf) { popdown(tipArgSelf) } }, delay, 1);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Nasal scripting language#settimer loops|More information about using the settimer function to create loops.]]&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;tt&amp;gt;maketimer()&amp;lt;/tt&amp;gt; (2.11+) ===&lt;br /&gt;
&lt;br /&gt;
As of 2.11, there is a new API for making a timer that allows more control over what happens in a timer – as opposed to setting one and forgetting about it.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
var timer = maketimer(&amp;lt;interval&amp;gt;, &amp;lt;function&amp;gt;)&lt;br /&gt;
var timer = maketimer(&amp;lt;interval&amp;gt;, &amp;lt;self&amp;gt;, &amp;lt;function&amp;gt;)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
timer.start()&lt;br /&gt;
timer.stop()&lt;br /&gt;
timer.restart(&amp;lt;interval&amp;gt;)&lt;br /&gt;
timer.singleShot [read/write]&lt;br /&gt;
timer.isRunning [read-only]&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
# create timer with 1 second interval&lt;br /&gt;
var timer = maketimer(1.0, func { &lt;br /&gt;
 print('timer called'); &lt;br /&gt;
 }&lt;br /&gt;
);&lt;br /&gt;
# start the timer (with 1 second inverval)&lt;br /&gt;
timer.start();&lt;br /&gt;
# restart timer with 4 second interval&lt;br /&gt;
timer.restart(4);&lt;br /&gt;
&lt;br /&gt;
# fire one single time in 6 seconds&lt;br /&gt;
timer.singleShot = 1;&lt;br /&gt;
timer.restart(6);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
var Tooltip = {&lt;br /&gt;
  new: func&lt;br /&gt;
  {&lt;br /&gt;
    var m = {&lt;br /&gt;
      parents: [Tooltip]&lt;br /&gt;
    }&lt;br /&gt;
    m._hideTimer = maketimer(1.0, m, Tooltip._hideTimeout);&lt;br /&gt;
    m._hideTimer.singleShot = 1;&lt;br /&gt;
&lt;br /&gt;
   return m;&lt;br /&gt;
  },&lt;br /&gt;
  run: func&lt;br /&gt;
  {&lt;br /&gt;
    if( !me._hideTimer.isRunning )&lt;br /&gt;
      me._hideTimer.start();&lt;br /&gt;
  }&lt;br /&gt;
  _hideTimeout: func&lt;br /&gt;
  {&lt;br /&gt;
    print('_hideTimeout');&lt;br /&gt;
  }&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;tt&amp;gt;geodinfo()&amp;lt;/tt&amp;gt; ===&lt;br /&gt;
Returns information about geodetic coordinates. Takes two arguments: lat, lon (in degree) and returns a vector with two entries, or nil if no information could be obtained because the terrain tile wasn't loaded. The first entry is the elevation (in meters) for the given point, and the second is a hash with information about the assigned material, or nil if there was no material information available, because there is, for instance, an untextured building at that spot or the scenery tile is not loaded.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
var lat = getprop(&amp;quot;/position/latitude-deg&amp;quot;);&lt;br /&gt;
var lon = getprop(&amp;quot;/position/longitude-deg&amp;quot;);&lt;br /&gt;
var info = geodinfo(lat, lon);&lt;br /&gt;
&lt;br /&gt;
if (info != nil) {&lt;br /&gt;
    print(&amp;quot;the terrain under the aircraft is at elevation &amp;quot;, info[0], &amp;quot; m&amp;quot;);&lt;br /&gt;
    if (info[1] != nil)&lt;br /&gt;
        print(&amp;quot;and it is &amp;quot;, info[1].solid ? &amp;quot;solid ground&amp;quot; : &amp;quot;covered by water&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A full data set looks like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
debug.dump(geodinfo(lat, lon));&lt;br /&gt;
&lt;br /&gt;
# outputs&lt;br /&gt;
[ 106.9892101062052, { light_coverage : 0, bumpiness : 0.5999999999999999, load_resistance : 1e+30,&lt;br /&gt;
solid : 0,  names : [ &amp;quot;Lake&amp;quot;, &amp;quot;Pond&amp;quot;, &amp;quot;Reservoir&amp;quot;, &amp;quot;Stream&amp;quot;, &amp;quot;Canal&amp;quot; ], friction_factor : 1, &lt;br /&gt;
rolling_friction : 1.5 } ]&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that geodinfo is a *very* CPU intensive operation, particularly in FG 2.4.0 and earlier, so use sparingly ([http://forum.flightgear.org/viewtopic.php?f=4&amp;amp;p=135044#p135044 forum discussion here]).&lt;br /&gt;
&lt;br /&gt;
=== airportinfo() ===&lt;br /&gt;
Function for retrieval of airport/runway information.&lt;br /&gt;
Usage:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
var apt = airportinfo(&amp;quot;KHAF&amp;quot;);   # get info about KHAF&lt;br /&gt;
var apt = airportinfo(lat, lon); # get info about apt closest to lat/lon&lt;br /&gt;
var apt = airportinfo();         # get info about apt closest to aircraft  &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The command debug.dump(airportinfo(&amp;quot;KHAF&amp;quot;)) outputs this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
{ lon : -122.4962626410256, lat : 37.51343502564102, has_metar : 0,&lt;br /&gt;
  runways : { 12 : { stopway2 : 0, threshold1 : 232.5624,&lt;br /&gt;
  lon : -122.5010889999999, lat : 37.513831, stopway1 : 0, width : 45.72,&lt;br /&gt;
  threshold2 : 232.5624, heading : 138.1199999999999, length : 1523.0856 } },&lt;br /&gt;
  elevation : 20.42159999999999, id : &amp;quot;KHAF&amp;quot;, name : &amp;quot;Half Moon Bay&amp;quot; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That is: a hash with elements lat/lon/elev/id/name/has_metar for the airport, and a hash with runways, each of which consists of lat/lon/length/width/heading/threshold[12]/stopway[12]. Only one side of each runway is listed -- the other can easily be deduced.&lt;br /&gt;
&lt;br /&gt;
To list all Runways :&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
var rwy = airportinfo(&amp;quot;KHAF&amp;quot;).runways;&lt;br /&gt;
foreach(var key;keys(rwy)) {&lt;br /&gt;
 print (key);    # returns &amp;quot;30&amp;quot;, &amp;quot;12&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Positioned Object Queries ===&lt;br /&gt;
&lt;br /&gt;
Several functions exist to query the [[Navdb|navigation database]] of 'positioned' objects, i.e items with a defined location in the simulation world. (These objects all inherit from &amp;lt;tt&amp;gt;FGPositioned&amp;lt;/tt&amp;gt; internally). The functions return either one, or a vector, of wrapped Nasal objects. These are efficient, but unlike a hash they cannot simply be &amp;lt;tt&amp;gt;debug.dump()&amp;lt;/tt&amp;gt; to view all their members and methods, many of which are computed in a lazy fashion (i.e. on demand).&lt;br /&gt;
&lt;br /&gt;
When the query functions take a position, the default value is the current aircraft location. An alternative location can be supplied by passing two number (lat, lon), a Geo.Coord object, or any positioned object or waypoint retrieved from another query.&lt;br /&gt;
&lt;br /&gt;
;findAirportsWithinRange()&lt;br /&gt;
: Find all airports within a specified range (in NM) of the current position or explicit position&lt;br /&gt;
;findAirportsByICAO()&lt;br /&gt;
: Find airports matching a complete or partial ICAO code. In particular this can search for all airports starting with a two or three letter prefix.&lt;br /&gt;
;navinfo()&lt;br /&gt;
: Return a list of navaids near a location by type and ident. Type should be 'fix', 'vor', 'ndb', 'dme'&lt;br /&gt;
;findNavaidsWithinRange()&lt;br /&gt;
: Search for navaids within a particular range (in NM), optionally limited by type. This provides a 'find the closest ILS' function&lt;br /&gt;
;findNavaidByFrequency()&lt;br /&gt;
: find the closest navaid (of a particular type) matching an exact frequency&lt;br /&gt;
;findNavaidsByFrequency()&lt;br /&gt;
: find all the navaids matching a particular frequency and optional type, sorted by distance from the search location.&lt;br /&gt;
&lt;br /&gt;
All positioned objects returned by the above methods have the following members:&lt;br /&gt;
;id&lt;br /&gt;
: Identifier - ICAO code for airports, published ident for navaids and fixes&lt;br /&gt;
;lon&lt;br /&gt;
: degrees longitude&lt;br /&gt;
;lat&lt;br /&gt;
: degrees latitude&lt;br /&gt;
&lt;br /&gt;
Depending on type, the following members are also available:&lt;br /&gt;
;name&lt;br /&gt;
: the full name of the airport or navaid if one is defined&lt;br /&gt;
;elevation&lt;br /&gt;
: the ASL elevation of the object in feet&lt;br /&gt;
&lt;br /&gt;
For navaids, the following members are available:&lt;br /&gt;
;frequency&lt;br /&gt;
: the navaid frequency in kHz&lt;br /&gt;
;type&lt;br /&gt;
: the navaid type as a string: vor, dme, loc, ils, ndb&lt;br /&gt;
;course&lt;br /&gt;
: the degrees course associated with the navaid, for localiser and ILS stations&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;tt&amp;gt;history() &amp;lt;/tt&amp;gt; (3.1+) ===&lt;br /&gt;
&lt;br /&gt;
Function to expose flight history as aircraft.history()&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
var hist = aircraft.history();&lt;br /&gt;
&lt;br /&gt;
# get history of aircraft position/orientation collapsing&lt;br /&gt;
# nodes with a distance smaller than the given minimum&lt;br /&gt;
# edge legth&lt;br /&gt;
debug.dump( hist.pathForHistory(&amp;lt;minimum-edge-length-meter&amp;gt;) );&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;tt&amp;gt;flightplan()&amp;lt;/tt&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Function to retrieve the active flight-plan object, or load a flight plan from a file path.&lt;br /&gt;
&lt;br /&gt;
Usage:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
var fp = flightplan();&lt;br /&gt;
var fp = flightplan('/some/path/to/a/flightplan.xml');&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In advance of converting the Map and NavDisplay to use the [[Canvas]], James has improved the &amp;quot;flightplan()&amp;quot; extension function of the [[Nasal]] scripting interpreter to expose the full route-path vector for each flight plan leg as a vector on the leg.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
var fp = flightplan();&lt;br /&gt;
for (var i=0; i&amp;lt;fp.getPlanSize(); i += 1)&lt;br /&gt;
{&lt;br /&gt;
  var leg = fp.getWP(i);&lt;br /&gt;
  debug.dump(leg.path());&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;tt&amp;gt;systime()&amp;lt;/tt&amp;gt; ===&lt;br /&gt;
Returns epoch time (time since 1970/01/01 00:00) in seconds as a floating point number with high resolution. This is useful for benchmarking purposes.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
#benchmarking example:&lt;br /&gt;
var start = systime();&lt;br /&gt;
how_fast_am_I(123);&lt;br /&gt;
var end = systime();&lt;br /&gt;
print(&amp;quot;took &amp;quot;, end - start, &amp;quot; seconds&amp;quot;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;tt&amp;gt;carttogeod()&amp;lt;/tt&amp;gt; ===&lt;br /&gt;
Converts cartesian coordinates x/y/z to geodetic coordinates lat/lon/alt, which are returned as a vector. Units are degree and meter.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
var geod = carttogeod(-2737504, -4264101, 3862172);&lt;br /&gt;
print(&amp;quot;lat=&amp;quot;, geod[0], &amp;quot; lon=&amp;quot;, geod[1], &amp;quot; alt=&amp;quot;, geod[2]);&lt;br /&gt;
&lt;br /&gt;
# outputs&lt;br /&gt;
lat=37.49999782141546 lon=-122.6999914632327 alt=998.6042055172776&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;tt&amp;gt;geodtocart()&amp;lt;/tt&amp;gt; ===&lt;br /&gt;
Converts geodetic coordinates lat/lon/alt to cartesian coordinates x/y/z. Units are degree and meter.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
var cart = geodtocart(37.5, -122.7, 1000); # lat/lon/alt(m)&lt;br /&gt;
print(&amp;quot;x=&amp;quot;, cart[0], &amp;quot; y=&amp;quot;, cart[1], &amp;quot; z=&amp;quot;, cart[2]);&lt;br /&gt;
&lt;br /&gt;
# outputs&lt;br /&gt;
x=-2737504.667684828 y=-4264101.900993474 z=3862172.834656495&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;tt&amp;gt;parsexml()&amp;lt;/tt&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
This function is an interface to the built-in [http://expat.sourceforge.net/ Expat XML parser]. It takes up to five arguments. The first is a mandatory absolute path to an XML file, the remaining four are optional callback functions, each of which can be nil (which is also the default value).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
var ret = parsexml(&amp;lt;path&amp;gt; [, &amp;lt;start-elem&amp;gt; [, &amp;lt;end-elem&amp;gt; [, &amp;lt;data&amp;gt; [, &amp;lt;pi&amp;gt; ]]]]);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;start-elem&amp;gt;  ... called for every starting tag with two arguments: the tag name, and an attribute hash&lt;br /&gt;
&amp;lt;end-elem&amp;gt;    ... called for every ending tag with one argument: the tag name&lt;br /&gt;
&amp;lt;data&amp;gt;        ... called for every piece of data with one argument: the data string&lt;br /&gt;
&amp;lt;pi&amp;gt;          ... called for every &amp;quot;processing information&amp;quot; with two args: target and data string&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ret&amp;gt;         ... the return value is nil on error, and the &amp;lt;path&amp;gt; otherwise&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
var start = func(name, attr) {&lt;br /&gt;
    print(&amp;quot;starting tag &amp;quot;, name);&lt;br /&gt;
    foreach (var a; keys(attr))&lt;br /&gt;
        print(&amp;quot;\twith attribute &amp;quot;, a, &amp;quot;=&amp;quot;, attr[a]);&lt;br /&gt;
}&lt;br /&gt;
var end = func(name) { print(&amp;quot;ending tag &amp;quot;, name) }&lt;br /&gt;
var data = func(data) { print(&amp;quot;data=&amp;quot;, data) }&lt;br /&gt;
var pi = func(target, data) { print(&amp;quot;processing instruction: target=&amp;quot;, target, &amp;quot; data=&amp;quot;, data) }&lt;br /&gt;
parsexml(&amp;quot;/tmp/foo.xml&amp;quot;, start, end, data, pi);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;tt&amp;gt;resolvepath()&amp;lt;/tt&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
SimGear features its own [[Resolving Paths|path resolving framework]] that takes a relative path and returns an absolute path, checking from base directories such as [[$FG_ROOT]], [[$FG_HOME]], [[$FG_AIRCRAFT]], and the current aircraft directory (&amp;lt;tt&amp;gt;/sim/aircraft-dir&amp;lt;/tt&amp;gt;). This function in Nasal takes a path string and returns the absolute path or an empty string if the path couldn't be resolved.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
var guess_path = func(path...) {&lt;br /&gt;
    var path_concat = string.join(path, &amp;quot;/&amp;quot;);&lt;br /&gt;
    var file_path = resolvepath(path_concat);&lt;br /&gt;
    if (file_path == &amp;quot;&amp;quot;) die(&amp;quot;Path not found: &amp;quot;~path_concat);&lt;br /&gt;
    return file_path;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== HTTP module (2.99+) ===&lt;br /&gt;
&lt;br /&gt;
Get remote data using the HTTP.&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;tt&amp;gt;http.load()&amp;lt;/tt&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Load resource identified by its URL into memory.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
var request = http.load(&amp;lt;url&amp;gt;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
http.load(&amp;quot;http://example.com/test.txt&amp;quot;)&lt;br /&gt;
    .done(func(r) print(&amp;quot;Got response: &amp;quot; ~ r.response));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;tt&amp;gt;http.save()&amp;lt;/tt&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Save resource to a local file.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
var request = http.save(&amp;lt;url&amp;gt;, &amp;lt;file_path&amp;gt;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
http.save(&amp;quot;http://example.com/test.png&amp;quot;, getprop('/sim/fg-home') ~ '/cache/test.png')&lt;br /&gt;
    .fail(func print(&amp;quot;Download failed!&amp;quot;))&lt;br /&gt;
    .done(func(r) print(&amp;quot;Finished request with status: &amp;quot; ~ r.status ~ &amp;quot; &amp;quot; ~ r.reason))&lt;br /&gt;
    .always(func print(&amp;quot;Request complete (fail or success)&amp;quot;));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;tt&amp;gt;rand()&amp;lt;/tt&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Return a random number as generated by &amp;lt;tt&amp;gt;sg_random&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;tt&amp;gt;srand()&amp;lt;/tt&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Seed the random number generator based upon the current time. Returns 0.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;tt&amp;gt;md5()&amp;lt;/tt&amp;gt; (3.1+) ===&lt;br /&gt;
&lt;br /&gt;
Get the md5 hash of a string.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot;&amp;gt;&lt;br /&gt;
var hash = md5(&amp;lt;str&amp;gt;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;tt&amp;gt;abort()&amp;lt;/tt&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
Wrapper for the C++ library abort() function – i.e. it just aborts the process without regard to what's happening. For exiting (gracefully) from FlightGear use the fgcommand &amp;quot;exit&amp;quot; instead.&lt;br /&gt;
&lt;br /&gt;
{{Appendix}}&lt;/div&gt;</summary>
		<author><name>Clm76</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Nasal_library_functions&amp;diff=85140</id>
		<title>Nasal library functions</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Nasal_library_functions&amp;diff=85140"/>
		<updated>2015-05-29T17:05:01Z</updated>

		<summary type="html">&lt;p&gt;Clm76: /* subvec() */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Nasal Navigation}}&lt;br /&gt;
&lt;br /&gt;
Please first see: http://plausible.org/nasal/lib.html&lt;br /&gt;
&lt;br /&gt;
== size() ==&lt;br /&gt;
&lt;br /&gt;
== keys() ==&lt;br /&gt;
&lt;br /&gt;
== append() ==&lt;br /&gt;
&lt;br /&gt;
== pop() ==&lt;br /&gt;
&lt;br /&gt;
== setsize() ==&lt;br /&gt;
&lt;br /&gt;
== subvec() ==&lt;br /&gt;
Extract items of a vector - equivalent to the left() function for a string&lt;br /&gt;
&lt;br /&gt;
Example :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
var FGRoot = getprop(&amp;quot;/sim/fg-root&amp;quot;);&lt;br /&gt;
var filename = &amp;quot;/Aircraft&amp;quot;;&lt;br /&gt;
var path_files = directory(FGRoot ~ filename);&lt;br /&gt;
&lt;br /&gt;
foreach(var key; path_files) {&lt;br /&gt;
print(key);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
returns : '''&amp;quot;.&amp;quot; , &amp;quot;..&amp;quot; , &amp;quot;Generic&amp;quot;, &amp;quot;Instruments&amp;quot;, &amp;quot;Instruments-3d&amp;quot;, &amp;quot;c172p&amp;quot;, &amp;quot;ufo&amp;quot;'''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
With subvec():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
var FGRoot = getprop(&amp;quot;/sim/fg-root&amp;quot;);&lt;br /&gt;
var filename = &amp;quot;/Aircraft&amp;quot;;&lt;br /&gt;
var path_files = subvec(directory(FGRoot ~ filename),2);&lt;br /&gt;
&lt;br /&gt;
foreach(var key; path_files) {&lt;br /&gt;
print(key);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
returns : '''&amp;quot;Generic&amp;quot;, &amp;quot;Instruments&amp;quot;, &amp;quot;Instruments-3d&amp;quot;, &amp;quot;c172p&amp;quot;, &amp;quot;ufo&amp;quot;'''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
var FGRoot = getprop(&amp;quot;/sim/fg-root&amp;quot;);&lt;br /&gt;
var filename = &amp;quot;/Aircraft&amp;quot;;&lt;br /&gt;
var path_files = subvec(directory(FGRoot ~ filename),3);&lt;br /&gt;
&lt;br /&gt;
foreach(var key; path_files) {&lt;br /&gt;
print(key);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
returns : '''&amp;quot;Instruments&amp;quot;, &amp;quot;Instruments-3d&amp;quot;, &amp;quot;c172p&amp;quot;, &amp;quot;ufo&amp;quot;'''&lt;br /&gt;
&lt;br /&gt;
== delete() ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== int() ==&lt;br /&gt;
&lt;br /&gt;
== num(str) ==&lt;br /&gt;
Convert string to number.&lt;br /&gt;
Returns nil if str is nil, empty string, string with no number.&lt;br /&gt;
&lt;br /&gt;
== streq() ==&lt;br /&gt;
&lt;br /&gt;
== cmp() ==&lt;br /&gt;
&lt;br /&gt;
== substr() ==&lt;br /&gt;
&lt;br /&gt;
== chr() ==&lt;br /&gt;
&lt;br /&gt;
== contains() ==&lt;br /&gt;
&lt;br /&gt;
== typeof() ==&lt;br /&gt;
&lt;br /&gt;
== ghosttype(ghost) ==&lt;br /&gt;
Returns a string containing either a descriptive name as passed to ''naNewGhost'' or ''naNewGhost2'' in C/C++ while creating the ghost or a unique id (aka the pointer to the C/C++ naGhostType instance) if no name has been set.&lt;br /&gt;
&lt;br /&gt;
== compile() ==&lt;br /&gt;
&lt;br /&gt;
== call() ==&lt;br /&gt;
&lt;br /&gt;
== die() ==&lt;br /&gt;
&lt;br /&gt;
== sprintf() ==&lt;br /&gt;
&lt;br /&gt;
== caller() ==&lt;br /&gt;
&lt;br /&gt;
== closure() ==&lt;br /&gt;
&lt;br /&gt;
== find() ==&lt;br /&gt;
&lt;br /&gt;
== split() ==&lt;br /&gt;
&lt;br /&gt;
== rand() ==&lt;br /&gt;
&lt;br /&gt;
== bind() ==&lt;br /&gt;
&lt;br /&gt;
== sort() ==&lt;br /&gt;
&lt;br /&gt;
== id() ==&lt;br /&gt;
&lt;br /&gt;
= math =&lt;br /&gt;
== Trigonometric_functions ==&lt;br /&gt;
'''Caution''': All trigonometric functions works with [https://en.wikipedia.org/wiki/Radian Radian]-values - not with Degree!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;To convert this&amp;lt;/u&amp;gt;:&amp;lt;br&amp;gt;&lt;br /&gt;
radians = degree * math.pi / 180&amp;lt;br&amp;gt;&lt;br /&gt;
degree  = radians * 180 / math.pi&lt;br /&gt;
=== sin() ===&lt;br /&gt;
=== cos() ===&lt;br /&gt;
=== tan() ===&lt;br /&gt;
=== asin() ===&lt;br /&gt;
=== acos() ===&lt;br /&gt;
=== atan2() ===&lt;br /&gt;
== exp() ==&lt;br /&gt;
== ln() ==&lt;br /&gt;
== pow() ==&lt;br /&gt;
== sqrt() ==&lt;br /&gt;
== floor() ==&lt;br /&gt;
Returns the value rounded downward&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
math.floor(1.2); # returns 1&lt;br /&gt;
math.floor(1.6); # returns 1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== ceil() ==&lt;br /&gt;
Returns the value rounded upward&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
math.ceil(1.2); # returns 2&lt;br /&gt;
math.ceil(1.6); # returns 2&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== fmod() ==&lt;br /&gt;
A '''modulo'''-operator, mod(x,y) also works...&amp;lt;br&amp;gt;&lt;br /&gt;
The [https://en.wikipedia.org/wiki/Modulo_operation modulo]-operation finds the remainder of division of one number by another (sometimes called modulus).&lt;br /&gt;
&lt;br /&gt;
== clamp() ==&lt;br /&gt;
== periodic() ==&lt;br /&gt;
== round() ==&lt;br /&gt;
== pi ==&lt;br /&gt;
== e ==&lt;br /&gt;
&lt;br /&gt;
= io =&lt;br /&gt;
&lt;br /&gt;
* SEEK_SET&lt;br /&gt;
* SEEK_CUR&lt;br /&gt;
* SEEK_END&lt;br /&gt;
* stdin&lt;br /&gt;
* stdout&lt;br /&gt;
* stderr&lt;br /&gt;
&lt;br /&gt;
== stat() ==&lt;br /&gt;
== directory() ==&lt;br /&gt;
returns the files in a given directory&lt;br /&gt;
&lt;br /&gt;
e.g. chatter_list = directory( chatter_dir );&lt;br /&gt;
&lt;br /&gt;
Example : &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
var FGRoot = getprop(&amp;quot;/sim/fg-root&amp;quot;);&lt;br /&gt;
var filename = &amp;quot;/Aircraft&amp;quot;;&lt;br /&gt;
var path_files = directory(FGRoot ~ filename);&lt;br /&gt;
&lt;br /&gt;
foreach(var key; path_files) {&lt;br /&gt;
print(key);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
returns :   &lt;br /&gt;
'''&amp;quot;.&amp;quot; , &amp;quot;..&amp;quot; , &amp;quot;Generic&amp;quot;, &amp;quot;Instruments&amp;quot;, &amp;quot;Instruments-3d&amp;quot;, &amp;quot;c172p&amp;quot;, &amp;quot;ufo&amp;quot;'''&lt;br /&gt;
&lt;br /&gt;
To delete the &amp;quot;.&amp;quot; and &amp;quot;..&amp;quot; see the subvec() function.&lt;br /&gt;
&lt;br /&gt;
== open() ==&lt;br /&gt;
== read() ==&lt;br /&gt;
== readln() ==&lt;br /&gt;
== seek() ==&lt;br /&gt;
== tell() ==&lt;br /&gt;
== write() ==&lt;br /&gt;
== flush() ==&lt;br /&gt;
== close() ==&lt;br /&gt;
&lt;br /&gt;
= utf8 =&lt;br /&gt;
== chstr() ==&lt;br /&gt;
== strc() ==&lt;br /&gt;
== substr() ==&lt;br /&gt;
== size() ==&lt;br /&gt;
== validate() ==&lt;/div&gt;</summary>
		<author><name>Clm76</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Nasal_library_functions&amp;diff=85138</id>
		<title>Nasal library functions</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Nasal_library_functions&amp;diff=85138"/>
		<updated>2015-05-29T16:29:36Z</updated>

		<summary type="html">&lt;p&gt;Clm76: /* directory() */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Nasal Navigation}}&lt;br /&gt;
&lt;br /&gt;
Please first see: http://plausible.org/nasal/lib.html&lt;br /&gt;
&lt;br /&gt;
== size() ==&lt;br /&gt;
&lt;br /&gt;
== keys() ==&lt;br /&gt;
&lt;br /&gt;
== append() ==&lt;br /&gt;
&lt;br /&gt;
== pop() ==&lt;br /&gt;
&lt;br /&gt;
== setsize() ==&lt;br /&gt;
&lt;br /&gt;
== subvec() ==&lt;br /&gt;
Extract items of a vector (equivalent to the left() function for a string)&lt;br /&gt;
Example :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
var currentPath = &amp;quot;/home/xxx/yyy/&amp;quot;;&lt;br /&gt;
print (directory(currentPath));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
returns : &amp;quot;.&amp;quot;, &amp;quot;..&amp;quot;, &amp;quot;file1&amp;quot;, &amp;quot;file2&amp;quot;, &amp;quot;file3&amp;quot;&lt;br /&gt;
&lt;br /&gt;
and :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
var currentPath = &amp;quot;/home/xxx/yyy/&amp;quot;;&lt;br /&gt;
print (subvec(directory(currentPath),2));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
returns : &amp;quot;file1&amp;quot;, &amp;quot;file2&amp;quot;, &amp;quot;file3&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
var currentPath = &amp;quot;/home/xxx/yyy/&amp;quot;;&lt;br /&gt;
print (subvec(directory(currentPath),3));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
returns : &amp;quot;file2&amp;quot;, &amp;quot;file3&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== delete() ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== int() ==&lt;br /&gt;
&lt;br /&gt;
== num(str) ==&lt;br /&gt;
Convert string to number.&lt;br /&gt;
Returns nil if str is nil, empty string, string with no number.&lt;br /&gt;
&lt;br /&gt;
== streq() ==&lt;br /&gt;
&lt;br /&gt;
== cmp() ==&lt;br /&gt;
&lt;br /&gt;
== substr() ==&lt;br /&gt;
&lt;br /&gt;
== chr() ==&lt;br /&gt;
&lt;br /&gt;
== contains() ==&lt;br /&gt;
&lt;br /&gt;
== typeof() ==&lt;br /&gt;
&lt;br /&gt;
== ghosttype(ghost) ==&lt;br /&gt;
Returns a string containing either a descriptive name as passed to ''naNewGhost'' or ''naNewGhost2'' in C/C++ while creating the ghost or a unique id (aka the pointer to the C/C++ naGhostType instance) if no name has been set.&lt;br /&gt;
&lt;br /&gt;
== compile() ==&lt;br /&gt;
&lt;br /&gt;
== call() ==&lt;br /&gt;
&lt;br /&gt;
== die() ==&lt;br /&gt;
&lt;br /&gt;
== sprintf() ==&lt;br /&gt;
&lt;br /&gt;
== caller() ==&lt;br /&gt;
&lt;br /&gt;
== closure() ==&lt;br /&gt;
&lt;br /&gt;
== find() ==&lt;br /&gt;
&lt;br /&gt;
== split() ==&lt;br /&gt;
&lt;br /&gt;
== rand() ==&lt;br /&gt;
&lt;br /&gt;
== bind() ==&lt;br /&gt;
&lt;br /&gt;
== sort() ==&lt;br /&gt;
&lt;br /&gt;
== id() ==&lt;br /&gt;
&lt;br /&gt;
= math =&lt;br /&gt;
== Trigonometric_functions ==&lt;br /&gt;
'''Caution''': All trigonometric functions works with [https://en.wikipedia.org/wiki/Radian Radian]-values - not with Degree!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;To convert this&amp;lt;/u&amp;gt;:&amp;lt;br&amp;gt;&lt;br /&gt;
radians = degree * math.pi / 180&amp;lt;br&amp;gt;&lt;br /&gt;
degree  = radians * 180 / math.pi&lt;br /&gt;
=== sin() ===&lt;br /&gt;
=== cos() ===&lt;br /&gt;
=== tan() ===&lt;br /&gt;
=== asin() ===&lt;br /&gt;
=== acos() ===&lt;br /&gt;
=== atan2() ===&lt;br /&gt;
== exp() ==&lt;br /&gt;
== ln() ==&lt;br /&gt;
== pow() ==&lt;br /&gt;
== sqrt() ==&lt;br /&gt;
== floor() ==&lt;br /&gt;
Returns the value rounded downward&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
math.floor(1.2); # returns 1&lt;br /&gt;
math.floor(1.6); # returns 1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== ceil() ==&lt;br /&gt;
Returns the value rounded upward&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
math.ceil(1.2); # returns 2&lt;br /&gt;
math.ceil(1.6); # returns 2&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== fmod() ==&lt;br /&gt;
A '''modulo'''-operator, mod(x,y) also works...&amp;lt;br&amp;gt;&lt;br /&gt;
The [https://en.wikipedia.org/wiki/Modulo_operation modulo]-operation finds the remainder of division of one number by another (sometimes called modulus).&lt;br /&gt;
&lt;br /&gt;
== clamp() ==&lt;br /&gt;
== periodic() ==&lt;br /&gt;
== round() ==&lt;br /&gt;
== pi ==&lt;br /&gt;
== e ==&lt;br /&gt;
&lt;br /&gt;
= io =&lt;br /&gt;
&lt;br /&gt;
* SEEK_SET&lt;br /&gt;
* SEEK_CUR&lt;br /&gt;
* SEEK_END&lt;br /&gt;
* stdin&lt;br /&gt;
* stdout&lt;br /&gt;
* stderr&lt;br /&gt;
&lt;br /&gt;
== stat() ==&lt;br /&gt;
== directory() ==&lt;br /&gt;
returns the files in a given directory&lt;br /&gt;
&lt;br /&gt;
e.g. chatter_list = directory( chatter_dir );&lt;br /&gt;
&lt;br /&gt;
Example : &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
var FGRoot = getprop(&amp;quot;/sim/fg-root&amp;quot;);&lt;br /&gt;
var filename = &amp;quot;/Aircraft&amp;quot;;&lt;br /&gt;
var path_files = directory(FGRoot ~ filename);&lt;br /&gt;
&lt;br /&gt;
foreach(var key; path_files) {&lt;br /&gt;
print(key);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
returns :   &lt;br /&gt;
'''&amp;quot;.&amp;quot; , &amp;quot;..&amp;quot; , &amp;quot;Generic&amp;quot;, &amp;quot;Instruments&amp;quot;, &amp;quot;Instruments-3d&amp;quot;, &amp;quot;c172p&amp;quot;, &amp;quot;ufo&amp;quot;'''&lt;br /&gt;
&lt;br /&gt;
To delete the &amp;quot;.&amp;quot; and &amp;quot;..&amp;quot; see the subvec() function.&lt;br /&gt;
&lt;br /&gt;
== open() ==&lt;br /&gt;
== read() ==&lt;br /&gt;
== readln() ==&lt;br /&gt;
== seek() ==&lt;br /&gt;
== tell() ==&lt;br /&gt;
== write() ==&lt;br /&gt;
== flush() ==&lt;br /&gt;
== close() ==&lt;br /&gt;
&lt;br /&gt;
= utf8 =&lt;br /&gt;
== chstr() ==&lt;br /&gt;
== strc() ==&lt;br /&gt;
== substr() ==&lt;br /&gt;
== size() ==&lt;br /&gt;
== validate() ==&lt;/div&gt;</summary>
		<author><name>Clm76</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Nasal_library_functions&amp;diff=85137</id>
		<title>Nasal library functions</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Nasal_library_functions&amp;diff=85137"/>
		<updated>2015-05-29T16:28:38Z</updated>

		<summary type="html">&lt;p&gt;Clm76: /* directory() */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Nasal Navigation}}&lt;br /&gt;
&lt;br /&gt;
Please first see: http://plausible.org/nasal/lib.html&lt;br /&gt;
&lt;br /&gt;
== size() ==&lt;br /&gt;
&lt;br /&gt;
== keys() ==&lt;br /&gt;
&lt;br /&gt;
== append() ==&lt;br /&gt;
&lt;br /&gt;
== pop() ==&lt;br /&gt;
&lt;br /&gt;
== setsize() ==&lt;br /&gt;
&lt;br /&gt;
== subvec() ==&lt;br /&gt;
Extract items of a vector (equivalent to the left() function for a string)&lt;br /&gt;
Example :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
var currentPath = &amp;quot;/home/xxx/yyy/&amp;quot;;&lt;br /&gt;
print (directory(currentPath));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
returns : &amp;quot;.&amp;quot;, &amp;quot;..&amp;quot;, &amp;quot;file1&amp;quot;, &amp;quot;file2&amp;quot;, &amp;quot;file3&amp;quot;&lt;br /&gt;
&lt;br /&gt;
and :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
var currentPath = &amp;quot;/home/xxx/yyy/&amp;quot;;&lt;br /&gt;
print (subvec(directory(currentPath),2));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
returns : &amp;quot;file1&amp;quot;, &amp;quot;file2&amp;quot;, &amp;quot;file3&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
var currentPath = &amp;quot;/home/xxx/yyy/&amp;quot;;&lt;br /&gt;
print (subvec(directory(currentPath),3));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
returns : &amp;quot;file2&amp;quot;, &amp;quot;file3&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== delete() ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== int() ==&lt;br /&gt;
&lt;br /&gt;
== num(str) ==&lt;br /&gt;
Convert string to number.&lt;br /&gt;
Returns nil if str is nil, empty string, string with no number.&lt;br /&gt;
&lt;br /&gt;
== streq() ==&lt;br /&gt;
&lt;br /&gt;
== cmp() ==&lt;br /&gt;
&lt;br /&gt;
== substr() ==&lt;br /&gt;
&lt;br /&gt;
== chr() ==&lt;br /&gt;
&lt;br /&gt;
== contains() ==&lt;br /&gt;
&lt;br /&gt;
== typeof() ==&lt;br /&gt;
&lt;br /&gt;
== ghosttype(ghost) ==&lt;br /&gt;
Returns a string containing either a descriptive name as passed to ''naNewGhost'' or ''naNewGhost2'' in C/C++ while creating the ghost or a unique id (aka the pointer to the C/C++ naGhostType instance) if no name has been set.&lt;br /&gt;
&lt;br /&gt;
== compile() ==&lt;br /&gt;
&lt;br /&gt;
== call() ==&lt;br /&gt;
&lt;br /&gt;
== die() ==&lt;br /&gt;
&lt;br /&gt;
== sprintf() ==&lt;br /&gt;
&lt;br /&gt;
== caller() ==&lt;br /&gt;
&lt;br /&gt;
== closure() ==&lt;br /&gt;
&lt;br /&gt;
== find() ==&lt;br /&gt;
&lt;br /&gt;
== split() ==&lt;br /&gt;
&lt;br /&gt;
== rand() ==&lt;br /&gt;
&lt;br /&gt;
== bind() ==&lt;br /&gt;
&lt;br /&gt;
== sort() ==&lt;br /&gt;
&lt;br /&gt;
== id() ==&lt;br /&gt;
&lt;br /&gt;
= math =&lt;br /&gt;
== Trigonometric_functions ==&lt;br /&gt;
'''Caution''': All trigonometric functions works with [https://en.wikipedia.org/wiki/Radian Radian]-values - not with Degree!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;To convert this&amp;lt;/u&amp;gt;:&amp;lt;br&amp;gt;&lt;br /&gt;
radians = degree * math.pi / 180&amp;lt;br&amp;gt;&lt;br /&gt;
degree  = radians * 180 / math.pi&lt;br /&gt;
=== sin() ===&lt;br /&gt;
=== cos() ===&lt;br /&gt;
=== tan() ===&lt;br /&gt;
=== asin() ===&lt;br /&gt;
=== acos() ===&lt;br /&gt;
=== atan2() ===&lt;br /&gt;
== exp() ==&lt;br /&gt;
== ln() ==&lt;br /&gt;
== pow() ==&lt;br /&gt;
== sqrt() ==&lt;br /&gt;
== floor() ==&lt;br /&gt;
Returns the value rounded downward&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
math.floor(1.2); # returns 1&lt;br /&gt;
math.floor(1.6); # returns 1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== ceil() ==&lt;br /&gt;
Returns the value rounded upward&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
math.ceil(1.2); # returns 2&lt;br /&gt;
math.ceil(1.6); # returns 2&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== fmod() ==&lt;br /&gt;
A '''modulo'''-operator, mod(x,y) also works...&amp;lt;br&amp;gt;&lt;br /&gt;
The [https://en.wikipedia.org/wiki/Modulo_operation modulo]-operation finds the remainder of division of one number by another (sometimes called modulus).&lt;br /&gt;
&lt;br /&gt;
== clamp() ==&lt;br /&gt;
== periodic() ==&lt;br /&gt;
== round() ==&lt;br /&gt;
== pi ==&lt;br /&gt;
== e ==&lt;br /&gt;
&lt;br /&gt;
= io =&lt;br /&gt;
&lt;br /&gt;
* SEEK_SET&lt;br /&gt;
* SEEK_CUR&lt;br /&gt;
* SEEK_END&lt;br /&gt;
* stdin&lt;br /&gt;
* stdout&lt;br /&gt;
* stderr&lt;br /&gt;
&lt;br /&gt;
== stat() ==&lt;br /&gt;
== directory() ==&lt;br /&gt;
returns the files in a given directory&lt;br /&gt;
&lt;br /&gt;
e.g. chatter_list = directory( chatter_dir );&lt;br /&gt;
&lt;br /&gt;
Example : &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
var FGRoot = getprop(&amp;quot;/sim/fg-root&amp;quot;);&lt;br /&gt;
var filename = &amp;quot;/Aircraft&amp;quot;;&lt;br /&gt;
var path = FGRoot ~ filename;&lt;br /&gt;
var path_files = directory(FGRoot ~ filename);&lt;br /&gt;
&lt;br /&gt;
foreach(var key; path_files) {&lt;br /&gt;
print(key);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
returns :   &lt;br /&gt;
'''&amp;quot;.&amp;quot; , &amp;quot;..&amp;quot; , &amp;quot;Generic&amp;quot;, &amp;quot;Instruments&amp;quot;, &amp;quot;Instruments-3d&amp;quot;, &amp;quot;c172p&amp;quot;, &amp;quot;ufo&amp;quot;'''&lt;br /&gt;
&lt;br /&gt;
To delete the &amp;quot;.&amp;quot; and &amp;quot;..&amp;quot; see the subvec() function.&lt;br /&gt;
&lt;br /&gt;
== open() ==&lt;br /&gt;
== read() ==&lt;br /&gt;
== readln() ==&lt;br /&gt;
== seek() ==&lt;br /&gt;
== tell() ==&lt;br /&gt;
== write() ==&lt;br /&gt;
== flush() ==&lt;br /&gt;
== close() ==&lt;br /&gt;
&lt;br /&gt;
= utf8 =&lt;br /&gt;
== chstr() ==&lt;br /&gt;
== strc() ==&lt;br /&gt;
== substr() ==&lt;br /&gt;
== size() ==&lt;br /&gt;
== validate() ==&lt;/div&gt;</summary>
		<author><name>Clm76</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Nasal_library_functions&amp;diff=85124</id>
		<title>Nasal library functions</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Nasal_library_functions&amp;diff=85124"/>
		<updated>2015-05-29T06:44:24Z</updated>

		<summary type="html">&lt;p&gt;Clm76: /* ceil() */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Nasal Navigation}}&lt;br /&gt;
&lt;br /&gt;
Please first see: http://plausible.org/nasal/lib.html&lt;br /&gt;
&lt;br /&gt;
== size() ==&lt;br /&gt;
&lt;br /&gt;
== keys() ==&lt;br /&gt;
&lt;br /&gt;
== append() ==&lt;br /&gt;
&lt;br /&gt;
== pop() ==&lt;br /&gt;
&lt;br /&gt;
== setsize() ==&lt;br /&gt;
&lt;br /&gt;
== subvec() ==&lt;br /&gt;
Extract items of a vector (equivalent to the left() function for a string)&lt;br /&gt;
Example :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
var currentPath = &amp;quot;/home/xxx/yyy/&amp;quot;;&lt;br /&gt;
print (directory(currentPath));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
returns : &amp;quot;.&amp;quot;, &amp;quot;..&amp;quot;, &amp;quot;file1&amp;quot;, &amp;quot;file2&amp;quot;, &amp;quot;file3&amp;quot;&lt;br /&gt;
&lt;br /&gt;
and :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
var currentPath = &amp;quot;/home/xxx/yyy/&amp;quot;;&lt;br /&gt;
print (subvec(directory(currentPath),2));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
returns : &amp;quot;file1&amp;quot;, &amp;quot;file2&amp;quot;, &amp;quot;file3&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
var currentPath = &amp;quot;/home/xxx/yyy/&amp;quot;;&lt;br /&gt;
print (subvec(directory(currentPath),3));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
returns : &amp;quot;file2&amp;quot;, &amp;quot;file3&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== delete() ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== int() ==&lt;br /&gt;
&lt;br /&gt;
== num(str) ==&lt;br /&gt;
Convert string to number.&lt;br /&gt;
Returns nil if str is nil, empty string, string with no number.&lt;br /&gt;
&lt;br /&gt;
== streq() ==&lt;br /&gt;
&lt;br /&gt;
== cmp() ==&lt;br /&gt;
&lt;br /&gt;
== substr() ==&lt;br /&gt;
&lt;br /&gt;
== chr() ==&lt;br /&gt;
&lt;br /&gt;
== contains() ==&lt;br /&gt;
&lt;br /&gt;
== typeof() ==&lt;br /&gt;
&lt;br /&gt;
== ghosttype(ghost) ==&lt;br /&gt;
Returns a string containing either a descriptive name as passed to ''naNewGhost'' or ''naNewGhost2'' in C/C++ while creating the ghost or a unique id (aka the pointer to the C/C++ naGhostType instance) if no name has been set.&lt;br /&gt;
&lt;br /&gt;
== compile() ==&lt;br /&gt;
&lt;br /&gt;
== call() ==&lt;br /&gt;
&lt;br /&gt;
== die() ==&lt;br /&gt;
&lt;br /&gt;
== sprintf() ==&lt;br /&gt;
&lt;br /&gt;
== caller() ==&lt;br /&gt;
&lt;br /&gt;
== closure() ==&lt;br /&gt;
&lt;br /&gt;
== find() ==&lt;br /&gt;
&lt;br /&gt;
== split() ==&lt;br /&gt;
&lt;br /&gt;
== rand() ==&lt;br /&gt;
&lt;br /&gt;
== bind() ==&lt;br /&gt;
&lt;br /&gt;
== sort() ==&lt;br /&gt;
&lt;br /&gt;
== id() ==&lt;br /&gt;
&lt;br /&gt;
= math =&lt;br /&gt;
== Trigonometric_functions ==&lt;br /&gt;
'''Caution''': All trigonometric functions works with [https://en.wikipedia.org/wiki/Radian Radian]-values - not with Degree!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;To convert this&amp;lt;/u&amp;gt;:&amp;lt;br&amp;gt;&lt;br /&gt;
radians = degree * math.pi / 180&amp;lt;br&amp;gt;&lt;br /&gt;
degree  = radians * 180 / math.pi&lt;br /&gt;
=== sin() ===&lt;br /&gt;
=== cos() ===&lt;br /&gt;
=== tan() ===&lt;br /&gt;
=== asin() ===&lt;br /&gt;
=== acos() ===&lt;br /&gt;
=== atan2() ===&lt;br /&gt;
== exp() ==&lt;br /&gt;
== ln() ==&lt;br /&gt;
== pow() ==&lt;br /&gt;
== sqrt() ==&lt;br /&gt;
== floor() ==&lt;br /&gt;
Returns the value rounded downward&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
math.floor(1.2); # returns 1&lt;br /&gt;
math.floor(1.6); # returns 1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== ceil() ==&lt;br /&gt;
Returns the value rounded upward&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
math.ceil(1.2); # returns 2&lt;br /&gt;
math.ceil(1.6); # returns 2&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== fmod() ==&lt;br /&gt;
A '''modulo'''-operator, mod(x,y) also works...&amp;lt;br&amp;gt;&lt;br /&gt;
The [https://en.wikipedia.org/wiki/Modulo_operation modulo]-operation finds the remainder of division of one number by another (sometimes called modulus).&lt;br /&gt;
&lt;br /&gt;
== clamp() ==&lt;br /&gt;
== periodic() ==&lt;br /&gt;
== round() ==&lt;br /&gt;
== pi ==&lt;br /&gt;
== e ==&lt;br /&gt;
&lt;br /&gt;
= io =&lt;br /&gt;
&lt;br /&gt;
* SEEK_SET&lt;br /&gt;
* SEEK_CUR&lt;br /&gt;
* SEEK_END&lt;br /&gt;
* stdin&lt;br /&gt;
* stdout&lt;br /&gt;
* stderr&lt;br /&gt;
&lt;br /&gt;
== stat() ==&lt;br /&gt;
== directory() ==&lt;br /&gt;
returns the files in a given directory&lt;br /&gt;
&lt;br /&gt;
e.g. chatter_list = directory( chatter_dir );&lt;br /&gt;
&lt;br /&gt;
Example : &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
var currentPath = &amp;quot;/home/xxx/yyy/&amp;quot;;&lt;br /&gt;
print (directory(currentPath));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
returns :&lt;br /&gt;
:&amp;quot;.&amp;quot;&lt;br /&gt;
:&amp;quot;..&amp;quot;&lt;br /&gt;
:&amp;quot;file1&amp;quot;&lt;br /&gt;
:&amp;quot;file2&amp;quot;&lt;br /&gt;
:&amp;quot;file3&amp;quot;&lt;br /&gt;
&lt;br /&gt;
To delete the &amp;quot;.&amp;quot; and &amp;quot;..&amp;quot; see the subvec() function.&lt;br /&gt;
&lt;br /&gt;
== open() ==&lt;br /&gt;
== read() ==&lt;br /&gt;
== readln() ==&lt;br /&gt;
== seek() ==&lt;br /&gt;
== tell() ==&lt;br /&gt;
== write() ==&lt;br /&gt;
== flush() ==&lt;br /&gt;
== close() ==&lt;br /&gt;
&lt;br /&gt;
= utf8 =&lt;br /&gt;
== chstr() ==&lt;br /&gt;
== strc() ==&lt;br /&gt;
== substr() ==&lt;br /&gt;
== size() ==&lt;br /&gt;
== validate() ==&lt;/div&gt;</summary>
		<author><name>Clm76</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Nasal_library_functions&amp;diff=85123</id>
		<title>Nasal library functions</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Nasal_library_functions&amp;diff=85123"/>
		<updated>2015-05-29T06:43:15Z</updated>

		<summary type="html">&lt;p&gt;Clm76: /* floor() */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Nasal Navigation}}&lt;br /&gt;
&lt;br /&gt;
Please first see: http://plausible.org/nasal/lib.html&lt;br /&gt;
&lt;br /&gt;
== size() ==&lt;br /&gt;
&lt;br /&gt;
== keys() ==&lt;br /&gt;
&lt;br /&gt;
== append() ==&lt;br /&gt;
&lt;br /&gt;
== pop() ==&lt;br /&gt;
&lt;br /&gt;
== setsize() ==&lt;br /&gt;
&lt;br /&gt;
== subvec() ==&lt;br /&gt;
Extract items of a vector (equivalent to the left() function for a string)&lt;br /&gt;
Example :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
var currentPath = &amp;quot;/home/xxx/yyy/&amp;quot;;&lt;br /&gt;
print (directory(currentPath));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
returns : &amp;quot;.&amp;quot;, &amp;quot;..&amp;quot;, &amp;quot;file1&amp;quot;, &amp;quot;file2&amp;quot;, &amp;quot;file3&amp;quot;&lt;br /&gt;
&lt;br /&gt;
and :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
var currentPath = &amp;quot;/home/xxx/yyy/&amp;quot;;&lt;br /&gt;
print (subvec(directory(currentPath),2));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
returns : &amp;quot;file1&amp;quot;, &amp;quot;file2&amp;quot;, &amp;quot;file3&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
var currentPath = &amp;quot;/home/xxx/yyy/&amp;quot;;&lt;br /&gt;
print (subvec(directory(currentPath),3));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
returns : &amp;quot;file2&amp;quot;, &amp;quot;file3&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== delete() ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== int() ==&lt;br /&gt;
&lt;br /&gt;
== num(str) ==&lt;br /&gt;
Convert string to number.&lt;br /&gt;
Returns nil if str is nil, empty string, string with no number.&lt;br /&gt;
&lt;br /&gt;
== streq() ==&lt;br /&gt;
&lt;br /&gt;
== cmp() ==&lt;br /&gt;
&lt;br /&gt;
== substr() ==&lt;br /&gt;
&lt;br /&gt;
== chr() ==&lt;br /&gt;
&lt;br /&gt;
== contains() ==&lt;br /&gt;
&lt;br /&gt;
== typeof() ==&lt;br /&gt;
&lt;br /&gt;
== ghosttype(ghost) ==&lt;br /&gt;
Returns a string containing either a descriptive name as passed to ''naNewGhost'' or ''naNewGhost2'' in C/C++ while creating the ghost or a unique id (aka the pointer to the C/C++ naGhostType instance) if no name has been set.&lt;br /&gt;
&lt;br /&gt;
== compile() ==&lt;br /&gt;
&lt;br /&gt;
== call() ==&lt;br /&gt;
&lt;br /&gt;
== die() ==&lt;br /&gt;
&lt;br /&gt;
== sprintf() ==&lt;br /&gt;
&lt;br /&gt;
== caller() ==&lt;br /&gt;
&lt;br /&gt;
== closure() ==&lt;br /&gt;
&lt;br /&gt;
== find() ==&lt;br /&gt;
&lt;br /&gt;
== split() ==&lt;br /&gt;
&lt;br /&gt;
== rand() ==&lt;br /&gt;
&lt;br /&gt;
== bind() ==&lt;br /&gt;
&lt;br /&gt;
== sort() ==&lt;br /&gt;
&lt;br /&gt;
== id() ==&lt;br /&gt;
&lt;br /&gt;
= math =&lt;br /&gt;
== Trigonometric_functions ==&lt;br /&gt;
'''Caution''': All trigonometric functions works with [https://en.wikipedia.org/wiki/Radian Radian]-values - not with Degree!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;To convert this&amp;lt;/u&amp;gt;:&amp;lt;br&amp;gt;&lt;br /&gt;
radians = degree * math.pi / 180&amp;lt;br&amp;gt;&lt;br /&gt;
degree  = radians * 180 / math.pi&lt;br /&gt;
=== sin() ===&lt;br /&gt;
=== cos() ===&lt;br /&gt;
=== tan() ===&lt;br /&gt;
=== asin() ===&lt;br /&gt;
=== acos() ===&lt;br /&gt;
=== atan2() ===&lt;br /&gt;
== exp() ==&lt;br /&gt;
== ln() ==&lt;br /&gt;
== pow() ==&lt;br /&gt;
== sqrt() ==&lt;br /&gt;
== floor() ==&lt;br /&gt;
Returns the value rounded downward&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
math.floor(1.2); # returns 1&lt;br /&gt;
math.floor(1.6); # returns 1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== ceil() ==&lt;br /&gt;
== fmod() ==&lt;br /&gt;
A '''modulo'''-operator, mod(x,y) also works...&amp;lt;br&amp;gt;&lt;br /&gt;
The [https://en.wikipedia.org/wiki/Modulo_operation modulo]-operation finds the remainder of division of one number by another (sometimes called modulus).&lt;br /&gt;
&lt;br /&gt;
== clamp() ==&lt;br /&gt;
== periodic() ==&lt;br /&gt;
== round() ==&lt;br /&gt;
== pi ==&lt;br /&gt;
== e ==&lt;br /&gt;
&lt;br /&gt;
= io =&lt;br /&gt;
&lt;br /&gt;
* SEEK_SET&lt;br /&gt;
* SEEK_CUR&lt;br /&gt;
* SEEK_END&lt;br /&gt;
* stdin&lt;br /&gt;
* stdout&lt;br /&gt;
* stderr&lt;br /&gt;
&lt;br /&gt;
== stat() ==&lt;br /&gt;
== directory() ==&lt;br /&gt;
returns the files in a given directory&lt;br /&gt;
&lt;br /&gt;
e.g. chatter_list = directory( chatter_dir );&lt;br /&gt;
&lt;br /&gt;
Example : &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
var currentPath = &amp;quot;/home/xxx/yyy/&amp;quot;;&lt;br /&gt;
print (directory(currentPath));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
returns :&lt;br /&gt;
:&amp;quot;.&amp;quot;&lt;br /&gt;
:&amp;quot;..&amp;quot;&lt;br /&gt;
:&amp;quot;file1&amp;quot;&lt;br /&gt;
:&amp;quot;file2&amp;quot;&lt;br /&gt;
:&amp;quot;file3&amp;quot;&lt;br /&gt;
&lt;br /&gt;
To delete the &amp;quot;.&amp;quot; and &amp;quot;..&amp;quot; see the subvec() function.&lt;br /&gt;
&lt;br /&gt;
== open() ==&lt;br /&gt;
== read() ==&lt;br /&gt;
== readln() ==&lt;br /&gt;
== seek() ==&lt;br /&gt;
== tell() ==&lt;br /&gt;
== write() ==&lt;br /&gt;
== flush() ==&lt;br /&gt;
== close() ==&lt;br /&gt;
&lt;br /&gt;
= utf8 =&lt;br /&gt;
== chstr() ==&lt;br /&gt;
== strc() ==&lt;br /&gt;
== substr() ==&lt;br /&gt;
== size() ==&lt;br /&gt;
== validate() ==&lt;/div&gt;</summary>
		<author><name>Clm76</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Nasal_library_functions&amp;diff=85122</id>
		<title>Nasal library functions</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Nasal_library_functions&amp;diff=85122"/>
		<updated>2015-05-29T06:40:35Z</updated>

		<summary type="html">&lt;p&gt;Clm76: /* floor() */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Nasal Navigation}}&lt;br /&gt;
&lt;br /&gt;
Please first see: http://plausible.org/nasal/lib.html&lt;br /&gt;
&lt;br /&gt;
== size() ==&lt;br /&gt;
&lt;br /&gt;
== keys() ==&lt;br /&gt;
&lt;br /&gt;
== append() ==&lt;br /&gt;
&lt;br /&gt;
== pop() ==&lt;br /&gt;
&lt;br /&gt;
== setsize() ==&lt;br /&gt;
&lt;br /&gt;
== subvec() ==&lt;br /&gt;
Extract items of a vector (equivalent to the left() function for a string)&lt;br /&gt;
Example :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
var currentPath = &amp;quot;/home/xxx/yyy/&amp;quot;;&lt;br /&gt;
print (directory(currentPath));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
returns : &amp;quot;.&amp;quot;, &amp;quot;..&amp;quot;, &amp;quot;file1&amp;quot;, &amp;quot;file2&amp;quot;, &amp;quot;file3&amp;quot;&lt;br /&gt;
&lt;br /&gt;
and :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
var currentPath = &amp;quot;/home/xxx/yyy/&amp;quot;;&lt;br /&gt;
print (subvec(directory(currentPath),2));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
returns : &amp;quot;file1&amp;quot;, &amp;quot;file2&amp;quot;, &amp;quot;file3&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
var currentPath = &amp;quot;/home/xxx/yyy/&amp;quot;;&lt;br /&gt;
print (subvec(directory(currentPath),3));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
returns : &amp;quot;file2&amp;quot;, &amp;quot;file3&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== delete() ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== int() ==&lt;br /&gt;
&lt;br /&gt;
== num(str) ==&lt;br /&gt;
Convert string to number.&lt;br /&gt;
Returns nil if str is nil, empty string, string with no number.&lt;br /&gt;
&lt;br /&gt;
== streq() ==&lt;br /&gt;
&lt;br /&gt;
== cmp() ==&lt;br /&gt;
&lt;br /&gt;
== substr() ==&lt;br /&gt;
&lt;br /&gt;
== chr() ==&lt;br /&gt;
&lt;br /&gt;
== contains() ==&lt;br /&gt;
&lt;br /&gt;
== typeof() ==&lt;br /&gt;
&lt;br /&gt;
== ghosttype(ghost) ==&lt;br /&gt;
Returns a string containing either a descriptive name as passed to ''naNewGhost'' or ''naNewGhost2'' in C/C++ while creating the ghost or a unique id (aka the pointer to the C/C++ naGhostType instance) if no name has been set.&lt;br /&gt;
&lt;br /&gt;
== compile() ==&lt;br /&gt;
&lt;br /&gt;
== call() ==&lt;br /&gt;
&lt;br /&gt;
== die() ==&lt;br /&gt;
&lt;br /&gt;
== sprintf() ==&lt;br /&gt;
&lt;br /&gt;
== caller() ==&lt;br /&gt;
&lt;br /&gt;
== closure() ==&lt;br /&gt;
&lt;br /&gt;
== find() ==&lt;br /&gt;
&lt;br /&gt;
== split() ==&lt;br /&gt;
&lt;br /&gt;
== rand() ==&lt;br /&gt;
&lt;br /&gt;
== bind() ==&lt;br /&gt;
&lt;br /&gt;
== sort() ==&lt;br /&gt;
&lt;br /&gt;
== id() ==&lt;br /&gt;
&lt;br /&gt;
= math =&lt;br /&gt;
== Trigonometric_functions ==&lt;br /&gt;
'''Caution''': All trigonometric functions works with [https://en.wikipedia.org/wiki/Radian Radian]-values - not with Degree!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;To convert this&amp;lt;/u&amp;gt;:&amp;lt;br&amp;gt;&lt;br /&gt;
radians = degree * math.pi / 180&amp;lt;br&amp;gt;&lt;br /&gt;
degree  = radians * 180 / math.pi&lt;br /&gt;
=== sin() ===&lt;br /&gt;
=== cos() ===&lt;br /&gt;
=== tan() ===&lt;br /&gt;
=== asin() ===&lt;br /&gt;
=== acos() ===&lt;br /&gt;
=== atan2() ===&lt;br /&gt;
== exp() ==&lt;br /&gt;
== ln() ==&lt;br /&gt;
== pow() ==&lt;br /&gt;
== sqrt() ==&lt;br /&gt;
== floor() ==&lt;br /&gt;
Returns the value rounded downward&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
floor(1.2); # returns 1&lt;br /&gt;
floor(1.6); # returns 1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== ceil() ==&lt;br /&gt;
== fmod() ==&lt;br /&gt;
A '''modulo'''-operator, mod(x,y) also works...&amp;lt;br&amp;gt;&lt;br /&gt;
The [https://en.wikipedia.org/wiki/Modulo_operation modulo]-operation finds the remainder of division of one number by another (sometimes called modulus).&lt;br /&gt;
&lt;br /&gt;
== clamp() ==&lt;br /&gt;
== periodic() ==&lt;br /&gt;
== round() ==&lt;br /&gt;
== pi ==&lt;br /&gt;
== e ==&lt;br /&gt;
&lt;br /&gt;
= io =&lt;br /&gt;
&lt;br /&gt;
* SEEK_SET&lt;br /&gt;
* SEEK_CUR&lt;br /&gt;
* SEEK_END&lt;br /&gt;
* stdin&lt;br /&gt;
* stdout&lt;br /&gt;
* stderr&lt;br /&gt;
&lt;br /&gt;
== stat() ==&lt;br /&gt;
== directory() ==&lt;br /&gt;
returns the files in a given directory&lt;br /&gt;
&lt;br /&gt;
e.g. chatter_list = directory( chatter_dir );&lt;br /&gt;
&lt;br /&gt;
Example : &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
var currentPath = &amp;quot;/home/xxx/yyy/&amp;quot;;&lt;br /&gt;
print (directory(currentPath));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
returns :&lt;br /&gt;
:&amp;quot;.&amp;quot;&lt;br /&gt;
:&amp;quot;..&amp;quot;&lt;br /&gt;
:&amp;quot;file1&amp;quot;&lt;br /&gt;
:&amp;quot;file2&amp;quot;&lt;br /&gt;
:&amp;quot;file3&amp;quot;&lt;br /&gt;
&lt;br /&gt;
To delete the &amp;quot;.&amp;quot; and &amp;quot;..&amp;quot; see the subvec() function.&lt;br /&gt;
&lt;br /&gt;
== open() ==&lt;br /&gt;
== read() ==&lt;br /&gt;
== readln() ==&lt;br /&gt;
== seek() ==&lt;br /&gt;
== tell() ==&lt;br /&gt;
== write() ==&lt;br /&gt;
== flush() ==&lt;br /&gt;
== close() ==&lt;br /&gt;
&lt;br /&gt;
= utf8 =&lt;br /&gt;
== chstr() ==&lt;br /&gt;
== strc() ==&lt;br /&gt;
== substr() ==&lt;br /&gt;
== size() ==&lt;br /&gt;
== validate() ==&lt;/div&gt;</summary>
		<author><name>Clm76</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Nasal_library_functions&amp;diff=85121</id>
		<title>Nasal library functions</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Nasal_library_functions&amp;diff=85121"/>
		<updated>2015-05-29T06:33:28Z</updated>

		<summary type="html">&lt;p&gt;Clm76: /* subvec() */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Nasal Navigation}}&lt;br /&gt;
&lt;br /&gt;
Please first see: http://plausible.org/nasal/lib.html&lt;br /&gt;
&lt;br /&gt;
== size() ==&lt;br /&gt;
&lt;br /&gt;
== keys() ==&lt;br /&gt;
&lt;br /&gt;
== append() ==&lt;br /&gt;
&lt;br /&gt;
== pop() ==&lt;br /&gt;
&lt;br /&gt;
== setsize() ==&lt;br /&gt;
&lt;br /&gt;
== subvec() ==&lt;br /&gt;
Extract items of a vector (equivalent to the left() function for a string)&lt;br /&gt;
Example :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
var currentPath = &amp;quot;/home/xxx/yyy/&amp;quot;;&lt;br /&gt;
print (directory(currentPath));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
returns : &amp;quot;.&amp;quot;, &amp;quot;..&amp;quot;, &amp;quot;file1&amp;quot;, &amp;quot;file2&amp;quot;, &amp;quot;file3&amp;quot;&lt;br /&gt;
&lt;br /&gt;
and :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
var currentPath = &amp;quot;/home/xxx/yyy/&amp;quot;;&lt;br /&gt;
print (subvec(directory(currentPath),2));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
returns : &amp;quot;file1&amp;quot;, &amp;quot;file2&amp;quot;, &amp;quot;file3&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
var currentPath = &amp;quot;/home/xxx/yyy/&amp;quot;;&lt;br /&gt;
print (subvec(directory(currentPath),3));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
returns : &amp;quot;file2&amp;quot;, &amp;quot;file3&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== delete() ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== int() ==&lt;br /&gt;
&lt;br /&gt;
== num(str) ==&lt;br /&gt;
Convert string to number.&lt;br /&gt;
Returns nil if str is nil, empty string, string with no number.&lt;br /&gt;
&lt;br /&gt;
== streq() ==&lt;br /&gt;
&lt;br /&gt;
== cmp() ==&lt;br /&gt;
&lt;br /&gt;
== substr() ==&lt;br /&gt;
&lt;br /&gt;
== chr() ==&lt;br /&gt;
&lt;br /&gt;
== contains() ==&lt;br /&gt;
&lt;br /&gt;
== typeof() ==&lt;br /&gt;
&lt;br /&gt;
== ghosttype(ghost) ==&lt;br /&gt;
Returns a string containing either a descriptive name as passed to ''naNewGhost'' or ''naNewGhost2'' in C/C++ while creating the ghost or a unique id (aka the pointer to the C/C++ naGhostType instance) if no name has been set.&lt;br /&gt;
&lt;br /&gt;
== compile() ==&lt;br /&gt;
&lt;br /&gt;
== call() ==&lt;br /&gt;
&lt;br /&gt;
== die() ==&lt;br /&gt;
&lt;br /&gt;
== sprintf() ==&lt;br /&gt;
&lt;br /&gt;
== caller() ==&lt;br /&gt;
&lt;br /&gt;
== closure() ==&lt;br /&gt;
&lt;br /&gt;
== find() ==&lt;br /&gt;
&lt;br /&gt;
== split() ==&lt;br /&gt;
&lt;br /&gt;
== rand() ==&lt;br /&gt;
&lt;br /&gt;
== bind() ==&lt;br /&gt;
&lt;br /&gt;
== sort() ==&lt;br /&gt;
&lt;br /&gt;
== id() ==&lt;br /&gt;
&lt;br /&gt;
= math =&lt;br /&gt;
== Trigonometric_functions ==&lt;br /&gt;
'''Caution''': All trigonometric functions works with [https://en.wikipedia.org/wiki/Radian Radian]-values - not with Degree!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;To convert this&amp;lt;/u&amp;gt;:&amp;lt;br&amp;gt;&lt;br /&gt;
radians = degree * math.pi / 180&amp;lt;br&amp;gt;&lt;br /&gt;
degree  = radians * 180 / math.pi&lt;br /&gt;
=== sin() ===&lt;br /&gt;
=== cos() ===&lt;br /&gt;
=== tan() ===&lt;br /&gt;
=== asin() ===&lt;br /&gt;
=== acos() ===&lt;br /&gt;
=== atan2() ===&lt;br /&gt;
== exp() ==&lt;br /&gt;
== ln() ==&lt;br /&gt;
== pow() ==&lt;br /&gt;
== sqrt() ==&lt;br /&gt;
== floor() ==&lt;br /&gt;
== ceil() ==&lt;br /&gt;
== fmod() ==&lt;br /&gt;
A '''modulo'''-operator, mod(x,y) also works...&amp;lt;br&amp;gt;&lt;br /&gt;
The [https://en.wikipedia.org/wiki/Modulo_operation modulo]-operation finds the remainder of division of one number by another (sometimes called modulus).&lt;br /&gt;
&lt;br /&gt;
== clamp() ==&lt;br /&gt;
== periodic() ==&lt;br /&gt;
== round() ==&lt;br /&gt;
== pi ==&lt;br /&gt;
== e ==&lt;br /&gt;
&lt;br /&gt;
= io =&lt;br /&gt;
&lt;br /&gt;
* SEEK_SET&lt;br /&gt;
* SEEK_CUR&lt;br /&gt;
* SEEK_END&lt;br /&gt;
* stdin&lt;br /&gt;
* stdout&lt;br /&gt;
* stderr&lt;br /&gt;
&lt;br /&gt;
== stat() ==&lt;br /&gt;
== directory() ==&lt;br /&gt;
returns the files in a given directory&lt;br /&gt;
&lt;br /&gt;
e.g. chatter_list = directory( chatter_dir );&lt;br /&gt;
&lt;br /&gt;
Example : &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
var currentPath = &amp;quot;/home/xxx/yyy/&amp;quot;;&lt;br /&gt;
print (directory(currentPath));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
returns :&lt;br /&gt;
:&amp;quot;.&amp;quot;&lt;br /&gt;
:&amp;quot;..&amp;quot;&lt;br /&gt;
:&amp;quot;file1&amp;quot;&lt;br /&gt;
:&amp;quot;file2&amp;quot;&lt;br /&gt;
:&amp;quot;file3&amp;quot;&lt;br /&gt;
&lt;br /&gt;
To delete the &amp;quot;.&amp;quot; and &amp;quot;..&amp;quot; see the subvec() function.&lt;br /&gt;
&lt;br /&gt;
== open() ==&lt;br /&gt;
== read() ==&lt;br /&gt;
== readln() ==&lt;br /&gt;
== seek() ==&lt;br /&gt;
== tell() ==&lt;br /&gt;
== write() ==&lt;br /&gt;
== flush() ==&lt;br /&gt;
== close() ==&lt;br /&gt;
&lt;br /&gt;
= utf8 =&lt;br /&gt;
== chstr() ==&lt;br /&gt;
== strc() ==&lt;br /&gt;
== substr() ==&lt;br /&gt;
== size() ==&lt;br /&gt;
== validate() ==&lt;/div&gt;</summary>
		<author><name>Clm76</name></author>
	</entry>
	<entry>
		<id>https://wiki.flightgear.org/w/index.php?title=Nasal_library_functions&amp;diff=85120</id>
		<title>Nasal library functions</title>
		<link rel="alternate" type="text/html" href="https://wiki.flightgear.org/w/index.php?title=Nasal_library_functions&amp;diff=85120"/>
		<updated>2015-05-29T06:18:41Z</updated>

		<summary type="html">&lt;p&gt;Clm76: /* directory() */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Nasal Navigation}}&lt;br /&gt;
&lt;br /&gt;
Please first see: http://plausible.org/nasal/lib.html&lt;br /&gt;
&lt;br /&gt;
== size() ==&lt;br /&gt;
&lt;br /&gt;
== keys() ==&lt;br /&gt;
&lt;br /&gt;
== append() ==&lt;br /&gt;
&lt;br /&gt;
== pop() ==&lt;br /&gt;
&lt;br /&gt;
== setsize() ==&lt;br /&gt;
&lt;br /&gt;
== subvec() ==&lt;br /&gt;
&lt;br /&gt;
== delete() ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== int() ==&lt;br /&gt;
&lt;br /&gt;
== num(str) ==&lt;br /&gt;
Convert string to number.&lt;br /&gt;
Returns nil if str is nil, empty string, string with no number.&lt;br /&gt;
&lt;br /&gt;
== streq() ==&lt;br /&gt;
&lt;br /&gt;
== cmp() ==&lt;br /&gt;
&lt;br /&gt;
== substr() ==&lt;br /&gt;
&lt;br /&gt;
== chr() ==&lt;br /&gt;
&lt;br /&gt;
== contains() ==&lt;br /&gt;
&lt;br /&gt;
== typeof() ==&lt;br /&gt;
&lt;br /&gt;
== ghosttype(ghost) ==&lt;br /&gt;
Returns a string containing either a descriptive name as passed to ''naNewGhost'' or ''naNewGhost2'' in C/C++ while creating the ghost or a unique id (aka the pointer to the C/C++ naGhostType instance) if no name has been set.&lt;br /&gt;
&lt;br /&gt;
== compile() ==&lt;br /&gt;
&lt;br /&gt;
== call() ==&lt;br /&gt;
&lt;br /&gt;
== die() ==&lt;br /&gt;
&lt;br /&gt;
== sprintf() ==&lt;br /&gt;
&lt;br /&gt;
== caller() ==&lt;br /&gt;
&lt;br /&gt;
== closure() ==&lt;br /&gt;
&lt;br /&gt;
== find() ==&lt;br /&gt;
&lt;br /&gt;
== split() ==&lt;br /&gt;
&lt;br /&gt;
== rand() ==&lt;br /&gt;
&lt;br /&gt;
== bind() ==&lt;br /&gt;
&lt;br /&gt;
== sort() ==&lt;br /&gt;
&lt;br /&gt;
== id() ==&lt;br /&gt;
&lt;br /&gt;
= math =&lt;br /&gt;
== Trigonometric_functions ==&lt;br /&gt;
'''Caution''': All trigonometric functions works with [https://en.wikipedia.org/wiki/Radian Radian]-values - not with Degree!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;To convert this&amp;lt;/u&amp;gt;:&amp;lt;br&amp;gt;&lt;br /&gt;
radians = degree * math.pi / 180&amp;lt;br&amp;gt;&lt;br /&gt;
degree  = radians * 180 / math.pi&lt;br /&gt;
=== sin() ===&lt;br /&gt;
=== cos() ===&lt;br /&gt;
=== tan() ===&lt;br /&gt;
=== asin() ===&lt;br /&gt;
=== acos() ===&lt;br /&gt;
=== atan2() ===&lt;br /&gt;
== exp() ==&lt;br /&gt;
== ln() ==&lt;br /&gt;
== pow() ==&lt;br /&gt;
== sqrt() ==&lt;br /&gt;
== floor() ==&lt;br /&gt;
== ceil() ==&lt;br /&gt;
== fmod() ==&lt;br /&gt;
A '''modulo'''-operator, mod(x,y) also works...&amp;lt;br&amp;gt;&lt;br /&gt;
The [https://en.wikipedia.org/wiki/Modulo_operation modulo]-operation finds the remainder of division of one number by another (sometimes called modulus).&lt;br /&gt;
&lt;br /&gt;
== clamp() ==&lt;br /&gt;
== periodic() ==&lt;br /&gt;
== round() ==&lt;br /&gt;
== pi ==&lt;br /&gt;
== e ==&lt;br /&gt;
&lt;br /&gt;
= io =&lt;br /&gt;
&lt;br /&gt;
* SEEK_SET&lt;br /&gt;
* SEEK_CUR&lt;br /&gt;
* SEEK_END&lt;br /&gt;
* stdin&lt;br /&gt;
* stdout&lt;br /&gt;
* stderr&lt;br /&gt;
&lt;br /&gt;
== stat() ==&lt;br /&gt;
== directory() ==&lt;br /&gt;
returns the files in a given directory&lt;br /&gt;
&lt;br /&gt;
e.g. chatter_list = directory( chatter_dir );&lt;br /&gt;
&lt;br /&gt;
Example : &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;nasal&amp;quot; enclose=&amp;quot;div&amp;quot;&amp;gt;&lt;br /&gt;
var currentPath = &amp;quot;/home/xxx/yyy/&amp;quot;;&lt;br /&gt;
print (directory(currentPath));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
returns :&lt;br /&gt;
:&amp;quot;.&amp;quot;&lt;br /&gt;
:&amp;quot;..&amp;quot;&lt;br /&gt;
:&amp;quot;file1&amp;quot;&lt;br /&gt;
:&amp;quot;file2&amp;quot;&lt;br /&gt;
:&amp;quot;file3&amp;quot;&lt;br /&gt;
&lt;br /&gt;
To delete the &amp;quot;.&amp;quot; and &amp;quot;..&amp;quot; see the subvec() function.&lt;br /&gt;
&lt;br /&gt;
== open() ==&lt;br /&gt;
== read() ==&lt;br /&gt;
== readln() ==&lt;br /&gt;
== seek() ==&lt;br /&gt;
== tell() ==&lt;br /&gt;
== write() ==&lt;br /&gt;
== flush() ==&lt;br /&gt;
== close() ==&lt;br /&gt;
&lt;br /&gt;
= utf8 =&lt;br /&gt;
== chstr() ==&lt;br /&gt;
== strc() ==&lt;br /&gt;
== substr() ==&lt;br /&gt;
== size() ==&lt;br /&gt;
== validate() ==&lt;/div&gt;</summary>
		<author><name>Clm76</name></author>
	</entry>
</feed>