8.9 KiB
8.9 KiB
Lua Scripting Guide
Mosis apps use Lua for scripting and interactivity. Each app runs in an isolated sandbox with access to Mosis-specific APIs.
Getting Started
Embed Lua directly in your RML files:
<body>
<button onclick="sayHello()">Click Me</button>
<script>
function sayHello()
print("Hello from Lua!")
end
</script>
</body>
Or use external files:
<head>
<script src="scripts/app.lua"/>
</head>
Lua Basics
If you're new to Lua, here's a quick primer:
Variables
-- Local variables (preferred)
local name = "Mosis"
local count = 42
local enabled = true
local items = {"apple", "banana", "cherry"}
-- Global variables (avoid when possible)
globalVar = "accessible everywhere"
Functions
-- Basic function
function greet(name)
return "Hello, " .. name .. "!"
end
-- Function with multiple returns
function getPosition()
return 100, 200
end
local x, y = getPosition()
-- Anonymous functions
local double = function(n) return n * 2 end
Control Flow
-- If statements
if score > 100 then
print("High score!")
elseif score > 50 then
print("Good job!")
else
print("Keep trying!")
end
-- Loops
for i = 1, 10 do
print(i)
end
for index, value in ipairs(items) do
print(index, value)
end
while condition do
-- loop body
end
Tables
-- Array-like table
local colors = {"red", "green", "blue"}
print(colors[1]) -- "red" (Lua is 1-indexed)
-- Dictionary-like table
local user = {
name = "Alice",
age = 25,
premium = true
}
print(user.name)
print(user["age"])
-- Mixed table
local app = {
name = "MyApp",
version = "1.0",
features = {"dark mode", "notifications"}
}
DOM Manipulation
Access and modify UI elements using the document object:
Getting Elements
-- By ID
local button = document:GetElementById("my-button")
-- By tag name
local paragraphs = document:GetElementsByTagName("p")
-- By class name
local cards = document:GetElementsByClassName("card")
Modifying Content
local element = document:GetElementById("message")
-- Set inner content (HTML-like)
element.inner_rml = "<strong>Hello!</strong>"
-- Get inner content
local content = element.inner_rml
-- Set text only (safer, no HTML parsing)
element:SetInnerRML("Plain text here")
Modifying Attributes
local input = document:GetElementById("username")
-- Get attribute
local value = input:GetAttribute("value")
-- Set attribute
input:SetAttribute("placeholder", "Enter username")
-- Remove attribute
input:RemoveAttribute("disabled")
Modifying Styles
local box = document:GetElementById("box")
-- Set individual properties
box.style.width = "200dp"
box.style.backgroundColor = "#00d4ff"
box.style.display = "none" -- hide element
-- Read properties
local width = box.style.width
Classes
local element = document:GetElementById("panel")
-- Add class
element:SetClass("active", true)
-- Remove class
element:SetClass("active", false)
-- Check class
if element:IsClassSet("active") then
print("Panel is active")
end
Event Handling
Inline Events
<button onclick="handleClick()">Click</button>
<input onchange="handleChange(event)"/>
<div onmouseover="handleHover()"/>
Event Listeners
local button = document:GetElementById("my-button")
-- Add listener
button:AddEventListener("click", function(event)
print("Button clicked!")
end)
-- Remove listener (need reference)
local handler = function(event)
print("Clicked")
end
button:AddEventListener("click", handler)
button:RemoveEventListener("click", handler)
Event Object
function handleEvent(event)
-- Event type
print(event.type) -- "click", "change", etc.
-- Target element
local target = event:GetCurrentElement()
-- Mouse position (for mouse events)
local x = event.parameters.mouse_x
local y = event.parameters.mouse_y
-- Stop propagation
event:StopPropagation()
end
Common Events
| Event | Description |
|---|---|
click |
Element clicked |
dblclick |
Element double-clicked |
mousedown |
Mouse button pressed |
mouseup |
Mouse button released |
mouseover |
Mouse enters element |
mouseout |
Mouse leaves element |
focus |
Element gains focus |
blur |
Element loses focus |
change |
Input value changed |
submit |
Form submitted |
keydown |
Key pressed |
keyup |
Key released |
Timers
setTimeout
-- Execute once after delay
local timerId = setTimeout(function()
print("Executed after 1 second")
end, 1000) -- milliseconds
-- Cancel timer
clearTimeout(timerId)
setInterval
-- Execute repeatedly
local intervalId = setInterval(function()
print("Tick")
end, 1000)
-- Cancel interval
clearInterval(intervalId)
Storage
Persist data between app sessions:
-- Save data
storage.set("username", "Alice")
storage.set("settings", {
darkMode = true,
notifications = false
})
-- Load data
local username = storage.get("username")
local settings = storage.get("settings")
-- Delete data
storage.remove("username")
-- Clear all data
storage.clear()
Navigation
Navigate between screens in your app:
-- Navigate to screen
navigateTo("settings") -- loads assets/settings.rml
-- Go back
goBack()
-- Go to home screen
goHome()
-- Replace current screen (no back)
replaceTo("login")
Navigation Events
-- Listen for navigation
onNavigate(function(screenName)
print("Navigated to: " .. screenName)
end)
-- Listen for back
onBack(function()
print("Going back")
end)
HTTP Requests
Make network requests (requires network permission):
-- GET request
http.get("https://api.example.com/data", function(response)
if response.ok then
local data = json.decode(response.body)
print(data.message)
else
print("Error: " .. response.status)
end
end)
-- POST request
http.post("https://api.example.com/submit", {
headers = {
["Content-Type"] = "application/json"
},
body = json.encode({
name = "Alice",
action = "subscribe"
})
}, function(response)
print("Status: " .. response.status)
end)
JSON
-- Parse JSON string
local data = json.decode('{"name": "Alice", "age": 25}')
print(data.name)
-- Convert to JSON string
local str = json.encode({
items = {"a", "b", "c"},
count = 3
})
Date and Time
-- Current timestamp
local now = os.time()
-- Format date
local formatted = os.date("%Y-%m-%d %H:%M:%S", now)
-- Parse date components
local t = os.date("*t", now)
print(t.year, t.month, t.day, t.hour, t.min, t.sec)
Utilities
String Functions
-- Concatenation
local greeting = "Hello, " .. name .. "!"
-- String functions
string.upper("hello") -- "HELLO"
string.lower("HELLO") -- "hello"
string.sub("hello", 1, 3) -- "hel"
string.find("hello", "ll") -- 3
string.gsub("hello", "l", "L") -- "heLLo"
string.format("Score: %d", 100) -- "Score: 100"
Math Functions
math.floor(3.7) -- 3
math.ceil(3.2) -- 4
math.round(3.5) -- 4
math.abs(-5) -- 5
math.min(1, 2, 3) -- 1
math.max(1, 2, 3) -- 3
math.random() -- 0-1
math.random(1, 6) -- 1-6
Table Functions
-- Insert
table.insert(items, "new item")
table.insert(items, 1, "at beginning")
-- Remove
table.remove(items) -- remove last
table.remove(items, 1) -- remove first
-- Sort
table.sort(items)
table.sort(items, function(a, b) return a > b end) -- descending
-- Length
local count = #items
Sandbox Restrictions
For security, these are NOT available:
os.execute,io.popen- No shell commandsloadfile,dofile- No arbitrary file loadingrequire- No external modules (useimportfor app modules)debuglibrary - No debugging hooksrawget,rawset- No metatable bypass
Best Practices
- Use local variables - Faster and prevents pollution
- Handle errors - Use
pcallfor operations that might fail - Clean up timers - Clear intervals when navigating away
- Minimize DOM queries - Cache element references
- Batch updates - Group style changes together
Error Handling
local success, result = pcall(function()
-- Code that might fail
local data = json.decode(invalidJson)
return data
end)
if success then
print("Parsed:", result)
else
print("Error:", result)
end
Module Pattern
-- utils.lua
local Utils = {}
function Utils.formatCurrency(amount)
return string.format("$%.2f", amount)
end
function Utils.capitalize(str)
return str:sub(1,1):upper() .. str:sub(2)
end
return Utils
-- main.lua
local Utils = import("utils")
print(Utils.formatCurrency(19.99))
Next Steps
- Permissions Guide - Request device capabilities
- API Reference - Complete API documentation
- Debugging Guide - Debug your Lua code