Module:Test

From Resonite Wiki
Revision as of 03:51, 25 January 2024 by Epsilion (talk | contribs)

Documentation for this module may be created at Module:Test/doc

-- Package definition to return to Scribunto - this allows us to define methods that can be called
-- when this module is targeted.
local p = {}

-- Type colors - RGB values to be included in the HTML output when a color is needed. This will be replaced with CSS at some point.
local typeColor = 
{
	User = '255, 128, 255',
	Impulse = '179, 255, 255',
	bool = '115, 115, 115',
	AsyncImpulse = '204, 179, 255',
	String = '245, 31, 31',
	Dummy = '255, 0, 255',
	IFormatProvider = '168, 143, 214',
	float = '0, 255, 255',
	ColorProfile = '255, 196, 54',
	colorX = '255, 89, 0',
	Component = '112, 76, 85',
	float3 = '0, 255, 255',
	float2 = '0, 255, 255',
    ulong = '0, 255, 127',
    int = '0, 255, 0',
    Continuation = '255, 255, 178',
    Slot = '191, 255, 127',
	WebsocketClient = '191, 242, 176'
}

--- Method that generates the HTML output
--	@param frame A Scribunto frame instance. frame.args contains the parameters passed into the module call
function p.GenerateUI( frame )
	-- Parse the JSON input, returning an empty array if no argument was passed
	local inputs = mw.text.jsonDecode(frame.args.Inputs or '[]')
	local outputs = mw.text.jsonDecode(frame.args.Outputs or '[]')
	local globals = mw.text.jsonDecode(frame.args.Globals or '[]')
	local minimal = frame.args.Minimal
	
	-- Create an HTML div element to contain our node UI
	local protofluxContainer = mw.html.create( 'div' )
	protofluxContainer
	    :attr('class', frame.args.Inline and '' or 'floatright')
		:cssText('color: rgb(224, 224, 224); background-color: rgb(18, 20, 28); width: 256px; display: grid; grid-template-columns: [input] 30px [label] 1fr [output] 30px; grid-template-rows: repeat(20, 35px 35px);')

	
	local processedInputs = {}
	local processedOutputs = {}
	
	for i=1,#inputs do
		for j=1,(inputs and inputs[i].Multi or 1) do
			inputs[i].MultiIndex = j
			table.insert(processedInputs, { Name=inputs[i].Name, Type=inputs[i].Type, Multi=inputs[i].Multi, MultiIndex=j })
		end
	end
	
	for i=1,#outputs do
		for j=1,(outputs and outputs[i].Multi or 1) do
			table.insert(processedOutputs, { Name=outputs[i].Name, Type=outputs[i].Type, Multi=outputs[i].Multi, MultiIndex=j })
		end
	end
	
	-- Calculate the larger number of rows required (either for inputs or outputs, if the node is asymmetric)
	local maxRows = math.max(#processedInputs, #processedOutputs)
	
	if not minimal then
		protofluxContainer
			:tag('div') -- HTML div to contain the node title
				:cssText('padding: 10px 0px 10px 0px; text-align:center; font-weight: bold; background-color: rgb(26, 41, 54); font-size: 18pt;  grid-column: input / output; grid-row: 1;')
				:wikitext(frame.args.Name)
				:done() -- Close node title div
	else
		protofluxContainer
			:tag('div') -- HTML div to contain the node title
				:cssText('text-align:center; font-weight: bold; font-size: 18pt;  grid-column: input / output; grid-row: 1/'.. ((maxRows - 1) * 2) + 3 ..'; align-self: center;')
				:wikitext(frame.args.Name)
				:done() -- Close node title div
	end
	-- Iterate over each row, and populate it with the inputs/outputs. If the node is asymmetric,
	-- the value passed for either input or output might be nil.
	
	for i=1,maxRows do
		CreateConnectorRow(i, protofluxContainer, processedInputs[i], processedOutputs[i], minimal)
	end
	
	-- Iterate over each global value in the node, and create a row. These elements take up the entire width
	-- of the node, and so don't need to be balanced in any way.
	for i=1,#globals do
		CreateGlobalsRow(protofluxContainer, globals[i])
	end
	
	protofluxContainer
		:tag('div') -- HTML div to contain the node category footer
			:cssText('text-align: center; padding: 10px; font-size: 18pt; color: rgb(64,64,64); font-weight: bold; grid-row:' .. ((maxRows) * 2 + 1) .. '; grid-column: input/output')
			:wikitext(frame.args.Category)
			:done() -- Close category footer div
	
	-- Return the HTML generated above to the wiki page this script is invoked from.
	return tostring(protofluxContainer) .. '[[Category:ProtoFlux:All]]'
end

--- Creates a new ProtoFlux connector row in the output
-- @param Container The Scribunto HTML node we'll be creating this row inside of. Should be a container of some sort.
-- @param Input Table containing a Name and Type for the input on this row. Can be nil if no input is to be placed on this row.
-- @param Output Table containing a Name and Type for the output on this row. Can be nil if no output is to be placed on this row.
function CreateConnectorRow(Index, Container, Input, Output, Minimal)

	
	-- Create the input (left) attachement point
	CreateConnectorAttachmentPoint(Index, Container, Input, true)
	

	local c = Container
	if Minimal then
	else
		if Input and Input.Multi and Input.MultiIndex == 1 then
			c
				:tag('div') -- HTML div to contain the input label
					:attr('title', Input and (Input.Name .. ' <' .. Input.Type .. '>') or '') -- Add a basic mouseover description
					:cssText('text-align: left; overflow: hidden; text-overflow: ellipsis; padding-left: 4px;  border-right: 20px solid #11151d; border-bottom: 4px solid #11151d; border-top: 4px solid #11151d; background-color: transparent;')
					:wikitext(Input and Input.Name or '' )
					:done() -- Close input label div
		elseif Input and Input.Multi and Input.MultiIndex > 1 then
			local c2 = c
				:tag('div') -- HTML div to contain the input label
					:attr('title', Input and (Input.Name .. ' <' .. Input.Type .. '>') or '') -- Add a basic mouseover description
					:cssText('text-align: left; padding-left: 4px;  border-right: 20px solid #11151d; border-bottom: 4px solid #11151d; border-top: 4px solid #11151d; background-color: transparent;')
			if Input.Multi == Input.MultiIndex then
				c2
					:tag('div')
						:cssText('position:relative; top:2rem; display:flex; gap: 4px;')
						:tag('div')
							:cssText('font-size: 1.75rem; width: 1em; height: 1em; background-color:grey; border-radius: 2em; display:flex; align-items:center; justify-content:center;')
							:wikitext('+')
							:done()
						:tag('div')
							:cssText('font-size: 1.75rem; width: 1em; height: 1em; background-color:grey; border-radius: 2em; display:flex; align-items:center; justify-content:center;')
							:wikitext('-')
							:done()
			end
		else
			c
				:tag('div') -- HTML div to contain the input label
					:attr('title', Input and (Input.Name .. ' <' .. Input.Type .. '>') or '') -- Add a basic mouseover description
					:cssText('text-align: left; overflow: hidden; text-overflow: ellipsis; padding-left: 4px;  border-right: 20px solid #11151d; border-bottom: 4px solid #11151d; border-top: 4px solid #11151d; background-color:' .. GetTypeColor(Input, 0.6) .. '; grid-column: label; grid-row: ' .. Index .. ';')
					:wikitext(Input and Input.Name or '' )
					:done() -- Close input label div
		end
		if Output and Output.Multi and Output.MultiIndex == 1 then
			c
				:tag('div') -- HTML div to contain the output label
					:attr('title', Output and (Output.Name .. ' <' .. Output.Type .. '>') or '') -- Add a basic mouseover description
					:cssText('text-align: right; overflow: hidden; text-overflow: ellipsis; padding-right: 4px; border-left: 20px solid #11151d; border-bottom: 4px solid #11151d; border-top: 4px solid #11151d; background-color: transparent;')
					:wikitext(Output and Output.Name or '')
					:done() -- Close output label div
		elseif Output and Output.Multi and Output.MultiIndex > 1 then
			local c2 = c
				:tag('div') -- HTML div to contain the output label
					:attr('title', Output and (Output.Name .. ' <' .. Output.Type .. '>') or '') -- Add a basic mouseover description
					:cssText('text-align: right; padding-right: 4px; border-left: 20px solid #11151d; border-bottom: 4px solid #11151d; border-top: 4px solid #11151d; background-color: transparent;;')
			if Output.Multi == Output.MultiIndex then
				c2
					:tag('div')
						:cssText('display:flex; justify-content:flex-end; gap: 4px;')
						:tag('div')
							:cssText('font-size: 1.75rem; width: 1em; height: 1em; background-color:grey; border-radius: 2em; display:flex; align-items:center; justify-content:center;')
							:wikitext('+')
							:done()
						:tag('div')
							:cssText('font-size: 1.75rem; width: 1em; height: 1em; background-color:grey; border-radius: 2em; display:flex; align-items:center; justify-content:center;')
							:wikitext('-')
							:done()
			end
		else
			c
			:tag('div') -- HTML div to contain the output label
				:attr('title', Output and (Output.Name .. ' <' .. Output.Type .. '>') or '') -- Add a basic mouseover description
				:cssText('text-align: right; overflow: hidden; text-overflow: ellipsis; padding-right: 4px; border-left: 20px solid #11151d; border-bottom: 4px solid #11151d; border-top: 4px solid #11151d; background-color: ' .. GetTypeColor(Output, 0.6) .. '; grid-column: label; grid-row: ' .. (((Index - 1) * 2) + 1) .. ';')
				:wikitext(Output and Output.Name or '')
				:done() -- Close output label div
		end
	end
	-- Create the output (right) attachment point
	CreateConnectorAttachmentPoint(Index, Container, Output, false);
end

--- Creates a new ProtoFlux global input field row in the output
--	@param Container The Scribunto HTML node we'll be creating this row inside of. Should be a container of some sort.
--	@param Global Table containing a Name and Type for the global field on this row.
function CreateGlobalsRow(Container, Global)
	Container
		:tag( 'div' )
			:attr('title', Global.Name .. ' <' .. Global.Type .. '>') -- Add a basic mouseover description
			:cssText('display: flex; min-height: 70px; flex-direction: column; border-left: 10px solid ' .. GetTypeColor(Global, 1.0) .. ';')
			:tag( 'div' )
				:cssText('display: flex; flex-direction: row; align-items: center; flex-grow:1; overflow: hidden;')
				:tag( 'span' )
					:cssText('text-align: center; overflow: hidden; text-overflow: ellipsis; font-size: 14pt; font-weight: bold; flex-grow:1;')
					:wikitext(Global.Name)
					:done()
				:done()
			:tag( 'div' )
				:attr('style', 'display:flex; gap: 10px; flex-grow: 1;')
				:tag('div')
					:cssText('border-radius: 16px; background-color: #777; font-style: italic; text-align:center; flex-grow: 3; display: flex; flex-direction: column; justify-content: center;')
					:tag( 'span' )
						:wikitext('null')
						:done()
					:done()
				:tag( 'div')
					:cssText('border-radius: 16px; background-color: #333; text-align:center; flex-grow: 1; display: flex; flex-direction: column; justify-content: center;')
					:tag ( 'span' )
						:wikitext('∅')   
end

--- Creates a new input or output attachement point for a connector row.
--	@param Container The Scribunto HTML node we'll be creating this row inside of. Should be a container of some sort.
--	@param Connector Table containing a Name (Optional) and Type (Required) for the input/output attachment point.
--	@param isInput	True if this is an input side attachment point, false if it is an output side attachment point.
function CreateConnectorAttachmentPoint(Index, Container, Connector, isInput)
	if Connector and (Connector.Type == "Impulse" or Connector.Type == "AsyncImpulse" or Connector.Type == "Continuation")  then
		Container
			:tag( 'div' )
				:attr('title', Connector and (Connector.Name .. ' <' .. Connector.Type .. '>') or '') -- Add a basic mouseover description
				:cssText('background-color: black; fill: ' .. GetTypeColor(Connector, 0.3) .. '; stroke: ' .. GetTypeColor(Connector, 1.0) .. '; grid-column: ' .. (isInput and 'input' or 'output') .. '; grid-row: ' .. (((Index - 1) * 2) + 1) .. '/' .. (((Index - 1) * 2) + 3) .. ';')
				:wikitext(mw.getCurrentFrame():expandTemplate({title = 'ProtoFluxConnector', args = {Type="InputArrow"}}))

	elseif Connector then
		Container
			:tag( 'div' )
				:attr('title', Connector and (Connector.Name .. ' <' .. Connector.Type .. '>') or '') -- Add a basic mouseover description
				:cssText('background-color: black; fill: ' .. GetTypeColor(Connector, 0.3) .. '; stroke: ' .. GetTypeColor(Connector, 1.0) .. '; grid-column: ' .. (isInput and 'input' or 'output') .. '; grid-row: ' .. (((Index - 1) * 2) + 1)  .. '/' .. (((Index - 1) * 2) + 3) .. ';')
				:wikitext(mw.getCurrentFrame():expandTemplate({title = 'ProtoFluxConnector', args = {Type=(isInput and "InputBox" or "OutputBox")}}))
	end
end

--- Returns an RGBA value representing the type color within Resonite
-- @param Connector Table containing a Name (Optional) and Type (Required) for the input/output attachment point.
-- @param Alpha The alpha to use in the RGBA value. 
function GetTypeColor(Connector, Alpha)
	return (Connector and 'rgba(' .. (typeColor[Connector.Type] or '0, 0, 0') .. ',' .. Alpha .. ')' or 'rgba(0, 0, 0, 0)')
end

return p