By request, here is a quick way to call external JavaScript functions and pass AppGameKit data to them, after exporting your app as HTML5. This is useful when you need to modify standard HTML elements with data retrieved from AppGameKit, or use advanced JavaScript functionality with custom data delivered straight from AGK.
For instance, you can update a standard HTML heading tag to reflect which level the user is currently on or build a more sophisticated UI using standard HTML to surround your AppGameKit app. No HTTP requests or server-side coding is needed for this and it works in Tier 1.
Ideally this will be offered as a native feature to AppGameKit in a future version, as well as the reverse to easily send JavaScript data back to AGK. For now the method I describe here will suffice. This guide only covers sending data from AppGameKit to JavaScript. There are several approaches to doing the opposite but it is generally easiest to use async HTTP calls for that purpose and that's beyond the scope of this thread.
Summary: We will modify the
Message() command so that we can optionally use it to call a custom JavaScript function and pass JSON-formatted AppGameKit data to it for further parsing in JavaScript. The command will still work in its usual way if you are simply passing a string message to it.
Part 1 - AppGameKit Source Code
1.) In your AppGameKit source code, construct a UDT for storing the data that you wish to eventually pass through to JavaScript. The only requirement is that it include a "
func_name" string property, which is used to tell the browser which specific JavaScript function you wish to call. You can add as many other properties as you wish for organizing the data you wish to send. The passed data can be strings, integers or floats.
For compatibility with how AppGameKit converts the UDTs to JSON and how it is read and referenced in JavaScript, use all small letters for the property names.
// Data container for the Javascript calls
Type JSData_obj
func_name As String // The name of the JavaScript function to call
str_data As String[4] // Generic string data
int_data As Integer[4] // Generic int data
float_data As Float[4] // Generic float data
EndType
Global JSData as JSData_obj
Above, I have defined a simple UDT that can store up to five strings, integers and floats along with the desired JavaScript function name.
2.) When you want to call a JavaScript function from within your code, set the properties of the previously defined UDT as desired:
// Make Javascript Call code
JSData.func_name = "UpdateHeader"
JSData.str_data[0] = userName
CallJavascript(JSData.toJSON())
In this example, the target JavaScript function to be executed is named "UpdateHeader" and it will send the AGK-derived username from AppGameKit to JavaScript/HTML. It also depends on a
CallJavascript(data) function as defined below, which stringifies the UDT and passes it along with a special tag to alert the JavaScript that it is requesting to execute an outside JavaScript function.
// FUNCTION: Call JavaScript
Function CallJavascript(data As String)
// Make sure the app is being run in a browser
If(GetDeviceName() = "html5")
// Send special JS message with data
Message("|JS|" + data)
Endif
EndFunction
The full AppGameKit source code for this example is below. It includes two simple JavaScript call tests. One test will update an HTML header with the AGK-entered username, and another will update the title bar (e.g., tab bar text) to indicate how many times the user has clicked within the AppGameKit app.
// Project: Call Javascript Example
// Created: 2017-12-29 (xCept)
// show all errors
SetErrorMode(2)
// set window properties
SetWindowTitle( "Call Javascript" )
SetWindowSize( 1024, 768, 0 )
SetWindowAllowResize( 1 ) // allow the user to resize the window
// set display properties
SetVirtualResolution( 1024, 768 ) // doesn't have to match the window
SetOrientationAllowed( 1, 1, 1, 1 ) // allow both portrait and landscape on mobile devices
SetSyncRate( 30, 0 ) // 30fps instead of 60 to save battery
SetScissor( 0,0,0,0 ) // use the maximum available screen space, no black borders
UseNewDefaultFonts( 1 ) // since version 2.0.22 we can use nicer default fonts
SetPrintSize(20)
// Data container for the Javascript calls
Type JSData_obj
func_name As String // The name of the JavaScript function to call
str_data As String[4] // Generic string data
int_data As Integer[4] // Generic int data
float_data As Float[4] // Generic float data
EndType
Global JSData as JSData_obj
// AGK Variables
Global clickCount As Integer : clickCount = 0
Global userName As String : userName = "User"
// Button to change name
AddVirtualButton(1, GetVirtualWidth() * 0.5, GetVirtualHeight() * 0.20, 128)
SetVirtualButtonText(1, "Name")
// Main Loop
Do
// Handle Interactions
HandleUsername()
HandleClicks()
// Display Variable Values
Print("User: " + userName)
Print("Total Clicks: " + Str(clickCount))
Sync()
Loop
// FUNCTION: Handle Username
Function HandleUsername()
// If Button Pressed
If GetVirtualButtonPressed(1)
// Begin accepting text input in textfield
StartTextInput()
Endif
// Text input is currently active
If GetTextInputState() = 1
// User has finished entering a name
If GetTextInputCompleted() = 1
// Store new name in AGK variable
userName = GetTextInput()
// Make Javascript Call code
JSData.func_name = "UpdateHeader"
JSData.str_data[0] = userName
CallJavascript(JSData.toJSON())
Endif
Endif
EndFunction
// FUNCTION: Handle Clicks
Function HandleClicks()
// Check if the pointer has been clicked/tapped
If GetPointerReleased()
// Update the click counter
Inc clickCount, 1
// Make Javascript Call code
JSData.func_name = "UpdateTitleBar"
JSData.int_data[0] = clickCount
CallJavascript(JSData.toJSON())
Endif
EndFunction
// FUNCTION: Call JavaScript
Function CallJavascript(data As String)
// Make sure the app is being run in a browser
If(GetDeviceName() = "html5")
// Send special JS message with data
Message("|JS|" + data)
Endif
EndFunction
Part 2 - JavaScript Source Code
Now that the AppGameKit code is done, we can export it as HTML5.
1.) Select
File > Export Project > As HTML5. Enter an Output Folder that you can easily access and transfer to a Web server and then click Export. In my case, I output it directly to a local server path (e.g., XAMPP) so that it is immediately accessible from my browser without needing to copy/paste or upload it elsewhere. You cannot open the generated HTML files directly from My Computer / Finder or you'll encounter security and script errors, it must be accessed from a local or remote server from your browser.
2.) Make a copy of the default generated .html file and rename the copy to
index.html. This will allow you to access the app without needing to specify the filename on the server and, more importantly, will prevent AppGameKit from overwriting it each time you export the AppGameKit project.
3.) Create a blank file in the same HTML5 output directory named
scripts.js. We will use this file to add our custom JavaScript code.
4.) Open the
index.html file in a text editor. Right above the closing
</head> tag, add a reference to the scripts file by including the line below:
<script type="text/javascript" src="scripts.js"></script>
5.) Now as an example, let's add a header to the HTML page so that it can be modified from AGK. Insert the following code directly below the
<body> tag. Then save the file.
<h1 style="text-align: center; color: #fff; text-transform: capitalize;">Welcome, <span id="userName">User</span></h1>
<hr />
6.) Open up the
scripts.js file in a text editor. Then add the following functions:
// FUNCTION: Override default alert functionality to check for AGK JavaScript requests.
(function(p) {
window.alert = function() {
sData = arguments[0];
if (sData.indexOf("|JS|") == 0) {
sData = sData.substr(4);
escapedStr = sData.replace(/"/g, '\\"').replace(/\n/g, '');
eval(JSON.parse(sData).func_name + "('" + escapedStr + "');");
} else {
return p.apply(this, arguments);
}
};
})(window.alert);
// FUNCTION: Update Title Bar with AGK Data
function UpdateTitleBar(data) {
// Convert data to a JavaScript object
data = JSON.parse(data);
// Update title bar (tab titles) with user and click count
document.title = "Score: " + data.int_data[0];
}
// FUNCTION: Update Header with AGK Data
function UpdateHeader(data) {
// Convert data to a JavaScript object
data = JSON.parse(data);
document.getElementById("userName").textContent = data.str_data[0];
}
The first function is the important one, and the other two are for the two tests in our AppGameKit project. The top function overrides (proxy prototype) the default behavior of the JavaScript alert function. It will check for the special |JS| attribute and, if found, will parse the data and pass it to the requested JavaScript function. Otherwise it is treated as a normal alert box.
It is important that the names of the other functions exactly match the func_name data in AGK. If everything has been done correctly, you should now be able to update your name in AppGameKit and it will be reflected in the standard HTML header. You can also click in blank areas in the AppGameKit web view and the title bar will be updated to reflect the total.
A working example of this project is online at: http://mjpsites.com/agk/CallJavascript/
The full project source is attached to this post.