Created
January 12, 2022 22:25
-
-
Save jeanbenitez/e3775db60ed65867be76cbe5cff8ef2b to your computer and use it in GitHub Desktop.
Modified version of Roku RALE's TrackerTask file to allow Video node inspection
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?rokuml version="1.0" encoding="utf-8" ?> | |
<!--********** Copyright 2019 Roku Corp. All Rights Reserved. **********--> | |
<component name="TrackerTask" extends="Task"> | |
<interface> | |
<field id="event" type="assocarray"/> | |
<field id="isExternallyExposed" type="boolean" value="true"/> | |
<function name="UIThread_init"/> | |
<function name="UIThread_showSelectorView"/> | |
<function name="UIThread_hideSelectorView"/> | |
<function name="UIThread_selectNode"/> | |
<function name="UIThread_updateNode"/> | |
<function name="UIThread_getNodeData"/> | |
<function name="UIThread_getItemList"/> | |
<function name="UIThread_getNodeTree"/> | |
<function name="UIThread_getNodeById"/> | |
<function name="UIThread_getNodeByName"/> | |
<function name="UIThread_addChild"/> | |
<function name="UIThread_removeChild"/> | |
<function name="UIThread_moveChild"/> | |
<function name="UIThread_setField"/> | |
<function name="UIThread_removeField"/> | |
<function name="UIThread_setFocus"/> | |
<function name="UIThread_selectFocusedNode"/> | |
<function name="UIThread_setBoundingRect"/> | |
<function name="UIThread_getRegistrySections"/> | |
<function name="UIThread_clearRegistry"/> | |
<function name="UIThread_addRegistrySection"/> | |
<function name="UIThread_removeRegistrySection"/> | |
<function name="UIThread_addRegistryField"/> | |
<function name="UIThread_removeRegistryField"/> | |
<function name="UIThread_editRegistryField"/> | |
<function name="UIThread_log"/> | |
<function name="UIThread_setLogVerbosity"/> | |
<function name="UIThread_setLogFormat"/> | |
<function name="UIThread_setRulerLine"/> | |
<function name="UIThread_setRulerLines"/> | |
<function name="UIThread_removeRulerLine"/> | |
<function name="UIThread_removeAllRulerLines"/> | |
</interface> | |
<script type = "text/brightscript" > | |
<![CDATA[ | |
' RALE_Config - RALE config singleton class | |
' @return {object} - RALE config properties | |
Function RALE_Config() as Object | |
if m.appConfig = Invalid then m.appConfig = RALE_InitConfig() | |
return m.appConfig | |
End Function | |
' RALE_InitConfig - RALE config init function. Defines all config properties | |
' @return {object} - RALE config properties | |
Function RALE_InitConfig() as Object | |
this = { | |
bufferSize: 1024, | |
minSocketPort: 49152, | |
maxSocketPort: 65535, | |
screensaverResetTimeout: 30000, | |
commandTimeout: 10000, | |
defaultLogLevel: 3, | |
logFormat: "[RALE][%level%] - %message%" | |
} | |
return this | |
End Function | |
' CreateGuides - Selector (live) view class constructor | |
' @return {object} - selector (live) view | |
Function CreateGuides() as Object | |
this = {} | |
node = CreateObject("roSGNode", "Group") | |
node.addFields({"isExternallyExposed": true}) | |
this.node = node | |
this.childMap = {} | |
this.setLine = Guides_SetLine | |
this.setRulerLines = Guides_SetRulerLines | |
this.removeLine = Guides_RemoveLine | |
this.removeAllLines = Guides_RemoveAllLines | |
this.resolution = m.top.getScene().getField("currentDesignResolution") | |
return this | |
End Function | |
Sub Guides_SetLine(id, position, coords) | |
if (m.childMap[id] = invalid) then | |
Guides_AddLine(id, position, coords, m.node, m.childMap) | |
else | |
Guides_UpdatePosition(id, coords, m.childMap) | |
end if | |
end Sub | |
Sub Guides_SetRulerLines(rulerLines) | |
For Each line In rulerLines.Items() | |
Guides_AddLine(line.key, line.value.position, line.value.coords, m.node, m.childMap) | |
End For | |
end Sub | |
Sub Guides_AddLine(id, position, coords, node, childMap) as Object | |
line = CreateObject("roSGNode", "Rectangle") | |
line.setField("id", id) | |
line.setField("color", "#00F8FF") | |
line.addFields({"position": position}) | |
Guides_SetLineCoords(line, coords) | |
node.appendChild(line) | |
childMap[id] = line | |
end Sub | |
Sub Guides_UpdatePosition(id, coords, childMap) as Object | |
line = childMap[id] | |
Guides_SetLineCoords(line, coords) | |
end Sub | |
Sub Guides_SetLineCoords(node, coords) as Integer | |
if node.position = "horizontal" then | |
node.setField("translation", [coords.x,0]) | |
node.setField("width", 1) | |
node.setField("height", m.resolution.height) | |
end if | |
if node.position = "vertical" then | |
node.setField("translation", [0,coords.y]) | |
node.setField("width", m.resolution.width) | |
node.setField("height", 1) | |
end if | |
end Sub | |
Sub Guides_RemoveLine(id) | |
line = m.childMap[id] | |
m.node.removeChild(line) | |
m.childMap[id] = invalid | |
end Sub | |
Sub Guides_RemoveAllLines() | |
childrenList = m.node.getChildren(-1,0) | |
m.node.removeChildren(childrenList) | |
m.childMap = {} | |
end Sub | |
' RALE_Logger - Constructor of Logger class | |
' @param {integer} level - log verbosity level (0-4) | |
' @param {string} format - log message format ("[MY_CUSTOM_PREFIX]%message%") | |
' @return {object} - logger instance | |
function RALE_Logger(level = -1 as Integer, format = "%message%" as String) as Object | |
this = {} | |
this.verbosityList = ["OFF", "ERROR", "WARN", "INFO", "DEBUG"] | |
this.verbosityMap = { | |
off : 0 | |
error : 1 | |
warning : 2 | |
info : 3 | |
debug : 4 | |
} | |
this.formatMethods = { | |
date: "getDate", | |
time: "getTime", | |
level: "getLevel", | |
message: "getMessage" | |
} | |
this.dt = createObject("roDateTime") | |
this.setVerbosity = RALE_Logger__SetVerbosityLevel | |
this.setFormat = RALE_Logger__SetFormat | |
this.getDate = RALE_Logger__GetDate | |
this.getTime = RALE_Logger__GetTime | |
this.getLevel = RALE_Logger__GetLevel | |
this.getMessage = RALE_Logger__GetMessage | |
this.debug = RALE_Logger__DebugLog | |
this.info = RALE_Logger__InfoLog | |
this.warning = RALE_Logger__WarningLog | |
this.error = RALE_Logger__ErrorLog | |
this.logMessage = RALE_Logger__LogMessage | |
this.setVerbosity(level) | |
this.setFormat(format) | |
return this | |
end function | |
' RALE_Logger__SetVerbosityLevel - change logger verbosity level | |
' @param {integer} level - log verbosity level (0-4) | |
sub RALE_Logger__SetVerbosityLevel(level = 4 as Integer) | |
if level > m.verbosityMap.debug then | |
m.verbosityLevel = m.verbosityMap.debug | |
else if level < m.verbosityMap.off then | |
m.verbosityLevel = m.verbosityMap.off | |
else | |
m.verbosityLevel = level | |
end if | |
end sub | |
' RALE_Logger__SetFormat - Change logger message format | |
' @param {string} format - log message format ("[MY_CUSTOM_PREFIX]%message%") | |
' Available format variables: | |
' %message% - message with which the log function is called (at the end by default) | |
' %date% - current date in format "[year]-[month]-[day]" | |
' %time% - current time in format "[hours]:[minutes]:[seconds].[miliseconds]" | |
' %level% - verbosity level ("ERROR", "WARN", "INFO" or "DEBUG") | |
sub RALE_Logger__SetFormat(format = "%message%" as String) | |
if Instr(1, format, "%message%") = 0 then | |
format = format + "%message%" | |
end if | |
m.formatList = format.split("%") | |
end sub | |
'========================================== | |
' Wrappers over various levels of logging | |
'========================================== | |
' RALE_Logger__DebugLog - Log message into console with DEBUG verbosity level | |
' @param {string} message - log message | |
sub RALE_Logger__DebugLog(message = "") | |
m.logMessage(message, m.verbosityMap.debug) | |
end sub | |
' RALE_Logger__InfoLog - Log message into console with INFO verbosity level | |
' @param {string} message - log message | |
sub RALE_Logger__InfoLog(message = "") | |
m.logMessage(message, m.verbosityMap.info) | |
end sub | |
' RALE_Logger__WarningLog - Log message into console with WARNING verbosity level | |
' @param {string} message - log message | |
sub RALE_Logger__WarningLog(message = "") | |
m.logMessage(message, m.verbosityMap.warning) | |
end sub | |
' RALE_Logger__ErrorLog - Log message into console with ERROR verbosity level | |
' @param {string} message - log message | |
sub RALE_Logger__ErrorLog(message) | |
m.logMessage(message, m.verbosityMap.error) | |
end sub | |
'========================================== | |
' Main log function | |
'========================================== | |
' RALE_Logger__LogMessage - Log message into console by message format if logLevel is more than current verbosityLevel | |
' @param {string} message - log message | |
' @param {integer} logLevel - log verbosity level (0-4) | |
sub RALE_Logger__LogMessage(message as String, logLevel as Integer) | |
if logLevel > 0 and m.verbosityLevel >= logLevel then | |
logMessage = "" | |
for each item in m.formatList | |
formatMethod = m.formatMethods[item] | |
if formatMethod = Invalid then | |
logMessage = logMessage + item | |
else | |
logMessage = logMessage + m[formatMethod](message, logLevel) | |
end If | |
end for | |
print logMessage | |
end if | |
end sub | |
'========================================== | |
' Utitlity functions | |
'========================================== | |
' RALE_Logger__GetTime - Returns current time | |
' @param {string} message - log message | |
' @return {string} - current time in format: "[hours]:[minutes]:[seconds].[miliseconds]" | |
function RALE_Logger__GetTime(message as String, level as Integer) | |
m.dt.Mark() | |
h = RALE_Logger__getZeroNumber(m.dt.getHours()) | |
n = RALE_Logger__getZeroNumber(m.dt.getMinutes()) | |
s = RALE_Logger__getZeroNumber(m.dt.getSeconds()) | |
ms = str(m.dt.getMilliseconds()).trim() | |
if len(ms) = 1 | |
ms = ms + "0" | |
else | |
ms = left(ms, 2) | |
end if | |
return h + ":" + n + ":" + s + "." + ms | |
end function | |
' RALE_Logger__GetDate - Returns current date | |
' @param {string} message - log message | |
' @return {string} - current date in format: "[year]-[month]-[day]" | |
function RALE_Logger__GetDate(message as String, level as Integer) | |
m.dt.Mark() | |
y = str(m.dt.getYear()).trim() | |
mm = RALE_Logger__getZeroNumber(m.dt.getMonth()) | |
d = RALE_Logger__getZeroNumber(m.dt.getDayOfMonth()) | |
return y + "-" + mm + "-" + d | |
end function | |
' RALE_Logger__GetLevel - Returns current verbosity level | |
' @param {string} message - log message | |
' @return {integer} - verbosity level | |
function RALE_Logger__GetLevel(message as String, level as Integer) | |
return m.verbosityList[level] | |
end function | |
' RALE_Logger__GetMessage - Returns log message (needed for formating log message) | |
' @param {string} message - log message | |
' @return {string} - log message | |
function RALE_Logger__GetMessage(message as String, level as Integer) | |
return message | |
end function | |
' RALE_Logger__getZeroNumber - Convert number to format "0[number]" if it has less then 2 digits | |
' @param {number} n - number for converting | |
' @param {number} to_length - the necessary count of digits | |
' @return {string} - converted number format | |
function RALE_Logger__getZeroNumber(n, to_length=2) | |
n = str(n).trim() | |
if len(n) < to_length | |
n = String(to_length - 1, "0") + n | |
end if | |
return n | |
end function | |
' CreateSelectorView - Selector (live) view class constructor | |
' @return {object} - selector (live) view | |
Function CreateSelectorView() as Object | |
view = CreateObject("roSGNode", "Group") | |
m.resolution = m.top.getScene().getField("currentDesignResolution") | |
m.resWidth = m.resolution.width | |
m.resHeight = m.resolution.height | |
children = [ | |
{subtype : "Rectangle", fields : {id : "l"}} | |
{subtype : "Rectangle", fields : {id : "r"}} | |
{subtype : "Rectangle", fields : {id : "b"}} | |
{subtype : "Rectangle", fields : {id : "t"}} | |
{subtype : "Rectangle", fields : {id : "horiz"}} | |
{subtype : "Rectangle", fields : {id : "vert"}} | |
{subtype : "Rectangle", fields : {id : "leftLine"}} | |
{subtype : "Rectangle", fields : {id : "topLine"}} | |
{subtype : "Rectangle", fields : {id : "rightLine"}} | |
{subtype : "Rectangle", fields : {id : "bottomLine"}} | |
{subtype : "Label", fields : {id : "leftCords"}} | |
{subtype : "Label", fields : {id : "topCords"}} | |
{subtype : "Label", fields : {id : "rightCords"}} | |
{subtype : "Label", fields : {id : "bottomCords"}} | |
{subtype : "Rectangle", fields : {id : "fill", color : "#ffffff44"}} | |
{subtype : "Timer", fields : {id : "timer", duration : 1 / 60, repeat : true}} | |
] | |
viewObject = NodeUtils_AddChildrenToNode(view, children) | |
childrenMap = {} | |
for each child in viewObject.children | |
node = child.node | |
childrenMap[node.id] = node | |
end for | |
interface = [ | |
{id:"width", type:"float", value : "1280", onChange : "SelectorView_UpdateView"} | |
{id:"height", type:"float", value : "40", onChange : "SelectorView_UpdateView"} | |
{id:"border", type:"float", value : "2", onChange : "SelectorView_UpdateView"} | |
{id:"oldBoundingRect", type:"assocarray" } | |
{id:"boundingRect", type:"assocarray", onChange : "SelectorView_UpdateViewFromBRect"} | |
{id:"color", type:"color", value : "#FFFFFF", onChange : "SelectorView_UpdateColors"} | |
{id:"attachedView", type:"node", onChange : "SelectorView_AttachToView"} | |
{id:"isExternallyExposed", type:"boolean", value : "true"} | |
{id:"childrenMap", type:"assocarray", value : childrenMap} | |
] | |
NodeUtils_AddInterfaceToNode(view, interface) | |
timer = view.childrenMap.timer | |
timer.observeField("fire","SelectorView_UpdateViewPosition") | |
timer.control = "start" | |
return view | |
End Function | |
' NodeUtils_AddInterfaceToNode - Adds interface to node | |
' @param {object} node - node | |
' @param {object} interface - interface | |
' @return {boolean} - is interface was added | |
Function NodeUtils_AddInterfaceToNode(node, interface as Object) as Boolean | |
if node = invalid OR interface = invalid then return false | |
for each field in interface | |
NodeUtils_AddNodeInterfaceField(node, field) | |
end for | |
return true | |
End Function | |
' NodeUtils_AddNodeInterfaceField - Adds interface field to node | |
' @param {object} node - node | |
' @param {object} fieldConfig - field config | |
' @return {boolean} - is interface was added | |
Function NodeUtils_AddNodeInterfaceField(node, fieldConfig as Object) as Boolean | |
if node = invalid OR fieldConfig = invalid then return false | |
if fieldConfig.id = invalid then return false | |
if fieldConfig.type = invalid then return false | |
if fieldConfig.alwaysNotify = invalid then fieldConfig.alwaysNotify = true | |
if node.hasField(fieldConfig.id) then return false | |
node.addField(fieldConfig.id, fieldConfig.type, fieldConfig.alwaysNotify) | |
if not node.hasField(fieldConfig.id) then return false | |
if fieldConfig.value <> invalid then | |
node[fieldConfig.id] = fieldConfig.value | |
end if | |
if fieldConfig.onChange <> invalid then | |
node.observeField(fieldConfig.id, fieldConfig.onChange) | |
end if | |
return true | |
End Function | |
' NodeUtils_AddChildrenToNode - Adds children to node | |
' @param {object} node - node | |
' @param {object} childrenConfig - children config | |
' @return {object} - view object | |
Function NodeUtils_AddChildrenToNode(node, childrenConfig as Object) as Object | |
if node = invalid then return invalid | |
viewObject = {node : node, children : []} | |
if childrenConfig <> invalid then | |
for each childConfig in childrenConfig | |
childObject = NodeUtils_AddChildToNode(node, childConfig) | |
viewObject.children.push(childObject) | |
end for | |
end if | |
return viewObject | |
End Function | |
' NodeUtils_AddChildToNode - Adds child to node | |
' @param {object} node - node | |
' @param {object} childConfig - child config | |
' @return {object} - view object | |
Function NodeUtils_AddChildToNode(node, childConfig) as Object | |
if node = invalid or childConfig = invalid then return invalid | |
if childConfig.subtype = invalid then return invalid | |
view = node.CreateChild(childConfig.subtype) | |
if view = invalid then return invalid | |
if childConfig.fields <> invalid then view.setFields(childConfig.fields) | |
return NodeUtils_AddChildrenToNode(view, childConfig.children) | |
End Function | |
' NumberToFixed - formats a number using fixed-point notation | |
' @param {float, double} number - formated number | |
' @param {integer} digits - the number of digits to appear after the decimal point | |
' @return {string} - a string representing the given number using fixed-point notation | |
Function NumberToFixed(number, digits) as String | |
str = number.toStr() | |
arr = str.split(".") | |
if arr.count() > 1 and digits > 0 then | |
return arr[0] + "." + Left(arr[1], digits) | |
end if | |
return arr[0] | |
End Function | |
' SelectorView_UpdateView - width, height and border change event handler. Updates Selector (live) view | |
' @param {object} event - event data | |
Sub SelectorView_UpdateView(event) | |
node = event.getRoSGNode() | |
border = node.border | |
width = node.width | |
height = node.height | |
x = node.boundingRect.x | |
y = node.boundingRect.y | |
rightPos = x + width + border*2 | |
bottomPos = y + height + border*2 | |
l = node.childrenMap.l | |
l.width = border | |
l.height = height + border*2 | |
l.translation = [-border, -border] | |
r = node.childrenMap.r | |
r.width = border | |
r.height = height + border*2 | |
r.translation = [width, -border] | |
t = node.childrenMap.t | |
t.width = width + border*2 | |
t.height = border | |
t.translation = [-border, -border] | |
b = node.childrenMap.b | |
b.width = width + border*2 | |
b.height = border | |
b.translation = [-border, height] | |
horiz = node.childrenMap.horiz | |
horiz.width = width | |
horiz.height = border | |
horiz.translation = [0, (height - border) / 2] | |
vert = node.childrenMap.vert | |
vert.width = border | |
vert.height = height | |
vert.translation = [(width - border) / 2, 0] | |
leftLine = node.childrenMap.leftLine | |
leftLine.width = x | |
leftLine.height = border | |
leftLine.translation = [-x, (height - border) / 2] | |
topLine = node.childrenMap.topLine | |
topLine.width = border | |
topLine.height = y | |
topLine.translation = [(width - border) / 2, -y] | |
rightLine = node.childrenMap.rightLine | |
rightLine.width = m.resWidth - x + 10 | |
rightLine.height = border | |
rightLine.translation = [width + border, (height - border) / 2] | |
bottomLine = node.childrenMap.bottomLine | |
bottomLine.width = border | |
bottomLine.height = m.resHeight - y + 10 | |
bottomLine.translation = [(width - border) / 2, height + border] | |
leftCords = node.childrenMap.leftCords | |
leftCords.font.size = 20 | |
leftCords.color = "#FF0000" | |
leftCords.text = NumberToFixed(x, 0) | |
leftCords.translation = [-x/2 - 10, (height - border) / 2 - 20] | |
topCords = node.childrenMap.topCords | |
topCords.font.size = 20 | |
topCords.color = "#FF0000" | |
topCords.text = NumberToFixed(y, 0) | |
topCords.translation = [(width - border) / 2 + 10, -y/2 - 10] | |
rightCords = node.childrenMap.rightCords | |
rightCords.font.size = 20 | |
rightCords.color = "#FF0000" | |
rightCords.text = NumberToFixed(m.resWidth - x - width, 0) | |
rightCords.translation = [width + (m.resWidth - rightPos)/2 - 5, (height - border) / 2 - 20] | |
bottomCords = node.childrenMap.bottomCords | |
bottomCords.font.size = 20 | |
bottomCords.color = "#FF0000" | |
bottomCords.text = NumberToFixed(m.resHeight - y - height, 0) | |
bottomCords.translation = [(width - border) / 2 + 10, height + (m.resHeight - bottomPos)/2 - 5] | |
fill = node.childrenMap.fill | |
fill.width = width | |
fill.height = height | |
End Sub | |
' SelectorView_UpdateColors - color change event handler. Updates Selector (live) view color | |
' @param {object} event - event data | |
Sub SelectorView_UpdateColors(event) | |
node = event.getRoSGNode() | |
for each view in node.getchildren(-1,0) | |
if view.isSubtype("Rectangle") and view.id <> "fill" then view.color = node.color | |
end for | |
End Sub | |
' SelectorView_UpdateViewFromBRect - boundingRect change event handler. | |
' Updates Selector (live) view translation, width and height | |
' @param {object} event - event data | |
Sub SelectorView_UpdateViewFromBRect(event) | |
node = event.getRoSGNode() | |
boundingRect = node.boundingRect | |
if boundingRect <> Invalid then | |
node.setFields({ | |
translation : [boundingRect.x, boundingRect.y] | |
width : boundingRect.width | |
height : boundingRect.height | |
}) | |
end if | |
End Sub | |
' isBoundingRectChanged - checks is bounding rect was changed | |
' @param {object} br - node bounding rect | |
' @param {object} node - node | |
' @return {boolean} - true if node bounding rect was changed | |
Sub isBoundingRectChanged(br, node) as Boolean | |
o = node.oldBoundingRect | |
if o = Invalid then | |
node.oldBoundingRect = br | |
return true | |
end if | |
if o.x = br.x and o.y = br.y and o.width = br.width and o.height = br.height then | |
return false | |
else | |
node.oldBoundingRect = br | |
return true | |
end if | |
End Sub | |
' SelectorView_UpdateViewPosition - interval event handler. Updates selector (live) view position | |
' @param {object} event - node bounding rect | |
Sub SelectorView_UpdateViewPosition(event) | |
timer = event.getRoSGNode() | |
node = timer.getparent() | |
if node.attachedView <> Invalid then | |
boundingRect = node.attachedView.sceneboundingRect() | |
else | |
boundingRect = {x: -100, y: -100, width: 0, height: 0} | |
end if | |
if isBoundingRectChanged(boundingRect, node) then | |
node.boundingRect = boundingRect | |
end if | |
End Sub | |
' getCommandMap - Defines map that contains all RALE commands handlers | |
' @return {object} - handlers map | |
Sub getCommandMap() as Object | |
return { | |
"init" : "UIThread_init", | |
' Config commands | |
"showSelectorView" : "UIThread_showSelectorView", | |
"hideSelectorView" : "UIThread_hideSelectorView", | |
' Node commands | |
"selectNode" : "UIThread_selectNode", | |
"updateNode" : "UIThread_updateNode", | |
"getNodeData" : "UIThread_getNodeData", | |
' Tree View commands | |
"getItemList" : "UIThread_getItemList", | |
"getNodeTree" : "UIThread_getNodeTree", | |
"getNodeById" : "UIThread_getNodeById", | |
"getNodeByName" : "UIThread_getNodeByName", | |
' Children commands | |
"addChild" : "UIThread_addChild", | |
"removeChild" : "UIThread_removeChild", | |
"moveChild" : "UIThread_moveChild", | |
' Field commands | |
"setField" : "UIThread_setField", | |
"removeField" : "UIThread_removeField", | |
' Focus commands | |
"setFocus" : "UIThread_setFocus", | |
"selectFocusedNode" : "UIThread_selectFocusedNode", | |
' Bounding Rect commands | |
"setBoundingRect" : "UIThread_setBoundingRect", | |
' Registry commands | |
"getRegistrySections" : "UIThread_getRegistrySections", | |
"clearRegistry" : "UIThread_clearRegistry", | |
"addRegistrySection" : "UIThread_addRegistrySection", | |
"removeRegistrySection" : "UIThread_removeRegistrySection", | |
"addRegistryField" : "UIThread_addRegistryField", | |
"removeRegistryField" : "UIThread_removeRegistryField", | |
"editRegistryField" : "UIThread_editRegistryField", | |
' Logger commands | |
"setLogVerbosity" : "UIThread_setLogVerbosity", | |
"setLogFormat" : "UIThread_setLogFormat", | |
"log" : "UIThread_log" | |
' Ruler lines commands | |
"setRulerLine" : "UIThread_setRulerLine", | |
"setRulerLines" : "UIThread_setRulerLines", | |
"removeRulerLine" : "UIThread_removeRulerLine", | |
"removeAllRulerLines" : "UIThread_removeAllRulerLines", | |
} | |
End Sub | |
' callCommand - Call RALE command functuin from UI thread | |
' @param {string} command - command name | |
' @param {object} args - command arguments | |
' @return {object} - response of RALE command | |
Sub callCommand(command, args) as Object | |
return m.top.callFunc(command, args) | |
End Sub | |
' UIThread_init - RALE "init" command | |
' @param {object} args - command arguments | |
' @return {object} - response of command. Contains RALE version and session id | |
Sub UIThread_init(args) as Object | |
root = m.top.GetScene() | |
m.currentNode = root | |
m.currentPath = [] | |
if m.selectorView = Invalid then | |
m.selectorView = CreateSelectorView() | |
m.selectorView.color = "#ff0000" | |
m.selectorView.opacity = 0.6 | |
m.showSelectorView = true | |
root.appendChild(m.selectorView) | |
end if | |
if m.guides = Invalid then | |
m.guides = CreateGuides() | |
root.appendChild(m.guides.node) | |
else | |
UIThread_removeAllRulerLines({}) | |
end if | |
if args.logVerbosity >= 0 then | |
UIThread_setLogVerbosity({ level: args.logVerbosity }) | |
end if | |
if FW_IsString(args.logFormat) then | |
UIThread_setLogFormat({ format: args.logFormat }) | |
end if | |
m.logger.info("RALE Initialized") | |
return { raleVersion: m.raleVersion, sessionid: m.sessionId } | |
End Sub | |
'======================= | |
' Config commands | |
'======================= | |
' UIThread_showSelectorView - RALE "showSelectorView" command handler. Shows Selector (Live) View | |
' @param {object} args - command arguments | |
Sub UIThread_showSelectorView(args) as Object | |
m.showSelectorView = true | |
setSelectorView(m.currentNode, type(m.currentNode), m.currentPath) | |
m.logger.debug("Selector View was disabled") | |
End Sub | |
' UIThread_hideSelectorView - RALE "hideSelectorView" command handler. Hides Selector (Live) View | |
' @param {object} args - command arguments | |
Sub UIThread_hideSelectorView(args) as Object | |
m.showSelectorView = false | |
setSelectorView(m.currentNode, type(m.currentNode), m.currentPath) | |
m.logger.debug("Selector View was enabled") | |
End Sub | |
'======================= | |
' Node commands | |
'======================= | |
' UIThread_selectNode - RALE "selectNode" command handler. Selects node by path | |
' @param {object} args - command arguments | |
' @return {object} - response of command. Contains node path and node data (id, type, fields, layout etc.) | |
Sub UIThread_selectNode(args) as Object | |
path = args["path"] | |
root = m.top.getScene() | |
node = RALE_getNodeByPath(root, path) | |
if node = Invalid then return getError("Invalid Path") | |
nodeType = type(node) | |
if nodeType <> "roSGNode" and nodeType = "roArray" and nodeType = "roAssociativeArray" then | |
return getError("Invalid Node Type") | |
end if | |
if nodeType = "roSGNode" and node.isExternallyExposed <> Invalid then | |
return getError("Node Is Externally Exposed") | |
end if | |
if nodeType <> "roSGNode" or not node.IsSameNode(m.currentNode) then | |
m.currentPath = path | |
m.currentNode = node | |
end if | |
setSelectorView(node, nodeType, path) | |
m.logger.debug("Node selected: " + FW_AsString(m.currentNode)) | |
return { | |
path: m.currentPath, | |
node: getNodeData(m.currentNode, m.currentPath, root) | |
} | |
End Sub | |
' UIThread_updateNode - RALE "updateNode" command handler. Updates selected node | |
' @param {object} args - command arguments | |
' @return {object} - response of command. Contains node path and data (id, type, fields, layout etc.) | |
Sub UIThread_updateNode(args) as Object | |
return { | |
path: m.currentPath, | |
node: getNodeData(m.currentNode, m.currentPath, Invalid) | |
} | |
End Sub | |
' UIThread_getNodeData - RALE "getNodeData" command handler. Returns selected node data | |
' @param {object} args - command arguments | |
' @return {object} - response of command. Node data (id, type, fields, layout etc.) | |
Sub UIThread_getNodeData(args) as Object | |
path = args["path"] | |
if path = Invalid then | |
path = m.currentPath | |
node = m.currentNode | |
else | |
root = m.top.getScene() | |
node = RALE_getNodeByPath(root, path) | |
if node = Invalid then return getError("Invalid Path") | |
end if | |
return getNodeData(node, path, Invalid) | |
End Sub | |
'======================= | |
' Tree View commands | |
'======================= | |
' UIThread_getItemList - RALE "getItemList" command handler. Returns node children and fields, by node path | |
' @param {object} args - command arguments | |
' @return {object} - response of command. List of node children and fields | |
Sub UIThread_getItemList(args) as Object | |
path = args["path"] | |
root = m.top.getScene() | |
node = RALE_getNodeByPath(root, path) | |
if node = Invalid then return getError("Invalid Path") | |
return getItemList(node, path) | |
End Sub | |
' UIThread_getNodeTree - RALE "getNodeTree" command handler. Returns children hierarchy by node path | |
' @param {object} args - command arguments | |
' @return {object} - response of command. Node children hierarchy | |
Sub UIThread_getNodeTree(args) as Object | |
path = args["path"] | |
maxLevel = args["maxLevel"] | |
root = m.top.getScene() | |
node = RALE_getNodeByPath(root, path) | |
index = RALE_getNodeIndexByPath(path) | |
if node = Invalid then return getError("Invalid Path") | |
return getNodeTree(node, index, maxLevel) | |
End Sub | |
' UIThread_getNodeById - RALE "getNodeById" command handler. Returns node with requested id | |
' @param {object} args - command arguments | |
' @return {object} - response of command. Found node data | |
Sub UIThread_getNodeById(args) as Object | |
path = args["path"] | |
id = args["id"] | |
if id = Invalid then return getError("id is required") | |
root = m.top.getScene() | |
rootNode = RALE_getNodeByPath(root, path) | |
if rootNode = Invalid then return getError("Invalid Path") | |
foundNode = getNodeById(rootNode, id) | |
foundPath = RALE_getNodePath(foundNode, root) | |
return getNodeData(foundNode, foundPath, root) | |
End Sub | |
' UIThread_getNodeByName - RALE "getNodeByName" command handler. Returns node with requested name | |
' @param {object} args - command arguments | |
' @return {object} - response of command. Found node path | |
Sub UIThread_getNodeByName(args) as Object | |
path = args["path"] | |
name = args["name"] | |
if name = Invalid then return getError("name is required") | |
root = m.top.getScene() | |
rootNode = RALE_getNodeByPath(root, path) | |
if rootNode = Invalid then return getError("Invalid Path") | |
foundNode = getNodeByName(rootNode, name) | |
foundPath = RALE_getNodePath(foundNode, root) | |
return getNodeData(foundNode, foundPath, root) | |
End Sub | |
'======================= | |
' Children commands | |
'======================= | |
' UIThread_addChild - RALE "addChild" command handler. Adds new child to node by path (if no path adds to selected node) | |
' @param {object} args - command arguments | |
' @return {object} - response of command. Contains node children hierarchy and index | |
Sub UIThread_addChild(args) as Object | |
childType = args["type"] | |
path = args["path"] | |
if not FW_IsString(childType) then return getError("childType is required") | |
index = FW_AsInteger(args["index"]) | |
if index = 0 AND FW_AsString(args["index"]) <> "0" then index = Invalid | |
if path <> Invalid then | |
root = m.top.getScene() | |
node = RALE_getNodeByPath(root, path) | |
if node = Invalid then return getError("Invalid Path") | |
else | |
node = m.currentNode | |
path = m.currentPath | |
end if | |
result = addChild(node, childType, index) | |
if result.success = true then | |
index = RALE_getNodeIndexByPath(path) | |
setNodeField(result.child, "rale_new_child", true, "boolean") | |
return { | |
tree: getNodeTree(result.child, index, 50), | |
childindex: result.index | |
} | |
else | |
return result | |
end if | |
End Sub | |
' UIThread_removeChild - RALE "removeChild" command handler. Removes child from node by path (if no path removes from selected node) | |
' @param {object} args - command arguments | |
' @return {object} - response of command | |
Sub UIThread_removeChild(args) as Object | |
index = FW_AsInteger(args["index"]) | |
path = args["path"] | |
if index = 0 AND FW_AsString(args["index"]) <> "0" then return getError("index is required") | |
if path <> Invalid then | |
root = m.top.getScene() | |
node = RALE_getNodeByPath(root, path) | |
if node = Invalid then return getError("Invalid Path") | |
else | |
node = m.currentNode | |
path = m.currentPath | |
end if | |
return removeChild(node, index) | |
End Sub | |
' UIThread_moveChild - RALE "moveChild" command handler. Moves selected node child | |
' @param {object} args - command arguments | |
' @return {object} - response of command | |
Sub UIThread_moveChild(args) as Object | |
fromIndex = FW_AsString(args["fromIndex"]) | |
fromIndexInt = FW_AsInteger(fromIndex) | |
if fromIndexInt = 0 AND fromIndex <> "0" then return getError("fromIndex is required") | |
toIndex = FW_AsString(args["toIndex"]) | |
toIndexInt = FW_AsInteger(toIndex) | |
if toIndexInt = 0 AND toIndex <> "0" then return getError("toIndex is required") | |
return moveChild(m.currentNode, fromIndexInt, toIndexInt) | |
End Sub | |
'======================= | |
' Fields commands | |
'======================= | |
' UIThread_setField - RALE "setField" command handler. Sets new value into node field (if the field doesn't exist creates it) | |
' @param {object} args - command arguments | |
' @return {array} - response of command. Node field list | |
Sub UIThread_setField(args) as Object | |
field = FW_AsString(args["field"]) | |
fieldType = args["type"] | |
fieldValue = args["value"] | |
if field = "" then return getError("Field ID is required") | |
if RALE_convertToType(fieldValue, FW_AsString(fieldType)) = Invalid then return getError("Invalid Field Type or Value") | |
path = m.currentPath | |
nodeType = type(m.currentNode) | |
if nodeType = "roSGNode" then | |
setNodeField(m.currentNode, field, fieldValue, fieldType) | |
else | |
m.currentNode = setObjField(path, field, fieldValue, fieldType) | |
end if | |
m.logger.debug("Field '" + field + "' of selected node was set to " + FW_AsString(fieldValue)) | |
return getFieldList(m.currentNode) | |
End Sub | |
' UIThread_removeField - RALE "removeField" command handler. Remove field from node by id | |
' @param {object} args - command arguments | |
' @return {array} - response of command. Node field list | |
Sub UIThread_removeField(args) as Object | |
field = args["field"] | |
if field = Invalid then return getError("Field ID is required") | |
node = m.currentNode | |
nodeType = type(node) | |
if nodeType = "roSGNode" then | |
node.removeField(field) | |
if node.hasField(field) then return getError("Field cannot be removed") | |
else if nodeType = "roAssociativeArray" or nodeType = "roArray" then | |
if nodeType = "roArray" then field = FW_AsInteger(field) | |
node.Delete(field) | |
inOwnerField = RALE_getNodeIndexByPath(m.currentPath) | |
if inOwnerField = Invalid then return getError("Invalid Path") | |
ownerPath = [] | |
ownerPath.append(m.currentPath) | |
ownerPath.pop() | |
owner = setInParentNode(ownerPath, inOwnerField, node) | |
end if | |
m.logger.debug("Field '" + FW_AsString(field) + "' of selected node was removed") | |
return getFieldList(node) | |
End Sub | |
'======================= | |
' Focus commands | |
'======================= | |
' UIThread_setFocus - RALE "setFocus" command handler. Sets focus in node by path | |
' @param {object} args - command arguments | |
' @return {object} - response of command. Status | |
Sub UIThread_setFocus(args) as Object | |
path = args["path"] | |
node = m.top.getScene() | |
node = RALE_getNodeByPath(node, path) | |
if node = Invalid then return getError("Invalid Path") | |
m.logger.debug(FW_AsString(node) + " was focused") | |
node.setFocus(true) | |
return { success: true } | |
End Sub | |
' UIThread_selectFocusedNode - RALE "selectFocusedNode" command handler. Select focused node | |
' @param {object} args - command arguments | |
' @return {object} - response of command. Contains node path and data (id, type, fields, layout etc.) | |
Sub UIThread_selectFocusedNode(args) as Object | |
root = m.top.getScene() | |
node = getFocusedNodeObj(root) | |
if m.currentNode.IsSameNode(node) then | |
return { | |
path: m.currentPath, | |
node: getNodeData(node, m.currentPath, root) | |
} | |
else | |
path = RALE_getNodePath(node, root) | |
m.currentNode = node | |
m.currentPath = path | |
setSelectorView(node, type(node), path) | |
m.logger.debug("Node selected: " + FW_AsString(node)) | |
return { | |
path: path, | |
node: getNodeData(node, path, root) | |
} | |
end if | |
End Sub | |
'======================= | |
' Bounding Rect commands | |
'======================= | |
' UIThread_setBoundingRect - RALE "setBoundingRect" command handler. Sets properties into selected node layout | |
' @param {object} args - command arguments | |
' @return {object} - response of command. Node sceneBoundingRect | |
Sub UIThread_setBoundingRect(args) as Object | |
boundingRect = args["boundingrect"] | |
if boundingRect = Invalid then return getError("boundingrect is required") | |
node = m.currentNode | |
if type(node) <> "roSGNode" then return getError("Invalid Node Type") | |
currentBoundingRect = node.sceneBoundingRect() | |
if currentBoundingRect <> Invalid then | |
deltaX = 0 | |
deltaY = 0 | |
deltaWidth = 0 | |
deltaHeight = 0 | |
if boundingRect.x <> Invalid and boundingRect.y <> Invalid then | |
deltaX = currentBoundingRect.x - boundingRect.x | |
deltaY = currentBoundingRect.y - boundingRect.y | |
end if | |
if boundingRect.width <> Invalid and boundingRect.height <> Invalid then | |
deltaWidth = currentBoundingRect.width - boundingRect.width | |
deltaHeight = currentBoundingRect.height - boundingRect.height | |
end if | |
fields = {} | |
if node.hasField("translation") AND (deltaY <> 0 or deltaX <> 0) then | |
fields.translation = [node.translation[0] - deltaX, node.translation[1] - deltaY] | |
m.logger.debug("Selected node translation was set to " + FW_AsString(fields.translation)) | |
end if | |
if node.hasField("width") AND deltaWidth <> 0 then | |
if node.width = 0 AND deltaWidth > 0 then | |
fields.width = boundingRect.width | |
else | |
fields.width = node.width - deltaWidth | |
end if | |
m.logger.debug("Selected node width was set to " + FW_AsString(fields.width)) | |
end if | |
if node.hasField("height") AND deltaHeight <> 0 then | |
if node.height = 0 AND deltaHeight > 0 then | |
fields.height = boundingRect.height | |
else | |
fields.height = node.height - deltaHeight | |
end if | |
m.logger.debug("Selected node height was set to " + FW_AsString(fields.height)) | |
end if | |
node.setFields(fields) | |
end if | |
return node.sceneBoundingRect() | |
End Sub | |
'======================= | |
' Layout ruler commands | |
'======================= | |
' UIThread_setRulerLine - TBD | |
Sub UIThread_setRulerLine(args) as Object | |
m.guides.setLine(FW_AsString(args.id), args.position, args.coords) | |
return { success: true } | |
End Sub | |
' UIThread_setRulerLines - TBD | |
Sub UIThread_setRulerLines(args) as Object | |
if args.rulerLines.Count() >= 0 then | |
m.guides.setRulerLines(args.rulerLines) | |
end if | |
return { success: true } | |
End Sub | |
' UIThread_removeRulerLine - TBD | |
Sub UIThread_removeRulerLine(args) as Object | |
m.guides.removeLine(FW_AsString(args.id)) | |
return { success: true } | |
End Sub | |
' UIThread_removeRulerLine - TBD | |
Sub UIThread_removeAllRulerLines(args) as Object | |
m.guides.removeAllLines() | |
return { success: true } | |
End Sub | |
'======================= | |
' Registry commands | |
'======================= | |
' UIThread_getRegistrySections - TBD | |
Sub UIThread_getRegistrySections(args) as Object | |
return getRegistrySections() | |
End Sub | |
' UIThread_addRegistrySection - TBD | |
Sub UIThread_clearRegistry(args) as Object | |
return clearRegistry() | |
End Sub | |
' UIThread_addRegistrySection - TBD | |
Sub UIThread_addRegistrySection(args) as Object | |
name = args.name | |
section = args.section | |
if not FW_IsString(name) or name = "" then return getError("Invalid name") | |
if not FW_IsAssociativeArray(section) then return getError("Invalid section. Expected an object") | |
return addRegistrySection(name, section) | |
End Sub | |
' UIThread_removeRegistrySection - TBD | |
Sub UIThread_removeRegistrySection(args) as Object | |
name = args.name | |
if not FW_IsString(name) or name = "" then return getError("Invalid name") | |
return removeRegistrySection(name) | |
End Sub | |
' UIThread_addRegistryField - TBD | |
Sub UIThread_addRegistryField(args) as Object | |
sectionName = args.sectionName | |
key = args.key | |
value = args.value | |
if not FW_IsString(sectionName) or sectionName = "" then return getError("Invalid section name") | |
if not FW_IsString(key) or key = "" then return getError("Invalid key") | |
if not FW_IsString(value) then return getError("Invalid value") | |
return addRegistryField(sectionName, key, value) | |
End Sub | |
' UIThread_removeRegistryField - TBD | |
Sub UIThread_removeRegistryField(args) as Object | |
sectionName = args.sectionName | |
key = args.key | |
if not FW_IsString(sectionName) or sectionName = "" then return getError("Invalid section name") | |
if not FW_IsString(key) or key = "" then return getError("Invalid key") | |
return removeRegistryField(sectionName, key) | |
End Sub | |
' UIThread_editRegistryField - TBD | |
Sub UIThread_editRegistryField(args) as Object | |
sectionName = args.sectionName | |
key = args.key | |
newKey = args.newKey | |
newValue = args.newValue | |
if not FW_IsString(sectionName) or sectionName = "" then return getError("Invalid section name") | |
if not FW_IsString(key) or key = "" then return getError("Invalid key") | |
if not FW_IsString(newKey) or newKey = "" then return getError("Invalid newKey") | |
return editRegistryField(sectionName, key, newKey, newValue) | |
End Sub | |
' UIThread_log - TBD | |
Sub UIThread_log(args) as Object | |
m.logger.logMessage(args.message, m.logger.verbosityMap[args.status]) | |
End Sub | |
' UIThread_setLogVerbosity - TBD | |
Sub UIThread_setLogVerbosity(args) as Object | |
level = args.level | |
if not FW_IsInteger(level) then return getError("Invalid level") | |
m.logger.setVerbosity(args.level) | |
return { success: true } | |
End Sub | |
' UIThread_setLogFormat - TBD | |
Sub UIThread_setLogFormat(args) as Object | |
format = args.format | |
if not FW_IsString(format) then return getError("Invalid format") | |
m.logger.setFormat(args.format) | |
return { success: true } | |
End Sub | |
'======================= | |
' Helpers functions | |
'======================= | |
' getLayout - Returns node layout fields (boundingRect, resolution, isMovable, isHeightResizable, isWidthResizable) | |
' @param {object} node - node | |
' @param {object} root - root of nodes (main scene) | |
' @return {object} - node layout | |
Sub getLayout(node, root) as Object | |
if not FW_IsSGNode(node) then return getError("Invalid Node Type") | |
if root = Invalid then root = m.top.getScene() | |
parent = node.getParent() | |
isWidthResizable = node.hasField("width") | |
isHeightResizable = node.hasField("height") | |
isMovable = false | |
if parent <> Invalid and not m.sceneBG.isSameNode(node) then | |
isMovable = node.hasField("translation") and parent.ParentSubtype("LayoutGroup") <> "" | |
end if | |
return { | |
isMovable : isMovable, | |
isHeightResizable : isHeightResizable, | |
isWidthResizable : isWidthResizable, | |
boundingRect : node.sceneBoundingRect(), | |
resolution : root.getField("currentDesignResolution") | |
} | |
End Sub | |
' getItemObj - Returns node object | |
' @param {object} item - node or filed | |
' @param {string} index - field id or child index | |
' @return {object} - node data object | |
Sub getItemObj(item, index) as Dynamic | |
itemType = type(item) | |
if itemType = "roSGNode" then | |
return { | |
index: index, | |
id: item.id, | |
childrenCount: item.getChildCount(), | |
subtype: item.subtype(), | |
type: itemType, | |
value: "{object}" | |
isExposed: FW_AsBoolean(item.isExternallyExposed) | |
} | |
else if itemType = "roArray" or itemType = "roAssociativeArray" then | |
return { | |
id: index, | |
type: itemType, | |
value: "{object}" | |
} | |
else | |
return { | |
id: index, | |
type: itemType, | |
value: FW_AsString(item) | |
} | |
end if | |
End Sub | |
' getFieldList - Returns node field list | |
' @param {object} node - node | |
' @return {array} - list of fields | |
Sub getFieldList(node) as Object | |
nodeType = type(node) | |
if nodeType = "roSGNode" then | |
fields = node.getFields() | |
else if nodeType = "roArray" or nodeType = "roAssociativeArray" then | |
fields = node | |
else | |
return getError("Invalid Node Type") | |
end if | |
fieldList = {} | |
result = 0 | |
if type(fields) = "roArray" then | |
count = fields.count() - 1 | |
for i = 0 to count | |
id = i.ToStr() | |
fieldList[id] = { item: getItemObj(fields[i], i) } | |
fieldList[id].item.id = i | |
end for | |
else | |
keys = fields.keys() | |
for each fieldId in keys | |
fieldList[fieldId] = { item: getItemObj(fields[fieldId], fieldId) } | |
if nodeType = "roSGNode" then fieldList[fieldId].item.fieldType = node.getFieldType(fieldId) | |
end for | |
end if | |
return fieldList | |
End Sub | |
' getChildList - Returns node children list | |
' @param {object} node - node | |
' @return {array} - list of children | |
Sub getChildList(node) as Object | |
nodeType = type(node) | |
if nodeType = "roSGNode" then | |
childList = [] | |
children = node.getChildren(-1, 0) | |
count = children.count() | |
if count > 0 then | |
count-- | |
for i = 0 to count | |
if children[i] <> Invalid then | |
childList[i] = { item: getItemObj(children[i], i) } | |
end if | |
end for | |
end if | |
return childList | |
else | |
return getError("Invalid Node Type") | |
end if | |
End Sub | |
' getItemList - Returns node children list and node data | |
' @param {object} node - node | |
' @param {object} index - node index | |
' @return {object} - node data object and list of node children | |
Sub getItemList(node, index) as Object | |
item = { item: getItemObj(node, index) } | |
nodeType = type(node) | |
if nodeType = "roSGNode" then | |
item.childList = getChildList(node) | |
else | |
return getError("Invalid Node Type") | |
end if | |
return item | |
End Sub | |
' getNodeTree - Goes through node children and returns node children hierarchy | |
' @param {object} node - node or filed | |
' @param {string} id - node index or filed id | |
' @return {array} - node children hierarchy | |
Sub getNodeTree(node, id, maxLevel) as Object | |
item = { | |
item: getItemObj(node, id), | |
childList: [] | |
} | |
result = RALE_forEachChild(node, { maxLevel: maxLevel }, getNodeTreeCallback, { item: item }) | |
if not result then | |
return getError("Invalid Node Type") | |
end if | |
return item | |
End Sub | |
Sub getNodeTreeCallback(node, index, parentObj, childObj, storage) as Boolean | |
if parentObj.item = Invalid then | |
parentObj.item = storage.item | |
end if | |
childItem = { | |
item : getItemObj(node, index), | |
childList : [] | |
} | |
parentObj.item.childlist[index] = childItem | |
if childObj <> Invalid then | |
childObj.item = childItem | |
end if | |
return true | |
End Sub | |
' getNodeById - Search for node by id | |
' @param {object} node - root node | |
' @param {string} id - node id | |
' @return {object} - found node | |
Sub getNodeById(rootNode, id) as Object | |
if rootNode.id = id then | |
return rootNode | |
end if | |
storage = { | |
node: Invalid, | |
id: id | |
} | |
result = RALE_forEachChild(rootNode, {}, getNodeByIdCallback, storage) | |
if not result then | |
return getError("Invalid Node Type") | |
end if | |
return storage.node | |
End Sub | |
Sub getNodeByIdCallback(node, index, parentObj, childObj, storage) as Boolean | |
if node.id = storage.id then | |
storage.node = node | |
return false | |
end if | |
return true | |
End Sub | |
' getNodeByName - Search for node by name (subtype) | |
' @param {object} node - root node | |
' @param {string} name - node name (subtype) | |
' @return {object} - found node | |
Sub getNodeByName(rootNode, name) as Object | |
if rootNode.subtype() = name then | |
return rootNode | |
end if | |
storage = { | |
node: Invalid, | |
name: name | |
} | |
result = RALE_forEachChild(rootNode, {}, getNodeByNameCallback, storage) | |
if not result then | |
return getError("Invalid Node Type") | |
end if | |
return storage.node | |
End Sub | |
Sub getNodeByNameCallback(node, index, parentObj, childObj, storage) as Boolean | |
if node.subtype() = storage.name then | |
storage.node = node | |
return false | |
end if | |
return true | |
End Sub | |
' getNodeData - Returns node children hierarchy | |
' @param {object} node - node or filed | |
' @param {array} path - path to node | |
' @param {object} root - root of nodes (main scene) | |
' @return {object} - node data (item, fieldlist, layout) | |
Sub getNodeData(node, path, root) as Object | |
index = RALE_getNodeIndexByPath(path) | |
if index = Invalid then return getError("Invalid Path") | |
result = { | |
item: getItemObj(node, index), | |
fieldlist: getFieldList(node), | |
layout: getLayout(node, root) | |
} | |
if type(node) = "roSGNode" then result.childlist = getChildList(node) | |
return result | |
End Sub | |
' addChild - Adds child to node by type and index | |
' @param {object} node - parent node | |
' @param {string} childType - path to node | |
' @param {string} index - child index | |
' @return {object} - status | |
Sub addChild(node, childType, index) as Object | |
if not FW_IsSGNode(node) then return getError("Cannot Add Child. Invalid Node Type") | |
child = CreateObject("roSGNode", childType) | |
if not FW_IsSGNode(child) then return getError("Cannot Add Child. Invalid Component Type") | |
childCount = node.getChildCount() | |
if FW_IsInteger(index) and index < childCount then | |
node.insertChild(child, index) | |
else | |
index = childCount | |
node.appendChild(child) | |
end if | |
addedChild = node.getChild(index) | |
if type(addedChild) = "roSGNode" and addedChild.isSameNode(child) then | |
m.logger.debug("Child " + childType + " was added to " + FW_AsString(node) + " by index " + FW_AsString(index)) | |
return { success: true, child: addedChild, index: index } | |
else | |
return getError("Cannot Add Child") | |
end if | |
End Sub | |
' removeChild - Removes child from node by index | |
' @param {object} node - parent node | |
' @param {string} index - child index | |
' @return {object} - status | |
Sub removeChild(node, index as Integer) as Object | |
if not FW_IsSGNode(node) then return getError("Invalid Node Type") | |
count = node.getChildCount() | |
if FW_IsSGNode(node.getChild(index)) then | |
node.removeChildIndex(index) | |
if node.getChildCount() < count then | |
m.logger.debug("Child with index " + FW_AsString(index) + " was removed from " + FW_AsString(node)) | |
return { success: true } | |
end if | |
return getError("Node cannot be removed") | |
else | |
return getError("No child with this index") | |
end if | |
End Sub | |
' moveChild - Moves node child by index to another position | |
' @param {object} node - parent node | |
' @param {string} fromIndex - current child index | |
' @param {string} toIndex - index where you want to move | |
' @return {object} - status | |
Sub moveChild(node, fromIndex, toIndex) as Object | |
if not FW_IsSGNode(node) then return getError("Invalid Node Type") | |
if fromIndex >= 0 AND toIndex >= 0 | |
child = node.getChild(fromIndex) | |
if child <> Invalid then | |
node.removeChildIndex(fromIndex) | |
if toIndex > fromIndex then toIndex-- | |
node.insertChild(child, toIndex) | |
m.logger.debug("Child of " + FW_AsString(node) + " was moved from index " + FW_AsString(fromIndex) + " to index " + FW_AsString(toIndex)) | |
return { success: true } | |
else | |
return getError("No child with this index") | |
end if | |
else | |
return getError("fromIndex and toIndex must be positive integer") | |
end if | |
End Sub | |
' setSelectorView - Shows selector (live) view for node | |
' @param {object} node - node | |
' @param {string} nodeType - type of node | |
' @param {array} nodePath - path to node | |
' @return {object} - status | |
Function setSelectorView(node, nodeType, nodePath) as Object | |
if m.selectorView <> Invalid then | |
if m.showSelectorView and nodeType = "roSGNode" and nodePath.count() > 0 then | |
m.selectorView.attachedView = node | |
else | |
m.selectorView.attachedView = Invalid | |
end if | |
end if | |
End Function | |
' setNodeField - Sets field into node | |
' @param {object} node - node | |
' @param {string} field - field id | |
' @param {dynamic} value - field value | |
' @param {string} fieldType - field type | |
Sub setNodeField(node, field, value, fieldType) as Object | |
fields = {} | |
if fieldType = Invalid then | |
fields[field] = value | |
if node.hasField(field) then | |
node.setFields(fields) | |
else | |
node.addFields(fields) | |
end if | |
else | |
fields[field] = RALE_convertToType(value, fieldType) | |
if node.hasField(field) and node.getFieldType(field) <> fieldType then | |
node.removeField(field) | |
end if | |
if not node.hasField(field) then | |
node.addField(field, RALE_parseType(fieldType), false) | |
end if | |
node.setFields(fields) | |
end if | |
End Sub | |
' setObjField - Sets field into AssociativeArray | |
' @param {array} parentPath - node | |
' @param {string} field - field id | |
' @param {dynamic} value - field value | |
' @param {string} fieldType - field type | |
' @return {object} - status | |
Sub setObjField(parentPath, field, value, fieldType) as Object | |
if fieldType <> Invalid then | |
value = RALE_convertToType(value, fieldType) | |
end if | |
return setInParentNode(parentPath, field, value) | |
End Sub | |
' setInParentNode - Sets field into parent node | |
' @param {array} path - path to field | |
' @param {string} field - field id | |
' @param {dynamic} value - field value | |
' @return {object} - parent node | |
Function setInParentNode(path, field, value) | |
root = m.top.getScene() | |
parentPath = [] | |
parentPath.append(path) | |
nodeList = RALE_getNodeListByPath(root, path) | |
if nodeList = Invalid then return getError("Invalid Path") | |
index = nodeList.count() - 1 | |
parent = nodeList[index] | |
if type(parent) = "roSGNode" then | |
setNodeField(parent, field, value, Invalid) | |
return parent | |
end if | |
while type(parent) <> "roSGNode" | |
if type(parent) = "roArray" then | |
field = FW_AsInteger(field) | |
if field > parent.count() then | |
field = parent.count() | |
end if | |
parent[field] = value | |
else | |
parent[field] = value | |
end if | |
value = parent | |
index-- | |
field = path[index].field | |
parent = nodeList[index] | |
parentPath.pop() | |
end while | |
parent = RALE_getNodeByPath(root, parentPath) | |
setNodeField(parent, field, value, Invalid) | |
return RALE_getNodeByPath(root, path) | |
End Function | |
' setFocus - Focus node | |
' @param {object} node - node | |
Sub setFocus(node) as Object | |
if not FW_IsSGNode(node) then return getError("Invalid Node Type") | |
node.setFocus(true) | |
End Sub | |
' getFocusedNodeObj - Returns focused node | |
' @param {object} root - root | |
' @param {object} maxDeep - search depth | |
' @return {object} - focused node | |
Function getFocusedNodeObj(root, maxDeep = 25) as Object | |
node = root | |
focusedChild = node.focusedChild | |
while maxDeep > 0 AND focusedChild <> Invalid AND not node.isSameNode(focusedChild) | |
node = focusedChild | |
focusedChild = node.focusedChild | |
maxDeep-- | |
end while | |
return node | |
End Function | |
' getRegistrySections - Returns map of roRegistry | |
' @return {object} - sections | |
Function getRegistrySections() as Object | |
sections = {} | |
RALE_forEachRegistryField(getRegistrySectionsCallback, sections) | |
return sections | |
End Function | |
Sub getRegistrySectionsCallback(value, key, section, sections) as Boolean | |
if not FW_IsAssociativeArray(sections[section]) then | |
sections[section] = {} | |
end If | |
sections[section][key] = value | |
return true | |
End Sub | |
' clearRegistry - Clear roRegistry | |
Function clearRegistry() as Object | |
RALE_forEachRegistrySection(clearRegistryCallback, {}) | |
return { success: true } | |
End Function | |
Sub clearRegistryCallback(section, sectionName, storage) as Object | |
return removeRegistrySection(sectionName) | |
End Sub | |
' addRegistrySection - TBD | |
' @return {object} - sections | |
Function addRegistrySection(sectionName, section) as Object | |
RegistrySection = CreateObject("roRegistrySection", sectionName) | |
keys = section.keys() | |
for each key in keys | |
RegistrySection.write(key, section[key]) | |
end for | |
RegistrySection.flush() | |
m.logger.debug("Registry section " + sectionName + " was added to roRegistry") | |
return { success: true } | |
End Function | |
' removeRegistrySection - TBD | |
' @return {object} - sections | |
Function removeRegistrySection(sectionName) as Object | |
Registry = CreateObject("roRegistry") | |
success = Registry.delete(sectionName) | |
Registry.flush() | |
m.logger.debug("Registry section " + sectionName + " was removed from roRegistry") | |
return { success: success } | |
End Function | |
' addRegistryField - TBD | |
' @return {object} - sections | |
Function addRegistryField(sectionName, key, value) as Object | |
RegistrySection = CreateObject("roRegistrySection", sectionName) | |
success = RegistrySection.write(key, value) | |
m.logger.debug("Field " + key + " with value " + value + " was added to " + sectionName + " registry section") | |
return { success: success } | |
End Function | |
' removeRegistryField - TBD | |
' @return {object} - sections | |
Function removeRegistryField(sectionName, key) as Object | |
RegistrySection = CreateObject("roRegistrySection", sectionName) | |
success = RegistrySection.delete(key) | |
m.logger.debug("Field " + key + " was removed from " + sectionName + " registry section") | |
return { success: success } | |
End Function | |
' getRegistrySections - TBD | |
' @return {object} - sections | |
Function editRegistryField(sectionName, key, newKey, newValue) as Object | |
result = removeRegistryField(sectionName, key) | |
if result.success then | |
result = addRegistryField(sectionName, newKey, newValue) | |
end if | |
return result | |
End Function | |
' getError - Returns error object | |
' @param {string} message - error message | |
Function getError(message as String) as Object | |
m.logger.error(message) | |
return { error: { message: message } } | |
End Function | |
' @desc TrackerTask init function | |
Sub init() | |
m.top.functionName = "tracker" | |
m.top.control = "RUN" | |
m.config = RALE_Config() | |
m.logger = RALE_Logger(m.config.defaultLogLevel, m.config.logFormat) | |
m.sceneBG = m.top.GetScene().getChild(0) | |
m.raleVersion = "2.2.18" | |
m.sessionId = CreateObject("roDeviceInfo").GetRandomUUID() | |
m.logger.info("Roku Advanced Layout Editor v." + m.raleVersion) | |
End Sub | |
' tracker - TrackerTask run function | |
Sub tracker() | |
appInfo = CreateObject("roAppInfo") | |
handlersMap = getCommandMap() | |
RALE_InfoLog("TrackerTask run") | |
buffer = CreateObject("roByteArray") | |
buffer[m.config.bufferSize] = 0 | |
messagePort = CreateObject("roMessagePort") | |
addr = CreateObject("roSocketAddress") | |
inputPort = CreateObject("roMessagePort") | |
inputObj = CreateObject("roInput") | |
inputObj.SetMessagePort(inputPort) | |
while True | |
raleEnabled = true | |
port = 0 | |
RALE_InfoLog("Waiting on ECP input request") | |
msg = wait(0, inputPort) | |
if type(msg) = "roInputEvent" | |
if msg.IsInput() | |
info = msg.GetInfo() | |
if type(info) = "roAssociativeArray" and info.rale <> Invalid then | |
port = FW_AsInteger(info.port) | |
end if | |
nonDev = FW_AsBoolean(info.nonDev) | |
if not nonDev and not appInfo.IsDev() then | |
RALE_InfoLog("Disable RALE for non-dev channels") | |
raleEnabled = false | |
end if | |
end if | |
end if | |
if raleEnabled and port > m.config.minSocketPort and port < m.config.maxSocketPort then | |
socketConnection(addr, messagePort, port, buffer, handlersMap, m.config) | |
end if | |
end while | |
End Sub | |
' socketConnection - Starts socket connection. Keep alive while connection won't be closed | |
' @param {object} addr - roSocketAddress | |
' @param {object} messagePort - roMessagePort | |
' @param {number} port - socket connection port | |
' @param {array} buffer - buffer for socket packets | |
' @param {object} handlersMap - map that contains all RALE commands handlers | |
' @param {object} config - configuration constants | |
' @return {string} - connection status | |
Sub socketConnection(addr, messagePort, port, buffer, handlersMap, config) as String | |
connections = {} | |
appManager = createObject("roAppManager") | |
screensaverTimer = CreateObject("roTimespan") | |
screensaverTimer.Mark() | |
addr.setPort(port) | |
tcpListen = CreateObject("roStreamSocket") | |
tcpListen.setMessagePort(messagePort) | |
tcpListen.setAddress(addr) | |
tcpListen.notifyReadable(true) | |
tcpListen.listen(4) | |
wasConnected = false | |
RALE_InfoLog("Start socket server on port " + FW_AsString(port)) | |
while True | |
if not wasConnected then | |
event = wait(3000, messagePort) | |
if event = Invalid then | |
tcpListen.close() | |
RALE_WarningLog("No 'init' command received. Socket connection closed") | |
return "connection timeout" | |
end if | |
else | |
event = wait(config.commandTimeout, messagePort) | |
end if | |
' Resets the screensaver timer | |
if screensaverTimer.TotalMilliseconds() > config.screensaverResetTimeout then | |
screensaverTimer.Mark() | |
appManager.UpdateLastKeyPressTime() | |
end if | |
' ? type(event) | |
if type(event) = "roSocketEvent" | |
wasConnected = true | |
changedID = event.getSocketID() | |
if changedID = tcpListen.getID() and tcpListen.isReadable() | |
' New | |
newConnection = tcpListen.accept() | |
if newConnection = Invalid | |
RALE_WarningLog("Connection accept failed") | |
else | |
RALE_InfoLog("Accepted new connection " + FW_AsString(newConnection.getID())) | |
newConnection.notifyReadable(true) | |
newConnection.setMessagePort(messagePort) | |
connections[Stri(newConnection.getID())] = newConnection | |
end if | |
else | |
' Activity on an open connection | |
connection = connections[Stri(changedID)] | |
closed = False | |
if connection.isReadable() | |
received = connection.receive(buffer, 0, 1024) | |
if received > 0 | |
timer = CreateObject("roTimespan") | |
timer.Mark() | |
str = buffer.ToAsciiString().Left(received) | |
request = ParseJson(str) | |
buffer = CreateObject("roByteArray") | |
buffer[1024] = 0 | |
if request <> Invalid then | |
response = {} | |
if request["command"] <> Invalid then | |
handler = handlersMap[request["command"]] | |
if handler <> Invalid then | |
RALE_DebugLog("Handling command: " + request["command"]) | |
RALE_DebugLog("Received arguments: " + FW_AsString(request["args"])) | |
response = callCommand(handler, request["args"]) | |
else | |
RALE_ErrorLog("No such command: " + request["command"]) | |
response = getError("No such command") | |
end if | |
end if | |
id = request.uuid | |
idLen = FW_AsString(Len(id)) | |
json = FormatJson(response) | |
res = "[start][uuid:" + idLen + "]" + id + json + "[end]" | |
resList = RALE_splitStringByLength(res, 3000) | |
count = resList.count() - 1 | |
for i = 0 to count | |
connection.SendStr(resList[i]) | |
sleep(20) | |
end for | |
RALE_DebugLog("Command handled in: " + FW_AsString(timer.TotalMilliseconds()) + "ms") | |
end if | |
else if received=0 ' client closed | |
closed = True | |
end if | |
end if | |
if closed or not connection.eOK() | |
connection.close() | |
connections.delete(Stri(changedID)) | |
exit while | |
end if | |
end if | |
end if | |
end while | |
tcpListen.close() | |
for each id in connections | |
connections[id].close() | |
end for | |
RALE_InfoLog("Socket connection closed") | |
return "connection closed" | |
End Sub | |
'IMPORTS= | |
'===================== | |
' Types | |
'===================== | |
'************************************************* | |
' FW_IsXmlElement - check if value contains XMLElement interface | |
' @param value As Dynamic | |
' @return As Boolean - true if value contains XMLElement interface, else return false | |
'************************************************* | |
Function FW_IsXmlElement(value As Dynamic) As Boolean | |
Return FW_IsValid(value) And GetInterface(value, "ifXMLElement") <> invalid | |
End Function | |
'************************************************* | |
' FW_IsFunction - check if value contains Function interface | |
' @param value As Dynamic | |
' @return As Boolean - true if value contains Function interface, else return false | |
'************************************************* | |
Function FW_IsFunction(value As Dynamic) As Boolean | |
Return FW_IsValid(value) And GetInterface(value, "ifFunction") <> invalid | |
End Function | |
'************************************************* | |
' FW_IsBoolean - check if value contains Boolean interface | |
' @param value As Dynamic | |
' @return As Boolean - true if value contains Boolean interface, else return false | |
'************************************************* | |
Function FW_IsBoolean(value As Dynamic) As Boolean | |
Return FW_IsValid(value) And GetInterface(value, "ifBoolean") <> invalid | |
End Function | |
'************************************************* | |
' FW_IsInteger - check if value type equals Integer | |
' @param value As Dynamic | |
' @return As Boolean - true if value type equals Integer, else return false | |
'************************************************* | |
Function FW_IsInteger(value As Dynamic) As Boolean | |
Return FW_IsValid(value) And GetInterface(value, "ifInt") <> invalid And (Type(value) = "roInt" Or Type(value) = "roInteger" Or Type(value) = "Integer") | |
End Function | |
'************************************************* | |
' FW_IsFloat - check if value contains Float interface | |
' @param value As Dynamic | |
' @return As Boolean - true if value contains Float interface, else return false | |
'************************************************* | |
Function FW_IsFloat(value As Dynamic) As Boolean | |
Return FW_IsValid(value) And GetInterface(value, "ifFloat") <> invalid | |
End Function | |
'************************************************* | |
' FW_IsDouble - check if value contains Double interface | |
' @param value As Dynamic | |
' @return As Boolean - true if value contains Double interface, else return false | |
'************************************************* | |
Function FW_IsDouble(value As Dynamic) As Boolean | |
Return FW_IsValid(value) And GetInterface(value, "ifDouble") <> invalid | |
End Function | |
'************************************************* | |
' FW_IsLongInteger - check if value contains LongInteger interface | |
' @param value As Dynamic | |
' @return As Boolean - true if value contains LongInteger interface, else return false | |
'************************************************* | |
Function FW_IsLongInteger(value As Dynamic) As Boolean | |
Return FW_IsValid(value) And GetInterface(value, "ifLongInt") <> invalid | |
End Function | |
'************************************************* | |
' FW_IsNumber - check if value contains LongInteger or Integer or Double or Float interface | |
' @param value As Dynamic | |
' @return As Boolean - true if value is number, else return false | |
'************************************************* | |
Function FW_IsNumber(value As Dynamic) As Boolean | |
Return FW_IsLongInteger(value) or FW_IsDouble(value) or FW_IsInteger(value) or FW_IsFloat(value) | |
End Function | |
'************************************************* | |
' FW_IsList - check if value contains List interface | |
' @param value As Dynamic | |
' @return As Boolean - true if value contains List interface, else return false | |
'************************************************* | |
Function FW_IsList(value As Dynamic) As Boolean | |
Return FW_IsValid(value) And GetInterface(value, "ifList") <> invalid | |
End Function | |
'************************************************* | |
' FW_IsArray - check if value contains Array interface | |
' @param value As Dynamic | |
' @return As Boolean - true if value contains Array interface, else return false | |
'************************************************* | |
Function FW_IsArray(value As Dynamic) As Boolean | |
Return FW_IsValid(value) And GetInterface(value, "ifArray") <> invalid | |
End Function | |
'************************************************* | |
' FW_IsAssociativeArray - check if value contains AssociativeArray interface | |
' @param value As Dynamic | |
' @return As Boolean - true if value contains AssociativeArray interface, else return false | |
'************************************************* | |
Function FW_IsAssociativeArray(value As Dynamic) As Boolean | |
Return FW_IsValid(value) And GetInterface(value, "ifAssociativeArray") <> invalid | |
End Function | |
'************************************************* | |
' FW_IsSGNode - check if value contains SGNodeChildren interface | |
' @param value As Dynamic | |
' @return As Boolean - true if value contains SGNodeChildren interface, else return false | |
'************************************************* | |
Function FW_IsSGNode(value As Dynamic, interface = "ifSGNodeChildren") As Boolean | |
Return FW_IsValid(value) And GetInterface(value, interface) <> invalid | |
End Function | |
'************************************************* | |
' FW_IsString - check if value contains String interface | |
' @param value As Dynamic | |
' @return As Boolean - true if value contains String interface, else return false | |
'************************************************* | |
Function FW_IsString(value As Dynamic) As Boolean | |
Return FW_IsValid(value) And GetInterface(value, "ifString") <> invalid | |
End Function | |
'************************************************* | |
' FW_IsNotEmptyString - check if value contains String interface and length more 0 | |
' @param value As Dynamic | |
' @return As Boolean - true if value contains String interface and length more 0, else return false | |
'************************************************* | |
Function FW_IsNotEmptyString(value As Dynamic) As Boolean | |
Return FW_IsString(value) and len(value) > 0 | |
End Function | |
'************************************************* | |
' FW_IsDateTime - check if value contains DateTime interface | |
' @param value As Dynamic | |
' @return As Boolean - true if value contains DateTime interface, else return false | |
'************************************************* | |
Function FW_IsDateTime(value As Dynamic) As Boolean | |
Return FW_IsValid(value) And (GetInterface(value, "ifDateTime") <> invalid Or Type(value) = "roDateTime") | |
End Function | |
'************************************************* | |
' FW_IsValid - check if value initialized and not equal invalid | |
' @param value As Dynamic | |
' @return As Boolean - true if value initialized and not equal invalid, else return false | |
'************************************************* | |
Function FW_IsValid(value As Dynamic) As Boolean | |
Return Type(value) <> "<uninitialized>" And value <> invalid | |
End Function | |
'************************************************* | |
' FW_ValidStr - return value if his contains String interface else return empty string | |
' @param value As Object | |
' @return As String - value if his contains String interface else return empty string | |
'************************************************* | |
Function FW_ValidStr(obj As Object) As String | |
if obj <> invalid and GetInterface(obj, "ifString") <> invalid | |
return obj | |
else | |
return "" | |
endif | |
End Function | |
'************************************************* | |
' FW_AsString - convert input to String if this possible, else return empty string | |
' @param input As Dynamic | |
' @return As String - return converted string | |
'************************************************* | |
Function FW_AsString(input As Dynamic) As String | |
If FW_IsValid(input) = False Then | |
Return "" | |
Else If FW_IsSGNode(input, "ifHttpAgent") Then | |
return "roHttpAgent" | |
Else If FW_IsString(input) Then | |
Return input | |
Else If FW_IsInteger(input) or FW_IsLongInteger(input) or FW_IsBoolean(input)Then | |
Return input.ToStr() | |
Else If FW_IsFloat(input) or FW_IsDouble(input) Then | |
Return Str(input).Trim() | |
Else If FW_IsBoolean(input) Then | |
If input Then | |
Return "true" | |
Else | |
Return "false" | |
End If | |
Else If FW_IsSGNode(input) Then | |
return input.subtype() | |
Else If FW_IsArray(input) Then | |
result = "" | |
For each item in input | |
result = result + FW_AsString(item) + ", " | |
End For | |
Return "[" + Left(result, Len(result) - 2) + "]" | |
Else If FW_IsAssociativeArray(input) Then | |
result = "" | |
For each key in input | |
result = result + key + " : " + FW_AsString(input[key]) + ", " | |
End For | |
Return "{ " + Left(result, Len(result) - 2) + " }" | |
End If | |
End Function | |
'************************************************* | |
' FW_AsInteger - convert input to Integer if this possible, else return 0 | |
' @param input As Dynamic | |
' @return As Integer - return converted Integer | |
'************************************************* | |
Function FW_AsInteger(input As Dynamic) As Integer | |
If FW_IsValid(input) = False Then | |
Return 0 | |
Else If FW_IsString(input) Then | |
Return input.ToInt() | |
Else If FW_IsInteger(input) Then | |
Return input | |
Else If FW_IsFloat(input) or FW_IsDouble(input) or FW_IsLongInteger(input) Then | |
Return Int(input) | |
Else | |
Return 0 | |
End If | |
End Function | |
'************************************************* | |
' FW_AsLongInteger - convert input to LongInteger if this possible, else return 0 | |
' @param input As Dynamic | |
' @return As Integer - return converted LongInteger | |
'************************************************* | |
Function FW_AsLongInteger(input As Dynamic) As LongInteger | |
If FW_IsValid(input) = False Then | |
Return 0 | |
Else If FW_IsString(input) Then | |
Return FW_AsInteger(input) | |
Else If FW_IsLongInteger(input) or FW_IsFloat(input) or FW_IsDouble(input) or FW_IsInteger(input) Then | |
Return input | |
Else | |
Return 0 | |
End If | |
End Function | |
'************************************************* | |
' FW_AsFloat - convert input to Float if this possible, else return 0.0 | |
' @param input As Dynamic | |
' @return As Float - return converted Float | |
'************************************************* | |
Function FW_AsFloat(input As Dynamic) As Float | |
If FW_IsValid(input) = False Then | |
Return 0.0 | |
Else If FW_IsString(input) Then | |
Return input.ToFloat() | |
Else If FW_IsInteger(input) Then | |
Return (input / 1) | |
Else If FW_IsFloat(input) or FW_IsDouble(input) or FW_IsLongInteger(input) Then | |
Return input | |
Else | |
Return 0.0 | |
End If | |
End Function | |
'************************************************* | |
' FW_AsDouble - convert input to Double if this possible, else return 0.0 | |
' @param input As Dynamic | |
' @return As Float - return converted Double | |
'************************************************* | |
Function FW_AsDouble(input As Dynamic) As Double | |
If FW_IsValid(input) = False Then | |
Return 0.0 | |
Else If FW_IsString(input) Then | |
Return FW_AsFloat(input) | |
Else If FW_IsInteger(input) or FW_IsLongInteger(input) or FW_IsFloat(input) or FW_IsDouble(input) Then | |
Return input | |
Else | |
Return 0.0 | |
End If | |
End Function | |
'************************************************* | |
' FW_AsBoolean - convert input to Boolean if this possible, else return False | |
' @param input As Dynamic | |
' @return As Boolean | |
'************************************************* | |
Function FW_AsBoolean(input As Dynamic) As Boolean | |
If FW_IsValid(input) = False Then | |
Return False | |
Else If FW_IsString(input) Then | |
Return LCase(input) = "true" | |
Else If FW_IsInteger(input) Or FW_IsFloat(input) Then | |
Return input <> 0 | |
Else If FW_IsBoolean(input) Then | |
Return input | |
Else | |
Return False | |
End If | |
End Function | |
'************************************************* | |
' FW_AsArray - if type of value equals array return value, else return array with one element [value] | |
' @param value As Object | |
' @return As Object - roArray | |
'************************************************* | |
Function FW_AsArray(value As Object) As Object | |
If FW_IsValid(value) | |
If Not FW_IsArray(value) Then | |
Return [value] | |
Else | |
Return value | |
End If | |
End If | |
Return [] | |
End Function | |
'************************************************* | |
' FW_ValidAA - check if obj contains all keys | |
' @param obj As Object - roAssociativeArray | |
' @param keys as Object - roArray of keys(string) to check | |
' @param delim as String - key delimiter (default = ".") | |
' @return As Boolean - return true if type of obj equals AssociativeArray and obj contains all keys, else return false | |
'************************************************* | |
Function FW_ValidAA(obj As Object, keys as Object, delim = "." as String) as Boolean | |
'All keys on level 0 | |
if not FW_IsAssociativeArray(obj) or not FW_IsNotEmptyString(delim) or not FW_isArray(keys) then | |
return false | |
end if | |
for each key in Keys | |
subKeys = FW_Split(key, delim) | |
aa_check = obj | |
'go down the hierarchy key.subkey.subsubkey... | |
for each subkey in subKeys | |
if FW_IsAssociativeArray(aa_check) and aa_check[subkey] <> invalid then | |
aa_check = aa_check[subkey] | |
else | |
print "Key::"; key | |
print "subkey::"; subkey | |
printAA(obj) | |
return false | |
end if | |
end for | |
end for | |
return true | |
end Function | |
'************************************************* | |
' FW_GetSubElement - check if obj contains subElementTree element and return him | |
' @param element as Dynamic - roAssociativeArray | |
' @param subElementTree as Dynamic - String keys tree | |
' @param delim as String - key delimiter (default = ".") | |
' @return As Dynamic - if exist obj[subElementTree] value else invalid | |
'************************************************* | |
function FW_GetSubElement(element as Dynamic, subElementTree as Dynamic, delim = "." as String) as Dynamic | |
if FW_IsValid(element) = false or FW_IsValid(subElementTree) = false then | |
return invalid | |
end if | |
subElementTreeArray = [] | |
result = element | |
if FW_IsNotEmptyString(subElementTree) and FW_IsNotEmptyString(delim) then | |
subElementTreeArray = FW_Split(subElementTree, delim) | |
else if FW_IsArray(subElementTree) then | |
subElementTreeArray = subElementTree | |
else | |
result = invalid | |
end if | |
for each field in subElementTreeArray | |
if FW_IsSGNode(result) then | |
key = FW_AsString(field) | |
if result.HasField(key) then | |
result = result.GetField(key) | |
else | |
index = FW_AsInteger(field) | |
if (index = 0 and key <> "0") or index < 0 or index >= result.GetChildCount() then | |
result = invalid | |
exit for | |
end if | |
result = result.GetChild(index) | |
end if | |
else if FW_IsAssociativeArray(result) then | |
result = result.LookupCI(FW_AsString(field)) 'use case-insensitive lookup for AAs | |
else if FW_IsArray(result) then | |
index = FW_AsInteger(field) | |
if (index = 0 and FW_AsString(field) <> "0") or index < 0 or index >= result.Count() then 'index is not an integer or out of range? | |
result = invalid | |
exit for | |
end if | |
result = result[index] | |
else | |
result = invalid | |
exit for | |
end if | |
end for | |
return result | |
end function | |
' RALE_parseType - Parse type name to brs type | |
' @param {string} _type - type | |
' @return {string} - brs type | |
Function RALE_parseType(_type as String) as String | |
_type = LCase(_type) | |
convertMap = { | |
"rointeger" : "integer", | |
"roint" : "integer", | |
"int" : "integer", | |
"num" : "integer", | |
"number" : "integer", | |
"rofloat" : "float", | |
"rostring" : "string", | |
"text" : "string", | |
"str" : "string", | |
"roboolean" : "bool", | |
"boolean" : "bool", | |
"rosgnode" : "node", | |
"roarray" : "array", | |
"array" : "array", | |
"arr" : "array", | |
"object" : "assocarray", | |
"associativearray" : "assocarray", | |
"roassociativearray" : "assocarray" | |
} | |
if convertMap[_type] <> Invalid then return convertMap[_type] | |
return _type | |
End function | |
' RALE_convertToType - Convert variable to type | |
' @param {dynamic} value - variable value | |
' @param {string} _type - type to which you want to convert | |
' @return {dynamic} - new variable value | |
Function RALE_convertToType(value as Dynamic, _type as String) as Dynamic | |
_type = RALE_parseType(_type) | |
convertMap = { | |
"integer" : FW_AsInteger, | |
"float" : FW_AsFloat, | |
"bool" : FW_AsBoolean, | |
"string" : FW_AsString, | |
"array" : FW_AsArray, | |
} | |
if _type = "assocarray" and type(value) <> "roAssociativeArray" then | |
value = CreateObject("roAssociativeArray") | |
end if | |
if _type = "array" and type(value) <> "roArray" then | |
value = FW_AsString(value).split(",") | |
end if | |
if _type = "node" then | |
value = CreateObject("roSGNode", FW_AsString(value)) | |
end if | |
if convertMap[_type] <> Invalid then | |
return convertMap[_type](value) | |
end if | |
return value | |
End function | |
' RALE_getNodeByItem - Returns node field or child by item data | |
' @param {dynamic} item - item data | |
' @param {dynamic} node - type to which you want to convert | |
' @return {dynamic} - node field or child | |
Function RALE_getNodeByItem(item as Dynamic, node as Dynamic) as Dynamic | |
itemType = type(item) | |
if itemType <> "roAssociativeArray" then return Invalid | |
nodeType = type(node) | |
if nodeType <> "roSGNode" and nodeType <> "roArray" and nodeType <> "roAssociativeArray" then | |
return Invalid | |
end if | |
if nodeType = "roSGNode" and item.child <> Invalid then | |
index = FW_AsInteger(item.child) | |
if index = 0 and FW_AsString(item.child) <> "0" then return Invalid | |
return node.getChild(index) | |
else if item.field <> Invalid then | |
key = item.field | |
if nodeType = "roSGNode" then | |
if node.hasField(key) then | |
return node.getField(key) | |
else | |
return Invalid | |
end if | |
else | |
key = item.field | |
if nodeType = "roArray" then | |
key = FW_AsInteger(key) | |
if key = 0 and FW_AsString(item.field) <> "0" then return Invalid | |
end if | |
return node[key] | |
end if | |
else | |
return Invalid | |
end if | |
End function | |
' RALE_getNodeByPath - Returns node by path | |
' @param {object} root - root of nodes (main scene) | |
' @param {array} path - path to node (for example [{ child: 1 }, { field: "fieldId" }]) | |
' @return {dynamic} - node or Invalid (if no node by this path) | |
Function RALE_getNodeByPath(root as Dynamic, path as Object) as Dynamic | |
node = root | |
if type(path) <> "roArray" then return Invalid | |
for each item in path | |
node = RALE_getNodeByItem(item, node) | |
if node = Invalid then return Invalid | |
end for | |
return node | |
End function | |
' RALE_getNodeIndex - Returns node index | |
' @param {object} node - node | |
' @param {object} parent - node parent | |
' @return {integer} - node index | |
Function RALE_getNodeIndex(node, parent) as Integer | |
for i = 0 to parent.getchildCount() - 1 | |
if parent.getChild(i).isSameNode(node) then return i | |
end for | |
return -1 | |
End Function | |
' RALE_getNodePath - Returns node path | |
' @param {object} node - node | |
' @param {object} root - root | |
' @return {array} - node path | |
Function RALE_getNodePath(node, root) as Object | |
path = [] | |
if node <> Invalid and root <> Invalid then | |
parent = node.getParent() | |
while parent <> Invalid | |
index = RALE_getNodeIndex(node, parent) | |
if index = -1 then exit while | |
node = parent | |
parent = node.getParent() | |
path.unshift({ child: index }) | |
if root.isSameNode(node) then exit while | |
end while | |
end if | |
return path | |
End Function | |
' RALE_getNodeListByPath - Returns node by path | |
' @param {object} root - root of nodes (main scene) | |
' @param {array} path - path to node (for example [{ child: 1 }, { field: "fieldId" }]) | |
' @return {dynamic} - list of nodes (parent nodes and selected one) or Invalid (if no node by this path) | |
Function RALE_getNodeListByPath(root as Dynamic, path as Object) as Dynamic | |
node = root | |
nodeList = [node] | |
if type(path) <> "roArray" then return Invalid | |
for each item in path | |
node = RALE_getNodeByItem(item, node) | |
if node = Invalid then return Invalid | |
nodeList.push(node) | |
end for | |
return nodeList | |
End function | |
' RALE_getNodeIndexByPath - Returns node index or id by path | |
' @param {array} path - path to node (for example [{ child: 1 }, { field: "fieldId" }]) | |
' @return {dynamic} - node index or id or Invalid (if no node by this path) | |
Function RALE_getNodeIndexByPath(path) as Dynamic | |
if type(path) <> "roArray" then return Invalid | |
length = path.count() | |
if length = 0 then return "" | |
index = path[length - 1] | |
if type(index) <> "roAssociativeArray" then return Invalid | |
if index.child <> Invalid then | |
index = index.child | |
else if index.field <> Invalid then | |
index = index.field | |
else | |
return Invalid | |
end if | |
return index | |
End function | |
' RALE_splitStringByLength - Splits string on parts and store in array | |
' @param {string} str - string which will be splitted | |
' @param {integer} length - max part langth | |
' @return {array} - splitted string | |
Function RALE_splitStringByLength(str, length) as Dynamic | |
list = [] | |
strLength = str.len() | |
currentPosition = 0 | |
while currentPosition < strLength | |
list.push(str.mid(currentPosition, length)) | |
currentPosition = currentPosition + length | |
end while | |
return list | |
End function | |
' RALE_forEachNode - Goes through all the children and call the callback function | |
' @param {object} node - root node | |
' @param {object} options - options | |
' @param {function} callback - callback function that will be called for each node | |
Function RALE_forEachChild(node, options, callback, storage) as Dynamic | |
nodeType = type(node) | |
maxLevel = 50 | |
if FW_IsInteger(options.maxLevel) then | |
maxLevel = options.maxLevel | |
end if | |
if nodeType = "roSGNode" then | |
level = 0 | |
stateStack = [] | |
stateObj = { | |
index : 0 | |
node : node | |
} | |
childCount = node.getchildcount() | |
while true | |
if childCount > stateObj.index then | |
childIndex = stateObj.index | |
childNode = node.getchild(childIndex) | |
stateObj.index++ | |
if childNode <> invalid then | |
childCountTmp = childNode.getchildcount() | |
parentObj = stateObj | |
childObj = Invalid | |
if childCountTmp > 0 and level < maxLevel then | |
childCount = childCountTmp | |
stateStack.push(stateObj) | |
level++ | |
stateObj = { | |
index : 0 | |
node : childNode | |
} | |
childObj = stateObj | |
node = childNode | |
end if | |
result = callback(childNode, childIndex, parentObj, childObj, storage) | |
if not result then | |
exit while | |
end if | |
end if | |
else if level > 0 then | |
stateObj = stateStack.pop() | |
level-- | |
node = stateObj.node | |
childCount = stateObj.node.getchildcount() | |
else | |
exit while | |
end if | |
end while | |
else | |
return false | |
end if | |
return true | |
End function | |
' RALE_forEachRegistryField - Goes through all the roRegistry and call the callback function for each field | |
' @param {function} callback - callback function that will be called for each field | |
Function RALE_forEachRegistryField(callback, storage) as Dynamic | |
Registry = CreateObject("roRegistry") | |
for each section in Registry.GetSectionList() | |
RegistrySection = CreateObject("roRegistrySection", section) | |
for each key in RegistrySection.GetKeyList() | |
callback(RegistrySection.Read(key), key, section, storage) | |
end for | |
end for | |
End function | |
' RALE_forEachRegistrySection - Goes through all the roRegistry and call the callback function for each section | |
' @param {function} callback - callback function that will be called for each section | |
Function RALE_forEachRegistrySection(callback, storage) as Dynamic | |
Registry = CreateObject("roRegistry") | |
for each section in Registry.GetSectionList() | |
callback(CreateObject("roRegistrySection", section), section, storage) | |
end for | |
End function | |
Function RALE_DebugLog(message as String) | |
callCommand("UIThread_log", { | |
message: message, | |
status: "debug" | |
}) | |
End Function | |
Function RALE_InfoLog(message as String) | |
callCommand("UIThread_log", { | |
message: message, | |
status: "info" | |
}) | |
End Function | |
Function RALE_WarningLog(message as String) | |
callCommand("UIThread_log", { | |
message: message, | |
status: "warning" | |
}) | |
End Function | |
Function RALE_ErrorLog(message as String) | |
callCommand("UIThread_log", { | |
message: message, | |
status: "error" | |
}) | |
End Function | |
]]> | |
</script> | |
</component> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment