681 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
			
		
		
	
	
			681 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
| -- PLAYER_ENTERING_WORLD
 | |
| function(e)
 | |
| 	if ReactiveValue then return end
 | |
| 
 | |
| 	---@diagnostic disable: missing-return
 | |
| 	local function dumpTable(table, depth)
 | |
| 		if (depth > 200) then
 | |
| 			print("Error: Depth > 200 in dumpTable()")
 | |
| 			return
 | |
| 		end
 | |
| 		for k, v in pairs(table) do
 | |
| 			if (type(v) == "table") then
 | |
| 				print(string.rep("  ", depth) .. k .. ":")
 | |
| 				dumpTable(v, depth + 1)
 | |
| 			else
 | |
| 				print(string.rep("  ", depth) .. k .. ": ", v)
 | |
| 			end
 | |
| 		end
 | |
| 	end
 | |
| 
 | |
| 	local metadata = {
 | |
| 		---@param self ReactiveValue
 | |
| 		---@param other ReactiveValue
 | |
| 		---@return ReactiveValue|nil
 | |
| 		__add = function(self, other)
 | |
| 			local otherType = type(other)
 | |
| 			if otherType == "table" and other._type and other._type == self._type and other._value then
 | |
| 				return self._value + other._value
 | |
| 			end
 | |
| 			if otherType == "string" and self._type == otherType then
 | |
| 				return self._value .. other
 | |
| 			end
 | |
| 			if otherType == "number" and self._type == otherType then
 | |
| 				return self._value + other
 | |
| 			end
 | |
| 			return nil
 | |
| 		end,
 | |
| 		---@param self ReactiveValue
 | |
| 		---@param other ReactiveValue
 | |
| 		---@return ReactiveValue|nil
 | |
| 		__mul = function(self, other)
 | |
| 			local otherType = type(other)
 | |
| 			if otherType == "table" and other._type and other._type == self._type and other._value then
 | |
| 				return self._value * other._value
 | |
| 			end
 | |
| 			if otherType == "number" and self._type == otherType then
 | |
| 				return self._value * other
 | |
| 			end
 | |
| 			return nil
 | |
| 		end,
 | |
| 		---@param self ReactiveValue
 | |
| 		---@param other ReactiveValue
 | |
| 		---@return ReactiveValue|nil
 | |
| 		__sub = function(self, other)
 | |
| 			local otherType = type(other)
 | |
| 			if otherType == "table" and other._type and other._type == self._type and other._value then
 | |
| 				return self._value - other._value
 | |
| 			end
 | |
| 			if otherType == "number" and self._type == otherType then
 | |
| 				return self._value - other
 | |
| 			end
 | |
| 			return nil
 | |
| 		end,
 | |
| 		---@param self ReactiveValue
 | |
| 		---@param other ReactiveValue
 | |
| 		---@return ReactiveValue|nil
 | |
| 		__div = function(self, other)
 | |
| 			local otherType = type(other)
 | |
| 			if otherType == "table" and other._type and other._type == self._type and other._value then
 | |
| 				return self._value / other._value
 | |
| 			end
 | |
| 			if otherType == "number" and self._type == otherType then
 | |
| 				return self._value / other
 | |
| 			end
 | |
| 			return nil
 | |
| 		end,
 | |
| 		---@param self ReactiveValue
 | |
| 		---@param other ReactiveValue
 | |
| 		---@return ReactiveValue|nil
 | |
| 		__mod = function(self, other)
 | |
| 			local otherType = type(other)
 | |
| 			if otherType == "table" and other._type and other._type == self._type and other._value then
 | |
| 				return self._value % other._value
 | |
| 			end
 | |
| 			if otherType == "number" and self._type == otherType then
 | |
| 				return self._value % other
 | |
| 			end
 | |
| 			return nil
 | |
| 		end,
 | |
| 		---@param self ReactiveValue
 | |
| 		---@param other ReactiveValue
 | |
| 		---@return ReactiveValue|nil
 | |
| 		__pow = function(self, other)
 | |
| 			local otherType = type(other)
 | |
| 			if otherType == "table" and other._type and other._type == self._type and other._value then
 | |
| 				return self._value ^ other._value
 | |
| 			end
 | |
| 			if otherType == "number" and self._type == otherType then
 | |
| 				return self._value ^ other
 | |
| 			end
 | |
| 			return nil
 | |
| 		end,
 | |
| 		---@param self ReactiveValue
 | |
| 		---@param other ReactiveValue
 | |
| 		---@return boolean
 | |
| 		__eq = function(self, other)
 | |
| 			local otherType = type(other)
 | |
| 			if otherType == "table" and other._type and other._type == self._type and other._value then
 | |
| 				return self._value == other._value
 | |
| 			end
 | |
| 			return self._value == other
 | |
| 		end,
 | |
| 		---@param self ReactiveValue
 | |
| 		---@param other ReactiveValue
 | |
| 		---@return boolean
 | |
| 		__lt = function(self, other)
 | |
| 			local otherType = type(other)
 | |
| 			if otherType == "table" and other._type and other._type == self._type and other._value then
 | |
| 				return self._value < other._value
 | |
| 			end
 | |
| 			return self._value < other
 | |
| 		end,
 | |
| 		---@param self ReactiveValue
 | |
| 		---@param other ReactiveValue
 | |
| 		---@return boolean
 | |
| 		__le = function(self, other)
 | |
| 			local otherType = type(other)
 | |
| 			if otherType == "table" and other._type and other._type == self._type and other._value then
 | |
| 				return self._value <= other._value
 | |
| 			end
 | |
| 			return self._value <= other
 | |
| 		end,
 | |
| 		---@param self ReactiveValue
 | |
| 		---@param other ReactiveValue
 | |
| 		---@return boolean
 | |
| 		__gt = function(self, other)
 | |
| 			local otherType = type(other)
 | |
| 			if otherType == "table" and other._type and other._type == self._type and other._value then
 | |
| 				return self._value > other._value
 | |
| 			end
 | |
| 			return self._value > other
 | |
| 		end,
 | |
| 		---@param self ReactiveValue
 | |
| 		---@param other ReactiveValue
 | |
| 		---@return boolean
 | |
| 		__ge = function(self, other)
 | |
| 			local otherType = type(other)
 | |
| 			if otherType == "table" and other._type and other._type == self._type and other._value then
 | |
| 				return self._value >= other._value
 | |
| 			end
 | |
| 			return self._value >= other
 | |
| 		end,
 | |
| 		---@param self ReactiveValue
 | |
| 		---@return number
 | |
| 		__len = function(self)
 | |
| 			if self._type == "table" then
 | |
| 				return #self._value
 | |
| 			end
 | |
| 			if self._type == "string" then
 | |
| 				return string.len(self._value)
 | |
| 			end
 | |
| 			return 0
 | |
| 		end,
 | |
| 		---@param self ReactiveValue
 | |
| 		---@return string
 | |
| 		__tostring = function(self)
 | |
| 			return tostring(self._value)
 | |
| 		end,
 | |
| 		---@param self ReactiveValue
 | |
| 		---@param key string
 | |
| 		---@param value any
 | |
| 		---@return nil
 | |
| 		__newindex = function(self, key, value)
 | |
| 			local setupComplete = rawget(self, "_setupComplete")
 | |
| 			if setupComplete == nil or setupComplete == false then
 | |
| 				rawset(self, key, value)
 | |
| 				return
 | |
| 			end
 | |
| 			if self._type ~= "table" then
 | |
| 				rawset(self, key, value)
 | |
| 				return
 | |
| 			end
 | |
| 
 | |
| 			self._value[key] = value
 | |
| 			local ChangedKey = { key }
 | |
| 
 | |
| 			-- If the value being assigned is a ReactiveValue
 | |
| 			-- Then listen to changes on it as well
 | |
| 			-- And propagate those changes upwards
 | |
| 			if self._recursive and getmetatable(value) == getmetatable(self) then
 | |
| 				self:_setupListeners(key, value)
 | |
| 			end
 | |
| 
 | |
| 			self:_notify()
 | |
| 			self:_notifyFieldChanged(ChangedKey)
 | |
| 			self:_notifyAnyFieldChanged(ChangedKey)
 | |
| 		end,
 | |
| 		---@param self ReactiveValue
 | |
| 		---@param key string
 | |
| 		---@return any|nil
 | |
| 		__index = function(self, key)
 | |
| 			local value = rawget(self, key)
 | |
| 			if value ~= nil then
 | |
| 				return value
 | |
| 			end
 | |
| 			if rawget(self, "_type") ~= "table" then
 | |
| 				return nil
 | |
| 			end
 | |
| 			local innerTable = rawget(self, "_value")
 | |
| 			if innerTable ~= nil then
 | |
| 				return rawget(innerTable, key)
 | |
| 			end
 | |
| 			return nil
 | |
| 		end
 | |
| 		-- __index = ReactiveValue
 | |
| 	}
 | |
| 
 | |
| 	--- Sadly I could not get @generic to play nice with this class
 | |
| 	--- I think it's not ready yet, there are issues on github describing similar problems and it is marked as WIP...
 | |
| 	--- Guess I'll have to live without it for now and specify type of a RV in #type
 | |
| 
 | |
| 	---## A type safe value that can be listened to for changes
 | |
| 	---### **Always use RV:set() for setting primitive values**
 | |
| 	--- Supports primitive values and tables<br>
 | |
| 	--- Tables can be listened to for changes on any field or a specific field<br>
 | |
| 	---### Example usage (value):<br>
 | |
| 	--- ```lua
 | |
| 	--- local test = ReactiveValue.new(1)
 | |
| 	--- test:onChange(function(value)
 | |
| 	---     print("test changed to " .. value)
 | |
| 	--- end)
 | |
| 	--- test:set(2)
 | |
| 	--- test:set(test + 3)
 | |
| 	--- ```
 | |
| 	---### Example usage (table):<br>
 | |
| 	--- ```lua
 | |
| 	--- local test = ReactiveValue.new({1, 2, 3})
 | |
| 	--- test:onAnyFieldChange(function(field, value)
 | |
| 	---     print(string.format("test.%s changed to %s", table.concat(field, "."), value))
 | |
| 	--- end)
 | |
| 	--- test[1] = 4 -- test.1 changed to 4
 | |
| 	--- test[4] = {1, 2, 3} -- test.4 changed to <table>
 | |
| 	--- test[4][1] = 14 -- No log(!!) because test[4] is a table and not a ReactiveValue
 | |
| 	--- ```
 | |
| 	---### To trigger a callback for `test[4][1]` in the previous example do:<br>
 | |
| 	--- ```lua
 | |
| 	--- local test = ReactiveValue.new({1, 2, 3}, true)
 | |
| 	--- test:onAnyFieldChange(function(field, value)
 | |
| 	---     print(string.format("test.%s changed to %s", table.concat(field, "."), value))
 | |
| 	--- end)
 | |
| 	--- test[1] = 4 -- test.1 changed to 4
 | |
| 	--- test[4] = {1, 2, 3} -- test.4 changed to <table>
 | |
| 	--- test[4][1] = 14 -- test.4.1 changed to 14
 | |
| 	--- ```
 | |
| 	---### To listen to a specific field of a table do:<br>
 | |
| 	--- ```lua
 | |
| 	--- local test = ReactiveValue.new({1, 2, 3}, true)
 | |
| 	--- test:onFieldChange("1", function(value)
 | |
| 	---     print("test.1 changed to " .. value)
 | |
| 	--- end)
 | |
| 	--- test[1] = 4 -- test.1 changed to 4
 | |
| 	--- test[4] = {1, 2, 3} -- Does not trigger callback
 | |
| 	-- ```
 | |
| 	---@class ReactiveValue
 | |
| 	---@field _listeners table<function, boolean>
 | |
| 	---@field _fieldListeners table<string, table<function, boolean>>
 | |
| 	---@field _anyFieldListeners table<number, table<function, boolean>>
 | |
| 	---@field _oneTimeListeners table<function, boolean>
 | |
| 	---@field _value any
 | |
| 	---@field _type string
 | |
| 	---@field _recursive boolean?
 | |
| 	ReactiveValue = {
 | |
| 		---#### Get the underlying value of a ReactiveValue
 | |
| 		---@param self ReactiveValue
 | |
| 		---@return any
 | |
| 		get = function(self) end,
 | |
| 
 | |
| 		---### Set the underlying value of a ReactiveValue triggering listener callbacks
 | |
| 		---@param self ReactiveValue
 | |
| 		---@param newValue any
 | |
| 		set = function(self, newValue) end,
 | |
| 
 | |
| 		---## EVENT
 | |
| 		---### Register a listener that is triggered whenever the underlying value changes
 | |
| 		--- Returns a function that can be called to undo the callback
 | |
| 		---@param self ReactiveValue
 | |
| 		---@param callback fun(value: any, type: string)
 | |
| 		---@return fun(): nil
 | |
| 		onChange = function(self, callback) end,
 | |
| 
 | |
| 		---## EVENT
 | |
| 		---### Register a listener that is triggered whenever a specific field of a table changes
 | |
| 		--- Returns a function that can be called to undo the callback
 | |
| 		---@param self ReactiveValue
 | |
| 		---@param field string
 | |
| 		---@param callback fun(field: string[], value: any, type: string)
 | |
| 		---@return fun(): nil
 | |
| 		onFieldChange = function(self, field, callback) end,
 | |
| 
 | |
| 		---## EVENT
 | |
| 		---### Register a listener that is triggered whenever any field of a table changes
 | |
| 		--- Returns a function that can be called to undo the callback
 | |
| 		---@param self ReactiveValue
 | |
| 		---@param callback fun(field: string[], value: any, type: string)
 | |
| 		---@param depth number? How deep to listen for changes
 | |
| 		---@return fun(): nil
 | |
| 		onAnyFieldChange = function(self, callback, depth) end,
 | |
| 
 | |
| 		---## EVENT
 | |
| 		---### Register a listener that is triggered ONCE whenever the underlying value changes
 | |
| 		--- Returns a function that can be called to undo the callback
 | |
| 		---@param self ReactiveValue
 | |
| 		---@param callback fun(value: any, type: string)
 | |
| 		---@return fun(): nil
 | |
| 		once = function(self, callback) end,
 | |
| 		---### Setup listeners for all fields of a table recursively
 | |
| 		--- This is used to ensure that listeners are notified recursively
 | |
| 
 | |
| 		---@param self ReactiveValue
 | |
| 		_setupAllListenersRecursively = function(self) end,
 | |
| 		---### Setup listeners for a specific field of a table recursively
 | |
| 		--- This is used to ensure that listeners are notified recursively
 | |
| 
 | |
| 		---@param self ReactiveValue
 | |
| 		_setupListeners = function(self, key, value, recursive) end,
 | |
| 
 | |
| 		---### Notify listeners that the underlying value has changed
 | |
| 		---@param self ReactiveValue
 | |
| 		---@return nil
 | |
| 		---#### Event contains:
 | |
| 		--- 2. value: any - The new value of the changed field
 | |
| 		--- 3. type: string - The type of the new value of the changed field
 | |
| 		_notify = function(self) end,
 | |
| 
 | |
| 		---### Notify listeners that a specific field of the underlying value has changed
 | |
| 		---#### Event contains:
 | |
| 		--- 1. field: table<string> - A list of keys that lead to the changed field
 | |
| 		--- 2. value: any - The new value of the changed field
 | |
| 		--- 3. type: string - The type of the new value of the changed field
 | |
| 		---@param self ReactiveValue
 | |
| 		_notifyFieldChanged = function(self, field) end,
 | |
| 
 | |
| 		---### Notify listeners that any field of the underlying value has changed
 | |
| 		---#### Event contains:
 | |
| 		--- 1. field: table<string> - A list of keys that lead to the changed field
 | |
| 		--- 2. value: any - The new value of the changed field
 | |
| 		--- 3. type: string - The type of the new value of the changed field
 | |
| 		_notifyAnyFieldChanged = function(self, field) end,
 | |
| 	}
 | |
| 	---### Constructor
 | |
| 	---@param initialValue any
 | |
| 	---@param recursive boolean?
 | |
| 	---@return ReactiveValue
 | |
| 	ReactiveValue.new = function(initialValue, recursive)
 | |
| 		local self = setmetatable({}, metadata)
 | |
| 		self._listeners = {}
 | |
| 		self._fieldListeners = {}
 | |
| 		self._anyFieldListeners = {}
 | |
| 		self._oneTimeListeners = {}
 | |
| 		self._value = initialValue
 | |
| 		self._type = type(initialValue)
 | |
| 		self._recursive = recursive or false
 | |
| 
 | |
| 		---@return any
 | |
| 		self.get = function(self)
 | |
| 			return self._value
 | |
| 		end
 | |
| 		---@param newValue any
 | |
| 		self.set = function(self, newValue)
 | |
| 			if self._value == newValue then
 | |
| 				return
 | |
| 			end
 | |
| 			if type(newValue) ~= self._type then
 | |
| 				error("Expected " .. self._type .. ", got " .. type(newValue))
 | |
| 				return
 | |
| 			end
 | |
| 			self._value = newValue
 | |
| 			self:_notify()
 | |
| 		end
 | |
| 		self.onChange = function(self, callback)
 | |
| 			if type(callback) ~= "function" then
 | |
| 				error("Expected function, got " .. type(callback))
 | |
| 				return function() end
 | |
| 			end
 | |
| 			self._listeners[callback] = true
 | |
| 			return function()
 | |
| 				self._listeners[callback] = nil
 | |
| 			end
 | |
| 		end
 | |
| 		self.onFieldChange = function(self, field, callback)
 | |
| 			if type(callback) ~= "function" then
 | |
| 				error("Expected function, got " .. type(callback))
 | |
| 				return function() end
 | |
| 			end
 | |
| 			if self._fieldListeners[field] == nil then
 | |
| 				self._fieldListeners[field] = {}
 | |
| 			end
 | |
| 			self._fieldListeners[field][callback] = true
 | |
| 			return function()
 | |
| 				self._fieldListeners[field][callback] = nil
 | |
| 			end
 | |
| 		end
 | |
| 		self.onAnyFieldChange = function(self, callback, depth)
 | |
| 			depth = depth or 99999
 | |
| 			if type(callback) ~= "function" then
 | |
| 				error("Expected function, got " .. type(callback))
 | |
| 				return function() end
 | |
| 			end
 | |
| 			if self._anyFieldListeners[depth] == nil then
 | |
| 				self._anyFieldListeners[depth] = {}
 | |
| 			end
 | |
| 			self._anyFieldListeners[depth][callback] = true
 | |
| 			return function()
 | |
| 				self._anyFieldListeners[depth][callback] = nil
 | |
| 			end
 | |
| 		end
 | |
| 		self.once = function(self, callback)
 | |
| 			if type(callback) ~= "function" then
 | |
| 				error("Expected function, got " .. type(callback))
 | |
| 				return function() end
 | |
| 			end
 | |
| 			self._oneTimeListeners[callback] = true
 | |
| 			return function()
 | |
| 				self._oneTimeListeners[callback] = nil
 | |
| 			end
 | |
| 		end
 | |
| 
 | |
| 		self._setupAllListenersRecursively = function(self)
 | |
| 			if self._type ~= "table" then
 | |
| 				return
 | |
| 			end
 | |
| 			for key, value in pairs(self._value) do
 | |
| 				self:_setupListeners(key, value, true)
 | |
| 			end
 | |
| 		end
 | |
| 		---@param key string
 | |
| 		---@param value any
 | |
| 		---@param recursive boolean?
 | |
| 		self._setupListeners = function(self, key, value, recursive)
 | |
| 			recursive = recursive or false
 | |
| 			if self._type ~= "table" then
 | |
| 				return
 | |
| 			end
 | |
| 			if getmetatable(value) ~= getmetatable(self) then
 | |
| 				return
 | |
| 			end
 | |
| 			value._recursive = true
 | |
| 			if value._type == "table" then
 | |
| 				value:onAnyFieldChange(function(key2)
 | |
| 					ChangedKey = { key, table.unpack(key2) }
 | |
| 					self:_notifyFieldChanged(ChangedKey)
 | |
| 					self:_notifyAnyFieldChanged(ChangedKey)
 | |
| 				end)
 | |
| 			else
 | |
| 				value:onChange(function(newVal)
 | |
| 					ChangedKey = { key }
 | |
| 					self:_notifyFieldChanged(ChangedKey)
 | |
| 					self:_notifyAnyFieldChanged(ChangedKey)
 | |
| 				end)
 | |
| 			end
 | |
| 
 | |
| 			if recursive then
 | |
| 				value:_setupAllListenersRecursively()
 | |
| 			end
 | |
| 		end
 | |
| 
 | |
| 		if recursive then
 | |
| 			self:_setupAllListenersRecursively()
 | |
| 		end
 | |
| 
 | |
| 		self._notify = function(self)
 | |
| 			for listener, _ in pairs(self._oneTimeListeners) do
 | |
| 				-- task.spawn(listener, self._value, self._type)
 | |
| 				listener(self._value, self._type)
 | |
| 				self._oneTimeListeners[listener] = nil
 | |
| 			end
 | |
| 			for listener, _ in pairs(self._listeners) do
 | |
| 				-- task.spawn(listener, self._value, self._type)
 | |
| 				listener(self._value, self._type)
 | |
| 			end
 | |
| 		end
 | |
| 		-- TODO: Maybe implement some sort of regex here or something...
 | |
| 		-- Such as listening to *.field1 or something
 | |
| 		-- But this (having to loop over listeners and evaluate some condition) would tank performance
 | |
| 		-- Compared to a simple lookup
 | |
| 		-- So I'm not going to do anything about it for now, until I figure out a better way
 | |
| 		---@param field table<string, string> A list of keys that lead to the changed field
 | |
| 		---@return nil
 | |
| 		self._notifyFieldChanged = function(self, field)
 | |
| 			local value = self._value
 | |
| 			for _, key in ipairs(field) do
 | |
| 				value = value[key]
 | |
| 			end
 | |
| 
 | |
| 			local strfield = table.concat(field, ".")
 | |
| 			if self._fieldListeners[strfield] == nil then
 | |
| 				return
 | |
| 			end
 | |
| 			for listener, _ in pairs(self._fieldListeners[strfield]) do
 | |
| 				-- task.spawn(listener, value, type(value))
 | |
| 				listener(value, type(value))
 | |
| 			end
 | |
| 		end
 | |
| 		---@param self ReactiveValue
 | |
| 		---@param field table<string, string> A list of keys that lead to the changed field
 | |
| 		---@return nil
 | |
| 		self._notifyAnyFieldChanged = function(self, field)
 | |
| 			local value = self._value
 | |
| 			for _, key in ipairs(field) do
 | |
| 				value = value[key]
 | |
| 			end
 | |
| 			local keyDepth = #field
 | |
| 			for listenerDepth, listeners in pairs(self._anyFieldListeners) do
 | |
| 				if listenerDepth >= keyDepth then
 | |
| 					for listener, _ in pairs(listeners) do
 | |
| 						-- The reason this also returns type(value) is so that clients don't have to compute type(value)
 | |
| 						-- I assume some of them might want to do it so computing it once is probably better than having every client compute it for themselves
 | |
| 						-- task.spawn(listener, field, value, type(value))
 | |
| 						listener(field, value, type(value))
 | |
| 					end
 | |
| 				end
 | |
| 			end
 | |
| 		end
 | |
| 
 | |
| 		self._setupComplete = true
 | |
| 		return self
 | |
| 	end
 | |
| 
 | |
| 	_G["ReactiveValue"] = ReactiveValue
 | |
| 
 | |
| 	-- S -- begintest
 | |
| 	-- S local invocations = 0
 | |
| 	-- S -- Integer example
 | |
| 	-- S local test = ReactiveValue.new(1)
 | |
| 	-- S test:onChange(function(value)
 | |
| 	-- S     invocations = invocations + 1
 | |
| 	-- S     print("test changed to " .. value)
 | |
| 	-- S end)
 | |
| 	-- S test:set(2)
 | |
| 	-- S assert(invocations == 1)
 | |
| 	-- S
 | |
| 	-- S invocations = 0
 | |
| 	-- String example
 | |
| 	-- S test = ReactiveValue.new("test")
 | |
| 	-- S test:onChange(function(value)
 | |
| 	-- S     invocations = invocations + 1
 | |
| 	-- S     print("test changed to " .. value)
 | |
| 	-- S end)
 | |
| 	-- S test:set("test2")
 | |
| 	-- S assert(invocations == 1)
 | |
| 	-- S
 | |
| 	-- S -- Type safety example
 | |
| 	-- S local res, err = pcall(test.set, test, 1)
 | |
| 	-- S assert(res == false)
 | |
| 	-- S assert(err:find("Expected string, got number"))
 | |
| 	-- S
 | |
| 	-- S -- Table example
 | |
| 	-- S invocations = 0
 | |
| 	-- S test = ReactiveValue.new({1, 2, 3})
 | |
| 	-- S local clbk = test:onChange(function(value)
 | |
| 	-- S     invocations = invocations + 1
 | |
| 	-- S     print("test changed to")
 | |
| 	-- S     dumpTable(value, 0)
 | |
| 	-- S end)
 | |
| 	-- S test:set({1, 2, 3, 4})
 | |
| 	-- S assert(invocations == 1)
 | |
| 	-- S
 | |
| 	-- S -- Callback removal example
 | |
| 	-- S clbk()
 | |
| 	-- S
 | |
| 	-- S invocations = 0
 | |
| 	-- S -- Any field change example
 | |
| 	-- S clbk = test:onAnyFieldChange(function(field, value)
 | |
| 	-- S     invocations = invocations + 1
 | |
| 	-- S     print("test." .. table.concat(field, ".") .. " changed to " .. tostring(value))
 | |
| 	-- S end)
 | |
| 	-- S test.Pero = 1
 | |
| 	-- S test.Pero = nil
 | |
| 	-- S assert(invocations == 2)
 | |
| 	-- S clbk()
 | |
| 	-- S
 | |
| 	-- S invocations = 0
 | |
| 	-- S -- Field change example
 | |
| 	-- S test:onFieldChange("Pero", function(value)
 | |
| 	-- S     invocations = invocations + 1
 | |
| 	-- S     print("test.Pero changed to " .. value)
 | |
| 	-- S end)
 | |
| 	-- S test.Pero = 2
 | |
| 	-- S assert(invocations == 1)
 | |
| 	-- S
 | |
| 	-- S invocations = 0
 | |
| 	-- S -- One time listener example
 | |
| 	-- S test:once(function(value)
 | |
| 	-- S     invocations = invocations + 1
 | |
| 	-- S     print("test changed to")
 | |
| 	-- S     dumpTable(value, 0)
 | |
| 	-- S end)
 | |
| 	-- S test:set({3, 2, 1})
 | |
| 	-- S assert(invocations == 1)
 | |
| 	-- S
 | |
| 	-- S invocations = 0
 | |
| 	-- S -- Table push example
 | |
| 	-- S test = ReactiveValue.new({})
 | |
| 	-- S test:onChange(function(value)
 | |
| 	-- S     invocations = invocations + 1
 | |
| 	-- S     print("test changed to")
 | |
| 	-- S     dumpTable(value, 0)
 | |
| 	-- S end)
 | |
| 	-- S test:onAnyFieldChange(function(field, value)
 | |
| 	-- S     invocations = invocations + 1
 | |
| 	-- S     print("test." .. table.concat(field, ".") .. " changed to " .. value)
 | |
| 	-- S end)
 | |
| 	-- S test[#test + 1] = 4
 | |
| 	-- S assert(invocations == 2)
 | |
| 	-- S
 | |
| 	-- S invocations = 0
 | |
| 	-- S test = ReactiveValue.new({
 | |
| 	-- S     name = "pero",
 | |
| 	-- S     coins = ReactiveValue.new(1)
 | |
| 	-- S })
 | |
| 	-- S test.coins:onChange(function(value)
 | |
| 	-- S     invocations = invocations + 1
 | |
| 	-- S     print("test.coins changed to " .. value)
 | |
| 	-- S end)
 | |
| 	-- S test.coins:set(2)
 | |
| 	-- S assert(invocations == 1)
 | |
| 	-- S
 | |
| 	-- S invocations = 0
 | |
| 	-- S test = ReactiveValue.new({
 | |
| 	-- S     name = "pero",
 | |
| 	-- S     coins = ReactiveValue.new(1)
 | |
| 	-- S }, true)
 | |
| 	-- S test:onAnyFieldChange(function(field, value)
 | |
| 	-- S     invocations = invocations + 1
 | |
| 	-- S     print("test." .. table.concat(field, ".") .. " changed to " .. tostring(value))
 | |
| 	-- S end)
 | |
| 	-- S test.coins:set(2)
 | |
| 	-- S test.pero2 = ReactiveValue.new({})
 | |
| 	-- S test.pero2.coins = ReactiveValue.new(1)
 | |
| 	-- S test.pero2.coins:set(2)
 | |
| 	-- S assert(invocations == 4)
 | |
| 	-- S
 | |
| 	-- S invocations = 0
 | |
| 	-- S test = ReactiveValue.new({
 | |
| 	-- S     name = "pero",
 | |
| 	-- S     coins = ReactiveValue.new({
 | |
| 	-- S         value = ReactiveValue.new(1)
 | |
| 	-- S     })
 | |
| 	-- S }, true)
 | |
| 	-- S test:onAnyFieldChange(function(field, value)
 | |
| 	-- S     invocations = invocations + 1
 | |
| 	-- S     print("test." .. table.concat(field, ".") .. " changed to " .. tostring(value))
 | |
| 	-- S end)
 | |
| 	-- S test.coins.value:set(2)
 | |
| 	-- S assert(invocations == 1)
 | |
| 	-- S
 | |
| 	-- S invocations = 0
 | |
| 	-- S test = ReactiveValue.new({}, true)
 | |
| 	-- S test.coins = ReactiveValue.new({})
 | |
| 	-- S test.coins.value = ReactiveValue.new(1)
 | |
| 	-- S test:onAnyFieldChange(function(field, value)
 | |
| 	-- S     invocations = invocations + 1
 | |
| 	-- S     print("test." .. table.concat(field, ".") .. " changed to " .. tostring(value))
 | |
| 	-- S end)
 | |
| 	-- S test.coins.value:set(3)
 | |
| 	-- S assert(invocations == 1)
 | |
| 	--S
 | |
| 	--S invocations = 0
 | |
| 	--S test = ReactiveValue.new({}, true)
 | |
| 	--S test:onAnyFieldChange(function(field, value)
 | |
| 	--S     invocations = invocations + 1
 | |
| 	--S     print("test." .. table.concat(field, ".") .. " changed to " .. tostring(value))
 | |
| 	--S end, 1)
 | |
| 	--S test.test2 = ReactiveValue.new({}, true)
 | |
| 	--S test.test2.test3 = ReactiveValue.new(1)
 | |
| 	--S assert(invocations == 1)
 | |
| 	--S
 | |
| 	-- S -- endtest
 | |
| 
 | |
| 	-- return ReactiveValue
 | |
| end |