aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authors-ol <s-ol@users.noreply.github.com>2018-10-19 03:12:28 +0000
committers-ol <s-ol@users.noreply.github.com>2018-10-19 03:12:28 +0000
commitc002284371f076997cae8b36dead35ae12da02ec (patch)
tree82033a9a0987012f928c11534b5fe0ea2d7db082
parenttowards self-compiling (diff)
downloadmmm-c002284371f076997cae8b36dead35ae12da02ec.tar.gz
mmm-c002284371f076997cae8b36dead35ae12da02ec.zip
fix all pages
-rw-r--r--Tupfile2
-rw-r--r--app/center_of_mass.moon324
-rw-r--r--app/index.moon23
-rw-r--r--app/test_component.moon411
-rw-r--r--app/todo.moon59
-rw-r--r--app/twisted.moon68
-rw-r--r--client.moon1
7 files changed, 441 insertions, 447 deletions
diff --git a/Tupfile b/Tupfile
index 8e86c17..a7630c7 100644
--- a/Tupfile
+++ b/Tupfile
@@ -1,4 +1,4 @@
.gitignore
-: foreach app/index.moon app/realities.moon |> moon render.moon %B > %o |> dist/%B.html
+: foreach app/* |> moon render.moon %B > %o |> dist/%B.html
: foreach lib/*.client.moon lib/*.shared.moon |> moonc -o %o %f |> dist/lib/%B.lua
diff --git a/app/center_of_mass.moon b/app/center_of_mass.moon
index 6e81cd2..845dd97 100644
--- a/app/center_of_mass.moon
+++ b/app/center_of_mass.moon
@@ -1,167 +1,165 @@
-window = js.global
-document = window.document
-
-import CanvasApp from require 'lib.canvasapp'
-import rgb from require 'lib.color'
-import h1, p, div, span, input, button from require 'lib.html'
-
-fast = true
-center = true
-center_char = do
- canvas = document\createElement 'canvas'
- ctx = canvas\getContext '2d'
-
- cache = {}
-
- (char, font, height) ->
- name = "#{char} #{height} #{font}"
- return table.unpack cache[name] if cache[name]
-
- ctx\resetTransform!
- ctx.font = "#{height}px #{font}"
- width = (ctx\measureText char).width
- canvas.width, canvas.height = width, height * 1.2
-
- ctx.font = "#{height}px #{font}"
- ctx.textBaseline = 'top'
- ctx.fillStyle = rgb 0, 0, 0
- ctx\fillText char, 0, 0
-
- data = ctx\getImageData 0, 0, width, height * 1.2
-
- local xx, yy
- if fast
- loop = window\eval '(function(data) {
- var xx = 0, yy = 0, n = 0;
- for (var x = 0; x < data.width - 1; x++) {
- for (var y = 0; y < data.height - 1; y++) {
- var i = y * (data.width * 4) + x * 4;
- var alpha = data.data[i + 3] / 255;
- xx += x * alpha;
- yy += y * alpha;
- n += alpha;
+on_client ->
+ import CanvasApp from require 'lib.canvasapp'
+ import rgb from require 'lib.color'
+ import h1, p, div, span, input, button from require 'lib.html'
+
+ fast = true
+ center = true
+ center_char = do
+ canvas = document\createElement 'canvas'
+ ctx = canvas\getContext '2d'
+
+ cache = {}
+
+ (char, font, height) ->
+ name = "#{char} #{height} #{font}"
+ return table.unpack cache[name] if cache[name]
+
+ ctx\resetTransform!
+ ctx.font = "#{height}px #{font}"
+ width = (ctx\measureText char).width
+ canvas.width, canvas.height = width, height * 1.2
+
+ ctx.font = "#{height}px #{font}"
+ ctx.textBaseline = 'top'
+ ctx.fillStyle = rgb 0, 0, 0
+ ctx\fillText char, 0, 0
+
+ data = ctx\getImageData 0, 0, width, height * 1.2
+
+ local xx, yy
+ if fast
+ loop = window\eval '(function(data) {
+ var xx = 0, yy = 0, n = 0;
+ for (var x = 0; x < data.width - 1; x++) {
+ for (var y = 0; y < data.height - 1; y++) {
+ var i = y * (data.width * 4) + x * 4;
+ var alpha = data.data[i + 3] / 255;
+ xx += x * alpha;
+ yy += y * alpha;
+ n += alpha;
+ }
}
- }
-
- xx /= n;
- yy /= n;
- return [xx, yy];
- })'
- res = loop nil, data
- xx, yy = res[0], res[1]
- else
- xx, yy, n = 0, 0, 0
- for x = 0, data.width - 1
- for y = 0, data.height - 1
- i = y * (data.width * 4) + x * 4
- alpha = data.data[i + 3] / 255
- xx += x * alpha
- yy += y * alpha
- n += alpha
-
- xx /= n
- yy /= n
- cache[name] = { xx, yy, width }
- xx, yy, width
-
-class CenterOfMass extends CanvasApp
- width: window.innerWidth - 20
- height: 300
- new: (text, @font, @size) =>
- super!
- @text = {}
- for i = 1,#text
- @add text\sub i, i
-
- add: (char) =>
- rcx, rcy, w = center_char char, @font, @size
- cx, cy = w/2, @size/2
- vx, vy = 0, 0
- table.insert @text, {
- :char, :rcx, :rcy, :w
- :cx, :cy, :vx, :vy
+
+ xx /= n;
+ yy /= n;
+ return [xx, yy];
+ })'
+ res = loop nil, data
+ xx, yy = res[0], res[1]
+ else
+ xx, yy, n = 0, 0, 0
+ for x = 0, data.width - 1
+ for y = 0, data.height - 1
+ i = y * (data.width * 4) + x * 4
+ alpha = data.data[i + 3] / 255
+ xx += x * alpha
+ yy += y * alpha
+ n += alpha
+
+ xx /= n
+ yy /= n
+ cache[name] = { xx, yy, width }
+ xx, yy, width
+
+ class CenterOfMass extends CanvasApp
+ width: window.innerWidth - 20
+ height: 300
+ new: (text, @font, @size) =>
+ super!
+ @text = {}
+ for i = 1,#text
+ @add text\sub i, i
+
+ add: (char) =>
+ rcx, rcy, w = center_char char, @font, @size
+ cx, cy = w/2, @size/2
+ vx, vy = 0, 0
+ table.insert @text, {
+ :char, :rcx, :rcy, :w
+ :cx, :cy, :vx, :vy
+ }
+
+ refresh: =>
+ for char in *@text
+ char.rcx, char.rcy, char.w = center_char char.char, @font, @size
+
+ keydown: (key) =>
+ if key == "Backspace" or key == "Delete"
+ table.remove @text
+ elseif string.len(key) == 1
+ @add key
+
+ update: (dt) =>
+ super dt
+
+ ACCEL = 4 * dt
+ DAMPING = 8 * dt
+
+ for char in *@text
+ { :rcx, :rcy, :cx, :cy, :w } = char
+ if not center
+ rcx, rcy = w/2, @size/2
+ dx, dy = rcx - cx, rcy - cy
+ char.vx += dx * ACCEL
+ char.vy += dy * ACCEL
+ char.cx += char.vx
+ char.cy += char.vy
+ char.vx *= DAMPING
+ char.vy *= DAMPING
+
+ draw: =>
+ @ctx\clearRect 0, 0, @width, @height
+
+ @ctx.font = "#{@size}px #{@font}"
+ @ctx.textBaseline = 'top'
+
+ x, y = @size * .1, @size
+ for { :char, :cx, :cy, :w } in *@text
+ if x + w > @width
+ x = 0
+ y += @size * 1.2
+
+ @ctx\fillText char, x + w/2 - cx, y - cy
+ x += w
+
+ append h1 'Fonts aligned by Center-of-Mass'
+ app = CenterOfMass "Click here and type Away!", "Times New Roman", 40
+ append app.canvas
+ app.canvas.style.backgroundColor = '#eee'
+
+ add = =>
+ append div {
+ span 'font: ',
+ with @font_input = input!
+ .type = 'text'
+ .value = 'Times New Roman'
+ with button 'set'
+ .onclick = (_, e) ->
+ app.font = @font_input.value
+ app\refresh!
}
- refresh: =>
- for char in *@text
- char.rcx, char.rcy, char.w = center_char char.char, @font, @size
-
- keydown: (key) =>
- if key == "Backspace" or key == "Delete"
- table.remove @text
- elseif string.len(key) == 1
- @add key
-
- update: (dt) =>
- super dt
-
- ACCEL = 4 * dt
- DAMPING = 8 * dt
-
- for char in *@text
- { :rcx, :rcy, :cx, :cy, :w } = char
- if not center
- rcx, rcy = w/2, @size/2
- dx, dy = rcx - cx, rcy - cy
- char.vx += dx * ACCEL
- char.vy += dy * ACCEL
- char.cx += char.vx
- char.cy += char.vy
- char.vx *= DAMPING
- char.vy *= DAMPING
-
- draw: =>
- @ctx\clearRect 0, 0, @width, @height
-
- @ctx.font = "#{@size}px #{@font}"
- @ctx.textBaseline = 'top'
-
- x, y = @size * .1, @size
- for { :char, :cx, :cy, :w } in *@text
- if x + w > @width
- x = 0
- y += @size * 1.2
-
- @ctx\fillText char, x + w/2 - cx, y - cy
- x += w
-
-append h1 'Fonts aligned by Center-of-Mass'
-app = CenterOfMass "Click here and type Away!", "Times New Roman", 40
-append app.canvas
-app.canvas.style.backgroundColor = '#eee'
-
-add = =>
- append div {
- span 'font: ',
- with @font_input = input!
- .type = 'text'
- .value = 'Times New Roman'
- with button 'set'
- .onclick = (_, e) ->
- app.font = @font_input.value
+ append div {
+ span 'size: ',
+ input type: 'range', min: 2, max: 120, value: 40, onchange: (_, e) ->
+ size = e.target.value
+ @size_label.innerText = size
+ app.size = size
app\refresh!
- }
-
- append div {
- span 'size: ',
- input type: 'range', min: 2, max: 120, value: 40, onchange: (_, e) ->
- size = e.target.value
- @size_label.innerText = size
- app.size = size
- app\refresh!
- with @size_label = span '40'
- ''
- }
-
- append div {
- span 'center characters by weight: ',
- input type: 'checkbox', checked: center, onchange: (_, e) ->
- center = e.target.checked
- }
-
- append div {
- span 'optimize inner loop: ',
- input type: 'checkbox', checked: fast, onchange: (_, e) ->
- fast = e.target.checked
- }
-add {}
+ with @size_label = span '40'
+ ''
+ }
+
+ append div {
+ span 'center characters by weight: ',
+ input type: 'checkbox', checked: center, onchange: (_, e) ->
+ center = e.target.checked
+ }
+
+ append div {
+ span 'optimize inner loop: ',
+ input type: 'checkbox', checked: fast, onchange: (_, e) ->
+ fast = e.target.checked
+ }
+ add {}
diff --git a/app/index.moon b/app/index.moon
index 3f4e80c..3e79c60 100644
--- a/app/index.moon
+++ b/app/index.moon
@@ -1,5 +1,3 @@
-require = relative ...
-
on_client ->
import h1, p, a, br, ul, tt, li from require 'lib.html'
@@ -8,22 +6,21 @@ on_client ->
'twisted': 'canvas animation',
'todo': 'Todo demo of a simple reactive UI framework',
'realities': 'draft of a paper on virtual and other realities',
- 'center-of-mass': 'aligning characters by their centers of mass',
- 'test-component': 'Test suite for the UI framework',
- 'play-tags': 'Playground for Functional Tags',
+ 'center_of_mass': 'aligning characters by their centers of mass',
+ 'test_component': 'Test suite for the UI framework',
+ 'tags': 'Playground for Functional Tags',
+
+ redirs =
+ 'center-of-mass': 'center_of_mass',
+ 'test-component': 'test_component',
+ 'play-tags': 'tags',
back_button = ->
append p a { '< back', href: '/' }
if window.location.search and #window.location.search > 1
name = window.location.search\sub 2
- if demos[name]
- back_button!
- filename = name\gsub '-', '_'
- require "app.#{filename}"
- else
- append h1 'are you lost?'
- append p a { '(go home)', href: '/' }
+ window.location.href = "#{redirs[name] or name}.html"
else
append h1 'mmm'
append p {
@@ -40,7 +37,7 @@ on_client ->
}
append p 'current demos:'
append ul for name, desc in pairs demos
- li (a name, href: "/?#{name}"), ': ', desc
+ li (a name, href: "/#{name}.html"), ': ', desc
append p {
"made with #{moon} by "
diff --git a/app/test_component.moon b/app/test_component.moon
index 78556a2..dba511b 100644
--- a/app/test_component.moon
+++ b/app/test_component.moon
@@ -1,205 +1,206 @@
-import div, h1, ul, li, pre from require 'lib.html'
-
-last = nil
-test_group = (name) ->
- if last
- append div (h1 name), ul last
-
- last = {}
- (name, test) ->
- ok, err = pcall test
- table.insert last, li if ok
- "passed '#{name}'"
- else
- "failed '#{name}'", pre err
-
-expect = (expected, note, ...) ->
- ok, msg = pcall ...
- print ok, msg\find expected
- if ok or not msg\find expected
- error note
-
-run_test = test_group 'component.moon'
-
-local ReactiveVar, ReactiveElement
-run_test "exports ReactiveVar, ReactiveElement", ->
- import ReactiveVar, ReactiveElement from require 'app.component'
- assert ReactiveVar, "ReactiveVar not exported"
- assert ReactiveElement, "ReactiveElement not exported"
-
-run_test "exports tohtml helper", ->
- import tohtml from require 'app.component'
- assert 'function' == (type tohtml), "tohtml not exported"
-
-run_test "exports append helper", ->
- import append from require 'app.component'
- assert 'function' == (type append), "append not exported"
-
-run_test "exports text helper", ->
- import text from require 'app.component'
- assert 'function' == (type text), "text not exported"
-
- node = text 'a test string'
- assert (js.instanceof node, js.global.Node), "expected text to generate a Node"
- assert node.data == 'a test string', "expected text to store the string"
-
-run_test "text joins multiple arguments", ->
- import text from require 'app.component'
-
- node = text 'a', 'test', 'string'
- assert node.data == 'a test string', "expected text to join arguments with spaces"
-
-run_test = test_group 'ReactiveVar'
-
-run_test "stores a value", ->
- reactive = ReactiveVar 'test'
- assert 'test' == reactive\get!, "expected x to be 'test'"
-
-run_test "propagates updates", ->
- local done
-
- reactive = ReactiveVar 'test'
- reactive\subscribe coroutine.wrap (next) ->
- assert next == 'toast', "expected next to be 'toast'"
- assert coroutine.yield! == 'cheese', "expected next to be 'cheese'"
- done = true
-
- reactive\set 'toast'
- assert 'toast' == reactive\get!, "expected #get to return 'toast'"
- reactive\set 'cheese'
- assert done, "expected to reach the end"
-
-run_test "passed old value as well", ->
- local done
-
- reactive = ReactiveVar 1
- reactive\subscribe coroutine.wrap (next, last) ->
- assert last == 1, "expected last:1 to be 1"
- next, last = coroutine.yield!
- assert last == 2, "expected last:2 to be 2"
- done = true
-
- reactive\set 2
- reactive\set 3
- assert done, "expected to reach the end"
-
-run_test "provides transform shorthand", ->
- local done
-
- reactive = ReactiveVar 1
- reactive\subscribe coroutine.wrap (next, last) ->
- assert last == 1, "expected last:1 to be 1"
- next, last = coroutine.yield!
- assert last == 2, "expected last:2 to be 2"
- done = true
-
- add_one = (a) -> a + 1
- reactive\transform add_one
- reactive\transform add_one
- assert done, "expected to reach the end"
-
-run_test "#subscribe returns function to unsubscribe", ->
- calls = 0
-
- reactive = ReactiveVar 1
- unsub = reactive\subscribe coroutine.wrap () ->
- calls += 1
- coroutine.yield!
- calls += 1
- coroutine.yield!
- calls += 1
-
- assert 'function' == (type unsub), "expected to receive a function"
-
- reactive\set 2
- reactive\set 3
- assert calls == 2, "wat"
-
- unsub!
- reactive\set 4
- assert calls == 2, "expected to stop receiving updates"
-
-run_test "tracks multiple subscriptions at once", ->
- reactive = ReactiveVar 'test'
- unsub = reactive\subscribe coroutine.wrap (next) ->
- assert next == 'toast', "expected next to be toast"
- next = coroutine.yield!
- assert next == 'cheese', "expected next to be cheese"
- coroutine.yield!
- error "expected not to get here"
-
- reactive\set 'toast'
-
- local done
- reactive\subscribe coroutine.wrap (next) ->
- assert next == 'cheese', "expected next to be cheese"
- next = coroutine.yield!
- assert next == 'test', "expected next to be test"
- done = true
-
- reactive\set 'cheese'
- unsub!
- reactive\set 'test'
- assert done, "expected to reach the end"
-
-run_test = test_group 'ReactiveElement'
-
-run_test "creates a HTML element", ->
- elem = ReactiveElement 'span'
- assert elem.node and elem.node.localName == 'span', "expected Node to be a <span>"
-
-run_test "sets attributes from a table arg", ->
- elem = ReactiveElement 'span', class: 'never'
- assert elem.node.class == 'never', "expected class to be 'never'"
-
-run_test "appends Nodes from arguments", ->
- e_div, e_pre = div!, pre!
- elem = ReactiveElement 'span', e_div, e_pre
- assert elem.node.firstElementChild == e_div, "expected div to be the first child of elem"
- assert elem.node.lastElementChild == e_pre, "expected pre to be the last child of elem"
-
-run_test "can append ReactiveElements and text", ->
- e_div = ReactiveElement 'div'
- elem = ReactiveElement 'div', e_div, 'testtext'
- assert elem.node.firstElementChild == e_div.node, "expected div to be the first child of elem"
- assert elem.node.lastChild.data == 'testtext', "expected last child of elem to be 'testtext'"
-
-run_test "accepts attributes after children", ->
- e_div = div!
- elem = ReactiveElement 'div', e_div, class: 'test'
- assert elem.node.firstElementChild == e_div, "expected div to be the first child of elem"
- assert elem.node.class == 'test', "expected class to be 'test'"
-
-run_test "allows mixing attributes and children in a single table", ->
- e_div, e_pre = div!, pre!
- elem = ReactiveElement 'div', { class: 'test', e_div, e_pre }
- assert elem.node.firstElementChild == e_div, "expected div to be the first child of elem"
- assert elem.node.lastElementChild == e_pre, "expected pre to be the last child of elem"
- assert elem.node.class == 'test', "expected class to be 'test'"
-
-run_test "can unwrap and track attributes from ReactiveVars", ->
- klass = ReactiveVar 'test'
- elem = ReactiveElement 'div', class: klass
- assert elem.node.class == 'test', "expected class to be 'test'"
- klass\set 'toast'
- assert elem.node.class == 'toast', "expected class to be 'toast'"
-
-run_test "can unwrap and track children from ReactiveVars", ->
- child = ReactiveVar h1 'test'
- elem = ReactiveElement 'div', child, pre 'fixed'
- assert elem.node.firstElementChild.localName == 'h1', "expected first child to be h1"
- assert elem.node.childElementCount == 2, "expected node to have two children"
- child\set div 'toast'
- assert elem.node.firstElementChild.localName == 'div', "expected first child to be div"
- assert elem.node.childElementCount == 2, "expected node to have two children"
-
-run_test "warns when appending a string from a ReactiveVar", ->
- import text from require 'app.component'
-
- str = ReactiveVar 'test'
- elem = ReactiveElement 'div', str
- expect 'cannot replace string node', 'expected error', str\set, 'string too'
- elem\destroy!
-
- elem = ReactiveElement 'div', str\map text
- str\set 'this is text'
+on_client ->
+ import div, h1, ul, li, pre from require 'lib.html'
+
+ last = nil
+ test_group = (name) ->
+ if last
+ append div (h1 name), ul last
+
+ last = {}
+ (name, test) ->
+ ok, err = pcall test
+ table.insert last, li if ok
+ "passed '#{name}'"
+ else
+ "failed '#{name}'", pre err
+
+ expect = (expected, note, ...) ->
+ ok, msg = pcall ...
+ print ok, msg\find expected
+ if ok or not msg\find expected
+ error note
+
+ run_test = test_group 'component.moon'
+
+ local ReactiveVar, ReactiveElement
+ run_test "exports ReactiveVar, ReactiveElement", ->
+ import ReactiveVar, ReactiveElement from require 'lib.component'
+ assert ReactiveVar, "ReactiveVar not exported"
+ assert ReactiveElement, "ReactiveElement not exported"
+
+ run_test "exports tohtml helper", ->
+ import tohtml from require 'lib.component'
+ assert 'function' == (type tohtml), "tohtml not exported"
+
+ run_test "exports append helper", ->
+ import append from require 'lib.component'
+ assert 'function' == (type append), "append not exported"
+
+ run_test "exports text helper", ->
+ import text from require 'lib.component'
+ assert 'function' == (type text), "text not exported"
+
+ node = text 'a test string'
+ assert (js.instanceof node, js.global.Node), "expected text to generate a Node"
+ assert node.data == 'a test string', "expected text to store the string"
+
+ run_test "text joins multiple arguments", ->
+ import text from require 'lib.component'
+
+ node = text 'a', 'test', 'string'
+ assert node.data == 'a test string', "expected text to join arguments with spaces"
+
+ run_test = test_group 'ReactiveVar'
+
+ run_test "stores a value", ->
+ reactive = ReactiveVar 'test'
+ assert 'test' == reactive\get!, "expected x to be 'test'"
+
+ run_test "propagates updates", ->
+ local done
+
+ reactive = ReactiveVar 'test'
+ reactive\subscribe coroutine.wrap (next) ->
+ assert next == 'toast', "expected next to be 'toast'"
+ assert coroutine.yield! == 'cheese', "expected next to be 'cheese'"
+ done = true
+
+ reactive\set 'toast'
+ assert 'toast' == reactive\get!, "expected #get to return 'toast'"
+ reactive\set 'cheese'
+ assert done, "expected to reach the end"
+
+ run_test "passed old value as well", ->
+ local done
+
+ reactive = ReactiveVar 1
+ reactive\subscribe coroutine.wrap (next, last) ->
+ assert last == 1, "expected last:1 to be 1"
+ next, last = coroutine.yield!
+ assert last == 2, "expected last:2 to be 2"
+ done = true
+
+ reactive\set 2
+ reactive\set 3
+ assert done, "expected to reach the end"
+
+ run_test "provides transform shorthand", ->
+ local done
+
+ reactive = ReactiveVar 1
+ reactive\subscribe coroutine.wrap (next, last) ->
+ assert last == 1, "expected last:1 to be 1"
+ next, last = coroutine.yield!
+ assert last == 2, "expected last:2 to be 2"
+ done = true
+
+ add_one = (a) -> a + 1
+ reactive\transform add_one
+ reactive\transform add_one
+ assert done, "expected to reach the end"
+
+ run_test "#subscribe returns function to unsubscribe", ->
+ calls = 0
+
+ reactive = ReactiveVar 1
+ unsub = reactive\subscribe coroutine.wrap () ->
+ calls += 1
+ coroutine.yield!
+ calls += 1
+ coroutine.yield!
+ calls += 1
+
+ assert 'function' == (type unsub), "expected to receive a function"
+
+ reactive\set 2
+ reactive\set 3
+ assert calls == 2, "wat"
+
+ unsub!
+ reactive\set 4
+ assert calls == 2, "expected to stop receiving updates"
+
+ run_test "tracks multiple subscriptions at once", ->
+ reactive = ReactiveVar 'test'
+ unsub = reactive\subscribe coroutine.wrap (next) ->
+ assert next == 'toast', "expected next to be toast"
+ next = coroutine.yield!
+ assert next == 'cheese', "expected next to be cheese"
+ coroutine.yield!
+ error "expected not to get here"
+
+ reactive\set 'toast'
+
+ local done
+ reactive\subscribe coroutine.wrap (next) ->
+ assert next == 'cheese', "expected next to be cheese"
+ next = coroutine.yield!
+ assert next == 'test', "expected next to be test"
+ done = true
+
+ reactive\set 'cheese'
+ unsub!
+ reactive\set 'test'
+ assert done, "expected to reach the end"
+
+ run_test = test_group 'ReactiveElement'
+
+ run_test "creates a HTML element", ->
+ elem = ReactiveElement 'span'
+ assert elem.node and elem.node.localName == 'span', "expected Node to be a <span>"
+
+ run_test "sets attributes from a table arg", ->
+ elem = ReactiveElement 'span', class: 'never'
+ assert elem.node.class == 'never', "expected class to be 'never'"
+
+ run_test "appends Nodes from arguments", ->
+ e_div, e_pre = div!, pre!
+ elem = ReactiveElement 'span', e_div, e_pre
+ assert elem.node.firstElementChild == e_div, "expected div to be the first child of elem"
+ assert elem.node.lastElementChild == e_pre, "expected pre to be the last child of elem"
+
+ run_test "can append ReactiveElements and text", ->
+ e_div = ReactiveElement 'div'
+ elem = ReactiveElement 'div', e_div, 'testtext'
+ assert elem.node.firstElementChild == e_div.node, "expected div to be the first child of elem"
+ assert elem.node.lastChild.data == 'testtext', "expected last child of elem to be 'testtext'"
+
+ run_test "accepts attributes after children", ->
+ e_div = div!
+ elem = ReactiveElement 'div', e_div, class: 'test'
+ assert elem.node.firstElementChild == e_div, "expected div to be the first child of elem"
+ assert elem.node.class == 'test', "expected class to be 'test'"
+
+ run_test "allows mixing attributes and children in a single table", ->
+ e_div, e_pre = div!, pre!
+ elem = ReactiveElement 'div', { class: 'test', e_div, e_pre }
+ assert elem.node.firstElementChild == e_div, "expected div to be the first child of elem"
+ assert elem.node.lastElementChild == e_pre, "expected pre to be the last child of elem"
+ assert elem.node.class == 'test', "expected class to be 'test'"
+
+ run_test "can unwrap and track attributes from ReactiveVars", ->
+ klass = ReactiveVar 'test'
+ elem = ReactiveElement 'div', class: klass
+ assert elem.node.class == 'test', "expected class to be 'test'"
+ klass\set 'toast'
+ assert elem.node.class == 'toast', "expected class to be 'toast'"
+
+ run_test "can unwrap and track children from ReactiveVars", ->
+ child = ReactiveVar h1 'test'
+ elem = ReactiveElement 'div', child, pre 'fixed'
+ assert elem.node.firstElementChild.localName == 'h1', "expected first child to be h1"
+ assert elem.node.childElementCount == 2, "expected node to have two children"
+ child\set div 'toast'
+ assert elem.node.firstElementChild.localName == 'div', "expected first child to be div"
+ assert elem.node.childElementCount == 2, "expected node to have two children"
+
+ run_test "warns when appending a string from a ReactiveVar", ->
+ import text from require 'lib.component'
+
+ str = ReactiveVar 'test'
+ elem = ReactiveElement 'div', str
+ expect 'cannot replace string node', 'expected error', str\set, 'string too'
+ elem\destroy!
+
+ elem = ReactiveElement 'div', str\map text
+ str\set 'this is text'
diff --git a/app/todo.moon b/app/todo.moon
index 2173087..5a1aa47 100644
--- a/app/todo.moon
+++ b/app/todo.moon
@@ -1,33 +1,34 @@
-import ReactiveVar, append, text, div, form, span, h3, a, input, textarea, button from require 'lib.component'
+on_client ->
+ import ReactiveVar, append, text, div, form, span, h3, a, input, textarea, button from require 'lib.component'
-parent = div!
-todoItem = (desc, done) ->
- -- convert into reactive data sources
- desc, done = (ReactiveVar desc), ReactiveVar done
- with me = div style:
- margin: '8px'
- padding: '8px'
- background: '#eeeeee'
- \append h3 (desc\map text), style: 'margin: 0;'
- \append span done\map (done) -> text if done then 'done' else 'not done yet'
- \append input type: 'checkbox', checked: done, onchange: (e) => done\set e.target.checked
- \append a (text 'delete'), href: '#', onclick: (e) => parent\remove me
+ parent = div!
+ todoItem = (desc, done) ->
+ -- convert into reactive data sources
+ desc, done = (ReactiveVar desc), ReactiveVar done
+ with me = div style:
+ margin: '8px'
+ padding: '8px'
+ background: '#eeeeee'
+ \append h3 (desc\map text), style: 'margin: 0;'
+ \append span done\map (done) -> text if done then 'done' else 'not done yet'
+ \append input type: 'checkbox', checked: done, onchange: (e) => done\set e.target.checked
+ \append a (text 'delete'), href: '#', onclick: (e) => parent\remove me
-parent\append todoItem 'write a Component System'
-parent\append todoItem 'eat Lasagna', true
+ parent\append todoItem 'write a Component System'
+ parent\append todoItem 'eat Lasagna', true
-desc = ReactiveVar 'start'
-form = with form {
- action: ''
- style:
- margin: '2px'
- onsubmit: (e) =>
- e\preventDefault!
- parent\append todoItem desc\get!
- desc\set ''
- }
- \append input type: 'text', value: desc, onchange: (e) => desc\set e.target.value
- \append input type: 'submit', value: 'add'
+ desc = ReactiveVar 'start'
+ form = with form {
+ action: ''
+ style:
+ margin: '2px'
+ onsubmit: (e) =>
+ e\preventDefault!
+ parent\append todoItem desc\get!
+ desc\set ''
+ }
+ \append input type: 'text', value: desc, onchange: (e) => desc\set e.target.value
+ \append input type: 'submit', value: 'add'
-append parent
-append form
+ append parent
+ append form
diff --git a/app/twisted.moon b/app/twisted.moon
index 5c34914..e90eda3 100644
--- a/app/twisted.moon
+++ b/app/twisted.moon
@@ -1,41 +1,39 @@
-window = js.global
-document = window.document
+on_client ->
+ Math = window.Math
-Math = window.Math
+ import CanvasApp from require 'lib.canvasapp'
+ import hsl from require 'lib.color'
-import CanvasApp from require 'lib.canvasapp'
-import hsl from require 'lib.color'
+ class TwistedDemo extends CanvasApp
+ width: 500
+ height: 400
+ length: math.pi * 4
+ new: =>
+ super!
+ @background = {Math.random!, Math.random!/3+.2, Math.random!/4}
+ hue = Math.random!
+ @shades = setmetatable {}, __index: (key) =>
+ with val = { hue, .7, key * .3 + .1} do rawset @, key, val
-class TwistedDemo extends CanvasApp
- width: 500
- height: 400
- length: math.pi * 4
- new: =>
- super!
- @background = {Math.random!, Math.random!/3+.2, Math.random!/4}
- hue = Math.random!
- @shades = setmetatable {}, __index: (key) =>
- with val = { hue, .7, key * .3 + .1} do rawset @, key, val
+ draw: =>
+ @ctx.fillStyle = hsl @background
+ @ctx\fillRect 0, 0, @width, @height
+ @ctx\translate @width/2, @height/2 + 70
- draw: =>
- @ctx.fillStyle = hsl @background
- @ctx\fillRect 0, 0, @width, @height
- @ctx\translate @width/2, @height/2 + 70
+ draw = (i) ->
+ @ctx\save!
+ @ctx\translate 0, -120*i
+ s = 1 - 0.1 * math.sin @time + i*2
+ s *= 0.8 - i * .4 * math.cos @time
+ @ctx\scale s, s/2
+ @ctx\rotate @time/4 + i * .6 * math.cos @time
+ @ctx.fillStyle = hsl table.unpack @shades[i]
+ @ctx\fillRect -80, -80, 160, 160
+ @ctx\restore!
- draw = (i) ->
- @ctx\save!
- @ctx\translate 0, -120*i
- s = 1 - 0.1 * math.sin @time + i*2
- s *= 0.8 - i * .4 * math.cos @time
- @ctx\scale s, s/2
- @ctx\rotate @time/4 + i * .6 * math.cos @time
- @ctx.fillStyle = hsl table.unpack @shades[i]
- @ctx\fillRect -80, -80, 160, 160
- @ctx\restore!
+ for i=0,1,1/(20 + 19 * math.sin(@time / 2))
+ draw i
+ draw 1
- for i=0,1,1/(20 + 19 * math.sin(@time / 2))
- draw i
- draw 1
-
-twisted = TwistedDemo!
-document.body\appendChild twisted.canvas
+ twisted = TwistedDemo!
+ document.body\appendChild twisted.canvas
diff --git a/client.moon b/client.moon
index d6fd433..dd2ee12 100644
--- a/client.moon
+++ b/client.moon
@@ -1,7 +1,6 @@
export MODE, print, warn, relative, append, on_client
export window, document
-
window = js.global
document = window.document